faraday_dynamic_timeout 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +38 -5
- data/VERSION +1 -1
- data/faraday_dynamic_timeout.gemspec +3 -1
- data/lib/faraday_dynamic_timeout/middleware.rb +76 -26
- data/lib/faraday_dynamic_timeout.rb +1 -0
- metadata +4 -12
- data/Appraisals +0 -7
- data/gemfiles/faraday_1.gemfile +0 -15
- data/gemfiles/faraday_2.gemfile +0 -15
- data/lib/faraday_dynamic_timeout/strategy.rb +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aba9d8b057d18dc3912677fd37e02148e5bc69ea78fbbb57cb8b4c3a790c4624
|
|
4
|
+
data.tar.gz: 3f17c539413375236113a2037a89717639b35669e0625926ea0d727525c37ab6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 57bca13ad57839868f5dec34419271325e72fde1d0e45e0c7a7cd0bedce5350ae62a41ec7b026e95bf3c57fadf50dfc76f44ec630d0bf14d86191d41b9e5b07f
|
|
7
|
+
data.tar.gz: e0f0c827dfdd3b16a9f38e45f7127aa4781ab8d25060ad7bc4a47705e9d0d8410ea7a997abbb8f27d81e332dfd0ee6ea16602bc2f4c9b9e91201f3b8e2cd58ed
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,33 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 1.1.1
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- The middleware now fails open on any Redis error: instead of raising and taking down HTTP traffic, the request is made using the highest configured timeout without throttling.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Padded the TTL used to clean up orphaned Redis entries for throttle slots and request counters. Previously the TTL was set to the bucket timeout, but a request's total wall time can legitimately exceed the timeout (adapters apply it to the open/read/write phases individually), so slots could expire and be reclaimed while requests were still in flight, allowing the concurrency limit to be exceeded.
|
|
16
|
+
- A `Restrainer::ThrottledError` raised from within the request itself (e.g. from a nested middleware) is no longer mistaken for a full bucket. Previously such an error would cause the request to be retried on the next bucket, executing the HTTP request a second time.
|
|
17
|
+
- Made bucket configuration memoization safe when the configuration array is mutated concurrently by another thread.
|
|
18
|
+
- Added missing `require "securerandom"`, which previously only worked because a dependency loaded it.
|
|
19
|
+
|
|
20
|
+
### Documentation
|
|
21
|
+
|
|
22
|
+
- Documented the `RequestInfo` fields, the behavior of `timed_out?` with respect to connection-level timeouts, the fact that `:callback` runs inline and can mask the request result if it raises, and the streaming/parallel adapter limitations.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- Removed unused internal `Strategy` class.
|
|
27
|
+
|
|
28
|
+
## 1.1.0
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Add `before_request` option on middleware to allow making custom changes to the request based on the timeout value being used.
|
|
33
|
+
|
|
7
34
|
## 1.0.0
|
|
8
35
|
|
|
9
36
|
### Added
|
data/README.md
CHANGED
|
@@ -50,11 +50,35 @@ In this example, the timeout will be set to 8 seconds if there are 5 or fewer re
|
|
|
50
50
|
|
|
51
51
|
- `:name` - An optional name for the resource. By default the hostname and port of the request URL will be used to identify the resource. Each resource will report a separate count of concurrent requests and processes. You can group multiple resources from different hosts together with the `:name` option.
|
|
52
52
|
|
|
53
|
-
- `:
|
|
53
|
+
- `:before_request` - An optional callback that will be called before each request is made. The callback can be a `Proc` or any object that responds to `call`. It will be called before the request is made with the `Faraday::Env` object and the timeout being used. You can use this to make changes to the request based on the timeout being used. This can be used, for example, to add the timeout to the request payload.
|
|
54
|
+
|
|
55
|
+
- `:filter` - An optional callback that will be called before each request with the `Faraday::Env` object. The callback can be a `Proc` or any object that responds to `call`. If it returns a falsey value, the middleware will pass the request through without applying any timeout or throttling. You can use this to limit the middleware to specific requests (for example, only search requests).
|
|
56
|
+
|
|
57
|
+
- `:callback` - An optional callback that will be called after each request. The callback can be a `Proc` or any object that responds to `call`. It will be called with a `FaradayDynamicTimeout::RequestInfo` argument. You can use this to log the number of concurrent requests or to report metrics to a monitoring system. This can be very useful for tuning the bucket settings. Note that the callback runs inline before the response is returned, so any exception it raises will propagate out of the request and can mask the original result. If your callback might raise (for example, when a metrics backend is unavailable), rescue inside the callback itself.
|
|
58
|
+
|
|
59
|
+
### The RequestInfo object
|
|
60
|
+
|
|
61
|
+
The `:callback` option receives a `FaradayDynamicTimeout::RequestInfo` object with details about the completed request:
|
|
62
|
+
|
|
63
|
+
- `duration` - The time spent making the request in seconds.
|
|
64
|
+
- `timeout` - The timeout that was applied, or `nil` if none was applied (for example when the request was throttled before it was made).
|
|
65
|
+
- `request_count` - The number of concurrent requests observed for the resource.
|
|
66
|
+
- `error` - The exception raised by the request, if any.
|
|
67
|
+
- `throttled?` - True if the request was rejected because all buckets were full.
|
|
68
|
+
- `timed_out?` - True if the request failed with a `Faraday::TimeoutError`. This reflects the read/request timeout that this middleware sets. Connection-level timeouts are reported by adapters as `Faraday::ConnectionFailed` on some adapters rather than `Faraday::TimeoutError`, so they are not counted as timeouts here; inspect `error` directly if you need to distinguish them.
|
|
69
|
+
- `error?` - True if the request raised any error.
|
|
70
|
+
|
|
71
|
+
### Redis availability
|
|
72
|
+
|
|
73
|
+
The middleware depends on Redis to coordinate counts across processes, but Redis problems will not take down your HTTP traffic. If any Redis call fails (a connection failure, a timeout, a command error, etc.), the middleware fails open: the request is made using the highest configured timeout without throttling, and the request count reported to the `:callback` will be `1`. Cleanup of Redis bookkeeping is best effort and any leftover entries expire automatically.
|
|
74
|
+
|
|
75
|
+
### Limitations
|
|
76
|
+
|
|
77
|
+
- **Streaming and parallel adapters.** A request occupies its bucket slot only for the duration of the underlying `app.call`. If you use a streaming response where the body is consumed after the middleware returns, or a Faraday adapter running requests in parallel, the concurrency accounting will not reflect the time spent streaming or the true number of in-flight requests.
|
|
54
78
|
|
|
55
79
|
### Capacity Strategy
|
|
56
80
|
|
|
57
|
-
You can use the `
|
|
81
|
+
You can use the `FaradayDynamicTimeout::CapacityStrategy` class to build a bucket configuration based on the current capacity of your application rather than hard coding bucket limits. This can be useful if you have a system that can scale up and down based on load. It works by estimating the total number of threads available in your application and uses that value to calculate bucket limits based on a percentage provided by the `:capacity` option.
|
|
58
82
|
|
|
59
83
|
```ruby
|
|
60
84
|
capacity = FaradayDynamicTimeout::CapacityStrategy.new(
|
|
@@ -80,6 +104,14 @@ For this example, we will configure the `opensearch` gem with this middleware al
|
|
|
80
104
|
# Set up a redis connection to coordinate counting concurrent requests.
|
|
81
105
|
redis = Redis.new(url: ENV.fetch("REDIS_URL"))
|
|
82
106
|
|
|
107
|
+
# Set the query timeout to match the request timeout so the search nodes will stop
|
|
108
|
+
# processing if the request times out.
|
|
109
|
+
set_query_timeout = ->(env, timeout) do
|
|
110
|
+
query_params = (Faraday::Utils.parse_query(env.url.query) || {})
|
|
111
|
+
query_params["timeout"] = "#{(timeout * 1000).ceil}ms"
|
|
112
|
+
env.url.query = Faraday::Utils.build_query(query_params)
|
|
113
|
+
end
|
|
114
|
+
|
|
83
115
|
# Set up a statsd client to report metrics with the DataDog extensions.
|
|
84
116
|
statsd = Statsd.new(ENV.fetch("STATSD_HOST"), ENV.fetch("STATSD_PORT"))
|
|
85
117
|
|
|
@@ -95,13 +127,14 @@ end
|
|
|
95
127
|
client = OpenSearch::Client.new(host: 'localhost', port: '9200') do |faraday|
|
|
96
128
|
faraday.request :dynamic_timeout,
|
|
97
129
|
buckets: [
|
|
98
|
-
{timeout: 8,
|
|
99
|
-
{timeout: 1,
|
|
100
|
-
{timeout: 0.5,
|
|
130
|
+
{timeout: 8, limit: 5},
|
|
131
|
+
{timeout: 1, limit: 10},
|
|
132
|
+
{timeout: 0.5, limit: 20}
|
|
101
133
|
],
|
|
102
134
|
name: "opensearch",
|
|
103
135
|
redis: redis,
|
|
104
136
|
filter: ->(env) { env.url.path.end_with?("/_search") },
|
|
137
|
+
before_request: set_query_timeout,
|
|
105
138
|
callback: metrics_callback
|
|
106
139
|
end
|
|
107
140
|
```
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.1.1
|
|
@@ -14,6 +14,8 @@ Gem::Specification.new do |spec|
|
|
|
14
14
|
.
|
|
15
15
|
Gemfile
|
|
16
16
|
Gemfile.lock
|
|
17
|
+
Appraisals
|
|
18
|
+
gemfiles/
|
|
17
19
|
Rakefile
|
|
18
20
|
bin/
|
|
19
21
|
spec/
|
|
@@ -29,5 +31,5 @@ Gem::Specification.new do |spec|
|
|
|
29
31
|
|
|
30
32
|
spec.add_development_dependency "bundler"
|
|
31
33
|
|
|
32
|
-
spec.required_ruby_version = ">= 2.
|
|
34
|
+
spec.required_ruby_version = ">= 2.6"
|
|
33
35
|
end
|
|
@@ -11,13 +11,15 @@ module FaradayDynamicTimeout
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
@memoized_buckets = []
|
|
14
|
-
@mutex = Mutex.new
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def call(env)
|
|
18
|
-
|
|
17
|
+
# A Redis outage must never take down HTTP traffic, so if building the bucket
|
|
18
|
+
# configuration touches Redis (e.g. a capacity based strategy) and it is
|
|
19
|
+
# unavailable, fail open and let the request through with no dynamic timeout.
|
|
20
|
+
buckets = safe_redis { sorted_buckets }
|
|
19
21
|
redis = redis_client
|
|
20
|
-
return app.call(env) if !enabled?(env) || buckets.empty? || redis.nil?
|
|
22
|
+
return app.call(env) if !enabled?(env) || buckets.nil? || buckets.empty? || redis.nil?
|
|
21
23
|
|
|
22
24
|
error = nil
|
|
23
25
|
bucket_timeout = nil
|
|
@@ -27,7 +29,7 @@ module FaradayDynamicTimeout
|
|
|
27
29
|
count_request(env.url, redis, buckets, callback) do |request_count|
|
|
28
30
|
execute_with_timeout(env.url, buckets, request_count, redis) do |timeout|
|
|
29
31
|
bucket_timeout = timeout
|
|
30
|
-
set_timeout(env
|
|
32
|
+
set_timeout(env, timeout) if timeout
|
|
31
33
|
|
|
32
34
|
# Resetting the start time to more accurately reflect the time spent in the request.
|
|
33
35
|
start_time = monotonic_time if callback
|
|
@@ -55,15 +57,16 @@ module FaradayDynamicTimeout
|
|
|
55
57
|
config = config.call if config.respond_to?(:call)
|
|
56
58
|
config = Array(config)
|
|
57
59
|
memoized_config, memoized_buckets = @memoized_buckets
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
return memoized_buckets if config == memoized_config
|
|
61
|
+
|
|
62
|
+
# Duplicate the config before storing it so a caller mutating the array or its
|
|
63
|
+
# hashes cannot change the memoized snapshot out from under us. The memo is
|
|
64
|
+
# published with a single assignment; concurrent threads may redundantly rebuild
|
|
65
|
+
# it, which is harmless.
|
|
66
|
+
config = config.collect(&:dup)
|
|
67
|
+
buckets = Bucket.from_hashes(config)
|
|
68
|
+
@memoized_buckets = [config, buckets]
|
|
69
|
+
buckets
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
def enabled?(env)
|
|
@@ -84,10 +87,13 @@ module FaradayDynamicTimeout
|
|
|
84
87
|
retval = yield(bucket.timeout)
|
|
85
88
|
break
|
|
86
89
|
else
|
|
87
|
-
restrainer = Restrainer.new(restrainer_name(uri, bucket.timeout), limit: bucket.limit, timeout: bucket.timeout, redis: redis)
|
|
90
|
+
restrainer = Restrainer.new(restrainer_name(uri, bucket.timeout), limit: bucket.limit, timeout: slot_ttl(bucket.timeout), redis: redis)
|
|
88
91
|
begin
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
# Acquire the slot explicitly rather than using Restrainer#throttle so that a
|
|
93
|
+
# ThrottledError raised from within the request itself (e.g. from a nested
|
|
94
|
+
# middleware) is not mistaken for this bucket being full, which would retry
|
|
95
|
+
# the request on the next bucket and execute it a second time.
|
|
96
|
+
process_id = restrainer.lock!
|
|
91
97
|
rescue Restrainer::ThrottledError
|
|
92
98
|
total_requests += bucket.limit
|
|
93
99
|
if buckets.empty?
|
|
@@ -96,6 +102,21 @@ module FaradayDynamicTimeout
|
|
|
96
102
|
request_count = [request_count, total_requests + 1].max
|
|
97
103
|
raise ThrottledError.new("Request to #{base_url(uri)} aborted due to #{request_count} concurrent requests", request_count: request_count)
|
|
98
104
|
end
|
|
105
|
+
rescue Redis::BaseError
|
|
106
|
+
# Redis is unavailable, so throttling cannot be enforced. Fail open using the
|
|
107
|
+
# current (highest available) timeout rather than failing the request.
|
|
108
|
+
retval = yield(bucket.timeout)
|
|
109
|
+
break
|
|
110
|
+
else
|
|
111
|
+
begin
|
|
112
|
+
retval = yield(bucket.timeout)
|
|
113
|
+
ensure
|
|
114
|
+
# Releasing the slot is best effort; if Redis is unavailable the slot will
|
|
115
|
+
# expire on its own via the TTL. A cleanup failure must not mask the result
|
|
116
|
+
# of a request that has already been made.
|
|
117
|
+
safe_redis { restrainer.release!(process_id) }
|
|
118
|
+
end
|
|
119
|
+
break
|
|
99
120
|
end
|
|
100
121
|
end
|
|
101
122
|
end
|
|
@@ -103,27 +124,56 @@ module FaradayDynamicTimeout
|
|
|
103
124
|
retval
|
|
104
125
|
end
|
|
105
126
|
|
|
106
|
-
def set_timeout(
|
|
127
|
+
def set_timeout(env, timeout)
|
|
128
|
+
request = env.request
|
|
107
129
|
request.timeout = timeout
|
|
108
130
|
request.open_timeout = nil
|
|
109
131
|
request.write_timeout = nil
|
|
110
132
|
request.read_timeout = nil
|
|
133
|
+
|
|
134
|
+
option(:before_request)&.call(env, timeout)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# The TTL used to clean up orphaned slot and counter entries in Redis. The bucket
|
|
138
|
+
# timeout only bounds each phase of the request (open/read/write), not its total
|
|
139
|
+
# wall time, so the TTL is padded to avoid expiring entries for requests that are
|
|
140
|
+
# still legitimately in flight.
|
|
141
|
+
def slot_ttl(timeout)
|
|
142
|
+
[timeout * 3, 60].max
|
|
111
143
|
end
|
|
112
144
|
|
|
113
145
|
# Track how many requests are currently being executed only if a callback has been configured.
|
|
146
|
+
#
|
|
147
|
+
# Each Redis operation is guarded so that a Redis outage degrades to a pass through
|
|
148
|
+
# (reporting a request count of 1) rather than blocking the request. The counter entry
|
|
149
|
+
# is released on the way out, but only when it was successfully added.
|
|
114
150
|
def count_request(uri, redis, buckets, callback)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
151
|
+
return yield(1) unless callback
|
|
152
|
+
|
|
153
|
+
# The counter entry is added before a bucket has been selected, so the TTL must
|
|
154
|
+
# conservatively cover the highest timeout (the last bucket); a shorter TTL could
|
|
155
|
+
# expire entries for requests still legitimately in flight. The TTL only comes
|
|
156
|
+
# into play for orphaned entries since entries are normally removed when the
|
|
157
|
+
# request finishes.
|
|
158
|
+
ttl = slot_ttl(buckets.last.timeout)
|
|
159
|
+
request_counter = Counter.new(name: request_counter_name(uri), redis: redis, ttl: ttl)
|
|
160
|
+
id = safe_redis { request_counter.track! }
|
|
161
|
+
begin
|
|
162
|
+
yield(safe_redis { request_counter.value } || 1)
|
|
163
|
+
ensure
|
|
164
|
+
safe_redis { request_counter.release!(id) } if id
|
|
124
165
|
end
|
|
125
166
|
end
|
|
126
167
|
|
|
168
|
+
# Run a block that talks to Redis, returning nil instead of raising if the Redis
|
|
169
|
+
# call fails for any reason. Used to keep Redis problems from taking down HTTP
|
|
170
|
+
# traffic.
|
|
171
|
+
def safe_redis
|
|
172
|
+
yield
|
|
173
|
+
rescue Redis::BaseError
|
|
174
|
+
nil
|
|
175
|
+
end
|
|
176
|
+
|
|
127
177
|
def request_counter_name(uri)
|
|
128
178
|
"#{redis_key_namespace(uri)}.requests"
|
|
129
179
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: faraday_dynamic_timeout
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Durand
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: faraday
|
|
@@ -52,33 +51,27 @@ dependencies:
|
|
|
52
51
|
- - ">="
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
53
|
version: '0'
|
|
55
|
-
description:
|
|
56
54
|
email:
|
|
57
55
|
- bbdurand@gmail.com
|
|
58
56
|
executables: []
|
|
59
57
|
extensions: []
|
|
60
58
|
extra_rdoc_files: []
|
|
61
59
|
files:
|
|
62
|
-
- Appraisals
|
|
63
60
|
- CHANGELOG.md
|
|
64
61
|
- MIT-LICENSE.txt
|
|
65
62
|
- README.md
|
|
66
63
|
- VERSION
|
|
67
64
|
- faraday_dynamic_timeout.gemspec
|
|
68
|
-
- gemfiles/faraday_1.gemfile
|
|
69
|
-
- gemfiles/faraday_2.gemfile
|
|
70
65
|
- lib/faraday_dynamic_timeout.rb
|
|
71
66
|
- lib/faraday_dynamic_timeout/bucket.rb
|
|
72
67
|
- lib/faraday_dynamic_timeout/capacity_strategy.rb
|
|
73
68
|
- lib/faraday_dynamic_timeout/counter.rb
|
|
74
69
|
- lib/faraday_dynamic_timeout/middleware.rb
|
|
75
70
|
- lib/faraday_dynamic_timeout/request_info.rb
|
|
76
|
-
- lib/faraday_dynamic_timeout/strategy.rb
|
|
77
71
|
homepage: https://github.com/bdurand/faraday_dynamic_timeout
|
|
78
72
|
licenses:
|
|
79
73
|
- MIT
|
|
80
74
|
metadata: {}
|
|
81
|
-
post_install_message:
|
|
82
75
|
rdoc_options: []
|
|
83
76
|
require_paths:
|
|
84
77
|
- lib
|
|
@@ -86,15 +79,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
86
79
|
requirements:
|
|
87
80
|
- - ">="
|
|
88
81
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '2.
|
|
82
|
+
version: '2.6'
|
|
90
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
84
|
requirements:
|
|
92
85
|
- - ">="
|
|
93
86
|
- !ruby/object:Gem::Version
|
|
94
87
|
version: '0'
|
|
95
88
|
requirements: []
|
|
96
|
-
rubygems_version:
|
|
97
|
-
signing_key:
|
|
89
|
+
rubygems_version: 4.0.3
|
|
98
90
|
specification_version: 4
|
|
99
91
|
summary: Faraday middleware to dynamically set a request timeout based on the number
|
|
100
92
|
of concurrent requests and throttle the number of requests that can be made.
|
data/Appraisals
DELETED
data/gemfiles/faraday_1.gemfile
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# This file was generated by Appraisal
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gem "webmock"
|
|
6
|
-
gem "appraisal"
|
|
7
|
-
gem "dotenv"
|
|
8
|
-
gem "rake"
|
|
9
|
-
gem "rspec", "~> 3.10"
|
|
10
|
-
gem "standard", "~> 1.0"
|
|
11
|
-
gem "simplecov", "~> 0.21", require: false
|
|
12
|
-
gem "yard"
|
|
13
|
-
gem "faraday", "~> 1.0"
|
|
14
|
-
|
|
15
|
-
gemspec path: "../"
|
data/gemfiles/faraday_2.gemfile
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# This file was generated by Appraisal
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gem "webmock"
|
|
6
|
-
gem "appraisal"
|
|
7
|
-
gem "dotenv"
|
|
8
|
-
gem "rake"
|
|
9
|
-
gem "rspec", "~> 3.10"
|
|
10
|
-
gem "standard", "~> 1.0"
|
|
11
|
-
gem "simplecov", "~> 0.21", require: false
|
|
12
|
-
gem "yard"
|
|
13
|
-
gem "faraday", "~> 2.0"
|
|
14
|
-
|
|
15
|
-
gemspec path: "../"
|