her 0.6 → 0.6.1

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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NWYxMjI2MTQ0YjdhOTYyNDA1YTkwZjU4MTNhMDFjZDQ1OTM0OTczMQ==
4
+ N2RhYjBkODQ3ZjE1NmNlM2VmZTExNWJkNDU3NTI4YjI3OGNhN2RiZQ==
5
5
  data.tar.gz: !binary |-
6
- ZDk0YzAxYjUzYTlmMGU4Y2FkMGQ0NGQ2MDM0YjM2MjBlZmRmMGVhZg==
6
+ OTBmNmRjM2I2NDE3ZmQ4MjZmZDJjMzI5NjQ2ODJjODUwMDczNWRiNw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZDE1MWExN2JkZDY5OGYwNWFkMGZhYTVhZDE1MzEzYjMxYjc3YTU3NDc0MDQ4
10
- OWMyOTEwMzY3ZTE4OGIxMTNiODlhZDExZGNjMzVlMjYzMWY1ZWNmZGVlM2Qz
11
- Yjk2ZWRkZDkxMGU0MmJlMmU2ZjU0ZGI3ODA3ZWU2NDUzNTcxZWQ=
9
+ YzZhZjExMWI1MmYwYTUwODU4OGM0OWM2OWI2NDgyOGU5OTM0ZWViY2UzYjdh
10
+ NTZmNTI1YmFhMDlmMzQ5YTBmZWQzMWZhZjRlOWUwMzcxZTY0NWQ5YWQ2ODk3
11
+ ZjA0M2ExMmMyM2UxNjMwYzJjOWNjNmVjYzY4MDJkZmM3NjM2NzY=
12
12
  data.tar.gz: !binary |-
13
- OTcxYzViZjM1MDVkMDJjODhlZTlkN2M3MTliYzRmZDE1NzQ4NDMzMDgzYzhk
14
- NjNhMGIwMjUxNzFiMzA4Mjg2NmE3OTQzNWRhNWNiMDIyM2Q4MzVlMDk3OWM3
15
- OTAwNTBlNWYxMmY1MWUwMTlkOWFlYTZkY2E1NDA3NGFkNDdjZjk=
13
+ ZDM4NWJjMzllNDdhZTc1NGI2NWVmMjlhYTAyYzgzYTIxOTkzNDgwNGEzZDVi
14
+ ZmI0OWRmYjdhNjVkNWMwNzVhYjIyYjg2YTM0ZjczMGY5OTBkNDU3Nzg4ZWVk
15
+ OWYwY2Y0NDk0Zjc1MDlkM2M1ZDJlNzkzZTA4NGMxNTI2M2FlODI=
data/README.md CHANGED
@@ -624,7 +624,12 @@ user.save # PUT /users/4fd89a42ff204b03a905c535
624
624
 
625
625
  ### Inheritance
626
626
 
627
- If all your models share the same settings, you might want to make them children of a class and only include `Her::Model` in that class.
627
+ If all your models share the same settings, you might want to make them children of a class and only include `Her::Model` in that class. However, there are a few settings that don’t get passed to the children classes:
628
+
629
+ * `root_element`
630
+ * `collection_path` and `resource_path`
631
+
632
+ Those settings are based on the class name, so you don’t have to redefine them each time you create a new children class (but you still can). Every other setting is inherited from the parent (associations, scopes, JSON settings, etc.).
628
633
 
629
634
  ```ruby
630
635
  module MyAPI
@@ -643,6 +648,29 @@ User.find(1)
643
648
  # GET /users/1
644
649
  ```
645
650
 
