mongomodel 0.1.6 → 0.2.0
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.
- data/README.md +6 -0
- data/bin/console +4 -4
- data/lib/mongomodel.rb +4 -4
- data/lib/mongomodel/concerns/associations/base/definition.rb +4 -0
- data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +7 -16
- data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +6 -12
- data/lib/mongomodel/concerns/attributes.rb +1 -7
- data/lib/mongomodel/document.rb +0 -1
- data/lib/mongomodel/document/dynamic_finders.rb +2 -69
- data/lib/mongomodel/document/indexes.rb +0 -6
- data/lib/mongomodel/document/persistence.rb +1 -21
- data/lib/mongomodel/document/scopes.rb +59 -135
- data/lib/mongomodel/document/validations/uniqueness.rb +7 -5
- data/lib/mongomodel/support/dynamic_finder.rb +68 -0
- data/lib/mongomodel/support/mongo_operator.rb +29 -0
- data/lib/mongomodel/support/mongo_options.rb +0 -101
- data/lib/mongomodel/support/mongo_order.rb +78 -0
- data/lib/mongomodel/support/scope.rb +186 -0
- data/lib/mongomodel/support/scope/dynamic_finders.rb +21 -0
- data/lib/mongomodel/support/scope/finder_methods.rb +61 -0
- data/lib/mongomodel/support/scope/query_methods.rb +43 -0
- data/lib/mongomodel/support/scope/spawn_methods.rb +35 -0
- data/lib/mongomodel/version.rb +1 -1
- data/mongomodel.gemspec +20 -3
- data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +1 -1
- data/spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb +1 -1
- data/spec/mongomodel/document/dynamic_finders_spec.rb +0 -1
- data/spec/mongomodel/document/finders_spec.rb +0 -144
- data/spec/mongomodel/document/indexes_spec.rb +2 -2
- data/spec/mongomodel/document/persistence_spec.rb +1 -15
- data/spec/mongomodel/document/scopes_spec.rb +64 -167
- data/spec/mongomodel/support/mongo_operator_spec.rb +29 -0
- data/spec/mongomodel/support/mongo_options_spec.rb +0 -150
- data/spec/mongomodel/support/mongo_order_spec.rb +127 -0
- data/spec/mongomodel/support/scope_spec.rb +932 -0
- data/spec/support/helpers/document_finder_stubs.rb +40 -0
- data/spec/support/matchers/find_with.rb +36 -0
- metadata +22 -5
- data/lib/mongomodel/document/finders.rb +0 -82
@@ -2,202 +2,99 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module MongoModel
|
4
4
|
describe Document do
|
5
|
-
|
6
|
-
class << MongoModel::Document
|
7
|
-
public :with_scope, :with_exclusive_scope
|
8
|
-
|
9
|
-
def should_find_with(hash)
|
10
|
-
should_receive(:_find).with(hash)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
5
|
+
define_class(:Post, Document)
|
14
6
|
|
15
|
-
describe "#
|
16
|
-
|
17
|
-
property :title, String
|
18
|
-
property :author, String
|
19
|
-
end
|
7
|
+
describe "#scoped" do
|
8
|
+
subject { Post.scoped }
|
20
9
|
|
21
|
-
it
|
22
|
-
Article.should_find_with({
|
23
|
-
:conditions => { :title => 'Hello World', :author => 'Editor' },
|
24
|
-
:limit => 10, :order => 'title ASC'
|
25
|
-
})
|
26
|
-
|
27
|
-
Article.with_scope(:find => { :conditions => { :title => 'Hello World' }, :limit => 10 }) do
|
28
|
-
Article.find(:all, :conditions => { :author => 'Editor' }, :order => 'title ASC')
|
29
|
-
end
|
30
|
-
end
|
10
|
+
it { should be_an_instance_of(MongoModel::Scope) }
|
31
11
|
|
32
|
-
it "should
|
33
|
-
|
34
|
-
:conditions => { :title => 'Hello World', :author => 'Editor' },
|
35
|
-
:limit => 10, :order => 'title ASC', :select => :title
|
36
|
-
})
|
37
|
-
|
38
|
-
Article.with_scope(:find => { :conditions => { :title => 'Hello World' }}) do
|
39
|
-
Article.with_scope(:find => { :select => :title, :order => 'title ASC' }) do
|
40
|
-
Article.find(:all, :conditions => { :author => 'Editor' }, :limit => 10)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
describe "#with_exclusive_scope" do
|
47
|
-
define_class(:Article, Document) do
|
48
|
-
property :title, String
|
49
|
-
property :author, String
|
12
|
+
it "should set the target class" do
|
13
|
+
subject.klass.should == Post
|
50
14
|
end
|
51
15
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
16
|
+
context "with default scope(s) set" do
|
17
|
+
define_class(:Post, Document) do
|
18
|
+
default_scope order(:title.asc)
|
19
|
+
default_scope limit(5)
|
20
|
+
end
|
57
21
|
|
58
|
-
|
59
|
-
|
22
|
+
it "should return the default scope" do
|
23
|
+
scope = Post.scoped
|
24
|
+
scope.order_values.should == [:title.asc]
|
25
|
+
scope.limit_value.should == 5
|
60
26
|
end
|
61
27
|
end
|
62
28
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
Article.with_scope(:find => { :conditions => { :title => 'Hello World' }}) do
|
70
|
-
Article.with_exclusive_scope(:find => { :select => :title, :order => 'title ASC' }) do
|
71
|
-
Article.find(:all, :conditions => { :author => 'Editor' }, :limit => 10)
|
29
|
+
context "within a with_scope block" do
|
30
|
+
it "should return the current scope" do
|
31
|
+
Post.class_eval do
|
32
|
+
with_scope(where(:published => true)) do
|
33
|
+
scoped.where_values.should == [{:published => true}]
|
34
|
+
end
|
72
35
|
end
|
73
36
|
end
|
74
37
|
end
|
75
|
-
end
|
76
|
-
|
77
|
-
describe "#default_scope" do
|
78
|
-
define_class(:User, Document) do
|
79
|
-
property :name, String
|
80
|
-
property :age, Integer
|
81
|
-
|
82
|
-
default_scope :conditions => { :age.gt => 18 }
|
83
|
-
end
|
84
|
-
|
85
|
-
it "should merge with other scopes" do
|
86
|
-
User.should_find_with(:conditions => { :age.gt => 18 })
|
87
|
-
User.find(:all)
|
88
|
-
end
|
89
38
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
39
|
+
context "within nested with_scope blocks" do
|
40
|
+
it "should return the merged scope" do
|
41
|
+
Post.class_eval do
|
42
|
+
with_scope(where(:published => true)) do
|
43
|
+
with_scope(limit(5)) do
|
44
|
+
scoped.where_values.should == [{:published => true}]
|
45
|
+
scoped.limit_value.should == 5
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
94
49
|
end
|
95
50
|
end
|
96
51
|
end
|
97
52
|
|
98
|
-
describe "#
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
named_scope :published, :conditions => { :published => true }
|
105
|
-
named_scope :latest, lambda { |num| { :limit => num, :order => 'created_at DESC' } }
|
106
|
-
named_scope :exclusive, :exclusive => true
|
107
|
-
end
|
108
|
-
|
109
|
-
define_class(:SpecialPost, :Post)
|
110
|
-
|
111
|
-
it "should create scope methods" do
|
112
|
-
Post.published.options_for(:find).should == { :conditions => { :published => true} }
|
113
|
-
Post.latest(5).options_for(:find).should == { :limit => 5, :order => 'created_at DESC' }
|
114
|
-
Post.exclusive.options_for(:find).should == {}
|
115
|
-
end
|
116
|
-
|
117
|
-
it "should find using scope options" do
|
118
|
-
Post.should_find_with(:conditions => { :published => true })
|
119
|
-
Post.published.find(:all)
|
120
|
-
|
121
|
-
Post.should_find_with(:limit => 5, :order => 'created_at DESC')
|
122
|
-
Post.latest(5).find(:all)
|
123
|
-
end
|
124
|
-
|
125
|
-
it "should find by id using scope conditions" do
|
126
|
-
@post1 = Post.create(:id => 'post-1', :published => true)
|
127
|
-
@post2 = Post.create(:id => 'post-2', :published => false)
|
128
|
-
|
129
|
-
Post.published.find('post-1').should == @post1
|
130
|
-
lambda { Post.published.find('post-2') }.should raise_error(DocumentNotFound)
|
131
|
-
end
|
132
|
-
|
133
|
-
it "should count using scope options" do
|
134
|
-
8.times { Post.create(:published => true) }
|
135
|
-
3.times { Post.create(:published => false) }
|
53
|
+
describe "#scope" do
|
54
|
+
it "should create a method returning the scope" do
|
55
|
+
Post.class_eval do
|
56
|
+
scope :published, where(:published => true)
|
57
|
+
end
|
136
58
|
|
137
|
-
Post.
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
it "should be chainable" do
|
142
|
-
Post.should_find_with(:conditions => { :published => true }, :limit => 5, :order => 'created_at DESC')
|
143
|
-
Post.published.latest(5).all
|
144
|
-
end
|
145
|
-
|
146
|
-
it "should inherit named scopes from parent classes" do
|
147
|
-
SpecialPost.published.options.should == Post.published.options
|
59
|
+
scope = Post.published
|
60
|
+
scope.should be_an_instance_of(MongoModel::Scope)
|
61
|
+
scope.where_values.should == [{:published => true}]
|
148
62
|
end
|
149
63
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
Post.published.exclusive.all
|
64
|
+
it "should create parameterized method returning the scope when given a lambda" do
|
65
|
+
Post.class_eval do
|
66
|
+
scope :latest, lambda { |num| order(:created_at.desc).limit(num) }
|
154
67
|
end
|
68
|
+
|
69
|
+
scope = Post.latest(4)
|
70
|
+
scope.order_values.should == [:created_at.desc]
|
71
|
+
scope.limit_value.should == 4
|
155
72
|
end
|
156
73
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
74
|
+
it "should allow existing scopes to be built upon" do
|
75
|
+
Post.class_eval do
|
76
|
+
scope :recent, order(:created_at.desc).limit(5)
|
77
|
+
scope :recently_published, recent.where(:published => true)
|
161
78
|
end
|
79
|
+
|
80
|
+
scope = Post.recently_published
|
81
|
+
scope.where_values.should == [{:published => true}]
|
82
|
+
scope.order_values.should == [:created_at.desc]
|
83
|
+
scope.limit_value.should == 5
|
162
84
|
end
|
163
85
|
end
|
164
|
-
end
|
165
|
-
|
166
|
-
module DocumentExtensions
|
167
|
-
describe Scope do
|
168
|
-
before(:each) do
|
169
|
-
@model = mock('Model')
|
170
|
-
end
|
171
86
|
|
172
|
-
|
173
|
-
|
174
|
-
scope
|
175
|
-
|
176
|
-
|
177
|
-
it "should deep merge options to create new scopes" do
|
178
|
-
original = Scope.new(@model, :find => { :conditions => { :foo => 'bar' } })
|
179
|
-
merged = original.merge(:find => { :conditions => { :baz => 123 }, :limit => 5 })
|
180
|
-
|
181
|
-
original.options.should == { :find => { :conditions => { :foo => 'bar' } } }
|
182
|
-
merged.options.should == { :find => { :conditions => { :foo => 'bar', :baz => 123 }, :limit => 5 } }
|
87
|
+
describe "named scopes" do
|
88
|
+
define_class(:Post, Document) do
|
89
|
+
scope :published, where(:published => true)
|
90
|
+
scope :recent, order(:created_at.desc).limit(5)
|
183
91
|
end
|
184
|
-
|
185
|
-
it "should deep merge options to update an existing scope" do
|
186
|
-
scope = Scope.new(@model, :find => { :conditions => { :foo => 'bar' } })
|
187
|
-
merged = scope.merge!(:find => { :conditions => { :baz => 123 }, :limit => 5 })
|
188
92
|
|
189
|
-
|
190
|
-
scope.
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
scope = Scope.new(@model, :find => { :conditions => { :foo => 'bar' } })
|
195
|
-
scope.options_for(:find).should == { :conditions => { :foo => 'bar' } }
|
196
|
-
end
|
197
|
-
|
198
|
-
it "should have default find options" do
|
199
|
-
scope = Scope.new(@model)
|
200
|
-
scope.options_for(:find).should == {}
|
93
|
+
it "should be chainable" do
|
94
|
+
scope = Post.published.recent
|
95
|
+
scope.where_values.should == [{:published => true}]
|
96
|
+
scope.order_values.should == [:created_at.desc]
|
97
|
+
scope.limit_value.should == 5
|
201
98
|
end
|
202
99
|
end
|
203
100
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MongoModel
|
4
|
+
describe MongoOperator do
|
5
|
+
subject { MongoOperator.new(:age, :gt) }
|
6
|
+
|
7
|
+
it "should convert to mongo selector" do
|
8
|
+
subject.to_mongo_selector(14).should == { '$gt' => 14 }
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should be equal to a MongoOperator with the same field and operator" do
|
12
|
+
subject.should == MongoOperator.new(:age, :gt)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should not be equal to a MongoOperator with a different field/operator" do
|
16
|
+
subject.should_not == MongoOperator.new(:age, :lte)
|
17
|
+
subject.should_not == MongoOperator.new(:date, :gt)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be created from symbol methods" do
|
21
|
+
:age.gt.should == MongoOperator.new(:age, :gt)
|
22
|
+
:date.lte.should == MongoOperator.new(:date, :lte)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be equal within a hash" do
|
26
|
+
{ :age.gt => 10 }.should == { :age.gt => 10 }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -142,154 +142,4 @@ module MongoModel
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
end
|
145
|
-
|
146
|
-
describe MongoOrder do
|
147
|
-
def c(field, order)
|
148
|
-
MongoOrder::Clause.new(field, order)
|
149
|
-
end
|
150
|
-
|
151
|
-
subject { MongoOrder.new(c(:name, :ascending), c(:age, :descending)) }
|
152
|
-
|
153
|
-
it "should convert to string" do
|
154
|
-
subject.to_s.should == "name ascending, age descending"
|
155
|
-
end
|
156
|
-
|
157
|
-
describe "#to_sort" do
|
158
|
-
it "should convert to mongo sort array" do
|
159
|
-
model = mock('model', :properties => mock('properties', :[] => nil))
|
160
|
-
subject.to_sort(model).should == [['name', :ascending], ['age', :descending]]
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
it "should be reversable" do
|
165
|
-
subject.reverse.should == MongoOrder.new(c(:name, :descending), c(:age, :ascending))
|
166
|
-
end
|
167
|
-
|
168
|
-
it "should equal another order object with identical clauses" do
|
169
|
-
subject.should == MongoOrder.new(c(:name, :ascending), c(:age, :descending))
|
170
|
-
end
|
171
|
-
|
172
|
-
it "should equal another order object with different clauses" do
|
173
|
-
subject.should_not == MongoOrder.new(c(:name, :ascending))
|
174
|
-
subject.should_not == MongoOrder.new(c(:age, :ascending), c(:name, :ascending))
|
175
|
-
end
|
176
|
-
|
177
|
-
describe "#parse" do
|
178
|
-
it "should not change a MongoOrder" do
|
179
|
-
MongoOrder.parse(subject).should == subject
|
180
|
-
end
|
181
|
-
|
182
|
-
it "should convert individual clause to MongoOrder" do
|
183
|
-
MongoOrder.parse(c(:name, :ascending)).should == MongoOrder.new(c(:name, :ascending))
|
184
|
-
end
|
185
|
-
|
186
|
-
it "should convert symbol to MongoOrder" do
|
187
|
-
MongoOrder.parse(:name).should == MongoOrder.new(c(:name, :ascending))
|
188
|
-
end
|
189
|
-
|
190
|
-
it "should convert array of clauses to MongoOrder" do
|
191
|
-
MongoOrder.parse([c(:name, :ascending), c(:age, :descending)]).should == MongoOrder.new(c(:name, :ascending), c(:age, :descending))
|
192
|
-
end
|
193
|
-
|
194
|
-
it "should convert array of symbols to MongoOrder" do
|
195
|
-
MongoOrder.parse([:name, :age]).should == MongoOrder.new(c(:name, :ascending), c(:age, :ascending))
|
196
|
-
end
|
197
|
-
|
198
|
-
it "should convert array of strings to MongoOrder" do
|
199
|
-
MongoOrder.parse(['name ASC', 'age DESC']).should == MongoOrder.new(c(:name, :ascending), c(:age, :descending))
|
200
|
-
end
|
201
|
-
|
202
|
-
it "should convert string (no order specified) to MongoOrder" do
|
203
|
-
MongoOrder.parse('name').should == MongoOrder.new(c(:name, :ascending))
|
204
|
-
end
|
205
|
-
|
206
|
-
it "should convert string (single order) to MongoOrder" do
|
207
|
-
MongoOrder.parse('name DESC').should == MongoOrder.new(c(:name, :descending))
|
208
|
-
end
|
209
|
-
|
210
|
-
it "should convert string (multiple orders) to MongoOrder" do
|
211
|
-
MongoOrder.parse('name DESC, age ASC').should == MongoOrder.new(c(:name, :descending), c(:age, :ascending))
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
describe MongoOrder::Clause do
|
217
|
-
subject { MongoOrder::Clause.new(:name, :ascending) }
|
218
|
-
|
219
|
-
it "should convert to string" do
|
220
|
-
subject.to_s.should == "name ascending"
|
221
|
-
end
|
222
|
-
|
223
|
-
it "should equal another clause with the same field and order" do
|
224
|
-
subject.should == MongoOrder::Clause.new(:name, :ascending)
|
225
|
-
end
|
226
|
-
|
227
|
-
it "should equal another clause with a different field or order" do
|
228
|
-
subject.should_not == MongoOrder::Clause.new(:age, :ascending)
|
229
|
-
subject.should_not == MongoOrder::Clause.new(:name, :descending)
|
230
|
-
end
|
231
|
-
|
232
|
-
it "should be reversable" do
|
233
|
-
subject.reverse.should == MongoOrder::Clause.new(:name, :descending)
|
234
|
-
end
|
235
|
-
|
236
|
-
describe "#to_sort" do
|
237
|
-
context "given property" do
|
238
|
-
it "should use property as value to convert to mongo sort" do
|
239
|
-
property = mock('property', :as => '_name')
|
240
|
-
subject.to_sort(property).should == ['_name', :ascending]
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
context "given nil" do
|
245
|
-
it "should convert to mongo sort" do
|
246
|
-
subject.to_sort(nil).should == ['name', :ascending]
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
describe "#parse" do
|
252
|
-
let(:asc) { MongoOrder::Clause.new(:name, :ascending) }
|
253
|
-
let(:desc) { MongoOrder::Clause.new(:name, :descending) }
|
254
|
-
|
255
|
-
it "should create Clause from string (no order)" do
|
256
|
-
MongoOrder::Clause.parse('name').should == asc
|
257
|
-
end
|
258
|
-
|
259
|
-
it "should create Clause from string (with order)" do
|
260
|
-
MongoOrder::Clause.parse('name ASC').should == asc
|
261
|
-
MongoOrder::Clause.parse('name asc').should == asc
|
262
|
-
MongoOrder::Clause.parse('name ascending').should == asc
|
263
|
-
MongoOrder::Clause.parse('name DESC').should == desc
|
264
|
-
MongoOrder::Clause.parse('name desc').should == desc
|
265
|
-
MongoOrder::Clause.parse('name descending').should == desc
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
describe MongoOperator do
|
271
|
-
subject { MongoOperator.new(:age, :gt) }
|
272
|
-
|
273
|
-
it "should convert to mongo selector" do
|
274
|
-
subject.to_mongo_selector(14).should == { '$gt' => 14 }
|
275
|
-
end
|
276
|
-
|
277
|
-
it "should be equal to a MongoOperator with the same field and operator" do
|
278
|
-
subject.should == MongoOperator.new(:age, :gt)
|
279
|
-
end
|
280
|
-
|
281
|
-
it "should not be equal to a MongoOperator with a different field/operator" do
|
282
|
-
subject.should_not == MongoOperator.new(:age, :lte)
|
283
|
-
subject.should_not == MongoOperator.new(:date, :gt)
|
284
|
-
end
|
285
|
-
|
286
|
-
it "should be created from symbol methods" do
|
287
|
-
:age.gt.should == MongoOperator.new(:age, :gt)
|
288
|
-
:date.lte.should == MongoOperator.new(:date, :lte)
|
289
|
-
end
|
290
|
-
|
291
|
-
it "should be equal within a hash" do
|
292
|
-
{ :age.gt => 10 }.should == { :age.gt => 10 }
|
293
|
-
end
|
294
|
-
end
|
295
145
|
end
|