hyperclient 0.5.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1bba91bc97f25344d522646fec5c34cb74b183d4
4
- data.tar.gz: b8289075f6c69400d84da795751f404030b9fdaa
3
+ metadata.gz: 743f6bc67f84cca3d8e2994ad47a38d7e031a300
4
+ data.tar.gz: 689b06b2eb057ca7bd28e74071ecca60ba954344
5
5
  SHA512:
6
- metadata.gz: 26e276baa5ea49d3e54c47b308c12efad26bb259cacc620cab7b44d84ff61e17296b904ea738b3a7917044044e37653944ef854577335575bc6f5d1f46b4a767
7
- data.tar.gz: 69d655ed12c311619476b43b595b7705ad69d590a960a356775b0cdb554c95f6b20d4a9b0f3dc34ee9b1281566a8aee3e5881de99f32a2d04321bf8eb9b6bee8
6
+ metadata.gz: 17ea7ceb37ecc125395b13d37caf7e134b7b9ef1e21ee258a1d92309f2cc3fadbe398916c3642b50cda8ccd62260b57d5eea3b38f786c4c03a2fea5a97f6e1f2
7
+ data.tar.gz: 1f5b27c757676834dfb1aaa33741b7be19ce09e9822c869f4ecabcaceeaac868799eb96a92e0b4ecdc0290e807a0fc6fff6cba924f2270ef0e01204cb8e08e77
data/.rubocop_todo.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2014-09-18 12:21:47 -0400 using RuboCop version 0.26.0.
2
+ # on 2014-10-17 09:13:36 -0400 using RuboCop version 0.26.1.
3
3
  # The point is for the user to remove these configuration records
4
4
  # one by one as the offenses are removed from the code base.
5
5
  # Note that changes in the inspected code, or installation of new
@@ -8,26 +8,18 @@
8
8
  # Offense count: 1
9
9
  # Configuration parameters: CountComments.
10
10
  Metrics/ClassLength:
11
- Max: 118
11
+ Max: 129
12
12
 
13
- # Offense count: 2
14
- Metrics/CyclomaticComplexity:
15
- Max: 8
16
-
17
- # Offense count: 39
18
- # Configuration parameters: AllowURI.
13
+ # Offense count: 72
14
+ # Configuration parameters: AllowURI, URISchemes.
19
15
  Metrics/LineLength:
20
- Max: 145
16
+ Max: 140
21
17
 
22
- # Offense count: 1
18
+ # Offense count: 3
23
19
  # Configuration parameters: CountComments.
24
20
  Metrics/MethodLength:
25
21
  Max: 14
26
22
 
27
- # Offense count: 2
28
- Metrics/PerceivedComplexity:
29
- Max: 9
30
-
31
23
  # Offense count: 1
32
24
  Style/AsciiComments:
33
25
  Enabled: false
@@ -41,11 +33,11 @@ Style/ClassAndModuleChildren:
41
33
  Style/Documentation:
42
34
  Enabled: false
43
35
 
44
- # Offense count: 1
36
+ # Offense count: 2
45
37
  Style/DoubleNegation:
46
38
  Enabled: false
47
39
 
48
- # Offense count: 3
40
+ # Offense count: 5
49
41
  Style/Lambda:
50
42
  Enabled: false