651
+ ### Scopes
652
+
653
+ Just like with ActiveRecord, you can define named scopes for your models. Scopes are chainable and can be used within other scopes.
654
+
655
+ ```ruby
656
+ class User
657
+ include Her::Model
658
+
659
+ scope :by_role, lambda { |role| where(:role => role) }
660
+ scope :admins, lambda { by_role('admin') }
661
+ scope :active, lambda { where(:active => 1) }
662
+ end
663
+
664
+ @admins = User.admins
665
+ # GET /users?role=admin
666
+
667
+ @moderators = User.by_role('moderator')
668
+ # GET /users?role=moderator
669
+
670
+ @active_admins = User.active.admins # @admins.active would have worked here too
671
+ # GET /users?role=admin&active=1
672
+ ```
673
+
646
674
  ### Multiple APIs
647
675
 
648
676
  It is possible to use different APIs for different models. Instead of calling `Her::API.setup`, you can create instances of `Her::API`:
@@ -2,6 +2,27 @@ module Her
2
2
  module Model
3
3
  module Associations
4
4
  class BelongsToAssociation < Association
5
+ # @private
6
+ def self.attach(klass, name, attrs)
7
+ attrs = {
8
+ :class_name => name.to_s.classify,
9
+ :name => name,
10
+ :data_key => name,
11
+ :foreign_key => "#{name}_id",
12
+ :path => "/#{name.to_s.pluralize}/:id"
13
+ }.merge(attrs)
14
+ klass.associations[:belongs_to] << attrs
15
+
16
+ klass.instance_eval do
17
+ define_method(name) do
18
+ cached_name = :"@_her_association_#{name}"
19
+
20
+ cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
21
+ cached_data || instance_variable_set(cached_name, Her::Model::Associations::BelongsToAssociation.new(self, attrs))
22
+ end
23
+ end
24
+ end
25
+
5
26
  def build(attributes = {})
6
27
  @klass.new(attributes)
7
28
  end
@@ -12,6 +33,7 @@ module Her
12
33
  resource
13
34
  end
14
35
 
36
+ # @private
15
37
  def fetch
16
38
  foreign_key_value = @parent.attributes[@opts[:foreign_key].to_sym]
17
39
  return nil if (@parent.attributes.include?(@name) && @parent.attributes[@name].nil? && @query_attrs.empty?) || foreign_key_value.blank?
@@ -2,6 +2,27 @@ module Her
2
2
  module Model
3
3
  module Associations
4
4
  class HasManyAssociation < Association
5
+ # @private
6
+ def self.attach(klass, name, attrs)
7
+ attrs = {
8
+ :class_name => name.to_s.classify,
9
+ :name => name,
10
+ :data_key => name,
11
+ :path => "/#{name}",
12
+ :inverse_of => nil
13
+ }.merge(attrs)
14
+ klass.associations[:has_many] << attrs
15
+
16
+ klass.instance_eval do
17
+ define_method(name) do
18
+ cached_name = :"@_her_association_#{name}"
19
+
20
+ cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
21
+ cached_data || instance_variable_set(cached_name, Her::Model::Associations::HasManyAssociation.new(self, attrs))
22
+ end
23
+ end
24
+ end
25
+
5
26
  def build(attributes = {})
6
27
  @klass.new(attributes.merge(:"#{@parent.singularized_resource_name}_id" => @parent.id))
7
28
  end
@@ -17,6 +38,7 @@ module Her
17
38
  resource
18
39
  end
19
40
 
41
+ # @private
20
42
  def fetch
21
43
  return Her::Collection.new if @parent.attributes.include?(@name) && @parent.attributes[@name].empty? && @query_attrs.empty?
22
44
 
@@ -2,6 +2,26 @@ module Her
2
2
  module Model
3
3
  module Associations
4
4
  class HasOneAssociation < Association
5
+ # @private
6
+ def self.attach(klass, name, attrs)
7
+ attrs = {
8
+ :class_name => name.to_s.classify,
9
+ :name => name,
10
+ :data_key => name,
11
+ :path => "/#{name}"
12
+ }.merge(attrs)
13
+ klass.associations[:has_one] << attrs
14
+
15
+ klass.instance_eval do
16
+ define_method(name) do
17
+ cached_name = :"@_her_association_#{name}"
18
+
19
+ cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
20
+ cached_data || instance_variable_set(cached_name, Her::Model::Associations::HasOneAssociation.new(self, attrs))
21
+ end
22
+ end
23
+ end
24
+
5
25
  def build(attributes = {})
