frenetic 0.0.1 → 0.0.2.alpha1
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.
- data/README.md +242 -1
- data/frenetic.gemspec +11 -9
- data/lib/frenetic.rb +8 -0
- data/lib/frenetic/configuration.rb +25 -5
- data/lib/frenetic/version.rb +1 -1
- data/spec/lib/frenetic/configuration_spec.rb +44 -8
- metadata +45 -23
data/README.md
CHANGED
@@ -5,6 +5,122 @@
|
|
5
5
|
|
6
6
|
An opinionated Ruby-based Hypermedia API (HAL+JSON) client.
|
7
7
|
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
## About
|
12
|
+
|
13
|
+
fre•net•ic |frəˈnetik|<br/>
|
14
|
+
adjective<br/>
|
15
|
+
fast and energetic in a rather wild and uncontrolled way : *a frenetic pace of activity.*
|
16
|
+
|
17
|
+
So basically, this is a crazy way to interact with your Hypermedia HAL+JSON API.
|
18
|
+
|
19
|
+
Get it? *Hypermedia*?
|
20
|
+
|
21
|
+
*Hyper*?
|
22
|
+
|
23
|
+
...
|
24
|
+
|
25
|
+
If you have not implemented a HAL+JSON API, then this will not work very well for you.
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
## Opinions
|
31
|
+
|
32
|
+
Like I said, it is opinionated. It is so opinionated, it is probably the biggest
|
33
|
+
a-hole you've ever met.
|
34
|
+
|
35
|
+
Maybe in time, if you teach it, it will become more open-minded.
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
### HAL+JSON Content Type
|
40
|
+
|
41
|
+
Frenetic expects all responses to be in [HAL+JSON][hal_json]. It chose that
|
42
|
+
standard because it is trying to make JSON API's respond in a predictable
|
43
|
+
manner, which it thinks is an awesome idea.
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
### Authentication
|
48
|
+
|
49
|
+
Frenetic is going to try and use Basic Auth whether you like it or not. If
|
50
|
+
that is not required, nothing will probably happen. But its going to send the
|
51
|
+
header anyway.
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
### API Description
|
56
|
+
|
57
|
+
The API's root URL must respond with a description, much like the
|
58
|
+
[Spire.io][spire.io] API. This is crucial in order for Frenetic to work. If
|
59
|
+
Frenetic doesn't know what the API contains, it can't parse any resource
|
60
|
+
responses.
|
61
|
+
|
62
|
+
It is expected that any subclasses of `Frenetic::Resource` will adhere to the
|
63
|
+
schema defined here.
|
64
|
+
|
65
|
+
Example:
|
66
|
+
|
67
|
+
```js
|
68
|
+
{
|
69
|
+
"_links":{
|
70
|
+
"self":{"href":"/api/"},
|
71
|
+
"inkers":{"href":"/api/inkers"},
|
72
|
+
},
|
73
|
+
"_embedded":{
|
74
|
+
"schema":{
|
75
|
+
"_links":{
|
76
|
+
"self":{"href":"/api/schema"}
|
77
|
+
},
|
78
|
+
"order":{
|
79
|
+
"description":"A widget order",
|
80
|
+
"type":"object",
|
81
|
+
"properties":{
|
82
|
+
"id":{"type":"integer"},
|
83
|
+
"first_name":{"type":"string"},
|
84
|
+
"last_name":{"type":"string"},
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
```
|
91
|
+
|
92
|
+
This response will be requested by Frenetic whenever a call to
|
93
|
+
`YourAPI.description` is made. The response is memoized so any future calls
|
94
|
+
will not trigger another API request.
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
### API Resources
|
99
|
+
|
100
|
+
While HAL+JSON is awesome, not all implementations are perfect. Frenetic
|
101
|
+
assumes a HAL+JSON response as built by [Roar], which may not be in 100%
|
102
|
+
compliance.
|
103
|
+
|
104
|
+
Example:
|
105
|
+
|
106
|
+
```js
|
107
|
+
{
|
108
|
+
"id":1,
|
109
|
+
"first_name":"Foo",
|
110
|
+
"last_name":"Bar",
|
111
|
+
"_links":{
|
112
|
+
"self":{"href":"/order/1"},
|
113
|
+
"next":{"href":"/order/2"}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
```
|
117
|
+
|
118
|
+
The problem here is that the entire response really should be wrapped in
|
119
|
+
`"_embedded"` and `"order"` keys.
|
120
|
+
|
121
|
+
So until that is fixed, Frenetic will continue to be pig headed and continue
|
122
|
+
to do the "wrong" thing.
|
123
|
+
|
8
124
|
## Installation
|
9
125
|
|
10
126
|
Add this line to your application's Gemfile:
|
@@ -19,9 +135,128 @@ Or install it yourself as:
|
|
19
135
|
|
20
136
|
$ gem install frenetic
|
21
137
|
|
138
|
+
|
139
|
+
|
140
|
+
|
22
141
|
## Usage
|
23
142
|
|
24
|
-
|
143
|
+
|
144
|
+
|
145
|
+
### Client Initialization
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
MyAPI = Frenetic.new(
|
149
|
+
'url' => 'https://api.yoursite.com',
|
150
|
+
'username' => 'yourname',
|
151
|
+
'password' => 'yourpassword',
|
152
|
+
'headers' => {
|
153
|
+
'accept' => 'application/vnd.yoursite-v1.hal+json'
|
154
|
+
# Optional
|
155
|
+
'user-agent' => 'Your Site's API Client', # Optional custom User Agent, just 'cuz
|
156
|
+
}
|
157
|
+
)
|
158
|
+
```
|
159
|
+
|
160
|
+
|
161
|
+
### Response Caching
|
162
|
+
|
163
|
+
If configured to do so, Frenetic will autotmatically cache appropriate responses
|
164
|
+
through [Rack::Cache][rach_cache]. Only on-disk stores are supported right now.
|
165
|
+
|
166
|
+
Add the following `Rack::Cache` configuration options when initializing Frenetic:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
MyAPI = Frenetic.new(
|
170
|
+
...
|
171
|
+
'cache' => {
|
172
|
+
'metastore' => 'file:/path/to/where/you/want/to/store/files/meta',
|
173
|
+
'entitystore' => 'file:/path/to/where/you/want/to/store/files/meta'
|
174
|
+
}
|
175
|
+
)
|
176
|
+
```
|
177
|
+
|
178
|
+
The `cache` options are passed directly to `Rack::Cache`, so anything it
|
179
|
+
supports can be added to the Hash.
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
### Making Requests
|
184
|
+
|
185
|
+
Once you have created a client instance, you are free to use it however you'd
|
186
|
+
like.
|
187
|
+
|
188
|
+
A Frenetic instance supports any HTTP verb that [Faraday][faraday] has
|
189
|
+
impletented. This includes GET, POST, PUT, PATCH, and DELETE.
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
#### Frenetic::Resource
|
194
|
+
|
195
|
+
An easier way to make requests for a resource is to have your model inherit from
|
196
|
+
`Frenetic::Resource`. This makes it a bit easier to encapsulate all of your
|
197
|
+
resource's API requests into one place.
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
class Order < Frenetic::Resource
|
201
|
+
|
202
|
+
api_client { MyAPI }
|
203
|
+
|
204
|
+
class << self
|
205
|
+
def find( id )
|
206
|
+
if response = api.get( api.description.links.order.href.gsub('{id}', id.to_s) ) and response.success?
|
207
|
+
self.new( response.body )
|
208
|
+
else
|
209
|
+
raise OrderNotFound, "No Order found for #{id}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
The `api_client` class method merely tells `Frenetic::Resource` which API Client
|
217
|
+
instance to use. If you lazily instantiate your client, then you should pass a
|
218
|
+
block as demonstrated above.
|
219
|
+
|
220
|
+
Otherwise, you may pass by reference:
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
class Order < Frenetic::Resource
|
224
|
+
api_client MyAPI
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
When your model is initialized, it will contain attribute readers for every
|
229
|
+
property defined in your API's schema or description. In theory, this allows an
|
230
|
+
API to add, remove, or change properties without the need to directly update
|
231
|
+
your model.
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
### Interpretting Responses
|
236
|
+
|
237
|
+
Any response body returned by a Frenetic generated API call will be returned as
|
238
|
+
an OpenStruct-like object. This object responds to dot-notation as well as Hash
|
239
|
+
keys and is enumerable.
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
response.body.resources.orders.first
|
243
|
+
```
|
244
|
+
|
245
|
+
or
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
response.body['_embedded']['orders'][0]
|
249
|
+
```
|
250
|
+
|
251
|
+
For your convenience, certain HAL+JSON keys have been aliased by methods a bit
|
252
|
+
more readable:
|
253
|
+
|
254
|
+
* `_embedded` can be referenced as `resources`
|
255
|
+
* `_links` can be referenced as `links`
|
256
|
+
* `href` can be referenced as `url`
|
257
|
+
|
258
|
+
|
259
|
+
|
25
260
|
|
26
261
|
## Contributing
|
27
262
|
|
@@ -30,3 +265,9 @@ Your guess is as good as mine. (*TODO*)
|
|
30
265
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
31
266
|
4. Push to the branch (`git push origin my-new-feature`)
|
32
267
|
5. Create new Pull Request
|
268
|
+
|
269
|
+
[hal_json]: http://stateless.co/hal_specification.html
|
270
|
+
[spire.io]: http://api.spire.io/
|
271
|
+
[roar]: https://github.com/apotonick/roar
|
272
|
+
[faraday]: https://github.com/technoweenie/faraday
|
273
|
+
[rack_cache]: https://github.com/rtomayko/rack-cache
|
data/frenetic.gemspec
CHANGED
@@ -15,14 +15,16 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Frenetic::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency 'faraday',
|
19
|
-
gem.add_dependency '
|
20
|
-
gem.add_dependency '
|
18
|
+
gem.add_dependency 'faraday', '~> 0.8.0.rc2'
|
19
|
+
gem.add_dependency 'faraday_middleware', '~> 0.8.6'
|
20
|
+
gem.add_dependency 'rack-cache', '~> 1.1'
|
21
|
+
gem.add_dependency 'addressable', '~> 2.2.7'
|
22
|
+
gem.add_dependency 'patron', '~> 0.4.18'
|
21
23
|
|
22
|
-
gem.add_development_dependency 'guard-spork',
|
23
|
-
gem.add_development_dependency 'guard-rspec',
|
24
|
-
gem.add_development_dependency 'rspec',
|
25
|
-
gem.add_development_dependency 'bourne',
|
26
|
-
gem.add_development_dependency 'webmock',
|
27
|
-
gem.add_development_dependency 'vcr',
|
24
|
+
gem.add_development_dependency 'guard-spork', '~> 0.6.0'
|
25
|
+
gem.add_development_dependency 'guard-rspec', '~> 0.7.0'
|
26
|
+
gem.add_development_dependency 'rspec', '~> 2.9.0'
|
27
|
+
gem.add_development_dependency 'bourne', '~> 1.1.2'
|
28
|
+
gem.add_development_dependency 'webmock', '~> 1.8.6'
|
29
|
+
gem.add_development_dependency 'vcr', '~> 2.0.1'
|
28
30
|
end
|
data/lib/frenetic.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'addressable/uri'
|
2
|
+
require 'patron' # Needed to prevent https://github.com/technoweenie/faraday/issues/140
|
2
3
|
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
require 'rack-cache'
|
3
6
|
|
4
7
|
require "frenetic/configuration"
|
5
8
|
require "frenetic/hal_json"
|
@@ -25,6 +28,11 @@ class Frenetic
|
|
25
28
|
@connection = Faraday.new( config ) do |builder|
|
26
29
|
builder.use HalJson
|
27
30
|
builder.request :basic_auth, config[:username], config[:password]
|
31
|
+
|
32
|
+
if config[:cache]
|
33
|
+
builder.use FaradayMiddleware::RackCompatible, Rack::Cache::Context, config[:cache]
|
34
|
+
end
|
35
|
+
|
28
36
|
builder.adapter :patron
|
29
37
|
end
|
30
38
|
end
|
@@ -15,11 +15,7 @@ class Frenetic
|
|
15
15
|
config[:headers] ||= {}
|
16
16
|
config[:request] ||= {}
|
17
17
|
|
18
|
-
|
19
|
-
config[:headers][:accepts] = config[:"content-type"]
|
20
|
-
else
|
21
|
-
config[:headers][:accepts] = "application/hal+json"
|
22
|
-
end
|
18
|
+
config[:headers][:accept] ||= "application/hal+json"
|
23
19
|
|
24
20
|
# Copy the config into this Configuration instance.
|
25
21
|
config.each { |k, v| self[k] = v }
|
@@ -27,6 +23,7 @@ class Frenetic
|
|
27
23
|
super()
|
28
24
|
|
29
25
|
configure_user_agent
|
26
|
+
configure_cache
|
30
27
|
|
31
28
|
validate
|
32
29
|
end
|
@@ -43,12 +40,35 @@ class Frenetic
|
|
43
40
|
end
|
44
41
|
end
|
45
42
|
|
43
|
+
def configure_cache
|
44
|
+
if self[:cache]
|
45
|
+
ignore_headers = (self[:cache][:ignore_headers] || '').split(' ')
|
46
|
+
|
47
|
+
self[:cache][:ignore_headers] = (ignore_headers + %w[Set-Cookie X-Content-Digest]).uniq
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
46
51
|
def validate
|
47
52
|
unless self[:url]
|
48
53
|
raise ConfigurationError, "No API URL defined!"
|
49
54
|
end
|
55
|
+
if self[:cache]
|
56
|
+
raise( ConfigurationError, "No cache :metastore defined!" ) if self[:cache][:metastore].to_s == ""
|
57
|
+
raise( ConfigurationError, "No cache :entitystore defined!" ) if self[:cache][:entitystore].to_s == ""
|
58
|
+
raise( ConfigurationError, "Required cache header filters are missing!" ) if missing_required_headers?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def missing_required_headers?
|
63
|
+
return true if self[:cache][:ignore_headers].empty?
|
64
|
+
|
65
|
+
header_set = self[:cache][:ignore_headers]
|
66
|
+
custom_headers = header_set - %w[Set-Cookie X-Content-Digest]
|
67
|
+
|
68
|
+
header_set == custom_headers
|
50
69
|
end
|
51
70
|
|
71
|
+
# TODO: Is this even being used?
|
52
72
|
def config_file
|
53
73
|
config_path = File.join( 'config', 'frenetic.yml' )
|
54
74
|
|
data/lib/frenetic/version.rb
CHANGED
@@ -4,7 +4,9 @@ describe Frenetic::Configuration do
|
|
4
4
|
{ 'test' => {
|
5
5
|
'url' => 'http://example.org',
|
6
6
|
'api_key' => '1234567890',
|
7
|
-
'
|
7
|
+
'headers' => {
|
8
|
+
'accept' => content_type,
|
9
|
+
},
|
8
10
|
'request' => {
|
9
11
|
'timeout' => 10000
|
10
12
|
}
|
@@ -37,16 +39,16 @@ describe Frenetic::Configuration do
|
|
37
39
|
subject[:headers][:user_agent].should =~ %r{Frenetic v.+; \S+$}
|
38
40
|
end
|
39
41
|
|
40
|
-
context "with a specified
|
41
|
-
it "should set an
|
42
|
-
subject[:headers].should include(:
|
42
|
+
context "with a specified Accept header" do
|
43
|
+
it "should set an Accept request header" do
|
44
|
+
subject[:headers].should include(:accept => 'application/vnd.frenetic-v1-hal+json')
|
43
45
|
end
|
44
46
|
end
|
45
|
-
context "without a specified
|
47
|
+
context "without a specified Accept header" do
|
46
48
|
let(:content_type) { nil }
|
47
49
|
|
48
|
-
it "should set an
|
49
|
-
subject[:headers].should include(:
|
50
|
+
it "should set an Accept request header" do
|
51
|
+
subject[:headers].should include(:accept => 'application/hal+json')
|
50
52
|
end
|
51
53
|
end
|
52
54
|
end
|
@@ -63,11 +65,45 @@ describe Frenetic::Configuration do
|
|
63
65
|
it { should be_a( Hash ) }
|
64
66
|
it { should_not be_empty }
|
65
67
|
it "should set an Accepts request header" do
|
66
|
-
subject[:headers].should include(:
|
68
|
+
subject[:headers].should include(:accept => 'application/hal+json')
|
67
69
|
end
|
68
70
|
it "should set a User Agent request header" do
|
69
71
|
subject[:headers][:user_agent].should =~ %r{Frenetic v.+; \S+$}
|
70
72
|
end
|
73
|
+
|
74
|
+
context "which includes incorrect cache settings" do
|
75
|
+
before { Frenetic::Configuration.any_instance.stubs(:configure_cache).returns(nil) }
|
76
|
+
|
77
|
+
it "should raise a configuration error for a missing :metastore" do
|
78
|
+
expect {
|
79
|
+
Frenetic::Configuration.new('url' => 'http://example.org', 'cache' => {} )
|
80
|
+
}.to raise_error(
|
81
|
+
Frenetic::Configuration::ConfigurationError, "No cache :metastore defined!"
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should raise a configuration error for a missing :entitystore" do
|
86
|
+
expect {
|
87
|
+
Frenetic::Configuration.new('url' => 'http://example.org', 'cache' => { 'metastore' => 'foo' } )
|
88
|
+
}.to raise_error(
|
89
|
+
Frenetic::Configuration::ConfigurationError, "No cache :entitystore defined!"
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise a configuration error for missing required header filters" do
|
94
|
+
cache_cfg = {
|
95
|
+
'metastore' => 'foo',
|
96
|
+
'entitystore' => 'bar',
|
97
|
+
'ignore_headers' => ['baz'] # `configure_cache` method is skipped to create a bad state
|
98
|
+
}
|
99
|
+
|
100
|
+
expect {
|
101
|
+
Frenetic::Configuration.new('url' => 'http://example.org', 'cache' => cache_cfg )
|
102
|
+
}.to raise_error(
|
103
|
+
Frenetic::Configuration::ConfigurationError, "Required cache header filters are missing!"
|
104
|
+
)
|
105
|
+
end
|
106
|
+
end
|
71
107
|
end
|
72
108
|
end
|
73
109
|
end
|
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: frenetic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2.alpha1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Derek Lindahl
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-18 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
16
|
-
requirement: &
|
16
|
+
requirement: &2156329760 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,32 @@ dependencies:
|
|
21
21
|
version: 0.8.0.rc2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2156329760
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: faraday_middleware
|
27
|
+
requirement: &2156328980 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.8.6
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2156328980
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rack-cache
|
38
|
+
requirement: &2156328220 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.1'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2156328220
|
25
47
|
- !ruby/object:Gem::Dependency
|
26
48
|
name: addressable
|
27
|
-
requirement: &
|
49
|
+
requirement: &2156327400 !ruby/object:Gem::Requirement
|
28
50
|
none: false
|
29
51
|
requirements:
|
30
52
|
- - ~>
|
@@ -32,10 +54,10 @@ dependencies:
|
|
32
54
|
version: 2.2.7
|
33
55
|
type: :runtime
|
34
56
|
prerelease: false
|
35
|
-
version_requirements: *
|
57
|
+
version_requirements: *2156327400
|
36
58
|
- !ruby/object:Gem::Dependency
|
37
59
|
name: patron
|
38
|
-
requirement: &
|
60
|
+
requirement: &2156326620 !ruby/object:Gem::Requirement
|
39
61
|
none: false
|
40
62
|
requirements:
|
41
63
|
- - ~>
|
@@ -43,10 +65,10 @@ dependencies:
|
|
43
65
|
version: 0.4.18
|
44
66
|
type: :runtime
|
45
67
|
prerelease: false
|
46
|
-
version_requirements: *
|
68
|
+
version_requirements: *2156326620
|
47
69
|
- !ruby/object:Gem::Dependency
|
48
70
|
name: guard-spork
|
49
|
-
requirement: &
|
71
|
+
requirement: &2156325900 !ruby/object:Gem::Requirement
|
50
72
|
none: false
|
51
73
|
requirements:
|
52
74
|
- - ~>
|
@@ -54,10 +76,10 @@ dependencies:
|
|
54
76
|
version: 0.6.0
|
55
77
|
type: :development
|
56
78
|
prerelease: false
|
57
|
-
version_requirements: *
|
79
|
+
version_requirements: *2156325900
|
58
80
|
- !ruby/object:Gem::Dependency
|
59
81
|
name: guard-rspec
|
60
|
-
requirement: &
|
82
|
+
requirement: &2156325160 !ruby/object:Gem::Requirement
|
61
83
|
none: false
|
62
84
|
requirements:
|
63
85
|
- - ~>
|
@@ -65,10 +87,10 @@ dependencies:
|
|
65
87
|
version: 0.7.0
|
66
88
|
type: :development
|
67
89
|
prerelease: false
|
68
|
-
version_requirements: *
|
90
|
+
version_requirements: *2156325160
|
69
91
|
- !ruby/object:Gem::Dependency
|
70
92
|
name: rspec
|
71
|
-
requirement: &
|
93
|
+
requirement: &2156324300 !ruby/object:Gem::Requirement
|
72
94
|
none: false
|
73
95
|
requirements:
|
74
96
|
- - ~>
|
@@ -76,10 +98,10 @@ dependencies:
|
|
76
98
|
version: 2.9.0
|
77
99
|
type: :development
|
78
100
|
prerelease: false
|
79
|
-
version_requirements: *
|
101
|
+
version_requirements: *2156324300
|
80
102
|
- !ruby/object:Gem::Dependency
|
81
103
|
name: bourne
|
82
|
-
requirement: &
|
104
|
+
requirement: &2156322980 !ruby/object:Gem::Requirement
|
83
105
|
none: false
|
84
106
|
requirements:
|
85
107
|
- - ~>
|
@@ -87,10 +109,10 @@ dependencies:
|
|
87
109
|
version: 1.1.2
|
88
110
|
type: :development
|
89
111
|
prerelease: false
|
90
|
-
version_requirements: *
|
112
|
+
version_requirements: *2156322980
|
91
113
|
- !ruby/object:Gem::Dependency
|
92
114
|
name: webmock
|
93
|
-
requirement: &
|
115
|
+
requirement: &2156322340 !ruby/object:Gem::Requirement
|
94
116
|
none: false
|
95
117
|
requirements:
|
96
118
|
- - ~>
|
@@ -98,10 +120,10 @@ dependencies:
|
|
98
120
|
version: 1.8.6
|
99
121
|
type: :development
|
100
122
|
prerelease: false
|
101
|
-
version_requirements: *
|
123
|
+
version_requirements: *2156322340
|
102
124
|
- !ruby/object:Gem::Dependency
|
103
125
|
name: vcr
|
104
|
-
requirement: &
|
126
|
+
requirement: &2156321440 !ruby/object:Gem::Requirement
|
105
127
|
none: false
|
106
128
|
requirements:
|
107
129
|
- - ~>
|
@@ -109,7 +131,7 @@ dependencies:
|
|
109
131
|
version: 2.0.1
|
110
132
|
type: :development
|
111
133
|
prerelease: false
|
112
|
-
version_requirements: *
|
134
|
+
version_requirements: *2156321440
|
113
135
|
description: An opinionated Ruby-based Hypermedia API client.
|
114
136
|
email:
|
115
137
|
- dlindahl@customink.com
|
@@ -155,9 +177,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
155
177
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
178
|
none: false
|
157
179
|
requirements:
|
158
|
-
- - ! '
|
180
|
+
- - ! '>'
|
159
181
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
182
|
+
version: 1.3.1
|
161
183
|
requirements: []
|
162
184
|
rubyforge_project:
|
163
185
|
rubygems_version: 1.8.10
|