her 0.8.2 → 0.9.0

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/.rubocop.yml +1291 -0
  4. data/.travis.yml +2 -0
  5. data/README.md +23 -3
  6. data/her.gemspec +1 -3
  7. data/lib/her/middleware/json_api_parser.rb +1 -1
  8. data/lib/her/model/associations/association.rb +31 -0
  9. data/lib/her/model/associations/association_proxy.rb +1 -1
  10. data/lib/her/model/attributes.rb +2 -0
  11. data/lib/her/model/orm.rb +79 -6
  12. data/lib/her/model/parse.rb +8 -12
  13. data/lib/her/model/relation.rb +45 -1
  14. data/lib/her/version.rb +1 -1
  15. data/spec/api_spec.rb +34 -31
  16. data/spec/collection_spec.rb +25 -10
  17. data/spec/json_api/model_spec.rb +75 -72
  18. data/spec/middleware/accept_json_spec.rb +1 -1
  19. data/spec/middleware/first_level_parse_json_spec.rb +20 -20
  20. data/spec/middleware/json_api_parser_spec.rb +26 -7
  21. data/spec/middleware/second_level_parse_json_spec.rb +8 -9
  22. data/spec/model/associations/association_proxy_spec.rb +2 -5
  23. data/spec/model/associations_spec.rb +248 -161
  24. data/spec/model/attributes_spec.rb +106 -99
  25. data/spec/model/callbacks_spec.rb +58 -26
  26. data/spec/model/dirty_spec.rb +30 -29
  27. data/spec/model/http_spec.rb +67 -35
  28. data/spec/model/introspection_spec.rb +26 -22
  29. data/spec/model/nested_attributes_spec.rb +31 -31
  30. data/spec/model/orm_spec.rb +312 -155
  31. data/spec/model/parse_spec.rb +77 -77
  32. data/spec/model/paths_spec.rb +109 -109
  33. data/spec/model/relation_spec.rb +76 -68
  34. data/spec/model/validations_spec.rb +6 -6
  35. data/spec/model_spec.rb +17 -17
  36. data/spec/spec_helper.rb +2 -3
  37. data/spec/support/macros/model_macros.rb +2 -2
  38. metadata +32 -59
@@ -1,163 +1,166 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe Her::JsonApi::Model do
4
4
  before do
5
- Her::API.setup :url => "https://api.example.com" do |connection|
5
+ Her::API.setup url: "https://api.example.com" do |connection|
6
6
  connection.use Her::Middleware::JsonApiParser
7
7
  connection.adapter :test do |stub|
8
- stub.get("/users/1") do |env|
9
- [
8
+ stub.get("/users/1") do
9
+ [
10
10
  200,
11
11
  {},
12
12
  {
13
13
  data: {
14
14
  id: 1,
15
- type: 'users',
15
+ type: "users",
16
16
  attributes: {
17
- name: "Roger Federer",
18
- },
17
+ name: "Roger Federer"
18
+ }
19
19
  }
20
-
20
+
21
21
  }.to_json
22
- ]
22
+ ]
23
23
  end
24
24
 
25
- stub.get("/users") do |env|
26
- [
25
+ stub.get("/users") do
26
+ [
27
27
  200,
28
28
  {},
29
29
  {
30
30
  data: [
31
31
  {
32
32
  id: 1,
33
- type: 'users',
33
+ type: "users",
34
34
  attributes: {
35
- name: "Roger Federer",
36
- },
37
- },
35
+ name: "Roger Federer"
36
+ }
37
+ },
38
38
  {
39
39
  id: 2,
40
- type: 'users',
40
+ type: "users",
41
41
  attributes: {
42
- name: "Kei Nishikori",
43
- },
42
+ name: "Kei Nishikori"
43
+ }
44
44
  }
45
45
  ]
46
46
  }.to_json
47
- ]
47
+ ]
48
48
  end
49
49
 
50
- stub.post("/users", data: {
51
- type: 'users',
50
+ stub.post("/users", data:
51
+ {
52
+ type: "users",
52
53
  attributes: {
53
- name: "Jeremy Lin",
54
- },
55
- }) do |env|
56
- [
54
+ name: "Jeremy Lin"
55
+ }
56
+ }) do
57
+ [
57
58
  201,
58
59
  {},
59
60
  {
60
61
  data: {
61
62
  id: 3,
62
- type: 'users',
63
+ type: "users",
63
64
  attributes: {
64
- name: 'Jeremy Lin',
65
- },
65
+ name: "Jeremy Lin"
66
+ }
66
67
  }
67
-
68
+
68
69
  }.to_json