51
43
 
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ### Next
2
+
3
+ * Your contribution here.
4
+
5
+ ### 0.6.1 (October 17, 2014)
6
+
7
+ This version introduces several backwards incompatible changes. See [UPGRADING](UPGRADING.md) for details.
8
+
9
+ * [#51](https://github.com/codegram/hyperclient/issues/51), [#75](https://github.com/codegram/hyperclient/pull/75): Added support for setting headers and overriding or extending the default Faraday connection block before a connection is constructed - [@dblock](https://github.com/dblock).
10
+ * [#41](https://github.com/codegram/hyperclient/issues/41), [#73](https://github.com/codegram/hyperclient/pull/73): All Link HTTP methods now return a Resource, including `_get`, which has been aliased to `_resource`, `_post`, `_put`, `_patch`, `_head` and `_options` - [@dblock](https://github.com/dblock).
11
+ * [#72](https://github.com/codegram/hyperclient/pull/72): The default Faraday block now uses `Faraday::Response::RaiseError` and will cause HTTP errors to be raised as exceptions - [@dblock](https://github.com/dblock).
12
+ * [#77](https://github.com/codegram/hyperclient/pull/77): Added support for templated links with all optional arguments - [@dblock](https://github.com/dblock).
13
+
1
14
  ### 0.5.0 (October 1, 2014)
2
15
 
3
16
  This version introduces several backwards incompatible changes. See [UPGRADING](UPGRADING.md) for details.
data/README.md CHANGED
@@ -8,7 +8,7 @@ Hyperclient is a Hypermedia API client written in Ruby. It fully supports [JSON
8
8
 
9
9
  ## Usage
10
10
 
11
- The examples in this README use the [Splines Demo API](https://github.com/dblock/grape-with-roar) running [here](https://grape-with-roar.herokuapp.com/api).
11
+ The examples in this README use the [Splines Demo API](https://github.com/dblock/grape-with-roar) running [here](https://grape-with-roar.herokuapp.com/api). If you're upgrading from a previous version, please make sure to read [UPGRADING](UPGRADING.md).
12
12
 
13
13
  ### API Client
14
14
 
@@ -20,15 +20,51 @@ require 'hyperclient'
20
20
  api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api')
21
21
  ```
22
22
 
23
- By default, Hyperclient adds `application/json` as `Content-Type` and `Accept` headers. It will also send requests as JSON and parse JSON responses. Specify additional headers or authentication if necessary. Hyperclient supports Basic, Token or Digest auth as well as many other [Faraday](http://github.com/lostisland/faraday) extensions.
23
+ By default, Hyperclient adds `application/json` as `Content-Type` and `Accept` headers. It will also send requests as JSON and parse JSON responses. Specify additional headers or authentication if necessary.
24
24
 
25
25
  ```ruby
26
- api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api').tap do |api|
27
- api.digest_auth('username', 'password')
28
- api.headers.update('Accept-Encoding' => 'deflate, gzip')
26
+ api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api') do |client|
27
+ client.headers['Access-Token'] = 'token'
29
28
  end
30
29
  ```
31
30
 
31
+ Hyperclient constructs a connection using typical [Faraday](http://github.com/lostisland/faraday) middleware for handling JSON requests and responses. You can specify additional Faraday middleware if necessary.
32
+
33
+ ```ruby
34
+ api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api') do |client|
35
+ client.connection do |conn|
36
+ conn.use Faraday::Request::OAuth
37
+ end
38
+ end
39
+ ```
40
+
41
+ You can build a new Faraday connection block without inheriting default middleware by specifying `default: false` in the `connection` block.
42
+
43
+ ```ruby
44
+ api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api') do |client|
45
+ client.connection(default: false) do |conn|
46
+ conn.request :json
47
+ conn.response :json, content_type: /\bjson$/
48
+ conn.adapter :net_http
49
+ end
50
+ end
51
+ ```
52
+
53
+ You can modify headers or specify authentication after a connection has been created. Hyperclient supports Basic, Token or Digest auth as well as many other Faraday extensions.
54
+
55
+ ```ruby
56
+ api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api')
57
+ api.digest_auth('username', 'password')
58
+ api.headers.update('Accept-Encoding' => 'deflate, gzip')
59
+ ```
60
+
61
+ You can access the Faraday connection directly after it has been created and add middleware to it. As an example, you could use the [faraday-http-cache-middleware](https://github.com/plataformatec/faraday-http-cache).
62
+
63
+ ```ruby
64
+ api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api')
65
+ api.connection.use :http_cache
66
+ ```
67
+
32
68
  ### Resources and Attributes
33
69
 
34
70
  Hyperclient will fetch and discover the resources from your API.
@@ -58,6 +94,8 @@ puts "Spline #{spline.uuid} is #{spline.reticulated ? 'reticulated' : 'not retic
58
94
 
59
95
  Invoking `api.spline(uuid: 'uuid').reticulated` is equivalent to `api._links.spline._expand(uuid: 'uuid')._resource._attributes.reticulated`.
60
96
 
97
+ The client is responsible for supplying all the necessary parameters. Templated links don't do any strict parameter name checking and don't support required vs. optional parameters. Parameters not declared by the API will be dropped and will not have any effect when passed to `_expand`.
98
+
61
99
  ### Curies
62
100
 
63
101
  Curies are named tokens that you can define in the document and use to express curie relation URIs in a friendlier, more compact fashion. For example, the demo API contains very long links to images that use an "images" curie. Hyperclient handles curies and resolves these into full links automatically.
@@ -117,14 +155,35 @@ spline = api.spline(uuid: 'uuid')
117
155
  spline._delete
118
156
  ```
119
157
 
120
- ### Faraday Connection
158
+ HTTP methods always return a new instance of Resource.
159
+
160
+ ## Testing Using Hyperclient
121
161
 
122
- You can access the Faraday connection directly to add middleware by calling `connection` on the entry point. As an example, you could use the [faraday-http-cache-middleware](https://github.com/plataformatec/faraday-http-cache).
162
+ You can combine RSpec, Faraday::Adapter::Rack and Hyperclient to test your HAL API without having to ever examine the raw JSON response.
123
163
 
124
164
  ```ruby
125
- api.connection.use :http_cache
165
+ describe Acme::Api do
166
+ def app
167
+ Acme::App.instance
168
+ end
169
+
170
+ let(:client) do
171
+ Hyperclient.new('http://example.org/api') do |client|
172
+ client.connection(default: false) do |conn|
173
+ conn.response :json
174
+ conn.use Faraday::Adapter::Rack, app
175
+ end
176
+ end
177
+ end
178
+
179
+ it 'splines returns 3 splines by default' do
180
+ expect(client.splines.count).to eq 3
181
+ end
182
+ end
126
183
  ```
127
184
 
185
+ For a complete example refer to [this Splines Demo API test](https://github.com/dblock/grape-with-roar/blob/master/spec/api/splines_endpoint_with_hyperclient_spec.rb).
186
+
128
187
  ## Reference
129
188
 
130
189
  [Hyperclient API Reference](http://rubydoc.org/github/codegram/hyperclient/master/frames).
data/UPGRADING.md CHANGED
@@ -1,6 +1,20 @@
1
1
  Upgrading Hyperclient
2
2
  =====================
3
3
 
4
+ ### Upgrading to >= 0.6.0
5
+
6
+ #### Changes in HTTP Error Handling
7
+
8
+ The default Faraday block now uses `Faraday::Response::RaiseError` and will cause HTTP errors to be raised as exceptions. Older versions of Hyperclient swallowed the error and returned an empty resource. If you relied on checking for an HTTP response `status`, rescue `Faraday::ClientError`.
9
+
10
+ #### Changes in Values Returned from HTTP Methods
11
+
12
+ The `Link#_get` method has been aliased to `_resource`. All HTTP methods, including `_post`, `_put`, `_delete`, `_patch`, `_options` and `_head` now return instances of Resource. Older versions returned a `Faraday::Response`.
13
+
14
+ #### Changes in URI Template Expansion
15
+
16
+ A `MissingURITemplateVariablesException` exception will no longer be raised when expanding a link with no arguments. The `Link#_expand` method will now also accept zero arguments and default the variables to `{}`. This enables support for templated links with all optional arguments.
17
+
4
18
  ### Upgrading to >= 0.5.0
5
19
 
6
20
  #### Remove Navigational Elements
@@ -1,7 +1,9 @@
1
1
  require 'hyperclient'
2
2
 
3
+ # create a new client
3
4
  api = Hyperclient.new('https://grape-with-roar.herokuapp.com/api')
4
5
 
6
+ # enumerate splines
5
7
  api.splines.each do |spline|
6
8
  puts "#{spline.uuid}"
7
9
  puts " reticulated: #{spline.reticulated ? 'yes' : 'no'}"
@@ -14,9 +16,22 @@ end
14
16
 
15
17
  puts '*' * 10
16
18
 
17
- spline = api.spline(uuid: 'random-uuid')
19
+ # retrieve an existing spline
20
+ spline = api.spline(uuid: 123)
18
21
  puts "Spline #{spline.uuid} is #{spline.reticulated ? 'reticulated' : 'not reticulated'}."
19
22
 
20
23
  # puts api._links.spline._expand(uuid: 'uuid')._resource._attributes.reticulated
21
24
 
22
25
  # spline.to_h
26
+
27
+ # create a new spline
28
+ spline = api.splines._post(reticulated: true)
29
+ puts "Created a #{spline.reticulated ? 'reticulated' : 'unreticulated'} spline #{spline.uuid}."
30
+
31
+ # update an existing spline
32
+ spline = api.spline(uuid: 123)._put(reticulated: true)
33
+ puts "Updated spline #{spline.uuid}, now #{spline.reticulated ? 'reticulated' : 'not reticulated'}."
34
+
35
+ # delete an existing spline
36
+ spline = api.spline(uuid: 123)._delete
37
+ puts "Deleted spline #{spline.uuid}."
@@ -11,7 +11,7 @@ class Spinach::Features::DefaultConfig < Spinach::FeatureSteps
11
11
 
12
12
  step 'I send some data to the API' do
13
13
  stub_request(:post, 'http://api.example.org/posts')
14
- assert_equal 200, api._links.posts._post(title: 'My first blog post').status
14
+ assert_equal 200, api._links.posts._post(title: 'My first blog post')._response.status
15
15
  end
16
16
 
17
17
  step 'it should have been encoded as JSON' do
data/lib/hyperclient.rb CHANGED
@@ -9,7 +9,7 @@ module Hyperclient
9
9
  # url - A String with the url of the API.
10
10
  #
11
11
  # Returns a Hyperclient::EntryPoint
12
- def self.new(url)
13
- Hyperclient::EntryPoint.new(url)
12
+ def self.new(url, &block)
13
+ Hyperclient::EntryPoint.new(url, &block)
14
14
  end
15
15
  end
@@ -3,6 +3,15 @@ require 'faraday_middleware'
3
3
  require_relative '../faraday/connection'
4
4
 
5
5
  module Hyperclient
6
+ # Public: Exception that is raised when trying to modify an
7
+ # already initialized connection.
8
+ class ConnectionAlreadyInitializedError < StandardError
9
+ # Public: Returns a String with the exception message.
10
+ def message
11
+ 'The connection has already been initialized.'
12
+ end
13
+ end
14
+
6
15
  # Public: The EntryPoint is the main public API for Hyperclient. It is used to
7
16
  # initialize an API client and setup the configuration.
8
17
  #
@@ -10,6 +19,13 @@ module Hyperclient
10
19
  #
11
20
  # client = Hyperclient::EntryPoint.new('http://my.api.org')
12
21
  #
22
+ # client = Hyperclient::EntryPoint.new('http://my.api.org') do |entry_point|
23
+ # entry_point.connection(default: true) do |conn|
24
+ # conn.use Faraday::Request::OAuth
25
+ # end
26
+ # entry_point.headers['Access-Token'] = 'token'
27
+ # end
28
+ #
13
29
  class EntryPoint < Link
14
30
  extend Forwardable
15
31
  # Public: Delegates common methods to be used with the Faraday connection.
@@ -18,16 +34,64 @@ module Hyperclient
18
34
  # Public: Initializes an EntryPoint.
19
35
  #
20
36
  # url - A String with the entry point of your API.
21
- def initialize(url)
37
+ def initialize(url, &_block)
22
38
  @link = { 'href' => url }
23
39
  @entry_point = self
40
+ yield self if block_given?
24
41
  end
25
42
 
26
43
  # Public: A Faraday connection to use as a HTTP client.
27
44
  #
45
+ # options - A Hash containing additional options.
46
+ #
47
+ # default - Set to true to reuse default Faraday connection options.
48
+ #
28
49
  # Returns a Faraday::Connection.
29
- def connection
30
- @connection ||= Faraday.new(_url, { headers: default_headers }, &default_faraday_block)
50
+ def connection(options = { default: true }, &block)
51
+ if block_given?
52
+ fail ConnectionAlreadyInitializedError if @connection
53
+ if options[:default]
54
+ @faraday_block = lambda do |conn|
55
+ default_faraday_block.call conn
56
+ block.call conn
57
+ end
58
+ else
59
+ @faraday_block = block
60
+ end
61
+ else
62
+ @connection ||= Faraday.new(_url, { headers: headers }, &faraday_block)
63
+ end
64
+ end
65
+
66
+ # Public: Set headers.
67
+ #
68
+ # value - A Hash containing headers to include with every API request.
69
+ def headers=(value)
70
+ fail ConnectionAlreadyInitializedError if @connection
71
+ @headers = value
72
+ end
73
+
74
+ # Public: Headers included with every API request.
75
+ #
76
+ # Returns a Hash.
77
+ def headers
78
+ return @connection.headers if @connection
79
+ @headers ||= default_headers
80
+ end
81
+
82
+ # Public: Faraday block used with every API request.
83
+ #
84
+ # Returns a Proc.
85
+ def faraday_block
86
+ @faraday_block ||= default_faraday_block
87
+ end
88
+
89
+ # Public: Set a Faraday block to use with every API request.
90
+ #
91
+ # value - A Proc accepting a Faraday::Connection.
92
+ def faraday_block=(value)
93
+ fail ConnectionAlreadyInitializedError if @connection
94
+ @faraday_block = value
31
95
  end
32
96
 
33
97
  private
@@ -42,11 +106,12 @@ module Hyperclient
42
106
  #
43
107
  # Returns a block.
44
108
  def default_faraday_block
45
- lambda do |faraday|
46
- faraday.use FaradayMiddleware::FollowRedirects
47
- faraday.request :json
48
- faraday.response :json, content_type: /\bjson$/
49
- faraday.adapter :net_http
109
+ lambda do |conn|
110
+ conn.use Faraday::Response::RaiseError
111
+ conn.use FaradayMiddleware::FollowRedirects
112
+ conn.request :json
113
+ conn.response :json, content_type: /\bjson$/
114
+ conn.adapter :net_http
50
115
  end
51
116
  end
52
117
 
@@ -33,19 +33,14 @@ module Hyperclient
33
33
  # uri_variables - The Hash with the variables to expand the URITemplate.
34
34
  #
35
35
  # Returns a new Link with the expanded variables.
36
- def _expand(uri_variables)
36
+ def _expand(uri_variables = {})
37
37
  self.class.new(@key, @link, @entry_point, uri_variables)
38
38
  end
39
39
 
40
40
  # Public: Returns the url of the Link.
41
- #
42
- # Raises MissingURITemplateVariables if the Link is templated but there are
43
- # no uri variables to expand it.
44
41
  def _url
45
42
  return @link['href'] unless _templated?
46
- fail MissingURITemplateVariablesException if @uri_variables.nil?
47
-
48
- @url ||= _uri_template.expand(@uri_variables)
43
+ @url ||= _uri_template.expand(@uri_variables || {})
49
44
  end
50
45
 
51
46
  # Public: Returns an array of variables from the URITemplate.
@@ -86,61 +81,74 @@ module Hyperclient
86
81
  end
87
82
 
88
83
  # Public: Returns the Resource which the Link is pointing to.
89
- def _resource
90
- @resource ||= begin
91
- response = _get
92
-
93
- if response.success?
94
- Resource.new(response.body, @entry_point, response)
95
- else
96
- Resource.new(nil, @entry_point, response)
84
+ def _get
85
+ @resource = begin
86
+ response = Futuroscope::Future.new do
87
+ _connection.get(_url)
97
88
  end
89
+ Resource.new(response.body, @entry_point, response)
98
90
  end
99
91
  end
100
92
 
101
- def _connection
102
- @entry_point.connection
93
+ def _resource
94
+ @resource || _get
103
95
  end
104
96
 
105
- def _get
106
- Futuroscope::Future.new do
107
- _connection.get(_url)
108
- end
97
+ def _connection
98
+ @entry_point.connection
109
99
  end
110
100
 
111
101
  def _options
112
- Futuroscope::Future.new do
113
- _connection.run_request(:options, _url, nil, nil)
102
+ @resource = begin
103
+ response = Futuroscope::Future.new do
104
+ _connection.run_request(:options, _url, nil, nil)
105
+ end
106
+ Resource.new(response.body, @entry_point, response)
114
107
  end
115
108
  end
116
109
 
117
110
  def _head
118
- Futuroscope::Future.new do
119
- _connection.head(_url)
111
+ @resource = begin
112
+ response = Futuroscope::Future.new do
113
+ _connection.head(_url)
114
+ end
115
+ Resource.new(response.body, @entry_point, response)
120
116
  end
121
117
  end
122
118
 
123
119
  def _delete
124
- Futuroscope::Future.new do
125
- _connection.delete(_url)
120
+ @resource = begin
121
+ response = Futuroscope::Future.new do
122
+ _connection.delete(_url)
123
+ end
124
+ Resource.new(response.body, @entry_point, response)
126
125
  end
127
126
  end
128
127
 
129
128
  def _post(params = {})
130
- Futuroscope::Future.new do
131
- _connection.post(_url, params)
129
+ @resource = begin
130
+ response = Futuroscope::Future.new do
131
+ _connection.post(_url, params)
132
+ end
133
+ Resource.new(response.body, @entry_point, response)
132
134
  end
133
135
  end
134
136
 
135
137
  def _put(params = {})
136
- Futuroscope::Future.new do
137
- _connection.put(_url, params)
138
+ @resource = begin
139
+ response = Futuroscope::Future.new do
140
+ _connection.put(_url, params)
141
+ end
142
+ Resource.new(response.body, @entry_point, response)
138
143
  end
139
144
  end
140
145
 
141
146
  def _patch(params = {})
142
- Futuroscope::Future.new do
143
- _connection.patch(_url, params)
147
+ @resource = begin
148
+ response = Futuroscope::Future.new do
149
+ _connection.patch(_url, params)
150
+ end
151
+ Resource.new(response.body, @entry_point, response)
144
152
  end
145
153
  end
146
154
 
@@ -191,13 +199,4 @@ module Hyperclient
191
199
  @uri_template ||= URITemplate.new(@link['href'])
192
200
  end
193
201
  end
194
-
195
- # Public: Exception that is raised when building a templated Link without uri
196
- # variables.
197
- class MissingURITemplateVariablesException < StandardError
198
- # Public: Returns a String with the exception message.
199
- def message
200
- 'The URL to this links is templated, but no variables where given.'
201
- end
202
- end
203
202
  end
@@ -1,3 +1,3 @@
1
1
  module Hyperclient
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.1'
3
3
  end
@@ -3,8 +3,76 @@ require 'hyperclient/entry_point'
3
3
 
4
4
  module Hyperclient
5
5
  describe EntryPoint do
6
+ describe 'default' do
7
+ let(:entry_point) do
8
+ EntryPoint.new 'http://my.api.org'
9
+ end
10
+
11
+ describe 'connection' do
12
+ it 'creates a Faraday connection with the entry point url' do
13
+ entry_point.connection.url_prefix.to_s.must_equal 'http://my.api.org/'
14
+ end
15
+
16
+ it 'creates a Faraday connection with the default headers' do
17
+ entry_point.headers['Content-Type'].must_equal 'application/json'
18
+ entry_point.headers['Accept'].must_equal 'application/json'
19
+ end
20
+
21
+ it 'can update headers after a connection has been constructed' do
22
+ entry_point.connection.must_be_kind_of Faraday::Connection
23
+ entry_point.headers.update('Content-Type' => 'application/foobar')
24
+ entry_point.headers['Content-Type'].must_equal 'application/foobar'
25
+ end
26
+
27
+ it 'can insert additional middleware after a connection has been constructed' do
28
+ entry_point.connection.must_be_kind_of Faraday::Connection
29
+ entry_point.connection.use :instrumentation
30
+ handlers = entry_point.connection.builder.handlers
31
+ handlers.must_include FaradayMiddleware::Instrumentation
32
+ end
33
+
34
+ it 'creates a Faraday connection with the default block' do
35
+ handlers = entry_point.connection.builder.handlers
36
+ handlers.must_include Faraday::Response::RaiseError
37
+ handlers.must_include FaradayMiddleware::FollowRedirects
38
+ handlers.must_include FaradayMiddleware::EncodeJson
39
+ handlers.must_include FaradayMiddleware::ParseJson
40
+ handlers.must_include Faraday::Adapter::NetHttp
41
+ end
42
+
43
+ it 'raises a ConnectionAlreadyInitializedError if attempting to modify headers' do
44
+ entry_point.connection.must_be_kind_of Faraday::Connection
45
+ lambda { entry_point.headers = {} }.must_raise ConnectionAlreadyInitializedError
46
+ end
47
+
48
+ it 'raises a ConnectionAlreadyInitializedError if attempting to modify the faraday block' do
49
+ entry_point.connection.must_be_kind_of Faraday::Connection
50
+ lambda { entry_point.connection {} }.must_raise ConnectionAlreadyInitializedError
51
+ end
52
+ end
53
+
54
+ describe 'initialize' do
55
+ it 'sets a Link with the entry point url' do
56
+ entry_point._url.must_equal 'http://my.api.org'
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ describe 'custom' do
6
63
  let(:entry_point) do
7
- EntryPoint.new 'http://my.api.org'
64
+ EntryPoint.new 'http://my.api.org' do |entry_point|
65
+ entry_point.connection(default: false) do |conn|
66
+ conn.request :json
67
+ conn.response :json, content_type: /\bjson$/
68
+ conn.adapter :net_http
69
+ end
70
+
71
+ entry_point.headers = {
72
+ 'Content-Type' => 'application/foobar',
73
+ 'Accept' => 'application/foobar'
74
+ }
75
+ end
8
76
  end
9
77
 
10
78
  describe 'connection' do
@@ -12,23 +80,51 @@ module Hyperclient
12
80
  entry_point.connection.url_prefix.to_s.must_equal 'http://my.api.org/'
13
81
  end
14
82
 
15
- it 'creates a Faraday connection with the default headers' do
16
- entry_point.headers['Content-Type'].must_equal 'application/json'
17
- entry_point.headers['Accept'].must_equal 'application/json'
83
+ it 'creates a Faraday connection with non-default headers' do
84
+ entry_point.headers['Content-Type'].must_equal 'application/foobar'
85
+ entry_point.headers['Accept'].must_equal 'application/foobar'
18
86
  end
19
87
 
20
88
  it 'creates a Faraday connection with the default block' do
21
89
  handlers = entry_point.connection.builder.handlers
22
- handlers.must_include FaradayMiddleware::FollowRedirects
90
+ handlers.wont_include Faraday::Response::RaiseError
91
+ handlers.wont_include FaradayMiddleware::FollowRedirects
23
92
  handlers.must_include FaradayMiddleware::EncodeJson
24
93
  handlers.must_include FaradayMiddleware::ParseJson
25
94
  handlers.must_include Faraday::Adapter::NetHttp
26
95
  end
27
96
  end
97
+ end
98
+
99
+ describe 'inherited' do
100
+ let(:entry_point) do
101
+ EntryPoint.new 'http://my.api.org' do |entry_point|
102
+ entry_point.connection(default: true) do |conn|
103
+ conn.use Faraday::Request::OAuth
104
+ end
105
+ entry_point.headers['Access-Token'] = 'token'
106
+ end
107
+ end
108
+
109
+ describe 'connection' do
110
+ it 'creates a Faraday connection with the default and additional headers' do
111
+ entry_point.headers['Content-Type'].must_equal 'application/json'
112
+ entry_point.headers['Accept'].must_equal 'application/json'
113
+ entry_point.headers['Access-Token'].must_equal 'token'
114
+ end
28
115
 
29
- describe 'initialize' do
30
- it 'sets a Link with the entry point url' do
31
- entry_point._url.must_equal 'http://my.api.org'
116
+ it 'creates a Faraday connection with the entry point url' do
117
+ entry_point.connection.url_prefix.to_s.must_equal 'http://my.api.org/'
118
+ end
119
+
120
+ it 'creates a Faraday connection with the default block plus any additional handlers' do
121
+ handlers = entry_point.connection.builder.handlers
122
+ handlers.must_include Faraday::Request::OAuth
123
+ handlers.must_include Faraday::Response::RaiseError
124
+ handlers.must_include FaradayMiddleware::FollowRedirects
125
+ handlers.must_include FaradayMiddleware::EncodeJson
126
+ handlers.must_include FaradayMiddleware::ParseJson
127
+ handlers.must_include Faraday::Adapter::NetHttp
32
128
  end
33
129
  end
34
130
  end
@@ -51,24 +51,38 @@ module Hyperclient
51
51
  end
52
52
 
53
53
  describe '_expand' do
54
- it 'buils a Link with the templated URI representation' do
55
- link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
54
+ describe 'required argument' do
55
+ it 'builds a Link with the templated URI representation' do
56
+ link = Link.new('key', { 'href' => '/orders/{id}', 'templated' => true }, entry_point)
57
+ link._expand(id: '1')._url.must_equal '/orders/1'
58
+ end
56
59
 
57
- Link.expects(:new).with('key', anything, entry_point, id: '1')
58
- link._expand(id: '1')
60
+ it 'expands an uri template without variables' do
61
+ link = Link.new('key', { 'href' => '/orders/{id}', 'templated' => true }, entry_point)
62
+ link._expand._url.must_equal '/orders/'
63
+ link._url.must_equal '/orders/'
64
+ end
59
65
  end
60
66
 
61
- it 'raises if no uri variables are given' do
62
- link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
63
- lambda { link._expand }.must_raise ArgumentError
67
+ describe 'query string argument' do
68
+ it 'builds a Link with the templated URI representation' do
69
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
70
+ link._expand(id: '1')._url.must_equal '/orders?id=1'
71
+ end
72
+
73
+ it 'expands an uri template without variables' do
74
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
75
+ link._expand._url.must_equal '/orders'
76
+ link._url.must_equal '/orders'
77
+ end
64
78
  end
65
79
  end
66
80
 
67
81
  describe '_url' do
68
- it 'raises when missing required uri_variables' do
82
+ it 'expands an uri template without variables' do
69
83
  link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
70
84
 
71
- lambda { link._url }.must_raise MissingURITemplateVariablesException
85
+ link._url.must_equal '/orders'
72
86
  end
73
87
 
74
88
  it 'expands an uri template with variables' do
@@ -91,23 +105,10 @@ module Hyperclient
91
105
 
92
106
  describe '_resource' do
93
107
  it 'builds a resource with the link href representation' do
94
- mock_response = mock(body: {}, success?: true)
95
-
96
- Resource.expects(:new).with({}, entry_point, mock_response)
97
-
98
- link = Link.new('key', { 'href' => '/' }, entry_point)
99
- link.expects(:_get).returns(mock_response)
100
-
101
- link._resource
102
- end
103
-
104
- it 'has an empty body when the response fails' do
105
- mock_response = mock(success?: false)
106
-
107
- Resource.expects(:new).with(nil, entry_point, mock_response)
108
+ Resource.expects(:new)
108
109
 
109
110
  link = Link.new('key', { 'href' => '/' }, entry_point)
110
- link.expects(:_get).returns(mock_response)
111
+ stub_request(:get, 'http://api.example.org/').to_return(body: {})
111
112
 
112
113
  link._resource
113
114
  end
@@ -123,8 +124,15 @@ module Hyperclient
123
124
  it 'sends a GET request with the link url' do
124
125
  link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
125
126
 
126
- entry_point.connection.expects(:get).with('/productions/1')
127
- link._get.inspect
127
+ stub_request(:get, 'http://api.example.org/productions/1').to_return(body: nil)
128
+ link._get.must_be_kind_of Resource
129
+ end
130
+
131
+ it 'raises exceptions by default' do
132
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
133
+
134
+ stub_request(:get, 'http://api.example.org/productions/1').to_return(status: 400)
135
+ lambda { link._get }.must_raise Faraday::ClientError
128
136
  end
129
137
  end
130
138
 
@@ -132,26 +140,24 @@ module Hyperclient
132
140
  it 'sends a OPTIONS request with the link url' do
133
141
  link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
134
142
 
135
- entry_point.connection.expects(:run_request).with(:options, '/productions/1', nil, nil)
136
- link._options.inspect
143
+ stub_request(:options, 'http://api.example.org/productions/1').to_return(body: nil)
144
+ link._options.must_be_kind_of Resource
137
145
  end
138
146
  end
139
147
 
140
148
  describe '_head' do
141
149
  it 'sends a HEAD request with the link url' do
142
150
  link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
143
-
144
- entry_point.connection.expects(:head).with('/productions/1')
145
- link._head.inspect
151
+ stub_request(:head, 'http://api.example.org/productions/1').to_return(body: nil)
152
+ link._head.must_be_kind_of Resource
146
153
  end
147
154
  end
148
155
 
149
156
  describe '_delete' do
150
157
  it 'sends a DELETE request with the link url' do
151
158
  link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
152
-
153
- entry_point.connection.expects(:delete).with('/productions/1')
154
- link._delete.inspect
159
+ stub_request(:delete, 'http://api.example.org/productions/1').to_return(body: nil)
160
+ link._delete.must_be_kind_of Resource
155
161
  end
156
162
  end
157
163
 
@@ -159,13 +165,13 @@ module Hyperclient
159
165
  let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
160
166
 
161
167
  it 'sends a POST request with the link url and params' do
162
- entry_point.connection.expects(:post).with('/productions/1', 'foo' => 'bar')
163
- link._post('foo' => 'bar').inspect
168
+ stub_request(:post, 'http://api.example.org/productions/1').to_return(body: nil)
169
+ link._post('foo' => 'bar').must_be_kind_of Resource
164
170
  end
165
171
 
166
172
  it 'defaults params to an empty hash' do
167
- entry_point.connection.expects(:post).with('/productions/1', {})
168
- link._post.inspect
173
+ stub_request(:post, 'http://api.example.org/productions/1').to_return(body: nil)
174
+ link._post.must_be_kind_of Resource
169
175
  end
170
176
  end
171
177
 
@@ -173,13 +179,13 @@ module Hyperclient
173
179
  let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
174
180
 
175
181
  it 'sends a PUT request with the link url and params' do
176
- entry_point.connection.expects(:put).with('/productions/1', 'foo' => 'bar')
177
- link._put('foo' => 'bar').inspect
182
+ stub_request(:put, 'http://api.example.org/productions/1').with(body: '{"foo":"bar"}').to_return(body: nil)
183
+ link._put('foo' => 'bar').must_be_kind_of Resource
178
184
  end
179
185
 
180
186
  it 'defaults params to an empty hash' do
181
- entry_point.connection.expects(:put).with('/productions/1', {})
182
- link._put.inspect
187
+ stub_request(:put, 'http://api.example.org/productions/1').to_return(body: nil)
188
+ link._put.must_be_kind_of Resource
183
189
  end
184
190
  end
185
191
 
@@ -187,13 +193,13 @@ module Hyperclient
187
193
  let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
188
194
 
189
195
  it 'sends a PATCH request with the link url and params' do
190
- entry_point.connection.expects(:patch).with('/productions/1', 'foo' => 'bar')
191
- link._patch('foo' => 'bar').inspect
196
+ stub_request(:patch, 'http://api.example.org/productions/1').with(body: '{"foo":"bar"}').to_return(body: nil)
197
+ link._patch('foo' => 'bar').must_be_kind_of Resource
192
198
  end
193
199
 
194
200
  it 'defaults params to an empty hash' do
195
- entry_point.connection.expects(:patch).with('/productions/1', {})
196
- link._patch.inspect
201
+ stub_request(:patch, 'http://api.example.org/productions/1').to_return(body: nil)
202
+ link._patch.must_be_kind_of Resource
197
203
  end
198
204
  end
199
205
 
@@ -8,5 +8,36 @@ describe Hyperclient do
8
8
 
9
9
  Hyperclient.new('http://api.example.org')
10
10
  end
11
+
12
+ describe 'with an optional block' do
13
+ let(:client) do
14
+ Hyperclient.new('http://api.example.org') do |client|
15
+ client.connection(default: true) do |conn|
16
+ conn.use Faraday::Request::OAuth
17
+ end
18
+ client.headers['Access-Token'] = 'token'
19
+ end
20
+ end
21
+
22
+ it 'creates a Faraday connection with the default and additional headers' do
23
+ client.headers['Content-Type'].must_equal 'application/json'
24
+ client.headers['Accept'].must_equal 'application/json'
25
+ client.headers['Access-Token'].must_equal 'token'
26
+ end
27
+
28
+ it 'creates a Faraday connection with the entry point url' do
29
+ client.connection.url_prefix.to_s.must_equal 'http://api.example.org/'
30
+ end
31
+
32
+ it 'creates a Faraday connection with the default block plus any additional handlers' do
33
+ handlers = client.connection.builder.handlers
34
+ handlers.must_include Faraday::Request::OAuth
35
+ handlers.must_include Faraday::Response::RaiseError
36
+ handlers.must_include FaradayMiddleware::FollowRedirects
37
+ handlers.must_include FaradayMiddleware::EncodeJson
38
+ handlers.must_include FaradayMiddleware::ParseJson
39
+ handlers.must_include Faraday::Adapter::NetHttp
40
+ end
41
+ end
11
42
  end
12
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperclient
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oriol Gual
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-01 00:00:00.000000000 Z
11
+ date: 2014-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -188,7 +188,6 @@ files:
188
188
  - .gitignore
189
189
  - .rubocop.yml
190
190
  - .rubocop_todo.yml
191
- - .rvmrc
192
191
  - .travis.yml
193
192
  - .yardopts
194
193
  - CHANGELOG.md
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm use --create 2.0.0@hyperclient