extended_her 0.5
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.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CONTRIBUTING.md +26 -0
- data/Gemfile +2 -0
- data/LICENSE +7 -0
- data/README.md +723 -0
- data/Rakefile +11 -0
- data/UPGRADE.md +32 -0
- data/examples/twitter-oauth/Gemfile +13 -0
- data/examples/twitter-oauth/app.rb +50 -0
- data/examples/twitter-oauth/config.ru +5 -0
- data/examples/twitter-oauth/views/index.haml +9 -0
- data/examples/twitter-search/Gemfile +12 -0
- data/examples/twitter-search/app.rb +55 -0
- data/examples/twitter-search/config.ru +5 -0
- data/examples/twitter-search/views/index.haml +9 -0
- data/extended_her.gemspec +27 -0
- data/lib/her.rb +23 -0
- data/lib/her/api.rb +108 -0
- data/lib/her/base.rb +17 -0
- data/lib/her/collection.rb +12 -0
- data/lib/her/errors.rb +5 -0
- data/lib/her/exceptions/exception.rb +4 -0
- data/lib/her/exceptions/record_invalid.rb +8 -0
- data/lib/her/exceptions/record_not_found.rb +13 -0
- data/lib/her/middleware.rb +9 -0
- data/lib/her/middleware/accept_json.rb +15 -0
- data/lib/her/middleware/first_level_parse_json.rb +34 -0
- data/lib/her/middleware/second_level_parse_json.rb +28 -0
- data/lib/her/model.rb +69 -0
- data/lib/her/model/base.rb +7 -0
- data/lib/her/model/hooks.rb +114 -0
- data/lib/her/model/http.rb +284 -0
- data/lib/her/model/introspection.rb +57 -0
- data/lib/her/model/orm.rb +191 -0
- data/lib/her/model/orm/comparison_methods.rb +20 -0
- data/lib/her/model/orm/create_methods.rb +29 -0
- data/lib/her/model/orm/destroy_methods.rb +53 -0
- data/lib/her/model/orm/error_methods.rb +19 -0
- data/lib/her/model/orm/fields_definition.rb +15 -0
- data/lib/her/model/orm/find_methods.rb +46 -0
- data/lib/her/model/orm/persistance_methods.rb +22 -0
- data/lib/her/model/orm/relation_mapper.rb +21 -0
- data/lib/her/model/orm/save_methods.rb +58 -0
- data/lib/her/model/orm/serialization_methods.rb +28 -0
- data/lib/her/model/orm/update_methods.rb +31 -0
- data/lib/her/model/paths.rb +82 -0
- data/lib/her/model/relationships.rb +191 -0
- data/lib/her/paginated_collection.rb +20 -0
- data/lib/her/relation.rb +94 -0
- data/lib/her/version.rb +3 -0
- data/spec/api_spec.rb +131 -0
- data/spec/collection_spec.rb +26 -0
- data/spec/middleware/accept_json_spec.rb +10 -0
- data/spec/middleware/first_level_parse_json_spec.rb +42 -0
- data/spec/middleware/second_level_parse_json_spec.rb +25 -0
- data/spec/model/hooks_spec.rb +406 -0
- data/spec/model/http_spec.rb +184 -0
- data/spec/model/introspection_spec.rb +59 -0
- data/spec/model/orm_spec.rb +552 -0
- data/spec/model/paths_spec.rb +286 -0
- data/spec/model/relationships_spec.rb +222 -0
- data/spec/model_spec.rb +31 -0
- data/spec/spec_helper.rb +46 -0
- metadata +222 -0
@@ -0,0 +1,184 @@
|
|
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 an instance of Her::API" do
|
16
|
+
before { Foo::User.uses_api api1 }
|
17
|
+
subject { Foo::User.her_api }
|
18
|
+
its(:base_uri) { should == "https://api1.example.com" }
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when binding a model directly to Her::API" do
|
22
|
+
before { spawn_model "Foo::User" }
|
23
|
+
subject { Foo::User.her_api }
|
24
|
+
its(:base_uri) { should == "https://api.example.com" }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when binding two models to two different instances of Her::API" do
|
28
|
+
before do
|
29
|
+
Foo::User.uses_api api1
|
30
|
+
Foo::Comment.uses_api api2
|
31
|
+
end
|
32
|
+
|
33
|
+
specify { Foo::User.her_api.base_uri.should == "https://api1.example.com" }
|
34
|
+
specify { Foo::Comment.her_api.base_uri.should == "https://api2.example.com" }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "binding one model to Her::API and another one to an instance of Her::API" do
|
38
|
+
before { Foo::Comment.uses_api api2 }
|
39
|
+
specify { Foo::User.her_api.base_uri.should == "https://api.example.com" }
|
40
|
+
specify { Foo::Comment.her_api.base_uri.should == "https://api2.example.com" }
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when binding a model to its superclass' her_api" do
|
44
|
+
before do
|
45
|
+
spawn_model "Foo::Superclass"
|
46
|
+
Foo::Superclass.uses_api api1
|
47
|
+
Foo::Subclass = Class.new(Foo::Superclass)
|
48
|
+
end
|
49
|
+
|
50
|
+
specify { Foo::Subclass.her_api.should == Foo::Superclass.her_api }
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when changing her_api without changing the parent class' her_api" do
|
54
|
+
before do
|
55
|
+
spawn_model "Foo::Superclass"
|
56
|
+
Foo::Subclass = Class.new(Foo::Superclass)
|
57
|
+
Foo::Superclass.uses_api api1
|
58
|
+
Foo::Subclass.uses_api api2
|
59
|
+
end
|
60
|
+
|
61
|
+
specify { Foo::Subclass.her_api.should_not == Foo::Superclass.her_api }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "making HTTP requests" do
|
66
|
+
before do
|
67
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
68
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
69
|
+
builder.use Faraday::Request::UrlEncoded
|
70
|
+
builder.adapter :test do |stub|
|
71
|
+
stub.get("/users") { |env| [200, {}, [{ :id => 1 }].to_json] }
|
72
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1 }.to_json] }
|
73
|
+
stub.get("/users/popular") do |env|
|
74
|
+
if env[:params]["page"] == "2"
|
75
|
+
[200, {}, [{ :id => 3 }, { :id => 4 }].to_json]
|
76
|
+
else
|
77
|
+
[200, {}, [{ :id => 1 }, { :id => 2 }].to_json]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
spawn_model "Foo::User"
|
84
|
+
end
|
85
|
+
|
86
|
+
describe :get do
|
87
|
+
subject { Foo::User.get(:popular) }
|
88
|
+
its(:length) { should == 2 }
|
89
|
+
specify { subject.first.id.should == 1 }
|
90
|
+
end
|
91
|
+
|
92
|
+
describe :get_raw do
|
93
|
+
context "with a block" do
|
94
|
+
specify do
|
95
|
+
Foo::User.get_raw("/users") do |parsed_data|
|
96
|
+
parsed_data[:data].should == [{ :id => 1 }]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "with a return value" do
|
102
|
+
subject { Foo::User.get_raw("/users") }
|
103
|
+
specify { subject[:data].should == [{ :id => 1 }] }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe :get_collection do
|
108
|
+
context "with a String path" do
|
109
|
+
subject { Foo::User.get_collection("/users/popular") }
|
110
|
+
its(:length) { should == 2 }
|
111
|
+
specify { subject.first.id.should == 1 }
|
112
|
+
end
|
113
|
+
|
114
|
+
context "with a Symbol" do
|
115
|
+
subject { Foo::User.get_collection(:popular) }
|
116
|
+
its(:length) { should == 2 }
|
117
|
+
specify { subject.first.id.should == 1 }
|
118
|
+
end
|
119
|
+
|
120
|
+
context "with extra parameters" do
|
121
|
+
subject { Foo::User.get_collection(:popular, :page => 2) }
|
122
|
+
its(:length) { should == 2 }
|
123
|
+
specify { subject.first.id.should == 3 }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe :get_resource do
|
128
|
+
context "with a String path" do
|
129
|
+
subject { Foo::User.get_resource("/users/1") }
|
130
|
+
its(:id) { should == 1 }
|
131
|
+
end
|
132
|
+
|
133
|
+
context "with a Symbol" do
|
134
|
+
subject { Foo::User.get_resource(:"1") }
|
135
|
+
its(:id) { should == 1 }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe :get_raw do
|
140
|
+
specify do
|
141
|
+
Foo::User.get_raw(:popular) do |parsed_data|
|
142
|
+
parsed_data[:data].should == [{ :id => 1 }, { :id => 2 }]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "setting custom HTTP requests" do
|
149
|
+
before do
|
150
|
+
Her::API.setup :url => "https://api.example.com" do |connection|
|
151
|
+
connection.use Her::Middleware::FirstLevelParseJSON
|
152
|
+
connection.adapter :test do |stub|
|
153
|
+
stub.get("/users/popular") { |env| [200, {}, [{ :id => 1 }, { :id => 2 }].to_json] }
|
154
|
+
stub.post("/users/from_default") { |env| [200, {}, { :id => 4 }.to_json] }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
spawn_model "Foo::User"
|
159
|
+
end
|
160
|
+
|
161
|
+
subject { Foo::User }
|
162
|
+
|
163
|
+
describe :custom_get do
|
164
|
+
before { Foo::User.custom_get :popular, :recent }
|
165
|
+
it { should respond_to(:popular) }
|
166
|
+
it { should respond_to(:recent) }
|
167
|
+
|
168
|
+
context "making the HTTP request" do
|
169
|
+
subject { Foo::User.popular }
|
170
|
+
its(:length) { should == 2 }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe :custom_post do
|
175
|
+
before { Foo::User.custom_post :from_default }
|
176
|
+
it { should respond_to(:from_default) }
|
177
|
+
|
178
|
+
context "making the HTTP request" do
|
179
|
+
subject { Foo::User.from_default(:name => "Tobias Fünke") }
|
180
|
+
its(:id) { should == 4 }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,59 @@
|
|
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
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
spawn_model "Foo::User"
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#inspect" do
|
22
|
+
it "outputs resource attributes for an existing resource" do
|
23
|
+
@user = Foo::User.find(1)
|
24
|
+
["#<Foo::User name: \"Tobias Funke\", id: 1>", "#<Foo::User id: 1, name: \"Tobias Funke\">"].should include(@user.inspect)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "outputs resource attributes for an not-saved-yet resource" do
|
28
|
+
@user = Foo::User.new(:name => "Tobias Funke")
|
29
|
+
@user.inspect.should == "#<Foo::User name: \"Tobias Funke\">"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "outputs resource attributes using getters" do
|
33
|
+
@user = Foo::User.new(:name => "Tobias Funke", :password => "Funke")
|
34
|
+
@user.instance_eval {def password; 'filtered'; end}
|
35
|
+
@user.inspect.should include("name: \"Tobias Funke\"")
|
36
|
+
@user.inspect.should include("password: \"filtered\"")
|
37
|
+
@user.inspect.should_not include("password: \"Funke\"")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#nearby_class" do
|
43
|
+
context "for a class inside of a module" do
|
44
|
+
before do
|
45
|
+
spawn_model "Foo::User"
|
46
|
+
spawn_model "Foo::AccessRecord"
|
47
|
+
spawn_model "AccessRecord"
|
48
|
+
spawn_model "Log"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "returns a sibling class, if found" do
|
52
|
+
Foo::User.nearby_class("AccessRecord").should == Foo::AccessRecord
|
53
|
+
AccessRecord.nearby_class("Log").should == Log
|
54
|
+
Foo::User.nearby_class("Log").should == Log
|
55
|
+
Foo::User.nearby_class("X").should be_nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,552 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::ORM do
|
5
|
+
context "mapping data to Ruby objects" do
|
6
|
+
before do
|
7
|
+
api = Her::API.new
|
8
|
+
api.setup :url => "https://api.example.com" do |builder|
|
9
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
10
|
+
builder.use Faraday::Request::UrlEncoded
|
11
|
+
builder.adapter :test do |stub|
|
12
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke" }.to_json] }
|
13
|
+
stub.get("/users") { |env| [200, {}, [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }].to_json] }
|
14
|
+
stub.get("/admin_users") { |env| [200, {}, [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }].to_json] }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
spawn_model "Foo::User" do
|
19
|
+
uses_api api
|
20
|
+
end
|
21
|
+
|
22
|
+
spawn_model "Foo::AdminUser" do
|
23
|
+
uses_api api
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "maps a single resource to a Ruby object" do
|
28
|
+
@user = Foo::User.find(1)
|
29
|
+
@user.id.should == 1
|
30
|
+
@user.name.should == "Tobias Fünke"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "maps a collection of resources to an array of Ruby objects" do
|
34
|
+
@users = Foo::User.all
|
35
|
+
@users.length.should == 2
|
36
|
+
@users.first.name.should == "Tobias Fünke"
|
37
|
+
|
38
|
+
@users = Foo::AdminUser.all
|
39
|
+
@users.length.should == 2
|
40
|
+
@users.first.name.should == "Tobias Fünke"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "handles new resource" do
|
44
|
+
@new_user = Foo::User.new(:fullname => "Tobias Fünke")
|
45
|
+
@new_user.new?.should be_true
|
46
|
+
@new_user.fullname.should == "Tobias Fünke"
|
47
|
+
|
48
|
+
@existing_user = Foo::User.find(1)
|
49
|
+
@existing_user.new?.should be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
it "accepts new resource with strings as hash keys" do
|
53
|
+
@new_user = Foo::User.new('fullname' => "Tobias Fünke")
|
54
|
+
@new_user.fullname.should == "Tobias Fünke"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "handles method missing for getter" do
|
58
|
+
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
59
|
+
lambda { @new_user.unknown_method_for_a_user }.should raise_error(NoMethodError)
|
60
|
+
expect { @new_user.fullname }.to_not raise_error(NoMethodError)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "handles method missing for setter" do
|
64
|
+
@new_user = Foo::User.new
|
65
|
+
expect { @new_user.fullname = "Tobias Fünke" }.to_not raise_error(NoMethodError)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "handles method missing for query" do
|
69
|
+
@new_user = Foo::User.new
|
70
|
+
expect { @new_user.fullname? }.to_not raise_error(NoMethodError)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "handles respond_to for getter" do
|
74
|
+
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
75
|
+
@new_user.should_not respond_to(:unknown_method_for_a_user)
|
76
|
+
@new_user.should respond_to(:fullname)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "handles respond_to for setter" do
|
80
|
+
@new_user = Foo::User.new
|
81
|
+
@new_user.should respond_to(:fullname=)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "handles respond_to for query" do
|
85
|
+
@new_user = Foo::User.new
|
86
|
+
@new_user.should respond_to(:fullname?)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "handles has_data? for getter" do
|
90
|
+
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
91
|
+
@new_user.should_not have_data(:unknown_method_for_a_user)
|
92
|
+
@new_user.should have_data(:fullname)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "handles get_data for getter" do
|
96
|
+
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
97
|
+
@new_user.get_data(:unknown_method_for_a_user).should be_nil
|
98
|
+
@new_user.get_data(:fullname).should == 'Mayonegg'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "mapping data, metadata and error data to Ruby objects" do
|
103
|
+
before do
|
104
|
+
api = Her::API.new
|
105
|
+
api.setup :url => "https://api.example.com" do |builder|
|
106
|
+
builder.use Her::Middleware::SecondLevelParseJSON
|
107
|
+
builder.use Faraday::Request::UrlEncoded
|
108
|
+
builder.adapter :test do |stub|
|
109
|
+
stub.get("/users") { |env| [200, {}, { :data => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }], :metadata => { :total_pages => 10, :next_page => 2 }, :errors => ["Oh", "My", "God"] }.to_json] }
|
110
|
+
stub.post("/users") { |env| [200, {}, { :data => { :name => "George Michael Bluth" }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
spawn_model :User do
|
115
|
+
uses_api api
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it "handles metadata on a collection" do
|
120
|
+
@users = User.all
|
121
|
+
@users.metadata[:total_pages].should == 10
|
122
|
+
end
|
123
|
+
|
124
|
+
it "handles error data on a collection" do
|
125
|
+
@users = User.all
|
126
|
+
@users.errors.length.should == 3
|
127
|
+
end
|
128
|
+
|
129
|
+
it "handles metadata on a resource" do
|
130
|
+
@user = User.create(:name => "George Michael Bluth")
|
131
|
+
@user.metadata[:foo].should == "bar"
|
132
|
+
end
|
133
|
+
|
134
|
+
it "handles error data on a resource" do
|
135
|
+
@user = User.create(:name => "George Michael Bluth")
|
136
|
+
@user.errors.should == ["Yes", "Sir"]
|
137
|
+
@user.should be_invalid
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "defining custom getters and setters" do
|
142
|
+
before do
|
143
|
+
api = Her::API.new
|
144
|
+
api.setup :url => "https://api.example.com" do |builder|
|
145
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
146
|
+
builder.use Faraday::Request::UrlEncoded
|
147
|
+
builder.adapter :test do |stub|
|
148
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :friends => ["Maeby", "GOB", "Anne"] }.to_json] }
|
149
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 1, :organization => true }.to_json] }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
spawn_model :User do
|
154
|
+
uses_api api
|
155
|
+
belongs_to :organization
|
156
|
+
|
157
|
+
def friends=(val)
|
158
|
+
val = val.gsub("\r", "").split("\n").map { |friend| friend.gsub(/^\s*\*\s*/, "") } if val and val.is_a?(String)
|
159
|
+
@data[:friends] = val
|
160
|
+
end
|
161
|
+
|
162
|
+
def friends
|
163
|
+
@data[:friends].map { |friend| "* #{friend}" }.join("\n")
|
164
|
+
end
|
165
|
+
|
166
|
+
# Why would anybody want to do this? I don’t know.
|
167
|
+
def organization=(organization)
|
168
|
+
@data[:organization] = { :foo => :bar }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "handles custom setters" do
|
174
|
+
@user = User.find(1)
|
175
|
+
@user.friends.should == "* Maeby\n* GOB\n* Anne"
|
176
|
+
@user.instance_eval do
|
177
|
+
@data[:friends] = ["Maeby", "GOB", "Anne"]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
it "handles custom setters with relationships" do
|
182
|
+
@user = User.find(2)
|
183
|
+
@user.organization.should == { :foo => :bar }
|
184
|
+
end
|
185
|
+
|
186
|
+
it "handles custom getters" do
|
187
|
+
@user = User.new
|
188
|
+
@user.friends = "* George\n* Oscar\n* Lucille"
|
189
|
+
@user.friends.should == "* George\n* Oscar\n* Lucille"
|
190
|
+
@user.instance_eval do
|
191
|
+
@data[:friends] = ["George", "Oscar", "Lucille"]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context "finding resources" do
|
197
|
+
before do
|
198
|
+
api = Her::API.new
|
199
|
+
api.setup :url => "https://api.example.com" do |builder|
|
200
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
201
|
+
builder.use Faraday::Request::UrlEncoded
|
202
|
+
builder.adapter :test do |stub|
|
203
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :age => 42 }.to_json] }
|
204
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 2, :age => 34 }.to_json] }
|
205
|
+
stub.get("/users?age=42") { |env| [200, {}, [{ :id => 1, :age => 42 }].to_json] }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
spawn_model :User do
|
210
|
+
uses_api api
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it "handles finding by a single id" do
|
215
|
+
@user = User.find(1)
|
216
|
+
@user.id.should == 1
|
217
|
+
end
|
218
|
+
|
219
|
+
it "handles finding by multiple ids" do
|
220
|
+
@users = User.find(1, 2)
|
221
|
+
@users.should be_kind_of(Array)
|
222
|
+
@users.length.should == 2
|
223
|
+
@users[0].id.should == 1
|
224
|
+
@users[1].id.should == 2
|
225
|
+
end
|
226
|
+
|
227
|
+
it "handles finding by an array of ids" do
|
228
|
+
@users = User.find([1, 2])
|
229
|
+
@users.should be_kind_of(Array)
|
230
|
+
@users.length.should == 2
|
231
|
+
@users[0].id.should == 1
|
232
|
+
@users[1].id.should == 2
|
233
|
+
end
|
234
|
+
|
235
|
+
it "handles finding by an array of ids of length 1" do
|
236
|
+
@users = User.find([1])
|
237
|
+
@users.should be_kind_of(Array)
|
238
|
+
@users.length.should == 1
|
239
|
+
@users[0].id.should == 1
|
240
|
+
end
|
241
|
+
|
242
|
+
it "handles finding with other parameters" do
|
243
|
+
@users = User.all(:age => 42)
|
244
|
+
@users.should be_kind_of(Array)
|
245
|
+
@users.should be_all { |u| u.age == 42 }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context "creating resources" do
|
250
|
+
before do
|
251
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
252
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
253
|
+
builder.use Faraday::Request::UrlEncoded
|
254
|
+
builder.adapter :test do |stub|
|
255
|
+
stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
|
256
|
+
stub.post("/companies") { |env| [200, {}, { :errors => ["name is required"] }.to_json] }
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
spawn_model "Foo::User"
|
261
|
+
spawn_model "Foo::Company"
|
262
|
+
end
|
263
|
+
|
264
|
+
it "handle one-line resource creation" do
|
265
|
+
@user = Foo::User.create(:fullname => "Tobias Fünke")
|
266
|
+
@user.id.should == 1
|
267
|
+
@user.fullname.should == "Tobias Fünke"
|
268
|
+
end
|
269
|
+
|
270
|
+
it "handle resource creation through Model.new + #save" do
|
271
|
+
@user = Foo::User.new(:fullname => "Tobias Fünke")
|
272
|
+
@user.save.should be_true
|
273
|
+
@user.fullname.should == "Tobias Fünke"
|
274
|
+
end
|
275
|
+
|
276
|
+
it "returns false when #save gets errors" do
|
277
|
+
@company = Foo::Company.new
|
278
|
+
@company.save.should be_false
|
279
|
+
end
|
280
|
+
|
281
|
+
it "don't overwrite data if response is empty" do
|
282
|
+
@company = Foo::Company.new(:name => 'Company Inc.')
|
283
|
+
@company.save.should be_false
|
284
|
+
@company.name.should == "Company Inc."
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context "updating resources" do
|
289
|
+
before do
|
290
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
291
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
292
|
+
builder.use Faraday::Request::UrlEncoded
|
293
|
+
builder.adapter :test do |stub|
|
294
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
|
295
|
+
stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
spawn_model "Foo::User"
|
300
|
+
end
|
301
|
+
|
302
|
+
it "handle resource data update without saving it" do
|
303
|
+
@user = Foo::User.find(1)
|
304
|
+
@user.fullname.should == "Tobias Fünke"
|
305
|
+
@user.fullname = "Kittie Sanchez"
|
306
|
+
@user.fullname.should == "Kittie Sanchez"
|
307
|
+
end
|
308
|
+
|
309
|
+
it "handle resource update through the .update class method" do
|
310
|
+
@user = Foo::User.save_existing(1, { :fullname => "Lindsay Fünke" })
|
311
|
+
@user.fullname.should == "Lindsay Fünke"
|
312
|
+
end
|
313
|
+
|
314
|
+
it "handle resource update through #save on an existing resource" do
|
315
|
+
@user = Foo::User.find(1)
|
316
|
+
@user.fullname = "Lindsay Fünke"
|
317
|
+
@user.save
|
318
|
+
@user.fullname.should == "Lindsay Fünke"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context "assigning new resource data" do
|
323
|
+
before do
|
324
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
325
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
326
|
+
builder.use Faraday::Request::UrlEncoded
|
327
|
+
builder.adapter :test do |stub|
|
328
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
spawn_model "Foo::User"
|
333
|
+
@user = Foo::User.find(1)
|
334
|
+
end
|
335
|
+
|
336
|
+
it "handles data update through #assign_attributes" do
|
337
|
+
@user.assign_attributes :active => true
|
338
|
+
@user.should be_active
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
context "deleting resources" do
|
343
|
+
before do
|
344
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
345
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
346
|
+
builder.use Faraday::Request::UrlEncoded
|
347
|
+
builder.adapter :test do |stub|
|
348
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
|
349
|
+
stub.delete("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :active => false }.to_json] }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
spawn_model "Foo::User"
|
354
|
+
end
|
355
|
+
|
356
|
+
it "handle resource deletion through the .destroy class method" do
|
357
|
+
@user = Foo::User.destroy_existing(1)
|
358
|
+
@user.active.should be_false
|
359
|
+
end
|
360
|
+
|
361
|
+
it "handle resource deletion through #destroy on an existing resource" do
|
362
|
+
@user = Foo::User.find(1)
|
363
|
+
@user.destroy
|
364
|
+
@user.active.should be_false
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context "saving resources with overridden to_params" do
|
369
|
+
before do
|
370
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
371
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
372
|
+
builder.use Faraday::Request::UrlEncoded
|
373
|
+
builder.adapter :test do |stub|
|
374
|
+
stub.post("/users") do |env|
|
375
|
+
body = {
|
376
|
+
:id => 1,
|
377
|
+
:fullname => Faraday::Utils.parse_query(env[:body])['fullname']
|
378
|
+
}.to_json
|
379
|
+
[200, {}, body]
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
spawn_model "Foo::User" do
|
385
|
+
def to_params
|
386
|
+
{ :fullname => "Lindsay Fünke" }
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
it "changes the request parameters for one-line resource creation" do
|
392
|
+
@user = Foo::User.create(:fullname => "Tobias Fünke")
|
393
|
+
@user.fullname.should == "Lindsay Fünke"
|
394
|
+
end
|
395
|
+
|
396
|
+
it "changes the request parameters for Model.new + #save" do
|
397
|
+
@user = Foo::User.new(:fullname => "Tobias Fünke")
|
398
|
+
@user.save
|
399
|
+
@user.fullname.should == "Lindsay Fünke"
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
context "checking resource equality" do
|
404
|
+
before do
|
405
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
406
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
407
|
+
builder.use Faraday::Request::UrlEncoded
|
408
|
+
builder.adapter :test do |stub|
|
409
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
|
410
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
|
411
|
+
stub.get("/admins/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
spawn_model "Foo::User"
|
416
|
+
spawn_model "Foo::Admin"
|
417
|
+
end
|
418
|
+
|
419
|
+
let(:user) { Foo::User.find(1) }
|
420
|
+
|
421
|
+
it "returns true for the exact same object" do
|
422
|
+
user.should == user
|
423
|
+
end
|
424
|
+
|
425
|
+
it "returns true for the same resource via find" do
|
426
|
+
user.should == Foo::User.find(1)
|
427
|
+
end
|
428
|
+
|
429
|
+
it "returns true for the same class with identical data" do
|
430
|
+
user.should == Foo::User.new(:id => 1, :fullname => "Lindsay Fünke")
|
431
|
+
end
|
432
|
+
|
433
|
+
it "returns true for a different resource with the same data" do
|
434
|
+
user.should == Foo::Admin.find(1)
|
435
|
+
end
|
436
|
+
|
437
|
+
it "returns false for the same class with different data" do
|
438
|
+
user.should_not == Foo::User.new(:id => 2, :fullname => "Tobias Fünke")
|
439
|
+
end
|
440
|
+
|
441
|
+
it "returns false for a non-resource with the same data" do
|
442
|
+
fake_user = stub(:data => { :id => 1, :fullname => "Lindsay Fünke" })
|
443
|
+
user.should_not == fake_user
|
444
|
+
end
|
445
|
+
|
446
|
+
it "delegates eql? to ==" do
|
447
|
+
other = Object.new
|
448
|
+
user.expects(:==).with(other).returns(true)
|
449
|
+
user.eql?(other).should be_true
|
450
|
+
end
|
451
|
+
|
452
|
+
it "treats equal resources as equal for Array#uniq" do
|
453
|
+
user2 = Foo::User.find(1)
|
454
|
+
[user, user2].uniq.should == [user]
|
455
|
+
end
|
456
|
+
|
457
|
+
it "treats equal resources as equal for hash keys" do
|
458
|
+
Foo::User.find(1)
|
459
|
+
hash = { user => true }
|
460
|
+
hash[Foo::User.find(1)] = false
|
461
|
+
hash.size.should == 1
|
462
|
+
hash.should == { user => false }
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
context "when include_root_in_json is true" do
|
467
|
+
context "when include_root_in_json is true" do
|
468
|
+
before do
|
469
|
+
spawn_model "Foo::User" do
|
470
|
+
include_root_in_json true
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
it "wraps params in the element name" do
|
475
|
+
@new_user = Foo::User.new(:fullname => "Tobias Fünke")
|
476
|
+
@new_user.to_params.should == { 'user' => { :fullname => "Tobias Fünke" } }
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
context "when include_root_in_json is set to another value" do
|
481
|
+
before do
|
482
|
+
spawn_model "Foo::User" do
|
483
|
+
include_root_in_json :person
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
it "wraps params in the specified value" do
|
488
|
+
@new_user = Foo::User.new(:fullname => "Tobias Fünke")
|
489
|
+
@new_user.to_params.should == { :person => { :fullname => "Tobias Fünke" } }
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
context "when parse_root_in_json is set" do
|
495
|
+
before do
|
496
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
497
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
498
|
+
builder.use Faraday::Request::UrlEncoded
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
context "when parse_root_in_json is true" do
|
503
|
+
before do
|
504
|
+
Her::API.default_api.connection.adapter :test do |stub|
|
505
|
+
stub.post("/users") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
|
506
|
+
stub.get("/users") { |env| [200, {}, [{ :user => { :id => 1, :fullname => "Lindsay Fünke" } }].to_json] }
|
507
|
+
stub.get("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
|
508
|
+
stub.put("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Tobias Fünke Jr." } }.to_json] }
|
509
|
+
end
|
510
|
+
|
511
|
+
spawn_model("Foo::User") { parse_root_in_json true }
|
512
|
+
end
|
513
|
+
|
514
|
+
it "parse the data from the JSON root element after .create" do
|
515
|
+
@new_user = Foo::User.create(:fullname => "Lindsay Fünke")
|
516
|
+
@new_user.fullname.should == "Lindsay Fünke"
|
517
|
+
end
|
518
|
+
|
519
|
+
it "parse the data from the JSON root element after .all" do
|
520
|
+
@users = Foo::User.all
|
521
|
+
@users.first.fullname.should == "Lindsay Fünke"
|
522
|
+
end
|
523
|
+
|
524
|
+
it "parse the data from the JSON root element after .find" do
|
525
|
+
@user = Foo::User.find(1)
|
526
|
+
@user.fullname.should == "Lindsay Fünke"
|
527
|
+
end
|
528
|
+
|
529
|
+
it "parse the data from the JSON root element after .save" do
|
530
|
+
@user = Foo::User.find(1)
|
531
|
+
@user.fullname = "Tobias Fünke"
|
532
|
+
@user.save
|
533
|
+
@user.fullname.should == "Tobias Fünke Jr."
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
context "when parse_root_in_json is set to a symbol" do
|
538
|
+
before do
|
539
|
+
Her::API.default_api.connection.adapter :test do |stub|
|
540
|
+
stub.post("/users") { |env| [200, {}, { :person => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
|
541
|
+
end
|
542
|
+
|
543
|
+
spawn_model("Foo::User") { parse_root_in_json :person }
|
544
|
+
end
|
545
|
+
|
546
|
+
it "parse the data with the symbol" do
|
547
|
+
@new_user = Foo::User.create(:fullname => "Lindsay Fünke")
|
548
|
+
@new_user.fullname.should == "Lindsay Fünke"
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|