Spec-Zone.ru › Ruby on Rails 5.0

модуль ActiveRecord::Persistence

Персистентность Active Record

Публичные методы экземпляра

becomes(klass) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 220
def becomes(klass)
  became = klass.new
  became.instance_variable_set("@attributes", @attributes)
  became.instance_variable_set("@mutation_tracker", @mutation_tracker) if defined?(@mutation_tracker)
  became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
  became.instance_variable_set("@new_record", new_record?)
  became.instance_variable_set("@destroyed", destroyed?)
  became.errors.copy!(errors)
  became
end

Возвращает экземпляр указанного klass с атрибутами текущей записи. Это в основном полезно в отношении структур наследования одной таблицы, когда вы хотите, чтобы подкласс выглядел как суперкласс. Это можно использовать вместе с идентификацией записей в Action Pack, чтобы разрешить, скажем, Client < Company делать что-то вроде отображения partial: @client.becomes(Company), чтобы отобразить этот экземпляр, используя частичный шаблон companies/company вместо clients/client.

Примечание: новый экземпляр будет совместно использовать ссылку на те же атрибуты, что и исходный класс. Поэтому значение столбца sti останется тем же самым. Любое изменение атрибутов в любом из экземпляров повлияет на оба экземпляра. Если вы также хотите изменить столбец sti, используйте becomes! вместо этого.

becomes!(klass) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 237
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 старого экземпляра также будет изменено, поскольку оба объекта используют один и тот же набор атрибутов.

decrement(attribute, by = 1) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 356
def decrement(attribute, by = 1)
  increment(attribute, -by)
end

Инициализирует attribute в ноль, если nil и вычитает значение, переданное как by (по умолчанию 1). Уменьшение выполняется непосредственно по базовому атрибуту, вызыватель не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.

decrement!(attribute, by = 1) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 364
def decrement!(attribute, by = 1)
  increment!(attribute, -by)
end

Обертка вокруг decrement, которая записывает обновление в базу данных. Обновляется только attribute; сама запись не сохраняется. Это означает, что любые другие измененные атрибуты останутся измененными. Валидация и обратные вызовы пропускаются. Возвращает self.

delete() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 175
def delete
  self.class.delete(id) if persisted?
  @destroyed = true
  freeze
end

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

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

Обратите внимание, что это также удалит записи, помеченные как #readonly?.

Чтобы обеспечить обратные вызовы before_destroy и after_destroy объекта или любые параметры ассоциации :dependent, используйте #destroy.

destroy() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 188
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 вызывает исключение :abort, действие отменяется, и destroy возвращает false. См. ActiveRecord::Callbacks для получения дополнительной информации.

destroy!() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 204
def destroy!
  destroy || _raise_record_not_destroyed
end

Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что изменения не должны вноситься (поскольку их нельзя сохранить).

Существует ряд обратных вызовов, связанных с destroy!. Если обратный вызов before_destroy вызывает исключение :abort, действие отменяется, и destroy! вызывает ActiveRecord::RecordNotDestroyed. См. ActiveRecord::Callbacks для получения дополнительной информации.

destroyed?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 91
def destroyed?
  sync_with_transaction_state
  @destroyed
end

Возвращает true, если этот объект был уничтожен, иначе возвращает false.

increment(attribute, by = 1) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 335
def increment(attribute, by = 1)
  self[attribute] ||= 0
  self[attribute] += by
  self
end

Инициализирует attribute в ноль, если nil и добавляет значение, переданное как by (по умолчанию 1). Приращение выполняется непосредственно по базовому атрибуту, вызыватель не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.

increment!(attribute, by = 1) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 345
def increment!(attribute, by = 1)
  increment(attribute, by)
  change = public_send(attribute) - (attribute_was(attribute.to_s) || 0)
  self.class.update_counters(id, attribute => change)
  clear_attribute_change(attribute) # eww
  self
end

Обертка вокруг increment, которая записывает обновление в базу данных. Обновляется только attribute; сама запись не сохраняется. Это означает, что любые другие измененные атрибуты останутся измененными. Валидация и обратные вызовы пропускаются. Возвращает self.

new_record?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 85
def new_record?
  sync_with_transaction_state
  @new_record
end

Возвращает true, если этот объект еще не сохранен, то есть запись для объекта еще не существует в базе данных; в противном случае возвращает false.

persisted?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 98
def persisted?
  sync_with_transaction_state
  !(@new_record || @destroyed)
end

Возвращает true, если запись сохранена, т. е. это не новая запись и она не была уничтожена, в противном случае возвращает false.

reload(options = nil) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 441
def reload(options = nil)
  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'>

Атрибуты перезагружаются из базы данных, и кеши очищаются, в частности кеш ассоциаций и QueryCache.

Если запись больше не существует в базе данных, возникает исключение 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
save(*args) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 128
def save(*args, &block)
  create_or_update(*args, &block)
rescue ActiveRecord::RecordInvalid
  false
end

Сохраняет модель.

Если модель новая, в базе данных создается запись, в противном случае существующая запись обновляется.

По умолчанию save всегда запускает валидацию. Если какая-либо из них завершается неудачей, действие отменяется, и save возвращает false, и запись не будет сохранена. Однако, если вы укажете validate: false, валидация полностью игнорируется. См. ActiveRecord::Validations для получения дополнительной информации.

По умолчанию save также устанавливает атрибуты updated_at/updated_on на текущее время. Однако, если вы предоставите touch: false, эти метки времени не будут обновлены.

