AaronLasseigne / active_interaction

:briefcase: Manage application specific business logic.
MIT License
2.07k stars 137 forks source link

input variable is undefined when used in default lambda for another input variable #507

Closed isfando closed 3 years ago

isfando commented 3 years ago
class XYZ < ActiveInteraction::Base
    object :foo, class: X, default: -> { baz&.contact }
    object :bar, class: X, default: -> { X.unscoped.find(baz.duplicate_contact_id) }
    object :baz, default: -> {
      ContactDuplicateContact.where(contact_id: foo.id, duplicate_contact_id: bar.id).first!
    }

  def execute
    ...
  end
end

When i call the interaction as following where "bar_obj" is not nil XYZ.run!(baz: baz_obj)

the default initialization of "bar" fails with following error because it finds baz as undefined

NoMethodError:
  undefined method `duplicate_contact_id' for nil:NilClass

I can circumvent the error by changing initialization order i.e. initializing baz as the first variable which makes the above scenario successful

class XYZ < ActiveInteraction::Base
    object :baz, default: -> {
      ContactDuplicateContact.where(contact_id: foo.id, duplicate_contact_id: bar.id).first!
    }
    object :foo, class: X, default: -> { baz&.contact }
    object :bar, class: X, default: -> { X.unscoped.find(baz.duplicate_contact_id) }

  def execute
    ...
  end
end

BUT it then fails when the interaction is called with XYZ.run!(foo: foo_obj, bar:bar _obj)

the default initialization of "baz" fails with following error because it finds "foo" and "bar" as undefined

NoMethodError:
  undefined method `id' for nil:NilClass

It look analogous to a circular dependency problem with defaults. One dirty solution where we expose extra complication and declare everything directly would be obviously this

baz_obj = get_baz_obj(..)
foo_obj = baz&.contact
bar_obj = X.unscoped.find(baz&.duplicate_contact_id)
XYZ.run!(baz: baz_obj, foo: foo_obj , bar: bar_obj )

BUT Can anybody suggest a better way to solve this?

isfando commented 3 years ago

@AaronLasseigne , any guidance on this if possible

AaronLasseigne commented 3 years ago

Yup, it's a circular dependency. I can't really offer help here. You'll have to rethink how you're approaching the problem. Best of luck.

isfando commented 3 years ago
class XYZ < ActiveInteraction::Base
    object :foo, class: X, default: -> { baz&.contact }
    object :bar, class: X, default: -> { X.unscoped.find(baz.duplicate_contact_id) }
    object :baz, default: -> {
      ContactDuplicateContact.where(contact_id: foo.id, duplicate_contact_id: bar.id).first!
    }

  def execute
    ...
  end
end

When i call the interaction as following where "bar_obj" is not nil XYZ.run!(baz: baz_obj)

But why is it undefined in default lambda of "bar", which fails with following error because it finds baz as undefined

NoMethodError:
  undefined method `duplicate_contact_id' for nil:NilClass
AaronLasseigne commented 3 years ago

We do checking of defaults early in process so it's running it and assigning it before the other objects have become available.