-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Simplify endpoint block execution and deprecate return call in it
#2577
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
Simplify endpoint block execution and deprecate return call in it
#2577
Conversation
1076e6d to
ea27c2d
Compare
3b56984 to
6f49321
Compare
6f49321 to
f6aebfa
Compare
|
Feel free to merge with the UPGRADING update @ericproulx. |
Co-authored-by: Daniel (dB.) Doubrovkine <dblock@dblock.org>
|
Hello, I just noticed this deprecation warning while testing the edge version, and I'm wondering what is the recommended alternative for people who did use delete ':id' do
if recipient = Notifier.recipients(current_user).find {|r| r.key == params[:id] }
if recipient.type == :sms
current_user.phone_numbers -= [recipient.identifier]
elsif recipient.type == :webhook
webhook = current_user.webhooks.where(id: recipient.identifier).first
return {'deleted' => webhook.destroy}
elsif recipient.type == :slack_compatible
current_user.slack_compatible -= [recipient.identifier]
elsif recipient.type == :msteams
current_user.msteams -= [recipient.identifier]
end
save_model current_user
{'deleted' => current_user.previous_changes.any?}
else
error! "Recipient not found: #{params[:id]}", 404, {"Cache-Control" => "public, max-age=60", "Vary" => "X-Api-Key"}
end
endHere the |
|
Maybe like this? delete ':id' do
if recipient = Notifier.recipients(current_user).find { |r| r.key == params[:id] }
deleted = false
case recipient.type
when :sms
current_user.phone_numbers -= [recipient.identifier]
when :webhook
webhook = current_user.webhooks.where(id: recipient.identifier).first
deleted = webhook.destroy
when :slack_compatible
current_user.slack_compatible -= [recipient.identifier]
when :msteams
current_user.msteams -= [recipient.identifier]
end
save_model current_user
{ 'deleted' => deleted || current_user.previous_changes.any? }
else
error! "Recipient not found: #{params[:id]}", 404, {
"Cache-Control" => "public, max-age=60",
"Vary" => "X-Api-Key"
}
end
end |
|
Thanks for the suggestion, it's not correct though as deleted can be false and that would execute unwanted code and return the wrong value, also the save_model method should not be executed in that case. I can rewrite the code to workaround this that's not an issue, it's just that it would be more verbose so I'm wondering if there's any feature I'm missing in Grape to continue doing this kind of flow. It's handy to be able to stop the flow with |
|
@ericproulx wdyt? Using |
|
You could extract the # in your `current_user` model
class RecipientNotFound < StandardError; end
def delete_recipient(recipient_id)
recipient = Notifier.recipients(self).find { |r| r.key == recipient_id }
raise RecipientNotFound, "Recipient not found: #{recipient_id}" unless recipient
deleted = false
case recipient.type
when :sms
self.phone_numbers -= [recipient.identifier]
when :webhook
webhook = webhooks.where(id: recipient.identifier).first
deleted = webhook.destroy
when :slack_compatible
self.slack_compatible -= [recipient.identifier]
when :msteams
self.msteams -= [recipient.identifier]
end
save # assuming save_model is doing that
deleted || previous_changes.any?
end
# Grape API
rescue_from CurrentUserModel::RecipientNotFound do |e| #
error! e.message, 404, { "Cache-Control" => "public, max-age=60", "Vary" => "X-Api-Key" }
end
delete ':id' do
{ 'deleted' => current_user.delete_recipient(params[:id]) }
end@jarthod wdyt ? |
|
Thanks for the suggestion, but I already know how to refactor my ruby code ^^ (and this examble has the two same problems I pointed above anyway). I'm just asking about Grape features here, if you decide to go ahead and remove this feature with no alternative that's fine for me I'll rewrite my code accordingly. I was just interested in knowing if there was an alternative I didn't knew about maybe. I'm just one user and I'm only using this If you do decide to keep it though, in terms of implementation I believe this could be cleanly implemented using throw / catch, like sinatra does for early response return in it's — similar to grape — block syntax DSL: Thanks for grape ❤️ |
|
Sorry about that, I miss read the situation. You could use |
|
I didn't thought those could work in a block with no iteration but it seems like it does, thanks for the tip!
next {'deleted' => webhook.destroy}It's not as explicit when reading but at least it works and does not require extra code on either side. I tried wrapping it in a "render" method or some alias but as a it's a keyword it's not easy to do (method gives an invalid next exception, alias gives method not found as it's not a method). I guess the simplest is to recommend people to use |
|
Thank you for your works on Grape. I’m also seeing the deprecation warnings related to using return in endpoints. I understand that the logic can be refactored, or It’s true that |
|
Hi @a5-stable, thanks for reaching out about this subject. Quite frankly, having |
|
@ericproulx Thank you for your kind response!
We frequently use early returns to handle different success scenarios: get '/resource/:id' do
return { item: cached } if cached = Rails.cache.fetch("item:#{params[:id]}")
resource = Item.find(params[:id])
# Different return values based on conditions
return { item: resource } if condition_a?
return { item: resource.related_items.first } if condition_b?
return { item: special_transform(resource) } if condition_c?
# Default case
{ item: default_transform(resource) }
end
Sometimes we need to return a response when a condition is met inside nested iterations: get '/search' do
categories.each do |category|
category.items.each do |item|
if item.matches?(params[:query])
# Want to return from the endpoint, not just skip to next iteration
return { item: item, category: category.name }
end
end
end
status 404
endThis is impossible with a simple substitution to Let me note that Sinatra supports get '/' do
return 'Hello world!'
endThank you again for your thoughtful consideration of this issue. |
|
@a5-stable glad to see I was not the only one ^^ |
|
@a5-stable, thanks I understand the appeal. I'll remove the deprecation and bring back the |
|
@ericproulx @jarthod |
|
Thanks @ericproulx ! As a quick reminder, if you still want to remove the unbound method thing, I believe this could also be cleanly implemented using throw / catch, like sinatra does for early response return in it's block syntax DSL: In which case it would need to be an explicit method of yours instead of return (like |
Thanks for sharing. I like the |
|
Thanks for sharing for your thoughts. |
Deprecate Endpoint Return Value
Summary
This PR simplifies endpoint's block execution. It was relying on an
unboundmethod but it always been re-binded to endpoint instance. Instead, I just callinstance_execfrom the endpoint instance. First, we deprecate and next version we will remove therescue LocalJumpError.Motivation
returnin an endpoint. Seems to be an old thing.generate_api_methodwas generating a couple of strings that were retained in memory. I've seen it in grape-on-rack.Changes
Grape::Endpoint#callas deprecated in the documentation and code comments.lambainstead ofProcto be able to capture theLocalJumpErrorand return its exit_value for not breaking anything.Impact
Grape::Endpoint#callshould update their code to avoid this pattern.Checklist