# 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