Существует ряд обратных вызовов, связанных с save. Если какой-либо из обратных вызовов before_* вызывает исключение :abort, действие отменяется, и save возвращает false. См. ActiveRecord::Callbacks для получения дополнительной информации.

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

save!(*args) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 159
def save!(*args, &block)
  create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end

Сохраняет модель.

Если модель новая, в базе данных создается запись, в противном случае существующая запись обновляется.

По умолчанию, save! всегда выполняет валидацию. Если какая-либо из них завершается неудачно, возникает ActiveRecord::RecordInvalid, и запись не будет сохранена. Однако, если вы укажете validate: false, валидация будет пропущена. Более подробная информация представлена в ActiveRecord::Validations.

По умолчанию, save! также устанавливает атрибуты updated_at/updated_on на текущее время. Однако, если вы укажете touch: false, эти временные метки не будут обновляться.

Существует ряд обратных вызовов, связанных с save!. Если любой из before_* обратных вызовов генерирует :abort, действие отменяется, и save! генерирует ActiveRecord::RecordNotSaved. Дополнительные сведения см. в ActiveRecord::Callbacks.

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

toggle(attribute) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 380
def toggle(attribute)
  self[attribute] = !public_send("#{attribute}?")
  self
end

Присваивает attribute логическое отрицание attribute?. Таким образом, если предикат возвращает true, атрибут станет false. Этот метод непосредственно переключает базовое значение без вызова любого метода-сеттера. Возвращает self.

Пример:

user = User.first
user.banned? # => false
user.toggle(:banned)
user.banned? # => true
toggle!(attribute) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 389
def toggle!(attribute)
  toggle(attribute).update_attribute(attribute, self[attribute])
end

Обертка над toggle, которая сохраняет запись. Этот метод отличается от своей версии без восклицательного знака тем, что он пропускает метод-сеттер атрибута. Сохранение не подчиняется проверкам валидации. Возвращает true, если запись была сохранена.

touch(*names, time: nil) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 490
    def touch(*names, time: nil)
      unless persisted?
        raise ActiveRecordError, "          cannot touch on a new or destroyed record object. Consider using
          persisted?, new_record?, or destroyed? before touching
".squish
      end

      time ||= current_time_from_proper_timezone
      attributes = timestamp_attributes_for_update_in_model
      attributes.concat(names)

      unless attributes.empty?
        changes = {}

        attributes.each do |column|
          column = column.to_s
          changes[column] = write_attribute(column, time)
        end

        clear_attribute_changes(changes.keys)
        primary_key = self.class.primary_key
        scope = self.class.unscoped.where(primary_key => _read_attribute(primary_key))

        if locking_enabled?
          locking_column = self.class.locking_column
          scope = scope.where(locking_column => _read_attribute(locking_column))
          changes[locking_column] = increment_lock
        end

        result = scope.update_all(changes) == 1

        if !result && locking_enabled?
          raise ActiveRecord::StaleObjectError.new(self, "touch")
        end

        result
      else
        true
      end
    end

Сохраняет запись, установив атрибуты updated_at/on на текущее время или заданное время. Обратите внимание, что валидация не выполняется, и вызываются только обратные вызовы after_touch, after_commit и after_rollback.

Этот метод может принимать имена атрибутов и необязательный аргумент time. Если передаются имена атрибутов, они обновляются вместе с атрибутами updated_at/on. Если аргумент time не передан, используется текущее время по умолчанию.

product.touch                         # updates updated_at/on with current time
product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time
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
update(attributes) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 270
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.

Также алиасировано как: update_attributes
update!(attributes) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 283
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!, поэтому исключение генерируется, если запись невалидна, и сохранение завершится неудачно.

Также алиасировано как: update_attributes!
update_attribute(name, value) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 259
def update_attribute(name, value)
  name = name.to_s
  verify_readonly_attribute(name)
  public_send("#{name}=", value)

  changed? ? save(validate: false) : true
end

Обновляет отдельный атрибут и сохраняет запись. Это особенно полезно для логических флагов существующих записей. Также обратите внимание, что:

  • Валидация пропускается.

  • Вызываются обратные вызовы.

  • Столбец updated_at/updated_on обновляется, если такой столбец существует.

  • Обновляются все грязные атрибуты в этом объекте.

Этот метод генерирует ActiveRecord::ActiveRecordError, если атрибут помечен как только для чтения.

См. также update_column.

update_attributes(attributes)
Псевдоним для: update
update_attributes!(attributes)
Псевдоним для: update!
update_column(name, value) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 295
def update_column(name, value)
  update_columns(name => value)
end

Эквивалентно update_columns(name => value).

update_columns(attributes) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 315
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

Обновляет атрибуты непосредственно в базе данных, выполняя SQL-запрос UPDATE, и устанавливает их в получателе:

user.update_columns(last_request_at: Time.current)

Это самый быстрый способ обновления атрибутов, поскольку он напрямую обращается к базе данных, но учтите, что стандартные процедуры обновления полностью пропущены. В частности:

  • Валидация пропускается.

  • Обратные вызовы пропускаются.

  • updated_at/updated_on не обновляются.

  • Однако, атрибуты сериализуются по тем же правилам, что и в ActiveRecord::Relation#update_all.

Этот метод генерирует ActiveRecord::ActiveRecordError при вызове для новых объектов или когда хотя бы один из атрибутов помечен как только для чтения.

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

Spec-Zone.ru

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