def define_ghost_method(matcher, &block)
raise "Must have a block" unless block_given?
raise ArgumentError, "Matcher argument must be either a 'string', :symbol, /regexp/ or proc" unless (matcher.nil? || [String, Symbol, Regexp, Proc].any?{|c| matcher.is_a?(c)})
uniq_ext = "#{self.name.gsub(/.+::/,'')}_#{matcher.class.name.gsub(/.+::/,'')}#{matcher.hash.abs.to_s}"
_ghost_method_handler = "_ghost_method_handler_#{uniq_ext}".to_sym
_ghost_method_matcher = "_ghost_method_matcher_#{uniq_ext}".to_sym
define_method(_ghost_method_handler, block)
private _ghost_method_handler
if matcher.is_a?(Proc)
define_method(_ghost_method_matcher, matcher)
private _ghost_method_matcher
end
define_chained_method(:method_missing, uniq_ext.to_sym) do |symbol, *args|
handled = case matcher
when Regexp then !(symbol.to_s =~ matcher).nil?
when String, Symbol then (symbol == matcher.to_sym)
when Proc
begin
__send__(_ghost_method_matcher, symbol)
rescue Exception => matcher_error
raise matcher_error, "#{matcher_error.message} in a ghost method matcher called for symbol :#{symbol}. Be sure to use self.class.method_defined? instead of respond_to? in a lambda matcher."
end
else nil
end
if handled
begin
__send__(_ghost_method_handler, (handled == true ? symbol : handled), *args)
rescue Exception => handler_error
raise handler_error, "#{handler_error.message} in a ghost method block called with symbol :#{symbol}."
end
else
__send__("method_missing_without_#{uniq_ext}".to_sym, symbol, *args)
end
end
define_chained_method(:respond_to?, uniq_ext.to_sym) do |method_name|
responds = case matcher
when Regexp then !(method_name.to_s =~ matcher).nil?
when String, Symbol then method_name == matcher.to_sym
when Proc
begin
__send__(_ghost_method_matcher, method_name)
rescue Exception => matcher_error
raise matcher_error, "#{matcher_error.message} in a ghost method matcher called in respond_to? for symbol :#{method_name}. Be sure to use self.class.method_defined? instead of respond_to? in a lambda matcher."
end
end
responds || __send__("respond_to_without_#{uniq_ext}?".to_sym, method_name)
end
end