Подтвердить что ты не робот

Как обновить атрибуты в after_save, не вызывая рекурсии в rails 2.3?

У меня есть модель, у которой есть видео, прикрепленное с помощью Paperclip. После сохранения я использую сохраненное видео для создания миниатюры. Мне нужно сделать это после каждого сохранения, даже если новое видео не было загружено, потому что пользователь может изменить время захвата эскиза.

В настоящее время я использую after_post_process для этого, но при загрузке файла он будет генерировать только миниатюру (это обратный вызов, который является частью Paperclip).

В идеале я бы использовал обратный вызов after_save следующим образом:

after_save :save_thumbnail
def save_thumbnail
  #generate thumbnail...
  self.update_attributes(
    :thumbnail_file_name => File.basename(thumb), 
    :thumbnail_content_type => 'image/jpeg'
  )
end

К сожалению, update_attributes вызывает сохранение, которое затем вызывает обратный вызов before_save, вызывающий бесконечный цикл. Есть ли простой способ обойти это поведение?

4b9b3361

Ответ 1

Вы можете обернуть его в условное выражение, например:

def save_thumbnail
  if File.basename(thumb) != thumbnail_file_name
    self.update_attributes(
      :thumbnail_file_name => File.basename(thumb), 
      :thumbnail_content_type => 'image/jpeg'
    )
  end
end

Таким образом, он будет запускаться только один раз.

Ответ 3

Любой after_save update_attribute в after_save вызовет рекурсию в Rails3+. Что должно быть сделано, это:

after_save :updater!
# Awesome Ruby code
# ...
# ...

private

  def updater!
    self.update_column(:column_name, new_value) # This will skip validation gracefully.
  end

Вот некоторая документация об этом: https://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks

Ответ 4

Вы можете (и должны) проверить, действительно ли вам нужно обновить миниатюру:

after_save :save_thumbnail
def save_thumbnail
  if capture_time_changed? #assuming capture_time contains time when the thumbnail has to be captured
    #generate thumbnail...
    self.update_attributes(
      :thumbnail_file_name => File.basename(thumb), 
      :thumbnail_content_type => 'image/jpeg'
    )
  end
end

Здесь вы можете узнать больше о 'грязных' атрибутах: http://apidock.com/rails/ActiveRecord/Dirty

Хотя я не уверен, что он все еще может видеть изменения атрибутов в after_save. Вы можете использовать переменную-член, чтобы указать изменения в случае, если она не может.

Ответ 5

Вместо этого вы можете запустить его как before_save.

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

before_save :save_thumbnail
def save_thumbnail
  self.thumbnail_file_name = File.basename(thumb), 
  self.thumbnail_content_type = 'image/jpeg'
end

Так как это не вызовет save, вы не будете рекурсивно, но он будет немедленно сохранен после выхода метода.

Что-то вроде этого должно работать, если только нет явной причины, по которой вам это нужно после сохранения.

Поскольку вы не обновляете отдельный объект, но тот же, это также сохранит вам вызов базы данных. Это как я делаю timestamps и тому подобное.