屬性存取器
如同原生 attr* 存取器用於實例屬性一樣,擴展模組物件,使其具有用於類別/模組屬性的類別/模組和實例存取器。
每個執行緒的屬性存取器
如同原生 attr* 存取器用於實例屬性一樣,擴展模組物件,使其具有用於類別/模組屬性的類別/模組和實例存取器,但會以每個執行緒為基礎進行。
因此,這些值會在模組類別名稱下的 Thread.current 空間中進行作用域限定。
請注意,如果 Rails.application.config.active_support.isolation_level
設定為 :fiber
,則也可以針對每個纖程進行作用域限定。
- A
- C
- D
- M
- R
- S
- T
屬性
[R] | attr_internal_naming_format |
類別公開方法
attr_internal_naming_format=(format) 連結
來源:顯示 | 在 GitHub 上
# 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 和 predicate。
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"
來源:顯示 | 在 GitHub 上
# 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
當模組第一次被賦值給常數時,它會獲得一個名稱。可以透過 module
或 class
關鍵字,或者透過明確的賦值來完成。
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
來源:顯示 | 在 GitHub 上
# File activesupport/lib/active_support/core_ext/module/anonymous.rb, line 27 def anonymous? name.nil? end
attr_internal_accessor(*attrs) 連結
宣告一個由內部命名實例變數支援的屬性讀取器和寫入器。
來源:顯示 | 在 GitHub 上
# 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) 連結
宣告一個由內部命名實例變數支援的屬性讀取器。
來源:顯示 | 在 GitHub 上
# 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) 連結
宣告一個由內部命名實例變數支援的屬性寫入器。
來源:顯示 | 在 GitHub 上
# 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) 連結
cattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil) 連結
deep_dup() 連結
如果模組或類別是匿名的,則傳回其副本。如果是具名的,則傳回 self
。
Object.deep_dup == Object # => true
klass = Class.new
klass.deep_dup == klass # => false
來源:顯示 | 在 GitHub 上
# 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
。
來源:顯示 | 在 GitHub 上
# 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
和 _dump
方法不受委派影響,因為在呼叫 Marshal.dump(object)
時,如果物件的委派目標方法新增或移除實例變數,可能會造成干擾。
來源:顯示 | 在 GitHub 上
# 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
來源:顯示 | 在 GitHub 上
# 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]
來源:顯示 | 在 GitHub 上
# 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: false
或 instance_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]
來源:顯示 | 在 GitHub 上
# 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: false
或 instance_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]
來源:顯示 | 在 GitHub 上
# 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
來源:顯示 | 在 GitHub 上
# 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"
來源:顯示 | 在 GitHub 上
# 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]
來源:顯示 | 在 GitHub 上
# 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) 連結
如果存在現有的方法定義,則將其替換為傳遞的程式碼塊作為其主體。
來源:顯示 | 在 GitHub 上
# 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) 連結
如果存在現有的單例方法定義,則將其替換為傳遞的程式碼塊作為其主體。
來源:顯示 | 在 GitHub 上
# 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) 連結
如果指定的方法存在,則將其移除。
來源:顯示 | 在 GitHub 上
# 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) 連結
如果指定的單例方法存在,則將其移除。
來源:顯示 | 在 GitHub 上
# 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
。
來源:顯示 | 在 GitHub 上
# 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
製並凍結。
來源:顯示 | 在 GitHub 上
# 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