her 0.5.2 → 0.5.3
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.
- 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
|