69
- ]
70
+ ]
70
71
  end
71
72
 
72
- stub.patch("/users/1", data: {
73
- type: 'users',
73
+ stub.patch("/users/1", data:
74
+ {
75
+ type: "users",
74
76
  id: 1,
75
77
  attributes: {
76
- name: "Fed GOAT",
77
- },
78
- }) do |env|
79
- [
78
+ name: "Fed GOAT"
79
+ }
80
+ }) do
81
+ [
80
82
  200,
81
83
  {},
82
84
  {
83
85
  data: {
84
86
  id: 1,
85
- type: 'users',
87
+ type: "users",
86
88
  attributes: {
87
- name: 'Fed GOAT',
88
- },
89
+ name: "Fed GOAT"
90
+ }
89
91
  }
90
-
92
+
91
93
  }.to_json
92
- ]
94
+ ]
93
95
  end
94
96
 
95
- stub.delete("/users/1") { |env|
96
- [ 204, {}, {}, ]
97
- }
97
+ stub.delete("/users/1") do
98
+ [204, {}, {}]
99
+ end
98
100
  end
99
-
100
101
  end
101
102
 
102
103
  spawn_model("Foo::User", type: Her::JsonApi::Model)
103
104
  end
104
105
 
105
- it 'allows configuration of type' do
106
+ it "allows configuration of type" do
106
107
  spawn_model("Foo::Bar", type: Her::JsonApi::Model) do
107
108
  type :foobars
108
109
  end
109
110
 
110
- expect(Foo::Bar.instance_variable_get('@type')).to eql('foobars')
111
+ expect(Foo::Bar.instance_variable_get("@type")).to eql("foobars")
111
112
  end
112
113
 
113
- it 'finds models by id' do
114
+ it "finds models by id" do
114
115
  user = Foo::User.find(1)
115
116
  expect(user.attributes).to eql(
116
- 'id' => 1,
117
- 'name' => 'Roger Federer',
117
+ "id" => 1,
118
+ "name" => "Roger Federer"
118
119
  )
119
120
  end
120
121
 
121
- it 'finds a collection of models' do
122
+ it "finds a collection of models" do
122
123
  users = Foo::User.all
123
- expect(users.map(&:attributes)).to match_array([
124
- {
125
- 'id' => 1,
126
- 'name' => 'Roger Federer',
127
- },
128
- {
129
- 'id' => 2,
130
- 'name' => 'Kei Nishikori',
131
- }
132
- ])
124
+ expect(users.map(&:attributes)).to match_array(
125
+ [
126
+ {
127
+ "id" => 1,
128
+ "name" => "Roger Federer"
129
+ },
130
+ {
131
+ "id" => 2,
132
+ "name" => "Kei Nishikori"
133
+ }
134
+ ]
135
+ )
133
136
  end
134
137
 
135
- it 'creates a Foo::User' do
136
- user = Foo::User.new(name: 'Jeremy Lin')
138
+ it "creates a Foo::User" do
139
+ user = Foo::User.new(name: "Jeremy Lin")
137
140
  user.save
138
141
  expect(user.attributes).to eql(
139
- 'id' => 3,
140
- 'name' => 'Jeremy Lin',
142
+ "id" => 3,
143
+ "name" => "Jeremy Lin"
141
144
  )
142
145
  end
143
146
 
144
- it 'updates a Foo::User' do
147
+ it "updates a Foo::User" do
145
148
  user = Foo::User.find(1)
146
- user.name = 'Fed GOAT'
149
+ user.name = "Fed GOAT"
147
150
  user.save
148
151
  expect(user.attributes).to eql(
149
- 'id' => 1,
150
- 'name' => 'Fed GOAT',
152
+ "id" => 1,
153
+ "name" => "Fed GOAT"
151
154
  )
152
155
  end
153
156
 
154
- it 'destroys a Foo::User' do
157
+ it "destroys a Foo::User" do
155
158
  user = Foo::User.find(1)
