質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

コールバック

コールバックは他のコードに引数として渡されるコードのことを指します。

Active Record

Active Recordは、一つのオブジェクトに対しドメインのロジックとストレージの抽象性を結合するデザインパターンです。

Q&A

3回答

3176閲覧

Rails: モデルのインスタンスごとにコールバックオブジェクトを割り当てたい

maisumakun

総合スコア145123

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

コールバック

コールバックは他のコードに引数として渡されるコードのことを指します。

Active Record

Active Recordは、一つのオブジェクトに対しドメインのロジックとストレージの抽象性を結合するデザインパターンです。

0グッド

1クリップ

投稿2016/07/05 00:31

RailsのActiveRecordには、いろんなタイミングで差し込めるコールバック機能があります。

そして、このコールバックには、#after_save#before_validationといった、呼び出すコールバックに応じた名前のメソッドを持ったオブジェクトを作って渡すことができます。

ただ、普通にやろうとするとafter_save SomeObject.newのようにクラスマクロで書くことになるので、クラス単位で同じコールバックオブジェクトを指定することになります。

今回、「モデルのインスタンスごとにに対応したデータをコールバック側で持って、それを複数のタイミングで引き回したい」というようなことがしたくなってきたのですが、そのデータをモデル側に持たせるのも不格好だと思うので、できるなら「コールバックオブジェクトをインスタンスごとに別々に設定する」ということをしたいのですが、それは可能なのでしょうか。

もしくは、それ以外の方法で「モデルごとのデータを、共通するコールバックインスタンスの間でエレガントに引き回す」ことができれば、それでも構いません。

むろん、以下のような手法で無理やり解決しようと思えばできなくはないのですが、「レールに乗った」解決法があれば教えていただければと思います。

  • モデル側にそのデータを格納するメソッドを入れる
  • 共通のコールバックオブジェクト側で、モデルのIDなどをキーにしたハッシュを使って、データを整理して入れておく

環境

  • Rails 4.2.6(5系列への更新も検討中)
  • バックエンドデータベースはMySQL

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rifuch

2016/07/05 12:19

ちょっと具体的な例が思いつかないのですが、どのような状況かもう少し具体的に教えていただけますか?
maisumakun

2016/07/05 13:33

after_updateでレコードの変更状況を見て、メールを送信しようとしたらメーラーがコミット前のデータを拾ってしまうことがあったので、メーラー作成だけafter_updateで行って、実際のdeliver_laterをafter_commitで行いたい、というような状況です。
guest

回答3

0

「モデルのインスタンスごとにに対応したデータをコールバック側で持って、それを複数のタイミングで引き回したい」
「コールバックオブジェクトをインスタンスごとに別々に設定する」
という事をやる時点で、すでにレールから外れていくように思えますが・・・

一つのアイディアとして、コールバックオブジェクトを引き回す方法は考えつきますが、問題は、コールバックオブジェクトとモデルインスタンスをそれぞれ一意に規定できない所でしょうか。
モデルクラス側のIDが決まった後だけはひもづける、という事であれば、うまい事永続化して引き回せそうです。

Ruby

1class Hoge < ActiveRecord::Base 2 after_update Fuga.new 3 after_commit Fuga.new 4end 5 6class Fuga 7 8 def after_update(object) 9 some_obj = #メーラーオブジェクトとかを生成 10 # メーラーオブジェクトを操作 11 cash = YAML.laod_file('tmp.yml') 12 # cashの中身はハッシュだとして 13 cash[object.id] = some_obj 14 YAML.dump(cash, File.open('tmp.yml', 'w') 15 end 16 17 def after_commit(object) 18 if object.id 19 cash = YAML.laod_file('tmp.yml') 20 data = cash[object.id] 21 if data 22 # メールを送るとかの処理 23 end 24 end 25 end 26end

永続化の方法は、適当にYAMLとFileを使いましたが、memcashとかを使えばもっとましになりそうです。
ただ、またこれもレールから外れる方向に進んでいる気がしますが。

で、質問の答えとしていただいたパターンであれば、after_commitのonオプションで解決できるのでは?と思うのですが、どうでしょうか。
問題は、after_updateのコールバックでデータを送ろうとすると、コミットに失敗して保存されていないのに保存されるはずの(保存に失敗してしまった)データが送信されてしまうと言う事だと思ったのですが。

投稿2016/07/05 15:01

rifuch

総合スコア1901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

これくらいのレベルになるとここでは回答が付きにくいですね ^_^;

私もRails勉強中なので正直よくわかりませんが、 メーリングリストの過去ログ検索とかマルチポストは推奨されないかも知れませんが、スタック・オーバーフローとかでも聞いてみるといいかもしれないですね。

もちろん調べているとは思いますが、良い質問なので流れていくのがもったいなくて回答してみましたw

投稿2016/07/05 05:36

Mr_Roboto

総合スコア2208

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ご存知だと思いますが,僕でしたらvirtual_attributeなどを使って動的にインスタンスのDBの紐付かない属性にオブジェクトを持たせることで済ませてしまいますね。同じaction内で紐付けられるデータが必要なコールバックが全て行われることを前提にしていますが。

そうでなければ,そもそもステイトレスというwebの原則に反している(この原則が時代遅れという風潮もありますがrailsの設計はこの原則をおおよそ守っていると思います)処理になるので,瞬間的に作成されるオブジェクトでもDBに作成されたmailerを何らかのモデルを作ってDBに突っ込むのがレールにそったやり方かなと思います。元々の発端がDBに紐付けられるデータの節約だと思うのでこの方法は論外だと思いますが,actionをまたぐ場合は,sessionにいれるくらいしかできないと思いますね。

共通のコールバックオブジェクト側で、モデルのIDなどをキーにしたハッシュを使って、データを整理して入れておく

この方法はコールバックオブジェクトはリクエストごとに初期化されると思うので,(アクションをまたがなければ)個人的には一番きれいな設計のように思います。コールバックオブジェクトのクラス変数にhashを持たせるだけで上手くいくのではないでしょうか。インスタンスと動的なデータを対応させなければいけないとなると,インスタンスに直接紐付けるか,weak_ref_dictionaryパターンかモデルを作ってDBに入れるかcacheするくらいの方法しかないと思うので,一番簡単そうで手軽なhashで作るのがいいのではないでしょうか。

紐付けなければいけないデータが静的なデータであれば,sendなどを使って,インスタンスのデータ毎にコールバック関数内で実行させるメソッドを条件分岐させるだけで済むんですけどね・・・。

投稿2016/09/11 17:46

編集2016/09/11 17:49
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問