Rails Tutorial 6章 編集中
ここで主に扱うのは、ユーザーのデータモデルの作成と、ユーザーを保存する手段の確保。
Userモデル
$ rails g model User name:string email:string
※コントローラー名には複数形、モデル名には単数形
すると、こんなマイグレーションファイルが作成される
class CreateUsers < ActiveRecord::Migration[5.0] def change create_table :users do |t| t.string :name t.string :email t.timestamps end end end
このファイルを
$ rails db:migrate
で実行。そうすることで、この変更データがDBに反映される。
ちなみに、マイグレーションを元に戻すには、
$ rails db:rollback
初めてdb:migrateが実行されると、 db/development.sqlite3 という名前のファイルが生成され、このファイルからDBの構造を確認できる。
modelファイル
app/models/user.rb class User < ApplicationRecord end
つまり、User < ApplicationRecord < ActiveRecord::Base
では、ActiveRecord::Baseでは何ができるのか?
1、オブジェクトの作成
例えば、
>> User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
オブジェクトの有効性
>> user.valid? true
オブジェクトをDBに保存
>>user.save ~~~~ =>true
saveメソッドは、成功すればtrueを、失敗すればfalseを返す。(timestanpが更新されたり)
また、
>>User.create(name: "A Nother", email: "another@example.org")
では、true/falseではなく、オブジェクト自身を返す。
2、オブジェクトの検索
User.find(1) => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-05-23 19:05:58", updated_at: "2016-05-23 19:05:58">
DBに存在しないオブジェクトを検索した場合、findメソッドは例外を発生。
>> User.find(3) ActiveRecord::RecordNotFound: Couldn't find User with ID=3
findメソッド以外にも、特定の属性でユーザーを検索する方法も
>> User.find_by(email: "mhartl@example.com") => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-05-23 19:05:58", updated_at: "2016-05-23 19:05:58">
また、こんなのも
>>User.first
>>User.all
3、オブジェクトの更新
属性のハッシュを受け取り、成功時には更新と保存を続けて同時に行う。しかし、検証に失敗すると、update_attributesの呼び出しは失敗する。
>>user.update_attributes(name: ~~, email: ~~) =>true
そこで、update_attributeを使えば検証を回避できる。(特定の属性のみを更新)
>>user.update_attribute(name: ~~) =>true
ユーザーを検証
app/models/user.rb
<存在性のバリデーション>
presence: t/f
validates :name, presence: true validates :email, presence: true #またはvalidates(:~~~, presence: true)
<長さ>
length: { }
validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 }
<emailのフォーマット>
正規表現というものを使ってメールアドレスフォーマットを指定。詳しくはまだ手をつけないほうがいいらしい。
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
<emailの一意性を検証>
これを加えるだけ。
uniqueness: true
通常、メールアドレスは大文字小文字が区別されないので、
uniqueness: { case_sensitive: false }
と書き換える。
trueをcase_sensitive: falseに置き換えただけだが、Railsは:uniquenessをtrueと判断する。
しかし、DBのレベルでは一意性を保証していないため、素早くリクエストが複数送信された際に同じ値を持つユーザーレコードが作成されてしまう。
そこで、DBにインデックスというものを追加。
(本でいう索引みたいなものらしい)
これによって、DBに一意性の設定が可能&検索効率の向上
データモデリングの変換が必要なので、
$ rails g migration add_index_to_users_email
オプション unique :true で一意性を強制
db/migrate/[timestamp]_add_index_to_users_email.rb def change add_index :users, :email, unique: true end
rails db:migrate
また、データベースによっては常に大文字と小文字を区別するとは限らないため、アプリケーション側でメールアドレスをすべて小文字にすることによって対策。
app/models/user.rb
before_save { self.email = email.downcase }
※ Userモデルの中では右式のselfを省略できる
ユーザーが有効かどうかは valid?メソッドで確認でき、検証に失敗した時はfalse、全てのバリデーションに通った時はtrueを返す。
>>User.valid? =>true
これの有効性・存在性のテスト
→ setup にてあらかじめユーザーを作成
test/models/user_test.rb def setup @user = User.new(name: "Example User", email: "user@example.com") end def test "should be valid" do assert @user.valid? end def test "name should be present" do @user.name = " " assert_not @user.valid? end def test "email should be present" do @user.email = " " assert_not @user.valid? end #長さの検証 test "name should not be too long" do @user.name = "a" * 51 assert_not @user.valid? end test "email should not be too long" do @user.email = "a" * 244 + "@example.com" assert_not @user.valid? end
Rails Tutorial 9章 編集中
Remember_me 機能
よくあるやつ。次回以降ログインを省略みたいな。
手順としては、
1、記憶トークンを作成
2、ユーザーと関連づける
3、CookieメソッドでDBに保存
$ rails g migration add_remember_me_digest_to_users remember_digest:string
$ rails db:migrate
記憶トークンには、安全性の高いものを使いたい。Ruby標準ライブラリのurlsafe_base64メソッドがいい。
def User.new_token SecureRandom.urlsafe_base64 end
ここから、 user.remember メソッドを作成して、これで記憶トークンをユーザーと関連づけ、トークンに対応する記憶ダイジェストをDBに保存
しかし、保存場所であるremember_token属性が追加されていない。
そこで、user.remember_token でトークンにアクセスできるようにする。(DBには保存しないように)
パスワードで使ったように、仮想のpasswordを作ったみたいに。
rememberメソッドをUserモデルに追加する
attr_accessor :remember_token . . def remember self.remember_token = User.new_token update_attribute(:remember_digest, User.digest(remember_token)) end
※ DBにアクセスする必要がないため、attr_accesorでremember_tokenを定義
※ update_attribute はバリデーションを通過可能(passにアクセスしなくていい)
ここで、Cookieメソッドの使い方。
sessionのようにハッシュで使用可能で、一つの値と、オプションのexpiresで構成されている
cookies[:remember_token] = { value: remember_token, expires: 20.years.from_now.utc }
これを permanent メソッドで書き換えて、
cookies.permanent[:remember_token] = remember_token
ユーザーIDをCookieに保存するには(署名あり)
cookies.signed[:user_id] = user_id
記憶トークンとペアで使うために
cookies.permanent.signed[:user_id] = user_id
以上より、以後のページでcookieからユーザーを取り出すことができる。
んで、なんたらかんたら
app/models/user.rb def authenticated?(remember_token) BCrypt::Password.new(remember_digest).is_password?(remember_token) end
この remember_token は、さっきのアクセサのやつじゃなくただのローカル変数。
remember_digest は、self.remember_digestと同じ使い方らしい。
app/controllers/sessions_controller.rb def create . . remember user . end
app/helpers/sessions_helper.rb #ユーザーのセッションを永続的にする def remember(user) cookies.permanent.signed[:user_id] = user_id cookies.permanent[:remember_token] = user.remember_token end