module ActiveRecord::Core
Ядро Active Record
Атрибуты
Открытые методы класса
# File activerecord/lib/active_record/core.rb, line 118 class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
Задает атрибуты, которые будут включены в вывод метода inspect:
Post.attributes_for_inspect = [:id, :title] Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
Если задано значение :all, метод inspect выведет список всех атрибутов записи:
Post.attributes_for_inspect = :all Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
# File activerecord/lib/active_record/core.rb, line 76 def self.configurations @@configurations end
Возвращает полностью разрешенный объект ActiveRecord::DatabaseConfigurations.
# File activerecord/lib/active_record/core.rb, line 70 def self.configurations=(config) @@configurations = ActiveRecord::DatabaseConfigurations.new(config) end
Содержит конфигурацию базы данных — обычно она хранится в config/database.yml — в виде объекта ActiveRecord::DatabaseConfigurations.
Например, следующий файл database.yml…
development: adapter: sqlite3 database: storage/development.sqlite3 production: adapter: sqlite3 database: storage/production.sqlite3
…приведет к тому, что ActiveRecord::Base.configurations будет выглядеть так:
#<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
#<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
@name="primary", @config={adapter: "sqlite3", database: "storage/development.sqlite3"}>,
#<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
@name="primary", @config={adapter: "sqlite3", database: "storage/production.sqlite3"}>
]> # File activerecord/lib/active_record/core.rb, line 132 def self.connection_handler ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] || default_connection_handler end
# File activerecord/lib/active_record/core.rb, line 136 def self.connection_handler=(handler) ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler end
# File activerecord/lib/active_record/core.rb, line 195
def self.current_preventing_writes
connected_to_stack.reverse_each do |hash|
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
end
false
end Возвращает символ, обозначающий текущую настройку запрета записи.
ActiveRecord::Base.connected_to(role: :reading) do ActiveRecord::Base.current_preventing_writes #=> true end ActiveRecord::Base.connected_to(role: :writing) do ActiveRecord::Base.current_preventing_writes #=> false end
# File activerecord/lib/active_record/core.rb, line 158
def self.current_role
connected_to_stack.reverse_each do |hash|
return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
end
default_role
end Возвращает символ, обозначающий текущую роль подключения.
ActiveRecord::Base.connected_to(role: :writing) do ActiveRecord::Base.current_role #=> :writing end ActiveRecord::Base.connected_to(role: :reading) do ActiveRecord::Base.current_role #=> :reading end
# File activerecord/lib/active_record/core.rb, line 176
def self.current_shard
connected_to_stack.reverse_each do |hash|
return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_class_for_self)
end
default_shard
end Возвращает символ, обозначающий текущий подключенный шард.
ActiveRecord::Base.connected_to(role: :reading) do ActiveRecord::Base.current_shard #=> :default end ActiveRecord::Base.connected_to(role: :writing, shard: :one) do ActiveRecord::Base.current_shard #=> :one end
# File activerecord/lib/active_record/core.rb, line 46 class_attribute :destroy_association_async_batch_size, instance_writer: false, instance_predicate: false, default: nil
Задает максимальное число записей, удаляемых одним фоновым заданием при использовании параметра ассоциации dependent: :destroy_async. Если значение равно nil (по умолчанию), все зависимые записи будут удалены одним фоновым заданием. Если значение задано, записи для удаления будут распределены по нескольким фоновым заданиям.
# File activerecord/lib/active_record/core.rb, line 26
def self.destroy_association_async_job
if _destroy_association_async_job.is_a?(String)
self._destroy_association_async_job = _destroy_association_async_job.constantize
end
_destroy_association_async_job
rescue NameError => error
raise NameError, "Unable to load destroy_association_async_job: #{error.message}"
end Класс задания, используемый для фонового удаления ассоциаций.
# File activerecord/lib/active_record/core.rb, line 86 class_attribute :enumerate_columns_in_select_statements, instance_accessor: false, default: false
Принудительно перечисляет все столбцы в операторах SELECT. Например, SELECT first_name, last_name FROM ... вместо SELECT * FROM .... Это позволяет избежать ошибок PreparedStatementCacheExpired, если во время работы приложения в базу данных добавляется столбец.
# File activerecord/lib/active_record/core.rb, line 21 class_attribute :logger, instance_writer: false
Принимает регистратор, соответствующий интерфейсу Log4r или стандартному классу Ruby Logger, и передает его всем новым подключениям к базе данных. Получить этот регистратор можно, вызвав logger у класса модели Active Record или ее экземпляра.
# File activerecord/lib/active_record/core.rb, line 472 def initialize(attributes = nil) @new_record = true @attributes = self.class._default_attributes.deep_dup init_internals initialize_internals_callback super yield self if block_given? _run_initialize_callbacks end
Новые объекты можно создавать пустыми (не передавая параметров конструктору) или с предварительно заданными атрибутами, но еще не сохраненными (передав хеш, имена ключей которого соответствуют именам столбцов связанной таблицы). В обоих случаях допустимые ключи атрибутов определяются именами столбцов связанной таблицы — поэтому атрибуты, отсутствующие среди столбцов таблицы, задать нельзя.
Пример
# Instantiates a single new object User.new(first_name: 'Jamie')
Открытые методы экземпляра
# File activerecord/lib/active_record/core.rb, line 666
def <=>(other_object)
if other_object.is_a?(self.class)
to_key <=> other_object.to_key
else
super
end
end Позволяет сортировать объекты
# File activerecord/lib/active_record/core.rb, line 632
def ==(comparison_object)
super ||
comparison_object.instance_of?(self.class) &&
primary_key_values_present? &&
comparison_object.id == id
end Возвращает true, если comparison_object является тем же самым объектом или если comparison_object имеет тот же тип, а self имеет идентификатор, равный comparison_object.id.
Обратите внимание: по определению новые записи отличаются от любых других записей, кроме случая, когда другая запись — это сам объект-получатель. Кроме того, если вы загружаете существующие записи с помощью select и не указываете идентификатор, действовать придется на свой страх и риск: этот предикат вернет false.
Также обратите внимание, что при удалении записи ее идентификатор сохраняется в экземпляре модели, поэтому удаленные модели по-прежнему можно сравнивать.
# File activerecord/lib/active_record/core.rb, line 524
Работает так же, как метод clone в Ruby. Это «поверхностная» копия. Обратите внимание: атрибуты не копируются. Это значит, что изменение атрибутов клона изменит и оригинал, поскольку оба объекта будут ссылаться на один и тот же хеш атрибутов. Если вам нужна копия хеша атрибутов, используйте метод dup.
user = User.first new_user = user.clone user.name # => "Bob" new_user.name = "Joe" user.name # => "Joe" user.object_id == new_user.object_id # => false user.name.object_id == new_user.name.object_id # => true user.name.object_id == user.dup.name.object_id # => false
# File activerecord/lib/active_record/core.rb, line 771 def connection_handler self.class.connection_handler end
# File activerecord/lib/active_record/core.rb, line 541
У дублированных объектов нет назначенного идентификатора, и они считаются новыми записями. Обратите внимание: это «поверхностная» копия — копируются только атрибуты объекта, но не его ассоциации. Глубина «глубокого» копирования зависит от приложения, поэтому его реализация остается на усмотрение приложения и определяется его потребностями. Метод dup не сохраняет временные метки (created|updated)_(at|on) и столбец блокировки.
# File activerecord/lib/active_record/core.rb, line 588 def encode_with(coder) self.class.yaml_encoder.encode(@attributes, coder) coder["new_record"] = new_record? coder["active_record_yaml_version"] = 2 end
Заполняет coder атрибутами этой записи, которые следует сериализовать. Структура coder, определенная в этом методе, гарантированно соответствует структуре coder, передаваемого методу init_with.
Пример:
class Post < ActiveRecord::Base
end
coder = {}
Post.new.encode_with(coder)
coder # => {"attributes" => {"id" => nil, ... }}
# File activerecord/lib/active_record/core.rb, line 655 def freeze @attributes = @attributes.clone.freeze self end
Клонирует и замораживает хеш атрибутов, чтобы ассоциации оставались доступными даже у удаленных записей, но клонированные модели не замораживались.
# File activerecord/lib/active_record/core.rb, line 661 def frozen? @attributes.frozen? end
Возвращает true, если хеш атрибутов заморожен.
# File activerecord/lib/active_record/core.rb, line 795 def full_inspect inspect_with_attributes(all_attributes_for_inspect) end
Возвращает все атрибуты записи в виде аккуратно отформатированной строки, игнорируя .attributes_for_inspect.
Post.first.full_inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
# File activerecord/lib/active_record/core.rb, line 642
def hash
id = self.id
if self.class.composite_primary_key? ? primary_key_values_present? : id
self.class.hash ^ id.hash
else
super
end
end Перенаправляет вызов к идентификатору, чтобы две записи одного типа с одинаковым идентификатором могли использоваться, например, так:
[ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
# File activerecord/lib/active_record/core.rb, line 499 def init_with(coder, &block) coder = LegacyYamlAdapter.convert(coder) attributes = self.class.yaml_encoder.decode(coder) init_with_attributes(attributes, coder["new_record"], &block) end
Инициализирует пустой объект модели из coder. coder должен быть результатом предварительной сериализации модели Active Record с помощью метода encode_with.
class Post < ActiveRecord::Base
end
old_post = Post.new(title: "hello world")
coder = {}
old_post.encode_with(coder)
post = Post.allocate
post.init_with(coder)
post.title # => 'hello world'
# File activerecord/lib/active_record/core.rb, line 785 def inspect inspect_with_attributes(attributes_for_inspect) end
Возвращает атрибуты записи в виде аккуратно отформатированной строки.
Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
Список атрибутов можно ограничить, задав .attributes_for_inspect.
Post.attributes_for_inspect = [:id, :title] Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
# File activerecord/lib/active_record/core.rb, line 801
def pretty_print(pp)
return super if custom_inspect_method_defined?
pp.object_address_group(self) do
if @attributes
attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
attr_name = attr_name.to_s
pp.breakable " "
pp.group(1) do
pp.text attr_name
pp.text ":"
pp.breakable
value = attribute_for_inspect(attr_name)
pp.text value
end
end
else
pp.breakable " "
pp.text "not initialized"
end
end
end Принимает объект PP и выводит в него эту запись в отформатированном виде. Если подключен модуль pp, это позволяет получить удобочитаемый результат с помощью pp record.
# File activerecord/lib/active_record/core.rb, line 767 def readonly! @readonly = true end
Запрещает сохранять записи в базу данных:
customer = Customer.new customer.readonly! customer.save # raises ActiveRecord::ReadOnlyRecord customer = Customer.first customer.readonly! customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
Записи только для чтения также нельзя удалить из базы данных:
customer = Customer.first customer.readonly! customer.destroy # raises ActiveRecord::ReadOnlyRecord
Обратите внимание: сами объекты по-прежнему можно изменять в памяти:
customer = Customer.new customer.readonly! customer.name = 'New Name' # OK
но сохранить изменения не получится.
# File activerecord/lib/active_record/core.rb, line 683 def readonly? @readonly end
Возвращает true, если запись доступна только для чтения.
# File activerecord/lib/active_record/core.rb, line 595
Возвращает хеш указанных методов, используя их имена в качестве ключей, а возвращенные значения — в качестве значений.
topic = Topic.new(title: "Budget", author_name: "Jason")
topic.slice(:title, :author_name)
# => { "title" => "Budget", "author_name" => "Jason" }
# File activerecord/lib/active_record/core.rb, line 723
def strict_loading!(value = true, mode: :all)
unless [:all, :n_plus_one_only].include?(mode)
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
end
@strict_loading_mode = mode
@strict_loading = value
end Включает для записи режим strict_loading. Если запись попытается лениво загрузить ассоциацию, будет вызвана ошибка.
ПРИМЕЧАНИЕ: во время валидации strict loading отключен, чтобы запись могла проверить свою ассоциацию.
user = User.first user.strict_loading! # => true user.address.city # => ActiveRecord::StrictLoadingViolationError user.comments.to_a # => ActiveRecord::StrictLoadingViolationError
Параметры
-
value— логическое значение, задающее включение или отключение strict loading. -
:mode—Symbol, задающий режим strict loading. По умолчанию используется :all. В режиме :n_plus_one_only ошибка будет вызвана только при ленивой загрузке ассоциации, которая приведет к запросу n плюс один.
Примеры
user = User.first user.strict_loading!(false) # => false user.address.city # => "Tatooine" user.comments.to_a # => [#<Comment:0x00...] user.strict_loading!(mode: :n_plus_one_only) user.address.city # => "Tatooine" user.comments.to_a # => [#<Comment:0x00...] user.comments.first.ratings.to_a # => ActiveRecord::StrictLoadingViolationError
# File activerecord/lib/active_record/core.rb, line 688 def strict_loading? @strict_loading end
Возвращает true, если запись находится в режиме strict_loading.
# File activerecord/lib/active_record/core.rb, line 740 def strict_loading_all? @strict_loading_mode == :all end
Возвращает true, если для записи используется strict_loading с включенным режимом :all.
# File activerecord/lib/active_record/core.rb, line 735 def strict_loading_n_plus_one_only? @strict_loading_mode == :n_plus_one_only end
Возвращает true, если для записи используется strict_loading с включенным режимом :n_plus_one_only.
# File activerecord/lib/active_record/core.rb, line 610
Возвращает массив значений, полученных указанными методами.
topic = Topic.new(title: "Budget", author_name: "Jason") topic.values_at(:title, :author_name) # => ["Budget", "Jason"]
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.