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