156
159
  expect(user.destroy).to be_destroyed
157
160
  end
158
161
 
159
- context 'undefined methods' do
160
- it 'removes methods that are not compatible with json api' do
162
+ context "undefined methods" do
163
+ it "removes methods that are not compatible with json api" do
161
164
  [:parse_root_in_json, :include_root_in_json, :root_element, :primary_key].each do |method|
162
165
  expect { Foo::User.new.send(method, :foo) }.to raise_error NoMethodError, "Her::JsonApi::Model does not support the #{method} configuration option"
163
166
  end
@@ -4,7 +4,7 @@ require "spec_helper"
4
4
  describe Her::Middleware::AcceptJSON do
5
5
  it "adds an Accept header" do
6
6
  described_class.new.add_header({}).tap do |headers|
7
- headers["Accept"].should == "application/json"
7
+ expect(headers["Accept"]).to eq("application/json")
8
8
  end
9
9
  end
10
10
  end
@@ -7,55 +7,55 @@ describe Her::Middleware::FirstLevelParseJSON do
7
7
  let(:body_with_errors) { "{\"id\": 1, \"name\": \"Tobias Fünke\", \"errors\": { \"name\": [ \"not_valid\", \"should_be_present\" ] }, \"metadata\": 3}" }
8
8
  let(:body_with_malformed_json) { "wut." }
9
9
  let(:body_with_invalid_json) { "true" }
10
- let(:empty_body) { '' }
10
+ let(:empty_body) { "" }
11
11
  let(:nil_body) { nil }
12
12
 
13
13
  it "parses body as json" do
14
14
  subject.parse(body_without_errors).tap do |json|
15
- json[:data].should == { :id => 1, :name => "Tobias Fünke" }
16
- json[:metadata].should == 3
15
+ expect(json[:data]).to eq(id: 1, name: "Tobias Fünke")
16
+ expect(json[:metadata]).to eq(3)
17
17
  end
18
18
  end
19
19
 
20
20
  it "parses :body key as json in the env hash" do
21
- env = { :body => body_without_errors }
21
+ env = { body: body_without_errors }
22
22
  subject.on_complete(env)
23
23
  env[:body].tap do |json|
24
- json[:data].should == { :id => 1, :name => "Tobias Fünke" }
25
- json[:metadata].should == 3
24
+ expect(json[:data]).to eq(id: 1, name: "Tobias Fünke")
25
+ expect(json[:metadata]).to eq(3)
26
26
  end
27
27
  end
28
28
 
29
- it 'ensures the errors are a hash if there are no errors' do
30
- subject.parse(body_without_errors)[:errors].should eq({})
29
+ it "ensures the errors are a hash if there are no errors" do
30
+ expect(subject.parse(body_without_errors)[:errors]).to eq({})
31
31
  end
32
32
 
33
- it 'ensures the errors are a hash if there are no errors' do
34
- subject.parse(body_with_errors)[:errors].should eq({:name => [ 'not_valid', 'should_be_present']})
33
+ it "ensures the errors are a hash if there are no errors" do
34
+ expect(subject.parse(body_with_errors)[:errors]).to eq(name: %w(not_valid should_be_present))
35
35
  end
36
36
 
37
- it 'ensures that malformed JSON throws an exception' do
37
+ it "ensures that malformed JSON throws an exception" do
38
38
  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.")')
39
39
  end
40
40
 
41
- it 'ensures that invalid JSON throws an exception' do
41
+ it "ensures that invalid JSON throws an exception" do
42
42
  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")')
43
43
  end
44
44
 
45
- it 'ensures that a nil response returns an empty hash' do
46
- subject.parse(nil_body)[:data].should eq({})
45
+ it "ensures that a nil response returns an empty hash" do
46
+ expect(subject.parse(nil_body)[:data]).to eq({})
47
47
  end
48
48
 
49
- it 'ensures that an empty response returns an empty hash' do
50
- subject.parse(empty_body)[:data].should eq({})
49
+ it "ensures that an empty response returns an empty hash" do
50
+ expect(subject.parse(empty_body)[:data]).to eq({})
51
51
  end
52
52
 
