mongomodel 0.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.
Files changed (108) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +34 -0
  3. data/Rakefile +47 -0
  4. data/bin/console +45 -0
  5. data/lib/mongomodel.rb +92 -0
  6. data/lib/mongomodel/attributes/mongo.rb +40 -0
  7. data/lib/mongomodel/attributes/store.rb +30 -0
  8. data/lib/mongomodel/attributes/typecasting.rb +51 -0
  9. data/lib/mongomodel/concerns/abstract_class.rb +17 -0
  10. data/lib/mongomodel/concerns/activemodel.rb +11 -0
  11. data/lib/mongomodel/concerns/associations.rb +103 -0
  12. data/lib/mongomodel/concerns/associations/base/association.rb +33 -0
  13. data/lib/mongomodel/concerns/associations/base/definition.rb +56 -0
  14. data/lib/mongomodel/concerns/associations/base/proxy.rb +58 -0
  15. data/lib/mongomodel/concerns/associations/belongs_to.rb +68 -0
  16. data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +159 -0
  17. data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +175 -0
  18. data/lib/mongomodel/concerns/attribute_methods.rb +55 -0
  19. data/lib/mongomodel/concerns/attribute_methods/before_type_cast.rb +29 -0
  20. data/lib/mongomodel/concerns/attribute_methods/dirty.rb +35 -0
  21. data/lib/mongomodel/concerns/attribute_methods/protected.rb +127 -0
  22. data/lib/mongomodel/concerns/attribute_methods/query.rb +22 -0
  23. data/lib/mongomodel/concerns/attribute_methods/read.rb +29 -0
  24. data/lib/mongomodel/concerns/attribute_methods/write.rb +29 -0
  25. data/lib/mongomodel/concerns/attributes.rb +85 -0
  26. data/lib/mongomodel/concerns/callbacks.rb +294 -0
  27. data/lib/mongomodel/concerns/logging.rb +15 -0
  28. data/lib/mongomodel/concerns/pretty_inspect.rb +29 -0
  29. data/lib/mongomodel/concerns/properties.rb +69 -0
  30. data/lib/mongomodel/concerns/record_status.rb +42 -0
  31. data/lib/mongomodel/concerns/timestamps.rb +32 -0
  32. data/lib/mongomodel/concerns/validations.rb +38 -0
  33. data/lib/mongomodel/concerns/validations/associated.rb +46 -0
  34. data/lib/mongomodel/document.rb +20 -0
  35. data/lib/mongomodel/document/callbacks.rb +46 -0
  36. data/lib/mongomodel/document/dynamic_finders.rb +88 -0
  37. data/lib/mongomodel/document/finders.rb +82 -0
  38. data/lib/mongomodel/document/indexes.rb +91 -0
  39. data/lib/mongomodel/document/optimistic_locking.rb +48 -0
  40. data/lib/mongomodel/document/persistence.rb +143 -0
  41. data/lib/mongomodel/document/scopes.rb +161 -0
  42. data/lib/mongomodel/document/validations.rb +68 -0
  43. data/lib/mongomodel/document/validations/uniqueness.rb +78 -0
  44. data/lib/mongomodel/embedded_document.rb +42 -0
  45. data/lib/mongomodel/locale/en.yml +55 -0
  46. data/lib/mongomodel/support/collection.rb +109 -0
  47. data/lib/mongomodel/support/configuration.rb +35 -0
  48. data/lib/mongomodel/support/core_extensions.rb +10 -0
  49. data/lib/mongomodel/support/exceptions.rb +25 -0
  50. data/lib/mongomodel/support/mongo_options.rb +177 -0
  51. data/lib/mongomodel/support/types.rb +35 -0
  52. data/lib/mongomodel/support/types/array.rb +11 -0
  53. data/lib/mongomodel/support/types/boolean.rb +25 -0
  54. data/lib/mongomodel/support/types/custom.rb +38 -0
  55. data/lib/mongomodel/support/types/date.rb +20 -0
  56. data/lib/mongomodel/support/types/float.rb +13 -0
  57. data/lib/mongomodel/support/types/hash.rb +18 -0
  58. data/lib/mongomodel/support/types/integer.rb +13 -0
  59. data/lib/mongomodel/support/types/object.rb +21 -0
  60. data/lib/mongomodel/support/types/string.rb +9 -0
  61. data/lib/mongomodel/support/types/symbol.rb +9 -0
  62. data/lib/mongomodel/support/types/time.rb +12 -0
  63. data/lib/mongomodel/version.rb +3 -0
  64. data/spec/mongomodel/attributes/store_spec.rb +273 -0
  65. data/spec/mongomodel/concerns/activemodel_spec.rb +61 -0
  66. data/spec/mongomodel/concerns/associations/belongs_to_spec.rb +153 -0
  67. data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +165 -0
  68. data/spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb +192 -0
  69. data/spec/mongomodel/concerns/attribute_methods/before_type_cast_spec.rb +46 -0
  70. data/spec/mongomodel/concerns/attribute_methods/dirty_spec.rb +131 -0
  71. data/spec/mongomodel/concerns/attribute_methods/protected_spec.rb +86 -0
  72. data/spec/mongomodel/concerns/attribute_methods/query_spec.rb +27 -0
  73. data/spec/mongomodel/concerns/attribute_methods/read_spec.rb +52 -0
  74. data/spec/mongomodel/concerns/attribute_methods/write_spec.rb +43 -0
  75. data/spec/mongomodel/concerns/attributes_spec.rb +152 -0
  76. data/spec/mongomodel/concerns/callbacks_spec.rb +90 -0
  77. data/spec/mongomodel/concerns/logging_spec.rb +20 -0
  78. data/spec/mongomodel/concerns/pretty_inspect_spec.rb +68 -0
  79. data/spec/mongomodel/concerns/properties_spec.rb +29 -0
  80. data/spec/mongomodel/concerns/timestamps_spec.rb +170 -0
  81. data/spec/mongomodel/concerns/validations_spec.rb +159 -0
  82. data/spec/mongomodel/document/callbacks_spec.rb +80 -0
  83. data/spec/mongomodel/document/dynamic_finders_spec.rb +183 -0
  84. data/spec/mongomodel/document/finders_spec.rb +231 -0
  85. data/spec/mongomodel/document/indexes_spec.rb +121 -0
  86. data/spec/mongomodel/document/optimistic_locking_spec.rb +57 -0
  87. data/spec/mongomodel/document/persistence_spec.rb +319 -0
  88. data/spec/mongomodel/document/scopes_spec.rb +204 -0
  89. data/spec/mongomodel/document/validations/uniqueness_spec.rb +217 -0
  90. data/spec/mongomodel/document/validations_spec.rb +132 -0
  91. data/spec/mongomodel/document_spec.rb +74 -0
  92. data/spec/mongomodel/embedded_document_spec.rb +66 -0
  93. data/spec/mongomodel/mongomodel_spec.rb +33 -0
  94. data/spec/mongomodel/support/collection_spec.rb +248 -0
  95. data/spec/mongomodel/support/mongo_options_spec.rb +295 -0
  96. data/spec/mongomodel/support/property_spec.rb +83 -0
  97. data/spec/spec.opts +6 -0
  98. data/spec/spec_helper.rb +21 -0
  99. data/spec/specdoc.opts +6 -0
  100. data/spec/support/callbacks.rb +44 -0
  101. data/spec/support/helpers/define_class.rb +24 -0
  102. data/spec/support/helpers/specs_for.rb +11 -0
  103. data/spec/support/matchers/be_a_subclass_of.rb +5 -0
  104. data/spec/support/matchers/respond_to_boolean.rb +17 -0
  105. data/spec/support/matchers/run_callbacks.rb +20 -0
  106. data/spec/support/models.rb +23 -0
  107. data/spec/support/time.rb +6 -0
  108. metadata +232 -0
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ describe Document do
5
+ describe "indexes" do
6
+ define_class(:Article, Document) do
7
+ property :title, String
8
+ property :age, Integer
9
+ end
10
+
11
+ def subclass(klass)
12
+ Class.new(klass)
13
+ end
14
+
15
+ it "should have an indexes collection" do
16
+ Article.indexes.should be_an_instance_of(Array)
17
+ end
18
+
19
+ it "should inherit indexes from parent classes" do
20
+ index = mock('index')
21
+ Article.indexes << index
22
+ subclass(Article).indexes.should include(index)
23
+ end
24
+
25
+ describe "#index" do
26
+ it "should add an index to the indexes collection" do
27
+ Article.index :title, :unique => true
28
+ Article.indexes.last.should == Index.new(:title, :unique => true)
29
+ end
30
+
31
+ it "should mark indexes as uninitialized" do
32
+ Article.ensure_indexes!
33
+
34
+ Article.indexes_initialized?.should be_true
35
+ Article.index :age
36
+ Article.indexes_initialized?.should be_false
37
+ end
38
+ end
39
+
40
+ describe "#ensure_indexes!" do
41
+ before(:each) do
42
+ Article.index :title, :unique => true
43
+ Article.index :age => :descending
44
+ end
45
+
46
+ it "should create indexes on the collection" do
47
+ Article.collection.should_receive(:create_index).with(:_type)
48
+ Article.collection.should_receive(:create_index).with(:title, true)
49
+ Article.collection.should_receive(:create_index).with([[:age, Mongo::DESCENDING]])
50
+ Article.ensure_indexes!
51
+ end
52
+
53
+ it "should mark indexes as initialized" do
54
+ Article.indexes_initialized?.should be_false
55
+ Article.ensure_indexes!
56
+ Article.indexes_initialized?.should be_true
57
+ end
58
+ end
59
+
60
+ describe "#_find" do
61
+ it "should run ensure_indexes!" do
62
+ Article.should_receive(:ensure_indexes!)
63
+ Article.find(:first)
64
+ end
65
+
66
+ it "should rerun ensure_indexes! if indexes are initialized" do
67
+ Article.ensure_indexes!
68
+ Article.should_not_receive(:ensure_indexes!)
69
+ Article.find(:first)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "index shortcuts" do
75
+ define_class(:TestDocument, Document)
76
+
77
+ describe ":index => true" do
78
+ it "should add an index on the property" do
79
+ TestDocument.should_receive(:index).with(:title)
80
+ TestDocument.property :title, String, :index => true
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ describe Index do
87
+ it "should convert index with single key to arguments for Mongo::Collection#create_index" do
88
+ Index.new(:title).to_args.should == [:title]
89
+ end
90
+
91
+ it "should convert nested index with single key to arguments for Mongo::Collection#create_index" do
92
+ Index.new('page.title').to_args.should == [:'page.title']
93
+ end
94
+
95
+ it "should convert index with unique option to arguments for Mongo::Collection#create_index" do
96
+ Index.new(:title, :unique => true).to_args.should == [:title, true]
97
+ end
98
+
99
+ it "should convert index with descending key to arguments for Mongo::Collection#create_index" do
100
+ Index.new(:title => :descending).to_args.should == [[[:title, Mongo::DESCENDING]]]
101
+ end
102
+
103
+ it "should convert index with multiple keys to arguments for Mongo::Collection#create_index" do
104
+ Index.new(:title, :age).to_args.should == [[[:age, Mongo::ASCENDING], [:title, Mongo::ASCENDING]]]
105
+ end
106
+
107
+ it "should convert index with multiple keys (ascending and descending) to arguments for Mongo::Collection#create_index" do
108
+ Index.new(:title => :ascending, :age => :descending).to_args.should == [[[:age, Mongo::DESCENDING], [:title, Mongo::ASCENDING]]]
109
+ end
110
+
111
+ it "should be equal to an equivalent index" do
112
+ Index.new(:title).should == Index.new(:title)
113
+ Index.new(:title, :age).should == Index.new(:title => :ascending, :age => :ascending)
114
+ end
115
+
116
+ it "should not be equal to an non-equivalent index" do
117
+ Index.new(:title).should_not == Index.new(:age)
118
+ Index.new(:title, :age).should_not == Index.new(:title => :ascending, :age => :descending)
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ specs_for(Document) do
5
+ describe "optimistic locking" do
6
+ define_class(:TestDocument, Document)
7
+
8
+ it "should not lock optimistically by default" do
9
+ TestDocument.locking_enabled?.should be_false
10
+ end
11
+
12
+ it "should not include a lock version property" do
13
+ TestDocument.properties.should_not include(:_lock_version)
14
+ end
15
+
16
+ context "with locking enabled" do
17
+ define_class(:TestDocument, Document) do
18
+ property :name, String
19
+ self.lock_optimistically = true
20
+ end
21
+
22
+ before(:each) do
23
+ @fresh = TestDocument.create!(:name => 'Original')
24
+ @stale = @fresh.dup
25
+
26
+ @fresh.name = 'Changed'
27
+ @fresh.save!
28
+ end
29
+
30
+ it "should be enabled" do
31
+ TestDocument.locking_enabled?.should be_true
32
+ end
33
+
34
+ it "should define a lock version property" do
35
+ TestDocument.properties.should include(:_lock_version)
36
+ end
37
+
38
+ it "should save a fresh document" do
39
+ @fresh.save.should be_true
40
+ end
41
+
42
+ it "should save! a fresh document" do
43
+ @fresh.save!.should be_true
44
+ end
45
+
46
+ it "should not save a stale document" do
47
+ @stale.save.should be_false
48
+ @stale._lock_version.should == 1
49
+ end
50
+
51
+ it "should raise an error when trying to save! a stale document" do
52
+ lambda { @stale.save! }.should raise_error(DocumentNotSaved)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,319 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ describe Document do
5
+ describe "persistence" do
6
+ define_class(:User, Document) do
7
+ property :name, String
8
+ property :age, Integer
9
+ end
10
+
11
+ context "an unsaved instance" do
12
+ subject { User.new(:name => 'Test') }
13
+
14
+ it { should be_a_new_record }
15
+
16
+ describe "#save" do
17
+ it "should return true" do
18
+ subject.save.should == true
19
+ end
20
+
21
+ it "should persist the document to the collection" do
22
+ subject.save
23
+
24
+ doc = User.collection.find_one
25
+ doc['_id'].to_s.should == subject.attributes[:id]
26
+ doc['name'].should == 'Test'
27
+ end
28
+ end
29
+
30
+ context "with a custom id" do
31
+ subject { User.new(:id => 'custom-id') }
32
+
33
+ it "should save the document using the custom id" do
34
+ subject.save
35
+ User.collection.find_one['_id'].should == 'custom-id'
36
+ end
37
+ end
38
+ end
39
+
40
+ context "a saved instance" do
41
+ subject { User.new(:name => 'Test') }
42
+
43
+ before(:each) { subject.save }
44
+
45
+ it { should_not be_a_new_record }
46
+
47
+ describe "#save" do
48
+ it "should return true" do
49
+ subject.save.should == true
50
+ end
51
+
52
+ it "should not create a new document" do
53
+ lambda {
54
+ subject.save
55
+ }.should_not change(User.collection, :count)
56
+ end
57
+
58
+ it "should update the document attributes" do
59
+ subject.attributes[:name] = 'Changed'
60
+ subject.save
61
+
62
+ doc = User.collection.find_one
63
+ doc['name'].should == 'Changed'
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "#collection_name" do
69
+ it "should infer the default collection name" do
70
+ User.collection_name.should == 'users'
71
+ end
72
+
73
+ it "should infer the default collection name for namespaced models" do
74
+ module ::Blog
75
+ class Post < MongoModel::Document; end
76
+ end
77
+
78
+ ::Blog::Post.collection_name.should == 'blog.posts'
79
+ end
80
+
81
+ it "should allow a custom collection name" do
82
+ class ::CustomCollectionName < Document
83
+ self.collection_name = 'foobar'
84
+ end
85
+
86
+ ::CustomCollectionName.collection_name.should == 'foobar'
87
+ end
88
+
89
+ it "should inherit a custom collection name" do
90
+ class ::CustomCollectionName < Document
91
+ self.collection_name = 'foobar'
92
+ end
93
+ class ::CustomCollectionNameSubclass < ::CustomCollectionName; end
94
+
95
+ ::CustomCollectionNameSubclass.collection_name.should == 'foobar'
96
+ end
97
+ end
98
+
99
+ describe "#collection" do
100
+ it "should be a mongo collection" do
101
+ User.collection.should be_a(Mongo::Collection)
102
+ end
103
+
104
+ it "should use the correct collection name" do
105
+ User.collection.name.should == 'users'
106
+ end
107
+ end
108
+
109
+ describe "#database" do
110
+ it "should return the current database" do
111
+ User.database.should == MongoModel.database
112
+ end
113
+ end
114
+
115
+ describe "#create" do
116
+ context "attributes hash" do
117
+ it "should pass attributes to instance" do
118
+ @user = User.create(:name => 'Test', :age => 18)
119
+ @user.name.should == 'Test'
120
+ @user.age.should == 18
121
+ end
122
+
123
+ it "should save the instance" do
124
+ User.create.should_not be_a_new_record
125
+ end
126
+
127
+ it "should yield the instance to a given block before saving" do
128
+ block_called = false
129
+
130
+ User.create do |u|
131
+ block_called = true
132
+
133
+ u.should be_an_instance_of(User)
134
+ u.should be_a_new_record
135
+ end
136
+
137
+ block_called.should be_true
138
+ end
139
+ end
140
+
141
+ context "array of attribute hashes" do
142
+ def create_users(&block)
143
+ User.create([{ :name => 'Test', :age => 18 }, { :name => 'Second', :age => 21 }], &block)
144
+ end
145
+
146
+ it "should return instances in array with associated attributes" do
147
+ @users = create_users
148
+ @users[0].name.should == 'Test'
149
+ @users[0].age.should == 18
150
+ @users[1].name.should == 'Second'
151
+ @users[1].age.should == 21
152
+ end
153
+
154
+ it "should save each instance" do
155
+ create_users.each { |user| user.should_not be_a_new_record }
156
+ end
157
+
158
+ it "should yield each instance to a given block before saving" do
159
+ block_called = 0
160
+
161
+ create_users do |u|
162
+ block_called += 1
163
+
164
+ u.should be_an_instance_of(User)
165
+ u.should be_a_new_record
166
+ end
167
+
168
+ block_called.should == 2
169
+ end
170
+ end
171
+ end
172
+
173
+ describe "#delete (class method)" do
174
+ before(:each) do
175
+ User.create(:id => 'user-1', :name => 'Test', :age => 10)
176
+ User.create(:id => 'user-2', :name => 'Another', :age => 20)
177
+ User.create(:id => 'user-3')
178
+ end
179
+
180
+ it "should delete by id" do
181
+ User.delete('user-1')
182
+
183
+ User.exists?('user-1').should be_false
184
+ User.exists?('user-2').should be_true
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
+
194
+ it "should delete by multiple ids in array" do
195
+ User.delete(['user-1', 'user-2'])
196
+
197
+ User.exists?('user-1').should be_false
198
+ User.exists?('user-2').should be_false
199
+ User.exists?('user-3').should be_true
200
+ end
201
+ end
202
+
203
+ describe "#delete (instance method)" do
204
+ before(:each) do
205
+ @user = User.create(:id => 'user-1')
206
+ User.create(:id => 'user-2', :name => 'Another')
207
+ end
208
+
209
+ it "should delete the instance from the database" do
210
+ @user.delete
211
+
212
+ User.exists?('user-1').should be_false
213
+ User.exists?('user-2').should be_true
214
+ end
215
+
216
+ it "should return the instance" do
217
+ @user.delete.should == @user
218
+ end
219
+
220
+ it "should freeze the instance" do
221
+ @user.delete
222
+ @user.should be_frozen
223
+ end
224
+
225
+ it "should mark the instance as destroyed" do
226
+ @user.delete
227
+ @user.should be_destroyed
228
+ end
229
+ end
230
+
231
+ describe "#destroy (instance method)" do
232
+ before(:each) do
233
+ @user = User.create(:id => 'user-1')
234
+ User.create(:id => 'user-2', :name => 'Another')
235
+ end
236
+
237
+ it "should delete the instance from the database" do
238
+ @user.destroy
239
+
240
+ User.exists?('user-1').should be_false
241
+ User.exists?('user-2').should be_true
242
+ end
243
+
244
+ it "should return the instance" do
245
+ @user.destroy.should == @user
246
+ end
247
+
248
+ it "should freeze the instance" do
249
+ @user.destroy
250
+ @user.should be_frozen
251
+ end
252
+
253
+ it "should mark the instance as destroyed" do
254
+ @user.destroy
255
+ @user.should be_destroyed
256
+ end
257
+ end
258
+
259
+ describe "#destroy (class method)" do
260
+ before(:each) do
261
+ User.create(:id => 'user-1', :name => 'Test', :age => 10)
262
+ User.create(:id => 'user-2', :name => 'Another', :age => 20)
263
+ User.create(:id => 'user-3')
264
+ end
265
+
266
+ it "should destroy by id" do
267
+ User.destroy('user-1')
268
+
269
+ User.exists?('user-1').should be_false
270
+ User.exists?('user-2').should be_true
271
+ end
272
+
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
+ it "should destroy by multiple ids in array" do
281
+ User.destroy(['user-1', 'user-2'])
282
+
283
+ User.exists?('user-1').should be_false
284
+ User.exists?('user-2').should be_false
285
+ User.exists?('user-3').should be_true
286
+ end
287
+ end
288
+
289
+ describe "#update_attributes" do
290
+ let(:user) { User.new(:name => 'Original', :age => 10) }
291
+
292
+ before(:each) { user.update_attributes(:name => 'Changed', :age => 20) }
293
+
294
+ it "should update the attributes" do
295
+ user.name.should == 'Changed'
296
+ user.age.should == 20
297
+ end
298
+
299
+ it "should save the document" do
300
+ user.should_not be_a_new_record
301
+ end
302
+ end
303
+
304
+ describe "#update_attribute" do
305
+ let(:user) { User.new(:name => 'Original', :age => 10) }
306
+
307
+ before(:each) { user.update_attribute(:name, 'Changed') }
308
+
309
+ it "should update the given attribute" do
310
+ user.name.should == 'Changed'
311
+ end
312
+
313
+ it "should save the document" do
314
+ user.should_not be_a_new_record
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end