YOSHINO日記

プログラミングに関すること

Rails: delegateを使ってモデルのフォームをすっきりさせる

delegate

delegateを使います。

最近知ったRailsの便利なメソッド

指定したクラスのメソッドを簡単に呼び出せるようにしてくれます。

モデルの定義

schema

  create_table "accounts", force: :cascade do |t|
    t.integer "user_id"
    t.string "email"
    t.binary "password_hash"
    t.binary "password_salt"
  end

  create_table "users", force: :cascade do |t|
    t.string "name"
  end

モデル定義

user.rb

以下のようにして、email, passwordをAccountから呼び出しています。

  has_one :account, dependent: :destroy
  accepts_nested_attributes_for :account, update_only: true

  delegate :email, to: :account, allow_nil: true
  def email=(value)
    build_account unless account
    account.email = value
  end

  delegate :password, to: :account, allow_nil: true
  def password=(value)
    build_account unless account
    account.password = value
  end

account.rb

account.password = value

の時に、before_validation、after_validationが実行されるので、

Userから受け取ったpasswordを暗号化して保存します。

  before_validation :generate_password_salt
  after_validation  :generate_password_hash, if: ->{ user.password.present? }

  def generate_password_salt
    self.password_salt ||= BCrypt::Engine.generate_salt
   end

  def generate_password_hash
    self.password_hash = self.class.encrypt(user.password, password_salt)
  end

View: form (html.haml)

userは、email、passwordをカラムとして持っていませんが、

delegateを利用しているので以下のように書くことができます。

= form_for @user do |f|
  = f.label :name, "名前"
  = f.text_field :name
  = f.label :name, "メール"
  = f.email_field :email, id: 'mail'
  = f.label :name, "パスワード"
  = f.password_field :password, id: 'password', required: true, maxlength: 50, data: {toggle: 'popover', trigger: 'focus'}
  = f.submit "Send"