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.
@@ -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
- type = nil
10
- [:belongs_to, :has_one, :has_many].each do |association_type|
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
- if type.nil?
16
- raise(AssociationUnknownError.new("Unknown association name :#{association_name}"))
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
- assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
37
+ self.#{association_name}.assign_nested_attributes(attributes)
24
38
  end
25
- eoruby
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
@@ -131,12 +131,33 @@ module Her
131
131
 
132
132
  # @private
133
133
  def scoped
134
- Relation.new(self)
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
- define_method(method) { |*attrs| scoped.send(method, *attrs) }
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
@@ -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 :include_root_in_json? :include_root_in_json
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 :parse_root_in_json? :parse_root_in_json
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
  #
@@ -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?
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.6.2"
2
+ VERSION = "0.6.3"
3
3
  end
@@ -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
- lambda { @new_user.unknown_method_for_a_user }.should raise_error(NoMethodError)
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 has_data? for getter" do
51
+ it "handles has_attribute? for getter" do
52
52
  @new_user = Foo::User.new(:fullname => 'Mayonegg')
53
- @new_user.should_not have_data(:unknown_method_for_a_user)
54
- @new_user.should have_data(:fullname)
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 get_data for getter" do
57
+ it "handles get_attribute for getter" do
58
58
  @new_user = Foo::User.new(:fullname => 'Mayonegg')
59
- @new_user.get_data(:unknown_method_for_a_user).should be_nil
60
- @new_user.get_data(:fullname).should == 'Mayonegg'
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
@@ -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
- Her::Model::Relation.any_instance.should_receive(:fetch).never
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
- Her::Model::Relation.any_instance.should_receive(:fetch).once.and_call_original
26
+ Foo::User.should_receive(:request).once.and_call_original
27
27
  @users = Foo::User.where(:admin => 1)
28
- @users.length.should == 1
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
@@ -11,7 +11,7 @@ module Her
11
11
  end
12
12
 
13
13
  def params(env)
14
- @params ||= Faraday::Utils.parse_query(env[:body]).with_indifferent_access
14
+ @params ||= Faraday::Utils.parse_query(env[:body]).with_indifferent_access.merge(env[:params])
15
15
  end
16
16
  end
17
17
  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.2
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-22 00:00:00.000000000 Z
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