ActiveModel::ModelモジュールとActiveModel::Attributesモジュール
[ActiveModel::Model]モジュールと[ActiveModel::Attributes]モジュールの違いがいまいち分からなかったので、こちらにまとめます。
・[ActiveModel::Model]モジュール
[ActiveModel::Model]モジュールをincludeしてできるようになる主な事は、以下のような事になります。(ActiveRecordのDBに連携しないようなモデルになる。)
モデル名の調査: モデル名からモデル名の複数形の予測などの機能が使えるようになる。(以下のような機能)
[1] pry(main)> Fish.model_name.name => "Fish" [2] pry(main)> Fish.model_name.singular => "fish" [3] pry(main)> Fish.model_name.plural => "fishes" [4] pry(main)> Fish.model_name.element => "fish" [5] pry(main)> Fish.model_name.human => "魚" [6] pry(main)> Fish.model_name.collection => "fishes" [7] pry(main)> Fish.model_name.param_key => "fish" [8] pry(main)> Fish.model_name.i18n_key => :fish [9] pry(main)> Fish.model_name.route_key => "fishes" [10] pry(main)> Fish.model_name.singular_route_key => "fish"
変換: Railsのモデルの変換メソッド[ to_model , to_key , to_param , to_partial_path ]が利用できるようになる。(form_with modelなどが利用できるようになる。)
翻訳: i18nによる翻訳機能が使えるようになる。
バリデーション: 検証用の機能を提供する。
ヘルパーメソッド: [form_with]や[render]などのヘルパーメソッドが使えるようになる。
[attr_accessor]メソッド: インスタンスにインスタンス変数を持たせることができ、インスタンス変数を呼び出せる。(以下のように検証)
・[ActiveModel::Model]モジュールをinclude
[app/forms/cooking_search_time_form.rb] class CookingSearchTimeForm include ActiveModel::Model attr_accessor :name end
[1] pry(main)> a = CookingSearchTimeForm.new(name: "テスト") => #<CookingSearchTimeForm:0x00007fb3684acc48 @name="テスト"> [2] pry(main)> a => #<CookingSearchTimeForm:0x00007fb3684acc48 @name="テスト">
・[ActiveModel::Model]モジュールをincludeしない
[app/forms/cooking_search_time_form.rb] class CookingSearchTimeForm attr_accessor :name end
[1] pry(main)> a = CookingSearchTimeForm.new(name: "テスト") ActiveModel::UnknownAttributeError: unknown attribute 'name' for CookingSearchTimeForm. from /Users/higmonta/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activemodel-6.0.4.1/lib/active_model/attribute_assignment.rb:52:in `_assign_attribute' [2] pry(main)> a => nil
・[ActiveModel::Attributes]モジュール
[ActiveModel::Attributes]モジュールをincludeすると[attribute]メソッドが使えるようになる。
[attribute]メソッドを使うと、[attr_accessor]メソッドと同じようにインスタンス変数が使えるようになり且つ、型の指定もできる。(以下のように検証)
・[ActiveModel::Model]モジュールをincludeして、[attr_accessor]メソッドを使ってインスタンス変数を設定(いろんな型でインスタンス変数を設定できる)
[app/forms/cooking_search_time_form.rb] class CookingSearchTimeForm include ActiveModel::Model attr_accessor :name end
[1] pry(main)> a = CookingSearchTimeForm.new(name: "テスト") => #<CookingSearchTimeForm:0x00007f9de0544900 @name="テスト"> [2] pry(main)> b = CookingSearchTimeForm.new(name: 11) => #<CookingSearchTimeForm:0x00007f9de58e6d60 @name=11> [3] pry(main)> a => #<CookingSearchTimeForm:0x00007f9de0544900 @name="テスト"> [4] pry(main)> b => #<CookingSearchTimeForm:0x00007f9de58e6d60 @name=11>
・[ActiveModel::Attributes]モジュールをincludeしない
[app/forms/cooking_search_time_form.rb] class CookingSearchTimeForm attribute :name, :string end
[1] pry(main)> a = CookingSearchTimeForm.new(name: "テスト") NoMethodError: undefined method `attribute' for CookingSearchTimeForm:Class Did you mean? attr_writer from /Users/higmonta/workspace/fishing_cooking/app/forms/cooking_search_time_form.rb:2:in `<class:CookingSearchTimeForm>'
・[ActiveModel::Attributes]モジュールをincludeする([ActiveModel::Model]モジュールは、includeしない)
[ActiveModel::Model]モジュールをincludeしないと、そもそもモデルのように使えないのでエラーになる。
[app/forms/cooking_search_time_form.rb] class CookingSearchTimeForm include ActiveModel::Attributes attribute :name, :string end
[1] pry(main)> a = CookingSearchTimeForm.new(name: "テスト") ArgumentError: wrong number of arguments (given 1, expected 0) from /Users/higmonta/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activemodel-6.0.4.1/lib/active_model/attributes.rb:79:in `initialize'
・[ActiveModel::Attributes]モジュールと[ActiveModel::Model]モジュールをincludeする。
[attribute]メソッドで型指定をしているので、[11]という数字でインスタンス変数を設定しても文字列型に変換されている。
[app/forms/cooking_search_time_form.rb] class CookingSearchTimeForm include ActiveModel::Model include ActiveModel::Attributes attribute :name, :string end
[1] pry(main)> a = CookingSearchTimeForm.new(name: "テスト") => #<CookingSearchTimeForm:0x00007fc441e03690 @attributes= #<ActiveModel::AttributeSet:0x00007fc441e035f0 @attributes= {"name"=> #<ActiveModel::Attribute::FromUser:0x00007fc441e03348 @name="name", @original_attribute= #<ActiveModel::Attribute::WithCastValue:0x00007fc441e03578 @name="name", @original_attribute=nil, @type=#<ActiveModel::Type::String:0x00007fc445894ea8 @limit=nil, @precision=nil, @scale=nil>, @value_before_type_cast=nil>, @type=#<ActiveModel::Type::String:0x00007fc445894ea8 @limit=nil, @precision=nil, @scale=nil>, @value_before_type_cast="テスト">}>> [2] pry(main)> b = CookingSearchTimeForm.new(name: 11) => #<CookingSearchTimeForm:0x00007fc44597afc0 @attributes= #<ActiveModel::AttributeSet:0x00007fc44597af48 @attributes= {"name"=> #<ActiveModel::Attribute::FromUser:0x00007fc44597ade0 @name="name", @original_attribute= #<ActiveModel::Attribute::WithCastValue:0x00007fc44597aed0 @name="name", @original_attribute=nil, @type=#<ActiveModel::Type::String:0x00007fc445894ea8 @limit=nil, @precision=nil, @scale=nil>, @value_before_type_cast=nil>, @type=#<ActiveModel::Type::String:0x00007fc445894ea8 @limit=nil, @precision=nil, @scale=nil>, @value_before_type_cast=11>}>> [3] pry(main)> a.name => "テスト" [4] pry(main)> b.name => "11"
参考記事
ActiveModelを使ってDBと関係ないFormの構築してみた - その辺にいるWebエンジニアの備忘録
ActiveModel::Model で DBに依存しないモデルを作ろう - What is it, naokirin?
form object 検索機能を追加する - mmm_stの日記
Formオブジェクト~1つのフォームで複数モデルとやりとりをする画期的なヤツ~ - Qiita
【Ruby on Rails】フォームオブジェクトを使って検索キーワードをcontrollerに送る - Qiita
ActiveModel::Model で DBに依存しないモデルを作ろう - What is it, naokirin?
ActiveModel::Attributesを使う - Qiita
[Rails] ActiveModel::Attributesの使い方(配列化やネストしたhashの取り扱いなども) - Qiita
RailsでDBと連携しないModelを作成する - Qiita
Railsの技: Attributes APIでPOROの属性を自動的にキャストする(翻訳)|TechRacho by BPS株式会社