модуль ActiveRecord::Persistence
Active Record Персистентность
Открытые методы экземпляров
# File activerecord/lib/active_record/persistence.rb, line 198
def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
changed_attributes = @changed_attributes if defined?(@changed_attributes)
became.instance_variable_set("@changed_attributes", changed_attributes || {})
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
became.instance_variable_set("@errors", errors)
became
end Возвращает экземпляр указанного klass с атрибутами текущей записи. Это полезно в основном в структурах наследования с одной таблицей, когда вы хотите, чтобы подкласс отображался как суперкласс. Это может быть использовано вместе с идентификацией записи в Action Pack, чтобы, например, Client < Company смог сделать что-то вроде рендеринга partial: @client.becomes(Company) для рендеринга этого экземпляра с помощью части companies/company вместо clients/client.
Примечание: новый экземпляр будет иметь ссылку на те же атрибуты, что и исходный класс. Таким образом, любое изменение атрибутов в любом из экземпляров повлияет на другой.
# File activerecord/lib/active_record/persistence.rb, line 215
def becomes!(klass)
became = becomes(klass)
sti_type = nil
if !klass.descends_from_active_record?
sti_type = klass.sti_name
end
became.public_send("#{klass.inheritance_column}=", sti_type)
became
end Обёртка вокруг becomes, которая также изменяет значение столбца sti экземпляра. Это особенно полезно, если вы хотите сохранить изменённый класс в вашей базе данных.
Примечание: значение столбца sti старого экземпляра также будет изменено, так как оба объекта имеют один и тот же набор атрибутов.
# File activerecord/lib/active_record/persistence.rb, line 328 def decrement(attribute, by = 1) self[attribute] ||= 0 self[attribute] -= by self end
Инициализирует attribute к нулю, если nil, и вычитает значение, переданное как by (по умолчанию 1). Уменьшение выполняется непосредственно на базовом атрибуте, вызов сеттера не выполняется. Имеет смысл только для числовых атрибутов. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 338 def decrement!(attribute, by = 1) decrement(attribute, by).update_attribute(attribute, self[attribute]) end
Обёртка вокруг decrement, которая сохраняет запись. Этот метод отличается от своей версии без символа "bang" тем, что он пропускает через сетер атрибута. Сохранение не подвергается проверкам валидации. Возвращает true если запись была сохранена.
# File activerecord/lib/active_record/persistence.rb, line 155 def delete self.class.delete(id) if persisted? @destroyed = true freeze end
Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что никакие изменения не должны быть внесены (поскольку они не могут быть сохранены). Возвращает замороженный экземпляр.
Строка просто удаляется с помощью SQL DELETE оператора по первичному ключу записи, и никакие колбэки не выполняются.
Чтобы применить колбэки before_destroy и after_destroy объекта или любые параметры ассоциации :dependent, используйте #destroy.
# File activerecord/lib/active_record/persistence.rb, line 168
def destroy
raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
destroy_associations
self.class.connection.add_transaction_record(self)
destroy_row if persisted?
@destroyed = true
freeze
end Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что никакие изменения не должны быть внесены (поскольку они не могут быть сохранены).
Существует ряд колбэков, связанных с destroy. Если колбэк before_destroy вернёт false, действие отменяется, и destroy возвращает false. Подробнее см. ActiveRecord::Callbacks.
# File activerecord/lib/active_record/persistence.rb, line 184
def destroy!
destroy || raise(RecordNotDestroyed.new("Failed to destroy the record", self))
end Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что никакие изменения не должны быть внесены (поскольку они не могут быть сохранены).
Существует ряд колбэков, связанных с destroy!. Если колбэк before_destroy вернёт false, действие отменяется, и destroy! вызывает исключение ActiveRecord::RecordNotDestroyed. Подробнее см. ActiveRecord::Callbacks.
# File activerecord/lib/active_record/persistence.rb, line 91 def destroyed? sync_with_transaction_state @destroyed end
Возвращает true, если этот объект был удалён, иначе false.
# File activerecord/lib/active_record/persistence.rb, line 311 def increment(attribute, by = 1) self[attribute] ||= 0 self[attribute] += by self end
Инициализирует attribute к нулю, если nil, и добавляет значение, переданное как by (по умолчанию 1). Увеличение выполняется непосредственно на базовом атрибуте, вызов сеттера не выполняется. Имеет смысл только для числовых атрибутов. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 321 def increment!(attribute, by = 1) increment(attribute, by).update_attribute(attribute, self[attribute]) end
Обёртка вокруг increment, которая сохраняет запись. Этот метод отличается от своей версии без символа "bang" тем, что он пропускает через сетер атрибута. Сохранение не подвергается проверкам валидации. Возвращает true если запись была сохранена.
# File activerecord/lib/active_record/persistence.rb, line 85 def new_record? sync_with_transaction_state @new_record end
Возвращает true, если этот объект ещё не сохранён — то есть, запись для объекта ещё не существует в базе данных; в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 98 def persisted? !(new_record? || destroyed?) end
Возвращает true, если запись сохранена, то есть это не новая запись и она не была удалена, иначе возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 407
def reload(options = nil)
clear_aggregation_cache
clear_association_cache
self.class.connection.clear_query_cache
fresh_object =
if options && options[:lock]
self.class.unscoped { self.class.lock(options[:lock]).find(id) }
else
self.class.unscoped { self.class.find(id) }
end
@attributes = fresh_object.instance_variable_get('@attributes')
@new_record = false
self
end Перезагружает запись из базы данных.
Этот метод находит запись по её первичному ключу (который может быть задан вручную) и изменяет получателя на месте:
account = Account.new # => #<Account id: nil, email: nil> account.id = 1 account.reload # Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]] # => #<Account id: 1, email: 'account@example.com'>
Атрибуты перезагружаются из базы данных, кэши очищаются, в частности, кэш ассоциаций и кэш запросов.
Если запись больше не существует в базе данных, ActiveRecord::RecordNotFound возникает. В противном случае, помимо изменения на месте, метод возвращает self для удобства.
Необязательный флаг :lock позволяет заблокировать перезагруженную запись:
reload(lock: true) # reload with pessimistic locking
Перезагрузка часто используется в наборах тестов для проверки того, что что-то действительно записано в базу данных, или когда какое-то действие изменяет соответствующую строку в базе данных, но не объект в памяти:
assert account.deposit!(25) assert_equal 25, account.credit # check it is updated in memory assert_equal 25, account.reload.credit # check it is also persisted
Ещё один распространённый случай использования — обработка оптимистической блокировки:
def with_optimistic_retry
begin
yield
rescue ActiveRecord::StaleObjectError
begin
# Reload lock_version in particular.
reload
rescue ActiveRecord::RecordNotFound
# If the record is gone there is nothing to do.
else
retry
end
end
end
# File activerecord/lib/active_record/persistence.rb, line 119 def save(*) create_or_update rescue ActiveRecord::RecordInvalid false end
Сохраняет модель.
Если модель новая, в базе данных создаётся запись, в противном случае существующая запись обновляется.
По умолчанию save всегда выполняет валидацию. Если какая-либо из них терпит неудачу, действие отменяется, и save возвращает false. Однако, если вы передаёте validate: false, валидации пропускаются полностью. Дополнительная информация в ActiveRecord::Validations.
Существует ряд колбэков, связанных с save. Если какой-либо из колбэков before_* возвращает false, действие отменяется, и save возвращает false. Подробнее см. ActiveRecord::Callbacks.
Атрибуты, помеченные как readonly, игнорируются при обновлении записи.
# File activerecord/lib/active_record/persistence.rb, line 141
def save!(*)
create_or_update || raise(RecordNotSaved.new("Failed to save the record", self))
end Сохраняет модель.
Если модель новая, в базе данных создаётся запись, в противном случае существующая запись обновляется.
При этом всегда выполняются save! валидации. Если какая-либо из них завершается неудачно, генерируется исключение ActiveRecord::RecordInvalid. Для получения дополнительной информации см. ActiveRecord::Validations.
Существует ряд обратных вызовов, связанных с save!. Если любой из before_* обратных вызовов возвращает false, действие отменяется, и save! генерируется исключение ActiveRecord::RecordNotSaved. Для получения дополнительной информации см. ActiveRecord::Callbacks.
Атрибуты, помеченные как только для чтения, игнорируются при обновлении записи.
# File activerecord/lib/active_record/persistence.rb, line 346
def toggle(attribute)
self[attribute] = !send("#{attribute}?")
self
end Присваивает attribute логическое отрицание attribute?. Таким образом, если предикат возвращает true, атрибут станет false. Этот метод напрямую переключает базовое значение без вызова любого установщика. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 355 def toggle!(attribute) toggle(attribute).update_attribute(attribute, self[attribute]) end
Обёртка вокруг toggle, сохраняющая запись. Этот метод отличается от версии без восклицательного знака тем, что он пропускает установщик атрибута. Сохранение не подвергается проверке на валидность. Возвращает true, если запись могла быть сохранена.
# File activerecord/lib/active_record/persistence.rb, line 455
def touch(*names)
raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
attributes = timestamp_attributes_for_update_in_model
attributes.concat(names)
unless attributes.empty?
current_time = current_time_from_proper_timezone
changes = {}
attributes.each do |column|
column = column.to_s
changes[column] = write_attribute(column, current_time)
end
changes[self.class.locking_column] = increment_lock if locking_enabled?
clear_attribute_changes(changes.keys)
primary_key = self.class.primary_key
self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
else
true
end
end Сохраняет запись с обновлёнными атрибутами updated_at/on, установленные на текущее время. Обратите внимание, что никакая проверка валидности не выполняется, и вызываются только обратные вызовы after_touch, after_commit и after_rollback.
Если переданы имена атрибутов, они обновляются вместе с атрибутами updated_at/on.
product.touch # updates updated_at/on product.touch(:designed_at) # updates the designed_at attribute and updated_at/on product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
Если используется вместе с belongs_to, touch вызовет метод touch для связанного объекта.
class Brake < ActiveRecord::Base belongs_to :car, touch: true end class Car < ActiveRecord::Base belongs_to :corporation, touch: true end # triggers @brake.car.touch and @brake.car.corporation.touch @brake.touch
Обратите внимание, что touch должен использоваться с сохранённым объектом, иначе будет генерировано исключение ActiveRecordError. Например:
ball = Ball.new ball.touch(:updated_at) # => raises ActiveRecordError
# File activerecord/lib/active_record/persistence.rb, line 247
def update(attributes)
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
assign_attributes(attributes)
save
end
end Обновляет атрибуты модели из переданного хэша и сохраняет запись, всё в рамках транзакции. Если объект невалиден, сохранение завершится неудачно, и будет возвращено false.
# File activerecord/lib/active_record/persistence.rb, line 260
def update!(attributes)
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
assign_attributes(attributes)
save!
end
end Обновляет свой получатель, как и update, но вызывает save! вместо save, поэтому исключение генерируется, если запись невалидна.
# File activerecord/lib/active_record/persistence.rb, line 237
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
send("#{name}=", value)
save(validate: false)
end Обновляет один атрибут и сохраняет запись. Это особенно полезно для логических флагов в существующих записях. Также обратите внимание, что:
-
Проверка валидности пропускается.
-
Вызываются обратные вызовы.
-
Столбец updated_at/updated_on обновляется, если этот столбец доступен.
-
Обновляются все атрибуты, которые изменены в этом объекте.
Этот метод генерирует исключение ActiveRecord::ActiveRecordError, если атрибут помечен как только для чтения.
См. также update_column.
# File activerecord/lib/active_record/persistence.rb, line 272 def update_column(name, value) update_columns(name => value) end
Эквивалентно update_columns(name => value).
# File activerecord/lib/active_record/persistence.rb, line 291
def update_columns(attributes)
raise ActiveRecordError, "cannot update a new record" if new_record?
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
attributes.each_key do |key|
verify_readonly_attribute(key.to_s)
end
updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
attributes.each do |k, v|
raw_write_attribute(k, v)
end
updated_count == 1
end Обновляет атрибуты непосредственно в базе данных, выполняя оператор UPDATE SQL, и устанавливает их в получателе:
user.update_columns(last_request_at: Time.current)
Это самый быстрый способ обновления атрибутов, поскольку он напрямую обращается к базе данных, но следует учитывать, что в результате обычные процедуры обновления полностью проигнорированы. В частности:
-
Валидации пропускаются.
-
Обратные вызовы пропускаются.
-
updated_at/updated_onне обновляются.
Этот метод генерирует исключение ActiveRecord::ActiveRecordError при вызове для новых объектов или когда хотя бы один из атрибутов помечен как только для чтения.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.