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 a predicate.
Allows you to make aliases for attributes, which includes
def alias_attribute(new_name, old_name) # The following reader methods use an explicit `self` receiver in order to # support aliases that start with an uppercase letter. Otherwise, they would # be resolved as constants instead. 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 anonymous?
m.name # => "M"
M = m # m gets a name here as a side-effect
m.anonymous? # => true
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.anonymous? # => false
module M; end
Module.new.anonymous? # => true
+anonymous?+ method returns true if module does not have a name, false otherwise:
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 as_json(options = nil) # :nodoc:
def as_json(options = nil) # :nodoc: name 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).delete_prefix("@") # use native attr_* methods as they are faster on some Ruby implementations public_send("attr_#{type}", internal_name) 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, to: nil, prefix: nil, allow_nil: nil, private: nil)
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
# => Module::DelegationError: User#age delegated to profile.age, but profile is nil
User.new.age
end
delegate :age, to: :profile
has_one :profile
class User < ActiveRecord::Base
use the :allow_nil option.
+Module::DelegationError+ is raised. If you wish to instead return +nil+,
If the target is +nil+ and does not respond to the delegated method a
User.new.age # => 2
User.new.date_of_birth # => NoMethodError: private method `date_of_birth' called for #
User.new.first_name # => "Tomas"
end
end
Date.today.year - date_of_birth.year
def age
delegate :date_of_birth, to: :profile, private: true
delegate :first_name, to: :profile
has_one :profile
class User < ActiveRecord::Base
Pass private: true to change that.
The delegated methods are public by default.
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
* :private - If set to true, changes method visibility to private
from being raised
* :allow_nil - If set to true, prevents a +Module::DelegationError+
* :prefix - Prefixes the new method with the target name or a custom prefix
* :to - Specifies the target object name as a symbol or string
==== Options
public methods as your own.
Provides a +delegate+ class method to easily expose contained objects'
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil) unless to raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)." end if prefix == true && /^[^a-z_]/.match?(to) 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 location = caller_locations(1, 1).first file, line = location.path, location.lineno to = to.to_s to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to) method_def = [] method_names = [] methods.map do |method| method_name = prefix ? "#{method_prefix}#{method}" : method method_names << method_name.to_sym # Attribute writer methods only accept one argument. Makes sure []= # methods still accept two arguments. definition = /[^\]]=\z/.match?(method) ? "arg" : "..." # The following generated method calls 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 conceptually, from the user point of view, the delegator should # be doing one call. if allow_nil method = method.to_s method_def << "def #{method_name}(#{definition})" << " _ = #{to}" << " if !_.nil? || nil.respond_to?(:#{method})" << " _.#{method}(#{definition})" << " end" << "end" else method = method.to_s method_name = method_name.to_s method_def << "def #{method_name}(#{definition})" << " _ = #{to}" << " _.#{method}(#{definition})" << "rescue NoMethodError => e" << " if _.nil? && e.name == :#{method}" << %( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") << " else" << " raise" << " end" << "end" end end module_eval(method_def.join(";"), file, line) private(*method_names) if private method_names end
def delegate_missing_to(target, allow_nil: nil)
Marshal.dump(object), should the delegation target method
delegation due to possible interference when calling
The marshal_dump and _dump methods are exempt from
use the :allow_nil option.
raise +DelegationError+. If you wish to instead return +nil+,
The delegated method must be public on the target, otherwise it will
variables, methods, constants, etc.
The target can be anything callable within the object, e.g. instance
end
end
detail.person || creator
def person
end
@event = event
def initialize(event)
delegate_missing_to :@event
class Partition
With Module#delegate_missing_to, the above is condensed to:
end
end
@event.send(method, *args, &block)
def method_missing(method, *args, &block)
end
@event.respond_to?(name, include_private)
def respond_to_missing?(name, include_private = false)
private
end
detail.person || creator
def person
end
@event = event
def initialize(event)
class Partition
When building decorators, a common pattern may emerge:
def delegate_missing_to(target, allow_nil: nil) target = target.to_s target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target) module_eval <<-RUBY, __FILE__, __LINE__ + 1 def respond_to_missing?(name, include_private = false) # It may look like an oversight, but we deliberately do not pass # +include_private+, because they do not get delegated. return false if name == :marshal_dump || name == :_dump #{target}.respond_to?(name) || super end def method_missing(method, *args, &block) if #{target}.respond_to?(method) #{target}.public_send(method, *args, &block) else begin super rescue NoMethodError if #{target}.nil? if #{allow_nil == true} nil else raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil" end else raise end end end end ruby2_keywords(:method_missing) RUBY 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 mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
end
include HairColors
class Person
end
mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red]
module HairColors
You can set a default value for the attribute.
Person.new.hair_colors # => NoMethodError
Person.new.hair_colors = [:brown] # => NoMethodError
end
include HairColors
class Person
end
mattr_accessor :hair_colors, instance_accessor: false
module HairColors
Or pass instance_accessor: false, to omit both instance methods.
Person.new.hair_colors # => NoMethodError
Person.new.hair_colors = [:brown] # => NoMethodError
end
include HairColors
class Person
end
mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
module HairColors
To omit the instance reader method, pass instance_reader: false.
To omit the instance writer method, pass instance_writer: false.
Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
Citizen.new.hair_colors << :blue
end
class Citizen < Person
change the value of subclasses too.
parent class. Similarly if parent class changes the value then that would
If a subclass changes the value then that would also change the value for
Person.new.hair_colors # => [:brown, :black, :blonde, :red]
HairColors.hair_colors # => [:brown, :black, :blonde, :red]
HairColors.hair_colors = [:brown, :black, :blonde, :red]
end
include HairColors
class Person
end
mattr_accessor :hair_colors
module HairColors
this method is called with a private or protected access modifier.
All class and instance methods created will be public, even if
Defines both class and instance accessors for class attributes.
def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk) location = caller_locations(1, 1).first mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk) mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location) end
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
end
include HairColors
class Person
end
mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red]
module HairColors
You can set a default value for the attribute.
Person.new.hair_colors # => NoMethodError
end
include HairColors
class Person
end
mattr_reader :hair_colors, instance_reader: false
module HairColors
instance_reader: false or instance_accessor: false.
To omit the instance reader method, pass
# => NameError: invalid attribute name: 1_Badname
end
mattr_reader :"1_Badname"
module Foo
The attribute name must be a valid method name in Ruby.
HairColors.hair_colors # => [:brown, :black]
HairColors.class_variable_set("@@hair_colors", [:brown, :black])
HairColors.hair_colors # => nil
end
mattr_reader :hair_colors
module HairColors
this method is called with a private or protected access modifier.
defined. All class and instance methods created will be public, even if
The underlying class variable is set to +nil+, if it is not previously
Defines a class attribute and creates a class and instance reader methods.
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil) raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class? location ||= caller_locations(1, 1).first definition = [] syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym) definition << "def self.#{sym}; @@#{sym}; end" if instance_reader && instance_accessor definition << "def #{sym}; @@#{sym}; end" end sym_default_value = (block_given? && default.nil?) ? yield : default class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}") end module_eval(definition.join(";"), location.path, location.lineno) end
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
end
include HairColors
class Person
end
mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red]
module HairColors
You can set a default value for the attribute.
Person.new.hair_colors = [:blonde, :red] # => NoMethodError
end
include HairColors
class Person
end
mattr_writer :hair_colors, instance_writer: false
module HairColors
instance_writer: false or instance_accessor: false.
To omit the instance writer method, pass
HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
Person.new.hair_colors = [:blonde, :red]
Person.class_variable_get("@@hair_colors") # => [:brown, :black]
HairColors.hair_colors = [:brown, :black]
end
include HairColors
class Person
end
mattr_writer :hair_colors
module HairColors
access modifier.
will be public, even if this method is called with a private or protected
allow assignment to the attribute. All class and instance methods created
Defines a class attribute and creates a class and instance writer methods to
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil) raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class? location ||= caller_locations(1, 1).first definition = [] syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym) definition << "def self.#{sym}=(val); @@#{sym} = val; end" if instance_writer && instance_accessor definition << "def #{sym}=(val); @@#{sym} = val; end" end sym_default_value = (block_given? && default.nil?) ? yield : default class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}") end module_eval(definition.join(";"), location.path, location.lineno) end
def method_visibility(method) # :nodoc:
def method_visibility(method) # :nodoc: case when private_method_defined?(method) :private when protected_method_defined?(method) :protected else :public end end
def module_parent
M.module_parent # => Object
The parent of top-level and anonymous modules is Object.
X.module_parent # => M
M::N.module_parent # => M
X = M::N
end
end
module N
module M
Returns the module which contains this one according to its name.
def module_parent module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object end
def module_parent_name
Returns the name of the module containing this one.
def module_parent_name if defined?(@parent_name) @parent_name else parent_name = name =~ /::[^:]+\z/ ? -$` : nil @parent_name = parent_name unless frozen? parent_name end end
def module_parents
M::N.module_parents # => [M, Object]
M.module_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 module_parents parents = [] if module_parent_name parts = module_parent_name.split("::") until parts.empty? parents << ActiveSupport::Inflector.constantize(parts * "::") parts.pop end end parents << Object unless parents.include? Object parents end
def redefine_method(method, &block)
Replaces the existing method definition, if there is one, with the passed
def redefine_method(method, &block) visibility = method_visibility(method) silence_redefinition_of_method(method) define_method(method, &block) send(visibility, method) end
def redefine_singleton_method(method, &block)
Replaces the existing singleton method definition, if there is one, with
def redefine_singleton_method(method, &block) singleton_class.redefine_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
def remove_possible_singleton_method(method)
def remove_possible_singleton_method(method) singleton_class.remove_possible_method(method) end
def silence_redefinition_of_method(method)
Suppresses the Ruby method redefinition warning. Prefer
Marks the named method as intended to be redefined, if it exists.
def silence_redefinition_of_method(method) if method_defined?(method) || private_method_defined?(method) # This suppresses the "method redefined" warning; the self-alias # looks odd, but means we don't need to generate a unique name alias_method method, method end end
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
Current.new.user = "DHH" # => NoMethodError
end
thread_mattr_accessor :user, instance_accessor: false
class Current
Or pass instance_accessor: false, to omit both instance methods.
Current.new.user # => NoMethodError
Current.new.user = "DHH" # => NoMethodError
end
thread_mattr_accessor :user, instance_writer: false, instance_reader: false
class Current
To omit the instance reader method, pass instance_reader: false.
To omit the instance writer method, pass instance_writer: false.
Account.user # => "DHH"
Customer.user # => "Rafael"
Customer.user = "Rafael"
Customer.user # => nil
Account.user # => "DHH"
end
class Customer < Account
If the parent class changes the value, the value of subclasses is not changed.
If a subclass changes the value, the parent class' value is not changed.
Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
Account.new.user # => "DHH"
Account.user # => "DHH"
Account.user = "DHH"
end
thread_mattr_accessor :user
class Account
Defines both class and instance accessors for class attributes.
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil) thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default) thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor) end
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
Current.new.user # => NoMethodError
end
thread_mattr_reader :user, instance_reader: false
class Current
instance_reader: false or instance_accessor: false.
To omit the instance reader method, pass
# => NameError: invalid attribute name: 1_Badname
end
thread_mattr_reader :"1_Badname"
module Foo
The attribute name must be a valid method name in Ruby.
Thread.new { Current.user }.value # => nil
Current.user # => "DHH"
Current.user = "DHH"
end
thread_mattr_reader :user
module Current
The underlying per-thread class variable is set to +nil+, if it is not previously defined.
Defines a per-thread class attribute and creates class and instance reader methods.
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc: syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym) # The following generated method concatenates `name` because we want it # to work with inheritance via polymorphism. class_eval(<<-EOS, __FILE__, __LINE__ + 1) def self.#{sym} @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}" ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] end EOS if instance_reader && instance_accessor class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{sym} self.class.#{sym} end EOS end ::ActiveSupport::IsolatedExecutionState["attr_#{name}_#{sym}"] = default unless default.nil? end end
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
Current.new.user = "DHH" # => NoMethodError
end
thread_mattr_writer :user, instance_writer: false
class Current
instance_writer: false or instance_accessor: false.
To omit the instance writer method, pass
Thread.current[:attr_Current_user] # => "DHH"
Current.user = "DHH"
end
thread_mattr_writer :user
module Current
allow assignment to the attribute.
Defines a per-thread class attribute and creates a class and instance writer methods to
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc: syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym) # The following generated method concatenates `name` because we want it # to work with inheritance via polymorphism. class_eval(<<-EOS, __FILE__, __LINE__ + 1) def self.#{sym}=(obj) @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}" ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj end EOS if instance_writer && instance_accessor class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{sym}=(obj) self.class.#{sym} = obj end EOS end public_send("#{sym}=", default) unless default.nil? end end