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) 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:
comes with Ruby 2.0 or newer instead.
NOTE: This method is deprecated. Please use Module#prepend that
def alias_method_chain(target, feature) ActiveSupport::Deprecation.warn("alias_method_chain is deprecated. Please, use Module#prepend instead. From module, you can access the original method using super.") # Strip out punctuation on predicates, bang or writer 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.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 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@/, '') # use native attr_* methods as they are faster on some Ruby implementations 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)
The target method must be public, otherwise it will raise +NoMethodError+.
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
* :allow_nil - if set to true, prevents a +NoMethodError+ from being raised
* :prefix - Prefixes the new method with the target name or a custom prefix
* :to - Specifies the target object
==== 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) unless 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 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 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) 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 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_def = [ "def #{method_prefix}#{method}(#{definition})", "_ = #{to}", "if !_.nil? || nil.respond_to?(:#{method})", " _.#{method}(#{definition})", "end", "end" ].join ';' else exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") method_def = [ "def #{method_prefix}#{method}(#{definition})", " _ = #{to}", " _.#{method}(#{definition})", "rescue NoMethodError => e", " if _.nil? && e.name == :#{method}", " #{exception}", " else", " raise", " end", "end" ].join ';' end module_eval(method_def, file, line) 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_constants #:nodoc:
def local_constants #:nodoc: ActiveSupport::Deprecation.warn(<<-MSG.squish) Module#local_constants is deprecated and will be removed in Rails 5.1. Use Module#constants(false) instead. MSG constants(false) end
def mattr_accessor(*syms, &blk)
end
include HairColors
class Person
end
end
[:brown, :black, :blonde, :red]
mattr_accessor :hair_colors do
module HairColors
Also you can pass a block to set up the attribute with a default value.
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 opt out 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 opt out of the instance reader method, pass instance_reader: false.
To opt out of the instance writer method, pass instance_writer: false.
Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
Male.new.hair_colors << :blue
end
class Male < 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
Defines both class and instance accessors for class attributes.
def mattr_accessor(*syms, &blk) mattr_reader(*syms, &blk) mattr_writer(*syms) end
def mattr_reader(*syms)
end
include HairColors
class Person
end
end
[:brown, :black, :blonde, :red]
mattr_reader :hair_colors do
module HairColors
Also, you can pass a block to set up the attribute with a default value.
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.
If you want to opt out the creation on 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
defined.
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) options = syms.extract_options! syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/ 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 class_variable_set("@@#{sym}", yield) if block_given? end end
def mattr_writer(*syms)
end
include HairColors
class Person
end
end
[:brown, :black, :blonde, :red]
mattr_writer :hair_colors do
module HairColors
Also, you can pass a block to set up the attribute with a default value.
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.
If you want to opt out 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
allow assignment to the attribute.
Defines a class attribute and creates a class and instance writer methods to
def mattr_writer(*syms) options = syms.extract_options! syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@#{sym} = nil unless defined? @@#{sym} 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 send("#{sym}=", yield) if block_given? end 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 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 @parent_name = parent_name unless frozen? parent_name 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) ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) Module#qualified_const_defined? is deprecated in favour of the builtin Module#const_defined? and will be removed in Rails 5.1. MESSAGE ActiveSupport::QualifiedConstUtils.raise_if_absolute(path) ActiveSupport::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) ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) Module#qualified_const_get is deprecated in favour of the builtin Module#const_get and will be removed in Rails 5.1. MESSAGE ActiveSupport::QualifiedConstUtils.raise_if_absolute(path) ActiveSupport::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) ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) Module#qualified_const_set is deprecated in favour of the builtin Module#const_set and will be removed in Rails 5.1. MESSAGE ActiveSupport::QualifiedConstUtils.raise_if_absolute(path) const_name = path.demodulize mod_name = path.deconstantize mod = mod_name.empty? ? self : 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)
Replaces the existing method definition, if there is one, with the passed
def redefine_method(method, &block) visibility = method_visibility(method) remove_possible_method(method) define_method(method, &block) send(visibility, method) 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.instance_eval do remove_possible_method(method) end end
def thread_mattr_accessor(*syms, &blk)
Current.new.user = "DHH" # => NoMethodError
end
mattr_accessor :user, instance_accessor: false
class Current
Or pass instance_accessor: false, to opt out 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 opt out of the instance reader method, pass instance_reader: false.
To opt out of the instance writer method, pass instance_writer: false.
Account.user # => "DHH"
Customer.user # => "Rafael"
Customer.user = "Rafael"
end
class Customer < Account
is not changed.
Similarly, if the parent class changes the value, the value of subclasses
If a subclass changes the value, the parent class' value is not changed.
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, &blk) thread_mattr_reader(*syms, &blk) thread_mattr_writer(*syms, &blk) end
def thread_mattr_reader(*syms) # :nodoc:
Current.new.user # => NoMethodError
end
thread_mattr_reader :user, instance_reader: false
class Current
instance_reader: false or instance_accessor: false.
If you want to opt out the creation on 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.
Current.user # => "DHH"
Thread.current[:attr_Current_user] = "DHH"
Current.user # => nil
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) # :nodoc: options = syms.extract_options! syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) def self.#{sym} Thread.current["attr_"+ name + "_#{sym}"] end EOS unless options[:instance_reader] == false || options[:instance_accessor] == false class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{sym} Thread.current["attr_"+ self.class.name + "_#{sym}"] end EOS end end end
def thread_mattr_writer(*syms) # :nodoc:
Current.new.user = "DHH" # => NoMethodError
end
thread_mattr_writer :user, instance_writer: false
class Current
instance_writer: false or instance_accessor: false.
If you want to opt out 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) # :nodoc: options = syms.extract_options! syms.each do |sym| raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) def self.#{sym}=(obj) Thread.current["attr_"+ name + "_#{sym}"] = obj end EOS unless options[:instance_writer] == false || options[:instance_accessor] == false class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{sym}=(obj) Thread.current["attr_"+ self.class.name + "_#{sym}"] = obj end EOS end end end