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