オブジェクト指向言語で使うのが避けられないのがインスタンスを作成する `new`ですよね。今回はRuby on Railsでインスタンスを作るときに使えるテクニックをご紹介します。
環境
- Ruby on Rails: 6.0.0
- Ruby: 2.6.3
不正なアトリビュートがないようにチェック
今回紹介したいのはActiveModel::Modelの不正アトリビュートチェック機能です。
`new`するときに引数を付与して初期データをアトリビュートに入れ込むことは多々あるかと思います。その時、最初に作った時はいいのですが、アトリビュートの数が多すぎて、本来入るべきではないアトリビュートが入っていないかわかってないときにこの手法を使うと便利です。
サンプルプログラム
ここで簡単なサンプルプログラムを作って説明します。
module Sample
class SampleModel
include ActiveModel::Model
FIELDS = %i[
aaa
bbb
ccc
ddd
]
attr_accessor(*FIELDS)
def self.init_with_data(data:)
new(
aaa: data[:aaa],
bbb: data[:bbb],
ccc: data[:ccc],
ddd: data[:ddd],
)
end
end
end
Rails cで機能確認
init_with_data
で正しくインスタンスが作成できている場合は下記のようになります。
$ rails c
> data = { aaa: 1, bbb: 2, ccc: 3, ddd: 4 }
=> {:aaa=>1, :bbb=>2, :ccc=>3, :ddd=>4}
> Sample::SampleModel.init_with_data(data: data)
=> #<Sample::SampleModel:0x00007febed273770 @aaa=1, @bbb=2, @ccc=3, @ddd=4>
ちゃんとインスタンスができていますね。
ちょっと変更
さてここで、 init_with_data
で new
するときの引数に余分な物を入れてやってみましょう。
module Sample
class SampleModel
include ActiveModel::Model
FIELDS = %i[
aaa
bbb
ccc
ddd
]
attr_accessor(*FIELDS)
def self.init_with_data(data:)
new(
aaa: data[:aaa],
bbb: data[:bbb],
ccc: data[:ccc],
ddd: data[:ddd],
zzz: data[:zzz]
)
end
end
end
new
に zzz
を追加しました。これで先ほどと同じようにやってみましょう。
$ rails c
> data = { aaa: 1, bbb: 2, ccc: 3, ddd: 4, zzz: 26 }
=> {:aaa=>1, :bbb=>2, :ccc=>3, :ddd=>4, :zzz=>26}
> Sample::SampleModel.init_with_data(data: data)
Traceback (most recent call last):
3: from (irb):37
2: from app/models/sample/sample_model.rb:15:in `init_with_data'
1: from app/models/sample/sample_model.rb:15:in `new'
ActiveModel::UnknownAttributeError (unknown attribute 'zzz' for Sample::SampleModel.)
怒られてしまいました。エラーを見てみましょう。
ActiveModel::UnknownAttributeError
`zzz`なんてアトリビュート知らないよ、って言われていますね。
attr_accessor
定義していないものはダメだよってことですね。
zzz
をこのクラスのインスタンスで使いたかったらちゃんと `attr_accessor`で登録して使わなければなりません。
では `zzz`を登録してみましょう。
module Sample
class SampleModel
include ActiveModel::Model
FIELDS = %i[
aaa
bbb
ccc
ddd
zzz ⬅︎ ここ
]
attr_accessor(*FIELDS)
def self.init_with_data(data:)
new(
aaa: data[:aaa],
bbb: data[:bbb],
ccc: data[:ccc],
ddd: data[:ddd],
zzz: data[:zzz]
)
end
end
end
また rails cで確認
$ rails c
> data = { aaa: 1, bbb: 2, ccc: 3, ddd: 4, zzz: 26 }
=> {:aaa=>1, :bbb=>2, :ccc=>3, :ddd=>4, :zzz=>26}
> Sample::SampleModel.init_with_data(data: data)
=> #<Sample::SampleModel:0x00007febebd44a20 @aaa=1, @bbb=2, @ccc=3, @ddd=4, @zzz=26>
今回は怒られずに `zzz`が入りました。
これの何がいいの?
さて、この有用性はなんなんでしょうか?直接
すればいいじゃない、と思いますよね。しかしこのように直接 new
new
するのではなく一回Factoryメソッドを通じてインスタンスを作るときにはこのようにするのが良しとされています。理由は2つです。
1. インスタンス作成時の引数の中身がわかりやすくなる
例えば、今回のように data
のように、中にいろんなデータが格納されている変数を引数にすることありますよね。
`data`の中には極端な話、100個も200個もデータを入れることができます。ActiveModel::Modelのこのチェック機能を使うことで、その変数を使ってインスタンスを作成する際に何が使われているかのチェックがしやすくなります。
2. 余分なアトリビュートを作らない
余分なアトリビュートを作ろうとするとエラーで落ちるのでいやでも気づきます。これで無駄コードを減らすことができます(^^)
以上、RailsのActiveModel::Modelのアトリビュートチェック機能の紹介でした。最後までお読みいただきありがとうございました。