lib/ruby_ext/more/callbacks.rb in ruby_ext-0.5.1 vs lib/ruby_ext/more/callbacks.rb in ruby_ext-0.5.2

- old
+ new

@@ -1,172 +1,212 @@ module RubyExt::Callbacks class AbstractCallback - attr_accessor :terminator - attr_reader :executor - def executor= executor + def executor= executor @executor = executor.must_be.a Symbol, Proc end - + attr_reader :conditions def conditions= conditions @conditions = {} conditions.each do |k, v| - @conditions[k.to_sym] = if v.is_a? Symbol - v.to_s + @conditions[k] = if v.is_a? Symbol + v elsif v.is_a? Array - v.collect{|e| e.to_s} + raise "method names must be symbols (#{v})!" unless v.all?{|e| e.is_a? Symbol} + v else v end end @conditions end - - def terminate? target, result - unless terminator.nil? - if terminator.is_a? Proc - terminator.call target, result - else - result == terminator - end - else - false - end - end - - def run? target, inf + + def run? target, data if cond = conditions[:if] - evaluate_if(cond, target, inf) + evaluate_if(cond, target, data) elsif cond = conditions[:unless] - !evaluate_if(cond, target, inf) + !evaluate_if(cond, target, data) elsif cond = conditions[:only] - evaluate_only(cond, inf) + evaluate_only(cond, data) elsif cond = conditions[:except] - !evaluate_only(cond, inf) + !evaluate_only(cond, data) else true end end - - def add_to_chain target, inf, &the_next - if run? target, inf - build_block target, &the_next - else - the_next - end - end - + alias_method :deep_clone, :clone - + protected - def evaluate_if cond, target, inf - if cond.is_a? String + def evaluate_if cond, target, data + if cond.is_a? Symbol target.send cond elsif cond.is_a? Proc - cond.call target, inf + cond.call target, data else must_be.never_called end end - - def evaluate_only cond, inf - method = inf[:method].to_s - if cond.is_a? String + + def evaluate_only cond, data + method = data[:method] + method.must_be.a Symbol if method + if cond.is_a? Symbol cond == method elsif cond.is_a? Array cond.include? method + else + must_be.never_called end end end - class BeforeCallback < AbstractCallback - def build_block target, &the_next - lambda do - result = if executor.is_a? Symbol - target.send executor - elsif executor.is_a? Proc - executor.call target - else - must_be.never_called + class BeforeCallback < AbstractCallback + attr_accessor :terminator + + def build_block target, data, &block + -> do + if run? target, data + block.call if run target, data + else + block.call end + end + end - the_next.call unless terminate? target, result + def run target, data + callback_result = if executor.is_a? Symbol + target.send executor + elsif executor.is_a? Proc + executor.call target + else + must_be.never_called end + + !terminate?(target, callback_result) end + + protected + def terminate? target, result + unless terminator.nil? + if terminator.is_a? Proc + terminator.call target, result + else + result == terminator + end + else + false + end + end end - class AroundCallback < AbstractCallback - def build_block target, &the_next - lambda do - if executor.is_a? Symbol - target.send executor, &the_next - elsif executor.is_a? Proc - executor.call target, the_next + class AfterCallback < AbstractCallback + def build_block target, data, &block + -> do + if run? target, data + result = block.call + run target, data + result else - must_be.never_called + block.call end - end + end end + + def run target, data + if executor.is_a? Symbol + target.send executor + elsif executor.is_a? Proc + executor.call target + else + must_be.never_called + end + end end - class AfterCallback < AbstractCallback - def build_block target, &the_next - lambda do - result = if executor.is_a? Symbol - target.send executor - elsif executor.is_a? Proc - executor.call target + class AroundCallback < AbstractCallback + def build_block target, data, &block + -> do + if run? target, data + run target, data, &block else - must_be.never_called + block.call end - - the_next.call unless terminate? target, result - end + end end + + def run target, data, &block + if executor.is_a? Symbol + target.send executor, &block + elsif executor.is_a? Proc + executor.call target, block + else + must_be.never_called + end + end end - def run_callbacks callback_name, additional_information = {}, &block - callback_name = callback_name.to_s - block.must_be.defined - - callbacks = self.class.callbacks[callback_name] - chain_head = block - if callbacks and !callbacks.empty? - callbacks.reverse_each do |callback| - block = callback.add_to_chain self, additional_information, &chain_head - chain_head = block if block + def run_before_callbacks callback_name, data = {} + callback_name.must_be.a Symbol + self.class.callbacks[callback_name].try :each do |callback| + if callback.is_a? BeforeCallback + return false unless callback.run self, data end end - chain_head.call + true end + def run_after_callbacks callback_name, data = {} + callback_name.must_be.a Symbol + self.class.callbacks[callback_name].try :each do |callback| + callback.run self, data if callback.is_a? AfterCallback + end + end + + def run_callbacks callback_name, data = {}, &block + callback_name.must_be.a Symbol + if callbacks = self.class.callbacks[callback_name] + chain = block || -> {} + chain = callbacks.reverse.reduce chain do |chain, callback| + callback.build_block self, data, &chain + end + chain.call + else + block.call if block + end + end + module ClassMethods inheritable_accessor :callbacks, {} - - def set_callback callback_name, type, *executor_or_options, &block + + def set_callback callback_name, type, *executor_or_options, &block + callback_name.must_be.a Symbol + type.must_be.a Symbol + # parsing arguments - type, callback_name = type.to_s, callback_name.to_s - opt = executor_or_options.extract_options! + opt = executor_or_options.extract_options! "You can't provide both method name and block for filter!" if block and !executor_or_options.empty? executor = block || executor_or_options.first - - type.must_be.in %w{before around after} + + type.must_be.in [:before, :around, :after] executor.must_be.defined - + # creating callback + callback = AbstractCallback.new callback = case type - when 'before' then BeforeCallback.new - when 'around' then AroundCallback.new - when 'after' then AfterCallback.new - end + when :before then BeforeCallback.new + when :around then AroundCallback.new + when :after then AfterCallback.new + end callback.executor = executor - callback.terminator = opt.delete :terminator + callback.terminator = opt.delete :terminator if type == :before callback.conditions = opt - - callbacks[callback_name] ||= [] - callbacks[callback_name] << callback + + (self.callbacks[callback_name] ||= []) << callback + # callbacks.send(type) << callback + # (callbacks[callback_name][type] ||= []) << callback end - + end end \ No newline at end of file