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

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

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

I идеально использовать обратный вызов 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 вызывает save, который затем вызывает обратный вызов before_save, вызывающий бесконечный цикл. Есть ли простой способ обойти это поведение?

5 ответов


вы можете обернуть его в условное, что-то вроде:

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

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


рельсы 2:

Model.send(:create_without_callbacks)
Model.send(:update_without_callbacks)

рельсы 3:

Vote.skip_callback(:save, :after, :add_points_to_user)

см. Этот вопрос:

как пропустить обратные вызовы ActiveRecord?


любой 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

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

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. Вы можете использовать переменную-член для указания изменений, если она не может.


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

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

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

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

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

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