From cef14da797b05d9fcf23aef598c7fe88b60b39c2 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garcia Date: Thu, 23 Nov 2023 22:36:15 +0100 Subject: [PATCH] fix(#1922): Updating UPGRADING and README files explaining the instance variables behavior. Extra tests added --- README.md | 37 ++++++++++++++++++++ UPGRADING.md | 8 ++++- spec/grape/api_spec.rb | 76 +++++++++++++++++++++++++++++++----------- 3 files changed, 101 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index e6b68bc9d3..f599d5bc44 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ - [Current Route and Endpoint](#current-route-and-endpoint) - [Before, After and Finally](#before-after-and-finally) - [Anchoring](#anchoring) +- [Instance Variables](#instance-variables) - [Using Custom Middleware](#using-custom-middleware) - [Grape Middleware](#grape-middleware) - [Rails Middleware](#rails-middleware) @@ -3595,6 +3596,42 @@ end This will match all paths starting with '/statuses/'. There is one caveat though: the `params[:status]` parameter only holds the first part of the request url. Luckily this can be circumvented by using the described above syntax for path specification and using the `PATH_INFO` Rack environment variable, using `env['PATH_INFO']`. This will hold everything that comes after the '/statuses/' part. +## Instance Variables + +You can use instance variables to pass information across the various stages of a request. An instance variable set within a `before` validator is accessible within the endpoint's code and can also be utilized within the `rescue_from` handler. + +```ruby +class TwitterAPI < Grape::API + before do + @var = 1 + end + + get '/' do + puts @var # => 1 + raise + end + + rescue_from :all do + puts @var # => 1 + end +end +``` + +The values of instance variables cannot be shared among various endpoints within the same API. This limitation arises due to Grape generating a new instance for each request made. Consequently, instance variables set within an endpoint during one request differ from those set during a subsequent request, as they exist within separate instances. + +```ruby +class TwitterAPI < Grape::API + get '/first' do + @var = 1 + puts @var # => 1 + end + + get '/second' do + puts @var # => nil + end +end +``` + ## Using Custom Middleware ### Grape Middleware diff --git a/UPGRADING.md b/UPGRADING.md index 4cb1d4c62a..89c3ddadf0 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,7 +1,7 @@ Upgrading Grape =============== -### Upgrading to >= 2.0.1 +### Upgrading to >= 2.1.0 #### Grape::Router::Route.route_xxx methods have been removed @@ -9,6 +9,12 @@ Upgrading Grape - `route_path` is accessible through `path` - Any other `route_xyz` are accessible through `options[xyz]` +#### Instance variables scope + +Due to the changes done in [#2377](https://github.com/ruby-grape/grape/pull/2377), the instance variables defined inside each of the endpoints (or inside a `before` validator) are now accessible inside the `rescue_from`. This means the scope of the instance variables has changed. + +If you were using the same variable name defined inside an endpoint or `before` validator inside a `rescue_from` handler, you need to take in mind that you can start getting different values or you can be overriding values. + ### Upgrading to >= 2.0.0 #### Headers diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb index ee7a427be7..f2ca6d55d7 100644 --- a/spec/grape/api_spec.rb +++ b/spec/grape/api_spec.rb @@ -2130,25 +2130,6 @@ 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 @@ -4371,4 +4352,61 @@ def uniqe_id_route expect(last_response.body).to be_eql('1-2') end end + + context 'instance variables' do + context 'when setting instance variables in a before validation' do + it 'is accessible inside the endpoint' do + expected_instance_variable_value = 'wadus' + + subject.before do + @my_var = expected_instance_variable_value + end + + subject.get('/') do + { my_var: @my_var }.to_json + end + + get '/' + expect(last_response.body).to eq({ my_var: expected_instance_variable_value }.to_json) + end + end + + context 'when setting instance variables inside the endpoint code' do + it 'is accessible inside the rescue_from handler' 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 + + it 'is NOT available in other endpoints of the same api' do + expected_instance_variable_value = 'wadus' + + subject.get('/first') do + @my_var = expected_instance_variable_value + { my_var: @my_var }.to_json + end + + subject.get('/second') do + { my_var: @my_var }.to_json + end + + get '/first' + expect(last_response.body).to eq({ my_var: expected_instance_variable_value }.to_json) + get '/second' + expect(last_response.body).to eq({ my_var: nil }.to_json) + end + end + end end