Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compose does not copy symbolic errors #87

Closed
justinsteffy opened this issue Dec 9, 2013 · 10 comments
Closed

compose does not copy symbolic errors #87

justinsteffy opened this issue Dec 9, 2013 · 10 comments
Assignees
Labels
Milestone

Comments

@justinsteffy
Copy link

When using compose with an interaction that uses add_sym to create symbolic errors, the compose method doesn't pass these symbolic errors along to the outcome.

@ghost ghost assigned tfausak Dec 9, 2013
@tfausak
Copy link
Collaborator

tfausak commented Dec 9, 2013

This might not make sense. When compose moves errors from one interaction to another, it uses full_messages. For instance, if errors.symbolic[:attribute] = [:required], that is currently translated into errors.messages[:base] = ["Attribute is required"]. Copying symbolic errors would instead make that errors.symbolic[:base] = [:required], which would then be rendered by full_messages as "is required". Not too useful.

And we can't copy symbolic errors over to arbitrary attributes because they might not exist on the receiver.

I don't have a good solution for this yet.

@tfausak
Copy link
Collaborator

tfausak commented Mar 3, 2014

Here's how this currently behaves:

class A < ActiveInteraction::Base
  integer :x, :y
  def execute; end
end

class B < ActiveInteraction::Base
  boolean :x
  def execute
    compose(A, inputs)
  end
end

errors = B.run(x: true).errors
errors.messages
# => {:base=>["X is not a valid integer", "Y is required"]}
errors.symbolic
# => {}

I think ideally it should behave like this:

errors.messages
# => {:base=>["Y is required"], :x=>["is not a valid integer"]}
errors.symbolic
# => {:x=>[:invalid_type]}

Do you agree?

@AaronLasseigne
Copy link
Owner

What do we gain with this change? Does this improve testing by attaching the error to the appropriate input?

@Szeliga
Copy link

Szeliga commented May 16, 2014

@tfausak I agree
@AaronLasseigne we would gain better form errors displaying.

I have the following use case:

  • User buys a new subscription
  • A form with his address data displays (which is copied from the user address data into to subscription receipt) - UpdateFromForm interactor
  • The form gets submitted
  • If everything went ok, I update the User with the newly inputted data, and make an API call to an invoicing service - UpdateUser interactor

The invoicing service validates the tax identification number (in Poland it's called NIP).

I have a simple_form for the UpdateFromForm interactor, everything works nice, the error messages get displayed properly by simple_form.

But when I composed the UpdateUser interactor, I expected the errors on the NIP field, to be passed along to the caller, instead it added it as a full message to the errors[:base].

The way I add the error is following:

self.errors.add err[0].downcase.to_sym, :invalid

err[0].downcase.to_sym this changes the field from "Nip" to :nip.

Is this there another way to add errors during execution, that will pass them on the the caller?

Best regards, Szymon.

@tfausak
Copy link
Collaborator

tfausak commented May 16, 2014

@Szeliga thanks for providing a real world use case! There's currently no other built in way to transfer errors as part of composition. If you want to do it yourself, that's always possible (but a little verbose). For instance:

class A < ActiveInteraction::Base
  def execute; end
end

class B < ActiveInteraction::Base
  def execute
    outcome = A.run(...)
    if outcome.invalid?
      # Do something with outcome.errors (both messages and symbolic).
      return
    end
    # ...
  end
end

I've started a pull request (#194) to discuss a possible solution to this problem.

@Szeliga
Copy link

Szeliga commented May 16, 2014

Ah, of course. Thanks for the tip. I forgot, I can acces the errors this way. Sometimes I expect too much magic from libraries :)

@tfausak tfausak added this to the v2.0.0 milestone May 28, 2014
@tfausak
Copy link
Collaborator

tfausak commented Aug 18, 2014

This is the last remaining issue on the v2.0.0 milestone. I'm not sure that #216 the best solution, but it's a solution. And I think it's better than the way it is now.

@tfausak
Copy link
Collaborator

tfausak commented Jan 29, 2015

I want to resurrect this issue for more discussion. I encountered a use case where this behavior would have been extremely useful.

Let's say you want to refactor an interaction. It will accept the same inputs and (hopefully) return the same result, but the internals will be different. One way to accomplish this would be to create a new interaction for the refactored version and run it side-by-side with the original version. (Gems like dat-science help with this part.) Since the two interactions have the same inputs, you should be able to call either of them from a top-level interaction and the errors should end up in the right place. More concretely:

class Addition < ActiveInteraction::Base
  integer :x, :y

  def execute
    Science::Experiment.new('Addition') do |e|
      e.control { compose(AdditionControl) }
      e.candidate { compose(AdditionCandidate) }
    end.run
  end
end

class AdditionControl < ActiveInteraction::Base
  import_filters Addition

  def execute
    x + y
  end
end

class AdditionCandidate < ActiveInteraction::Base
  import_filters Addition

  def execute
    x - (-y)
  end
end

It looks like everything should Just Work™, but the resulting errors leave a lot to be desired.

errors = Addition.run(x: 1, y: 'oops').errors
errors.messages
# => {:base=>["Y is not a valid integer"]}
errors.symbolic
# => {}

@tfausak tfausak reopened this Jan 29, 2015
@tfausak tfausak removed the wontfix label Jan 29, 2015
@tfausak tfausak removed their assignment Feb 25, 2015
@tfausak
Copy link
Collaborator

tfausak commented Apr 7, 2015

I'm taking this off the v2.0.0 milestone, but it's still something that we might want to do.

@tfausak
Copy link
Collaborator

tfausak commented Jul 30, 2015

I am adding this to the v3.0.0 milestone. We either need to make a decision about it and fix it, or close it and commit to keeping things the way they are.

@tfausak tfausak added this to the v3.0.0 milestone Jul 30, 2015
@tfausak tfausak self-assigned this Dec 16, 2015
This was referenced Dec 16, 2015
tfausak added a commit that referenced this issue Jan 10, 2016
Copy symbolic errors when composing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants