1
0
Fork 0
mirror of https://github.com/aasm/aasm synced 2023-03-27 23:22:41 -04:00

Fix ClassInvoker instantiating class twice

* I'm in the process of updating a rails app from rails 5.2 to rails 7.
  Currently the app is using AASM version 4.12.3. When I update to AASM
  version 5.x we start getting a failing spec in our app. The
  implementation uses the `after:` dsl and passes it a class.

  ``` ruby
   event :run_payment_create do
      transitions to: :active, after: Payment::GoCardless::Subscription::Charge
    end
  ```

  The spec in our app expects that class to receive new 1 time with the args
  passed from after, but the spec is failing saying that `Payment::GoCardless::Subscription::Charge`
  received `.new` with the expected args twice.
  I started debugging with `bundle open aasm` and pry and I found that the
  `ClassInvoker` has a `instance` method that memoizes the instance
  returned from the `retrieve_instance` method, but the `instance`
  method was only being used by `log_source_location` and `log_method_info`
  methods while `invoke_subject` was calling `retrieve_instance` directly
  resulting in `retrieve_instance` being called twice.
* Update `invoke_subject` method to use `instance.call` so that the
  instance will be memoized and subsequent calls to `instance` won't try
  to instantiate the class a second time.
This commit is contained in:
Daniel Nolan 2023-01-20 13:50:39 -05:00 committed by Anil Kumar Maurya
parent 7d49b11a86
commit 5d22f691fe
2 changed files with 13 additions and 1 deletions

View file

@ -17,7 +17,7 @@ module AASM
end end
def invoke_subject def invoke_subject
@result = retrieve_instance.call @result = instance.call
end end
private private

View file

@ -52,6 +52,18 @@ describe AASM::Core::Invokers::ClassInvoker do
expect(subject.failures.first).to be_a(Method) expect(subject.failures.first).to be_a(Method)
end end
context 'when a failure occurs' do
let(:target) { Class.new { def initialize(_r, *_a); end; def call; end } }
it 'only instantiates subject class one time' do
target_instance = target.new(record, *args)
expect(target).to receive(:new).with(record, *args).and_return(target_instance).once
expect { subject.invoke }.not_to raise_error
end
end
end end
end end