跳到内容 跳到搜索

属性访问器

使用类/模块和实例访问器扩展模块对象,用于类/模块属性,就像本机 attr* 访问器用于实例属性一样。

每个线程的属性访问器

使用类/模块和实例访问器扩展模块对象,用于类/模块属性,就像本机 attr* 访问器用于实例属性一样,但每个线程执行一次。

因此,这些值在模块类名称下的 Thread.current 空间中被作用域。

请注意,如果Rails.application.config.active_support.isolation_level 设置为:fiber,它也可以被每个 fiber 作用域。

命名空间
方法
A
C
D
M
R
S
T
包含的模块

属性

[R] attr_internal_naming_format

类公共方法

attr_internal_naming_format=(format)

# File activesupport/lib/active_support/core_ext/module/attr_internal.rb, line 25
    def attr_internal_naming_format=(format)
      if format.start_with?("@")
        raise ArgumentError, <<~MESSAGE.squish
          Setting `attr_internal_naming_format` with a `@` prefix is not supported.

          You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
        MESSAGE
      end

      @attr_internal_naming_format = format
    end

实例公共方法

alias_attribute(new_name, old_name)

允许您为属性创建别名,包括 getter、setter 和谓词。

class Content < ActiveRecord::Base
  # has a title attribute
end

class Email < Content
  alias_attribute :subject, :title
end

e = Email.find(1)
e.title    # => "Superstars"
e.subject  # => "Superstars"
e.subject? # => true
e.subject = "Megastars"
e.title    # => "Megastars"
# File activesupport/lib/active_support/core_ext/module/aliasing.rb, line 21
  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

anonymous?()

模块可能有或可能没有名称。

module M; end
M.name # => "M"

m = Module.new
m.name # => nil

anonymous? 方法如果模块没有名称,则返回 true,否则返回 false

Module.new.anonymous? # => true

module M; end
M.anonymous?          # => false

模块在首次分配给常量时获得名称。通过moduleclass 关键字,或通过显式分配。

m = Module.new # creates an anonymous module
m.anonymous?   # => true
M = m          # m gets a name here as a side-effect
m.name         # => "M"
m.anonymous?   # => false
# File activesupport/lib/active_support/core_ext/module/anonymous.rb, line 27
def anonymous?
  name.nil?
end

attr_internal(*attrs)

attr_internal_accessor(*attrs)

声明一个由内部命名实例变量支持的属性读取器和写入器。

也称为: attr_internal
# File activesupport/lib/active_support/core_ext/module/attr_internal.rb, line 16
def attr_internal_accessor(*attrs)
  attr_internal_reader(*attrs)
  attr_internal_writer(*attrs)
end

attr_internal_reader(*attrs)

声明一个由内部命名实例变量支持的属性读取器。

# File activesupport/lib/active_support/core_ext/module/attr_internal.rb, line 5
def attr_internal_reader(*attrs)
  attrs.each { |attr_name| attr_internal_define(attr_name, :reader) }
end

attr_internal_writer(*attrs)

声明一个由内部命名实例变量支持的属性写入器。

# File activesupport/lib/active_support/core_ext/module/attr_internal.rb, line 10
def attr_internal_writer(*attrs)
  attrs.each { |attr_name| attr_internal_define(attr_name, :writer) }
end

cattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)

cattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)

别名: mattr_reader

cattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)

别名: mattr_writer

deep_dup()

如果模块或类是匿名的,则返回该模块或类的副本。如果它已命名,则返回self

Object.deep_dup == Object # => true
klass = Class.new
klass.deep_dup == klass # => false
# File activesupport/lib/active_support/core_ext/object/deep_dup.rb, line 64
def deep_dup
  if name.nil?
    super
  else
    self
  end
end

delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)

提供一个delegate 类方法,可以轻松地将包含对象的公共方法公开为您自己的方法。

选项

  • :to - 将目标对象名称指定为符号或字符串

  • :prefix - 使用目标名称或自定义前缀为新方法添加前缀

  • :allow_nil - 如果设置为 true,则阻止引发ActiveSupport::DelegationError

  • :private - 如果设置为 true,则将方法可见性更改为私有

