moduleTemplemoduleMixins# @api privatemoduleCoreDispatcherdefon_multi(*exps)multi=[:multi]exps.each{|exp|multi<<compile(exp)}multienddefon_capture(name,exp)[:capture,name,compile(exp)]endend# @api privatemoduleEscapeDispatcherdefon_escape(flag,exp)[:escape,flag,compile(exp)]endend# @api privatemoduleControlFlowDispatcherdefon_if(condition,*cases)[:if,condition,*cases.compact.map{|e|compile(e)}]enddefon_case(arg,*cases)[:case,arg,*cases.map{|condition,exp|[condition,compile(exp)]}]enddefon_block(code,content)[:block,code,compile(content)]enddefon_cond(*cases)[:cond,*cases.map{|condition,exp|[condition,compile(exp)]}]endend# @api privatemoduleCompiledDispatcherdefcall(exp)compile(exp)enddefcompile(exp)dispatcher(exp)endprivatedefdispatcher(exp)replace_dispatcher(exp)enddefreplace_dispatcher(exp)tree=DispatchNode.newdispatched_methods.eachdo|method|method.split('_')[1..-1].inject(tree){|node,type|node[type.to_sym]}.method=methodendself.class.class_eval%{
def dispatcher(exp)
if self.class == #{self.class}#{tree.compile}
else
replace_dispatcher(exp)
end
end
}dispatcher(exp)enddefdispatched_methodsre=/^on(_[a-zA-Z0-9]+)*$/self.methods.map(&:to_s).select(&re.method(:=~))end# @api privateclassDispatchNode<Hashattr_accessor:methoddefinitializesuper{|hsh,key|hsh[key]=DispatchNode.new}@method=nilenddefcompile(level=0,parent=nil)ifempty?ifmethod(' '*level)+"#{method}(*exp[#{level}..-1])"elsif!parent'exp'elseraise'Invalid dispatcher node'endelsecode=[(' '*level)+"case(exp[#{level}])"]eachdo|key,child|code<<(' '*level)+"when #{key.inspect}"code<<child.compile(level+1,parent||method)endifmethod||!parentcode<<(' '*level)+"else"ifmethodcode<<(' '*level)+" #{method}(*exp[#{level}..-1])"elsecode<<(' '*level)+" exp"endendcode<<(' '*level)+"end"code.join("\n")endendendend# @api public## Implements a compatible call-method# based on the including classe's methods.## It uses every method starting with# "on" and uses the rest of the method# name as prefix of the expression it# will receive. So, if a dispatcher# has a method named "on_x", this method# will be called with arg0,..,argN# whenever an expression like [:x, arg0,..,argN ]# is encountered.## This works with longer prefixes, too.# For example a method named "on_y_z"# will be called whenever an expression# like [:y, :z, .. ] is found. Furthermore,# if additionally a method named "on_y"# is present, it will be called when an# expression starts with :y but then does# not contain with :z. This way a# dispatcher can implement namespaces.## @note# Processing does not reach into unknown# expression types by default.## @example# class MyAwesomeDispatch# include Temple::Mixins::Dispatcher# def on_awesome(thing) # keep awesome things# return [:awesome, thing]# end# def on_boring(thing) # make boring things awesome# return [:awesome, thing+" with bacon"]# end# def on(type,*args) # unknown stuff is boring too# return [:awesome, 'just bacon']# end# end# filter = MyAwesomeDispatch.new# # Boring things are converted:# filter.call([:boring, 'egg']) #=> [:awesome, 'egg with bacon']# # Unknown things too:# filter.call([:foo]) #=> [:awesome, 'just bacon']# # Known but not boring things won't be touched:# filter.call([:awesome, 'chuck norris']) #=>[:awesome, 'chuck norris']#moduleDispatcherincludeCompiledDispatcherincludeCoreDispatcherincludeEscapeDispatcherincludeControlFlowDispatcherendendend