frenetic 0.0.1.alpha1 → 0.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.
- data/README.md +1 -192
- data/lib/frenetic.rb +0 -1
- data/lib/frenetic/configuration.rb +5 -1
- data/lib/frenetic/version.rb +1 -1
- data/spec/lib/frenetic/configuration_spec.rb +8 -10
- metadata +23 -23
data/README.md
CHANGED
@@ -5,108 +5,6 @@
|
|
5
5
|
|
6
6
|
An opinionated Ruby-based Hypermedia API (HAL+JSON) client.
|
7
7
|
|
8
|
-
## About
|
9
|
-
|
10
|
-
fre•net•ic |frəˈnetik|<br/>
|
11
|
-
adjective<br/>
|
12
|
-
fast and energetic in a rather wild and uncontrolled way : *a frenetic pace of activity.*
|
13
|
-
|
14
|
-
So basically, this is a crazy way to interact with your Hypermedia HAL+JSON API.
|
15
|
-
|
16
|
-
Get it? *Hypermedia*?
|
17
|
-
|
18
|
-
*Hyper*?
|
19
|
-
|
20
|
-
...
|
21
|
-
|
22
|
-
If you have not implemented a HAL+JSON API, then this will not work very well for you.
|
23
|
-
|
24
|
-
## Opinions
|
25
|
-
|
26
|
-
Like I said, it is opinionated. It is so opinionated, it is probably the biggest
|
27
|
-
a-hole you've ever met.
|
28
|
-
|
29
|
-
Maybe in time, if you teach it, it will become more open-minded.
|
30
|
-
|
31
|
-
### HAL+JSON Content Type
|
32
|
-
|
33
|
-
Frenetic expects all responses to be in [HAL+JSON][hal_json]. It chose that
|
34
|
-
standard because it is trying to make JSON API's respond in a predictable
|
35
|
-
manner, which it thinks is an awesome idea.
|
36
|
-
|
37
|
-
### Authentication
|
38
|
-
|
39
|
-
Frenetic is going to try and use Basic Auth whether you like it or not. If
|
40
|
-
that is not required, nothing will probably happen. But its going to send the
|
41
|
-
header anyway.
|
42
|
-
|
43
|
-
### API Description
|
44
|
-
|
45
|
-
The API's root URL must respond with a description, much like the
|
46
|
-
[Spire.io][spire.io] API. This is crucial in order for Frenetic to work. If
|
47
|
-
Frenetic doesn't know what the API contains, it can't parse any resource
|
48
|
-
responses.
|
49
|
-
|
50
|
-
It is expected that any subclasses of `Frenetic::Resource` will adhere to the
|
51
|
-
schema defined here.
|
52
|
-
|
53
|
-
Example:
|
54
|
-
|
55
|
-
```js
|
56
|
-
{
|
57
|
-
"_links":{
|
58
|
-
"self":{"href":"/api/"},
|
59
|
-
"inkers":{"href":"/api/inkers"},
|
60
|
-
},
|
61
|
-
"_embedded":{
|
62
|
-
"schema":{
|
63
|
-
"_links":{
|
64
|
-
"self":{"href":"/api/schema"}
|
65
|
-
},
|
66
|
-
"order":{
|
67
|
-
"description":"A widget order",
|
68
|
-
"type":"object",
|
69
|
-
"properties":{
|
70
|
-
"id":{"type":"integer"},
|
71
|
-
"first_name":{"type":"string"},
|
72
|
-
"last_name":{"type":"string"},
|
73
|
-
}
|
74
|
-
}
|
75
|
-
}
|
76
|
-
}
|
77
|
-
}
|
78
|
-
```
|
79
|
-
|
80
|
-
This response will be requested by Frenetic whenever a call to
|
81
|
-
`YourAPI.description` is made. The response is memoized so any future calls
|
82
|
-
will not trigger another API request.
|
83
|
-
|
84
|
-
### API Resources
|
85
|
-
|
86
|
-
While HAL+JSON is awesome, not all implementations are perfect. Frenetic
|
87
|
-
assumes a HAL+JSON response as built by [Roar], which may not be in 100%
|
88
|
-
compliance.
|
89
|
-
|
90
|
-
Example:
|
91
|
-
|
92
|
-
```js
|
93
|
-
{
|
94
|
-
"id":1,
|
95
|
-
"first_name":"Foo",
|
96
|
-
"last_name":"Bar",
|
97
|
-
"_links":{
|
98
|
-
"self":{"href":"/order/1"},
|
99
|
-
"next":{"href":"/order/2"}
|
100
|
-
}
|
101
|
-
}
|
102
|
-
```
|
103
|
-
|
104
|
-
The problem here is that the entire response really should be wrapped in
|
105
|
-
`"_embedded"` and `"order"` keys.
|
106
|
-
|
107
|
-
So until that is fixed, Frenetic will continue to be pig headed and continue
|
108
|
-
to do the "wrong" thing.
|
109
|
-
|
110
8
|
## Installation
|
111
9
|
|
112
10
|
Add this line to your application's Gemfile:
|
@@ -123,91 +21,7 @@ Or install it yourself as:
|
|
123
21
|
|
124
22
|
## Usage
|
125
23
|
|
126
|
-
|
127
|
-
|
128
|
-
```ruby
|
129
|
-
MyAPI = Frenetic.new(
|
130
|
-
'url' => 'https://api.yoursite.com',
|
131
|
-
'username' => 'yourname',
|
132
|
-
'password' => 'yourpassword',
|
133
|
-
'headers' => {
|
134
|
-
'accept' => 'application/vnd.yoursite-v1.hal+json'
|
135
|
-
# Optional
|
136
|
-
'user-agent' => 'Your Site's API Client', # Optional custom User Agent, just 'cuz
|
137
|
-
}
|
138
|
-
)
|
139
|
-
```
|
140
|
-
|
141
|
-
### Making Requests
|
142
|
-
|
143
|
-
Once you have created a client instance, you are free to use it however you'd
|
144
|
-
like.
|
145
|
-
|
146
|
-
A Frenetic instance supports any HTTP verb that [Faraday][faraday] has
|
147
|
-
impletented. This includes GET, POST, PUT, PATCH, and DELETE.
|
148
|
-
|
149
|
-
#### Frenetic::Resource
|
150
|
-
|
151
|
-
An easier way to make requests for a resource is to have your model inherit from
|
152
|
-
`Frenetic::Resource`. This makes it a bit easier to encapsulate all of your
|
153
|
-
resource's API requests into one place.
|
154
|
-
|
155
|
-
```ruby
|
156
|
-
class Order < Frenetic::Resource
|
157
|
-
|
158
|
-
api_client { MyAPI }
|
159
|
-
|
160
|
-
class << self
|
161
|
-
def find( id )
|
162
|
-
if response = api.get( api.description.links.order.href.gsub('{id}', id.to_s) ) and response.success?
|
163
|
-
self.new( response.body )
|
164
|
-
else
|
165
|
-
raise OrderNotFound, "No Order found for #{id}"
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
```
|
171
|
-
|
172
|
-
The `api_client` class method merely tells `Frenetic::Resource` which API Client
|
173
|
-
instance to use. If you lazily instantiate your client, then you should pass a
|
174
|
-
block as demonstrated above.
|
175
|
-
|
176
|
-
Otherwise, you may pass by reference:
|
177
|
-
|
178
|
-
```ruby
|
179
|
-
class Order < Frenetic::Resource
|
180
|
-
api_client MyAPI
|
181
|
-
end
|
182
|
-
```
|
183
|
-
|
184
|
-
When your model is initialized, it will contain attribute readers for every
|
185
|
-
property defined in your API's schema or description. In theory, this allows an
|
186
|
-
API to add, remove, or change properties without the need to directly update
|
187
|
-
your model.
|
188
|
-
|
189
|
-
### Interpretting Responses
|
190
|
-
|
191
|
-
Any response body returned by a Frenetic generated API call will be returned as
|
192
|
-
an OpenStruct-like object. This object responds to dot-notation as well as Hash
|
193
|
-
keys and is enumerable.
|
194
|
-
|
195
|
-
```ruby
|
196
|
-
response.body.resources.orders.first
|
197
|
-
```
|
198
|
-
|
199
|
-
or
|
200
|
-
|
201
|
-
```ruby
|
202
|
-
response.body['_embedded']['orders'][0]
|
203
|
-
```
|
204
|
-
|
205
|
-
For your convenience, certain HAL+JSON keys have been aliased by methods a bit
|
206
|
-
more readable:
|
207
|
-
|
208
|
-
* `_embedded` can be referenced as `resources`
|
209
|
-
* `_links` can be referenced as `links`
|
210
|
-
* `href` can be referenced as `url`
|
24
|
+
Your guess is as good as mine. (*TODO*)
|
211
25
|
|
212
26
|
## Contributing
|
213
27
|
|
@@ -216,8 +30,3 @@ more readable:
|
|
216
30
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
217
31
|
4. Push to the branch (`git push origin my-new-feature`)
|
218
32
|
5. Create new Pull Request
|
219
|
-
|
220
|
-
[hal_json]: http://stateless.co/hal_specification.html
|
221
|
-
[spire.io]: http://api.spire.io/
|
222
|
-
[roar]: https://github.com/apotonick/roar
|
223
|
-
[faraday]: https://github.com/technoweenie/faraday
|
data/lib/frenetic.rb
CHANGED
@@ -15,7 +15,11 @@ class Frenetic
|
|
15
15
|
config[:headers] ||= {}
|
16
16
|
config[:request] ||= {}
|
17
17
|
|
18
|
-
config[:
|
18
|
+
if config[:"content-type"]
|
19
|
+
config[:headers][:accepts] = config[:"content-type"]
|
20
|
+
else
|
21
|
+
config[:headers][:accepts] = "application/hal+json"
|
22
|
+
end
|
19
23
|
|
20
24
|
# Copy the config into this Configuration instance.
|
21
25
|
config.each { |k, v| self[k] = v }
|
data/lib/frenetic/version.rb
CHANGED
@@ -4,9 +4,7 @@ describe Frenetic::Configuration do
|
|
4
4
|
{ 'test' => {
|
5
5
|
'url' => 'http://example.org',
|
6
6
|
'api_key' => '1234567890',
|
7
|
-
'
|
8
|
-
'accept' => content_type,
|
9
|
-
},
|
7
|
+
'content-type' => content_type,
|
10
8
|
'request' => {
|
11
9
|
'timeout' => 10000
|
12
10
|
}
|
@@ -39,16 +37,16 @@ describe Frenetic::Configuration do
|
|
39
37
|
subject[:headers][:user_agent].should =~ %r{Frenetic v.+; \S+$}
|
40
38
|
end
|
41
39
|
|
42
|
-
context "with a specified
|
43
|
-
it "should set an
|
44
|
-
subject[:headers].should include(:
|
40
|
+
context "with a specified Content-Type" do
|
41
|
+
it "should set an Accepts request header" do
|
42
|
+
subject[:headers].should include(:accepts => 'application/vnd.frenetic-v1-hal+json')
|
45
43
|
end
|
46
44
|
end
|
47
|
-
context "without a specified
|
45
|
+
context "without a specified Content-Type" do
|
48
46
|
let(:content_type) { nil }
|
49
47
|
|
50
|
-
it "should set an
|
51
|
-
subject[:headers].should include(:
|
48
|
+
it "should set an Accepts request header" do
|
49
|
+
subject[:headers].should include(:accepts => 'application/hal+json')
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
@@ -65,7 +63,7 @@ describe Frenetic::Configuration do
|
|
65
63
|
it { should be_a( Hash ) }
|
66
64
|
it { should_not be_empty }
|
67
65
|
it "should set an Accepts request header" do
|
68
|
-
subject[:headers].should include(:
|
66
|
+
subject[:headers].should include(:accepts => 'application/hal+json')
|
69
67
|
end
|
70
68
|
it "should set a User Agent request header" do
|
71
69
|
subject[:headers][:user_agent].should =~ %r{Frenetic v.+; \S+$}
|
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.1
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
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-11 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
16
|
-
requirement: &
|
16
|
+
requirement: &2157361220 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.8.0.rc2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2157361220
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: addressable
|
27
|
-
requirement: &
|
27
|
+
requirement: &2157360720 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 2.2.7
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2157360720
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: patron
|
38
|
-
requirement: &
|
38
|
+
requirement: &2157360240 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.4.18
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2157360240
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: guard-spork
|
49
|
-
requirement: &
|
49
|
+
requirement: &2157359780 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.6.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *2157359780
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: guard-rspec
|
60
|
-
requirement: &
|
60
|
+
requirement: &2157359320 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.7.0
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *2157359320
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
|
-
requirement: &
|
71
|
+
requirement: &2157358860 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 2.9.0
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *2157358860
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: bourne
|
82
|
-
requirement: &
|
82
|
+
requirement: &2157358400 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: 1.1.2
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *2157358400
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: webmock
|
93
|
-
requirement: &
|
93
|
+
requirement: &2157357940 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ~>
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: 1.8.6
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *2157357940
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: vcr
|
104
|
-
requirement: &
|
104
|
+
requirement: &2157357480 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ~>
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
version: 2.0.1
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *2157357480
|
113
113
|
description: An opinionated Ruby-based Hypermedia API client.
|
114
114
|
email:
|
115
115
|
- dlindahl@customink.com
|
@@ -155,9 +155,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
155
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
156
|
none: false
|
157
157
|
requirements:
|
158
|
-
- - ! '
|
158
|
+
- - ! '>='
|
159
159
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
160
|
+
version: '0'
|
161
161
|
requirements: []
|
162
162
|
rubyforge_project:
|
163
163
|
rubygems_version: 1.8.10
|