-
Notifications
You must be signed in to change notification settings - Fork 3
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
Capture just one callback per result #30
Conversation
I love this addition. I'd argue that we shouldn't solve this problem by not propagating results, though. I think it's useful to have a way to run a callback on any failure: Service.(args)
.on_failure(:error_1) { do_stuff }
.on_failure(:error_2) { do_other_stuff }
.on_failure { "This runs on any failure" } # error_1 and error_2 are failures, it's confusing not running this Also, changing this is a breaking change! Maybe we can have additional callbacks? Service.(args)
.on_failure(:error_1) { do_stuff }
.on_failure(:error_2) { do_other_stuff }
.on_unhandled_failure { "This only runs on unhandled failures" }
.on_failure { "This runs on any failure" } or maybe Service.(args)
.on_failure(:error_1) { do_stuff }
.on_failure(:error_2) { do_other_stuff }
.on_failure(unhandled: true) { "This runs on unhandled failures" }
.on_failure(:unhandled) { "This runs on unhandled failures" } # we could use a symbol to make it shorter, but it would be a small breaking change as well |
Man, I like to mutch about this point... My main concern about this point are:
For example:
Makes it sense? |
@bvicenzo Yeah using a symbol would make |
@MatheusRich Thinking a little bit more, here? Service.(args)
.on_success(:ok) { 'Ok operation' }
.on_unhundled_success { 'a generic message' }
.on_failure(:error, :other_error) { 'Error occurred' }
.on_unhundled_failure { 'An unknown error happened here' } Then, we don't need to reserve any word... 🤔 |
@bvicenzo Yeah, I mentioned that on my first comment. It's a bit more verbose, but I think it's an easy addition and doesn't break compatibility. It's an interesting opportunity to experiment with it and come up with better naming in the future. |
I agree with @MatheusRich suggestion, specially because it doesn't break compatibility. I'm not happy with the names, but I didn't think in something better, so it's fine. |
d1638be
to
e305816
Compare
@MatheusRich @matheusbsilva @euricovidal Could you take a look again? Service.(args)
.on_failure(:error_1) { do_stuff }
.on_failure(:error_2) { do_other_stuff }
.on_failure(unhandled: true) { "This runs on unhandled failures" } |
lib/f_service/result/base.rb
Outdated
@@ -75,7 +82,13 @@ def on(success:, failure:) | |||
# @return [Success, Failure] the original Result object | |||
# @api public | |||
def on_success(*target_types) | |||
yield(*to_ary) if successful? && expected_type?(target_types) | |||
old_callback_any_matching_warn(method_name: __method__, from: caller[0]) if target_types.empty? |
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.
Can you clarify why are we warning on this? I think having a plain on_success { do_x }/on_failure { do_x }
is okay, since not always we will use result types.
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.
Well, the problem we were wanting to solve here was exactly that...
- The callback propagation never stops even if some callback processes the result;
- The callback without args matches everything;
Then if we run this...
User::Update.(user: user)
.on_success { |value| return json_success(value) }
.on_failure(:error_1) { |value| return catch_error_1(value) }
.on_failure(:error_2) { |value| return catch_error_2(value) }
.on_failure { |value| return catch_any_unknown_error(value) }
Even if error 1 or 2 is processed, the unknown error will be processed as well.
Then, the PR proposed just to add a stop propagation to if error 1 or 2 process the result the * matcher does not process anything...
Then, you suggested (if I understood well), to change the on_(failure/success)
to add an argument to indicate that it matches anything unhandled until that callback runs.
Then, seems that does not make sense to have 2 ways to process any unhandled result, because...
User::Update.(user: user)
.on_success { |value| return json_success(value) }
.on_failure(:error_1) { |value| return catch_error_1(value) }
.on_failure(:error_2) { |value| return catch_error_2(value) }
.on_failure { |value| return catch_any_unknown_error(value) } # Process any unhandled result
.on_failure(unhandled: true) { |value| return catch_any_unknown_error(value) } # Will never run
User::Update.(user: user)
.on_success { |value| return json_success(value) }
.on_failure(:error_1) { |value| return catch_error_1(value) }
.on_failure(:error_2) { |value| return catch_error_2(value) }
.on_failure(unhandled: true) { |value| return catch_any_unknown_error(value) } # Process any unhandled result
.on_failure { |value| return catch_any_unknown_error(value) } # Will never run also
This, to me, could result in more confusion than let as it was and use just as it was (passing no arguments)...
That's why without any args becomes deprecated.
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 still think it's valuable to have a plain on_success/on_failure
without a type. Many interactors only have two flows: failure and success, so requiring a type is cumbersome and error-prone (imagine misspelling an error type .on_failure(:user_not_fonud)
)
One alternative would be making this configurable Gem-level: i.e., allowing users to define if on_success/on_failure
should run only on unhandled failures or not.
FService.config do |config|
config.callbacks_run_only_on_unhandled_results = true
end
I think this is a bit too much to my taste, but I imagine y'all ran into some kind of bug due to this.
10f4b49
to
053339d
Compare
Nowadays, if we are catching the result we have to take care about the callback matching with more than on callback for one result...
Ex:
With this PR implementation it could be done as bellow:
Then, basically, on this suggestion, the callback propagation stops when some callback matches with the error or success.