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
@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 }
people += 1
puts rule["入場料"].call { @people = people }
people += 1
puts rule["入場料"].call { @people = people }
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) }