6
26
  @klass.new(attributes.merge(:"#{@parent.singularized_resource_name}_id" => @parent.id))
7
27
  end
@@ -12,6 +32,7 @@ module Her
12
32
  resource
13
33
  end
14
34
 
35
+ # @private
15
36
  def fetch
16
37
  return nil if @parent.attributes.include?(@name) && @parent.attributes[@name].nil? && @query_attrs.empty?
17
38
 
@@ -29,11 +29,7 @@ module Her
29
29
  # @private
30
30
  def associations
31
31
  @_her_associations ||= begin
32
- if superclass.respond_to?(:associations)
33
- superclass.associations.dup
34
- else
35
- {}
36
- end
32
+ superclass.respond_to?(:associations) ? superclass.associations.dup : Hash.new { |h,k| h[k] = [] }
37
33
  end
38
34
  end
39
35
  alias :relationships :associations
@@ -82,21 +78,7 @@ module Her
82
78
  # @user.articles # => [#<Article(articles/2) id=2 title="Hello world.">]
83
79
  # # Fetched via GET "/users/1/articles"
84
80
  def has_many(name, attrs={})
85
- attrs = {
86
- :class_name => name.to_s.classify,
87
- :name => name,
88
- :data_key => name,
89
- :path => "/#{name}",
90
- :inverse_of => nil
91
- }.merge(attrs)
92
- (associations[:has_many] ||= []) << attrs
93
-
94
- define_method(name) do
95
- cached_name = :"@_her_association_#{name}"
96
-
97
- cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
98
- cached_data || instance_variable_set(cached_name, Her::Model::Associations::HasManyAssociation.new(self, attrs))
99
- end
81
+ Her::Model::Associations::HasManyAssociation.attach(self, name, attrs)
100
82
  end
101
83
 
102
84
  # Define an *has_one* association.
@@ -118,20 +100,7 @@ module Her
118
100
  # @user.organization # => #<Organization(organizations/2) id=2 name="Foobar Inc.">
119
101
  # # Fetched via GET "/users/1/organization"
120
102
  def has_one(name, attrs={})
121
- attrs = {
122
- :class_name => name.to_s.classify,
123
- :name => name,
124
- :data_key => name,
125
- :path => "/#{name}"
126
- }.merge(attrs)
127
- (associations[:has_one] ||= []) << attrs
128
-
129
- define_method(name) do
130
- cached_name = :"@_her_association_#{name}"
131
-
132
- cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
133
- cached_data || instance_variable_set(cached_name, Her::Model::Associations::HasOneAssociation.new(self, attrs))
134
- end
103
+ Her::Model::Associations::HasOneAssociation.attach(self, name, attrs)
135
104
  end
136
105
 
137
106
  # Define a *belongs_to* association.
@@ -153,21 +122,7 @@ module Her
153
122
  # @user.team # => #<Team(teams/2) id=2 name="Developers">
154
123
  # # Fetched via GET "/teams/2"
155
124
  def belongs_to(name, attrs={})
156
- attrs = {
157
- :class_name => name.to_s.classify,
158
- :name => name,
159
- :data_key => name,
160
- :foreign_key => "#{name}_id",
161
- :path => "/#{name.to_s.pluralize}/:id"
162
- }.merge(attrs)
163
- (associations[:belongs_to] ||= []) << attrs
164
-
165
- define_method(name) do
166
- cached_name = :"@_her_association_#{name}"
167
-
168
- cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
169
- cached_data || instance_variable_set(cached_name, Her::Model::Associations::BelongsToAssociation.new(self, attrs))
170
- end
125
+ Her::Model::Associations::BelongsToAssociation.attach(self, name, attrs)
171
126
  end
172
127
  end
173
128
  end
