# 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 に対応していないと考えたほうが良い)