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

Introduces on_request and on_complete methods in Faraday::Middleware. #1194

Merged
merged 4 commits into from Oct 22, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 14 additions & 2 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 Down Expand Up @@ -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`?
iMacTia marked this conversation as resolved.
Show resolved Hide resolved

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