オブジェクト指向言語で使うのが避けられないのがインスタンスを作成する `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>
Code language: PHP (php)

ちゃんとインスタンスができていますね。

ちょっと変更

さてここで、 init_with_datanewするときの引数に余分な物を入れてやってみましょう。

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

newzzzを追加しました。これで先ほどと同じようにやってみましょう。

$ 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.)
Code language: PHP (php)

怒られてしまいました。エラーを見てみましょう。

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>
Code language: PHP (php)

今回は怒られずに `zzz`が入りました。

これの何がいいの?

さて、この有用性はなんなんでしょうか?直接 new すればいいじゃない、と思いますよね。しかしこのように直接 new するのではなく一回Factoryメソッドを通じてインスタンスを作るときにはこのようにするのが良しとされています。理由は2つです。

1. インスタンス作成時の引数の中身がわかりやすくなる

例えば、今回のように dataのように、中にいろんなデータが格納されている変数を引数にすることありますよね。

`data`の中には極端な話、100個も200個もデータを入れることができます。ActiveModel::Modelのこのチェック機能を使うことで、その変数を使ってインスタンスを作成する際に何が使われているかのチェックがしやすくなります。

2. 余分なアトリビュートを作らない

余分なアトリビュートを作ろうとするとエラーで落ちるのでいやでも気づきます。これで無駄コードを減らすことができます(^^)

以上、RailsのActiveModel::Modelのアトリビュートチェック機能の紹介でした。最後までお読みいただきありがとうございました。

By user