rein - Rule Engine IN ruby

reinとは

reinはRubyで書かれた簡易ルールエンジンです。 メール振り分けやデータ検証など、アプリケーションの動作をルールで制御することができます。

使い方

ルールファイルにルールを書いておき、 RuleEngine#fire() でルールエンジンを始動します。 引数にはルールを適用するオブジェクトを渡します。

require 'rein'
engine = Rein::RuleEngine.new "rules.yaml"
engine.fire obj

ルールファイル

ルールファイルはYAMLで記述します。 rules にルールを並べて記述し、個々のルールに name(ルール名), condition(条件), action(アクション) の項目を設定します。

以下がルールファイルの例です。

rules:
  -
    name:      MailingList
    condition: from = "mlname@xxx.com"
    action:    move "MailBox"

  -
    name:      DearFriends
    condition: (from = "john@xxx.net") or (from = "paul@xxx.net")
    action:    move "DearFriends"

ルール

ルールは「条件」と「アクション」から構成されます。 オブジェクトが条件にマッチすると、そのオブジェクトに対してアクションが実行されます。

条件

条件は「左辺、オペレータ、右辺」で記述します。 複数の条件式をつなげるにはand/orを、否定の条件式にするにはnotを条件式の前に記述します。論理演算子を使うときは、条件式をかっこで囲んでください。 以下は条件式の例です。

name =‘MyName’
register_date > '2000/1/1’
(title =~ /R*/) and (name =~ /O*/)
not (time < 20)

"name"や"register_date"などのクォートで囲まれていない文字列は、評価するオブジェクトへのメッセージになります。 例えば上の条件をそれぞれRubyのコードで表すと以下のようになります。

object.name == 'MyName'
object.register_date > '2000/1/1'
(object.title =~ /R*/) and (object.name =~ /O*/)
not (object.time < 20)

オペレータ

デフォルトでは「=, !=, >, <, >=, <=, =~」のオペレータを使用できます。 これ以外のオペレータを定義するには、Qualifier.set_operator()で 「オペレータ、(オペレータを評価する)メソッド、オブジェクト」を指定します。

文字列の先頭が右辺の値で始まっているかを評価するオペレータ "start" を定義してみましょう。 まず、オペレータを評価するクラスとメソッドを定義します。

class StartOperatorSupport
  def self.start( left, right )
    left =~ /\A#{right}/
  end
end

続いて、オペレータと評価するクラス(もしくはオブジェクト)を指定します。

Qualifier.set_operator('start', :start, StartOperatorSupport)

アクション

オブジェクトが条件にマッチしたとき、アクションとして指定した処理が実行されます。 アクションは複数指定することができます。

現在の実装では、アクションはそのままRubyのコードとして instance_eval() で解釈されます。

競合の解決

条件にマッチしたルールが複数ある場合、そのうちの1つのみルールが適用されます。 現在のバージョンでは、ルールの競合を解決する方法として優先順位を設定することができます。

優先順位は各ルールの priority に0から1の範囲で設定します。 ルールが競合する場合、優先順位が1に近いルールが適用されます。 もし優先順位も同じときはルールファイル内での定義がより先頭に近いものが適用されます。