@@ -3,6 +3,7 @@ module Her
3
3
  # This module interacts with Her::API to fetch HTTP data
4
4
  module HTTP
5
5
  extend ActiveSupport::Concern
6
+ METHODS = [:get, :post, :put, :patch, :delete]
6
7
 
7
8
  module ClassMethods
8
9
  # Change which API the model will use to make its HTTP requests
@@ -36,7 +37,7 @@ module Her
36
37
  # - <method>_collection(path, attrs, &block)
37
38
  # - <method>_resource(path, attrs, &block)
38
39
  # - custom_<method>(path, attrs)
39
- [:get, :post, :put, :patch, :delete].each do |method|
40
+ METHODS.each do |method|
40
41
  define_method method do |path, attrs={}|
41
42
  path = build_request_path_from_string_or_symbol(path, attrs)
42
43
  send(:"#{method}_raw", path, attrs) do |parsed_data, response|
data/lib/her/model/orm.rb CHANGED
@@ -109,10 +109,23 @@ module Her
109
109
  end
110
110
  end
111
111
 
112
- # Delegate the following methods to `scoped`
113
- [:all, :where, :create, :page, :per_page].each do |method|
114
- define_method method do |*attrs|
115
- scoped.send(method, attrs.first)
112
+ # Create a new chainable scope
113
+ #
114
+ # @example
115
+ # class User
116
+ # include Her::Model
117
+ #
118
+ # scope :admins, lambda { where(:admin => 1) }
119
+ # scope :page, lambda { |page| where(:page => page) }
120
+ # enc
121
+ #
122
+ # User.admins # Called via GET "/users?admin=1"
123
+ # User.page(2).all # Called via GET "/users?page=2"
124
+ def scope(name, code)
125
+ define_singleton_method(name) { |*args| instance_exec(*args, &code) }
126
+
127
+ Relation.instance_eval do
128
+ define_method(name) { |*args| instance_exec(*args, &code) }
116
129
  end
117
130
  end
118
131
 
@@ -121,6 +134,11 @@ module Her
121
134
  Relation.new(self)
122
135
  end
123
136
 
137
+ # Delegate the following methods to `scoped`
138
+ [:all, :where, :create].each do |method|
139
+ define_method(method) { |*attrs| scoped.send(method, attrs.first) }
140
+ end
141
+
124
142
  # Save an existing resource and return it
125
143
  #
126
144
  # @example
@@ -17,18 +17,12 @@ module Her
17
17
  # Add a query string parameter
18
18
  def where(attrs = {})
19
19
  return self if attrs.blank?
20
- self.clone.tap { |a| a.query_attrs = a.query_attrs.merge(attrs) }
21
- end
22
- alias :all :where
23
-
24
- def page(page)
25
- where(:page => page)
26
- end
27
-
28
- def per_page(per_page)
29
- where(:per_page => per_page)
20
+ self.clone.tap do |r|
21
+ r.query_attrs = r.query_attrs.merge(attrs)
22
+ r.clear_fetch_cache!
23
+ end
30
24
  end
31
- alias :per :per_page
25
+ alias all where
32
26
 
33
27
  # Bubble all methods to the fetched collection
34
28
  def method_missing(method, *args, &blk)
@@ -108,6 +102,10 @@ module Her
108
102
  def first_or_initialize(attrs = {})
109
103
  fetch.first || build(attrs)
110
104
  end
105
+
106
+ def clear_fetch_cache!
107
+ instance_variable_set(:@_fetch, nil)
108
+ end
111
109
  end
112
110
  end
113
111
  end
data/lib/her/model.rb CHANGED
@@ -50,7 +50,7 @@ module Her
50
50
  # Define the default primary key
51
51
  primary_key :id
52
52
 
53
- # Define default storage variables for errors and metadata
53
+ # Define default storage accessors for errors and metadata
54
54
  store_response_errors :response_errors
55
55
  store_metadata :metadata
56
56
 
data/lib/her/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.6"
2
+ VERSION = "0.6.1"
3
3
  end
