her 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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