宏接收一个或多个方法名称(指定为符号或字符串)以及目标对象的名称,通过:to 选项(也是符号或字符串)。

委托对 Active Record 关联特别有用

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>

允许对同一目标进行多次委托

class Foo < ActiveRecord::Base
  belongs_to :greeter
  delegate :hello, :goodbye, to: :greeter
end

Foo.new.goodbye # => "goodbye"

方法可以通过提供符号来委托给实例变量、类变量或常量

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

也可以通过使用:class 将方法委托给类

class Foo
  def self.hello
    "world"
  end

  delegate :hello, to: :class
end

Foo.new.hello # => "world"

委托可以可选地使用:prefix 选项添加前缀。如果该值为true,则委托方法将以被委托对象名称为前缀。

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"

也可以提供自定义前缀。

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'

委托方法默认是公共的。传递private: true 来改变它。

class User < ActiveRecord::Base
  has_one :profile
  delegate :first_name, to: :profile
  delegate :date_of_birth, to: :profile, private: true

  def age
    Date.today.year - date_of_birth.year
  end
end

User.new.first_name # => "Tomas"
User.new.date_of_birth # => NoMethodError: private method `date_of_birth' called for #<User:0x00000008221340>
User.new.age # => 2

如果目标为nil 并且没有响应委托方法,则会引发ActiveSupport::DelegationError。如果您希望改为返回nil,请使用:allow_nil 选项。

class User < ActiveRecord::Base
  has_one :profile
  delegate :age, to: :profile
end

User.new.age
# => ActiveSupport::DelegationError: User#age delegated to profile.age, but profile is nil

但如果没有配置文件,则可以接受这种情况,不应将其视为错误条件

class User < ActiveRecord::Base
  has_one :profile
  delegate :age, to: :profile, allow_nil: true
end

User.new.age # nil

请注意,如果目标不是nil,则无论:allow_nil 选项如何,都会尝试调用,因此如果所述对象没有响应该方法,仍然会引发异常

class Foo
  def initialize(bar)
    @bar = bar
  end

  delegate :name, to: :@bar, allow_nil: true
end

Foo.new("Bar").name # raises NoMethodError: undefined method `name'

目标方法必须是公共的,否则将引发NoMethodError

# File activesupport/lib/active_support/core_ext/module/delegation.rb, line 160
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
  ::ActiveSupport::Delegation.generate(
    self,
    methods,
    location: caller_locations(1, 1).first,
    to: to,
    prefix: prefix,
    allow_nil: allow_nil,
    private: private,
  )
end

delegate_missing_to(target, allow_nil: nil)

在构建装饰器时,可能会出现常见的模式

class Partition
  def initialize(event)
    @event = event
  end

  def person
    detail.person || creator
  end

  private
    def respond_to_missing?(name, include_private = false)
      @event.respond_to?(name, include_private)
    end

    def method_missing(method, *args, &block)
      @event.send(method, *args, &block)
    end
end

使用Module#delegate_missing_to,上述内容可以简化为

class Partition
  delegate_missing_to :@event

  def initialize(event)
    @event = event
  end

  def person
    detail.person || creator
  end
end

目标可以在对象中任何可调用内容,例如实例变量、方法、常量等。

委托方法在目标上必须是公共的,否则将引发ActiveSupport::DelegationError。如果您希望改为返回nil,请使用:allow_nil 选项。

由于当调用Marshal.dump(object) 时可能存在干扰,因此marshal_dump_dump 方法不包含在委托中,因为object 的委托目标方法可能会添加或删除实例变量。

# File activesupport/lib/active_support/core_ext/module/delegation.rb, line 218
def delegate_missing_to(target, allow_nil: nil)
  ::ActiveSupport::Delegation.generate_method_missing(
    self,
    target,
    allow_nil: allow_nil,
  )
end

deprecate(*method_names, deprecator:, **options)

deprecate :foo, deprecator: MyLib.deprecator
deprecate :foo, bar: "warning!", deprecator: MyLib.deprecator

一个废弃器通常是 ActiveSupport::Deprecation 的实例,但您也可以传递任何响应deprecation_warning(deprecated_method_name, message, caller_backtrace) 的对象,您可以在其中实现自定义警告行为。

