Skip to content

Instantly share code, notes, and snippets.

@chrisjpowers
Created July 14, 2009 21:30
Show Gist options
  • Save chrisjpowers/147210 to your computer and use it in GitHub Desktop.
Save chrisjpowers/147210 to your computer and use it in GitHub Desktop.
Allows you to declare alias_method_chain before either method has been defined.
# Allows you to declare an alias_method_chain before the original
# method has been defined.
#
# class A
# delayed_method_chain :name, :hello
#
# def name_with_hello
# "Hello #{name_without_hello}"
# end
#
# # This method is immediately chained when defined
# def name
# 'Bill'
# end
# end
#
# A.new.name #=> "Hello Bill"
#
module DelayedMethodChain
def delayed_method_chain(name, suffix)
# if both methods already exist, just chain and forget the method_added stuff
if method_defined?(name.to_sym) && method_defined?(DelayedMethodChain.suffixed_name(name, 'with', suffix))
alias_method DelayedMethodChain.suffixed_name(name, 'without', suffix), name.to_sym
alias_method name.to_sym, DelayedMethodChain.suffixed_name(name, 'with', suffix)
return
end
(class << self; self; end).class_eval do
@@delayed_methods ||= []
@@delayed_methods << [name, suffix]
return if method_defined?(:method_added_with_delay_chain)
def method_added_with_delay_chain(added_name)
# prevent infinite looping of method_added when aliasing method
@currently_being_delayed_methods ||= {}
return if @currently_being_delayed_methods[added_name]
# listen for both methods, only chain if both are defined
@@delayed_methods.select {|name, suffix| [name.to_sym, DelayedMethodChain.suffixed_name(name, 'with', suffix)].include?(added_name.to_sym)}.each do |name, suffix|
next unless method_defined?(name.to_sym) && method_defined?(DelayedMethodChain.suffixed_name(name, 'with', suffix))
@currently_being_delayed_methods[name] = :aliasing
alias_method DelayedMethodChain.suffixed_name(name, 'without', suffix), name.to_sym
alias_method name.to_sym, DelayedMethodChain.suffixed_name(name, 'with', suffix)
@currently_being_delayed_methods[name] = nil
end
if respond_to?(:method_added_without_delay_chain)
method_added_without_delay_chain(added_name)
end
end
# chain method_added if this class already had one
if method_defined?(:method_added)
alias_method :method_added_without_delay_chain, :method_added
end
alias_method :method_added, :method_added_with_delay_chain
end
end
# takes query, bang and setter method names into account
def self.suffixed_name(name, with, suffix)
if m = name.to_s.match(/(.*)([\?!=])$/)
"#{m[1]}_#{with}_#{suffix}#{m[2]}".to_sym
else
"#{name}_#{with}_#{suffix}".to_sym
end
end
end
Object.send :extend, DelayedMethodChain
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment