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 +8 -8
- data/README.md +29 -1
- data/lib/her/model/associations/belongs_to_association.rb +22 -0
- data/lib/her/model/associations/has_many_association.rb +22 -0
- data/lib/her/model/associations/has_one_association.rb +21 -0
- data/lib/her/model/associations.rb +4 -49
- data/lib/her/model/http.rb +2 -1
- data/lib/her/model/orm.rb +22 -4
- data/lib/her/model/relation.rb +9 -11
- data/lib/her/model.rb +1 -1
- data/lib/her/version.rb +1 -1
- data/spec/model/associations_spec.rb +54 -45
- data/spec/model/orm_spec.rb +0 -10
- data/spec/model/relation_spec.rb +74 -27
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
N2RhYjBkODQ3ZjE1NmNlM2VmZTExNWJkNDU3NTI4YjI3OGNhN2RiZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OTBmNmRjM2I2NDE3ZmQ4MjZmZDJjMzI5NjQ2ODJjODUwMDczNWRiNw==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YzZhZjExMWI1MmYwYTUwODU4OGM0OWM2OWI2NDgyOGU5OTM0ZWViY2UzYjdh
|
10
|
+
NTZmNTI1YmFhMDlmMzQ5YTBmZWQzMWZhZjRlOWUwMzcxZTY0NWQ5YWQ2ODk3
|
11
|
+
ZjA0M2ExMmMyM2UxNjMwYzJjOWNjNmVjYzY4MDJkZmM3NjM2NzY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/her/model/http.rb
CHANGED
@@ -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
|
-
|
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
|
-
#
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
data/lib/her/model/relation.rb
CHANGED
@@ -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
|
21
|
-
|
22
|
-
|
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
|
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
|
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
@@ -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
|
7
|
-
|
8
|
-
end
|
6
|
+
before { spawn_model "Foo::User" }
|
7
|
+
subject { Foo::User.associations }
|
9
8
|
|
10
|
-
|
11
|
-
Foo::User.has_many :comments
|
12
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
22
|
-
Foo::User.has_one :category
|
23
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
33
|
-
Foo::User.belongs_to :organization
|
34
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
46
|
-
|
47
|
-
end
|
53
|
+
before { spawn_model "Foo::User" }
|
54
|
+
subject { Foo::User.associations }
|
48
55
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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 "
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
data/spec/model/orm_spec.rb
CHANGED
@@ -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
|
data/spec/model/relation_spec.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
16
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
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?
|
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
|
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
|
75
|
-
@user = Foo::User.
|
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
|
-
|
81
|
-
|
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
|
84
|
-
@
|
85
|
-
@
|
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:
|
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-
|
11
|
+
date: 2013-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|