diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f41a89f5d..a41eb4d8a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,37 +1,44 @@ ## Contributing -You can run the test suite against a live server by running `script/test`. It -automatically starts a test server in background. Only tests in -`test/adapters/*_test.rb` require a server, though. +In Faraday we always welcome new ideas and features, however we also have to ensure +that the overall code quality stays on reasonable levels. +For this reason, before adding any contribution to Faraday, we highly recommend reading this +quick guide to ensure your PR can be reviewed and approved as quickly as possible. + +We are pushing towards a 1.0 release, when we will have to follow [Semantic +Versioning][semver]. If your patch includes changes to break compatibility, +note that so we can add it to the [Changelog][]. -``` sh -# setup development dependencies -$ script/bootstrap -# run the whole suite -$ script/test +### Required Checks -# run only specific files -$ script/test excon patron +Before pushing your code and opening a PR, we recommend you run the following checks to avoid +our CircleCI Workflow to block your contribution. -# run tests using SSL -$ SSL=yes script/test +```bash +# Run unit tests and check code coverage +$ bundle exec rspec + +# Run Rubocop and check code style +$ bundle exec rubocop ``` + ### New Features -When adding a feature Faraday: +When adding a feature in Faraday: 1. also add tests to cover your new feature. 2. if the feature is for an adapter, the **attempt** must be made to add the same feature to all other adapters as well. -3. start opening an issue describing how the new feature will work, and only after receiving the green light by the core team start working on the PR. +3. start opening an issue describing how the new feature will work, and only after receiving +the green light by the core team start working on the PR. -### New Middlewares + +### New Middleware We will accept middleware that: -1. is useful to a broader audience, but can be implemented relatively - simple; and +1. is useful to a broader audience, but can be implemented relatively simple; and 2. which isn't already present in [faraday_middleware][] project. @@ -43,10 +50,26 @@ We will accept adapters that: 1. are proven and may have better performance than existing ones; or 2. if they have features not present in included adapters. -We are pushing towards a 1.0 release, when we will have to follow [Semantic -Versioning][semver]. If your patch includes changes to break compatibility, -note that so we can add it to the [Changelog][]. -[semver]: http://semver.org/ -[changelog]: https://github.com/lostisland/faraday/releases -[faraday_middleware]: https://github.com/lostisland/faraday_middleware/wiki +### Changes to Faraday Website + +The [Faraday Website][website] is included in the Faraday repository, under the `/docs` folder. +If you want to apply changes to it, please test it locally using `Jekyll`. + +```bash +# Navigate into the /docs folder +$ cd docs + +# Install Jekyll dependencies, this bundle is different from Faraday's one. +$ bundle install + +# Run the Jekyll server with the Faraday website +$ bundle exec jekyll serve + +# The site will now be reachable at http://127.0.0.1:4000/faraday/ +``` + +[semver]: http://semver.org/ +[changelog]: https://github.com/lostisland/faraday/releases +[faraday_middleware]: https://github.com/lostisland/faraday_middleware +[website]: https://lostisland.github.io/faraday diff --git a/README.md b/README.md index 7ac3d732e..a8dd71cd7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Faraday +# ![Faraday](./docs/assets/img/repo-card-slim.png) [![Gem Version](https://badge.fury.io/rb/faraday.svg)](https://rubygems.org/gems/faraday) [![CircleCI](https://circleci.com/gh/lostisland/faraday/tree/master.svg?style=svg)](https://circleci.com/gh/lostisland/faraday/tree/master) @@ -11,325 +11,14 @@ Faraday is an HTTP client library that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle. -Faraday supports these adapters out of the box: +## Getting Started -* [Net::HTTP][net_http] _(default)_ -* [Net::HTTP::Persistent][persistent] -* [Excon][excon] -* [Patron][patron] -* [EM-Synchrony][em-synchrony] -* [HTTPClient][httpclient] - -Adapters are slowly being moved into their own gems, or bundled with HTTP clients. -Here is the list of known external adapters: - -* [Typhoeus][typhoeus] - -It also includes a Rack adapter for hitting loaded Rack applications through -Rack::Test, and a Test adapter for stubbing requests by hand. - -## API documentation - -Available at [rubydoc.info](http://www.rubydoc.info/gems/faraday). - -## Usage - -### Basic Use - -```ruby -response = Faraday.get 'http://sushi.com/nigiri/sake.json' -``` -A simple `get` request can be performed by using the syntax described above. This works if you don't need to set up anything; you can roll with just the default middleware -stack and default adapter (see [Faraday::RackBuilder#initialize](https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb)). - -A more flexible way to use Faraday is to start with a Connection object. If you want to keep the same defaults, you can use this syntax: - -```ruby -conn = Faraday.new(:url => 'http://www.example.com/api') -response = conn.get 'users' # GET http://www.example.com/api/users' - -# You can override the path from the connection initializer by using an absolute path -response = conn.get '/users' # GET http://www.example.com/users' -``` - -Connections can also take an options hash as a parameter or be configured by using a block. Checkout the section called [Advanced middleware usage](#advanced-middleware-usage) for more details about how to use this block for configurations. -Since the default middleware stack uses url\_encoded middleware and default adapter, use them on building your own middleware stack. - -```ruby -conn = Faraday.new(:url => 'http://sushi.com') do |faraday| - faraday.request :url_encoded # form-encode POST params - faraday.response :logger # log requests and responses to $stdout - faraday.adapter Faraday.default_adapter # make requests with Net::HTTP -end - -# Filter sensitive information from logs with a regex matcher - -conn = Faraday.new(:url => 'http://sushi.com/api_key=s3cr3t') do |faraday| - faraday.request :url_encoded # form-encode POST params - faraday.response :logger do | logger | - logger.filter(/(api_key=)(\w+)/,'\1[REMOVED]') - end - faraday.adapter Faraday.default_adapter # make requests with Net::HTTP -end - -# Override the log formatting on demand - -class MyFormatter < Faraday::Response::Logger::Formatter - def request(env) - info('Request', env) - end - - def response(env) - info('Response', env) - end -end - -conn = Faraday.new(:url => 'http://sushi.com/api_key=s3cr3t') do |faraday| - faraday.response :logger, StructLogger.new(STDOUT), formatter: MyFormatter -end -``` - -Once you have the connection object, use it to make HTTP requests. You can pass parameters to it in a few different ways: - -```ruby -## GET ## - -response = conn.get '/nigiri/sake.json' # GET http://sushi.com/nigiri/sake.json -response.body - -conn.get '/nigiri', { :name => 'Maguro' } # GET http://sushi.com/nigiri?name=Maguro - -conn.get do |req| # GET http://sushi.com/search?page=2&limit=100 - req.url '/search', :page => 2 - req.params['limit'] = 100 -end - -## POST ## - -conn.post '/nigiri', { :name => 'Maguro' } # POST "name=maguro" to http://sushi.com/nigiri -``` - -Some configuration options can be adjusted per request: - -```ruby -# post payload as JSON instead of "www-form-urlencoded" encoding: -conn.post do |req| - req.url '/nigiri' - req.headers['Content-Type'] = 'application/json' - req.body = '{ "name": "Unagi" }' -end - -## Per-request options ## - -conn.get do |req| - req.url '/search' - req.options.timeout = 5 # open/read timeout in seconds - req.options.open_timeout = 2 # connection open timeout in seconds -end - -## Streaming responses ## - -streamed = [] # A buffer to store the streamed data -conn.get('/nigiri/sake.json') do |req| - # Set a callback which will receive tuples of chunk Strings - # and the sum of characters received so far - req.options.on_data = Proc.new do |chunk, overall_received_bytes| - puts "Received #{overall_received_bytes} characters" - streamed << chunk - end -end -streamed.join -``` - -And you can inject arbitrary data into the request using the `context` option: - -```ruby -# Anything you inject using context option will be available in the env on all middlewares - -conn.get do |req| - req.url '/search' - req.options.context = { - foo: 'foo', - bar: 'bar' - } -end -``` - -### Changing how parameters are serialized - -Sometimes you need to send the same URL parameter multiple times with different -values. This requires manually setting the parameter encoder and can be done on -either per-connection or per-request basis. - -```ruby -# per-connection setting -conn = Faraday.new :request => { :params_encoder => Faraday::FlatParamsEncoder } - -conn.get do |req| - # per-request setting: - # req.options.params_encoder = my_encoder - req.params['roll'] = ['california', 'philadelphia'] -end -# GET 'http://sushi.com?roll=california&roll=philadelphia' -``` - -The value of Faraday `params_encoder` can be any object that responds to: - -* `encode(hash) #=> String` -* `decode(string) #=> Hash` - -The encoder will affect both how query strings are processed and how POST bodies -get serialized. The default encoder is Faraday::NestedParamsEncoder. - -## Authentication - -Basic and Token authentication are handled by Faraday::Request::BasicAuthentication and Faraday::Request::TokenAuthentication respectively. These can be added as middleware manually or through the helper methods. - -```ruby -Faraday.new(...) do |conn| - conn.basic_auth('username', 'password') -end - -Faraday.new(...) do |conn| - conn.token_auth('authentication-token') -end -``` - -## Proxy - -Faraday will try to automatically infer the proxy settings from your system using `URI#find_proxy`. -This will retrieve them from environment variables such as http_proxy, ftp_proxy, no_proxy, etc. -If for any reason you want to disable this behaviour, you can do so by setting the global varibale `ignore_env_proxy`: - -```ruby -Faraday.ignore_env_proxy = true -``` - -You can also specify a custom proxy when initializing the connection - -```ruby -Faraday.new('http://www.example.com', :proxy => 'http://proxy.com') -``` - -## Advanced middleware usage - -The order in which middleware is stacked is important. Like with Rack, the -first middleware on the list wraps all others, while the last middleware is the -innermost one, so that must be the adapter. - -```ruby -Faraday.new(...) do |conn| - # POST/PUT params encoders: - conn.request :multipart - conn.request :url_encoded - - # Last middleware must be the adapter: - conn.adapter :net_http -end -``` - -This request middleware setup affects POST/PUT requests in the following way: - -1. `Request::Multipart` checks for files in the payload, otherwise leaves - everything untouched; -2. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not - already encoded or of another type - -Swapping middleware means giving the other priority. Specifying the -"Content-Type" for the request is explicitly stating which middleware should -process it. - -Examples: - -```ruby -# uploading a file: -payload[:profile_pic] = Faraday::UploadIO.new('/path/to/avatar.jpg', 'image/jpeg') - -# "Multipart" middleware detects files and encodes with "multipart/form-data": -conn.put '/profile', payload -``` - -## Writing middleware - -Middleware are classes that implement a `call` instance method. They hook into -the request/response cycle. - -```ruby -def call(request_env) - # do something with the request - # request_env[:request_headers].merge!(...) - - @app.call(request_env).on_complete do |response_env| - # do something with the response - # response_env[:response_headers].merge!(...) - end -end -``` - -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. - -The `env` is a hash with symbol keys that contains info about the request and, -later, response. Some keys are: - -``` -# request phase -:method - :get, :post, ... -:url - URI for the current request; also contains GET parameters -:body - POST parameters for :post/:put requests -:request_headers - -# response phase -:status - HTTP response status code, such as 200 -:body - the response body -:response_headers -``` - -## Ad-hoc adapters customization - -Faraday is intended to be a generic interface between your code and the adapter. However, sometimes you need to access a feature specific to one of the adapters that is not covered in Faraday's interface. - -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 below for some examples. - -## Using Faraday for testing - -```ruby -# It's possible to define stubbed request outside a test adapter block. -stubs = Faraday::Adapter::Test::Stubs.new do |stub| - stub.get('/tamago') { |env| [200, {}, 'egg'] } -end - -# You can pass stubbed request to the test adapter or define them in a block -# or a combination of the two. -test = Faraday.new do |builder| - builder.adapter :test, stubs do |stub| - stub.get('/ebi') { |env| [ 200, {}, 'shrimp' ]} - end -end - -# It's also possible to stub additional requests after the connection has -# been initialized. This is useful for testing. -stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]} - -resp = test.get '/tamago' -resp.body # => 'egg' -resp = test.get '/ebi' -resp.body # => 'shrimp' -resp = test.get '/uni' -resp.body # => 'urchin' -resp = test.get '/else' #=> raises "no such stub" error - -# If you like, you can treat your stubs as mocks by verifying that all of -# the stubbed calls were made. NOTE that this feature is still fairly -# experimental: It will not verify the order or count of any stub, only that -# it was called once during the course of the test. -stubs.verify_stubbed_calls -``` +The best starting point is the [Faraday Website][website], with its introduction and explanation. +Need more details? See the [Faraday API Documentation][apidoc] to see how it works internally. ## Supported Ruby versions -This library aims to support and is [tested against][travis] the following Ruby +This library aims to support and is [tested against][circle_ci] the following Ruby implementations: * Ruby 2.3+ @@ -351,21 +40,16 @@ of a major release, support for that Ruby version may be dropped. Do you want to contribute to Faraday? Open the issues page and check for the `help wanted` label! -But before you start coding, please read our [Contributing Guide](https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md) +But before you start coding, please read our [Contributing Guide][contributing] ## Copyright +© 2009 - 2019, the [Faraday Team][faraday_team]. Website and branding design by [Elena Lo Piccolo](https://elelopic.design). -Copyright (c) 2009-2019 [Rick Olson](mailto:technoweenie@gmail.com), Zack Hobson. -See [LICENSE][] for details. - -[net_http]: ./docs/adapters/net_http.md -[persistent]: ./docs/adapters/net_http_persistent.md -[travis]: https://travis-ci.org/lostisland/faraday -[excon]: ./docs/adapters/excon.md -[patron]: ./docs/adapters/patron.md -[em-synchrony]: ./docs/adapters/em-synchrony.md -[httpclient]: ./docs/adapters/httpclient.md -[typhoeus]: https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb +[website]: https://lostisland.github.io/faraday +[faraday_team]: https://lostisland.github.io/faraday/team +[contributing]: https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md +[apidoc]: http://www.rubydoc.info/gems/faraday +[circle_ci]: https://circleci.com/gh/lostisland/faraday [jruby]: http://jruby.org/ [rubinius]: http://rubini.us/ [license]: LICENSE.md diff --git a/UPGRADING.md b/UPGRADING.md index 6bbd21c94..aca39ff33 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -11,6 +11,7 @@ Please note `Faraday::ClientError` was previously used for both. * Faraday::UnauthorizedError (401) * Faraday::ForbiddenError (403) * Faraday::ProxyAuthError (407). Please note this raised a `Faraday::ConnectionFailed` before. + * Faraday::ConflictError (409) * Faraday::UnprocessableEntityError (422) ### Custom adapters diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..45c150536 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +_site +.sass-cache +.jekyll-metadata diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 000000000..c472b4ea0 --- /dev/null +++ b/docs/404.html @@ -0,0 +1,24 @@ +--- +layout: default +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 000000000..14d91b3d4 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! +# gem "jekyll", "~> 3.7.4" + +# This is the default theme for new Jekyll sites. +# You may change this to anything you like. +# gem "minima", "~> 2.0" +# gem "jekyll-theme-type" +gem 'jekyll-remote-theme' + +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +gem 'github-pages', group: :jekyll_plugins + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem 'jekyll-feed', '~> 0.6' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] + +# Performance-booster for watching directories on Windows +gem 'wdm', '~> 0.1.0' if Gem.win_platform? diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..26127d74a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,19 @@ +# Faraday Website + +This is the root directory of the [Faraday Website][website]. +If you want to apply changes to it, please test it locally using `Jekyll`. + +```bash +# Navigate into the /docs folder +$ cd docs + +# Install Jekyll dependencies, this bundle is different from Faraday's one. +$ bundle install + +# Run the Jekyll server with the Faraday website +$ bundle exec jekyll serve + +# The site will now be reachable at http://127.0.0.1:4000/faraday/ +``` + +[website]: https://lostisland.github.io/faraday \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..aec132250 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,52 @@ +# SITE CONFIGURATION +url: 'https://lostisland.github.io' +baseurl: '/faraday' + +# THEME-SPECIFIC CONFIGURATION +theme_settings: + title: Faraday + avatar: assets/img/logo.png + favicon: assets/img/favicon.png + # email: your-email@example.com + description: >- + Simple, but flexible HTTP client library, with support for multiple backends + footer_text: "© 2009 - 2019, the Faraday Team. Website and branding design by Elena Lo Piccolo." + + # Icons + github: 'lostisland/faraday' + gitter: 'lostisland/faraday' + + # Post navigation + post_navigation: true + site_navigation_sort: 'order' + +# BUILD SETTINGS +markdown: kramdown +remote_theme: rohanchandra/type-theme +plugins: + - jekyll-feed + +# GitHub settings +lsi: false +safe: true +#source: [your repo's top level directory] +incremental: false +highlighter: rouge +gist: + noscript: false +kramdown: + math_engine: mathjax + syntax_highlighter: rouge + +# Exclude from processing. +# The following items will not be processed, by default. Create a custom list +# to override the default setting. +exclude: + - Gemfile + - Gemfile.lock + - README.md + - node_modules + - vendor/bundle/ + - vendor/cache/ + - vendor/gems/ + - vendor/ruby/ diff --git a/docs/_includes/docs_nav.md b/docs/_includes/docs_nav.md new file mode 100644 index 000000000..098d23627 --- /dev/null +++ b/docs/_includes/docs_nav.md @@ -0,0 +1,17 @@ +
+

