kikeda1104's blog

備忘録・技術に関することを書いています。(webエンジニア)

FormObject実装(1回目)

form_objectの実装を進める際に、gem virtusを使って実装を進めたので、何度かに分けて書いていきます。

環境

gemの導入

Gemfileに追加します。

  # Gemfile
  ...
  gem 'virtus'
  ...

$ bundle install

app/forms

app/formsディレクトリを作成します。

  mkdir app/forms

config/initialze/forms.rbを作成して,下記を追加します。

# forms.rb
# require_dependecy 'parent_class'

Dir.glob('app/forms/**/*.rb') do |f|
  require_dependency(Rails.roots.join(f))
end

formクラス作成します。

## vim app/forms/offer_form.rb
class OfferForm
  include Virtus.model
  include ActiveModel::Model

  attr_accessor :errors
  attribute :id, Integer
  attribute :title, String
  attribute :description, String
  attribute :main_image, String
  attribute :side_image_left, String
  attribute :side_image_right, String
  attribute :status, Integer, default: 0
  attribute :type_of_job_id, Integer
  attribute :company_id, Integer
  attribute :adoption_of_shape_id, Integer
  attribute :confirming, Boolean, default: false
  attribute :type_of_job_id, Integer
  attribute job_of_offer, JobOffer

  validates :title, :status, :type_of_job_id, :adoption_of_shape_id, presence: true

  def persisted?
    job_offer.persisted?
  end
  ...
end

virtusREADMEに記載されていて、1:多の関係にある構造も実現できますので、accepts_nested_attributesを利用せずに 実装をこちらに変更することができます。

余談ですが、Optimistic lockingを実装する上で、accepts_nested_attributesでは、実現できない(仕様が通れば...)ケースがあり formObjectを通して、lock_versionをコントロールする設計を実現するのに、virtusを採用(しようとした)経緯がありました。

補足: 誤解を与えそうな表現になっていたので、訂正。virtusは、属性の型、タイプキャスト、初期値を実現します。 virtusを使わずに、form objectを作成することもできます。

今回は、ここまでです。

参考

GitHub - solnic/virtus: Attributes on Steroids for Plain Old Ruby Objects

肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳)

Rails4でFormオブジェクトを作る際に気をつける3つのポイント|江の島エンジニアBlog

Railsで導入してよかったデザインパターンと各クラスの役割について - masato_hiのブログ

ActiveRecord::Locking::Optimistic