building modelsvwith active model

44
Crafting Rails 4 Applications 2Active Modelを使って モデルを作る

Upload: shozo-hatta

Post on 12-Jul-2015

1.003 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Building Modelsvwith Active Model

Crafting Rails 4 Applications

第2章Active Modelを使って

モデルを作る

Page 2: Building Modelsvwith Active Model

目標Mail Formプラグインを作ってActive Modelを勉強する• ActiveModel::AttributeMethods

• API準拠• ActiveModel::Conversion

• ActiveModel::Naming

• ActiveModel::Translation

• ActiveModel::Validations

• ActiveModel::Callbacks

• そして最後に…

Page 3: Building Modelsvwith Active Model

以下のプラグインを作ってみようPost リクエストから送信されたハッシュを受け取り、バリデーションを行ってから、特定のメールアドレスに送信する

まずは一発

Page 4: Building Modelsvwith Active Model

attributes()

最初にattributes()メソッドを作ってみる

(動作: Mail Formオブジェクトに含める属性を指定する)

Page 5: Building Modelsvwith Active Model

ActiveModel::AttributeMethodsMail Formオブジェクトに含める属性を指定する

テスト:

Page 6: Building Modelsvwith Active Model

attributes()この時点ではMailForm::Baseがないのでbundle exec rake testすると当然失敗する

Page 7: Building Modelsvwith Active Model

テストが通るようにするためにMailForm::Baseを実装

属性の作成はattr_accessor()に委譲(delegate)している

attributes()

Page 8: Building Modelsvwith Active Model

attributes()autoload()とは

• MailForm::Baseという定数が最初に参照されるまではmail_form/base.rbが読み込まない

• 起動を早めるためによく使われるテクニック

autoload()を追加することでテストがパスする

Page 9: Building Modelsvwith Active Model

ActiveModel::AttributeMethods

ActiveModel::AttributeMethodsは、定義済みの属性を追いかけて共通の動作を追加する

ここではclear_というprefixにマッチするメソッドを定義してみる(ActiveRecordでおなじみの、定義した属性に自動的にメソッドが追加される、あの動作を再現する)

まずはテストを作成

Page 10: Building Modelsvwith Active Model

ActiveModel::AttributeMethods

clear_で始まる属性を定義し、それを使用してメソッドを定義する

Page 11: Building Modelsvwith Active Model

ActiveModel::AttributeMethods

これでテストに通る

Page 12: Building Modelsvwith Active Model

ActiveModel::AttributeMethods

attribute_method_prefix()をattribute_method_suffixに変えればsuffix

でも同じことができるname?()とemail?()にマッチさせる場合

attribute_method_affix()を使用すれば両方を同時に指定することもできる

Page 13: Building Modelsvwith Active Model

秘密はmethod_missing()にあり

ActiveModel::AttributeMethodsは、動的なメソッドをキャッチするために内部でmethod_missing()を使用する

clear_name()やclear_email()のようなメソッドが明示的には定義されていないのに、属性を定義するだけで利用できるのはこのおかげ

そしてActiveModelは、clear_*をキャッチしたらclear_attributeを呼び出してくれる

Page 14: Building Modelsvwith Active Model

method_missing()method_missing()は、メッセージの継承パスの最後のところでメッセージをキャッチする

(排水口に取り付けたストッキングみたいなもの?)

method_missing()はしばしば黒魔法にたとえられる

非常に強力だが、乱用すると流れを追いにくくなり、えらいことになるらしい

このあたりのことは「Metaprogramming Ruby」に詳しく載っている

Page 15: Building Modelsvwith Active Model

method_missingActiveRecordではこのような動的なattribute methodが広く使われている

でも、Rails 4 で find_by_* 形式の動的な検索メソッドが非推奨になったのは、もしかしてやりすぎてしまったからなのかも。

Railscastより:

Page 16: Building Modelsvwith Active Model

ぼそっとrubyのクラス定義でクラス名をファジーマッチできたらもっとシンプルにならないだろうか?

冗談です

Page 17: Building Modelsvwith Active Model

Active Model APIに準拠する

自作モデルをActive Model APIに沿ったものにする:

(そうしないとcontrollerやviewで使えない)

実は ActiveModel::Lint::Testsですべて行える

Page 18: Building Modelsvwith Active Model

Active Model APIに準拠する

APIに合致するまでエラーを出しまくる

Page 19: Building Modelsvwith Active Model

Active Model APIに準拠する

以下のエラーを解決しよう

以下を定義すればよい

でもこれをもろに実装する必要はない

Page 20: Building Modelsvwith Active Model

ActiveModel::Conversion

ActiveModel::Conversionを使用すれば、to_model()だけでなくto_key()やto_param()、to_partial_path()などの必須メソッドを実装できる

• to_key() : モデルを指定するキーの配列を返す。viewのdom_id()で使用され、dom_id()はRailsで広く使われている

• to_param() : ルーティングで使用され、モデルのユニークURL生成のために広く使われている

