drools の記法が DSL らしくて「へー、いいなー」と思ったりしたので、なんとなく書いてみた。バックトラックを考慮していない(最初の答えを見つけたら break する)うえに速度が速いわけでもないので記法の違う if 文と言われると返す言葉もない。
要は rule, when, then のブロックでルールを定義するというのをやってみたかったということで。
when と then はメソッド定義まではできるんだけど DSL 風なアクセスをうまいこと API として定義できなくて _when と _then で妥協した。残念。
class Rrools class Rule def initialize @rule = {} end def _when &condition @rule[:condition] = condition end alias on _when def _then &consequence @rule[:consequence] = consequence end alias run _then def pack context instance_eval &context @rule[:context] = self @rule end end def self.describe &block rools = self.new rools.instance_eval &block Proc.new {|&context| rools.valuate &context } end def initialize @rules = [] @description_context = Proc.new {} end def rule &rule_block rule = Rule.new rule.instance_eval &rule_block @rules << rule.pack(@description_context) end def context &context # rule の生成時に都度評価される @description_context = context end def valuate &context @rules.each do |rule| rule[:context].instance_eval &context if rule[:condition].call break rule[:consequence].call end end end end
使い方はこんなかんじ。
rule = {}
rule["入場料"] = Rrools.describe do
rule do
_when { @people > 4 }
_then { 5000 * @people }
end
rule do
_when { @people <= 4 }
_then { 6000 * @people }
end
end
people = 3
puts rule["入場料"].call { @people = people } # => 18000
people += 1
puts rule["入場料"].call { @people = people } # => 24000
people += 1
puts rule["入場料"].call { @people = people } # => 25000
# やってみる。
# http://amateras.sourceforge.jp/cgi-bin/fswiki/wiki.cgi/free?page=Drools
class User
attr_accessor :age, :name
def initialize name, age
@age = age
@name = name
end
end
is_adult = Rrools.describe do
rule do
on { @user.age >= 20 }
run { puts "#{@user.name} is an adult." }
end
rule do
on { @user.age < 20 }
run { puts "#{@user.name} isn't an adult." }
end
end
is_adult.call { @user = User.new("Bob", 21) }
is_adult.call { @user = User.new("Amy", 17) }