61 votes

Rails 3: Comment identifier l'action after_commit chez les observateurs? (créer / mettre à jour / détruire)

J'ai un observateur et je enregistrer un after_commitde rappel. Comment puis-je savoir le temps qu'il a été congédié après avoir créer ou mettre à jour? Je peux dire un élément a été détruit en demandant item.destroyed? mais new_record? ne fonctionne pas étant donné que l'élément a été enregistré.

J'allais à résoudre par l'ajout d' after_create/after_update comme @action = :create à l'intérieur et vérifier l' @action à after_commit, mais il semble que l'observateur de l'instance est un singleton, et je pourrais juste remplacer une valeur avant qu'il arrive à l' after_commit. Donc je l'ai résolu en une plus laide façon, le stockage de l'action dans une carte basée sur l'élément.id sur after_create/mise à jour et la vérification de sa valeur sur after_commit. Vraiment moche.

Est-il un autre moyen?

Mise à jour

@Tardate dit, transaction_include_action? est une bonne indication, mais c'est une méthode privée, et un observateur, il doit être accessible avec #send.

class ProductScoreObserver < ActiveRecord::Observer
  observe :product

  def after_commit(product)
    if product.send(:transaction_include_action?, :destroy)
      ...

Malheureusement, l' :on option ne fonctionne pas en qualité d'observateurs.

Assurez-vous de tester l'enfer de vos observateurs ( test_after_commit gem si vous utilisez use_transactional_fixtures) ainsi, lorsque vous mettez à niveau à nouveau les Rails de la version que vous savez si il fonctionne encore.

(Testé sur 3.2.9)

Mise à jour 2

Au lieu d'Observateurs, j'ai maintenant utilisons::de la sollicitude et de l' after_commit :blah, on: :create il y travaille.

57voto

tardate Points 6809

Je pense que transaction_include_action? est ce que vous êtes après. Il donne une indication fiable de la transaction spécifique dans le processus (vérifié dans 3.0.8).

Officiellement, il détermine si une transaction inclus une action :la création, l' :mise à jour, ou :détruire. Utilisé dans le filtrage des rappels.

class Item < ActiveRecord::Base
  after_commit lambda {    
    Rails.logger.info "transaction_include_action?(:create): #{transaction_include_action?(:create)}"
    Rails.logger.info "transaction_include_action?(:destroy): #{transaction_include_action?(:destroy)}"
    Rails.logger.info "transaction_include_action?(:update): #{transaction_include_action?(:update)}"
  }
end

Également d'intérêt peut être transaction_record_state qui peut être utilisé pour déterminer si un enregistrement a été créée ou détruite dans une transaction. L'état doit être :new_record ou :détruit.

56voto

Jure Triglav Points 784

J'ai appris aujourd'hui que vous pouvez faire quelque chose comme ça:

 after_commit :do_something, :on => :create

after_commit :do_something, :on => :update
 

do_something est la méthode de rappel que vous souhaitez appeler pour certaines actions.

Si vous souhaitez appeler le même rappel pour la mise à jour et la création , mais pas pour la détruire , vous pouvez également utiliser: after_commit :do_something, :if => :persisted?

Ce n'est vraiment pas bien documenté et j'ai eu du mal à le googler. Heureusement, je connais quelques personnes brillantes. J'espère que ça aide!

7voto

leenasn Points 1036

Vous pouvez résoudre en utilisant deux techniques.

  • L’approche suggérée par @nathanvda, c’est-à-dire vérifier les éléments created_at et updated_at. Si elles sont identiques, l’enregistrement est nouvellement créé, sinon c’est une mise à jour.

  • En utilisant des attributs virtuels dans le modèle. Les étapes sont les suivantes:

    • Ajouter un champ dans le modèle avec le code attr_accessor newly_created
    • Mettre à jour la même chose dans les before_create et before_update callbacks

       def before_create (record)
          record.newly_created = true
      end
      
      def before_update (record)
          record.newly_created = false
      end
       

3voto

lacco Points 298

Sur la base de mon idée, j’ai créé des modules qui permettent d’utiliser les rappels after_commit_on_update et after_commit_on_create : https://gist.github.com/2392664

Usage:

 class User < ActiveRecord::Base
  include AfterCommitCallbacks
  after_commit_on_create :foo

  def foo
    puts "foo"
  end
end

class UserObserver < ActiveRecord::Observer
  def after_commit_on_create(user)
    puts "foo"
  end
end
 

2voto

kenn Points 1314

Jetez un coup d'œil au code de test: https://github.com/rails/rails/blob/master/activerecord/test/cases/transaction_callbacks_test.rb

Là vous pouvez trouver:

 after_commit(:on => :create)
after_commit(:on => :update)
after_commit(:on => :destroy)
 

et

 after_rollback(:on => :create)
after_rollback(:on => :update)
after_rollback(:on => :destroy)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X