class MyLib::Deprecator
  def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
    message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
    Kernel.warn message
  end
end
# File activesupport/lib/active_support/core_ext/module/deprecation.rb, line 17
def deprecate(*method_names, deprecator:, **options)
  if deprecator.is_a?(ActiveSupport::Deprecation)
    deprecator.deprecate_methods(self, *method_names, **options)
  elsif deprecator
    # we just need any instance to call deprecate_methods, but the deprecation will be emitted by deprecator
    ActiveSupport.deprecator.deprecate_methods(self, *method_names, **options, deprecator: deprecator)
  end
end

mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)

定义类属性的类和实例访问器。所有创建的类和实例方法都将是公开的,即使此方法在私有或受保护的访问修饰符下调用。

module HairColors
  mattr_accessor :hair_colors
end

class Person
  include HairColors
end

HairColors.hair_colors = [:brown, :black, :blonde, :red]
HairColors.hair_colors # => [:brown, :black, :blonde, :red]
Person.new.hair_colors # => [:brown, :black, :blonde, :red]

如果子类更改了值,那么这也将更改父类的值。类似地,如果父类更改了值,那么也将更改子类的值。

class Citizen < Person
end

Citizen.new.hair_colors << :blue
Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]

要省略实例写入器方法,请传递 instance_writer: false。要省略实例读取器方法,请传递 instance_reader: false

module HairColors
  mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
end

class Person
  include HairColors
end

Person.new.hair_colors = [:brown]  # => NoMethodError
Person.new.hair_colors             # => NoMethodError

或者传递 instance_accessor: false,省略两个实例方法。

module HairColors
  mattr_accessor :hair_colors, instance_accessor: false
end

class Person
  include HairColors
end

Person.new.hair_colors = [:brown]  # => NoMethodError
Person.new.hair_colors             # => NoMethodError

您可以为属性设置默认值。

module HairColors
  mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red]
  mattr_accessor(:hair_styles) { [:long, :short] }
end

class Person
  include HairColors
end

Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
Person.class_variable_get("@@hair_styles") # => [:long, :short]
也别名为:cattr_accessor
# File activesupport/lib/active_support/core_ext/module/attribute_accessors.rb, line 208
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

mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)

定义一个类属性并创建类和实例读取器方法。如果未事先定义,则基础类变量将设置为 nil。所有创建的类和实例方法都将是公开的,即使此方法在私有或受保护的访问修饰符下调用。

module HairColors
  mattr_reader :hair_colors
end

HairColors.hair_colors # => nil
HairColors.class_variable_set("@@hair_colors", [:brown, :black])
HairColors.hair_colors # => [:brown, :black]

属性名称必须是 Ruby 中有效的 方法名。

module Foo
  mattr_reader :"1_Badname"
end
# => NameError: invalid attribute name: 1_Badname

要省略实例读取器方法,请传递 instance_reader: falseinstance_accessor: false

module HairColors
  mattr_reader :hair_colors, instance_reader: false
end

class Person
  include HairColors
end

Person.new.hair_colors # => NoMethodError

您可以为属性设置默认值。

module HairColors
  mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red]
  mattr_reader(:hair_styles) { [:long, :short] }
end

class Person
  include HairColors
end

Person.new.hair_colors # => [:brown, :black, :blonde, :red]
Person.new.hair_styles # => [:long, :short]
也别名为:cattr_reader
# File activesupport/lib/active_support/core_ext/module/attribute_accessors.rb, line 55
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

mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)

定义一个类属性并创建类和实例写入器方法,以允许对属性进行赋值。所有创建的类和实例方法都将是公开的,即使此方法在私有或受保护的访问修饰符下调用。

module HairColors
  mattr_writer :hair_colors
end

class Person
  include HairColors
end

HairColors.hair_colors = [:brown, :black]
Person.class_variable_get("@@hair_colors") # => [:brown, :black]
Person.new.hair_colors = [:blonde, :red]
HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]

要省略实例写入器方法,请传递 instance_writer: falseinstance_accessor: false

module HairColors
  mattr_writer :hair_colors, instance_writer: false