+ {% if page.prev_link %} + {{ page.prev_name }} + {% endif %} +

+

+ {% if page.top_link %} + {{ page.top_name }} + {% endif %} +

+

+ {% if page.next_link %} + {{ page.next_name }} + {% endif %} +

+
\ No newline at end of file diff --git a/docs/_layouts/documentation.md b/docs/_layouts/documentation.md new file mode 100644 index 000000000..21ead50da --- /dev/null +++ b/docs/_layouts/documentation.md @@ -0,0 +1,7 @@ +--- +layout: page +--- + +{{ content }} + +{% include docs_nav.md %} diff --git a/docs/_posts/2019-03-12-welcome-to-jekyll.markdown b/docs/_posts/2019-03-12-welcome-to-jekyll.markdown new file mode 100644 index 000000000..386d4f0a8 --- /dev/null +++ b/docs/_posts/2019-03-12-welcome-to-jekyll.markdown @@ -0,0 +1,25 @@ +--- +layout: post +title: "Welcome to Jekyll!" +date: 2019-03-12 10:25:23 +0000 +categories: jekyll update +--- +You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated. + +To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works. + +Jekyll also offers powerful support for code snippets: + +{% highlight ruby %} +def print_hi(name) + puts "Hi, #{name}" +end +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +{% endhighlight %} + +Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk]. + +[jekyll-docs]: https://jekyllrb.com/docs/home +[jekyll-gh]: https://github.com/jekyll/jekyll +[jekyll-talk]: https://talk.jekyllrb.com/ diff --git a/docs/_sass/_variables.scss b/docs/_sass/_variables.scss new file mode 100644 index 000000000..c1de38999 --- /dev/null +++ b/docs/_sass/_variables.scss @@ -0,0 +1,8 @@ +// Override theme variables. + +@import url('https://fonts.googleapis.com/css?family=Raleway:700'); + +$link-color: #EE4266; +$text-color: #3C3C3C; +$font-family-main: 'KohinoorTelugu-Regular', Helvetica, Arial, sans-serif; +$font-family-headings: 'Raleway', Helvetica, Arial, sans-serif; \ No newline at end of file diff --git a/docs/_sass/faraday.sass b/docs/_sass/faraday.sass new file mode 100644 index 000000000..61f0143f2 --- /dev/null +++ b/docs/_sass/faraday.sass @@ -0,0 +1,122 @@ +// Custom Styles added on top of the theme. + +.btn + display: inline-block + background-color: $link-color + padding: 5px 10px + box-shadow: 0 4px 10px 5px rgba(238, 66, 102, 0.30) + border-radius: 20px + width: 200px + color: #FFFFFF + letter-spacing: -0.41px + text-align: center + margin: 0 10px + + &:hover + background-color: darken($link-color, 10%) + color: white + text-decoration: none + +.text-center + text-align: center + +.mt-60 + margin-top: 60px + +.hidden + display: none + +.docs-nav + display: flex + margin-top: 40px + +.docs-nav-item + flex: 1 1 0 + text-align: center + +pre.highlight + padding: 20px + background-color: #F6F6F6 + border-radius: 4px + + code + word-wrap: normal + overflow: scroll + +code.highlighter-rouge + background-color: #EEE + padding: 0 5px + border-radius: 3px + +.site-header .site-nav li + margin-right: 1.2em + +h1, h2, h3, h4, h5, h6 + font-weight: bold + +.feature-image header + @media (max-width: 1000px) + padding: 7% 12.5% + @media (max-width: 576px) + padding: 4% 5% 1% 5% + +#team-content + h3 + margin: 30px 0 + +#contributors-list + text-align: justify + +.team-tile + width: 200px + display: inline-block + margin: 0 20px + + img + width: 100% + border-radius: 50% + +footer + background-color: #f1f3f4 + +#active-maintainers-list, #historical-team-list + text-align: center + +#loader + margin-top: 20% + margin-bottom: 20% + text-align: center + +.lds-ring + display: inline-block + position: relative + width: 200px + height: 200px + +.lds-ring div + box-sizing: border-box + display: block + position: absolute + width: 187px + height: 187px + margin: 6px + border: 12px solid $link-color + border-radius: 50% + animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite + border-color: $link-color transparent transparent transparent + +.lds-ring div:nth-child(1) + animation-delay: -0.45s + +.lds-ring div:nth-child(2) + animation-delay: -0.3s + +.lds-ring div:nth-child(3) + animation-delay: -0.15s + +@keyframes lds-ring + 0% + transform: rotate(0deg) + + 100% + transform: rotate(360deg) diff --git a/docs/adapters/em-http.md b/docs/adapters/em-http.md index d994bd4a9..ba0d861cc 100644 --- a/docs/adapters/em-http.md +++ b/docs/adapters/em-http.md @@ -1,4 +1,11 @@ -# EM-HTTP Adapter +--- +layout: documentation +title: "EM-HTTP Adapter" +permalink: /adapters/em-http +hide: true +top_name: Adapters +top_link: ./ +--- This Adapter uses the [em-http-request][rdoc] gem to make HTTP requests. @@ -14,7 +21,6 @@ end * [Gem RDoc][rdoc] * [Gem source][src] * [Adapter RDoc][adapter_rdoc] -* [EM-HTTP Adapter](./em-http.md) [rdoc]: https://www.rubydoc.info/gems/em-http-request [src]: https://github.com/igrigorik/em-http-request#readme diff --git a/docs/adapters/em-synchrony.md b/docs/adapters/em-synchrony.md index 8184ae6ae..a362c8a6b 100644 --- a/docs/adapters/em-synchrony.md +++ b/docs/adapters/em-synchrony.md @@ -1,4 +1,11 @@ -# EM-Synchrony Adapter +--- +layout: documentation +title: "EM-Synchrony Adapter" +permalink: /adapters/em-synchrony +hide: true +top_name: Adapters +top_link: ./ +--- This Adapter uses the [em-synchrony][rdoc] gem to make HTTP requests. diff --git a/docs/adapters/excon.md b/docs/adapters/excon.md index 3e65c7655..a526327f8 100644 --- a/docs/adapters/excon.md +++ b/docs/adapters/excon.md @@ -1,4 +1,11 @@ -# Excon Adapter +--- +layout: documentation +title: "Excon Adapter" +permalink: /adapters/excon +hide: true +top_name: Adapters +top_link: ./ +--- This Adapter uses the [excon][rdoc] gem to make HTTP requests. diff --git a/docs/adapters/httpclient.md b/docs/adapters/httpclient.md index c962506dd..fe52fc7ab 100644 --- a/docs/adapters/httpclient.md +++ b/docs/adapters/httpclient.md @@ -1,4 +1,11 @@ -# HTTPClient Adapter +--- +layout: documentation +title: "HTTPClient Adapter" +permalink: /adapters/httpclient +hide: true +top_name: Adapters +top_link: ./ +--- This Adapter uses the [httpclient][rdoc] gem to make HTTP requests. diff --git a/docs/adapters/index.md b/docs/adapters/index.md new file mode 100644 index 000000000..3bab1263f --- /dev/null +++ b/docs/adapters/index.md @@ -0,0 +1,54 @@ +--- +layout: documentation +title: "Adapters" +permalink: /adapters +order: 2 +--- + +The Faraday Adapter interface determines how a Faraday request is turned into +a Faraday response object. Adapters are typically implemented with common Ruby +HTTP clients, but can have custom implementations. Adapters can be configured +either globally or per Faraday Connection through the configuration block. + +{: .mt-60} +## Built-in adapters + +Faraday includes these adapters (but not the HTTP client libraries): + +* [Net::HTTP][net_http] _(this is the default adapter)_ +* [Net::HTTP::Persistent][persistent] +* [Excon][excon] +* [Patron][patron] +* [EM-Synchrony][em-synchrony] +* [HTTPClient][httpclient] + +While most adapters use a common Ruby HTTP client library, adapters can also +have completely custom impelmentations. + +* [Test Adapter][testing] +* Rack Adapter (link TBD) + +## External adapters + +Adapters are slowly being moved into their own gems, or bundled with HTTP clients. +Please refer to their documentation for usage examples. + +* [Typhoeus][typhoeus] +* [HTTP.rb][faraday-http] + +## Ad-hoc adapters customization + +Faraday is intended to be a generic interface between your code and the adapter. +However, sometimes you need to access a feature specific to one of the adapters that is not covered in Faraday's interface. +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. + +[net_http]: ./net-http +[persistent]: ./net-http-persistent +[excon]: ./excon +[patron]: ./patron +[em-synchrony]: ./em-synchrony +[httpclient]: ./httpclient +[typhoeus]: https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb +[faraday-http]: https://github.com/lostisland/faraday-http +[testing]: ./testing diff --git a/docs/adapters/net_http.md b/docs/adapters/net_http.md index 13d481c4b..7a0411574 100644 --- a/docs/adapters/net_http.md +++ b/docs/adapters/net_http.md @@ -1,6 +1,13 @@ -# Net::HTTP Adapter +--- +layout: documentation +title: "Net::HTTP Adapter" +permalink: /adapters/net-http +hide: true +top_name: Adapters +top_link: ./ +--- -This Adapter uses the Net::HTTP client from the ruby standard library to make +This Adapter uses the [`Net::HTTP`][rdoc] client from the Ruby standard library to make HTTP requests. ```ruby diff --git a/docs/adapters/net_http_persistent.md b/docs/adapters/net_http_persistent.md index ea9bbc77a..503443c08 100644 --- a/docs/adapters/net_http_persistent.md +++ b/docs/adapters/net_http_persistent.md @@ -1,4 +1,11 @@ -# Net::HTTP::Persistent Adapter +--- +layout: documentation +title: "Net::HTTP::Persistent Adapter" +permalink: /adapters/net-http-persistent +hide: true +top_name: Adapters +top_link: ./ +--- This Adapter uses the [net-http-persistent][rdoc] gem to make HTTP requests. diff --git a/docs/adapters/patron.md b/docs/adapters/patron.md index aecf0314f..97a22d6d9 100644 --- a/docs/adapters/patron.md +++ b/docs/adapters/patron.md @@ -1,4 +1,11 @@ -# Patron Adapter +--- +layout: documentation +title: "Patron Adapter" +permalink: /adapters/patron +hide: true +top_name: Adapters +top_link: ./ +--- This Adapter uses the [patron][rdoc] gem to make HTTP requests. diff --git a/docs/adapters/testing.md b/docs/adapters/testing.md new file mode 100644 index 000000000..ac0549794 --- /dev/null +++ b/docs/adapters/testing.md @@ -0,0 +1,64 @@ +--- +layout: documentation +title: "Testing" +permalink: /adapters/testing +hide: true +top_name: Adapters +top_link: ./ +--- + +The built-in Faraday Test adapter lets you define stubbed HTTP requests. This can +be used to mock out network services in an application's unit tests. + +The easiest way to do this is to create the stubbed requests when initializing +a `Faraday::Connection`. Stubbing a request by path yields a block with a +`Faraday::Env` object. The stub block expects an Array return value with three +values: an Integer HTTP status code, a Hash of key/value headers, and a +response body. + +```ruby +conn = Faraday.new do |builder| + builder.adapter :test do |stub| + stub.get('/ebi') do |env| + [ + 200, + { 'Content-Type': 'text/plain', }, + 'shrimp' + ] + end + end +end +``` + +You can define the stubbed requests outside of the test adapter block: + +```ruby +stubs = Faraday::Adapter::Test::Stubs.new do |stub| + stub.get('/tamago') { |env| [200, {}, 'egg'] } +end +``` + +This Stubs instance can be passed to a new Connection: + +```ruby +conn = Faraday.new do |builder| + builder.adapter :test, stubs do |stub| + stub.get('/ebi') { |env| [ 200, {}, 'shrimp' ]} + end +end +``` + +It's also possible to stub additional requests after the connection has been +initialized. This is useful for testing. + +```ruby +stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]} +``` + +Finally, you can treat your stubs as mocks by verifying that all of the stubbed +calls were made. NOTE: this feature is still fairly experimental. It will not +verify the order or count of any stub. + +```ruby +stubs.verify_stubbed_calls +``` diff --git a/docs/assets/css/main.scss b/docs/assets/css/main.scss new file mode 100644 index 000000000..601041bc0 --- /dev/null +++ b/docs/assets/css/main.scss @@ -0,0 +1,6 @@ +--- +--- + +@import "variables"; +@import "type-theme"; +@import "faraday"; \ No newline at end of file diff --git a/docs/assets/img/favicon.png b/docs/assets/img/favicon.png new file mode 100644 index 000000000..d722f8989 Binary files /dev/null and b/docs/assets/img/favicon.png differ diff --git a/docs/assets/img/featured-bg.svg b/docs/assets/img/featured-bg.svg new file mode 100644 index 000000000..97a190228 --- /dev/null +++ b/docs/assets/img/featured-bg.svg @@ -0,0 +1,52 @@ + + + + Background and Stripes + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/img/home-banner.jpg b/docs/assets/img/home-banner.jpg new file mode 100644 index 000000000..3e71d8b70 Binary files /dev/null and b/docs/assets/img/home-banner.jpg differ diff --git a/docs/assets/img/home-logo.svg b/docs/assets/img/home-logo.svg new file mode 100644 index 000000000..4182de2f4 --- /dev/null +++ b/docs/assets/img/home-logo.svg @@ -0,0 +1,34 @@ + + + + Custom Preset + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/img/logo.png b/docs/assets/img/logo.png new file mode 100644 index 000000000..3db015a71 Binary files /dev/null and b/docs/assets/img/logo.png differ diff --git a/docs/assets/img/middleware.png b/docs/assets/img/middleware.png new file mode 100644 index 000000000..4958f0202 Binary files /dev/null and b/docs/assets/img/middleware.png differ diff --git a/docs/assets/img/repo-card-slim.png b/docs/assets/img/repo-card-slim.png new file mode 100644 index 000000000..b25f4e518 Binary files /dev/null and b/docs/assets/img/repo-card-slim.png differ diff --git a/docs/assets/img/repo-card.png b/docs/assets/img/repo-card.png new file mode 100644 index 000000000..e0dc7ce7d Binary files /dev/null and b/docs/assets/img/repo-card.png differ diff --git a/docs/assets/js/team.js b/docs/assets/js/team.js new file mode 100644 index 000000000..5c61fcbdc --- /dev/null +++ b/docs/assets/js/team.js @@ -0,0 +1,49 @@ +function teamTile(member) { + console.log(member); + return '
' + + '' + + '' + + '' + + '' + + '
'; +} + +function fetchTeam(json, team, div) { + let el = document.querySelector(div); + el.innerHTML = team.map(function (m) { + let index = json.findIndex(function(e) { + return e.author.login === m + }); + return teamTile(json.splice(index, 1)[0]); + }).join(''); +} + +function fetchContributors(json) { + let el = document.querySelector('#contributors-list'); + el.innerHTML = json.reverse().map(function (c) { + return '' + c.author.login + ''; + }).join(' · '); +} + +function hideLoader() { + let el = document.querySelector('#loader'); + el.classList.add('hidden'); +} + +function showTeam() { + let el = document.querySelector('#team-content'); + el.classList.remove('hidden'); +} + +fetch('https://api.github.com/repos/lostisland/faraday/stats/contributors') + .then(function (response) { + response.json().then(function (json) { + fetchTeam(json, ['technoweenie', 'iMacTia', 'olleolleolle'], '#active-maintainers-list'); + fetchTeam(json, ['mislav', 'sferik'], '#historical-team-list'); + fetchContributors(json); + hideLoader(); + showTeam(); + }); + }); \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..31dbb8ccd --- /dev/null +++ b/docs/index.md @@ -0,0 +1,51 @@ +--- +# You don't need to edit this file, it's empty on purpose. +# Edit theme's home layout instead if you wanna make some changes +# See: https://jekyllrb.com/docs/themes/#overriding-theme-defaults +layout: page +title: Homepage +feature-title: +feature-img: "assets/img/featured-bg.svg" +hide: true +--- + +Faraday is an HTTP client library that provides a common interface over many adapters (such as Net::HTTP) +and embraces the concept of Rack middleware when processing the request/response cycle. + +{: .text-center} +[ Fork on GitHub][github]{: .btn} +[ Chat with us][gitter]{: .btn} + +{: .mt-60} +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'faraday' +``` + +And then execute: + +```bash +$ bundle +``` + +Or install it yourself as: + +```bash +$ gem install faraday +``` + +You can also install the [`faraday_middleware`][faraday_middleware] +extension gem to access a collection of useful Faraday middleware. + +{: .mt-60} + +{: .text-center} +[ Read the docs][usage]{: .btn} + +[github]: https://github.com/lostisland/faraday +[gitter]: https://gitter.im/lostisland/faraday +[faraday_middleware]: https://github.com/lostisland/faraday_middleware +[usage]: ./usage diff --git a/docs/middleware/custom.md b/docs/middleware/custom.md new file mode 100644 index 000000000..5fa82b78c --- /dev/null +++ b/docs/middleware/custom.md @@ -0,0 +1,45 @@ +--- +layout: documentation +title: "Writing Middleware" +permalink: /middleware/custom +hide: true +top_name: Middleware +top_link: ./ +prev_name: Available Middleware +prev_link: ./list +--- + +Middleware are classes that implement a `call` instance method. They hook into +the request/response cycle. + +```ruby +def call(request_env) + # do something with the request + # request_env[:request_headers].merge!(...) + + @app.call(request_env).on_complete do |response_env| + # do something with the response + # response_env[:response_headers].merge!(...) + end +end +``` + +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. + +The `env` is a hash with symbol keys that contains info about the request and, +later, response. Some keys are: + +``` +# request phase +:method - :get, :post, ... +:url - URI for the current request; also contains GET parameters +:body - POST parameters for :post/:put requests +:request_headers + +# response phase +:status - HTTP response status code, such as 200 +:body - the response body +:response_headers +``` diff --git a/docs/middleware/index.md b/docs/middleware/index.md new file mode 100644 index 000000000..5630c0244 --- /dev/null +++ b/docs/middleware/index.md @@ -0,0 +1,51 @@ +--- +layout: documentation +title: "Middleware" +permalink: /middleware +next_name: Available Middleware +next_link: ./list +order: 3 +--- + +A `Faraday::Connection` uses a `Faraday::RackBuilder` to assemble a +Rack-inspired middleware stack for making HTTP requests. Each middleware runs +and passes an Env object around to the next one. After the final middleware has +run, Faraday will return a `Faraday::Response` to the end user. + +![Middleware](../assets/img/middleware.png) + +The order in which middleware is stacked is important. Like with Rack, the +first middleware on the list wraps all others, while the last middleware is the +innermost one, so that must be the adapter. + +```ruby +Faraday.new(...) do |conn| + # POST/PUT params encoders: + conn.request :multipart + conn.request :url_encoded + + # Last middleware must be the adapter: + conn.adapter :net_http +end +``` + +This request middleware setup affects POST/PUT requests in the following way: + +1. `Request::Multipart` checks for files in the payload, otherwise leaves + everything untouched; +2. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not + already encoded or of another type + +Swapping middleware means giving the other priority. Specifying the +"Content-Type" for the request is explicitly stating which middleware should +process it. + +Examples: + +```ruby +# uploading a file: +payload[:profile_pic] = Faraday::UploadIO.new('/path/to/avatar.jpg', 'image/jpeg') + +# "Multipart" middleware detects files and encodes with "multipart/form-data": +conn.put '/profile', payload +``` diff --git a/docs/middleware/list.md b/docs/middleware/list.md new file mode 100644 index 000000000..e912c195c --- /dev/null +++ b/docs/middleware/list.md @@ -0,0 +1,50 @@ +--- +layout: documentation +title: "Available Middleware" +permalink: /middleware/list +hide: true +top_name: Middleware +top_link: ./ +next_name: Writing Middleware +next_link: ./custom +--- + +Faraday ships with some useful middleware that you can use to customize your request/response lifecycle. +Middleware are separated into two macro-categories: **Request Middleware** and **Response Middleware**. +The former usually deal with the request, encoding the parameters or setting headers. +The latter instead activate after the request is completed and a response has been received, like +parsing the response body, logging useful info or checking the response status. + +### Request Middleware + +**Request middleware** can modify Request details before the Adapter runs. Most +middleware set Header values or transform the request body based on the +content type. + +* [`BasicAuthentication`][authentication] sets the `Authorization` header to the `user:password` +base64 representation. +* [`TokenAuthentication`][authentication] sets the `Authorization` header to the specified token. +* [`Multipart`][multipart] converts a `Faraday::Request#body` hash of key/value pairs into a +multipart form request. +* [`UrlEncoded`][url_encoded] converts a `Faraday::Request#body` hash of key/value pairs into a url-encoded request body. +* [`Retry`][retry] automatically retries requests that fail due to intermittent client +or server errors (such as network hiccups). +* [`Instrumentation`][instrumentation] allows to instrument requests using different tools. + + +### Response Middleware + +**Response middleware** receives the response from the adapter and can modify its details +before returning it. + +* [`Logger`][logger] logs both the request and the response body and headers. +* [`RaiseError`][raise_error] checks the response HTTP code and raises an exception if it is a 4xx or 5xx code. + + +[authentication]: ./authentication +[multipart]: ./multipart +[url_encoded]: ./url-encoded +[retry]: ./retry +[instrumentation]: ./instrumentation +[logger]: ./logger +[raise_error]: ./raise-error diff --git a/docs/middleware/request/authentication.md b/docs/middleware/request/authentication.md new file mode 100644 index 000000000..00771c71f --- /dev/null +++ b/docs/middleware/request/authentication.md @@ -0,0 +1,30 @@ +--- +layout: documentation +title: "Authentication Middleware" +permalink: /middleware/authentication +hide: true +next_name: Multipart Middleware +next_link: ./multipart +top_name: Back to Middleware +top_link: ./list +--- + +Basic and Token authentication are handled by Faraday::Request::BasicAuthentication +and Faraday::Request::TokenAuthentication respectively. +These can be added as middleware manually or through the helper methods. + +### Basic Authentication + +```ruby +Faraday.new(...) do |conn| + conn.basic_auth('username', 'password') +end +``` + +### Token Authentication + +```ruby +Faraday.new(...) do |conn| + conn.token_auth('authentication-token') +end +``` \ No newline at end of file diff --git a/docs/middleware/request/instrumentation.md b/docs/middleware/request/instrumentation.md new file mode 100644 index 000000000..33fcaae08 --- /dev/null +++ b/docs/middleware/request/instrumentation.md @@ -0,0 +1,45 @@ +--- +layout: documentation +title: "Instrumentation Middleware" +permalink: /middleware/instrumentation +hide: true +prev_name: Retry Middleware +prev_link: ./retry +next_name: Logger Middleware +next_link: ./logger +top_name: Back to Middleware +top_link: ./list +--- + +The `Instrumentation` middleware allows to instrument requests using different tools. +Options for this middleware include the instrumentation `name` and the `instrumenter` you want to use. +They default to `request.faraday` and `ActiveSupport::Notifications` respectively, but you can provide your own: + +```ruby +conn = Faraday.new(...) do |f| + f.request :instrumentation, name: 'custom_name', instrumenter: MyInstrumenter + ... +end +``` + +### Example Usage + +The `Instrumentation` middleware will use `ActiveSupport::Notifications` by default as instrumenter, +allowing you to subscribe to the default event name and instrument requests: + +```ruby +conn = Faraday.new('http://example.com') do |f| + f.request :instrumentation + ... +end + +ActiveSupport::Notifications.subscribe('request.faraday') do |name, starts, ends, _, env| + url = env[:url] + http_method = env[:method].to_s.upcase + duration = ends - starts + $stdout.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration] +end + +conn.get('/search', { a: 1, b: 2 }) +#=> [example.com] GET /search?a=1&b=2 (0.529 s) +``` diff --git a/docs/middleware/request/multipart.md b/docs/middleware/request/multipart.md new file mode 100644 index 000000000..227246724 --- /dev/null +++ b/docs/middleware/request/multipart.md @@ -0,0 +1,43 @@ +--- +layout: documentation +title: "Multipart Middleware" +permalink: /middleware/multipart +hide: true +prev_name: Authentication Middleware +prev_link: ./authentication +next_name: UrlEncoded Middleware +next_link: ./url-encoded +top_name: Back to Middleware +top_link: ./list +--- + +The `Multipart` middleware converts a `Faraday::Request#body` hash of key/value pairs into a multipart form request. +This only happens if the middleware finds an object in the request body that responds to `content_type`. +The middleware also automatically adds the boundary to the request body. +You can use `Faraday::UploadIO` or `Faraday::CompositeReadIO` to wrap your multipart parameters, +which are in turn wrappers of the equivalent classes from the [`multipart-post`][multipart_post] gem. + +### Example Usage + +```ruby +conn = Faraday.new(...) do |f| + f.request :multipart + ... +end +``` + +Payload can be a mix of POST data and UploadIO objects. + +```ruby +payload = { + file_name: 'multipart_example.rb', + file: Faraday::UploadIO.new(__FILE__, 'text/x-ruby') +} + +conn.post('/', payload) +# POST with +# Content-Type: "multipart/form-data; boundary=-----------RubyMultipartPost-b7f5d9a9b5f201e7af7d7af730ac4bf4" +# Body: # +``` + +[multipart_post]: https://github.com/socketry/multipart-post \ No newline at end of file diff --git a/docs/middleware/request/retry.md b/docs/middleware/request/retry.md new file mode 100644 index 000000000..4d5396fad --- /dev/null +++ b/docs/middleware/request/retry.md @@ -0,0 +1,113 @@ +--- +layout: documentation +title: "Retry Middleware" +permalink: /middleware/retry +hide: true +prev_name: UrlEncoded Middleware +prev_link: ./url-encoded +next_name: Instrumentation Middleware +next_link: ./instrumentation +top_name: Back to Middleware +top_link: ./list +--- + +The `Retry` middleware automatically retries requests that fail due to intermittent client +or server errors (such as network hiccups). +By default, it retries 2 times and handles only timeout exceptions. +It can be configured with an arbitrary number of retries, a list of exceptions to handle, +a retry interval, a percentage of randomness to add to the retry interval, and a backoff factor. + +### Example Usage + +This example will result in a first interval that is random between 0.05 and 0.075 +and a second interval that is random between 0.1 and 0.15. + +```ruby +retry_options = { + max: 2, + interval: 0.05, + interval_randomness: 0.5, + backoff_factor: 2 +} + +conn = Faraday.new(...) do |f| + f.request :retry, retry_options + ... +end + +conn.get('/') +``` + +### Control when the middleware will retry requests + +By default, the `Retry` middleware will only retry idempotent methods and the most common network-related exceptions. +You can change this behaviour by providing the right option when adding the middleware to your connection. + +#### Specify which methods will be retried + +You can provide a `methods` option with a list of HTTP methods. +This will replace the default list of HTTP methods: `delete`, `get`, `head`, `options`, `put`. + +```ruby +retry_options = { + methods: %i[get post] +} +``` + +#### Specify which exceptions should trigger a retry + +You can provide an `exceptions` option with a list of exceptions that will replace +the default list of network-related exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`. +This can be particularly useful when combined with the [RaiseError][raise_error] middleware. + +```ruby +retry_options = { + exceptions: [Faraday::ResourceNotFound, Faraday::UnauthorizedError] +} +``` + +#### Specify on which response statuses to retry + +By default the `Retry` middleware will only retry the request if one of the expected exceptions arise. +However, you can specify a list of HTTP statuses you'd like to be retried. When you do so, the middleware will +check the response `status` code and will retry the request if included in the list. + +```ruby +retry_options = { + retry_statuses: [401, 409] +} +``` + +#### Specify a custom retry logic + +You can also specify a custom retry logic with the `retry_if` option. +This option accepts a block that will receive the `env` object and the exception raised +and should decide if the code should retry still the action or not independent of the retry count. +This would be useful if the exception produced is non-recoverable or if the the HTTP method called is not idempotent. + +**NOTE:** this option will only be used for methods that are not included in the `methods` option. +If you want this to apply to all HTTP methods, pass `methods: []` as an additional option. + +```ruby +# Retries the request if response contains { success: false } +retry_options = { + retry_if: -> (env, _exc) { env.body[:success] == 'false' } +} +``` + +### Call a block on every retry + +You can specify a block through the `retry_block` option that will be called every time the request is retried. +There are many different applications for this feature, spacing from instrumentation to monitoring. +Request environment, middleware options, current number of retries and the exception is passed to the block as parameters. +For example, you might want to keep track of the response statuses: + +```ruby +response_statuses = [] +retry_options = { + retry_block: -> (env, options, retries, exc) { response_statuses << env.status } +} +``` + + +[raise_error]: ../middleware/raise-error \ No newline at end of file diff --git a/docs/middleware/request/url_encoded.md b/docs/middleware/request/url_encoded.md new file mode 100644 index 000000000..95cdf528c --- /dev/null +++ b/docs/middleware/request/url_encoded.md @@ -0,0 +1,42 @@ +--- +layout: documentation +title: "UrlEncoded Middleware" +permalink: /middleware/url-encoded +hide: true +prev_name: Multipart Middleware +prev_link: ./multipart +next_name: Retry Middleware +next_link: ./retry +top_name: Back to Middleware +top_link: ./list +--- + +The `UrlEncoded` middleware converts a `Faraday::Request#body` hash of key/value pairs into a url-encoded request body. +The middleware also automatically sets the `Content-Type` header to `application/x-www-form-urlencoded`. +The way parameters are serialized can be [customized][customize]. + + +### Example Usage + +```ruby +conn = Faraday.new(...) do |f| + f.request :url_encoded + ... +end + +conn.post('/', { a: 1, b: 2 }) +# POST with +# Content-Type: application/x-www-form-urlencoded +# Body: a=1&b=2 +``` + +Complex structures can also be passed + +```ruby +conn.post('/', { a: [1, 3], b: { c: 2, d: 4} }) +# POST with +# Content-Type: application/x-www-form-urlencoded +# Body: a%5B%5D=1&a%5B%5D=3&b%5Bc%5D=2&b%5Bd%5D=4 +``` + +[customize]: ../usage/customize/#changing-how-parameters-are-serialized diff --git a/docs/middleware/response/logger.md b/docs/middleware/response/logger.md new file mode 100644 index 000000000..1468dbf20 --- /dev/null +++ b/docs/middleware/response/logger.md @@ -0,0 +1,98 @@ +--- +layout: documentation +title: "Logger Middleware" +permalink: /middleware/logger +hide: true +prev_name: Instrumentation Middleware +prev_link: ./instrumentation +next_name: RaiseError Middleware +next_link: ./raise-error +top_name: Back to Middleware +top_link: ./list +--- + +The `Logger` middleware logs both the request and the response body and headers. +It is highly customizable and allows to mask confidential information if necessary. + +### Basic Usage + +```ruby +conn = Faraday.new(url: 'http://sushi.com') do |faraday| + faraday.response :logger # log requests and responses to $stdout +end + +conn.get +# => INFO -- request: GET http://sushi.com/ +# => DEBUG -- request: User-Agent: "Faraday v1.0.0" +# => INFO -- response: Status 301 +# => DEBUG -- response: date: "Sun, 19 May 2019 16:05:40 GMT" +``` + +### Customize the logger + +By default, the `Logger` middleware uses the Ruby `Logger.new($stdout)`. +You can customize it to use any logger you want by providing it when you add the middleware to the stack: + +```ruby +conn = Faraday.new(url: 'http://sushi.com') do |faraday| + faraday.response :logger, MyLogger.new($stdout) +end +``` + +### Include and exclude headers/bodies + +By default, the `logger` middleware logs only headers for security reasons, however, you can configure it +to log bodies as well, or disable headers logging if you need to. To do so, simply provide a configuration hash +when you add the middleware to the stack: + +```ruby +conn = Faraday.new(url: 'http://sushi.com') do |faraday| + faraday.response :logger, nil, { headers: true, bodies: true } +end +``` + +Please note this only works with the default formatter. + +### Filter sensitive information + +You can filter sensitive information from Faraday logs using a regex matcher: + +```ruby +conn = Faraday.new(url: 'http://sushi.com') do |faraday| + faraday.response :logger do | logger | + logger.filter(/(api_key=)(\w+)/, '\1[REMOVED]') + end +end + +conn.get('/', api_key: 'secret') +# => INFO -- request: GET http://sushi.com/?api_key=[REMOVED] +# => DEBUG -- request: User-Agent: "Faraday v1.0.0" +# => INFO -- response: Status 301 +# => DEBUG -- response: date: "Sun, 19 May 2019 16:12:36 GMT" +``` + +### Customize the formatter + +You can also provide a custom formatter to control how requests and responses are logged. +Any custom formatter MUST implement the `request` and `response` method, with one argument which +will be passed being the Faraday environment. +If you make your formatter inheriting from `Faraday::Response::Logger::Formatter`, +then the methods `debug`, `info`, `warn`, `error` and `fatal` are automatically delegated to the logger. + +```ruby +class MyFormatter < Faraday::Logging::Formatter + def request(env) + # Build a custom message using `env` + info('Request') { 'Sending Request' } + end + + def response(env) + # Build a custom message using `env` + info('Response') { 'Response Received' } + end +end + +conn = Faraday.new(url: 'http://sushi.com/api_key=s3cr3t') do |faraday| + faraday.response :logger, nil, formatter: MyFormatter +end +``` \ No newline at end of file diff --git a/docs/middleware/response/raise_error.md b/docs/middleware/response/raise_error.md new file mode 100644 index 000000000..8a46f371e --- /dev/null +++ b/docs/middleware/response/raise_error.md @@ -0,0 +1,41 @@ +--- +layout: documentation +title: "Raise Error Middleware" +permalink: /middleware/raise-error +hide: true +prev_name: Logger Middleware +prev_link: ./logger +top_name: Back to Middleware +top_link: ./list +--- + +The `RaiseError` middleware checks the response HTTP code and raises an exception if it is a 4xx or 5xx code. +Specific exceptions are raised based on the HTTP Status code, according to the list below: + +``` +## 4xx HTTP codes +400 => Faraday::BadRequestError +401 => Faraday::UnauthorizedError +403 => Faraday::ForbiddenError +404 => Faraday::ResourceNotFound +407 => Faraday::ProxyAuthError +409 => Faraday::ConflictError +422 => Faraday::UnprocessableEntityError +4xx => Faraday::ClientError (all exceptions above inherit from this one. + +## 5xx HTTP codes +5xx => Faraday::ServerError +``` + +All exceptions classes listed above inherit from `Faraday::Error`, and are initialized providing +the response `status`, `headers` and `body`, available for you to access on rescue: + +```ruby +begin + conn.get('/wrong-url') # => Assume this raises a 404 response +rescue Faraday::ResourceNotFound => e + e.response[:status] #=> 404 + e.response[:headers] #=> { ... } + e.response[:body] #=> "..." +end +``` diff --git a/docs/team.md b/docs/team.md new file mode 100644 index 000000000..9833cdbba --- /dev/null +++ b/docs/team.md @@ -0,0 +1,26 @@ +--- +layout: page +title: Team +permalink: /team/ +order: 4 +--- + +
+
+
+ + + + diff --git a/docs/usage/customize.md b/docs/usage/customize.md new file mode 100644 index 000000000..adf40031a --- /dev/null +++ b/docs/usage/customize.md @@ -0,0 +1,96 @@ +--- +layout: documentation +title: "Customizing the Request" +permalink: /usage/customize +hide: true +top_name: Usage +top_link: ./ +next_name: Streaming Responses +next_link: ./streaming +--- + +Configuration can be set up with the connection and/or adjusted per request. + +As connection options: + +```ruby +conn = Faraday.new('http://sushi.com', request: { timeout: 5 }) +conn.get('/search') +``` + +Or as per-request options: + +```ruby +conn.get do |req| + req.url '/search' + req.options.timeout = 5 +end +``` + +You can also inject arbitrary data into the request using the `context` option. +This will be available in the `env` on all middleware. + +```ruby +conn.get do |req| + req.url '/search' + req.options.context = { + foo: 'foo', + bar: 'bar' + } +end +``` + +## Changing how parameters are serialized + +Sometimes you need to send the same URL parameter multiple times with different values. +This requires manually setting the parameter encoder and can be done on +either per-connection or per-request basis. +This applies to all HTTP verbs. + +Per-connection setting: + +```ruby +conn = Faraday.new request: { params_encoder: Faraday::FlatParamsEncoder } +conn.get('', { roll: ['california', 'philadelphia'] }) +``` + +Per-request setting: + +```ruby +conn.get do |req| + req.options.params_encoder = Faraday::FlatParamsEncoder + req.params = { roll: ['california', 'philadelphia'] } +end +``` + +### Custom serializers + +You can build your custom encoder, if you like. + +The value of Faraday `params_encoder` can be any object that responds to: + +* `#encode(hash) #=> String` +* `#decode(string) #=> Hash` + +The encoder will affect both how Faraday processes query strings and how it +serializes POST bodies. + +The default encoder is `Faraday::NestedParamsEncoder`. + +## Proxy + +Faraday will try to automatically infer the proxy settings from your system using [`URI#find_proxy`][ruby-find-proxy]. +This will retrieve them from environment variables such as http_proxy, ftp_proxy, no_proxy, etc. +If for any reason you want to disable this behaviour, you can do so by setting the global variable `ignore_env_proxy`: + +```ruby +Faraday.ignore_env_proxy = true +``` + +You can also specify a custom proxy when initializing the connection: + +```ruby +conn = Faraday.new('http://www.example.com', proxy: 'http://proxy.com') +``` + +[ruby-find-proxy]: https://ruby-doc.org/stdlib-2.6.3/libdoc/uri/rdoc/URI/Generic.html#method-i-find_proxy diff --git a/docs/usage/index.md b/docs/usage/index.md new file mode 100644 index 000000000..37ec70e80 --- /dev/null +++ b/docs/usage/index.md @@ -0,0 +1,132 @@ +--- +layout: documentation +title: "Usage" +permalink: /usage +next_name: Customizing the Request +next_link: ./customize +order: 1 +--- + +Make a simple `GET` request by requiring the Faraday gem and using `Faraday.get`: + +```ruby +response = Faraday.get 'http://sushi.com/nigiri/sake.json' +``` + +This returns a `Faraday::Response` object with the response status, headers, and +body. + +```ruby +response.status +# => 200 + +response.headers +# => {"server"=>"sushi.com", "content-type"=>"text/html; charset=utf-8"... + +response.body +# => "... +``` + +### Requests without a body + +Faraday supports the following HTTP verbs that typically don't include a request +body: + +* `get` +* `head` +* `delete` +* `trace` + +You can specify URI query parameters and HTTP headers when making a request. + + +```ruby +url = 'http://sushi.com/nigiri/sake.json' +resp = Faraday.get(url, {a: 1}, {'Accept' => 'application/json'}) +# => GET http://sushi.com/nigiri/sake.json?a=1 +``` + +[Learn more about parameters encoding][encoding]. + +### Requests with a body + +Faraday also supports HTTP verbs that do include request bodies, though the +optional method arguments are different. Instead of HTTP query params, these +methods accept a response body. + +* `post` +* `put` +* `patch` + +```ruby +# POST 'application/x-www-form-urlencoded' content +url = 'http://sushi.com/fave' +resp = Faraday.post(url, "choice=sake") + +# POST JSON content +resp = Faraday.post(url, '{"choice": "sake"}', + "Content-Type" => "application/json") +``` + +#### Form upload + +Faraday can automatically convert hashes to values for form or multipart request +bodies. + +```ruby +url = 'http://sushi.com/fave' +resp = Faraday.post(url, choice: 'sake') +# => POST 'choice=sake' to http://sushi.com/fave +``` + +[Learn more about uploading files][multipart]. + +### Detailed HTTP Requests + +All HTTP verbs support a longer form style of making requests. This is handy if +you need to change a lot of the defaults, or if the details of the HTTP request +change according to method arguments. Each of the HTTP verb helpers can yield a +`Faraday::Request` that can be modified before being sent. + +This example shows a hypothetical search endpoint that accepts a JSON request +body as the actual search query. + +```ruby +resp = Faraday.get('http://sushi.com/search') do |req| + req.params['limit'] = 100 + req.headers['Content-Type'] = 'application/json' + req.body = {query: 'salmon'}.to_json +end +# => GET http://sushi.com/search?limit=100 +``` + +### The Connection Object + +A more flexible way to use Faraday is to start with a `Faraday::Connection` +object. Connection objects can store a common URL base path or HTTP headers to +apply to every request. All of the HTTP verb helpers described above +(`Faraday.get`, `Faraday.post`, etc) are available on the `Faraday::Connection` +instance. + +```ruby +conn = Faraday.new( + url: 'http://sushi.com', + params: {param: '1'}, + headers: {'Content-Type' => 'application/json'} +) + +resp = conn.get('search') do |req| + req.params['limit'] = 100 + req.body = {query: 'salmon'}.to_json +end +# => GET http://sushi.com/search?param=1&limit=100 +``` + +A `Faraday::Connection` object can also be used to change the default HTTP +adapter or add custom middleware that runs during Faraday's request/response +cycle. + +[Learn more about Middleware](../middleware). + +[encoding]: ../middleware/url-encoded +[multipart]: ../middleware/multipart diff --git a/docs/usage/streaming.md b/docs/usage/streaming.md new file mode 100644 index 000000000..24df5255b --- /dev/null +++ b/docs/usage/streaming.md @@ -0,0 +1,37 @@ +--- +layout: documentation +title: "Streaming Responses" +permalink: /usage/streaming +hide: true +top_name: Usage +top_link: ./ +prev_name: Customizing the Request +prev_link: ./customize +--- + +Sometimes you might need to receive a streaming response. +You can do this with the `on_data` request option. + +The `on_data` callback is a receives tuples of chunk Strings, and the total +of received bytes so far. + +This example implements such a callback: + +```ruby +# A buffer to store the streamed data +streamed = [] + +conn.get('/nigiri/sake.json') do |req| + # Set a callback which will receive tuples of chunk Strings + # and the sum of characters received so far + req.options.on_data = Proc.new do |chunk, overall_received_bytes| + puts "Received #{overall_received_bytes} characters" + streamed << chunk + end +end + +# Joins all response chunks together +streamed.join +``` + +The `on_data` streaming is currently only supported by the `Net::HTTP` adapter.