From b4e7ca3dc878d75cd5143b019f4075d6ec8170b1 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Wed, 21 Oct 2020 21:44:09 +0200 Subject: [PATCH 1/2] docs: Adapters, how to create them --- docs/adapters/index.md | 130 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/docs/adapters/index.md b/docs/adapters/index.md index 3a44bc1d0..765e9edac 100644 --- a/docs/adapters/index.md +++ b/docs/adapters/index.md @@ -43,6 +43,136 @@ However, sometimes you need to access a feature specific to one of the adapters When that happens, you can pass a block when specifying the adapter to customize it. The block parameter will change based on the adapter you're using. See each adapter page for more details. +## Write your own adapter + +Adapters have methods that can help you implement support for a new backend. + +This example will use a fictional HTTP backend gem called `FlorpHttp`. It doesn't +exist. Its only function is to make this example more concrete. + +### An Adapter _is_ a Middleware + +There are only two things which are actually mandatory for an adapter middleware to function: + +- a `#call` implementation +- a call to `#save_response` inside `#call`, which will keep the Response around. + +These are the only two things. + +The rest of this text is about methods which make the authoring easier. + +### Helpful class: `::Faraday::Adapter` + +By subclassing `::Faraday::Adapter`, you get helpful methods defined: + +```ruby +class FlorpHttp < ::Faraday::Adapter +end +``` + +### Helpful method: `#build_connection` + +Faraday abstracts all your backend's concrete stuff behind its user-facing API. +You take care of setting up the connection from the supplied parameters. + +Example from the excon adapter: it gets an `Env` and reads its information +to instantiate an `Excon` object: + +```ruby +class FlorpHttp < ::Faraday::Adapter + def build_connection(env) + opts = opts_from_env(env) + ::Excon.new(env[:url].to_s, opts.merge(@connection_options)) + end +end +``` + +The `env` contains stuff like: + +- `env[:ssl]` +- `env[:request]` + +There are helper to fetch timeouts: `#request_timeout(type, options)` knows +about supported timeout types, and falls back to `:timeout` if they are not set. +You can use those when building the options you need for your backend's instantiation. + +So, use the information provided in `env` to instantiate your backend's connection class. +Return that instance. Now, Faraday knows how to create and reuse that connection. + +### Nickname for your adapter: `.register_middleware` + +You may register a nickname for your adapter. People can then refer to your adapter with that name. +You do that using `.register_middleware`, like this: + +```ruby +class FlorpHttp < ::Faraday::Adapter + register_middleware( + File.expand_path('adapter', __dir__), + florp_http: [ :FlorpHttp, 'florp_http' ] + ) + # ... +end +``` + +## Does your backend support parallel operation? + +:warning: This is slightly more involved, and this section is not fully formed. + +Vague example, excerpted from [the test suite about parallel requests](https://github.com/lostisland/faraday/blob/master/spec/support/shared_examples/request_method.rb#L179) + +```ruby +response_1 = nil +response_2 = nil + +conn.in_parallel do + response_1 = conn.get('/about') + response_2 = conn.get('/products') +end + +puts response_1.status +puts response_2.status +``` + +First, in your class definition, you can tell Faraday that your backend supports parallel operation: + +```ruby +class FlorpHttp < ::Faraday::Adapter + dependency do + require 'florp_http' + end + + self.supports_parallel = true +end +``` + +Then, implement a method which returns a ParallelManager: + +```ruby +class FlorpHttp < ::Faraday::Adapter + dependency do + require 'florp_http' + end + + self.supports_parallel = true + + def self.setup_parallel_manager(_options = nil) + FlorpParallelManager.new # NB: we will need to define this + end +end + +class FlorpParallelManager + def add(request, method, *args, &block) + # Collect the requests + end + + def run + # Process the requests + end +end +``` + +Compare to the finished example [em-synchrony](https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/em_synchrony.rb) and its [ParallelManager implementation](https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/em_synchrony/parallel_manager.rb). + [net_http]: ./net-http [persistent]: ./net-http-persistent [excon]: ./excon From ad73418e71ff5b728f2ccce74c09fcd28513679c Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Thu, 22 Oct 2020 11:49:23 +0200 Subject: [PATCH 2/2] docs: Move a section, for clarity --- docs/adapters/index.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/adapters/index.md b/docs/adapters/index.md index 765e9edac..4c2e4d8da 100644 --- a/docs/adapters/index.md +++ b/docs/adapters/index.md @@ -52,7 +52,14 @@ exist. Its only function is to make this example more concrete. ### An Adapter _is_ a Middleware -There are only two things which are actually mandatory for an adapter middleware to function: +When you subclass `::Faraday::Adapter`, you get helpful methods defined: + +```ruby +class FlorpHttp < ::Faraday::Adapter +end +``` + +Now, there are only two things which are actually mandatory for an adapter middleware to function: - a `#call` implementation - a call to `#save_response` inside `#call`, which will keep the Response around. @@ -61,15 +68,6 @@ These are the only two things. The rest of this text is about methods which make the authoring easier. -### Helpful class: `::Faraday::Adapter` - -By subclassing `::Faraday::Adapter`, you get helpful methods defined: - -```ruby -class FlorpHttp < ::Faraday::Adapter -end -``` - ### Helpful method: `#build_connection` Faraday abstracts all your backend's concrete stuff behind its user-facing API.