• to_partial_path() : ビューのrender()で使用される

Page 21: Building Modelsvwith Active Model

ActiveModel::Conversion

これらのメソッドをカスタマイズすることもできる

to_param() : URLのidにタイトルを加えたい:

to_partial_path() : postでフォーマットを指定したいビューでこう書くより

オーバーライドしてから呼び出す方がスマートかつ高速(render呼び出しが1回で済むので)

Page 22: Building Modelsvwith Active Model

ActiveModel::Conversion

ActiveModel::Conversionをインクルードするだけでよい

これでエラーが3つに減った

Page 23: Building Modelsvwith Active Model

ActiveModel::Namingmodel_nameのエラーを減らすには、ActiveModel::Namingをエクステンド

これを追加すると、human()やsingular()などのモデル名活用が行われる

Page 24: Building Modelsvwith Active Model

ActiveModel::Translation

i18n対応するにはActiveModel::Translationをエクステンド

* i18nバックエンドが確実にリロードされるようにbegin…ensure…endで囲んでいる

Page 25: Building Modelsvwith Active Model

ActiveModel::Validation

以下のエラーを解消するためにActiveModel::Validationをインクルード

Page 26: Building Modelsvwith Active Model

persisted?()今回、以下のエラーについては自前で実装が必要

Page 27: Building Modelsvwith Active Model

persisted?()このmail_formで永続性は不要なのでfalseを返すようにする(※protectedの下に書かないこと)

これでテストはすべてパスする

Page 28: Building Modelsvwith Active Model

Formを配信する次はdeliver()メソッドを実装してみる• モデルのemail属性に保存されているメアドに送信する

でテスト

Page 29: Building Modelsvwith Active Model

Formを配信する実装(MailForm::Notifierを作成)

ActionMailer::Baseを継承して実装

contact()はMailFormクラスではなく子クラスのメールデータ(:to, :from, :subject)を返す(配信先をカスタマイズするのにNotifierクラスをいじる必要がない

Page 30: Building Modelsvwith Active Model

Formを配信するappend_view_path()はプラグインフォルダ内にlib/viewsフォルダを追加しテンプレート検索の対象とする

以下を追加

Page 31: Building Modelsvwith Active Model

Formを配信する準備が整ったのでサンプルクラスにメソッドを追加してみる

メイラーにビューテンプレートがないのでこの時点では失敗する

ビューテンプレートを追加してみる

Page 32: Building Modelsvwith Active Model

Formを配信するattrubete_names()というclass_attributeを定義して属性のリストを取得できる

• attributes()を呼ぶたびに更新される• 継承時に自動的に動作する

これでテストに通る

Page 33: Building Modelsvwith Active Model

capybaraで結合テスト(略)

Page 34: Building Modelsvwith Active Model

validationvalidates_presence_of() の動作:

以下の呼び出しは基本的にどれも同じ

Railsは:presenceキーを PresenceValidateに変換し、現在のクラスにPresenceValidatorという名前の定数があるかどうかを調べる:

Page 35: Building Modelsvwith Active Model

validationRubyでは定数の探索はすべての先祖クラスに対して行われるので、これは動作する

Page 36: Building Modelsvwith Active Model

validationスパム判定用のabsence validatorを作ってみる(ボットにしか見えないnicknameフィールドをわざとフォームに作っておき、そこに書き込みがあればスパムとみなす)

まずはテスト

AbsenceValidatorクラスを定義 (EachValidatorを継承)

Page 37: Building Modelsvwith Active Model

validationMailForm::Baseでインクルードする

MailForm::ValidatorsはMailForm::Baseの先祖チェインに追加される

それにより、:absenceをvalidates()のキーとして与えると、AbsenceValidator定数の値が探索され、MailForm::Validatorsの中を検索、初期化が行われる(PresenceValidatorのときと同様)

あとはautoloadすればよい

Page 38: Building Modelsvwith Active Model

ActiveModel::Callbacks

最後に、deliver()メソッドにbeforeとafterを追加してみよう

まずはfixture

evaluated_callbacks()メソッドを定義

Page 39: Building Modelsvwith Active Model

ActiveModel::Callbacks

callbackが評価されていることを確認するテスト

実装 (ActiveModel::Callbacksをextendしてコールバックを定義)

Page 40: Building Modelsvwith Active Model

ActiveModel::Callbacks

これでテストは通る

Page 41: Building Modelsvwith Active Model

ActiveModel::Modelここまでは勉強のために個別に実装を行ってきたが、実はActive

Modelに準拠するのは非常に簡単:

ActiveModel::Modelをインクルードするだけでいい

これだけで、Lintテストはすべてパスする

Page 42: Building Modelsvwith Active Model

ActiveModel::Modelその実装(これまでやってきたことを集約しただけ)

Page 43: Building Modelsvwith Active Model

おまけこの章と同じコンセプトで作られたmail_form gemがあるhttps://github.com/plataformatec/mail_form

Page 44: Building Modelsvwith Active Model

おつかれさまでした