hurley 0.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 +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +28 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +28 -0
- data/LICENSE.md +20 -0
- data/README.md +317 -0
- data/Rakefile +1 -0
- data/contributors.yaml +8 -0
- data/hurley.gemspec +29 -0
- data/lib/hurley.rb +104 -0
- data/lib/hurley/addressable.rb +9 -0
- data/lib/hurley/client.rb +349 -0
- data/lib/hurley/connection.rb +123 -0
- data/lib/hurley/header.rb +144 -0
- data/lib/hurley/multipart.rb +235 -0
- data/lib/hurley/options.rb +142 -0
- data/lib/hurley/query.rb +252 -0
- data/lib/hurley/tasks.rb +111 -0
- data/lib/hurley/test.rb +101 -0
- data/lib/hurley/test/integration.rb +249 -0
- data/lib/hurley/test/server.rb +102 -0
- data/lib/hurley/url.rb +197 -0
- data/script/bootstrap +2 -0
- data/script/package +7 -0
- data/script/test +168 -0
- data/test/client_test.rb +585 -0
- data/test/header_test.rb +108 -0
- data/test/helper.rb +14 -0
- data/test/live/net_http_test.rb +16 -0
- data/test/multipart_test.rb +306 -0
- data/test/query_test.rb +189 -0
- data/test/test_test.rb +38 -0
- data/test/url_test.rb +443 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 70bb285608ba9cef1503ea1acf003fbdc48c0648
|
4
|
+
data.tar.gz: 0b9e00c2e5880c432c41865ed996cffdb9174d46
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9d694cf97f96c3099171fa5479b6c2ad5e3037ec02fa53776945a1b1166e45084e13508c660d5948e2c4b4445fa29020eae28e2187f1474629e28c05509b324
|
7
|
+
data.tar.gz: f48211a98ac3616638afbdd58c5b95dca7e6e486cf2eec6503147321ea628c2f6bbe082840568cff4923e865f9842247aadf5d3c3a352fe2b69a0b887660317a
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
install: script/bootstrap
|
4
|
+
script: script/test
|
5
|
+
rvm:
|
6
|
+
- 1.9.3
|
7
|
+
- 2.0.0
|
8
|
+
- 2.1
|
9
|
+
- jruby-18mode
|
10
|
+
- jruby-19mode
|
11
|
+
- jruby-head
|
12
|
+
|
13
|
+
matrix:
|
14
|
+
allow_failures:
|
15
|
+
# "A fatal error has been detected by the Java Runtime Environment:
|
16
|
+
# Internal Error (sharedRuntime.cpp:843)"
|
17
|
+
- rvm: jruby-18mode
|
18
|
+
- rvm: jruby-19mode
|
19
|
+
- rvm: jruby-head
|
20
|
+
# random crashes
|
21
|
+
fast_finish: true
|
22
|
+
|
23
|
+
env:
|
24
|
+
matrix:
|
25
|
+
- HURLEY_SSL=no
|
26
|
+
- HURLEY_SSL=yes
|
27
|
+
- HURLEY_SSL=no HURLEY_ADDRESSABLE=1
|
28
|
+
- HURLEY_SSL=yes HURLEY_ADDRESSABLE=1
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
hurley (0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
addressable (2.3.6)
|
10
|
+
minitest (5.5.0)
|
11
|
+
rack (1.6.0)
|
12
|
+
rack-protection (1.5.3)
|
13
|
+
rack
|
14
|
+
sinatra (1.4.5)
|
15
|
+
rack (~> 1.4)
|
16
|
+
rack-protection (~> 1.4)
|
17
|
+
tilt (~> 1.3, >= 1.3.4)
|
18
|
+
tilt (1.4.1)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
addressable (~> 2.3.6)
|
25
|
+
bundler (~> 1.0)
|
26
|
+
hurley!
|
27
|
+
minitest (~> 5.5.0)
|
28
|
+
sinatra (~> 1.4.5)
|
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2015 Rick Olson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
# Hurley
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
Hurley is a ruby gem with no runtime dependencies that provides a common
|
6
|
+
interface for working with different HTTP adapters. It is an evolution of
|
7
|
+
Faraday, with rethought internals.
|
8
|
+
|
9
|
+
Hurley revolves around three main classes: Client, Request, and Response. A
|
10
|
+
Client sets the default properties for all HTTP requests, including the base
|
11
|
+
url, headers, and options.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
require "hurley"
|
15
|
+
|
16
|
+
# If you prefer Addressable::URI, require this too:
|
17
|
+
# This is required automatically if `Addressable::URI` is defined when Hurley
|
18
|
+
# is being loaded.
|
19
|
+
require "hurley/addressable"
|
20
|
+
|
21
|
+
client = Hurley::Client.new "https://api.github.com"
|
22
|
+
client.header[:accept] = "application/vnd.github+json"
|
23
|
+
client.query["a"] = "?a is set on every request too"
|
24
|
+
|
25
|
+
client.scheme # => "https"
|
26
|
+
client.host # => "api.github.com"
|
27
|
+
client.port # => 443
|
28
|
+
|
29
|
+
# See Hurley::RequestOptions in lib/hurley/options.rb
|
30
|
+
client.request_options.timeout = 3
|
31
|
+
|
32
|
+
# See Hurley::SslOptions in lib/hurley/options.rb
|
33
|
+
client.ssl_options.ca_file = "path/to/cert.crt"
|
34
|
+
|
35
|
+
# Verbs head, get, put, post, patch, delete, and options are supported.
|
36
|
+
response = client.get("users/tater") do |req|
|
37
|
+
# These properties can be changed on a per-request basis.
|
38
|
+
req.header[:accept] = "application/vnd.github.preview+json"
|
39
|
+
req.query["a"] = "override!"
|
40
|
+
|
41
|
+
req.options.timeout = 1
|
42
|
+
req.ssl_options.ca_file = "path/to/cert.crt"
|
43
|
+
|
44
|
+
req.verb # => :get
|
45
|
+
req.scheme # => "https"
|
46
|
+
req.host # => "api.github.com"
|
47
|
+
req.port # => 443
|
48
|
+
end
|
49
|
+
|
50
|
+
# You can also use Hurley class level shortcuts, which use Hurley.default_client.
|
51
|
+
response = Hurley.get("https://api.github.com/users/tater")
|
52
|
+
|
53
|
+
response.header[:content_type] # => "application/json"
|
54
|
+
response.status_code # => 200
|
55
|
+
response.body # => {"id": 1, ...}
|
56
|
+
response.request # => same as `request`
|
57
|
+
|
58
|
+
# Is this a 2xx response?
|
59
|
+
response.success?
|
60
|
+
|
61
|
+
# Is this a 3xx redirect?
|
62
|
+
response.redirection?
|
63
|
+
|
64
|
+
# Is this is a 4xx response?
|
65
|
+
response.client_error?
|
66
|
+
|
67
|
+
# Is this a 5xx response?
|
68
|
+
response.server_error?
|
69
|
+
|
70
|
+
# What kind of response is this?
|
71
|
+
response.status_type # => One of :success, :redirection, :client_error, :server_error, or :other
|
72
|
+
|
73
|
+
# Timing of the response, in ms
|
74
|
+
response.ms
|
75
|
+
|
76
|
+
# Responses automatically follow 5 redirections by default.
|
77
|
+
|
78
|
+
response.via # Array of Request objects that redirected.
|
79
|
+
response.location # => New Request built from Location header URL.
|
80
|
+
|
81
|
+
# You can tune the number of redirections, or disable them per Client or Request.
|
82
|
+
|
83
|
+
# This client follows up to 10 redirects
|
84
|
+
client.request_options.redirection_limit = 10
|
85
|
+
client.get "/foo" do |req|
|
86
|
+
# this specific request never follows any redirects.
|
87
|
+
req.options.redirection_limit = 0
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
## Connections
|
92
|
+
|
93
|
+
By default, a `Hurley::Client` uses a `Hurley::Connection` instance to make
|
94
|
+
requests with net/http. You can swap the connection with any object that
|
95
|
+
responds to `#call` with a Request, and returns a Response. This will not
|
96
|
+
interrupt other client properties or callbacks.
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
client = Hurley::Client.new "https://api.github.com"
|
100
|
+
client.connection = lambda do |req|
|
101
|
+
# return a Hurley::Response!
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
## URLs
|
106
|
+
|
107
|
+
Hurley joins a Client endpoint with a given request URL to produce the final
|
108
|
+
URL that is requested.
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
client = Hurley::Client.new "https://a:b@api.com/v1?a=1"
|
112
|
+
|
113
|
+
client.get "user" do |req|
|
114
|
+
req.url.user # => "a"
|
115
|
+
req.url.password # => "b"
|
116
|
+
req.url # https://api.com/v1/user?a=1
|
117
|
+
end
|
118
|
+
|
119
|
+
# Absolute paths remove any path prefix
|
120
|
+
client.get "/v2/user" do |req|
|
121
|
+
req.url.user # => "a"
|
122
|
+
req.url.password # => "b"
|
123
|
+
req.url # https://api.com/v2/user?a=1
|
124
|
+
end
|
125
|
+
|
126
|
+
client.get "user?a=2" do |req|
|
127
|
+
req.url.user # => "a"
|
128
|
+
req.url.password # => "b"
|
129
|
+
req.url # https://api.com/v1/user?a=2
|
130
|
+
end
|
131
|
+
|
132
|
+
# Basic auth can be overridden
|
133
|
+
client.get "https://c:d@api.com/v1/user" do |req|
|
134
|
+
req.url.user # => "c"
|
135
|
+
req.url.password # => "d"
|
136
|
+
req.url # https://api.com/v1/user?a=1
|
137
|
+
end
|
138
|
+
|
139
|
+
client.get "https://staging.api.com/v1/user" do |req|
|
140
|
+
req.url.user # => nil, since the host changed
|
141
|
+
req.url.password # => nil
|
142
|
+
req.url # https://staging.api.com/v1/user
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
Hurley uses `Hurley::Query::Nested` for all query encoding and decoding by
|
147
|
+
default. This can be changed globally, per client, or per request. Typically
|
148
|
+
you won't create `Hurley::Query` instances manually, and will use
|
149
|
+
`Hurley::Query.parse` for parsing.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
# Nested queries
|
153
|
+
|
154
|
+
q = Hurley::Query::Nested.new(:a => [1,2], :h => {:a => 1})
|
155
|
+
q.to_query_string # => "a[]=1&a[]=2&h[a]=1"
|
156
|
+
|
157
|
+
Hurley::Query::Nested.parse(q.to_query_string)
|
158
|
+
# => #<Hurley::Query::Nested {"a"=>["1", "2"], "h"=>{"a"=>"1"}}>
|
159
|
+
|
160
|
+
# Flat queries
|
161
|
+
|
162
|
+
q = Hurley::Query::Flat.new(:a => [1,2])
|
163
|
+
q.to_query_string # => "a=1&a=2"
|
164
|
+
|
165
|
+
Hurley::Query::Flat.parse(q.to_query_string)
|
166
|
+
# => #<Hurley::Query::Nested {"a"=>["1", "2"]}>
|
167
|
+
|
168
|
+
# Change it globally.
|
169
|
+
Hurley.default = Hurley::Query::Flat
|
170
|
+
|
171
|
+
# Change it for just this client.
|
172
|
+
client = Hurley::Client.new
|
173
|
+
client.request_options.query_class = Hurley::Query::Flat
|
174
|
+
|
175
|
+
# Change it for just this request.
|
176
|
+
client.get "/foo" do |req|
|
177
|
+
req.options.query_class = Hurley::Query::Flat
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
## Headers
|
182
|
+
|
183
|
+
A Client's Header is passed down to each request. Header keys can be overridden
|
184
|
+
by the request. Headers are stored internally in canonical form, which is
|
185
|
+
capitalized with dashes: `"Content-Type"`, for example.
|
186
|
+
|
187
|
+
See `Hurley::Header` for all of the common header keys that have symbolized
|
188
|
+
shortcuts.
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
client = Hurley::Client.new "https://api.com"
|
192
|
+
client.header[:content_type] = "application/json"
|
193
|
+
|
194
|
+
# Same as:
|
195
|
+
client.header["content-type"] = "application/json"
|
196
|
+
client.header["Content-Type"] = "application/json"
|
197
|
+
|
198
|
+
client.get "/something.atom" do |req|
|
199
|
+
# Default user agent
|
200
|
+
req.header[:user_agent] # => "Hurley v#{Hurley::VERSION}"
|
201
|
+
|
202
|
+
# Change a header
|
203
|
+
req.header[:content_type] = "application/atom"
|
204
|
+
end
|
205
|
+
```
|
206
|
+
|
207
|
+
## Posting Forms
|
208
|
+
|
209
|
+
Hurley will encode form bodies for you, while setting default Content-Type and
|
210
|
+
Content-Length values as necessary. Multipart forms are supported too, using
|
211
|
+
`Hurley::UploadIO` objects.
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
# Works with HTTP verbs: post, put, and patch
|
215
|
+
|
216
|
+
# Send a=1 with Content-Type: application/x-www-form-urlencoded
|
217
|
+
client.post("/form", :a => 1)
|
218
|
+
|
219
|
+
# Send a=1 with Content-Type: text/plain
|
220
|
+
client.post("/form", {:a => 1}, "text/plain")
|
221
|
+
|
222
|
+
# Send file with Content-Type: multipart/form-data
|
223
|
+
client.post("/multipart", :file => Hurley::UploadIO.new("filename.txt", "text/plain"))
|
224
|
+
```
|
225
|
+
|
226
|
+
The default query parser (`Hurley::Query::Nested`) is used by default. You can
|
227
|
+
change it globally, per client, or per request. See the "URLs" section.
|
228
|
+
|
229
|
+
## Client Callbacks
|
230
|
+
|
231
|
+
Clients can define "before callbacks" that yield a Request, or "after callbacks"
|
232
|
+
that yield a Response. Multiple callbacks of the same type are added in order.
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
client.before_call do |req|
|
236
|
+
# modify request before it's called
|
237
|
+
end
|
238
|
+
|
239
|
+
client.before_callbacks # => ["#<Proc:...>"]
|
240
|
+
|
241
|
+
client.after_call do |res|
|
242
|
+
# modify response after it's called
|
243
|
+
end
|
244
|
+
|
245
|
+
# You can set a name to identify the callback
|
246
|
+
client.before_call :upcase do |req|
|
247
|
+
req.body.upcase!
|
248
|
+
end
|
249
|
+
|
250
|
+
client.before_callbacks # => [:upcase]
|
251
|
+
|
252
|
+
# You can also pass an object that responds to #call and #name.
|
253
|
+
class Upcaser
|
254
|
+
def name
|
255
|
+
:upcaser
|
256
|
+
end
|
257
|
+
|
258
|
+
def call(req)
|
259
|
+
req.body.upcase!
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
client.before_call(Upcaser.new)
|
264
|
+
client.before_callbacks # => [:upcaser]
|
265
|
+
```
|
266
|
+
|
267
|
+
## Streaming the Response Body
|
268
|
+
|
269
|
+
A Request object can take a callback that receives the response body in chunks
|
270
|
+
as they are read from the socket. Hurley connections that don't support
|
271
|
+
streaming will yield the entire response body once.
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
client.get "big-file" do |req|
|
275
|
+
req.on_body do |res, chunk|
|
276
|
+
puts "#{res.status_code}: #{chunk}"
|
277
|
+
end
|
278
|
+
|
279
|
+
# This streams the body for 200 or 201 responses only:
|
280
|
+
req.on_body(200, 201) do |res, chunk|
|
281
|
+
puts "#{res.status_code}: #{chunk}"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
## Testing
|
287
|
+
|
288
|
+
Hurley includes a Test connection object for testing. This lets you make
|
289
|
+
requests without hitting a real endpoint.
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
require "hurley"
|
293
|
+
require "hurley/test"
|
294
|
+
|
295
|
+
client = Hurley::Client.new "https://api.github.com"
|
296
|
+
|
297
|
+
client.connection = Hurley::Test.new do |test|
|
298
|
+
# Verbs head, get, put, post, patch, delete, and options are supported.
|
299
|
+
test.get "/user" do |req|
|
300
|
+
# req is a Hurley::Request
|
301
|
+
# Return a Rack-compatible response.
|
302
|
+
[200, {"Content-Type" => "application/json"}, %({"id": 1})]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
client.get("/user").body # => {"id": 1}
|
307
|
+
```
|
308
|
+
|
309
|
+
## TODO
|
310
|
+
|
311
|
+
* [ ] Backport Faraday adapters as gems
|
312
|
+
* [ ] Integrate into Faraday reliant gems:
|
313
|
+
* [ ] [Sawyer](https://github.com/lostisland/sawyer)
|
314
|
+
* [ ] [Octokit](https://github.com/octokit/octokit.rb)
|
315
|
+
* [ ] [Elastomer](https://github.com/github/elastomer-client)
|
316
|
+
* [ ] Fix allll the bugs
|
317
|
+
* [ ] Release v1.0
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path("../lib/hurley/tasks", __FILE__)
|
data/contributors.yaml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Contributors
|
2
|
+
# https://github.com/orgs/lostisland/teams/owners
|
3
|
+
# https://github.com/orgs/lostisland/teams/faraday-collaborators
|
4
|
+
# Change your name, or add your email if you want to be credited in the gemspec.
|
5
|
+
# Ordered according to https://github.com/lostisland/hurley/graphs/contributors
|
6
|
+
Rick Olson: technoweenie@gmail.com
|
7
|
+
Wynn Netherland:
|
8
|
+
Ben Maraney:
|
data/hurley.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = "hurley"
|
3
|
+
lib_file = File.expand_path("../lib/#{lib}.rb", __FILE__)
|
4
|
+
File.read(lib_file) =~ /\bVERSION\s*=\s*["'](.+?)["']/
|
5
|
+
version = $1
|
6
|
+
|
7
|
+
require "yaml"
|
8
|
+
contributors = YAML.load(IO.read(File.expand_path("../contributors.yaml", __FILE__)))
|
9
|
+
|
10
|
+
Gem::Specification.new do |spec|
|
11
|
+
spec.add_development_dependency "addressable", "~> 2.3", ">= 2.3.6"
|
12
|
+
spec.add_development_dependency "bundler", "~> 1.0"
|
13
|
+
spec.add_development_dependency "minitest", "~> 5.5", ">= 5.5.0"
|
14
|
+
spec.add_development_dependency "rake", "~> 10.4.0", ">= 10.4.2"
|
15
|
+
spec.add_development_dependency "sinatra", "~> 1.4", ">= 1.4.5"
|
16
|
+
spec.authors = contributors.keys.compact
|
17
|
+
spec.description = %q{Hurley provides a common interface for working with different HTTP adapters.}
|
18
|
+
spec.email = contributors.values.compact
|
19
|
+
spec.homepage = "https://github.com/lostisland/hurley"
|
20
|
+
dev_null = File.exist?("/dev/null") ? "/dev/null" : "NUL"
|
21
|
+
git_files = `git ls-files -z 2>#{dev_null}`
|
22
|
+
spec.files = git_files.split("\0") if $?.success?
|
23
|
+
spec.test_files = Dir.glob("test/**/*.rb")
|
24
|
+
spec.licenses = ["MIT"]
|
25
|
+
spec.name = lib
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
spec.summary = "HTTP client wrapper"
|
28
|
+
spec.version = version
|
29
|
+
end
|