Rails Tutorialをやってみよう07
第6章 ユーザーのモデルを作成する
6.1 Userモデル
第6章 ユーザーのモデルを作成する | Rails チュートリアル
Gitでバージョン管理を行なっているのであれば、このタイミングでユーザーをモデリングするためのトピックブランチを作成しておいてください。
$ git checkout master $ git checkout -b modeling-users
gitがどのように記録されているかわからなくなったら
$ git log
でgitの記録を見てみましょう。
ちなみにgit logは、"q"で終了して、元のコマンドラインに戻ることができます。
では、引き続き、ユーザーモデルを作成していきます。
userモデルを作成していきます。
userのデーターベースをイメージします。
まず、userのコントローラーを作成します。
$ rails generate controller Users new
コントローラーの作成には、
rails generate controller <大文字かつ複数形> new
というメソッドを使います。
次に、userのモデルを作成します。
このときに、データモデルのスケッチの属性を付属させて作成します。
id属性は自動で作成されます。
$ rails generate model User name:string email:string
モデルの作成には、
rails generate model <大文字かつ単数形> <属性名>:<データ形式> <属性名>:<データ形式>
というメソッドを使います。
generateコマンドの結果のひとつとして、マイグレーションと呼ばれる新しいファイルが生成されます。マイグレーションは、データベースの構造をインクリメンタルに変更する手段を提供します。
db/migrate/[timestamp]_create_users.rbが作成されています。
t.timestamps null: falseも同時に作成されています。
このt.timestamps null: falseがあることで、
created_atとupdated_atが自動作成されます。
この2つはユーザーが作成または更新された時の時刻を記録するタイムスタンプを記録します。
マイグレーションは、以下のようにrakeコマンドを使って実行することができます。
$ bundle exec rake db:migrate
SQliteのデータベース構造を可視化する方法
SQLiteBrowserをインストールします。
DB Browser for SQLite
macでhomebrewを使ってインストールする場合は、以下のbrew caskコマンドでインストールできます。
$ brew cask install sqlitebrowser
RubyMineを使っている場合は、このブログを参考にしてください。mi813.hatenablog.com
6.1.2 modelファイル
6.1.3 ユーザーオブジェクトを作成する
$ rails console --sandbox
sandboxモードでrailsを起動します。
sandboxモードでrails consoleを起動すると、
データ・モデルを変更せずに調べることができます。
rails consoleは、"control + d"にて終了できます。
>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
nameとemail属性が期待通り設定されていることがわかります。
>> user.valid? true
>> user.save (0.1ms) SAVEPOINT active_record_1 SQL (3.2ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Michael Hartl"], ["email", "mhartl@example.com"], ["created_at", "2015-11-23 03:36:30.051170"], ["updated_at", "2015-11-23 03:36:30.051170"]] (0.1ms) RELEASE SAVEPOINT active_record_1 => true>
saveメソッドは、成功すればtrueを、失敗すればfalseを返します (現状では、保存はすべて成功するはずです。
>> user => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-07-24 00:57:46", updated_at: "2014-07-24 00:57:46">
以下の3つのタイトルでは、データ・モデルの変更をrails consoleにて編集するやり方が記載されています。
6.1.3 ユーザーオブジェクトを作成する
6.1.4 ユーザーオブジェクトを検索する
6.1.5 ユーザーオブジェクトを更新する
取りあえず、テストは飛ばして次に進みます。
Rubyの正規表現を試すことが出来るWebサービス。www.rubular.com
メールフォーマットを正規表現で検証する (GREEN app/models/user.rb)
class User < ActiveRecord::Base validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX } end
既に存在するモデルに構造を追加する
$rails neerate migration add_index_to_user_email
メールアドレスの一意性を強制するためのマイグレーション db/migrate/[timestamp]_add_index_to_users_email.rb
class AddIndexToUsersEmail < ActiveRecord::Migration def change add_index :users, :email, unique: true end end
$ bundle exec rake db:migrate
Userのデフォルトfixture RED
test/fixtures/users.yml
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/ # FixtureSet.html one: name: MyString email: MyString two: name: MyString email: MyString
上記のファイルを空にしておきます。
空のfixtureファイル GREEN test/fixtures/users.yml
# empty
email属性を小文字に変換してメールアドレスの一意性を保証する GREEN app/models/user.rb
class User < ActiveRecord::Base before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } end
6.3.1 ハッシュ化されたパスワード
セキュアなパスワードの実装は、has_secure_passwordというRailsのメソッドを呼び出すだけでほとんど終わってしまいます。このメソッドは、Userモデルで次のように呼び出せます。
app/models/user.rb
class User < ActiveRecord::Base . . . has_secure_password end
のようにモデルにこのメソッドを追加すると、次のような機能が使えるようになります。
・セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
・2つのペアの仮想的な属性18 (passwordとpassword_confirmation)が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される。
・authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalse返すメソッド)。
この魔術的なhas_secure_password機能を使えるようにするには、1つだけ条件があります。それは、モデル内にpassword_digestという属性が含まれていることです。
データモデルにするために、まずはpassword_digestカラム用の適切なマイグレーションを生成します。マイグレーション名は自由に指定できますが、上のように末尾をto_usersにしておくことをお勧めします。こうしておくと、usersテーブルにカラムを追加するマイグレーションがRailsによって自動的に作成されるからです。add_password_digest_to_usersというマイグレーションファイルを生成するためには、以下のコマンドを実行します。
$ rails generate migration \ add_password_digest_to_users password_digest:string
usersテーブルにpassword_digestカラムを追加するマイグレーション db/migrate/[timestamp]_add_password_digest_to_users.rb
class AddPasswordDigestToUsers < ActiveRecord::Migration def change add_column :users, :password_digest, :string end end
$ bundle exec rake db:migrate
has_secure_passwordを使ってパスワードをハッシュ化するためには、最先端のハッシュ関数であるbcryptが必要になります。パスワードを適切にハッシュ化することで、たとえ攻撃者によってデータベースからパスワードが漏れてしまった場合でも、Webサイトにログインされないようにできます。サンプルアプリケーションでbcryptを使用するために、bcrypt-ruby gemをGemfileに追加します
bcryptをtGemfileに追加する
source 'https://rubygems.org' gem 'rails', '4.2.2' gem 'bcrypt', '3.1.7' . . .
6.3.2 ユーザーがセキュアなパスワードを持っている
Userモデルにpassword_digest属性を追加し、Gemfileにbcryptを追加したことで、ようやくUserモデル内でhas_secure_passwordが使えるようになりました。
Userモデルにhas_secure_passwordを追加する RED app/models/user.rb
class User < ActiveRecord::Base before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } has_secure_password end
bundle exec rake test
空のパスワードを入力させないために、存在性のバリデーション (6.2.2) も一緒に追加します。結果として、Userモデルのコードはリスト6.39のようになります。(has_secure_passwordメソッドは存在性のバリデーションもしてくれるのですが、これは新しくレコードが追加されたときだけに適用されます。したがって、たとえばユーザーが " " (6文字分の空白スペース) といった文字列をパスワード欄に入力して更新しようとすると、バリデーションが適用されずに更新されてしまう問題が発生してしまいます。)
セキュアパスワードの完全な実装 GREEN app/models/user.rb
class User < ActiveRecord::Base before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } has_secure_password validates :password, presence: true, length: { minimum: 6 } end
6.3.4 ユーザーの作成と認証
ユーザーモデルの基本部分が完成しました。
データベースに新規ユーザーを1人作成しましょう。
$ rails console >> User.create(name: "Michael Hartl", email: "mhartl@example.com", ?> password: "foobar", password_confirmation: "foobar") => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-09-11 14:26:42", updated_at: "2014-09-11 14:26:42", password_digest: "$2a$10$sLcMI2f8VglgirzjSJOln.Fv9NdLMbqmR4rdTWIXY1G...">
RubyMineのデータベースで確認してみると、rails consoleで作成したデータが記録されているのが確認できます。
コンソールに戻ってpassword_digest属性を参照してみると、has_secure_passwordの効果を確認できます。
>> user = User.find_by(email: "mhartl@example.com") >> user.password_digest => "$2a$10$YmQTuuDNOszvu5yi7auOC.F4G//FGhyQSWCpghqRWQWITUYlG3XVy"