Spec-Zone.ru › Ruby on Rails 7.2

класс Module

Родитель:
Объект
Включенные модули:
Модуль::Concerning

Доступ к атрибутам

Расширяет объект модуля с доступом к атрибутам класса/модуля и экземпляра, подобно встроенным аксессорам attr* для атрибутов экземпляра.

Доступ к атрибутам на нить

Расширяет объект модуля с доступом к атрибутам класса/модуля и экземпляра, подобно встроенным аксессорам attr* для атрибутов экземпляра, но делает это на основе отдельной нити.

Значения ограничены областью действия Thread.current под именем класса модуля.

Обратите внимание, что область действия также может быть ограничена на основе волокна, если Rails.application.config.active_support.isolation_level установлено в :fiber.

Атрибуты

attr_internal_naming_format[R]

Публичные методы класса

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?("@")
        ActiveSupport.deprecator.warn <<~MESSAGE
          Setting `attr_internal_naming_format` with a `@` prefix is deprecated and will be removed in Rails 8.0.

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

        format = format.delete_prefix("@")
      end
      @attr_internal_naming_format = format
    end

Общедоступные методы экземпляров

alias_attribute(new_name, old_name) Показать исходный код
# 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

Позволяет создавать псевдонимы для атрибутов, что включает геттеры, сеттеры и предикат.

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"
anonymous?() Показать исходный код
# File activesupport/lib/active_support/core_ext/module/anonymous.rb, line 27
def anonymous?
  name.nil?
end

Модуль может или не может иметь имя.

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
attr_internal(*attrs)
Псевдоним для: attr_internal_accessor
attr_internal_accessor(*attrs) Показать исходный код
# 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
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)
Псевдоним для: mattr_accessor
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() Показать исходный код
# File activesupport/lib/active_support/core_ext/object/deep_dup.rb, line 64
def deep_dup
  if name.nil?
    super
  else
    self
  end
end

Возвращает копию модуля или класса, если он анонимный. Если он имеет имя, возвращает self.

Object.deep_dup == Object # => true
klass = Class.new
klass.deep_dup == klass # => false
Вызывает метод суперкласса Object#deep_dup
delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil) Показать исходный код
# 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, чтобы легко экспонировать публичные методы содержащихся объектов как свои собственные.

Параметры

  • :to - Указывает имя целевого объекта как символ или строку

  • :prefix - Добавляет к новому методу имя целевого или настраиваемый префикс

  • :allow_nil - Если установлено в true, предотвращает поднятие ActiveSupport::DelegationError

  • :private - Если установлено в true, меняет видимость метода на private

Макрос получает одно или несколько имён методов (указанных как символы или строки) и имя целевого объекта через параметр :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.

delegate_missing_to(target, allow_nil: nil) Показать исходный код
# 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

При построении декораторов может появиться общая модель:

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), если целевой метод делегирования object добавляет или удаляет переменные экземпляра.

deprecate(*method_names, deprecator:, **options) Показать исходный код
# 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
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
mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk) Показать исходный код
# 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

Определяет чтение и запись атрибутов класса и экземпляра. Все созданные методы класса и экземпляра будут общедоступными, даже если этот метод был вызван с модификатором доступа private или protected.

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
mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil) { |: default| ... } Показать исходный код
# 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

Определяет атрибут класса и создаёт методы чтения для класса и экземпляра. Базовая переменная класса устанавливается в nil, если она не определена ранее. Все созданные методы класса и экземпляра будут публичными, даже если этот метод вызван с модификатором доступа private или protected.

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]
Также алиас: cattr_reader
mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil) { |: default| ... } Показать исходный код
# 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

Определяет атрибут класса и создаёт методы записи для класса и экземпляра, чтобы разрешить присваивание атрибуту. Все созданные методы класса и экземпляра будут публичными, даже если этот метод вызван с модификатором доступа private или protected.

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]
Также алиас: cattr_writer
module_parent() Показать исходный код
# 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 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
module_parent_name() Показать исходный код
# 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

Возвращает имя модуля, содержащего этот модуль.

M::N.module_parent_name # => "M"
module_parents() Показать исходный код
# 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

Возвращает всех родителей этого модуля в соответствии с его именем, упорядоченных от вложенных к внешним. Сам объект не входит в результат.

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

M.module_parents    # => [Object]
M::N.module_parents # => [M, Object]
X.module_parents    # => [M, Object]
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) Показать исходный код
# 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

Помечает именованный метод как предназначенный для переопределения, если он существует. Подавляет предупреждение Ruby о переопределении метода. Предпочтительнее использовать redefine_method при возможности.

thread_cattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
Алиас для: thread_mattr_accessor
thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil) Показать исходный код
# 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

Определяет методы доступа и для класса, и для экземпляров к атрибутам класса.

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

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API