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

fix(#1922): Allow to use instance variables defined in the endpoints inside rescue_from #2377

Merged
merged 6 commits into from
Nov 24, 2023
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### Features

* [#2371](https://github.com/ruby-grape/grape/pull/2371): Use a param value as the `default` value of other param - [@jcagarcia](https://github.com/jcagarcia).
* [#2377](https://github.com/ruby-grape/grape/pull/2377): Allow to use instance variables values inside rescue_from - [@jcagarcia](https://github.com/jcagarcia).
* Your contribution here.

#### Fixes
Expand Down
17 changes: 17 additions & 0 deletions lib/grape/dsl/inside_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,23 @@ def error!(message, status = nil, additional_headers = nil)
throw :error, message: message, status: self.status, headers: headers
end

# Creates a Rack response based on the provided message, status, and headers.
# The content type in the headers is set to the default content type unless provided.
# The message is HTML-escaped if the content type is 'text/html'.
#
# @param message [String] The content of the response.
# @param status [Integer] The HTTP status code.
# @params headers [Hash] (optional) Headers for the response
# (default: {Rack::CONTENT_TYPE => content_type}).
#
# Returns:
# A Rack::Response object containing the specified message, status, and headers.
#
def rack_response(message, status = 200, headers = { Rack::CONTENT_TYPE => content_type })
dblock marked this conversation as resolved.
Show resolved Hide resolved
message = ERB::Util.html_escape(message) if headers[Rack::CONTENT_TYPE] == 'text/html'
Rack::Response.new([message], Rack::Utils.status_code(status), headers)
end

# Redirect to a new url.
#
# @param url [String] The url to be redirect.
Expand Down
16 changes: 12 additions & 4 deletions lib/grape/middleware/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def call!(env)
rescue_handler_for_any_class(e.class) ||
raise

run_rescue_handler(handler, e)
run_rescue_handler(@env[Grape::Env::API_ENDPOINT], handler, e)
end
end

Expand Down Expand Up @@ -119,21 +119,29 @@ def rescue_handler_for_any_class(klass)
options[:all_rescue_handler] || :default_rescue_handler
end

def run_rescue_handler(handler, error)
def run_rescue_handler(endpoint, handler, error)
if handler.instance_of?(Symbol)
raise NoMethodError, "undefined method '#{handler}'" unless respond_to?(handler)

handler = public_method(handler)
end

response = handler.arity.zero? ? instance_exec(&handler) : instance_exec(error, &handler)
response = (catch(:error) do
handler.arity.zero? ? endpoint.instance_exec(&handler) : endpoint.instance_exec(error, &handler)
end)

response = error!(response[:message], response[:status], response[:headers]) if error?(response)
dblock marked this conversation as resolved.
Show resolved Hide resolved

if response.is_a?(Rack::Response)
response
else
run_rescue_handler(:default_rescue_handler, Grape::Exceptions::InvalidResponse.new)
run_rescue_handler(endpoint, :default_rescue_handler, Grape::Exceptions::InvalidResponse.new)
end
end

def error?(response)
response.is_a?(Hash) && response[:message] && response[:status] && response[:headers]
end
end
end
end
19 changes: 19 additions & 0 deletions spec/grape/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,25 @@ class CustomError < Grape::Exceptions::Base; end
expect(last_response.status).to be 500
expect(last_response.body).to eql 'Invalid response'
end

context 'when using instance variables inside the rescue_from' do
it 'is able to access the values' do
expected_instance_variable_value = 'wadus'

subject.rescue_from(:all) do
body = { my_var: @my_var }
error!(body, 400)
end
subject.get('/') do
@my_var = expected_instance_variable_value
raise
end

get '/'
expect(last_response.status).to be 400
expect(last_response.body).to eq({ my_var: expected_instance_variable_value }.to_json)
end
end
end

describe '.rescue_from klass, block' do
Expand Down