her 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +17 -12
- data/lib/her.rb +3 -2
- data/lib/her/api.rb +25 -30
- data/lib/her/middleware.rb +5 -0
- data/lib/her/middleware/default_parse_json.rb +18 -0
- data/lib/her/model/http.rb +1 -2
- data/lib/her/version.rb +1 -1
- data/spec/api_spec.rb +48 -22
- metadata +5 -3
data/README.md
CHANGED
@@ -86,25 +86,27 @@ By default, Her handles JSON data. It expects the data to be formatted in a cert
|
|
86
86
|
}
|
87
87
|
```
|
88
88
|
|
89
|
-
However, you can define your own parsing method,
|
89
|
+
However, you can define your own parsing method, using a Faraday response middleware. The middleware is expected to return a hash with three keys: `data`, `errors` and `metadata`. The following code enables parsing JSON data and treating this data as first-level properties:
|
90
90
|
|
91
91
|
```ruby
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
:
|
98
|
-
|
99
|
-
|
100
|
-
|
92
|
+
class MyCustomParser < Faraday::Response::Middleware
|
93
|
+
def on_complete(env)
|
94
|
+
json = JSON.parse(env[:body], :symbolize_names => true)
|
95
|
+
errors = json.delete(:errors) || []
|
96
|
+
metadata = json.delete(:metadata) || []
|
97
|
+
env[:body] = {
|
98
|
+
:data => json,
|
99
|
+
:errors => errors,
|
100
|
+
:metadata => metadata,
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
Her::API.setup :base_uri => "https://api.example.com", :middleware => [MyCustomParser] + Her::API.default_middleware
|
101
105
|
end
|
102
106
|
|
103
107
|
# User.find(1) will now expect "https://api.example.com/users/1" to return something like '{ "id": 1, "name": "Tobias Fünke" }'
|
104
108
|
```
|
105
109
|
|
106
|
-
This feature is not stable and might change in the future, probably by using a middleware through [Faraday](https://github.com/technoweenie/faraday).
|
107
|
-
|
108
110
|
## Relationships
|
109
111
|
|
110
112
|
You can define `has_many`, `has_one` and `belongs_to` relationships in your models. The relationship data is handled in two different ways. When parsing a resource from JSON data, if there’s a relationship data included, it will be used to create new Ruby objects.
|
@@ -179,6 +181,9 @@ class User
|
|
179
181
|
self.internal_id = 42 # Will be passed in the HTTP request
|
180
182
|
end
|
181
183
|
end
|
184
|
+
|
185
|
+
@user = User.create(:fullname => "Tobias Fünke")
|
186
|
+
# POST /users&fullname=Tobias+Fünke&internal_id=42
|
182
187
|
```
|
183
188
|
|
184
189
|
In the future, adding hooks to all models will be possible, as well as defining and triggering your own hooks (eg. for your custom requests).
|
data/lib/her.rb
CHANGED
data/lib/her/api.rb
CHANGED
@@ -7,7 +7,7 @@ module Her
|
|
7
7
|
# $my_api.setup :base_uri => "https://api.example.com"
|
8
8
|
class API
|
9
9
|
# @private
|
10
|
-
attr_reader :base_uri, :
|
10
|
+
attr_reader :base_uri, :middleware
|
11
11
|
|
12
12
|
# Setup a default API connection
|
13
13
|
def self.setup(attrs={}) # {{{
|
@@ -20,20 +20,32 @@ module Her
|
|
20
20
|
defined?(@@default_api) ? @@default_api : nil
|
21
21
|
end # }}}
|
22
22
|
|
23
|
+
# @private
|
24
|
+
def self.default_middleware # {{{
|
25
|
+
[Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
|
26
|
+
end # }}}
|
27
|
+
|
23
28
|
# Setup the API connection
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# module MyAPI
|
32
|
+
# class ParseResponse
|
33
|
+
# def on_complete(env)
|
34
|
+
# json = JSON.parse(env[:body], :symbolize_names => true)
|
35
|
+
# {
|
36
|
+
# :data => json,
|
37
|
+
# :errors => json[:errors] || [],
|
38
|
+
# :metadata => json[:metadata] || {},
|
39
|
+
# }
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# Her::API.setup :base_url => "https://api.example.com", :middleware => [MyAPI::ParseResponse, Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
|
24
44
|
def setup(attrs={}) # {{{
|
25
45
|
@base_uri = attrs[:base_uri]
|
26
|
-
@
|
27
|
-
json = JSON.parse(response.body, :symbolize_names => true)
|
28
|
-
{
|
29
|
-
:data => json[:data],
|
30
|
-
:errors => json[:errors],
|
31
|
-
:metadata => json[:metadata],
|
32
|
-
}
|
33
|
-
end
|
46
|
+
middleware = @middleware = attrs[:middleware] || [Her::Middleware::DefaultParseJSON] + Her::API.default_middleware
|
34
47
|
@connection = Faraday.new(:url => @base_uri) do |builder|
|
35
|
-
builder.
|
36
|
-
builder.adapter :net_http
|
48
|
+
middleware.each { |m| builder.use(m) }
|
37
49
|
end
|
38
50
|
end # }}}
|
39
51
|
|
@@ -42,25 +54,12 @@ module Her
|
|
42
54
|
# and a metadata Hash.
|
43
55
|
#
|
44
56
|
# @example
|
45
|
-
# $my_api.parse_with do |response|
|
46
|
-
# json = JSON.parse(response.body)
|
47
|
-
# { :resource => json[:data], :errors => json[:errors], :metadata => json[:metdata] }
|
48
|
-
# end
|
49
|
-
def parse_with(&block) # {{{
|
50
|
-
@custom_parsing_block = true
|
51
|
-
@parse_with = block
|
52
|
-
end # }}}
|
53
|
-
|
54
|
-
# Return whether a custom parsing block has been defined
|
55
|
-
def custom_parsing_block? # {{{
|
56
|
-
@custom_parsing_block
|
57
|
-
end # }}}
|
58
57
|
|
59
58
|
# Make an HTTP request to the API
|
60
59
|
def request(attrs={}) # {{{
|
61
60
|
method = attrs.delete(:_method)
|
62
61
|
path = attrs.delete(:_path)
|
63
|
-
@connection.send method do |request|
|
62
|
+
response = @connection.send method do |request|
|
64
63
|
if method == :get
|
65
64
|
# For GET requests, treat additional parameters as querystring data
|
66
65
|
request.url path, attrs
|
@@ -70,11 +69,7 @@ module Her
|
|
70
69
|
request.body = attrs
|
71
70
|
end
|
72
71
|
end
|
73
|
-
|
74
|
-
|
75
|
-
# Parse the HTTP response
|
76
|
-
def parse(response) # {{{
|
77
|
-
@parse_with.call(response)
|
72
|
+
response.env[:body]
|
78
73
|
end # }}}
|
79
74
|
end
|
80
75
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Her
|
2
|
+
module Middleware
|
3
|
+
class DefaultParseJSON < Faraday::Response::Middleware
|
4
|
+
def parse(body)
|
5
|
+
json = JSON.parse(body, :symbolize_names => true)
|
6
|
+
return {
|
7
|
+
:data => json[:data],
|
8
|
+
:errors => json[:errors],
|
9
|
+
:metadata => json[:metadata],
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_complete(env)
|
14
|
+
env[:body] = parse env[:body]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/her/model/http.rb
CHANGED
@@ -34,8 +34,7 @@ module Her
|
|
34
34
|
# Main request wrapper around Her::API. Used to make custom request to the API.
|
35
35
|
# @private
|
36
36
|
def request(attrs={}, &block) # {{{
|
37
|
-
|
38
|
-
yield @her_api.parse(response)
|
37
|
+
yield @her_api.request(attrs)
|
39
38
|
end # }}}
|
40
39
|
|
41
40
|
# Make a GET request and return the parsed JSON response (not mapped to objects)
|
data/lib/her/version.rb
CHANGED
data/spec/api_spec.rb
CHANGED
@@ -17,43 +17,69 @@ describe Her::API do
|
|
17
17
|
@api.base_uri.should == "https://api.example.com"
|
18
18
|
end # }}}
|
19
19
|
|
20
|
-
it "sets
|
20
|
+
it "sets middleware" do # {{{
|
21
|
+
class Foo < Faraday::Response::Middleware; end;
|
22
|
+
class Bar < Faraday::Response::Middleware; end;
|
23
|
+
|
21
24
|
@api = Her::API.new
|
22
|
-
@api.setup :base_uri => "https://api.example.com"
|
23
|
-
@api.
|
24
|
-
response.body
|
25
|
-
end
|
26
|
-
@api.custom_parsing_block?.should be_true
|
25
|
+
@api.setup :base_uri => "https://api.example.com", :middleware => [Foo, Bar]
|
26
|
+
@api.middleware.should == [Foo, Bar]
|
27
27
|
end # }}}
|
28
28
|
end
|
29
29
|
|
30
30
|
describe "#request" do
|
31
|
-
|
32
|
-
@api = Her::API.new
|
33
|
-
@api.setup :base_uri => "https://api.example.com"
|
31
|
+
it "makes HTTP requests" do # {{{
|
34
32
|
FakeWeb.register_uri(:get, "https://api.example.com/foo", :body => "Foo, it is.")
|
35
|
-
end # }}}
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
class Foo < Faraday::Response::Middleware
|
35
|
+
def on_complete(env)
|
36
|
+
env[:body] = { :data => env[:body] }
|
37
|
+
end
|
38
|
+
end
|
42
39
|
|
43
|
-
describe "#parse" do
|
44
|
-
before do # {{{
|
45
40
|
@api = Her::API.new
|
46
|
-
@api.setup :base_uri => "https://api.example.com"
|
47
|
-
|
41
|
+
@api.setup :base_uri => "https://api.example.com", :middleware => []
|
42
|
+
@api.request(:_method => :get, :_path => "/foo") do |parsed_data|
|
43
|
+
parsed_data[:data] == "Foo, it is."
|
44
|
+
end
|
48
45
|
end # }}}
|
49
46
|
|
50
|
-
it "parses a request" do # {{{
|
51
|
-
|
52
|
-
|
47
|
+
it "parses a request with the default parser" do # {{{
|
48
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "George Michael Bluth" }, :errors => ["This is a single error"], :metadata => { :page => 1, :per_page => 10 }}.to_json)
|
49
|
+
|
50
|
+
@api = Her::API.new
|
51
|
+
@api.setup :base_uri => "https://api.example.com"
|
52
|
+
@api.request(:_method => :get, :_path => "users/1") do |parsed_data|
|
53
|
+
parsed_data[:data].should == { :id => 1, :name => "George Michael Bluth" }
|
53
54
|
parsed_data[:errors].should == ["This is a single error"]
|
54
55
|
parsed_data[:metadata].should == { :page => 1, :per_page => 10 }
|
55
56
|
end
|
56
57
|
end # }}}
|
58
|
+
|
59
|
+
it "parses a request with a custom parser" do # {{{
|
60
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :id => 1, :name => "George Michael Bluth" }.to_json)
|
61
|
+
|
62
|
+
class CustomParser < Faraday::Response::Middleware
|
63
|
+
def on_complete(env)
|
64
|
+
json = JSON.parse(env[:body], :symbolize_names => true)
|
65
|
+
errors = json.delete(:errors) || []
|
66
|
+
metadata = json.delete(:metadata) || {}
|
67
|
+
env[:body] = {
|
68
|
+
:data => json,
|
69
|
+
:errors => errors,
|
70
|
+
:metadata => metadata,
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
@api = Her::API.new
|
76
|
+
@api.setup :base_uri => "https://api.example.com", :middleware => [CustomParser] + Her::API.default_middleware
|
77
|
+
@api.request(:_method => :get, :_path => "users/1") do |parsed_data|
|
78
|
+
parsed_data[:data].should == { :id => 1, :name => "George Michael Bluth" }
|
79
|
+
parsed_data[:errors].should == []
|
80
|
+
parsed_data[:metadata].should == {}
|
81
|
+
end
|
82
|
+
end # }}}
|
57
83
|
end
|
58
84
|
end
|
59
85
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: her
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -171,6 +171,8 @@ files:
|
|
171
171
|
- her.gemspec
|
172
172
|
- lib/her.rb
|
173
173
|
- lib/her/api.rb
|
174
|
+
- lib/her/middleware.rb
|
175
|
+
- lib/her/middleware/default_parse_json.rb
|
174
176
|
- lib/her/model.rb
|
175
177
|
- lib/her/model/base.rb
|
176
178
|
- lib/her/model/hooks.rb
|
@@ -198,7 +200,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
198
200
|
version: '0'
|
199
201
|
segments:
|
200
202
|
- 0
|
201
|
-
hash:
|
203
|
+
hash: -2767021996149695300
|
202
204
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
205
|
none: false
|
204
206
|
requirements:
|
@@ -207,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
207
209
|
version: '0'
|
208
210
|
segments:
|
209
211
|
- 0
|
210
|
-
hash:
|
212
|
+
hash: -2767021996149695300
|
211
213
|
requirements: []
|
212
214
|
rubyforge_project:
|
213
215
|
rubygems_version: 1.8.18
|