her 0.6.3 → 0.6.4
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/.yardopts +2 -0
- data/README.md +3 -1
- data/her.gemspec +1 -1
- data/lib/her/api.rb +17 -17
- data/lib/her/middleware/accept_json.rb +2 -0
- data/lib/her/middleware/first_level_parse_json.rb +2 -0
- data/lib/her/middleware/parse_json.rb +1 -0
- data/lib/her/middleware/second_level_parse_json.rb +2 -0
- data/lib/her/model.rb +1 -0
- data/lib/her/model/associations.rb +19 -17
- data/lib/her/model/associations/association.rb +54 -5
- data/lib/her/model/associations/belongs_to_association.rb +14 -25
- data/lib/her/model/associations/has_many_association.rb +9 -22
- data/lib/her/model/associations/has_one_association.rb +9 -33
- data/lib/her/model/attributes.rb +55 -58
- data/lib/her/model/http.rb +33 -24
- data/lib/her/model/introspection.rb +1 -0
- data/lib/her/model/orm.rb +2 -2
- data/lib/her/model/parse.rb +2 -1
- data/lib/her/model/paths.rb +7 -11
- data/lib/her/model/relation.rb +27 -24
- data/lib/her/version.rb +1 -1
- data/spec/model/associations_spec.rb +15 -15
- data/spec/model/attributes_spec.rb +81 -0
- data/spec/model/relation_spec.rb +19 -1
- metadata +4 -3
data/lib/her/model/relation.rb
CHANGED
@@ -1,29 +1,38 @@
|
|
1
1
|
module Her
|
2
2
|
module Model
|
3
3
|
class Relation
|
4
|
-
|
4
|
+
# @private
|
5
|
+
attr_accessor :params
|
5
6
|
|
6
7
|
# @private
|
7
8
|
def initialize(parent)
|
8
9
|
@parent = parent
|
9
|
-
@
|
10
|
+
@params = {}
|
10
11
|
end
|
11
12
|
|
12
13
|
# @private
|
13
14
|
def apply_to(attributes)
|
14
|
-
@
|
15
|
+
@params.merge(attributes)
|
15
16
|
end
|
16
17
|
|
17
18
|
# Build a new resource
|
18
|
-
def build(
|
19
|
-
@parent.new(@
|
19
|
+
def build(attributes = {})
|
20
|
+
@parent.new(@params.merge(attributes))
|
20
21
|
end
|
21
22
|
|
22
23
|
# Add a query string parameter
|
23
|
-
|
24
|
-
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# @users = User.all
|
27
|
+
# # Fetched via GET "/users"
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# @users = User.where(:approved => 1).all
|
31
|
+
# # Fetched via GET "/users?approved=1"
|
32
|
+
def where(params = {})
|
33
|
+
return self if params.blank? && @_fetch.blank?
|
25
34
|
self.clone.tap do |r|
|
26
|
-
r.
|
35
|
+
r.params = r.params.merge(params)
|
27
36
|
r.clear_fetch_cache!
|
28
37
|
end
|
29
38
|
end
|
@@ -53,18 +62,12 @@ module Her
|
|
53
62
|
|
54
63
|
# Fetch a collection of resources
|
55
64
|
#
|
56
|
-
# @
|
57
|
-
# @users = User.all
|
58
|
-
# # Fetched via GET "/users"
|
59
|
-
#
|
60
|
-
# @example
|
61
|
-
# @users = User.where(:approved => 1).all
|
62
|
-
# # Fetched via GET "/users?approved=1"
|
65
|
+
# @private
|
63
66
|
def fetch
|
64
67
|
@_fetch ||= begin
|
65
|
-
path = @parent.build_request_path(@
|
68
|
+
path = @parent.build_request_path(@params)
|
66
69
|
method = @parent.method_for(:find)
|
67
|
-
@parent.request(@
|
70
|
+
@parent.request(@params.merge(:_method => method, :_path => path)) do |parsed_data, response|
|
68
71
|
@parent.new_collection(parsed_data)
|
69
72
|
end
|
70
73
|
end
|
@@ -79,9 +82,9 @@ module Her
|
|
79
82
|
# @example
|
80
83
|
# @user = User.where(:email => "tobias@bluth.com").create(:fullname => "Tobias Fünke")
|
81
84
|
# # Called via POST "/users/1" with `&email=tobias@bluth.com&fullname=Tobias+Fünke`
|
82
|
-
def create(
|
83
|
-
|
84
|
-
resource = @parent.new(@
|
85
|
+
def create(attributes = {})
|
86
|
+
attributes ||= {}
|
87
|
+
resource = @parent.new(@params.merge(attributes))
|
85
88
|
resource.save
|
86
89
|
|
87
90
|
resource
|
@@ -97,8 +100,8 @@ module Her
|
|
97
100
|
#
|
98
101
|
# # If collection is empty:
|
99
102
|
# # POST /users with `email=remi@example.com`
|
100
|
-
def first_or_create(
|
101
|
-
fetch.first || create(
|
103
|
+
def first_or_create(attributes = {})
|
104
|
+
fetch.first || create(attributes)
|
102
105
|
end
|
103
106
|
|
104
107
|
# Fetch a resource and build it if it's not found
|
@@ -112,8 +115,8 @@ module Her
|
|
112
115
|
# # If collection is empty:
|
113
116
|
# @user.email # => "remi@example.com"
|
114
117
|
# @user.new? # => true
|
115
|
-
def first_or_initialize(
|
116
|
-
fetch.first || build(
|
118
|
+
def first_or_initialize(attributes = {})
|
119
|
+
fetch.first || build(attributes)
|
117
120
|
end
|
118
121
|
|
119
122
|
# @private
|
data/lib/her/version.rb
CHANGED
@@ -8,7 +8,7 @@ describe Her::Model::Associations do
|
|
8
8
|
|
9
9
|
context "single has_many association" do
|
10
10
|
before { Foo::User.has_many :comments }
|
11
|
-
its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }] }
|
11
|
+
its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :default => [], :class_name => "Comment", :path => "/comments", :inverse_of => nil }] }
|
12
12
|
end
|
13
13
|
|
14
14
|
context "multiple has_many associations" do
|
@@ -17,12 +17,12 @@ describe Her::Model::Associations do
|
|
17
17
|
Foo::User.has_many :posts
|
18
18
|
end
|
19
19
|
|
20
|
-
its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }, { :name => :posts, :data_key => :posts, :class_name => "Post", :path => "/posts", :inverse_of => nil }] }
|
20
|
+
its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :default => [], :class_name => "Comment", :path => "/comments", :inverse_of => nil }, { :name => :posts, :data_key => :posts, :default => [], :class_name => "Post", :path => "/posts", :inverse_of => nil }] }
|
21
21
|
end
|
22
22
|
|
23
23
|
context "single has_one association" do
|
24
24
|
before { Foo::User.has_one :category }
|
25
|
-
its([:has_one]) { should eql [{ :name => :category, :data_key => :category, :class_name => "Category", :path => "/category" }] }
|
25
|
+
its([:has_one]) { should eql [{ :name => :category, :data_key => :category, :default => nil, :class_name => "Category", :path => "/category" }] }
|
26
26
|
end
|
27
27
|
|
28
28
|
context "multiple has_one associations" do
|
@@ -31,12 +31,12 @@ describe Her::Model::Associations do
|
|
31
31
|
Foo::User.has_one :role
|
32
32
|
end
|
33
33
|
|
34
|
-
its([:has_one]) { should eql [{ :name => :category, :data_key => :category, :class_name => "Category", :path => "/category" }, { :name => :role, :data_key => :role, :class_name => "Role", :path => "/role" }] }
|
34
|
+
its([:has_one]) { should eql [{ :name => :category, :data_key => :category, :default => nil, :class_name => "Category", :path => "/category" }, { :name => :role, :data_key => :role, :default => nil, :class_name => "Role", :path => "/role" }] }
|
35
35
|
end
|
36
36
|
|
37
37
|
context "single belongs_to association" do
|
38
38
|
before { Foo::User.belongs_to :organization }
|
39
|
-
its([:belongs_to]) { should eql [{ :name => :organization, :data_key => :organization, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }] }
|
39
|
+
its([:belongs_to]) { should eql [{ :name => :organization, :data_key => :organization, :default => nil, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }] }
|
40
40
|
end
|
41
41
|
|
42
42
|
context "multiple belongs_to association" do
|
@@ -45,7 +45,7 @@ describe Her::Model::Associations do
|
|
45
45
|
Foo::User.belongs_to :family
|
46
46
|
end
|
47
47
|
|
48
|
-
its([:belongs_to]) { should eql [{ :name => :organization, :data_key => :organization, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }, { :name => :family, :data_key => :family, :class_name => "Family", :foreign_key => "family_id", :path => "/families/:id" }] }
|
48
|
+
its([:belongs_to]) { should eql [{ :name => :organization, :data_key => :organization, :default => nil, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }, { :name => :family, :data_key => :family, :default => nil, :class_name => "Family", :foreign_key => "family_id", :path => "/families/:id" }] }
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -55,18 +55,18 @@ describe Her::Model::Associations do
|
|
55
55
|
|
56
56
|
context "in base class" do
|
57
57
|
context "single has_many association" do
|
58
|
-
before { Foo::User.has_many :comments, :class_name => "Post", :inverse_of => :admin, :data_key => :user_comments }
|
59
|
-
its([:has_many]) { should eql [{ :name => :comments, :data_key => :user_comments, :class_name => "Post", :path => "/comments", :inverse_of => :admin }] }
|
58
|
+
before { Foo::User.has_many :comments, :class_name => "Post", :inverse_of => :admin, :data_key => :user_comments, :default => {} }
|
59
|
+
its([:has_many]) { should eql [{ :name => :comments, :data_key => :user_comments, :default => {}, :class_name => "Post", :path => "/comments", :inverse_of => :admin }] }
|
60
60
|
end
|
61
61
|
|
62
62
|
context "signle has_one association" do
|
63
|
-
before { Foo::User.has_one :category, :class_name => "Topic", :foreign_key => "topic_id", :data_key => :topic }
|
64
|
-
its([:has_one]) { should eql [{ :name => :category, :data_key => :topic, :class_name => "Topic", :foreign_key => "topic_id", :path => "/category" }] }
|
63
|
+
before { Foo::User.has_one :category, :class_name => "Topic", :foreign_key => "topic_id", :data_key => :topic, :default => nil }
|
64
|
+
its([:has_one]) { should eql [{ :name => :category, :data_key => :topic, :default => nil, :class_name => "Topic", :foreign_key => "topic_id", :path => "/category" }] }
|
65
65
|
end
|
66
66
|
|
67
67
|
context "single belongs_to association" do
|
68
|
-
before { Foo::User.belongs_to :organization, :class_name => "Business", :foreign_key => "org_id", :data_key => :org }
|
69
|
-
its([:belongs_to]) { should eql [{ :name => :organization, :data_key => :org, :class_name => "Business", :foreign_key => "org_id", :path => "/organizations/:id" }] }
|
68
|
+
before { Foo::User.belongs_to :organization, :class_name => "Business", :foreign_key => "org_id", :data_key => :org, :default => true }
|
69
|
+
its([:belongs_to]) { should eql [{ :name => :organization, :data_key => :org, :default => true, :class_name => "Business", :foreign_key => "org_id", :path => "/organizations/:id" }] }
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -76,7 +76,7 @@ describe Her::Model::Associations do
|
|
76
76
|
describe "associations accessor" do
|
77
77
|
subject { Class.new(Foo::User).associations }
|
78
78
|
its(:object_id) { should_not eql Foo::User.associations.object_id }
|
79
|
-
its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :class_name => "Post", :path => "/comments", :inverse_of => nil }] }
|
79
|
+
its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :default => [], :class_name => "Post", :path => "/comments", :inverse_of => nil }] }
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -145,11 +145,11 @@ describe Her::Model::Associations do
|
|
145
145
|
end
|
146
146
|
|
147
147
|
it "does not refetch the parents models data if they have been fetched before" do
|
148
|
-
@user_with_included_data.comments.first.user.object_id.should == @user_with_included_data.object_id
|
148
|
+
@user_with_included_data.comments.first.user.fetch.object_id.should == @user_with_included_data.object_id
|
149
149
|
end
|
150
150
|
|
151
151
|
it "uses the given inverse_of key to set the parent model" do
|
152
|
-
@user_with_included_data.posts.first.admin.object_id.should == @user_with_included_data.object_id
|
152
|
+
@user_with_included_data.posts.first.admin.fetch.object_id.should == @user_with_included_data.object_id
|
153
153
|
end
|
154
154
|
|
155
155
|
it "fetches data that was not included through has_many" do
|
@@ -163,4 +163,85 @@ describe Her::Model::Attributes do
|
|
163
163
|
expect { @user.metadata }.to raise_error(NoMethodError)
|
164
164
|
end
|
165
165
|
end
|
166
|
+
|
167
|
+
context "overwriting default attribute methods" do
|
168
|
+
context "for getter method" do
|
169
|
+
before do
|
170
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
171
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
172
|
+
builder.adapter :test do |stub|
|
173
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :document => { :url => "http://example.com" } }.to_json] }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
spawn_model 'Foo::User' do
|
178
|
+
def document
|
179
|
+
@attributes[:document][:url]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it "bypasses Her's method" do
|
185
|
+
@user = Foo::User.find(1)
|
186
|
+
@user.document.should == "http://example.com"
|
187
|
+
|
188
|
+
@user = Foo::User.find(1)
|
189
|
+
@user.document.should == "http://example.com"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "for setter method" do
|
194
|
+
before do
|
195
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
196
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
197
|
+
builder.adapter :test do |stub|
|
198
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :document => { :url => "http://example.com" } }.to_json] }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
spawn_model 'Foo::User' do
|
203
|
+
def document=(document)
|
204
|
+
@attributes[:document] = document[:url]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "bypasses Her's method" do
|
210
|
+
@user = Foo::User.find(1)
|
211
|
+
@user.document.should == "http://example.com"
|
212
|
+
|
213
|
+
@user = Foo::User.find(1)
|
214
|
+
@user.document.should == "http://example.com"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "for predicate method" do
|
219
|
+
before do
|
220
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
221
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
222
|
+
builder.adapter :test do |stub|
|
223
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :document => { :url => nil } }.to_json] }
|
224
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :document => { :url => "http://example.com" } }.to_json] }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
spawn_model 'Foo::User' do
|
229
|
+
def document?
|
230
|
+
document[:url].present?
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
it "byoasses Her's method" do
|
236
|
+
@user = Foo::User.find(1)
|
237
|
+
@user.document?.should be_false
|
238
|
+
|
239
|
+
@user = Foo::User.find(1)
|
240
|
+
@user.document?.should be_false
|
241
|
+
|
242
|
+
@user = Foo::User.find(2)
|
243
|
+
@user.document?.should be_true
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
166
247
|
end
|
data/spec/model/relation_spec.rb
CHANGED
@@ -10,7 +10,19 @@ describe Her::Model::Relation do
|
|
10
10
|
builder.adapter :test do |stub|
|
11
11
|
stub.get("/users?foo=1&bar=2") { |env| ok! [{ :id => 2, :fullname => "Tobias Fünke" }] }
|
12
12
|
stub.get("/users?admin=1") { |env| ok! [{ :id => 1, :fullname => "Tobias Fünke" }] }
|
13
|
-
|
13
|
+
|
14
|
+
stub.get("/users") do |env|
|
15
|
+
ok! [
|
16
|
+
{ :id => 1, :fullname => "Tobias Fünke" },
|
17
|
+
{ :id => 2, :fullname => "Lindsay Fünke" },
|
18
|
+
@created_user,
|
19
|
+
].compact
|
20
|
+
end
|
21
|
+
|
22
|
+
stub.post('/users') do |env|
|
23
|
+
@created_user = { :id => 3, :fullname => 'George Michael Bluth' }
|
24
|
+
ok! @created_user
|
25
|
+
end
|
14
26
|
end
|
15
27
|
end
|
16
28
|
|
@@ -33,6 +45,12 @@ describe Her::Model::Relation do
|
|
33
45
|
@user = Foo::User.where(:foo => 1).where(:bar => 2).first
|
34
46
|
@user.id.should == 2
|
35
47
|
end
|
48
|
+
|
49
|
+
it "does not reuse relations" do
|
50
|
+
Foo::User.all.should have(2).items
|
51
|
+
Foo::User.create(:fullname => 'George Michael Bluth').id.should == 3
|
52
|
+
Foo::User.all.should have(3).items
|
53
|
+
end
|
36
54
|
end
|
37
55
|
|
38
56
|
context "for parent class" do
|
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.6.
|
4
|
+
version: 0.6.4
|
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-04-
|
11
|
+
date: 2013-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- .gitignore
|
119
119
|
- .rspec
|
120
120
|
- .travis.yml
|
121
|
+
- .yardopts
|
121
122
|
- CONTRIBUTING.md
|
122
123
|
- Gemfile
|
123
124
|
- LICENSE
|
@@ -175,7 +176,7 @@ files:
|
|
175
176
|
- spec/support/macros/her_macros.rb
|
176
177
|
- spec/support/macros/model_macros.rb
|
177
178
|
- spec/support/macros/request_macros.rb
|
178
|
-
homepage: http://
|
179
|
+
homepage: http://her-rb.org
|
179
180
|
licenses:
|
180
181
|
- MIT
|
181
182
|
metadata: {}
|