class Module
def alias_attribute(new_name, old_name)
e.subject = "Megastars"
e.subject? # => true
e.subject # => "Superstars"
e.title # => "Superstars"
e = Email.find(1)
end
alias_attribute :subject, :title
class Email < Content
end
# has a title attribute
class Content < ActiveRecord::Base
getter, setter, and query methods.
Allows you to make aliases for attributes, which includes
def alias_attribute(new_name, old_name) module_eval <<-STR, __FILE__, __LINE__ + 1 def #{new_name}; self.#{old_name}; end # def subject; self.title; end def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end STR end
def alias_method_chain(target, feature)
alias_method :foo?, :foo_with_feature?
alias_method :foo_without_feature?, :foo?
is equivalent to
alias_method_chain :foo?, :feature
Query and bang methods (foo?, foo!) keep the same punctuation:
And both aliases are set up for you.
alias_method_chain :foo, :feature
With this, you simply do:
alias_method :foo, :foo_with_feature
alias_method :foo_without_feature, :foo
Encapsulates the common pattern of:
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 = "#{aliased_target}_with_#{feature}#{punctuation}" without_method = "#{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
def anonymous?
M = m # => m gets a name here as a side-effect
m = Module.new # creates an anonymous module
via the +module+ or +class+ keyword or by an explicit assignment:
A module gets a name when it is first assigned to a constant. Either
m.name # => nil
m = Module.new
M.name # => "M"
module M; end
A module may or may not have a name.
def anonymous? name.nil? end
def attr_internal_accessor(*attrs)
Declares an attribute reader and writer backed by an internally-named instance
def attr_internal_accessor(*attrs) attr_internal_reader(*attrs) attr_internal_writer(*attrs) end
def attr_internal_define(attr_name, type)
def attr_internal_define(attr_name, type) internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '') class_eval do # class_eval is necessary on 1.9 or else the methods a made private # use native attr_* methods as they are faster on some Ruby implementations send("attr_#{type}", internal_name) end attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer alias_method attr_name, internal_name remove_method internal_name end
def attr_internal_ivar_name(attr)
def attr_internal_ivar_name(attr) Module.attr_internal_naming_format % attr end
def attr_internal_reader(*attrs)
def attr_internal_reader(*attrs) attrs.each {|attr_name| attr_internal_define(attr_name, :reader)} end
def attr_internal_writer(*attrs)
def attr_internal_writer(*attrs) attrs.each {|attr_name| attr_internal_define(attr_name, :writer)} end
def delegate(*methods)
Foo.new("Bar").name # raises NoMethodError: undefined method `name'
end
delegate :name, to: :@bar, allow_nil: true
end
@bar = bar
def initialize(bar)
class Foo
does not respond to the method:
:allow_nil option, and thus an exception is still raised if said object
Note that if the target is not +nil+ then the call is attempted regardless of the
User.new.age # nil
end
delegate :age, to: :profile, allow_nil: true
has_one :profile
class User < ActiveRecord::Base
condition:
But if not having a profile yet is fine and should not be an error
User.new.age # raises NoMethodError: undefined method `age'
end
delegate :age, to: :profile
has_one :profile
class User < ActiveRecord::Base
does not respond to the delegated method, +nil+ is returned.
responds to the method, everything works as usual. But if it is +nil+ and
:allow_nil option: If the target is not +nil+, or it is and
makes sense to be robust to that situation and that is the purpose of the
+NoMethodError+ is raised, as with any other value. Sometimes, however, it
If the target is +nil+ and does not respond to the delegated method a
invoice.customer_address # => 'Vimmersvej 13'
invoice.customer_name # => 'John Doe'
invoice = Invoice.new(john_doe)
end
delegate :name, :address, to: :client, prefix: :customer
class Invoice < Struct.new(:client)
It is also possible to supply a custom prefix.
invoice.client_address # => "Vimmersvej 13"
invoice.client_name # => "John Doe"
invoice = Invoice.new(john_doe)
john_doe = Person.new('John Doe', 'Vimmersvej 13')
end
delegate :name, :address, to: :client, prefix: true
class Invoice < Struct.new(:client)
Person = Struct.new(:name, :address)
delegated to.
is true, the delegate methods are prefixed with the name of the object being
Delegates can optionally be prefixed using the :prefix option. If the value
Foo.new.hello # => "world"
end
delegate :hello, to: :class
end
"world"
def self.hello
class Foo
It's also possible to delegate a method to the class by using +:class+:
Foo.new.max # => 11
Foo.new.min # => 4
Foo.new.sum # => 6
end
delegate :max, to: :@instance_array
delegate :min, to: :@@class_array
delegate :sum, to: :CONSTANT_ARRAY
end
@instance_array = [8,9,10,11]
def initialize
@@class_array = [4,5,6,7]
CONSTANT_ARRAY = [0,1,2,3]
class Foo
by providing them as a symbols:
Methods can be delegated to instance variables, class variables, or constants
Foo.new.goodbye # => "goodbye"
end
delegate :hello, :goodbye, to: :greeter
belongs_to :greeter
class Foo < ActiveRecord::Base
Multiple delegates to the same target are allowed:
Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #
Foo.new.hello # => "hello"
end
delegate :hello, to: :greeter
belongs_to :greeter
class Foo < ActiveRecord::Base
end
end
'goodbye'
def goodbye
end
'hello'
def hello
class Greeter < ActiveRecord::Base
Delegation is particularly useful with Active Record associations:
(also a symbol or string).
strings) and the name of the target object via the :to option
The macro receives one or more method names (specified as symbols or
public methods as your own.
Provides a +delegate+ class method to easily expose contained objects'
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 prefix, allow_nil = options.values_at(:prefix, :allow_nil) if prefix == true && to =~ /^[^a-z_]/ raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.' end method_prefix = \ if prefix "#{prefix == true ? to : prefix}_" else '' end file, line = caller.first.split(':', 2) line = line.to_i to = to.to_s to = 'self.class' if to == 'class' methods.each do |method| # Attribute writer methods only accept one argument. Makes sure []= # methods still accept two arguments. definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block' # The following generated methods call the target exactly once, storing # the returned value in a dummy variable. # # Reason is twofold: On one hand doing less calls is in general better. # On the other hand it could be that the target has side-effects, # whereas conceptualy, from the user point of view, the delegator should # be doing one call. if allow_nil module_eval(<<-EOS, file, line - 3) def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block) _ = #{to} # _ = client if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name) _.#{method}(#{definition}) # _.name(*args, &block) end # end end # end EOS else exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") module_eval(<<-EOS, file, line - 2) def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block) _ = #{to} # _ = client _.#{method}(#{definition}) # _.name(*args, &block) rescue NoMethodError => e # rescue NoMethodError => e location = "%s:%d:in `%s'" % [__FILE__, __LINE__ - 2, '#{method_prefix}#{method}'] # location = "%s:%d:in `%s'" % [__FILE__, __LINE__ - 2, 'customer_name'] if _.nil? && e.backtrace.first == location # if _.nil? && e.backtrace.first == location #{exception} # # add helpful message to the exception else # else raise # raise end # end end # end EOS end end end
def deprecate(*method_names)
end
Kernel.warn message
message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
class MyLib::Deprecator
method where you can implement your custom warning behavior.
\Custom deprecators must respond to deprecation_warning(deprecated_method_name, message, caller_backtrace)
deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
deprecate :foo, deprecator: MyLib::Deprecator.new
You can also use custom deprecator instance:
deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
deprecate bar: 'message'
deprecate :foo
def deprecate(*method_names) ActiveSupport::Deprecation.deprecate_methods(self, *method_names) end
def local_constant_names
This method is useful for forward compatibility, since Ruby 1.8 returns
M.local_constant_names # => ["X"]
end
X = 1
module M
Returns the names of the constants defined locally as strings.
*DEPRECATED*: Use +local_constants+ instead.
def local_constant_names ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead' local_constants.map { |c| c.to_s } end
def local_constants #:nodoc:
def local_constants #:nodoc: constants(false) end
def mattr_accessor(*syms)
To opt out of the instance reader method, pass instance_reader: false.
To opt out of the instance writer method, pass instance_writer: false.
AppConfiguration.google_api_key # => "overriding the api key!"
AppConfiguration.google_api_key = "overriding the api key!"
AppConfiguration.google_api_key # => "123456789"
end
self.google_api_key = "123456789"
mattr_accessor :google_api_key
module AppConfiguration
just like the native attr* accessors for instance attributes.
Extends the module object with module and instance accessors for class attributes,
def mattr_accessor(*syms) mattr_reader(*syms) mattr_writer(*syms) end
def mattr_reader(*syms)
def mattr_reader(*syms) options = syms.extract_options! syms.each do |sym| raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@#{sym} = nil unless defined? @@#{sym} def self.#{sym} @@#{sym} end EOS unless options[:instance_reader] == false || options[:instance_accessor] == false class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{sym} @@#{sym} end EOS end end end
def mattr_writer(*syms)
def mattr_writer(*syms) options = syms.extract_options! syms.each do |sym| raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) def self.#{sym}=(obj) @@#{sym} = obj end EOS unless options[:instance_writer] == false || options[:instance_accessor] == false class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{sym}=(obj) @@#{sym} = obj end EOS end end end
def parent
M.parent # => Object
The parent of top-level and anonymous modules is Object.
X.parent # => M
M::N.parent # => M
X = M::N
end
end
module N
module M
Returns the module which contains this one according to its name.
def parent parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object end
def parent_name
Returns the name of the module containing this one.
def parent_name if defined? @parent_name @parent_name else @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil end end
def parents
M::N.parents # => [M, Object]
M.parents # => [Object]
X = M::N
end
end
module N
module M
nested outwards. The receiver is not contained within the result.
Returns all the parents of this module according to its name, ordered from
def parents parents = [] if parent_name parts = parent_name.split('::') until parts.empty? parents << ActiveSupport::Inflector.constantize(parts * '::') parts.pop end end parents << Object unless parents.include? Object parents end
def qualified_const_defined?(path, search_parents=true)
def qualified_const_defined?(path, search_parents=true) QualifiedConstUtils.raise_if_absolute(path) QualifiedConstUtils.names(path).inject(self) do |mod, name| return unless mod.const_defined?(name, search_parents) mod.const_get(name) end return true end
def qualified_const_get(path)
def qualified_const_get(path) QualifiedConstUtils.raise_if_absolute(path) QualifiedConstUtils.names(path).inject(self) do |mod, name| mod.const_get(name) end end
def qualified_const_set(path, value)
def qualified_const_set(path, value) QualifiedConstUtils.raise_if_absolute(path) const_name = path.demodulize mod_name = path.deconstantize mod = mod_name.empty? ? self : qualified_const_get(mod_name) mod.const_set(const_name, value) end
def reachable? #:nodoc:
def reachable? #:nodoc: !anonymous? && name.safe_constantize.equal?(self) end
def redefine_method(method, &block)
def redefine_method(method, &block) remove_possible_method(method) define_method(method, &block) end
def remove_possible_method(method)
def remove_possible_method(method) if method_defined?(method) || private_method_defined?(method) undef_method(method) end end