her 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +31 -11
- data/lib/her/errors.rb +1 -0
- data/lib/her/middleware.rb +1 -0
- data/lib/her/middleware/first_level_parse_json.rb +6 -6
- data/lib/her/middleware/parse_json.rb +20 -0
- data/lib/her/middleware/second_level_parse_json.rb +9 -3
- data/lib/her/model/orm.rb +1 -1
- data/lib/her/version.rb +1 -1
- data/spec/middleware/first_level_parse_json_spec.rb +15 -0
- data/spec/middleware/second_level_parse_json_spec.rb +23 -13
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MmIxMjIyZWU2NTIyOTdmZDdlNzU2MjBiM2NlNDU2MmFhMjJlNzVhNQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MDJmNTM0YzI1ZmY1OTM3NDM4NTczNDYxNWZjOWI0NjU1NzNlYzY2Mg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
M2Q3MDc2NWJmNDI1NDM3Y2M1YjNkNjgwN2JlMWY3M2M5ZjhmYWZmMTY4Njdk
|
10
|
+
MzViMjE0NDY1ZDlmOTNkNzBkNWI1MzgwNWFmYWYyM2YzOWE3YTE5YjcyZTg4
|
11
|
+
YzM5ZWZkMDFjODBjMjBlN2Q0MDBiNDI5MzYyYWNhODZlZDQzNDU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YWJmZDEwMzlhMzkxZWJlMDZjOTZlZDRkNzM4MTMyZDBlZTlkZmM1NWUzOTMz
|
14
|
+
YWFkMjBlNjgzY2U2ZWQxYTg3ZThlODZkMGVkOWU3MzVjNTU2ODc5ZjYyMGQ2
|
15
|
+
YzJkNWFhYjRiY2EzZGI1YzE1OGQ5YzJkYzVjYTY0MWJiYjBmNmQ=
|
data/README.md
CHANGED
@@ -296,16 +296,14 @@ If there’s association data in the resource, no extra HTTP request is made whe
|
|
296
296
|
```ruby
|
297
297
|
@user = User.find(1)
|
298
298
|
# {
|
299
|
-
# :
|
300
|
-
#
|
301
|
-
#
|
302
|
-
# :
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
# :organization => { :id => 2, :name => "Bluth Company" }
|
308
|
-
# }
|
299
|
+
# :id => 1,
|
300
|
+
# :name => "George Michael Bluth",
|
301
|
+
# :comments => [
|
302
|
+
# { :id => 1, :text => "Foo" },
|
303
|
+
# { :id => 2, :text => "Bar" }
|
304
|
+
# ],
|
305
|
+
# :role => { :id => 1, :name => "Admin" },
|
306
|
+
# :organization => { :id => 2, :name => "Bluth Company" }
|
309
307
|
# }
|
310
308
|
@user.comments
|
311
309
|
# [#<Comment id=1 text="Foo">, #<Comment id=2 text="Bar">]
|
@@ -319,7 +317,7 @@ If there’s no association data in the resource, Her makes a HTTP request to re
|
|
319
317
|
|
320
318
|
```ruby
|
321
319
|
@user = User.find(1)
|
322
|
-
# { :
|
320
|
+
# { :id => 1, :name => "George Michael Bluth", :organization_id => 2 }
|
323
321
|
|
324
322
|
# has_many association:
|
325
323
|
@user.comments
|
@@ -340,6 +338,28 @@ If there’s no association data in the resource, Her makes a HTTP request to re
|
|
340
338
|
|
341
339
|
Subsequent calls to `#comments`, `#role` and `#organization` will not trigger extra HTTP requests and will return the cached objects.
|
342
340
|
|
341
|
+
#### Notes about paths
|
342
|
+
|
343
|
+
Resources must always have all the required attributes to build their complete path. For example, if you have these models:
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
class User
|
347
|
+
include Her::Model
|
348
|
+
collection_path "organizations/:organization_id/users"
|
349
|
+
end
|
350
|
+
|
351
|
+
class Organization
|
352
|
+
include Her::Model
|
353
|
+
has_many :users
|
354
|
+
end
|
355
|
+
```
|
356
|
+
|
357
|
+
Her expects all `User` resources to have an `:organization_id` (or `:_organization_id`) attribute. Otherwise, calling mostly all methods, like `User.all`, will thrown an exception like this one:
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
Her::Errors::PathError: Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users`. Parameters are `{ … }`.
|
361
|
+
```
|
362
|
+
|
343
363
|
### Validations
|
344
364
|
|
345
365
|
Her includes `ActiveModel::Validations` so you can declare validations the same way you do in Rails.
|
data/lib/her/errors.rb
CHANGED
data/lib/her/middleware.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
module Her
|
2
2
|
module Middleware
|
3
3
|
# This middleware treat the received first-level JSON structure as the resource data.
|
4
|
-
class FirstLevelParseJSON <
|
4
|
+
class FirstLevelParseJSON < ParseJSON
|
5
5
|
# Parse the response body
|
6
6
|
#
|
7
7
|
# @param [String] body The response body
|
8
8
|
# @return [Mixed] the parsed response
|
9
9
|
def parse(body)
|
10
|
-
json =
|
10
|
+
json = parse_json(body)
|
11
11
|
errors = json.delete(:errors) || {}
|
12
|
-
metadata = json.delete(:metadata) ||
|
12
|
+
metadata = json.delete(:metadata) || {}
|
13
13
|
{
|
14
14
|
:data => json,
|
15
15
|
:errors => errors,
|
@@ -22,11 +22,11 @@ module Her
|
|
22
22
|
#
|
23
23
|
# @param [Hash] env The response environment
|
24
24
|
def on_complete(env)
|
25
|
-
case env[:status]
|
25
|
+
env[:body] = case env[:status]
|
26
26
|
when 204
|
27
|
-
|
27
|
+
parse('{}')
|
28
28
|
else
|
29
|
-
|
29
|
+
parse(env[:body])
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Her
|
2
|
+
module Middleware
|
3
|
+
class ParseJSON < Faraday::Response::Middleware
|
4
|
+
def parse_json(body = nil)
|
5
|
+
body ||= '{}'
|
6
|
+
message = "Response from the API must behave like a Hash or an Array (last JSON response was #{body.inspect})"
|
7
|
+
|
8
|
+
json = begin
|
9
|
+
MultiJson.load(body, :symbolize_keys => true)
|
10
|
+
rescue MultiJson::LoadError
|
11
|
+
raise Her::Errors::ParseError, message
|
12
|
+
end
|
13
|
+
|
14
|
+
raise Her::Errors::ParseError, message unless json.is_a?(Hash) or json.is_a?(Array)
|
15
|
+
|
16
|
+
json
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -2,13 +2,14 @@ module Her
|
|
2
2
|
module Middleware
|
3
3
|
# This middleware expects the resource/collection data to be contained in the `data`
|
4
4
|
# key of the JSON object
|
5
|
-
class SecondLevelParseJSON <
|
5
|
+
class SecondLevelParseJSON < ParseJSON
|
6
6
|
# Parse the response body
|
7
7
|
#
|
8
8
|
# @param [String] body The response body
|
9
9
|
# @return [Mixed] the parsed response
|
10
10
|
def parse(body)
|
11
|
-
json =
|
11
|
+
json = parse_json(body)
|
12
|
+
|
12
13
|
{
|
13
14
|
:data => json[:data],
|
14
15
|
:errors => json[:errors],
|
@@ -21,7 +22,12 @@ module Her
|
|
21
22
|
#
|
22
23
|
# @param [Hash] env The response environment
|
23
24
|
def on_complete(env)
|
24
|
-
env[:body] =
|
25
|
+
env[:body] = case env[:status]
|
26
|
+
when 204
|
27
|
+
parse('{}')
|
28
|
+
else
|
29
|
+
parse(env[:body])
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
27
33
|
end
|
data/lib/her/model/orm.rb
CHANGED
@@ -265,7 +265,7 @@ module Her
|
|
265
265
|
resource = nil
|
266
266
|
request(params.merge(:_method => :get, :_path => "#{build_request_path(params.merge(:id => id))}")) do |parsed_data, response|
|
267
267
|
if response.success?
|
268
|
-
resource = new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:
|
268
|
+
resource = new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
|
269
269
|
resource.run_callbacks :find
|
270
270
|
else
|
271
271
|
return nil
|
data/lib/her/version.rb
CHANGED
@@ -5,6 +5,9 @@ describe Her::Middleware::FirstLevelParseJSON do
|
|
5
5
|
subject { described_class.new }
|
6
6
|
let(:body_without_errors) { "{\"id\": 1, \"name\": \"Tobias Fünke\", \"metadata\": 3}" }
|
7
7
|
let(:body_with_errors) { "{\"id\": 1, \"name\": \"Tobias Fünke\", \"errors\": { \"name\": [ \"not_valid\", \"should_be_present\" ] }, \"metadata\": 3}" }
|
8
|
+
let(:body_with_malformed_json) { "wut." }
|
9
|
+
let(:body_with_invalid_json) { "true" }
|
10
|
+
let(:nil_body) { nil }
|
8
11
|
|
9
12
|
it "parses body as json" do
|
10
13
|
subject.parse(body_without_errors).tap do |json|
|
@@ -30,6 +33,18 @@ describe Her::Middleware::FirstLevelParseJSON do
|
|
30
33
|
subject.parse(body_with_errors)[:errors].should eq({:name => [ 'not_valid', 'should_be_present']})
|
31
34
|
end
|
32
35
|
|
36
|
+
it 'ensures that malformed JSON throws an exception' do
|
37
|
+
expect { subject.parse(body_with_malformed_json) }.to raise_error(Her::Errors::ParseError, 'Response from the API must behave like a Hash or an Array (last JSON response was "wut.")')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'ensures that invalid JSON throws an exception' do
|
41
|
+
expect { subject.parse(body_with_invalid_json) }.to raise_error(Her::Errors::ParseError, 'Response from the API must behave like a Hash or an Array (last JSON response was "true")')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'ensures that a nil response returns an empty hash' do
|
45
|
+
subject.parse(nil_body)[:data].should eq({})
|
46
|
+
end
|
47
|
+
|
33
48
|
context 'with status code 204' do
|
34
49
|
it 'returns an empty body' do
|
35
50
|
env = { :status => 204 }
|
@@ -3,23 +3,33 @@ require "spec_helper"
|
|
3
3
|
|
4
4
|
describe Her::Middleware::SecondLevelParseJSON do
|
5
5
|
subject { described_class.new }
|
6
|
-
let(:body) { "{\"data\": 1, \"errors\": 2, \"metadata\": 3}" }
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
context "with valid JSON body" do
|
8
|
+
let(:body) { "{\"data\": 1, \"errors\": 2, \"metadata\": 3}" }
|
9
|
+
it "parses body as json" do
|
10
|
+
subject.parse(body).tap do |json|
|
11
|
+
json[:data].should == 1
|
12
|
+
json[:errors].should == 2
|
13
|
+
json[:metadata].should == 3
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "parses :body key as json in the env hash" do
|
18
|
+
env = { :body => body }
|
19
|
+
subject.on_complete(env)
|
20
|
+
env[:body].tap do |json|
|
21
|
+
json[:data].should == 1
|
22
|
+
json[:errors].should == 2
|
23
|
+
json[:metadata].should == 3
|
24
|
+
end
|
13
25
|
end
|
14
26
|
end
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
json[:data].should == 1
|
21
|
-
json[:errors].should == 2
|
22
|
-
json[:metadata].should == 3
|
28
|
+
context "with invalid JSON body" do
|
29
|
+
let(:body) { '"foo"' }
|
30
|
+
it 'ensures that invalid JSON throws an exception' do
|
31
|
+
expect { subject.parse(body) }.to raise_error(Her::Errors::ParseError, 'Response from the API must behave like a Hash or an Array (last JSON response was "\"foo\"")')
|
23
32
|
end
|
24
33
|
end
|
34
|
+
|
25
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: her
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rémi Prévost
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -140,6 +140,7 @@ files:
|
|
140
140
|
- lib/her/middleware.rb
|
141
141
|
- lib/her/middleware/accept_json.rb
|
142
142
|
- lib/her/middleware/first_level_parse_json.rb
|
143
|
+
- lib/her/middleware/parse_json.rb
|
143
144
|
- lib/her/middleware/second_level_parse_json.rb
|
144
145
|
- lib/her/model.rb
|
145
146
|
- lib/her/model/associations.rb
|