53
- context 'with status code 204' do
54
- it 'returns an empty body' do
55
- env = { :status => 204 }
53
+ context "with status code 204" do
54
+ it "returns an empty body" do
55
+ env = { status: 204 }
56
56
  subject.on_complete(env)
57
57
  env[:body].tap do |json|
58
- json[:data].should == { }
58
+ expect(json[:data]).to eq({})
59
59
  end
60
60
  end
61
61
  end
@@ -12,21 +12,40 @@ describe Her::Middleware::JsonApiParser do
12
12
  subject.on_complete(env)
13
13
  env.fetch(:body).tap do |json|
14
14
  expect(json[:data]).to eql(
15
- :type => "foo",
16
- :id => "bar",
17
- :attributes => { :baz => "qux" }
15
+ type: "foo",
16
+ id: "bar",
17
+ attributes: { baz: "qux" }
18
18
  )
19
19
  expect(json[:errors]).to eql([])
20
- expect(json[:metadata]).to eql(:api => "json api")
20
+ expect(json[:metadata]).to eql(api: "json api")
21
21
  end
22
22
  end
23
23
  end
24
24
 
25
- #context "with invalid JSON body" do
25
+ context "with status code 204" do
26
+ it "returns an empty body" do
27
+ env = { status: 204 }
28
+ subject.on_complete(env)
29
+ env[:body].tap do |json|
30
+ expect(json[:data]).to eq({})
31
+ end
32
+ end
33
+ end
34
+
35
+ context 'with status code 304' do
36
+ it 'returns an empty body' do
37
+ env = { :status => 304 }
38
+ subject.on_complete(env)
39
+ env[:body].tap do |json|
40
+ expect(json[:data]).to eq({})
41
+ end
42
+ end
43
+ end
44
+
45
+ # context "with invalid JSON body" do
26
46
  # let(:body) { '"foo"' }
27
47
  # it 'ensures that invalid JSON throws an exception' do
28
48
  # 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\"")')
29
49
  # end
30
- #end
31
-
50
+ # end
32
51
  end
@@ -8,28 +8,27 @@ describe Her::Middleware::SecondLevelParseJSON do
8
8
  let(:body) { "{\"data\": 1, \"errors\": 2, \"metadata\": 3}" }
9
9
  it "parses body as json" do
10
10
  subject.parse(body).tap do |json|
11
- json[:data].should == 1
12
- json[:errors].should == 2
13
- json[:metadata].should == 3
11
+ expect(json[:data]).to eq(1)
12
+ expect(json[:errors]).to eq(2)
13
+ expect(json[:metadata]).to eq(3)
14
14
  end
15
15
  end
16
16
 
17
17
  it "parses :body key as json in the env hash" do
18
- env = { :body => body }
18
+ env = { body: body }
19
19
  subject.on_complete(env)
20
20
  env[:body].tap do |json|
21
- json[:data].should == 1
22
- json[:errors].should == 2
23
- json[:metadata].should == 3
21
+ expect(json[:data]).to eq(1)
22
+ expect(json[:errors]).to eq(2)
23
+ expect(json[:metadata]).to eq(3)
24
24
  end
25
25
  end
26
26
  end
27
27
 
28
28
  context "with invalid JSON body" do
29
29
  let(:body) { '"foo"' }
30
- it 'ensures that invalid JSON throws an exception' do
30
+ it "ensures that invalid JSON throws an exception" do
31
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\"")')
32
32
  end
33
33
  end
34
-
35
34
  end
@@ -8,8 +8,8 @@ describe Her::Model::Associations::AssociationProxy do
8
8
  builder.use Her::Middleware::FirstLevelParseJSON
9
9
  builder.use Faraday::Request::UrlEncoded
10
10
  builder.adapter :test do |stub|
11
- stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke" }.to_json ] }
12
- stub.get("/users/1/fish") { |env| [200, {}, { :id => 1, :name => "Tobias's Fish" }.to_json ] }
11
+ stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke" }.to_json] }
12
+ stub.get("/users/1/fish") { [200, {}, { id: 1, name: "Tobias's Fish" }.to_json] }
13
13
  end
14
14
  end
15
15
  spawn_model "User" do
@@ -26,6 +26,3 @@ describe Her::Model::Associations::AssociationProxy do
26
26
  end
27
27
  end
28
28
  end
29
-
30
-
31
-