セキュリティ系の勉強、その他開発メモとか雑談. GithubはUnity触っていた頃ものがメイン Twitterフォローもよろしくです

ruby チュートリアル6章 メモ

Model

データモデルとして扱うデフォルトのデータ構造のことをモデルと呼ぶ。データベースとのやり取りを行うライブラリはActiveRecordで、データオブジェクトの生成・保存・検索のメソッドをもつ。よって、SQL文を書かない。マイグレーション機能はデータの定義をRubyでできるようにしてくれている。

モデルの生成

$ rails generate model User name:string email:string

コントローラ名には複数形を使って、モデル名には単数形を使用する。これによって生成されるテーブル名は複数形 (users) になる。上のコマンドで以下のマイグレーションファイルが生成される。タイムスタンプを名前に使用する事で多人数での開発でもコンフリクトを起こさず順番を保持することができる。t.timestampsは特別なコマンドで、created_atとupdated_atという2つの「マジックカラム (Magic Columns)」を作成する。これらは、あるユーザーが作成または更新されたときに、その時刻を自動的に記録するタイムスタンプの役割。また、記述されていないが、id属性が自動で生成される。

# db/migrate/[timestamp]_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

マイグレーションは以下のコマンドで実行される。

$ bin/rails db:migrate

ロールバック

マイグレーションロールバックが容易にできる。

$ rails db:rollback

マイグレーションファイルのchangeメソッドはdrop_tablecreate_tableメソッドがそれぞれ対応していることを理解しており、マイグレーションファイルを呼ぶだけでロールバックが可能になっている。一部カラム削除等の操作はchangeメソッドを使用せず、updownメソッドを自分で別々に定義する必要がある。

schema.rb

schema.rbにはマイグレートされたものが記録されていく。ロールバックすると、その分消える。

モデルの生成と保存

$u = User.new(name:"hoge") # hoge以外の値がnil
$u.save #idとタイムスタンプに値が入る

#上2行と同じ
$ User.create(name:"hoge")

validation

モデルのファイルに書く。例えば上のUserモデルであれば以下のようにする。

#app/models/user.rb
class User < ApplicationRecord
  validates :name, presence: true
end

validatesはメソッドなので以下のようにカッコを使用してもかける。

class User < ApplicationRecord
  validates(:name, presence: true)
end

この検証をコンソールで確認することも可能。エラー内容を確認することもできる

$ rails console --sandbox
>> user = User.new(name: "", email: "mhartl@example.com")
>> user.valid?
=> false
>> user.errors.full_messages
=> ["Name can't be blank"]

インスタンス変数

Minitestではsetup, RSpecではbeforeあたりでテスト毎の実行前に呼びたい共通コードを書く。

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

頭に@をつけたインスタンス変数を使用すれば、このコード内でどこからでも参照可能になる。@をつけなければ、例えばit内で呼ぶことはできない。

DBレベルでの一意性

一意性を保ちたいものに対して、インデックスを追加し、インデックスの一意性を保つように変更する。

インデックスの追加

インデックスを追加することによって、find等の検索のスピードも早くなる。

$ bin/rails generate migration add_index_to_users_email

上のコードでファイルは生成されるが中身は無い状態なので自分で追加する必要がある。

class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
  def change
    add_index :users, :email, unique: true #trueにすることによって
  end
end

追記したら、以下のコマンドで反映。

$ bin/rails db:migrate

ActiveRecordのコールバックメソッド

ある特定の時点で呼ばれるメソッド。例えば、before_saveを使用すればユーザをデータベースに保存する前に特定の処理を挟むことができる。

class User < ApplicationRecord
  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

ハッシュ化されたパスワード

has_secure_passwordメソッドを呼び出す。ハッシュ関数を利用するにはgem 'bcrypt'のジェムを入れておくこと。利用するには以下のように追記すればok。

class User < ApplicationRecord
  .
  .
  .
  has_secure_password
end

これにより以下の利点がある。

  • セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
  • 2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される 。
  • authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド) 。


よって、モデルにpassword_digestというカラムを追加する必要がある。

$ rails generate migration add_password_digest_to_users password_digest:string

末尾にto_usersとつけることで、自動でadd_password_digest_to_usersというマイグレーションファイルを生成してくれる。以下がそれ。

class AddPasswordDigestToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :password_digest, :string
  end
end

最後にいつも通りマイグレーションコマンドで変更を反映してあげればok。