@@ -3,71 +3,80 @@ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
3
 
4
4
  describe Her::Model::Associations do
5
5
  context "setting associations without details" do
6
- before do
7
- spawn_model "Foo::User"
8
- end
6
+ before { spawn_model "Foo::User" }
7
+ subject { Foo::User.associations }
9
8
 
10
- it "handles a single 'has_many' association" do
11
- Foo::User.has_many :comments
12
- Foo::User.associations[:has_many].should == [{ :name => :comments, :data_key => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }]
9
+ context "single has_many association" do
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 }] }
13
12
  end
14
13
 
15
- it "handles multiples 'has_many' association" do
16
- Foo::User.has_many :comments
17
- Foo::User.has_many :posts
18
- Foo::User.associations[:has_many].should == [{ :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 }]
14
+ context "multiple has_many associations" do
15
+ before do
16
+ Foo::User.has_many :comments
17
+ Foo::User.has_many :posts
18
+ end
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 }] }
19
21
  end
20
22
 
21
- it "handles a single 'has_one' association" do
22
- Foo::User.has_one :category
23
- Foo::User.associations[:has_one].should == [{ :name => :category, :data_key => :category, :class_name => "Category", :path => "/category" }]
23
+ context "single has_one association" do
24
+ before { Foo::User.has_one :category }
25
+ its([:has_one]) { should eql [{ :name => :category, :data_key => :category, :class_name => "Category", :path => "/category" }] }
24
26
  end
25
27
 
26
- it "handles multiples 'has_one' association" do
27
- Foo::User.has_one :category
28
- Foo::User.has_one :role
29
- Foo::User.associations[:has_one].should == [{ :name => :category, :data_key => :category, :class_name => "Category", :path => "/category" }, { :name => :role, :data_key => :role, :class_name => "Role", :path => "/role" }]
28
+ context "multiple has_one associations" do
29
+ before do
30
+ Foo::User.has_one :category
31
+ Foo::User.has_one :role
32
+ end
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" }] }
30
35
  end
31
36
 
32
- it "handles a single belongs_to association" do
33
- Foo::User.belongs_to :organization
34
- Foo::User.associations[:belongs_to].should == [{ :name => :organization, :data_key => :organization, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }]
37
+ context "single belongs_to association" do
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" }] }
35
40
  end
36
41
 
37
- it "handles multiples 'belongs_to' association" do
38
- Foo::User.belongs_to :organization
39
- Foo::User.belongs_to :family
40
- Foo::User.associations[:belongs_to].should == [{ :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" }]
42
+ context "multiple belongs_to association" do
43
+ before do
44
+ Foo::User.belongs_to :organization
45
+ Foo::User.belongs_to :family
46
+ end
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" }] }
41
49
  end
42
50
  end
43
51
 
44
52
  context "setting associations with details" do
45
- before do
46
- spawn_model "Foo::User"
47
- end
53
+ before { spawn_model "Foo::User" }
54
+ subject { Foo::User.associations }
48
55
 
49
- it "handles a single 'has_many' association" do
50
- Foo::User.has_many :comments, :class_name => "Post", :inverse_of => :admin, :data_key => :user_comments
51
- Foo::User.associations[:has_many].should == [{ :name => :comments, :data_key => :user_comments, :class_name => "Post", :path => "/comments", :inverse_of => :admin }]
52
- end
56
+ context "in base class" do
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 }] }
60
+ end
53
61
 
54
- it "handles a single 'has_one' association" do
55
- Foo::User.has_one :category, :class_name => "Topic", :foreign_key => "topic_id", :data_key => :topic
56
- Foo::User.associations[:has_one].should == [{ :name => :category, :data_key => :topic, :class_name => "Topic", :foreign_key => "topic_id", :path => "/category" }]
57
- end
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" }] }
65
+ end
58
66
 
