Created
July 14, 2009 21:30
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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