end

class Person
  include HairColors
end

Person.new.hair_colors = [:blonde, :red] # => NoMethodError

您可以为属性设置默认值。

module HairColors
  mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red]
  mattr_writer(:hair_styles) { [:long, :short] }
end

class Person
  include HairColors
end

Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
Person.class_variable_get("@@hair_styles") # => [:long, :short]
也别名为:cattr_writer
# File activesupport/lib/active_support/core_ext/module/attribute_accessors.rb, line 121
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

module_parent()

根据其名称返回包含此模块的模块。

module M
  module N
  end
end
X = M::N

M::N.module_parent # => M
X.module_parent    # => M

顶级模块和匿名模块的父级是 Object

M.module_parent          # => Object
Module.new.module_parent # => Object
# File activesupport/lib/active_support/core_ext/module/introspection.rb, line 34
def module_parent
  module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object
end

module_parent_name()

返回包含此模块的模块的名称。

M::N.module_parent_name # => "M"
# File activesupport/lib/active_support/core_ext/module/introspection.rb, line 9
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

module_parents()

返回此模块的所有父级(根据其名称),按从嵌套到外部的顺序排列。接收器不包含在结果中。

module M
  module N
  end
end
X = M::N

M.module_parents    # => [Object]
M::N.module_parents # => [M, Object]
X.module_parents    # => [M, Object]
# File activesupport/lib/active_support/core_ext/module/introspection.rb, line 50
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

redefine_method(method, &block)

用传递的块替换现有的方法定义(如果有)。

# File activesupport/lib/active_support/core_ext/module/redefine_method.rb, line 17
def redefine_method(method, &block)
  visibility = method_visibility(method)
  silence_redefinition_of_method(method)
  define_method(method, &block)
  send(visibility, method)
end

redefine_singleton_method(method, &block)

用传递的块替换现有的单例方法定义(如果有)。

# File activesupport/lib/active_support/core_ext/module/redefine_method.rb, line 26
def redefine_singleton_method(method, &block)
  singleton_class.redefine_method(method, &block)
end

remove_possible_method(method)

如果存在,则删除命名方法。

# File activesupport/lib/active_support/core_ext/module/remove_method.rb, line 7
def remove_possible_method(method)
  if method_defined?(method) || private_method_defined?(method)
    undef_method(method)
  end
end

remove_possible_singleton_method(method)

如果存在,则删除命名的单例方法。

# File activesupport/lib/active_support/core_ext/module/remove_method.rb, line 14
def remove_possible_singleton_method(method)
  singleton_class.remove_possible_method(method)
end

silence_redefinition_of_method(method)

如果存在,则将命名方法标记为打算重新定义。抑制 Ruby 方法重新定义警告。如果可能,请优先使用 redefine_method

# File activesupport/lib/active_support/core_ext/module/redefine_method.rb, line 7
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

thread_cattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)

thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)

定义类属性的类和实例访问器。

class Account
  thread_mattr_accessor :user
end

Account.user = "DHH"
Account.user     # => "DHH"
Account.new.user # => "DHH"

mattr_accessor 不同,值**不**与子类或父类共享。如果子类更改了值,则不会更改父类的值。如果父类更改了值,则不会更改子类的值。

class Customer < Account
end

Account.user   # => "DHH"
Customer.user  # => nil
Customer.user  = "Rafael"
Customer.user  # => "Rafael"
Account.user   # => "DHH"

要省略实例写入器方法,请传递 instance_writer: false。要省略实例读取器方法,请传递 instance_reader: false

class Current
  thread_mattr_accessor :user, instance_writer: false, instance_reader: false
end

Current.new.user = "DHH"  # => NoMethodError
Current.new.user          # => NoMethodError

或者传递 instance_accessor: false,省略两个实例方法。

class Current
  thread_mattr_accessor :user, instance_accessor: false
end

Current.new.user = "DHH"  # => NoMethodError
Current.new.user          # => NoMethodError

可以使用 :default 选项指定默认值。由于多个线程可以访问默认值,因此未冻结的默认值将被 dup 并冻结。

也别名为:thread_cattr_accessor
# File activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb, line 170
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