UFO ET IT

Rails 3 : 옵저버에서 after_commit 작업을 식별하는 방법은 무엇입니까?

ufoet 2020. 11. 30. 20:26
반응형

Rails 3 : 옵저버에서 after_commit 작업을 식별하는 방법은 무엇입니까? (생성 / 업데이트 / 파괴)


관찰자가 있고 after_commit콜백을 등록합니다 . 생성 또는 업데이트 후 실행되었는지 어떻게 알 수 있습니까? 질문을 통해 항목이 파괴되었음을 알 수 item.destroyed?있지만 #new_record?항목이 저장되었으므로 작동하지 않습니다.

after_create/ 를 추가 after_update하고 @action = :create내부와 같은 작업을 수행 하고 @actionat을 확인하여 해결하려고 after_commit했지만 옵저버 인스턴스가 싱글 톤 인 것 같고 after_commit. 그래서 나는 그것을 추악한 방식으로 해결하여 after_create / update의 item.id를 기반으로 한 맵에 액션을 저장하고 after_commit에서 그 값을 확인했습니다. 정말 못 생겼어.

다른 방법이 있습니까?

최신 정보

@tardate가 말했듯이, transaction_include_action?은 개인 방법이지만 좋은 표시이며 관찰자에서는 #send.

class ProductScoreObserver < ActiveRecord::Observer
  observe :product

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

불행히도 :on옵저버 에서는이 옵션이 작동하지 않습니다.

관찰자의 지옥 ( test_after_commituse_transactional_fixtures를 사용하는 경우 gem을 찾으세요)을 테스트하여 새로운 Rails 버전으로 업그레이드 할 때 여전히 작동하는지 알 수 있습니다.

(3.2.9에서 테스트 됨)

업데이트 2

Observers 대신 나는 이제 ActiveSupport :: Concern을 사용하고 after_commit :blah, on: :create거기에서 일합니다.


나는 transaction_include_action? 당신이 추구하는 것입니다. 처리중인 특정 트랜잭션에 대한 신뢰할 수있는 표시를 제공합니다 (3.0.8에서 확인 됨).

공식적으로 트랜잭션에 : create, : update 또는 : destroy에 대한 작업이 포함되어 있는지 확인합니다. 콜백 필터링에 사용됩니다.

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

또한 트랜잭션 에서 레코드가 생성되었는지 또는 삭제되었는지 확인하는 데 사용할 수있는 transaction_record_state 가 관심을 가질 수 있습니다 . 상태는 : new_record 또는 : destroyed 중 하나 여야합니다.

Rails 4 업데이트

Rails 4에서 문제를 해결하고자하는 사람들을 위해,이 메소드는 이제 더 이상 사용되지 않습니다 . transaction_include_any_action?어떤 array작업 을 허용 하는지 사용해야 합니다.

사용 예 :

transaction_include_any_action?([:create])

저는 오늘 여러분이 다음과 같이 할 수 있다는 것을 배웠습니다.

after_commit :do_something, :on => :create

after_commit :do_something, :on => :update

어디 do_something는 특정 행동에 호출 할 콜백 방법이다.

updatecreate에 대해 동일한 콜백을 호출 하지만 destroy가 아닌 경우 다음을 사용할 수도 있습니다.after_commit :do_something, :if => :persisted?

정말 잘 문서화되지 않았고 인터넷 검색이 어려웠습니다. 다행히도 저는 몇 명의 훌륭한 사람들을 알고 있습니다. 도움이 되었기를 바랍니다.


두 가지 기술을 사용하여 해결할 수 있습니다.

  • @nathanvda가 제안한 접근 방식, 즉 created_at 및 updated_at 확인. 동일하다면 레코드가 새로 생성되고 그렇지 않으면 업데이트가됩니다.

  • 모델에서 가상 속성을 사용합니다. 단계는 다음과 같습니다.

    • 코드를 사용하여 모델에 필드 추가 attr_accessor newly_created
    • 에서 같은 업데이트 before_createbefore_update callbacks같은

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

leenasn 아이디어를 기반으로 사용 after_commit_on_updateafter_commit_on_create콜백을 가능하게하는 몇 가지 모듈을 만들었습니다 . https://gist.github.com/2392664

용법:

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

테스트 코드를 살펴보십시오 : https://github.com/rails/rails/blob/master/activerecord/test/cases/transaction_callbacks_test.rb

다음을 찾을 수 있습니다.

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

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

I'm curious to know why you couldn't move your after_commit logic into after_create and after_update. Is there some important state change that happens between the latter 2 calls and after_commit?

If your create and update handling has some overlapping logic, you could just have the latter 2 methods call a third method, passing in the action:

# Tip: on ruby 1.9 you can use __callee__ to get the current method name, so you don't have to hardcode :create and :update.
class WidgetObserver < ActiveRecord::Observer
  def after_create(rec)
    # create-specific logic here...
    handler(rec, :create)
    # create-specific logic here...
  end
  def after_update(rec)
    # update-specific logic here...
    handler(rec, :update)
    # update-specific logic here...
  end

  private
  def handler(rec, action)
    # overlapping logic
  end
end

If you still rather use after_commit, you can use thread variables. This won't leak memory as long as dead threads are allowed to be garbage-collected.

class WidgetObserver < ActiveRecord::Observer
  def after_create(rec)
    warn "observer: after_create"
    Thread.current[:widget_observer_action] = :create
  end

  def after_update(rec)
    warn "observer: after_update"
    Thread.current[:widget_observer_action] = :update
  end

  # this is needed because after_commit also runs for destroy's.
  def after_destroy(rec)
    warn "observer: after_destroy"
    Thread.current[:widget_observer_action] = :destroy
  end

  def after_commit(rec)
    action = Thread.current[:widget_observer_action]
    warn "observer: after_commit: #{action}"
  ensure
    Thread.current[:widget_observer_action] = nil
  end

  # isn't strictly necessary, but it's good practice to keep the variable in a proper state.
  def after_rollback(rec)
    Thread.current[:widget_observer_action] = nil
  end
end

I use the following code to determine whether it is a new record or not:

previous_changes[:id] && previous_changes[:id][0].nil?

It based on idea that a new record has default id equal to nil and then changes it on save. Of course id changing is not a common case, so in most cases the second condition can be omitted.


This is similar to your 1st approach but it only uses one method (before_save or before_validate to really be safe) and I don't see why this would override any value

class ItemObserver
  def before_validation(item) # or before_save
    @new_record = item.new_record?
  end

  def after_commit(item)
    @new_record ? do_this : do_that
  end
end

Update

This solution doesn't work because as stated by @eleano, ItemObserver is a Singleton, it has only one instance. So if 2 Item are saved at the same time @new_record could take its value from item_1 while after_commit is triggered by item_2. To overcome this problem there should be an item.id checking/mapping to "post-synchornize" the 2 callback methods : hackish.


You can change your event hook from after_commit to after_save, to capture all create and update events. You can then use:

id_changed?

...helper in the observer. This will be true on create and false on an update.

참고URL : https://stackoverflow.com/questions/7238699/rails-3-how-to-identify-after-commit-action-in-observers-create-update-destro

반응형