her 0.6 → 0.6.1

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