59
- it "handles a single belongs_to association" do
60
- Foo::User.belongs_to :organization, :class_name => "Business", :foreign_key => "org_id", :data_key => :org
61
- Foo::User.associations[:belongs_to].should == [{ :name => :organization, :data_key => :org, :class_name => "Business", :foreign_key => "org_id", :path => "/organizations/:id" }]
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" }] }
70
+ end
62
71
  end
63
72
 
64
- context "inheriting associations from a superclass" do
65
- it "copies associations to the subclass" do
66
- Foo::User.has_many :comments, :class_name => "Post"
67
- subclass = Class.new(Foo::User)
68
- subclass.associations.object_id.should_not == Foo::User.associations.object_id
69
- subclass.associations[:has_many].length.should == 1
70
- subclass.associations[:has_many].first[:class_name].should == "Post"
73
+ context "in parent class" do
74
+ before { Foo::User.has_many :comments, :class_name => "Post" }
75
+
76
+ describe "associations accessor" do
77
+ subject { Class.new(Foo::User).associations }
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 }] }
71
80
  end
72
81
  end
73
82
  end
@@ -205,16 +205,6 @@ describe Her::Model::ORM do
205
205
  @users.where(:age => 42).should be_all { |u| u.age == 42 }
206
206
  @users.where(:age => 40).should be_all { |u| u.age == 40 }
207
207
  end
208
-
209
- it "handles finding with paging parameters" do
210
- @users = User.page(2).per_page(20)
211
- @users.query_attrs[:page].should == 2
212
- @users.query_attrs[:per_page].should == 20
213
-
214
- @users = User.all.page(2).per_page(20)
215
- @users.query_attrs[:page].should == 2
216
- @users.query_attrs[:per_page].should == 20
217
- end
218
208
  end
219
209
 
220
210
  context "creating resources" do
@@ -3,27 +3,58 @@ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
3
 
4
4
  describe Her::Model::Relation do
5
5
  describe :where do
6
- before do
7
- Her::API.setup :url => "https://api.example.com" do |builder|
8
- builder.use Her::Middleware::FirstLevelParseJSON
9
- builder.adapter :test do |stub|
10
- stub.get("/users") { |env| ok! [{ :id => 1, :fullname => "Tobias Fünke" }, { :id => 2, :fullname => "Lindsay Fünke" }] }
11
- stub.get("/users?admin=1") { |env| ok! [{ :id => 1, :fullname => "Tobias Fünke" }] }
6
+ context "for base classes" do
7
+ before do
8
+ Her::API.setup :url => "https://api.example.com" do |builder|
9
+ builder.use Her::Middleware::FirstLevelParseJSON
10
+ builder.adapter :test do |stub|
11
+ stub.get("/users?foo=1&bar=2") { |env| ok! [{ :id => 2, :fullname => "Tobias Fünke" }] }
12
+ stub.get("/users?admin=1") { |env| ok! [{ :id => 1, :fullname => "Tobias Fünke" }] }
13
+ stub.get("/users") { |env| ok! [{ :id => 1, :fullname => "Tobias Fünke" }, { :id => 2, :fullname => "Lindsay Fünke" }] }
14
+ end
12
15
  end
16
+
17
+ spawn_model "Foo::User"
13
18
  end
14
19
 
15
- spawn_model "Foo::User"
16
- end
20
+ it "doesn't fetch the data immediatly" do
21
+ Her::Model::Relation.any_instance.should_receive(:fetch).never
22
+ @users = Foo::User.where(:admin => 1)
23
+ end
24
+
25
+ it "fetches the data and passes query parameters" do
26
+ Her::Model::Relation.any_instance.should_receive(:fetch).once.and_call_original
27
+ @users = Foo::User.where(:admin => 1)
28
+ @users.length.should == 1
29
+ end
17
30
 
18
- it "doesn't fetch the data immediatly" do
19
- Her::Model::Relation.any_instance.should_receive(:fetch).never
20
- @users = Foo::User.where(:admin => 1)
31
+ it "chains multiple where statements" do
32
+ @user = Foo::User.where(:foo => 1).where(:bar => 2).first
33
+ @user.id.should == 2
34
+ end
21
35
  end
