Rails4 DB連携なしモデルでバリデーション機能を使う

More than 1 year has passed since last update.

Goal

  • RailsでActiveRecord機能モデルを使うときは、必ずバックエンドでDBと紐付いていなければならない
  • 今回は、たとえばCSVファイルのCRUDなどRails4でDB連携しないモデルを作成したい
  • RailsのActiveModelが搭載するvalidation機能・callback機能を使って、モデル呼び出し側が意識することなく自動でオブジェクトをチェックするエコシステムをつくりたい

Milestone

  • rails generate modelを実行するとDBと関連付けられてしまうため、これは使わない
    • ActiveRecordを継承したモデルが作成されてしまう、migrationファイルが作成されてしまう
  • モデルクラスを新規作成し、クラスにActiveModelモジュールをMixInさせることで、必要な機能だけをクラスに取り込むことができる(Rails3とRails4ではやりかたがけっこう違うらしい)
  • include ActiveModel::Modelによってvalidationが使えるようになる
  • (追記) ActiveModel::ModelをincludeしていればCallbacksはincludeしなくてもOKでした
    • // include ActiveModel::Callbacksによってコールバックが使えるようになる

Example

  • フォームを使ってアップロードされてきたcsvファイルをそのままサーバに保存するケース
app/models/csv.rb
class Csv
  include ActiveModel::Model
  #include ActiveModel::Callbacks

  attr_accessor :operator_id, :type, :data_csv
  define_model_callbacks :save

  before_save {
    self.valid?
  }

  validates :file_path, presence: true
  validates :type, presence: true, numericality: true
  validates :data_csv, presence: true

  def save
    run_callbacks :save do
      @uploaded_file_str = self.data_csv.read
      File.open(self.file_path, 'wb') do | new_file |
        new_file.write(@uploaded_file_str)
      end
    end
  end
end

ベーシックなクラスに機能をMixIn

class Csv
  include ActiveModel::Model
  #include ActiveModel::Callbacks
  • include ActiveModel::Modelによってvalidatesメソッドなどが有効に // include ActiveModel::Callbacksによってコールバック機能が有効になる
  • ActiveRecord継承のモデルでない場合、validatesメソッドに'on'を渡しても作動せず、またbefore_saveなどのメソッドがデフォルトで使えないので、コールバックを明示的に指定する
  • モデル呼び出し側で、model.valid?のようにバリデーションを実行したいだけならばコールバック機能を使う必要はない

コールバックを使ってValidation自動実行をつくる

Csvモデルに定義されたsaveメソッドにコールバックを設定する
define_model_callbacks定義をいれることで、デフォルトでは、before_save, around_save, after_saveが有効になる

define_model_callbacks :save

さらに、before_saveなどのイベントを発火させるためにsaveメソッドをrun_callbacksでラップする

def save
  run_callbacks :save do
    # save時に実行させたい実装
  end
end

before_saveなどのタイミングで実行させたいロジックを実装する
ブロックでなくてもメソッドを指定してもいい

before_save {
  self.valid?
}

References