Перегрузка и обход обновления активной записи - PullRequest
0 голосов
/ 04 февраля 2010

Я пытаюсь внедрить контрольный журнал в мое приложение, но из-за некоторых требований я не могу использовать любые существующие гемы или плагины.

Я бы хотел отклонить любую обычную попытку обновить модель на пользовательский метод, который сохраняет все обновления в другой отдельной таблице (называемой Обновления).

Затем приложение использует таблицу обновлений для фактического выполнения обновления.

Сейчас я перегружен create_or_update, чтобы получить первую часть функциональности

def create_or_update
  raise ReadOnlyRecord if readonly?
  result = new_record? ? create : create_updates
  result != false
end


class Update < ActiveRecord::Base
  belongs_to :updatable, :polymorphic => true

  after_create :update_model

  private

  def update_model
    self.updatable.update_attribute self.attribute, self.new_value #infinite loop
  end
end  

Проблема теперь в том, что это вызывает бесконечный цикл, когда Модель обновления пытается фактически выполнить обновление.

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

Любые предложения будут с благодарностью.

1 Ответ

1 голос
/ 04 февраля 2010

Вам действительно нужно сохранить атрибуты в отдельной таблице, а затем выполнить обновление после того, как администратор просмотрит и утвердит их? Если это сценарий, вы можете просто перезаписать метод обновления, чтобы сделать что-то вроде этого:

def update(perform_updates = false)
  if perform_updates
    latest_approved_update = UpdateAuditor.first(:conditions => { :updatable_id => self.id, :updatable_type => self.class.name, :approved => true })
    self.attributes = latest_approved_update.attributes
    self.save
  else 
    UpdateAuditor.create(:updatable_id => self.id, :updatable_type => self.class.name, :attributes => self.attributes)
  end
end

ОБНОВЛЕНИЕ: Автор прокомментировал, что он хочет иметь возможность применить эту модель к всем обновлениям. Для этого вы можете добавить в модель attr_accessor, скажем, что-то вроде «execute_updates», которое, конечно, будет по умолчанию нулевым.

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

class Person < ActiveRecord::Base
  has_many :audits, :class_name => "UpdateAudit", :as => :auditable

  attr_accessor :perform_updates

  private

  def create_or_update
    raise ReadOnlyRecord if readonly?

    if new_record?
      result = create
      result != false
    else
      if perform_updates
        latest_approved_update = audits.approved.last

        if latest_approved_update
          self.attributes = latest_approved_update.attributes
          update
        else
          return false
        end
      else
        audits.create(:updated_attributes => self.attributes)
      end 
    end
  end
end

Для справки, я думаю, что переписывание методов обновления по умолчанию - опасная игра, и такое программирование лучше использовать в обратном вызове before_update, где он и находится. Как только обновление утверждено в каком-то интерфейсе, тогда наблюдатель может выполнить обновление, перезаписывая то, что там в данный момент, до тех пор, пока не будет одобрено другое внесенное изменение. Если в данный момент в очереди есть обновляемые объекты для утверждения, пользователи могут быть предупреждены о том, что изменения ожидают утверждения и т. Д.

...