22
36
 
23
- it "fetches the data and passes query parameters" do
24
- Her::Model::Relation.any_instance.should_receive(:fetch).once.and_call_original
25
- @users = Foo::User.where(:admin => 1)
26
- @users.length.should == 2
37
+ context "for parent class" do
38
+ before do
39
+ Her::API.setup :url => "https://api.example.com" do |builder|
40
+ builder.use Her::Middleware::FirstLevelParseJSON
41
+ builder.adapter :test do |stub|
42
+ stub.get("/users?page=2") { |env| ok! [{ :id => 1, :fullname => "Tobias Fünke" }, { :id => 2, :fullname => "Lindsay Fünke" }] }
43
+ end
44
+ end
45
+
46
+ spawn_model("Foo::Model") do
47
+ scope :page, lambda { |page| where(:page => page) }
48
+ end
49
+
50
+ class User < Foo::Model; end
51
+ @spawned_models << :User
52
+ end
53
+
54
+ it "propagates the scopes through its children" do
55
+ @users = User.page(2)
56
+ @users.length.should == 2
57
+ end
27
58
  end
28
59
  end
29
60
 
@@ -59,31 +90,47 @@ describe Her::Model::Relation do
59
90
  end
60
91
  end
61
92
 
62
- describe "#page and #per_page" do
93
+ describe :build do
94
+ before { spawn_model "Foo::User" }
95
+
96
+ it "handles new resource with build" do
97
+ @new_user = Foo::User.where(:fullname => "Tobias Fünke").build
98
+ @new_user.new?.should be_true
99
+ @new_user.fullname.should == "Tobias Fünke"
100
+ end
101
+ end
102
+
103
+ describe :scope do
63
104
  before do
64
105
  Her::API.setup :url => "https://api.example.com" do |builder|
65
106
  builder.use Her::Middleware::FirstLevelParseJSON
66
107
  builder.adapter :test do |stub|
67
- stub.get("/users?page=2&per_page=1") { |env| ok! [{ :id => 2, :fullname => "Lindsay Fünke" }] }
108
+ stub.get("/users?what=4&where=3") { |env| ok! [{ :id => 3, :fullname => "Maeby Fünke" }] }
109
+ stub.get("/users?what=2") { |env| ok! [{ :id => 2, :fullname => "Lindsay Fünke" }] }
110
+ stub.get("/users?where=6") { |env| ok! [{ :id => 4, :fullname => "Tobias Fünke" }] }
68
111
  end
69
112
  end
70
113
 
71
- spawn_model "Foo::User"
114
+ spawn_model 'Foo::User' do
115
+ scope :foo, lambda { |v| where(:what => v) }
116
+ scope :bar, lambda { |v| where(:where => v) }
117
+ scope :baz, lambda { bar(6) }
118
+ end
72
119
  end
73
120
 
74
- it "passes the pagination parameters" do
75
- @user = Foo::User.page(2).per_page(1).first
121
+ it "passes query parameters" do
122
+ @user = Foo::User.foo(2).first
76
123
  @user.id.should == 2
77
124
  end
78
- end
79
125
 
80
- describe :build do
81
- before { spawn_model "Foo::User" }
126
+ it "passes multiple query parameters" do
127
+ @user = Foo::User.foo(4).bar(3).first
128
+ @user.id.should == 3
129
+ end
82
130
 
83
- it "handles new resource with build" do
84
- @new_user = Foo::User.where(:fullname => "Tobias Fünke").build
85
- @new_user.new?.should be_true
86
- @new_user.fullname.should == "Tobias Fünke"
131
+ it "handles embedded scopes" do
132
+ @user = Foo::User.baz.first
133
+ @user.id.should == 4
87
134
  end
88
135
  end
89
136
  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.1
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-20 00:00:00.000000000 Z
11
+ date: 2013-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake