トリガー可能-ActiveRecordモデルのイベント指向ロジック

現在のRailsの主な傾向の1つは、アプリケーションでのActiveRecordクラスの役割の再考です。これから、モデルは、クエリ、アソシエーション、検証、ドメインメソッド、プレゼンテーションメソッドの寄せ集めではなく、データベースの操作を担当するクラスになる必要があります。 巨大なモデルにもかかわらず、ドメインロジックの一部は依然としてアプリケーションの他の部分に移動するため、理解が非常に複雑になります。 多くのアプリケーションでは、イベントが発生するとActiveRecord :: Callbacksを使用して多くのアクションが実行されます。 このgemは、ActiveRecordモデルのビジネスルールの記述を再考する試みです。



トリガー可能なのは、高レベルのイベント駆動型ビジネスルールを記述するための宝石です。 ルールは、モデルクラスのコンテキストで宣言することも、別のファイルで削除することもできます。 現在、ライブラリには、トリガーと自動化の2種類のルールの実装が含まれています。



トリガー



トリガーは、イベント、実行条件、およびアクションを含むルールです。 たとえば、登録後に新しいユーザーにSMSを送信する必要がありますが、ユーザーが私たちからのメッセージを受信することに同意している場合に限ります。 単純なトリガーを宣言します。



User.trigger name: 'SMS to user', on: :after_create, if: { receives_sms: true } do SmsGateway.send_welcome_sms(phone_number) end
      
      





仕組み:特別なコールバックがモデルに追加され、条件が満たされると宣言されたすべてのトリガーの実行が開始されます。 アクション( doブロック)は、モデルのコンテキストで実行されます。 このルールはActiveRecord :: Callbacksに基づいて実装されているため、同じイベントリストが使用されます( before_saveafter_createなど)。 オプションのname属性はルール宣言に渡され、たとえばルールのアクションを記録するために使用できます。



DSLの制限



条件は2つの方法で定義できます。最初の方法は組み込みDSLで、この場合、 if値はハッシュです。 フィールドに制限を課すには、その名前をキーとして使用する必要があり、値は条件付きのハッシュになります。 上記の例では、短い形式の比較が使用されています-完全な形式では、条件{receives_sms:{is:true}}を使用できます。 現在、次の単純な条件が利用可能です。



種類 フルフォーム ショートフォーム
価値 {フィールド:{is ::値}} {フィールド::値}
所属 {status:{in:[:open ,: Accepted]}} {ステータス:[:オープン、:承認済み]}
否認 {フィールド:{is_not ::値}
もっと {フィールド:{greater_then ::値}
少ない {フィールド:{less_then ::値}
存在 {フィールド:{存在する:true}


さらに、 andおよびorを介した条件の組み合わせが利用可能です



 { and: [{ field1: :value1 }, { field2: :value2 }] } { or: [{ field1: :value1 }, { field2: :value2 }] }
      
      





アソシエーションチェック(現在DSLでサポートされていない)またはその他の複雑なケースを使用する必要がある場合は、2番目の方法であるラムダ条件を使用できます。 この場合、 if値はブロックですが、モデルコンテキストはブロック内に格納されます。たとえば、次のようになります。



 User.trigger on: :after_create, if: { receives_sms? && payments.count > 0 } do send_welcome_sms end
      
      







アクション



以前にdoブロックを使用しアクションを発表しましたが、同じアクションが異なるクラスのオブジェクトによって実行される場合、独自のアクションクラスを使用してコードの重複を回避できます。 そのためには、 Triggerable :: Actions :: Actionクラスから継承し、 def run_for!(Object、rule_name)メソッドのみを実装する必要があります。上記)。

SMSを送信する例に戻りましょう。 顧客はシステム( 顧客クラス)に登録でき、登録後にSMSも受信する必要があるとします。 新しいアクションクラスとトリガーを作成します。



 class SendWelcomeSms < Triggerable::Actions::Action def run_for! object, trigger_name SmsGateway.send_welcome_sms(object.phone_number) end end User.trigger on: :after_create, if: { receives_sms: true }, do: :send_welcome_sms Customer.trigger on: :after_create, if: { and: [{ receives_sms: true }, { active: true}] }, do: :send_welcome_sms
      
      







自動化



自動化-条件が満たされたときに保留中のアクションを実行します。 たとえば、すぐにではなく24時間後にユーザーにメッセージを送信する必要があるとします。 自動化は次のようになります。



 User.automation name: 'SMS to user', if: { created_at: { after: 24.hours }, receives_sms: true } do: :send_welcome_sms
      
      





トリガー宣言との違い:

1.イベントは示されていません( on

2.条件ブロックは実行時間を示します( または

3.ラムダ条件は禁止されています



仕組み:自動化が機能するためには、スケジュールされたタスクを整理するためのエンジンを接続し(例: いつでも )、自動化エンジンが起動することを確認する必要があります: Triggerable :: Engine.run_automations(interval)intervalはタスクの起動間隔 起動時に、自動化ごとに、宣言された条件に基づいてデータベースクエリが実行され(したがって、ラムダ条件は機能しません)、選択したモデルに対してアクションが実行されます。 発表されたアクションは、指定された期間の後に正確に実行されるのではなく、間隔の後に実行されます!



結論の代わりに



アプリケーションへの接続の詳細とgithubで詳細を読むことができます(ソースもご覧ください!)。 コメントで質問、批判、フィードバックを待っています。



UPD:このソリューションの可能なアプリケーションの1つは、ユーザールールを宣言するためのインターフェイスを作成することです(Zendeskなど)。つまり、可能なアクションはハードコーディングされており、ユーザーが選択した条件下でモデルでこれらのアクションを起動できます。 ActiveRecord :: Observerの使用との違いは、トリガーと同様の方法で自動化を宣言することです。



All Articles