class Module
Public Instance Methods
Encapsulates the common pattern of:
alias_method :foo_without_feature, :foo alias_method :foo, :foo_with_feature
With this, you simply do:
alias_method_chain :foo, :feature
And both aliases are set up for you.
Query and bang methods (foo?, foo!) keep the same punctuation:
alias_method_chain :foo?, :feature
is equivalent to
alias_method :foo_without_feature?, :foo? alias_method :foo?, :foo_with_feature?
so you can safely chain foo, foo?, and foo! with the same feature.
# File lib/gorillib/deprecated/metaprogramming/aliasing.rb, line 23 def alias_method_chain(target, feature) # Strip out punctuation on predicates or bang methods since # e.g. target?_without_feature is not a valid method name. aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 yield(aliased_target, punctuation) if block_given? with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" alias_method without_method, target alias_method target, with_method case when public_method_defined?(without_method) public target when protected_method_defined?(without_method) protected target when private_method_defined?(without_method) private target end end
Lists the differences in methods between two modules/classes
Breaks them down by providing module, and shows class and instance methods. @param other [Module] other class or module to compare with @param show_common [true,false] true to show methods they have in common; false by default
@example Range
has several extra instance methods; the Foo class and its instances have methods via the Happy module
module Happy ; def hello() 3 ; end ; end class Foo ; include Enumerable ; include Happy ; extend Happy ; end { "Foo#" => { Happy => [:hello] }, "Foo." => { Happy => [:hello] }, "Range#" => { Range => [:each, :step, :begin, :end, :last, :exclude_end?, :cover?]} }
# File lib/gorillib/utils/console.rb, line 89 def compare_methods(other=Object, show_common=false) result = Hash.new{|h,k| h[k] = Hash.new{|hh,hk| hh[hk] = [] } } inst_ancestors_both = ancestors & other.ancestors klass_ancestors_both = singleton_class.ancestors & other.singleton_class.ancestors inst_meths = (self.instance_methods | other.instance_methods) klass_meths = (self.methods | other.methods) [ [:both, inst_ancestors_both, klass_ancestors_both], [self, (self.ancestors - inst_ancestors_both), (self.singleton_class.ancestors - klass_ancestors_both)], [other, (other.ancestors - inst_ancestors_both), (other.singleton_class.ancestors - klass_ancestors_both)], ].each do |mod, inst_anc, klass_anc| inst_anc.reverse.each do |ancestor| result["#{mod}#"][ancestor] = inst_meths & ancestor.instance_methods inst_meths -= ancestor.instance_methods end klass_anc.reverse.each do |ancestor| result["#{mod}."][ancestor] = klass_meths & ancestor.instance_methods klass_meths -= ancestor.instance_methods end end unless show_common then result.delete("both#") ; result.delete("both.") ; end result.each{|type,hsh| hsh.reject!{|k,v| v.empty? } } result.reject!{|type,hsh| hsh.empty? } result end
Provides a delegate class method to easily expose contained objects’ methods as your own. Pass one or more methods (specified as symbols or strings) and the name of the target object via the :to
option (also a symbol or string). At least one method and the :to
option are required.
Delegation is particularly useful with Active Record associations:
class Greeter < ActiveRecord::Base def hello "hello" end def goodbye "goodbye" end end class Foo < ActiveRecord::Base belongs_to :greeter delegate :hello, :to => :greeter end Foo.new.hello # => "hello" Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
Multiple delegates to the same target are allowed:
class Foo < ActiveRecord::Base belongs_to :greeter delegate :hello, :goodbye, :to => :greeter end Foo.new.goodbye # => "goodbye"
Methods can be delegated to instance variables, class variables, or constants by providing them as a symbols:
class Foo CONSTANT_ARRAY = [0,1,2,3] @@class_array = [4,5,6,7] def initialize @instance_array = [8,9,10,11] end delegate :sum, :to => :CONSTANT_ARRAY delegate :min, :to => :@@class_array delegate :max, :to => :@instance_array end Foo.new.sum # => 6 Foo.new.min # => 4 Foo.new.max # => 11
Delegates can optionally be prefixed using the :prefix
option. If the value is true
, the delegate methods are prefixed with the name of the object being delegated to.
Person = Struct.new(:name, :address) class Invoice < Struct.new(:client) delegate :name, :address, :to => :client, :prefix => true end john_doe = Person.new("John Doe", "Vimmersvej 13") invoice = Invoice.new(john_doe) invoice.client_name # => "John Doe" invoice.client_address # => "Vimmersvej 13"
It is also possible to supply a custom prefix.
class Invoice < Struct.new(:client) delegate :name, :address, :to => :client, :prefix => :customer end invoice = Invoice.new(john_doe) invoice.customer_name # => "John Doe" invoice.customer_address # => "Vimmersvej 13"
If the delegate object is nil
an exception is raised, and that happens no matter whether nil
responds to the delegated method. You can get a nil
instead with the :allow_nil
option.
class Foo attr_accessor :bar def initialize(bar = nil) @bar = bar end delegate :zoo, :to => :bar end Foo.new.zoo # raises NoMethodError exception (you called nil.zoo) class Foo attr_accessor :bar def initialize(bar = nil) @bar = bar end delegate :zoo, :to => :bar, :allow_nil => true end Foo.new.zoo # returns nil
# File lib/gorillib/metaprogramming/delegation.rb, line 106 def delegate(*methods) options = methods.pop unless options.is_a?(Hash) && to = options[:to] raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)." end if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method." end prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_" || '' file, line = caller.first.split(':', 2) line = line.to_i methods.each do |method| on_nil = if options[:allow_nil] 'return' else %(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") end module_eval(<<-EOS, file, line - 5) if instance_methods(false).map(&:to_s).include?("#{prefix}#{method}") remove_possible_method("#{prefix}#{method}") end def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block) rescue NoMethodError # rescue NoMethodError if #{to}.nil? # if client.nil? #{on_nil} # return # depends on :allow_nil else # else raise # raise end # end end # end EOS end end
Removes all constants in the module’s namespace – this is useful when writing specs for metaprogramming methods
# File lib/gorillib/utils/nuke_constants.rb, line 6 def nuke_constants constants.each{|const| remove_const(const) } end
# File lib/gorillib/metaprogramming/remove_method.rb, line 7 def redefine_method(method, &block) remove_possible_method(method) define_method(method, &block) end
# File lib/gorillib/metaprogramming/remove_method.rb, line 2 def remove_possible_method(method) remove_method(method) rescue NameError end
# File lib/gorillib/object/try_dup.rb, line 23 def try_dup self end