Skip to content

Commit

Permalink
Introduces on_request and on_complete methods in Faraday::Middleware. (
Browse files Browse the repository at this point in the history
  • Loading branch information
iMacTia committed Oct 22, 2020
1 parent 7326cd8 commit d56742a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 19 deletions.
18 changes: 15 additions & 3 deletions docs/middleware/custom.md
Expand Up @@ -9,8 +9,7 @@ prev_name: Available Middleware
prev_link: ./list
---

Middleware are classes that implement a `call` instance method. They hook into
the request/response cycle.
Middleware are classes that implement a `#call` instance method. They hook into the request/response cycle.

```ruby
def call(request_env)
Expand All @@ -24,7 +23,7 @@ def call(request_env)
end
```

It's important to do all processing of the response only in the `on_complete`
It's important to do all processing of the response only in the `#on_complete`
block. This enables middleware to work in parallel mode where requests are
asynchronous.

Expand All @@ -43,3 +42,16 @@ later, response. Some keys are:
:body - the response body
:response_headers
```

### Faraday::Middleware

There's an easier way to write middleware, and it's also the recommended one: make your middleware subclass `Faraday::Middleware`.
`Faraday::Middleware` already implements the `#call` method for you and looks for two methods in your subclass: `#on_request(env) and `#on_complete(env)`.
`#on_request` is called when the request is being built and is given the `env` representing the request.
`#on_complete` is called after the response has been received (that's right, it already supports parallel mode!) and receives the `env` of the response.

### Do I need to override `#call`?

For the majority of middleware, it's not necessary to override the `#call` method, as you can simply use `#on_request` and `#on_response`.
However, in some cases you may need to wrap the call in a block, or work around it somehow (think of a begin-rescue, for example).
When that happens, then you can simply override `#call`. When you do so, remember to call either `app.call(env)` or `super` to avoid breaking the middleware stack call!
18 changes: 14 additions & 4 deletions lib/faraday/middleware.rb
Expand Up @@ -6,15 +6,25 @@ class Middleware
extend MiddlewareRegistry
extend DependencyLoader

def initialize(app = nil)
attr_reader :app, :options

def initialize(app = nil, options = {})
@app = app
@options = options
end

def call(env)
on_request(env) if respond_to?(:on_request)
app.call(env).on_complete do |environment|
on_complete(environment) if respond_to?(:on_complete)
end
end

def close
if @app.respond_to?(:close)
@app.close
if app.respond_to?(:close)
app.close
else
warn "#{@app} does not implement \#close!"
warn "#{app} does not implement \#close!"
end
end
end
Expand Down
6 changes: 0 additions & 6 deletions lib/faraday/response.rb
Expand Up @@ -7,12 +7,6 @@ module Faraday
class Response
# Used for simple response middleware.
class Middleware < Faraday::Middleware
def call(env)
@app.call(env).on_complete do |environment|
on_complete(environment)
end
end

# Override this to modify the environment after the response has finished.
# Calls the `parse` method if defined
# `parse` method can be defined as private, public and protected
Expand Down
38 changes: 32 additions & 6 deletions spec/faraday/middleware_spec.rb
Expand Up @@ -2,23 +2,49 @@

RSpec.describe Faraday::Middleware do
subject { described_class.new(app) }
let(:app) { double }

describe 'options' do
context 'when options are passed to the middleware' do
subject { described_class.new(app, options) }
let(:options) { { field: 'value' } }

it 'accepts options when initialized' do
expect(subject.options[:field]).to eq('value')
end
end
end

describe '#on_request' do
subject do
Class.new(described_class) do
def on_request(env)
# do nothing
end
end.new(app)
end

it 'is called by #call' do
expect(app).to receive(:call).and_return(app)
expect(app).to receive(:on_complete)
is_expected.to receive(:call).and_call_original
is_expected.to receive(:on_request)
subject.call(double)
end
end

describe '#close' do
context "with app that doesn't support \#close" do
let(:app) { double }

it 'should issue warning' do
expect(subject).to receive(:warn)
is_expected.to receive(:warn)
subject.close
end
end

context "with app that supports \#close" do
let(:app) { double }

it 'should issue warning' do
expect(app).to receive(:close)
expect(subject).to_not receive(:warn)
is_expected.to_not receive(:warn)
subject.close
end
end
Expand Down

0 comments on commit d56742a

Please sign in to comment.