castle-her 1.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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.travis.yml +17 -0
- data/.yardopts +2 -0
- data/CONTRIBUTING.md +26 -0
- data/Gemfile +10 -0
- data/LICENSE +7 -0
- data/README.md +1017 -0
- data/Rakefile +11 -0
- data/UPGRADE.md +110 -0
- data/castle-her.gemspec +30 -0
- data/gemfiles/Gemfile.activemodel-3.2.x +7 -0
- data/gemfiles/Gemfile.activemodel-4.0 +7 -0
- data/gemfiles/Gemfile.activemodel-4.1 +7 -0
- data/gemfiles/Gemfile.activemodel-4.2 +7 -0
- data/gemfiles/Gemfile.activemodel-5.0.x +7 -0
- data/lib/castle-her.rb +20 -0
- data/lib/castle-her/api.rb +113 -0
- data/lib/castle-her/collection.rb +12 -0
- data/lib/castle-her/errors.rb +27 -0
- data/lib/castle-her/json_api/model.rb +46 -0
- data/lib/castle-her/middleware.rb +12 -0
- data/lib/castle-her/middleware/accept_json.rb +17 -0
- data/lib/castle-her/middleware/first_level_parse_json.rb +36 -0
- data/lib/castle-her/middleware/json_api_parser.rb +36 -0
- data/lib/castle-her/middleware/parse_json.rb +21 -0
- data/lib/castle-her/middleware/second_level_parse_json.rb +36 -0
- data/lib/castle-her/model.rb +75 -0
- data/lib/castle-her/model/associations.rb +141 -0
- data/lib/castle-her/model/associations/association.rb +103 -0
- data/lib/castle-her/model/associations/association_proxy.rb +45 -0
- data/lib/castle-her/model/associations/belongs_to_association.rb +96 -0
- data/lib/castle-her/model/associations/has_many_association.rb +100 -0
- data/lib/castle-her/model/associations/has_one_association.rb +79 -0
- data/lib/castle-her/model/attributes.rb +284 -0
- data/lib/castle-her/model/base.rb +33 -0
- data/lib/castle-her/model/deprecated_methods.rb +61 -0
- data/lib/castle-her/model/http.rb +114 -0
- data/lib/castle-her/model/introspection.rb +65 -0
- data/lib/castle-her/model/nested_attributes.rb +45 -0
- data/lib/castle-her/model/orm.rb +207 -0
- data/lib/castle-her/model/parse.rb +216 -0
- data/lib/castle-her/model/paths.rb +126 -0
- data/lib/castle-her/model/relation.rb +164 -0
- data/lib/castle-her/version.rb +3 -0
- data/spec/api_spec.rb +114 -0
- data/spec/collection_spec.rb +26 -0
- data/spec/json_api/model_spec.rb +166 -0
- data/spec/middleware/accept_json_spec.rb +10 -0
- data/spec/middleware/first_level_parse_json_spec.rb +62 -0
- data/spec/middleware/json_api_parser_spec.rb +32 -0
- data/spec/middleware/second_level_parse_json_spec.rb +35 -0
- data/spec/model/associations/association_proxy_spec.rb +31 -0
- data/spec/model/associations_spec.rb +504 -0
- data/spec/model/attributes_spec.rb +389 -0
- data/spec/model/callbacks_spec.rb +145 -0
- data/spec/model/dirty_spec.rb +91 -0
- data/spec/model/http_spec.rb +158 -0
- data/spec/model/introspection_spec.rb +76 -0
- data/spec/model/nested_attributes_spec.rb +134 -0
- data/spec/model/orm_spec.rb +506 -0
- data/spec/model/parse_spec.rb +345 -0
- data/spec/model/paths_spec.rb +347 -0
- data/spec/model/relation_spec.rb +226 -0
- data/spec/model/validations_spec.rb +42 -0
- data/spec/model_spec.rb +44 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/extensions/array.rb +5 -0
- data/spec/support/extensions/hash.rb +5 -0
- data/spec/support/macros/her_macros.rb +17 -0
- data/spec/support/macros/model_macros.rb +36 -0
- data/spec/support/macros/request_macros.rb +27 -0
- metadata +290 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe "Her::Model and ActiveModel::Dirty" do
|
5
|
+
context "checking dirty attributes" do
|
6
|
+
before do
|
7
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
8
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
9
|
+
builder.use Faraday::Request::UrlEncoded
|
10
|
+
builder.adapter :test do |stub|
|
11
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
|
12
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 2, :fullname => "Maeby Fünke" }.to_json] }
|
13
|
+
stub.get("/users/3") { |env| [200, {}, { :user_id => 3, :fullname => "Maeby Fünke" }.to_json] }
|
14
|
+
stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
|
15
|
+
stub.put("/users/2") { |env| [400, {}, { :errors => ["Email cannot be blank"] }.to_json] }
|
16
|
+
stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
spawn_model "Foo::User" do
|
21
|
+
attributes :fullname, :email
|
22
|
+
end
|
23
|
+
spawn_model "Dynamic::User" do
|
24
|
+
primary_key :user_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "for existing resource" do
|
29
|
+
let(:user) { Foo::User.find(1) }
|
30
|
+
it "has no changes" do
|
31
|
+
expect(user.changes).to be_empty
|
32
|
+
expect(user).not_to be_changed
|
33
|
+
end
|
34
|
+
context "with successful save" do
|
35
|
+
it "tracks dirty attributes" do
|
36
|
+
user.fullname = "Tobias Fünke"
|
37
|
+
expect(user.fullname_changed?).to be_truthy
|
38
|
+
expect(user.email_changed?).to be_falsey
|
39
|
+
expect(user).to be_changed
|
40
|
+
user.save
|
41
|
+
expect(user).not_to be_changed
|
42
|
+
end
|
43
|
+
|
44
|
+
it "tracks only changed dirty attributes" do
|
45
|
+
user.fullname = user.fullname
|
46
|
+
expect(user.fullname_changed?).to be_falsey
|
47
|
+
end
|
48
|
+
|
49
|
+
it "tracks previous changes" do
|
50
|
+
user.fullname = "Tobias Fünke"
|
51
|
+
user.save
|
52
|
+
expect(user.previous_changes).to eq({"fullname"=>"Lindsay Fünke"})
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'tracks dirty attribute for mass assign for dynamic created attributes' do
|
56
|
+
user = Dynamic::User.find(3)
|
57
|
+
user.assign_attributes(:fullname => 'New Fullname')
|
58
|
+
expect(user.fullname_changed?).to be_truthy
|
59
|
+
expect(user).to be_changed
|
60
|
+
expect(user.changes.length).to eq(1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "with erroneous save" do
|
65
|
+
it "tracks dirty attributes" do
|
66
|
+
user = Foo::User.find(2)
|
67
|
+
user.fullname = "Tobias Fünke"
|
68
|
+
expect(user.fullname_changed?).to be_truthy
|
69
|
+
expect(user.email_changed?).to be_falsey
|
70
|
+
expect(user).to be_changed
|
71
|
+
user.save
|
72
|
+
expect(user).to be_changed
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "for new resource" do
|
78
|
+
let(:user) { Foo::User.new(:fullname => "Lindsay Fünke") }
|
79
|
+
it "has changes" do
|
80
|
+
expect(user).to be_changed
|
81
|
+
end
|
82
|
+
it "tracks dirty attributes" do
|
83
|
+
user.fullname = "Tobias Fünke"
|
84
|
+
expect(user.fullname_changed?).to be_truthy
|
85
|
+
expect(user).to be_changed
|
86
|
+
user.save
|
87
|
+
expect(user).not_to be_changed
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::HTTP do
|
5
|
+
context "binding a model with an API" do
|
6
|
+
let(:api1) { Her::API.new :url => "https://api1.example.com" }
|
7
|
+
let(:api2) { Her::API.new :url => "https://api2.example.com" }
|
8
|
+
|
9
|
+
before do
|
10
|
+
spawn_model("Foo::User")
|
11
|
+
spawn_model("Foo::Comment")
|
12
|
+
Her::API.setup :url => "https://api.example.com"
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when binding a model to its superclass' her_api" do
|
16
|
+
before do
|
17
|
+
spawn_model "Foo::Superclass"
|
18
|
+
Foo::Superclass.uses_api api1
|
19
|
+
Foo::Subclass = Class.new(Foo::Superclass)
|
20
|
+
end
|
21
|
+
|
22
|
+
specify { expect(Foo::Subclass.her_api).to eq(Foo::Superclass.her_api) }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when changing her_api without changing the parent class' her_api" do
|
26
|
+
before do
|
27
|
+
spawn_model "Foo::Superclass"
|
28
|
+
Foo::Subclass = Class.new(Foo::Superclass)
|
29
|
+
Foo::Superclass.uses_api api1
|
30
|
+
Foo::Subclass.uses_api api2
|
31
|
+
end
|
32
|
+
|
33
|
+
specify { expect(Foo::Subclass.her_api).not_to eq(Foo::Superclass.her_api) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "making HTTP requests" do
|
38
|
+
before do
|
39
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
40
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
41
|
+
builder.use Faraday::Request::UrlEncoded
|
42
|
+
builder.adapter :test do |stub|
|
43
|
+
stub.get("/users") { |env| [200, {}, [{ :id => 1 }].to_json] }
|
44
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1 }.to_json] }
|
45
|
+
stub.get("/users/popular") do |env|
|
46
|
+
if env[:params]["page"] == "2"
|
47
|
+
[200, {}, [{ :id => 3 }, { :id => 4 }].to_json]
|
48
|
+
else
|
49
|
+
[200, {}, [{ :id => 1 }, { :id => 2 }].to_json]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
spawn_model "Foo::User"
|
56
|
+
end
|
57
|
+
|
58
|
+
describe :get do
|
59
|
+
subject { Foo::User.get(:popular) }
|
60
|
+
its(:length) { should == 2 }
|
61
|
+
specify { expect(subject.first.id).to eq(1) }
|
62
|
+
end
|
63
|
+
|
64
|
+
describe :get_raw do
|
65
|
+
context "with a block" do
|
66
|
+
specify do
|
67
|
+
Foo::User.get_raw("/users") do |parsed_data, response|
|
68
|
+
expect(parsed_data[:data]).to eq([{ :id => 1 }])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with a return value" do
|
74
|
+
subject { Foo::User.get_raw("/users") }
|
75
|
+
specify { expect(subject[:parsed_data][:data]).to eq([{ :id => 1 }]) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe :get_collection do
|
80
|
+
context "with a String path" do
|
81
|
+
subject { Foo::User.get_collection("/users/popular") }
|
82
|
+
its(:length) { should == 2 }
|
83
|
+
specify { expect(subject.first.id).to eq(1) }
|
84
|
+
end
|
85
|
+
|
86
|
+
context "with a Symbol" do
|
87
|
+
subject { Foo::User.get_collection(:popular) }
|
88
|
+
its(:length) { should == 2 }
|
89
|
+
specify { expect(subject.first.id).to eq(1) }
|
90
|
+
end
|
91
|
+
|
92
|
+
context "with extra parameters" do
|
93
|
+
subject { Foo::User.get_collection(:popular, :page => 2) }
|
94
|
+
its(:length) { should == 2 }
|
95
|
+
specify { expect(subject.first.id).to eq(3) }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe :get_resource do
|
100
|
+
context "with a String path" do
|
101
|
+
subject { Foo::User.get_resource("/users/1") }
|
102
|
+
its(:id) { should == 1 }
|
103
|
+
end
|
104
|
+
|
105
|
+
context "with a Symbol" do
|
106
|
+
subject { Foo::User.get_resource(:"1") }
|
107
|
+
its(:id) { should == 1 }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe :get_raw do
|
112
|
+
specify do
|
113
|
+
Foo::User.get_raw(:popular) do |parsed_data, response|
|
114
|
+
expect(parsed_data[:data]).to eq([{ :id => 1 }, { :id => 2 }])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "setting custom HTTP requests" do
|
121
|
+
before do
|
122
|
+
Her::API.setup :url => "https://api.example.com" do |connection|
|
123
|
+
connection.use Her::Middleware::FirstLevelParseJSON
|
124
|
+
connection.adapter :test do |stub|
|
125
|
+
stub.get("/users/popular") { |env| [200, {}, [{ :id => 1 }, { :id => 2 }].to_json] }
|
126
|
+
stub.post("/users/from_default") { |env| [200, {}, { :id => 4 }.to_json] }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
spawn_model "Foo::User"
|
131
|
+
end
|
132
|
+
|
133
|
+
subject { Foo::User }
|
134
|
+
|
135
|
+
describe :custom_get do
|
136
|
+
context "without cache" do
|
137
|
+
before { Foo::User.custom_get :popular, :recent }
|
138
|
+
it { should respond_to(:popular) }
|
139
|
+
it { should respond_to(:recent) }
|
140
|
+
|
141
|
+
context "making the HTTP request" do
|
142
|
+
subject { Foo::User.popular }
|
143
|
+
its(:length) { should == 2 }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe :custom_post do
|
149
|
+
before { Foo::User.custom_post :from_default }
|
150
|
+
it { should respond_to(:from_default) }
|
151
|
+
|
152
|
+
context "making the HTTP request" do
|
153
|
+
subject { Foo::User.from_default(:name => "Tobias Fünke") }
|
154
|
+
its(:id) { should == 4 }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::Introspection do
|
5
|
+
context "introspecting a resource" do
|
6
|
+
before do
|
7
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
8
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
9
|
+
builder.use Faraday::Request::UrlEncoded
|
10
|
+
builder.adapter :test do |stub|
|
11
|
+
stub.post("/users") { |env| [200, {}, { :id => 1, :name => "Tobias Funke" }.to_json] }
|
12
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Funke" }.to_json] }
|
13
|
+
stub.put("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Funke" }.to_json] }
|
14
|
+
stub.delete("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Funke" }.to_json] }
|
15
|
+
stub.get("/projects/1/comments") { |env| [200, {}, [{ :id => 1, :body => "Hello!" }].to_json] }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
spawn_model "Foo::User"
|
20
|
+
spawn_model "Foo::Comment" do
|
21
|
+
collection_path "projects/:project_id/comments"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#inspect" do
|
26
|
+
it "outputs resource attributes for an existing resource" do
|
27
|
+
@user = Foo::User.find(1)
|
28
|
+
expect(["#<Foo::User(users/1) name=\"Tobias Funke\" id=1>", "#<Foo::User(users/1) id=1 name=\"Tobias Funke\">"]).to include(@user.inspect)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "outputs resource attributes for an not-saved-yet resource" do
|
32
|
+
@user = Foo::User.new(:name => "Tobias Funke")
|
33
|
+
expect(@user.inspect).to eq("#<Foo::User(users) name=\"Tobias Funke\">")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "outputs resource attributes using getters" do
|
37
|
+
@user = Foo::User.new(:name => "Tobias Funke", :password => "Funke")
|
38
|
+
@user.instance_eval {def password; 'filtered'; end}
|
39
|
+
expect(@user.inspect).to include("name=\"Tobias Funke\"")
|
40
|
+
expect(@user.inspect).to include("password=\"filtered\"")
|
41
|
+
expect(@user.inspect).not_to include("password=\"Funke\"")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "support dash on attribute" do
|
45
|
+
@user = Foo::User.new(:'life-span' => "3 years")
|
46
|
+
expect(@user.inspect).to include("life-span=\"3 years\"")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#inspect with errors in resource path" do
|
51
|
+
it "prints the resource path as “unknown”" do
|
52
|
+
@comment = Foo::Comment.where(:project_id => 1).first
|
53
|
+
path = '<unknown path, missing `project_id`>'
|
54
|
+
expect(["#<Foo::Comment(#{path}) body=\"Hello!\" id=1>", "#<Foo::Comment(#{path}) id=1 body=\"Hello!\">"]).to include(@comment.inspect)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#her_nearby_class" do
|
60
|
+
context "for a class inside of a module" do
|
61
|
+
before do
|
62
|
+
spawn_model "Foo::User"
|
63
|
+
spawn_model "Foo::AccessRecord"
|
64
|
+
spawn_model "AccessRecord"
|
65
|
+
spawn_model "Log"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns a sibling class, if found" do
|
69
|
+
expect(Foo::User.her_nearby_class("AccessRecord")).to eq(Foo::AccessRecord)
|
70
|
+
expect(AccessRecord.her_nearby_class("Log")).to eq(Log)
|
71
|
+
expect(Foo::User.her_nearby_class("Log")).to eq(Log)
|
72
|
+
expect(Foo::User.her_nearby_class("X")).to eq(nil)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::NestedAttributes do
|
5
|
+
context "with a belongs_to association" do
|
6
|
+
before do
|
7
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
8
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
9
|
+
builder.use Faraday::Request::UrlEncoded
|
10
|
+
end
|
11
|
+
|
12
|
+
spawn_model "Foo::User" do
|
13
|
+
belongs_to :company, :path => "/organizations/:id", :foreign_key => :organization_id
|
14
|
+
accepts_nested_attributes_for :company
|
15
|
+
end
|
16
|
+
|
17
|
+
spawn_model "Foo::Company"
|
18
|
+
|
19
|
+
@user_with_data_through_nested_attributes = Foo::User.new :name => "Test", :company_attributes => { :name => "Example Company" }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when child does not yet exist" do
|
23
|
+
it "creates an instance of the associated class" do
|
24
|
+
expect(@user_with_data_through_nested_attributes.company).to be_a(Foo::Company)
|
25
|
+
expect(@user_with_data_through_nested_attributes.company.name).to eq("Example Company")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when child does exist" do
|
30
|
+
it "updates the attributes of the associated object" do
|
31
|
+
@user_with_data_through_nested_attributes.company_attributes = { :name => "Fünke's Company" }
|
32
|
+
expect(@user_with_data_through_nested_attributes.company).to be_a(Foo::Company)
|
33
|
+
expect(@user_with_data_through_nested_attributes.company.name).to eq("Fünke's Company")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with a has_one association" do
|
39
|
+
before do
|
40
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
41
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
42
|
+
builder.use Faraday::Request::UrlEncoded
|
43
|
+
end
|
44
|
+
|
45
|
+
spawn_model "Foo::User" do
|
46
|
+
has_one :pet
|
47
|
+
accepts_nested_attributes_for :pet
|
48
|
+
end
|
49
|
+
|
50
|
+
spawn_model "Foo::Pet"
|
51
|
+
|
52
|
+
@user_with_data_through_nested_attributes = Foo::User.new :name => "Test", :pet_attributes => { :name => "Hasi" }
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when child does not yet exist" do
|
56
|
+
it "creates an instance of the associated class" do
|
57
|
+
expect(@user_with_data_through_nested_attributes.pet).to be_a(Foo::Pet)
|
58
|
+
expect(@user_with_data_through_nested_attributes.pet.name).to eq("Hasi")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when child does exist" do
|
63
|
+
it "updates the attributes of the associated object" do
|
64
|
+
@user_with_data_through_nested_attributes.pet_attributes = { :name => "Rodriguez" }
|
65
|
+
expect(@user_with_data_through_nested_attributes.pet).to be_a(Foo::Pet)
|
66
|
+
expect(@user_with_data_through_nested_attributes.pet.name).to eq("Rodriguez")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with a has_many association" do
|
72
|
+
before do
|
73
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
74
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
75
|
+
builder.use Faraday::Request::UrlEncoded
|
76
|
+
end
|
77
|
+
|
78
|
+
spawn_model "Foo::User" do
|
79
|
+
has_many :pets
|
80
|
+
accepts_nested_attributes_for :pets
|
81
|
+
end
|
82
|
+
|
83
|
+
spawn_model "Foo::Pet"
|
84
|
+
|
85
|
+
@user_with_data_through_nested_attributes = Foo::User.new :name => "Test", :pets_attributes => [{ :name => "Hasi" }, { :name => "Rodriguez" }]
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when children do not yet exist" do
|
89
|
+
it "creates an instance of the associated class" do
|
90
|
+
expect(@user_with_data_through_nested_attributes.pets.length).to eq(2)
|
91
|
+
expect(@user_with_data_through_nested_attributes.pets[0]).to be_a(Foo::Pet)
|
92
|
+
expect(@user_with_data_through_nested_attributes.pets[1]).to be_a(Foo::Pet)
|
93
|
+
expect(@user_with_data_through_nested_attributes.pets[0].name).to eq("Hasi")
|
94
|
+
expect(@user_with_data_through_nested_attributes.pets[1].name).to eq("Rodriguez")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "with a has_many association as a Hash" do
|
100
|
+
before do
|
101
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
102
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
103
|
+
builder.use Faraday::Request::UrlEncoded
|
104
|
+
end
|
105
|
+
|
106
|
+
spawn_model "Foo::User" do
|
107
|
+
has_many :pets
|
108
|
+
accepts_nested_attributes_for :pets
|
109
|
+
end
|
110
|
+
|
111
|
+
spawn_model "Foo::Pet"
|
112
|
+
|
113
|
+
@user_with_data_through_nested_attributes_as_hash = Foo::User.new :name => "Test", :pets_attributes => { '0' => { :name => "Hasi" }, '1' => { :name => "Rodriguez" }}
|
114
|
+
end
|
115
|
+
|
116
|
+
context "when children do not yet exist" do
|
117
|
+
it "creates an instance of the associated class" do
|
118
|
+
expect(@user_with_data_through_nested_attributes_as_hash.pets.length).to eq(2)
|
119
|
+
expect(@user_with_data_through_nested_attributes_as_hash.pets[0]).to be_a(Foo::Pet)
|
120
|
+
expect(@user_with_data_through_nested_attributes_as_hash.pets[1]).to be_a(Foo::Pet)
|
121
|
+
expect(@user_with_data_through_nested_attributes_as_hash.pets[0].name).to eq("Hasi")
|
122
|
+
expect(@user_with_data_through_nested_attributes_as_hash.pets[1].name).to eq("Rodriguez")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "with an unknown association" do
|
128
|
+
it "raises an error" do
|
129
|
+
expect {
|
130
|
+
spawn_model("Foo::User") { accepts_nested_attributes_for :company }
|
131
|
+
}.to raise_error(Her::Errors::AssociationUnknownError, 'Unknown association name :company')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|