mongomodel 0.1.6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,21 @@
|
|
1
|
+
module MongoModel
|
2
|
+
class Scope
|
3
|
+
module DynamicFinders
|
4
|
+
def respond_to?(method_id, include_private = false)
|
5
|
+
if DynamicFinder.match(self, method_id)
|
6
|
+
true
|
7
|
+
else
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method_id, *args, &block)
|
13
|
+
if finder = DynamicFinder.match(self, method_id)
|
14
|
+
finder.execute(*args)
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module MongoModel
|
2
|
+
class Scope
|
3
|
+
module FinderMethods
|
4
|
+
def find(*ids, &block)
|
5
|
+
if block_given?
|
6
|
+
to_a.find(&block)
|
7
|
+
else
|
8
|
+
ids.flatten!
|
9
|
+
|
10
|
+
case ids.size
|
11
|
+
when 0
|
12
|
+
raise ArgumentError, "At least one id must be specified"
|
13
|
+
when 1
|
14
|
+
id = ids.first.to_s
|
15
|
+
where(:id => id).first || raise(DocumentNotFound, "Couldn't find document with id: #{id}")
|
16
|
+
else
|
17
|
+
docs = where(:id.in => ids.map { |id| id.to_s }).to_a
|
18
|
+
raise DocumentNotFound if docs.size != ids.size
|
19
|
+
docs.sort_by { |doc| ids.index(doc.id) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def first
|
25
|
+
if loaded?
|
26
|
+
@documents.first
|
27
|
+
else
|
28
|
+
@first ||= limit(1).to_a[0]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def last
|
33
|
+
if loaded?
|
34
|
+
@documents.last
|
35
|
+
else
|
36
|
+
@last ||= reverse_order.limit(1).to_a[0]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def all
|
41
|
+
to_a
|
42
|
+
end
|
43
|
+
|
44
|
+
def exists?(id)
|
45
|
+
where(:id => id).any?
|
46
|
+
end
|
47
|
+
|
48
|
+
def apply_finder_options(options={})
|
49
|
+
result = clone
|
50
|
+
|
51
|
+
result = result.where(options[:conditions]) if options[:conditions]
|
52
|
+
result = result.order(options[:order]) if options[:order]
|
53
|
+
result = result.select(options[:select]) if options[:select]
|
54
|
+
result = result.limit(options[:limit]) if options[:limit]
|
55
|
+
result = result.offset(options[:offset]) if options[:offset]
|
56
|
+
|
57
|
+
result
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module MongoModel
|
2
|
+
class Scope
|
3
|
+
module QueryMethods
|
4
|
+
def initialize(*)
|
5
|
+
SINGLE_VALUE_METHODS.each { |m| instance_variable_set("@#{m}_value", nil) }
|
6
|
+
MULTI_VALUE_METHODS.each { |m| instance_variable_set("@#{m}_values", []) }
|
7
|
+
end
|
8
|
+
|
9
|
+
MULTI_VALUE_METHODS.each do |query_method|
|
10
|
+
attr_accessor :"#{query_method}_values"
|
11
|
+
|
12
|
+
class_eval <<-CEVAL, __FILE__
|
13
|
+
def #{query_method}(*args, &block)
|
14
|
+
new_scope = clone
|
15
|
+
value = Array.wrap(args.flatten).reject {|x| x.blank? }
|
16
|
+
new_scope.#{query_method}_values += value if value.present?
|
17
|
+
new_scope
|
18
|
+
end
|
19
|
+
CEVAL
|
20
|
+
end
|
21
|
+
|
22
|
+
SINGLE_VALUE_METHODS.each do |query_method|
|
23
|
+
attr_accessor :"#{query_method}_value"
|
24
|
+
|
25
|
+
class_eval <<-CEVAL, __FILE__
|
26
|
+
def #{query_method}(value, &block)
|
27
|
+
new_scope = clone
|
28
|
+
new_scope.#{query_method}_value = value
|
29
|
+
new_scope
|
30
|
+
end
|
31
|
+
CEVAL
|
32
|
+
end
|
33
|
+
|
34
|
+
def reverse_order
|
35
|
+
if order_values.empty?
|
36
|
+
order(:id.desc)
|
37
|
+
else
|
38
|
+
except(:order).order(MongoOrder.parse(order_values).reverse.to_a)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module MongoModel
|
2
|
+
class Scope
|
3
|
+
module SpawnMethods
|
4
|
+
def merge(scope)
|
5
|
+
result = clone
|
6
|
+
|
7
|
+
MULTI_VALUE_METHODS.each do |method|
|
8
|
+
values = send(:"#{method}_values") + scope.send(:"#{method}_values")
|
9
|
+
result.send(:"#{method}_values=", values.uniq)
|
10
|
+
end
|
11
|
+
|
12
|
+
SINGLE_VALUE_METHODS.each do |method|
|
13
|
+
value = scope.send(:"#{method}_value")
|
14
|
+
result.send(:"#{method}_value=", value) if value
|
15
|
+
end
|
16
|
+
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
def except(*exceptions)
|
21
|
+
result = self.class.new(klass)
|
22
|
+
|
23
|
+
MULTI_VALUE_METHODS.each do |method|
|
24
|
+
result.send(:"#{method}_values=", send(:"#{method}_values")) unless exceptions.include?(method)
|
25
|
+
end
|
26
|
+
|
27
|
+
SINGLE_VALUE_METHODS.each do |method|
|
28
|
+
result.send(:"#{method}_value=", send(:"#{method}_value")) unless exceptions.include?(method)
|
29
|
+
end
|
30
|
+
|
31
|
+
result
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/mongomodel/version.rb
CHANGED
data/mongomodel.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mongomodel}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Sam Pohlenz"]
|
12
|
-
s.date = %q{2010-04-
|
12
|
+
s.date = %q{2010-04-21}
|
13
13
|
s.default_executable = %q{console}
|
14
14
|
s.description = %q{MongoModel is a MongoDB ORM for Ruby/Rails similar to ActiveRecord and DataMapper.}
|
15
15
|
s.email = %q{sam@sampohlenz.com}
|
@@ -59,7 +59,6 @@ Gem::Specification.new do |s|
|
|
59
59
|
"lib/mongomodel/document.rb",
|
60
60
|
"lib/mongomodel/document/callbacks.rb",
|
61
61
|
"lib/mongomodel/document/dynamic_finders.rb",
|
62
|
-
"lib/mongomodel/document/finders.rb",
|
63
62
|
"lib/mongomodel/document/indexes.rb",
|
64
63
|
"lib/mongomodel/document/optimistic_locking.rb",
|
65
64
|
"lib/mongomodel/document/persistence.rb",
|
@@ -71,8 +70,16 @@ Gem::Specification.new do |s|
|
|
71
70
|
"lib/mongomodel/support/collection.rb",
|
72
71
|
"lib/mongomodel/support/configuration.rb",
|
73
72
|
"lib/mongomodel/support/core_extensions.rb",
|
73
|
+
"lib/mongomodel/support/dynamic_finder.rb",
|
74
74
|
"lib/mongomodel/support/exceptions.rb",
|
75
|
+
"lib/mongomodel/support/mongo_operator.rb",
|
75
76
|
"lib/mongomodel/support/mongo_options.rb",
|
77
|
+
"lib/mongomodel/support/mongo_order.rb",
|
78
|
+
"lib/mongomodel/support/scope.rb",
|
79
|
+
"lib/mongomodel/support/scope/dynamic_finders.rb",
|
80
|
+
"lib/mongomodel/support/scope/finder_methods.rb",
|
81
|
+
"lib/mongomodel/support/scope/query_methods.rb",
|
82
|
+
"lib/mongomodel/support/scope/spawn_methods.rb",
|
76
83
|
"lib/mongomodel/support/types.rb",
|
77
84
|
"lib/mongomodel/support/types/array.rb",
|
78
85
|
"lib/mongomodel/support/types/boolean.rb",
|
@@ -120,16 +127,21 @@ Gem::Specification.new do |s|
|
|
120
127
|
"spec/mongomodel/embedded_document_spec.rb",
|
121
128
|
"spec/mongomodel/mongomodel_spec.rb",
|
122
129
|
"spec/mongomodel/support/collection_spec.rb",
|
130
|
+
"spec/mongomodel/support/mongo_operator_spec.rb",
|
123
131
|
"spec/mongomodel/support/mongo_options_spec.rb",
|
132
|
+
"spec/mongomodel/support/mongo_order_spec.rb",
|
124
133
|
"spec/mongomodel/support/property_spec.rb",
|
134
|
+
"spec/mongomodel/support/scope_spec.rb",
|
125
135
|
"spec/spec.opts",
|
126
136
|
"spec/spec_helper.rb",
|
127
137
|
"spec/specdoc.opts",
|
128
138
|
"spec/support/callbacks.rb",
|
129
139
|
"spec/support/helpers/define_class.rb",
|
140
|
+
"spec/support/helpers/document_finder_stubs.rb",
|
130
141
|
"spec/support/helpers/specs_for.rb",
|
131
142
|
"spec/support/matchers/be_a_subclass_of.rb",
|
132
143
|
"spec/support/matchers/be_truthy.rb",
|
144
|
+
"spec/support/matchers/find_with.rb",
|
133
145
|
"spec/support/matchers/respond_to_boolean.rb",
|
134
146
|
"spec/support/matchers/run_callbacks.rb",
|
135
147
|
"spec/support/models.rb",
|
@@ -174,14 +186,19 @@ Gem::Specification.new do |s|
|
|
174
186
|
"spec/mongomodel/embedded_document_spec.rb",
|
175
187
|
"spec/mongomodel/mongomodel_spec.rb",
|
176
188
|
"spec/mongomodel/support/collection_spec.rb",
|
189
|
+
"spec/mongomodel/support/mongo_operator_spec.rb",
|
177
190
|
"spec/mongomodel/support/mongo_options_spec.rb",
|
191
|
+
"spec/mongomodel/support/mongo_order_spec.rb",
|
178
192
|
"spec/mongomodel/support/property_spec.rb",
|
193
|
+
"spec/mongomodel/support/scope_spec.rb",
|
179
194
|
"spec/spec_helper.rb",
|
180
195
|
"spec/support/callbacks.rb",
|
181
196
|
"spec/support/helpers/define_class.rb",
|
197
|
+
"spec/support/helpers/document_finder_stubs.rb",
|
182
198
|
"spec/support/helpers/specs_for.rb",
|
183
199
|
"spec/support/matchers/be_a_subclass_of.rb",
|
184
200
|
"spec/support/matchers/be_truthy.rb",
|
201
|
+
"spec/support/matchers/find_with.rb",
|
185
202
|
"spec/support/matchers/respond_to_boolean.rb",
|
186
203
|
"spec/support/matchers/run_callbacks.rb",
|
187
204
|
"spec/support/models.rb",
|
@@ -82,150 +82,6 @@ module MongoModel
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
85
|
-
|
86
|
-
describe "first" do
|
87
|
-
context "documents exist" do
|
88
|
-
subject { User.find(:first) }
|
89
|
-
|
90
|
-
it "should return the first document" do
|
91
|
-
subject.id.should == '1'
|
92
|
-
subject.name.should == 'Fred'
|
93
|
-
end
|
94
|
-
|
95
|
-
it "should be aliased as #first" do
|
96
|
-
User.first.should == subject
|
97
|
-
end
|
98
|
-
|
99
|
-
context "with order" do
|
100
|
-
it "should find first document by order" do
|
101
|
-
User.find(:first, :order => :name.asc).name.should == 'Alistair'
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context "no documents" do
|
107
|
-
before(:each) { User.collection.remove }
|
108
|
-
|
109
|
-
it "should return nil" do
|
110
|
-
User.find(:first).should be_nil
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
describe "last" do
|
116
|
-
context "documents exist" do
|
117
|
-
subject { User.find(:last) }
|
118
|
-
|
119
|
-
it "should return the last document (by id)" do
|
120
|
-
subject.id.should == '3'
|
121
|
-
subject.name.should == 'Barney'
|
122
|
-
end
|
123
|
-
|
124
|
-
it "should be aliased as #last" do
|
125
|
-
User.last.should == subject
|
126
|
-
end
|
127
|
-
|
128
|
-
context "with order" do
|
129
|
-
it "should find last document by order" do
|
130
|
-
User.find(:last, :order => :name.asc).name.should == 'Fred'
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context "no documents" do
|
136
|
-
before(:each) { User.collection.remove }
|
137
|
-
|
138
|
-
it "should return nil" do
|
139
|
-
User.find(:last).should be_nil
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
describe "all" do
|
145
|
-
subject { User.find(:all, :order => 'id ASC') }
|
146
|
-
|
147
|
-
it "should return all documents as User instances" do
|
148
|
-
subject.should have(3).users
|
149
|
-
subject.each { |d| d.should be_a(User) }
|
150
|
-
end
|
151
|
-
|
152
|
-
it "should load attributes for each document" do
|
153
|
-
subject[0].name.should == 'Fred'
|
154
|
-
subject[1].name.should == 'Alistair'
|
155
|
-
subject[2].name.should == 'Barney'
|
156
|
-
end
|
157
|
-
|
158
|
-
it "should be aliased as #all" do
|
159
|
-
User.all.should == subject
|
160
|
-
end
|
161
|
-
|
162
|
-
context "with exact-match conditions" do
|
163
|
-
subject { User.find(:all, :conditions => { :name => 'Alistair' }) }
|
164
|
-
|
165
|
-
it "should only return documents matching conditions" do
|
166
|
-
subject.should have(1).user
|
167
|
-
subject[0].name.should == 'Alistair'
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
context "with inequality conditions" do
|
172
|
-
subject { User.find(:all, :conditions => { :age.lt => 21 }) }
|
173
|
-
|
174
|
-
it "should only return documents matching conditions" do
|
175
|
-
subject.should have(2).users
|
176
|
-
subject[0].name.should == 'Alistair'
|
177
|
-
subject[1].name.should == 'Barney'
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
describe "#count" do
|
184
|
-
before(:each) do
|
185
|
-
5.times { User.create(:age => 18) }
|
186
|
-
7.times { User.create(:age => 42) }
|
187
|
-
3.times { NonUser.create }
|
188
|
-
end
|
189
|
-
|
190
|
-
context "without arguments" do
|
191
|
-
it "should return the count for that particular model" do
|
192
|
-
User.count.should == 12
|
193
|
-
NonUser.count.should == 3
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
context "with conditions" do
|
198
|
-
it "should return the count for the model that match the conditions" do
|
199
|
-
User.count(:age => 18).should == 5
|
200
|
-
User.count(:age.gte => 18).should == 12
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
describe "#exists?" do
|
206
|
-
before(:each) do
|
207
|
-
User.create(:id => 'user-1', :name => 'Test', :age => 10)
|
208
|
-
end
|
209
|
-
|
210
|
-
context "by id" do
|
211
|
-
it "should return true if the document exists" do
|
212
|
-
User.exists?('user-1').should == true
|
213
|
-
end
|
214
|
-
|
215
|
-
it "should return false if the document does not exist" do
|
216
|
-
User.exists?('user-2').should == false
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
context "by conditions" do
|
221
|
-
it "should return true if the document exists" do
|
222
|
-
User.exists?(:name => 'Test').should == true
|
223
|
-
end
|
224
|
-
|
225
|
-
it "should return false if the document does not exist" do
|
226
|
-
User.exists?(:name => 'Nonexistant').should == false
|
227
|
-
end
|
228
|
-
end
|
229
85
|
end
|
230
86
|
end
|
231
87
|
end
|
@@ -60,13 +60,13 @@ module MongoModel
|
|
60
60
|
describe "#_find" do
|
61
61
|
it "should run ensure_indexes!" do
|
62
62
|
Article.should_receive(:ensure_indexes!)
|
63
|
-
Article.
|
63
|
+
Article.first
|
64
64
|
end
|
65
65
|
|
66
66
|
it "should rerun ensure_indexes! if indexes are initialized" do
|
67
67
|
Article.ensure_indexes!
|
68
68
|
Article.should_not_receive(:ensure_indexes!)
|
69
|
-
Article.
|
69
|
+
Article.first
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
@@ -183,14 +183,7 @@ module MongoModel
|
|
183
183
|
User.exists?('user-1').should be_false
|
184
184
|
User.exists?('user-2').should be_true
|
185
185
|
end
|
186
|
-
|
187
|
-
it "should delete by conditions" do
|
188
|
-
User.delete(:age.gt => 15)
|
189
|
-
|
190
|
-
User.exists?('user-2').should be_false
|
191
|
-
User.exists?('user-1').should be_true
|
192
|
-
end
|
193
|
-
|
186
|
+
|
194
187
|
it "should delete by multiple ids in array" do
|
195
188
|
User.delete(['user-1', 'user-2'])
|
196
189
|
|
@@ -270,13 +263,6 @@ module MongoModel
|
|
270
263
|
User.exists?('user-2').should be_true
|
271
264
|
end
|
272
265
|
|
273
|
-
it "should destroy by conditions" do
|
274
|
-
User.destroy(:age.gt => 15)
|
275
|
-
|
276
|
-
User.exists?('user-2').should be_false
|
277
|
-
User.exists?('user-1').should be_true
|
278
|
-
end
|
279
|
-
|
280
266
|
it "should destroy by multiple ids in array" do
|
281
267
|
User.destroy(['user-1', 'user-2'])
|
282
268
|
|