# frozen_string_literal: truemoduleTemplemoduleMixins# @api privatemoduleEngineDSLdefchain_modified!enddefappend(*args,&block)chain<<chain_element(args,block)chain_modified!enddefprepend(*args,&block)chain.unshift(chain_element(args,block))chain_modified!enddefremove(name)name=chain_name(name)raise"#{name} not found"unlesschain.reject!{|i|name===i.first}chain_modified!endaliasuseappenddefbefore(name,*args,&block)name=chain_name(name)e=chain_element(args,block)chain.map!{|f|name===f.first?[e,f]:[f]}.flatten!(1)raise"#{name} not found"unlesschain.include?(e)chain_modified!enddefafter(name,*args,&block)name=chain_name(name)e=chain_element(args,block)chain.map!{|f|name===f.first?[f,e]:[f]}.flatten!(1)raise"#{name} not found"unlesschain.include?(e)chain_modified!enddefreplace(name,*args,&block)name=chain_name(name)e=chain_element(args,block)chain.map!{|f|name===f.first?e:f}raise"#{name} not found"unlesschain.include?(e)chain_modified!end# Shortcuts to access namespaces{filter: Temple::Filters,generator: Temple::Generators,html: Temple::HTML}.eachdo|method,mod|define_method(method)do|name,*options|use(name,mod.const_get(name),*options)endendprivatedefchain_name(name)casenamewhenClassname.name.to_symwhenSymbol,Stringname.to_symwhenRegexpnameelseraise(ArgumentError,'Name argument must be Class, Symbol, String or Regexp')endenddefchain_class_constructor(filter,local_options)define_options(filter.options.valid_keys)ifrespond_to?(:define_options)&&filter.respond_to?(:options)procdo|engine|opts={}.update(engine.options)opts.delete_if{|k,v|!filter.options.valid_key?(k)}iffilter.respond_to?(:options)opts.update(local_options)iflocal_optionsfilter.new(opts)endenddefchain_proc_constructor(name,filter)raise(ArgumentError,'Proc or blocks must have arity 0 or 1')iffilter.arity>1method_name="FILTER #{name}"c=Class===self?self:singleton_classfilter=c.class_eval{define_method(method_name,&filter);instance_method(method_name)}procdo|engine|iffilter.arity==1# the proc takes one argument, e.g. use(:Filter) {|exp| exp }filter.bind(engine)elsef=filter.bind(engine).calliff.respond_to?:call# the proc returns a callable object, e.g. use(:Filter) { Filter.new }felseraise(ArgumentError,'Proc or blocks must return a Callable or a Class')unlessf.respond_to?:new# the proc returns a class, e.g. use(:Filter) { Filter }f.new(f.respond_to?(:options)?engine.options.to_hash.select{|k,v|f.options.valid_key?(k)}:engine.options)endendendenddefchain_element(args,block)name=args.shiftifClass===namefilter=namename=filter.name.to_symelseraise(ArgumentError,'Name argument must be Class or Symbol')unlessSymbol===nameendifblockraise(ArgumentError,'Class and block argument are not allowed at the same time')iffilterfilter=blockendfilter||=args.shiftcasefilterwhenProc# Proc or block argument# The proc is converted to a method of the engine class.# The proc can then access the option hash of the engine.raise(ArgumentError,'Too many arguments')unlessargs.empty?[name,chain_proc_constructor(name,filter)]whenClass# Class argument (e.g Filter class)# The options are passed to the classes constructor.raise(ArgumentError,'Too many arguments')ifargs.size>1[name,chain_class_constructor(filter,args.first)]else# Other callable argument (e.g. Object of class which implements #call or Method)# The callable has no access to the option hash of the engine.raise(ArgumentError,'Too many arguments')unlessargs.empty?raise(ArgumentError,'Class or callable argument is required')unlessfilter.respond_to?(:call)[name,proc{filter}]endendendendend