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
Allow replacement of internal steps #109
Conversation
@@ -26,7 +26,7 @@ def define_dsl | |||
module_exec(@step_adapters) do |step_adapters| | |||
step_adapters.each do |adapter_name, adapter| | |||
define_method(adapter_name) do |step_name, with: nil, **options| | |||
operation_name = with || step_name | |||
operation_name = with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timriley I think it would make more readable the code if we actually use something like source
for the Step
source = with ? :external : :internal
operation_name = with || step_name
That way the method internal?
and external?
are much better to reason about.
https://github.com/dry-rb/dry-transaction/pull/109/files#diff-503c8ab3d6cf49b4e18fe227885fca0fR78
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@GustavoCaso Thanks for the suggestion, but I'm not sure introducing a new magical symbol really helps with improving the understandability of those methods. If anything, it forces you to go up a layer and work out why that symbol was set.
Either way, I think this will be something we can fully clear with some documentation (both API-level and user-facing docs). I intend to do some work on the user-facing docs to go along with this change being released, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timriley Thanks for the comment.
Totally agree that is something we can work and clear with some documentation, let me know if I can help you with the documentation.
|
||
def resolve_operation(step, **operations) | ||
if methods.include?(step.name) || private_methods.include?(step.name) | ||
if step.internal? && operations[step.name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you did ;)
Is actually much simpler than the solution I proposed using polymorphism.
Just one style annotation, could we have all the good path branches first and the error ones at the end?
if step.internal? && operations[step.name]
operations[step.name]
elsif methods.include?(step.name) || private_methods.include?(step.name)
method(step.name)
elsif operations[step.name].respond_to?(:call)
operations[step.name]
elsif operations[step.name].nil?
raise MissingStepError.new(step.name)
else
raise InvalidStepError.new(step.name)
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@GustavoCaso Nice idea. Pushed that.
this looks fine to me btw, probably someone will come up with a better idea in future but nothing comes into my mind atm |
Stopping by to say thanks, as I hit this problem earlier today, and while debugging I ended up realizing that it was fixed in the latest release 😃 thanks for the great work everyone! |
So @GustavoCaso and I have been talking about addressing #75 and making it possible to provide replacement for "internal" transaction steps (i.e. steps which are backed by instance methods only). The inability to do this feels to me the only big gap we left after moving to class-based transactions, and given an increased in the number of people using container-less transactions, it would be good to get it in place.
@GustavoCaso, I've taken a different approach to what you've done recently, and I think it might work out.
This PR makes one big change in the way we understand steps:
with:
option (where "external" means the step operation can be resolved from a container or provided whenever the transaction is initializedThen, when an "internal" step ("internal" meaning a step implanted by an instance method only) has a matching object passed to the initializer, that object fully replaces whatever was previously provided by the instance method
This is a breaking change:
with:
will need to be provided if anyone was previously relying on the implicit behaviour of the step name also being used to try and find the step operation in a container. Now that we have class-based transactions, my hunch is that not many people will be doing this. Anyone seriously working with containers will have longer container identifiers and will already havewith:
options in every place they need.step :foo
and then passfoo:
to the initializer, and have a the#foo
instance method still run and callsuper
to call the operation object passed in asfoo:
. However, I don't think many people would be doing this, and they can still keep this same behaviour by defining the step asstep :foo, with: :foo
.So I don't think the breaking changes here are too big, and I think the result is actually a much clearer separation of behaviours.
Plus, I'm much happier to merge something like this in given how small a change it ended up being.
What do you think?
@flash-gordon would be great to get your input, too.