# Ruby (on Rails) における、 non-ActiveRecord な Model の作り方 ## 背景 ### 課題 Ruby で ActiveRecord 以外のモデルを作りたい。 ```rb # app/models/foo.rb class Foo attr_reader :aaaaa, :bbbb, :ccc_ccc, :dd def initialize(aaaaa:, bbbb:, ccc_ccc:, dd:) @aaaaa = aaaaa @bbbb = bbbb @ccc_ccc = ccc_ccc @dd = dd end def ab "#{@aaaaa} and #{@bbbb}" end end ``` 普通に書くなら以上のような形になるが、記述の冗長さが気になる(1つのフィールドが4回出現している)。 ### 制約 - できるならRuby標準の機能で。次点でRails標準の機能で。わざわざ新しいライブラリはいれたくない。 ## 手法 ### 0. 普通に Ruby の class を書く 一番最初に書いたのと同じ手法。 Pros: - Ruby 本体の機能だけで実現できる - 細かいことでも表現できる Cons: - 記述量が毎度多くて面倒(=ミスの可能性も増える) ### 1. Struct を利用する ```rb # app/models/foo.rb Foo = Struct.new('Foo', :aaaaa, :bbbb, :ccc_ccc, :dd, keyword_init: true) do def ab "#{@aaaaa} and #{@bbbb}" end end ``` 上記のままだと Mutable である。 Immutable (一度作ったら変更不可) にしたい場合、以下のようにする ```rb # app/models/foo.rb Foo = Struct.new('Foo', :aaaaa, :bbbb, :ccc_ccc, :dd, keyword_init: true) do def initialize(**) super freeze end def ab "#{@aaaaa} and #{@bbbb}" end end ``` なお Ruby 本体においても以上のような動きをするクラス Struct::Value が提案されているが、現段階でまだ実装されておらず、入るとしてもしばらく先になりそう。 https://bugs.ruby-lang.org/issues/16122 #### Pros/Cons Pros: - Ruby 本体の機能だけで実現できる - 記述量が少ない - Immutable も実現できる Cons: たとえば field aaaaa は再代入可能 (attr_accessor) だけど bbbb は再代入不可 (attr_reader) のような細かいことをやろうとすると、表現できなさそう (freeze できないので) ### 2. ActiveModel を利用する ```rb # app/models/foo.rb class Foo include ActiveModel::Model attr_accessor :aaaaa, :bbbb, :ccc_ccc, :dd def ab "#{@aaaaa} and #{@bbbb}" end end ``` Pros: - 記述量が少ない - ActiveModel 専用の機能 (例えば validation) も使える Cons: - Immutable を表現できない (例えばvalidationを使うと内部フィールドの errors を更新するので、 freeze もすることができない。ActiveModel は Immutable に対応していないと考えたほうが良い)