her 0.6.2 → 0.6.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/.gitignore +2 -7
- data/README.md +3 -3
- data/UPGRADE.md +51 -44
- data/lib/her/model.rb +2 -0
- data/lib/her/model/associations.rb +21 -23
- data/lib/her/model/associations/association.rb +3 -2
- data/lib/her/model/associations/belongs_to_association.rb +53 -5
- data/lib/her/model/associations/has_many_association.rb +48 -4
- data/lib/her/model/associations/has_one_association.rb +52 -4
- data/lib/her/model/attributes.rb +73 -23
- data/lib/her/model/base.rb +4 -0
- data/lib/her/model/deprecated_methods.rb +61 -0
- data/lib/her/model/http.rb +46 -37
- data/lib/her/model/introspection.rb +3 -1
- data/lib/her/model/nested_attributes.rb +25 -37
- data/lib/her/model/orm.rb +29 -2
- data/lib/her/model/parse.rb +2 -2
- data/lib/her/model/relation.rb +12 -0
- data/lib/her/version.rb +1 -1
- data/spec/model/associations_spec.rb +1 -1
- data/spec/model/attributes_spec.rb +7 -7
- data/spec/model/nested_attributes_spec.rb +8 -0
- data/spec/model/relation_spec.rb +56 -3
- data/spec/support/macros/request_macros.rb +1 -1
- metadata +3 -2
@@ -22,7 +22,6 @@ module Her
|
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
25
|
-
# @private
|
26
25
|
def attribute_for_inspect(value)
|
27
26
|
if value.is_a?(String) && value.length > 50
|
28
27
|
"#{value[0..50]}...".inspect
|
@@ -35,6 +34,7 @@ module Her
|
|
35
34
|
|
36
35
|
module ClassMethods
|
37
36
|
# Finds a class at the same level as this one or at the global level.
|
37
|
+
#
|
38
38
|
# @private
|
39
39
|
def her_nearby_class(name)
|
40
40
|
her_sibling_class(name) || name.constantize rescue nil
|
@@ -42,6 +42,7 @@ module Her
|
|
42
42
|
|
43
43
|
protected
|
44
44
|
# Looks for a class at the same level as this one with the given name.
|
45
|
+
#
|
45
46
|
# @private
|
46
47
|
def her_sibling_class(name)
|
47
48
|
if mod = self.her_containing_module
|
@@ -51,6 +52,7 @@ module Her
|
|
51
52
|
end
|
52
53
|
|
53
54
|
# If available, returns the containing Module for this class.
|
55
|
+
#
|
54
56
|
# @private
|
55
57
|
def her_containing_module
|
56
58
|
return unless self.name =~ /::/
|
@@ -4,54 +4,42 @@ module Her
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
module ClassMethods
|
7
|
+
# Allow nested attributes for an association
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class User
|
11
|
+
# include Her::Model
|
12
|
+
#
|
13
|
+
# has_one :role
|
14
|
+
# accepts_nested_attributes_for :role
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class Role
|
18
|
+
# include Her::Model
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# user = User.new(name: "Tobias", role_attributes: { title: "moderator" })
|
22
|
+
# user.role # => #<Role title="moderator">
|
7
23
|
def accepts_nested_attributes_for(*association_names)
|
24
|
+
allowed_association_names = associations.inject([]) { |memo, (name, details)| memo << details }.flatten.map { |a| a[:name] }
|
25
|
+
|
8
26
|
association_names.each do |association_name|
|
9
|
-
|
10
|
-
|
11
|
-
if !associations[association_type].nil? && associations[association_type].any? { |association| association[:name] == association_name }
|
12
|
-
type = association_type
|
13
|
-
end
|
27
|
+
unless allowed_association_names.include?(association_name)
|
28
|
+
raise Her::Errors::AssociationUnknownError.new("Unknown association name :#{association_name}")
|
14
29
|
end
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
class_eval <<-eoruby, __FILE__, __LINE__ + 1
|
30
|
+
|
31
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
19
32
|
if method_defined?(:#{association_name}_attributes=)
|
20
33
|
remove_method(:#{association_name}_attributes=)
|
21
34
|
end
|
35
|
+
|
22
36
|
def #{association_name}_attributes=(attributes)
|
23
|
-
|
37
|
+
self.#{association_name}.assign_nested_attributes(attributes)
|
24
38
|
end
|
25
|
-
|
39
|
+
RUBY
|
26
40
|
end
|
27
41
|
end
|
28
42
|
end
|
29
|
-
|
30
|
-
def assign_nested_attributes_for_belongs_to_association(association_name, attributes)
|
31
|
-
assign_nested_attributes_for_simple_association(:belongs_to, association_name, attributes)
|
32
|
-
end
|
33
|
-
|
34
|
-
def assign_nested_attributes_for_has_one_association(association_name, attributes)
|
35
|
-
assign_nested_attributes_for_simple_association(:has_one, association_name, attributes)
|
36
|
-
end
|
37
|
-
|
38
|
-
def assign_nested_attributes_for_has_many_association(association_name, attributes)
|
39
|
-
association = self.class.associations[:has_many].find { |association| association[:name] == association_name }
|
40
|
-
klass = self.class.her_nearby_class(association[:class_name])
|
41
|
-
self.send("#{association[:name]}=", Her::Model::Attributes.initialize_collection(klass, :data => attributes))
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
def assign_nested_attributes_for_simple_association(association_type, association_name, attributes)
|
46
|
-
association = self.class.associations[association_type].find { |association| association[:name] == association_name }
|
47
|
-
if has_data?(association[:name])
|
48
|
-
self.send(association[:name]).assign_data(attributes)
|
49
|
-
else
|
50
|
-
klass = self.class.her_nearby_class(association[:class_name])
|
51
|
-
instance = klass.new(klass.parse(attributes))
|
52
|
-
self.send("#{association[:name]}=", instance)
|
53
|
-
end
|
54
|
-
end
|
55
43
|
end
|
56
44
|
end
|
57
45
|
end
|
data/lib/her/model/orm.rb
CHANGED
@@ -131,12 +131,33 @@ module Her
|
|
131
131
|
|
132
132
|
# @private
|
133
133
|
def scoped
|
134
|
-
|
134
|
+
@_her_default_scope || blank_relation
|
135
|
+
end
|
136
|
+
|
137
|
+
# Define the default scope for the model
|
138
|
+
#
|
139
|
+
# @example
|
140
|
+
# class User
|
141
|
+
# include Her::Model
|
142
|
+
#
|
143
|
+
# default_scope lambda { where(:admin => 1) }
|
144
|
+
# enc
|
145
|
+
#
|
146
|
+
# User.all # Called via GET "/users?admin=1"
|
147
|
+
# User.new.admin # => 1
|
148
|
+
def default_scope(block=nil)
|
149
|
+
@_her_default_scope ||= superclass.respond_to?(:default_scope) ? superclass.default_scope : scoped
|
150
|
+
@_her_default_scope = @_her_default_scope.instance_exec(&block) unless block.nil?
|
151
|
+
@_her_default_scope
|
135
152
|
end
|
136
153
|
|
137
154
|
# Delegate the following methods to `scoped`
|
138
155
|
[:all, :where, :create, :build, :first_or_create, :first_or_initialize].each do |method|
|
139
|
-
|
156
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
157
|
+
def #{method}(*attrs)
|
158
|
+
scoped.send(#{method.to_sym.inspect}, *attrs)
|
159
|
+
end
|
160
|
+
RUBY
|
140
161
|
end
|
141
162
|
|
142
163
|
# Save an existing resource and return it
|
@@ -174,6 +195,12 @@ module Her
|
|
174
195
|
return @method_for[action] if method.nil?
|
175
196
|
@method_for[action] = method.to_s.downcase.to_sym
|
176
197
|
end
|
198
|
+
|
199
|
+
private
|
200
|
+
# @private
|
201
|
+
def blank_relation
|
202
|
+
@blank_relation ||= Relation.new(self)
|
203
|
+
end
|
177
204
|
end
|
178
205
|
end
|
179
206
|
end
|
data/lib/her/model/parse.rb
CHANGED
@@ -36,7 +36,7 @@ module Her
|
|
36
36
|
return @_her_include_root_in_json unless value
|
37
37
|
@_her_include_root_in_json = value
|
38
38
|
end
|
39
|
-
alias
|
39
|
+
alias include_root_in_json? include_root_in_json
|
40
40
|
|
41
41
|
# Return or change the value of `parse_root_in`
|
42
42
|
#
|
@@ -53,7 +53,7 @@ module Her
|
|
53
53
|
return @_her_parse_root_in_json unless value
|
54
54
|
@_her_parse_root_in_json = value
|
55
55
|
end
|
56
|
-
alias
|
56
|
+
alias parse_root_in_json? parse_root_in_json
|
57
57
|
|
58
58
|
# Return or change the value of `root_element`. Always defaults to the base name of the class.
|
59
59
|
#
|
data/lib/her/model/relation.rb
CHANGED
@@ -9,6 +9,11 @@ module Her
|
|
9
9
|
@query_attrs = {}
|
10
10
|
end
|
11
11
|
|
12
|
+
# @private
|
13
|
+
def apply_to(attributes)
|
14
|
+
@query_attrs.merge(attributes)
|
15
|
+
end
|
16
|
+
|
12
17
|
# Build a new resource
|
13
18
|
def build(attrs = {})
|
14
19
|
@parent.new(@query_attrs.merge(attrs))
|
@@ -25,10 +30,17 @@ module Her
|
|
25
30
|
alias all where
|
26
31
|
|
27
32
|
# Bubble all methods to the fetched collection
|
33
|
+
#
|
34
|
+
# @private
|
28
35
|
def method_missing(method, *args, &blk)
|
29
36
|
fetch.send(method, *args, &blk)
|
30
37
|
end
|
31
38
|
|
39
|
+
# @private
|
40
|
+
def respond_to?(method, *args)
|
41
|
+
super || fetch.respond_to?(method, *args)
|
42
|
+
end
|
43
|
+
|
32
44
|
# @private
|
33
45
|
def nil?
|
34
46
|
fetch.nil?
|
data/lib/her/version.rb
CHANGED
@@ -93,7 +93,7 @@ describe Her::Model::Associations do
|
|
93
93
|
stub.get("/users/2/comments") { |env| [200, {}, [{ :comment => { :id => 4, :body => "They're having a FIRESALE?" } }, { :comment => { :id => 5, :body => "Is this the tiny town from Footloose?" } }].to_json] }
|
94
94
|
stub.get("/users/2/role") { |env| [200, {}, { :id => 2, :body => "User" }.to_json] }
|
95
95
|
stub.get("/users/1/role") { |env| [200, {}, { :id => 3, :body => "User" }.to_json] }
|
96
|
-
stub.get("/users/1/posts") { |env| [200, {}, {:id => 1, :body => 'blogging stuff', :admin_id => 1 }.to_json] }
|
96
|
+
stub.get("/users/1/posts") { |env| [200, {}, [{:id => 1, :body => 'blogging stuff', :admin_id => 1 }].to_json] }
|
97
97
|
stub.get("/organizations/1") { |env| [200, {}, { :organization => { :id => 1, :name => "Bluth Company Foo" } }.to_json] }
|
98
98
|
stub.post("/users") { |env| [200, {}, { :id => 5, :name => "Mr. Krabs", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
99
99
|
stub.put("/users/5") { |env| [200, {}, { :id => 5, :name => "Clancy Brown", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
@@ -18,7 +18,7 @@ describe Her::Model::Attributes do
|
|
18
18
|
|
19
19
|
it "handles method missing for getter" do
|
20
20
|
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
21
|
-
|
21
|
+
expect { @new_user.unknown_method_for_a_user }.to raise_error(NoMethodError)
|
22
22
|
expect { @new_user.fullname }.to_not raise_error(NoMethodError)
|
23
23
|
end
|
24
24
|
|
@@ -48,16 +48,16 @@ describe Her::Model::Attributes do
|
|
48
48
|
@new_user.should respond_to(:fullname?)
|
49
49
|
end
|
50
50
|
|
51
|
-
it "handles
|
51
|
+
it "handles has_attribute? for getter" do
|
52
52
|
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
53
|
-
@new_user.should_not
|
54
|
-
@new_user.should
|
53
|
+
@new_user.should_not have_attribute(:unknown_method_for_a_user)
|
54
|
+
@new_user.should have_attribute(:fullname)
|
55
55
|
end
|
56
56
|
|
57
|
-
it "handles
|
57
|
+
it "handles get_attribute for getter" do
|
58
58
|
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
59
|
-
@new_user.
|
60
|
-
@new_user.
|
59
|
+
@new_user.get_attribute(:unknown_method_for_a_user).should be_nil
|
60
|
+
@new_user.get_attribute(:fullname).should == 'Mayonegg'
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -95,4 +95,12 @@ describe Her::Model::NestedAttributes do
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
98
|
+
|
99
|
+
context "with an unknown association" do
|
100
|
+
it "raises an error" do
|
101
|
+
expect {
|
102
|
+
spawn_model("Foo::User") { accepts_nested_attributes_for :company }
|
103
|
+
}.to raise_error(Her::Errors::AssociationUnknownError, 'Unknown association name :company')
|
104
|
+
end
|
105
|
+
end
|
98
106
|
end
|
data/spec/model/relation_spec.rb
CHANGED
@@ -18,14 +18,15 @@ describe Her::Model::Relation do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
it "doesn't fetch the data immediatly" do
|
21
|
-
|
21
|
+
Foo::User.should_receive(:request).never
|
22
22
|
@users = Foo::User.where(:admin => 1)
|
23
23
|
end
|
24
24
|
|
25
25
|
it "fetches the data and passes query parameters" do
|
26
|
-
|
26
|
+
Foo::User.should_receive(:request).once.and_call_original
|
27
27
|
@users = Foo::User.where(:admin => 1)
|
28
|
-
@users.
|
28
|
+
@users.should respond_to(:length)
|
29
|
+
@users.should have(1).items
|
29
30
|
end
|
30
31
|
|
31
32
|
it "chains multiple where statements" do
|
@@ -133,4 +134,56 @@ describe Her::Model::Relation do
|
|
133
134
|
@user.id.should == 4
|
134
135
|
end
|
135
136
|
end
|
137
|
+
|
138
|
+
describe :default_scope do
|
139
|
+
context "for new objects" do
|
140
|
+
before do
|
141
|
+
spawn_model 'Foo::User' do
|
142
|
+
default_scope lambda { where(:active => true) }
|
143
|
+
default_scope lambda { where(:admin => true) }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should apply the scope to the attributes" do
|
148
|
+
Foo::User.new.should be_active
|
149
|
+
Foo::User.new.should be_admin
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "for fetched resources" do
|
154
|
+
before do
|
155
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
156
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
157
|
+
builder.use Faraday::Request::UrlEncoded
|
158
|
+
builder.adapter :test do |stub|
|
159
|
+
stub.post("/users") { |env| ok! :id => 3, :active => (params(env)[:active] == "true" ? true : false) }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
spawn_model 'Foo::User' do
|
164
|
+
default_scope lambda { where(:active => true) }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it("should apply the scope to the request") { Foo::User.create.should be_active }
|
169
|
+
end
|
170
|
+
|
171
|
+
context "for fetched collections" do
|
172
|
+
before do
|
173
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
174
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
175
|
+
builder.use Faraday::Request::UrlEncoded
|
176
|
+
builder.adapter :test do |stub|
|
177
|
+
stub.get("/users?active=true") { |env| ok! [{ :id => 3, :active => (params(env)[:active] == "true" ? true : false) }] }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
spawn_model 'Foo::User' do
|
182
|
+
default_scope lambda { where(:active => true) }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it("should apply the scope to the request") { Foo::User.all.first.should be_active }
|
187
|
+
end
|
188
|
+
end
|
136
189
|
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.6.
|
4
|
+
version: 0.6.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-04-
|
11
|
+
date: 2013-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -142,6 +142,7 @@ files:
|
|
142
142
|
- lib/her/model/associations/has_one_association.rb
|
143
143
|
- lib/her/model/attributes.rb
|
144
144
|
- lib/her/model/base.rb
|
145
|
+
- lib/her/model/deprecated_methods.rb
|
145
146
|
- lib/her/model/http.rb
|
146
147
|
- lib/her/model/introspection.rb
|
147
148
|
- lib/her/model/nested_attributes.rb
|