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) }