lib/method_found/interceptor.rb in method_found-0.1.2 vs lib/method_found/interceptor.rb in method_found-0.1.3
- old
+ new
@@ -1,30 +1,84 @@
module MethodFound
+=begin
+
+Class for intercepting method calls using method_missing. Initialized by
+passing in a matcher object which can be a Regexp, a proc or lambda, or a
+string/symbol.
+
+=end
class Interceptor < Module
- attr_reader :regex
+ attr_reader :matcher
- def initialize(regex = nil, &intercept_block)
- define_method_missing(regex, &intercept_block) unless regex.nil?
+ # Creates an interceptor module to include into a class.
+ # @param (see #define_method_missing)
+ def initialize(matcher = nil, &intercept_block)
+ define_method_missing(matcher, &intercept_block) unless matcher.nil?
end
- def define_method_missing(regex, &intercept_block)
- @regex = regex
+ # Define method_missing and respond_to_missing? on this interceptor. Can be
+ # called after interceptor has been created.
+ # @param [Regexp,Proc,String,Symbol] matcher Matcher for intercepting
+ # method calls.
+ # @yield [method_name, matches, &block] Yiels method_name matched, set of
+ # matches returned from matcher, and block passed to method when called.
+ def define_method_missing(matcher, &intercept_block)
+ @matcher = matcher_ = Matcher.new(matcher)
+ assign_intercept_method(&intercept_block)
+ method_cacher = method(:cache_method)
+
define_method :method_missing do |method_name, *arguments, &method_block|
- if matches = regex.match(method_name)
- instance_exec(method_name, matches, *arguments, &intercept_block)
+ if matches = matcher_.match(method_name)
+ method_cacher.(method_name, matches)
+ send(method_name, *arguments, &method_block)
else
super(method_name, *arguments, &method_block)
end
end
define_method :respond_to_missing? do |method_name, include_private = false|
- (method_name =~ regex) || super(method_name, include_private)
+ if matches = matcher_.match(method_name)
+ method_cacher.(method_name, matches)
+ else
+ super(method_name, include_private)
+ end
end
end
def inspect
klass = self.class
name = klass.name || klass.inspect
- "#<#{name}: #{regex.inspect}>"
+ "#<#{name}: #{matcher.inspect}>"
+ end
+
+ private
+
+ def cache_method(method_name, matches)
+ intercept_method = @intercept_method
+ define_method method_name do |*arguments, &block|
+ send(intercept_method, method_name, matches, *arguments, &block)
+ end
+ end
+
+ def assign_intercept_method(&intercept_block)
+ @intercept_method ||= "__intercept_#{SecureRandom.hex}".freeze.tap do |method_name|
+ define_method method_name, &intercept_block
+ end
+ end
+
+ class Matcher < Struct.new(:matcher)
+ def match(method_name)
+ if matcher.is_a?(Regexp)
+ matcher.match(method_name)
+ elsif matcher.respond_to?(:call)
+ matcher.call(method_name) && [method_name.to_s]
+ else
+ (matcher.to_sym == method_name) && [method_name.to_s]
+ end
+ end
+
+ def inspect
+ matcher.inspect
+ end
end
end
end