mongomodel 0.2.20 → 0.3.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.
Files changed (40) hide show
  1. data/Gemfile +1 -1
  2. data/bin/console +2 -0
  3. data/lib/mongomodel.rb +10 -7
  4. data/lib/mongomodel/attributes/mongo.rb +3 -2
  5. data/lib/mongomodel/attributes/typecasting.rb +6 -1
  6. data/lib/mongomodel/concerns/associations.rb +0 -61
  7. data/lib/mongomodel/concerns/associations/base/association.rb +1 -2
  8. data/lib/mongomodel/concerns/associations/belongs_to.rb +8 -3
  9. data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +7 -3
  10. data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +5 -1
  11. data/lib/mongomodel/document.rb +1 -0
  12. data/lib/mongomodel/document/collection_modifiers.rb +83 -0
  13. data/lib/mongomodel/document/optimistic_locking.rb +1 -1
  14. data/lib/mongomodel/document/persistence.rb +4 -5
  15. data/lib/mongomodel/log_subscriber.rb +48 -0
  16. data/lib/mongomodel/railtie.rb +8 -0
  17. data/lib/mongomodel/railties/controller_runtime.rb +33 -0
  18. data/lib/mongomodel/support/collection.rb +1 -1
  19. data/lib/mongomodel/support/configuration.rb +1 -2
  20. data/lib/mongomodel/support/instrumented_collection.rb +122 -0
  21. data/lib/mongomodel/support/mongo_options.rb +18 -4
  22. data/lib/mongomodel/support/reference.rb +48 -6
  23. data/lib/mongomodel/support/scope.rb +8 -3
  24. data/lib/mongomodel/support/scope/finder_methods.rb +3 -2
  25. data/lib/mongomodel/support/scope/load_methods.rb +13 -0
  26. data/lib/mongomodel/support/scope/query_methods.rb +1 -1
  27. data/lib/mongomodel/support/types.rb +13 -10
  28. data/lib/mongomodel/support/types/array.rb +1 -1
  29. data/lib/mongomodel/support/types/hash.rb +1 -1
  30. data/lib/mongomodel/support/types/rational.rb +42 -0
  31. data/lib/mongomodel/tasks/database.rake +54 -2
  32. data/lib/mongomodel/version.rb +1 -1
  33. data/spec/mongomodel/attributes/store_spec.rb +15 -2
  34. data/spec/mongomodel/concerns/logging_spec.rb +1 -1
  35. data/spec/mongomodel/document/collection_modifiers_spec.rb +103 -0
  36. data/spec/mongomodel/document/persistence_spec.rb +3 -3
  37. data/spec/mongomodel/mongomodel_spec.rb +1 -1
  38. data/spec/mongomodel/support/scope_spec.rb +5 -1
  39. data/spec/support/helpers/document_finder_stubs.rb +4 -4
  40. metadata +13 -6
@@ -1,9 +1,61 @@
1
1
  namespace :db do
2
+ desc "Migrate string-based object ids to BSON object ids"
3
+ task :migrate_ids => :environment do
4
+ MongoModel.database.collections.each do |collection|
5
+ unless collection.name =~ /(.*\.)?system\..*/
6
+ puts "Updating collection #{collection.name}..."
7
+
8
+ updated_documents = 0
9
+ updated_ids = 0
10
+ updated_fks = 0
11
+ updated_fk_arrays = 0
12
+
13
+ collection.find.each do |doc|
14
+ id = doc['_id']
15
+
16
+ update_required = false
17
+
18
+ doc.each do |k, v|
19
+ if k =~ /_id$/ && v.is_a?(String) && BSON::ObjectId.legal?(v)
20
+ doc[k] = BSON::ObjectId(v)
21
+ update_required = true
22
+
23
+ if k == "_id"
24
+ updated_ids += 1
25
+ else
26
+ updated_fks += 1
27
+ end
28
+ elsif k =~ /_ids$/ && v.is_a?(Array)
29
+ ids = v.map { |id| id.is_a?(String) && BSON::ObjectId.legal?(id) ? BSON::ObjectId(id) : id }
30
+
31
+ unless doc[k] == ids
32
+ doc[k] = ids
33
+ update_required = true
34
+ updated_fk_arrays += 1
35
+ end
36
+ end
37
+ end
38
+
39
+ if id != doc['_id']
40
+ collection.remove('_id' => id)
41
+ end
42
+
43
+ if update_required
44
+ collection.save(doc)
45
+ updated_documents += 1
46
+ end
47
+ end
48
+
49
+ puts " (updated #{updated_documents} documents, #{updated_ids} ids, #{updated_fks} foreign keys, #{updated_fk_arrays} foreign key arrays)\n\n"
50
+ end
51
+ end
52
+ end
53
+
2
54
  unless Rake::Task.task_defined?("db:drop")
3
55
  desc 'Drops all the collections for the database for the current Rails.env'
4
56
  task :drop => :environment do
5
- MongoModel.database.collections.each do |coll|
6
- coll.drop unless coll.name =~ /(.*\.)?system\..*/
57
+ MongoModel.database.collections.each do |collection|
58
+ collection.drop unless collection.name =~ /(.*\.)?system\..*/
7
59
  end
8
60
  end
9
61
  end
@@ -1,3 +1,3 @@
1
1
  module MongoModel
2
- VERSION = "0.2.20"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -14,6 +14,7 @@ module MongoModel
14
14
  properties[:array] = MongoModel::Properties::Property.new(:array, Array)
15
15
  properties[:date] = MongoModel::Properties::Property.new(:date, Date)
16
16
  properties[:time] = MongoModel::Properties::Property.new(:time, Time)
17
+ properties[:rational] = MongoModel::Properties::Property.new(:rational, Rational)
17
18
  properties[:custom] = MongoModel::Properties::Property.new(:custom, CustomClass)
18
19
  properties[:custom_default] = MongoModel::Properties::Property.new(:custom_default, CustomClassWithDefault)
19
20
  properties[:default] = MongoModel::Properties::Property.new(:default, String, :default => 'Default')
@@ -129,6 +130,11 @@ module MongoModel
129
130
  "Sat Jan 01 20:15:01.123456 UTC 2000" => Time.utc(2000, 1, 1, 20, 15, 1, 123000),
130
131
  "2009/3/4" => Time.utc(2009, 3, 4, 0, 0, 0, 0),
131
132
  nil => nil
133
+ },
134
+ :rational =>
135
+ {
136
+ Rational(1, 15) => Rational(1, 15),
137
+ "2/3" => Rational(2, 3)
132
138
  }
133
139
  }
134
140
 
@@ -165,10 +171,11 @@ module MongoModel
165
171
  :string => [ "abc", 123 ],
166
172
  :integer => [ 123, 55.123, "999", "12.123" ],
167
173
  :float => [ 55.123, 123, "12.123" ],
168
- :boolean => [ true, false, "true", "false", 1, 0, "1", "0" ],
174
+ :boolean => [ true, false, "true", "false", 1, 0, "1", "0", "" ],
169
175
  :symbol => [ :some_symbol, "some_string" ],
170
176
  :hash => [ { :foo => 'bar' } ],
171
177
  :array => [ [123, 'abc', :foo, true] ],
178
+ :rational => [ "2/3" ],
172
179
  :date => [ Date.civil(2009, 11, 15), Time.local(2008, 12, 3, 0, 0, 0, 0), "2009/3/4", "Sat Jan 01 20:15:01 UTC 2000" ],
173
180
  :time => [ Time.local(2008, 5, 14, 1, 2, 3, 4), Date.civil(2009, 11, 15), "Sat Jan 01 20:15:01 UTC 2000", "2009/3/4" ]
174
181
  }
@@ -176,7 +183,7 @@ module MongoModel
176
183
  BeforeTypeCastExamples.each do |type, examples|
177
184
  context "assigning to #{type} property" do
178
185
  examples.each do |example|
179
- it "should access pre-typecasted value of #{example.inspect}" do
186
+ it "should access pre-typecast value of #{example.inspect}" do
180
187
  subject[type] = example
181
188
  subject.before_type_cast(type).should == example
182
189
  end
@@ -196,6 +203,7 @@ module MongoModel
196
203
  :array => [ [''], [123, 'abc', :foo, true] ],
197
204
  :date => [ Date.civil(2009, 11, 15) ],
198
205
  :time => [ Time.local(2008, 5, 14, 1, 2, 3, 4) ],
206
+ :rational => [ Rational(2, 3) ],
199
207
  :custom => [ CustomClass.new('foobar'), 'baz' ]
200
208
  }
201
209
 
@@ -209,6 +217,7 @@ module MongoModel
209
217
  :array => [ [] ],
210
218
  :date => [ nil, '' ],
211
219
  :time => [ nil, '' ],
220
+ :rational => [ nil ],
212
221
  :custom => [ nil ]
213
222
  }
214
223
 
@@ -246,6 +255,7 @@ module MongoModel
246
255
  subject[:array] = [ 123, 'abc', 45.67, true, :bar, CustomClass.new('custom in array') ]
247
256
  subject[:date] = Date.civil(2009, 11, 15)
248
257
  subject[:time] = Time.local(2008, 5, 14, 1, 2, 3, 4, 0.5)
258
+ subject[:rational] = Rational(2, 3)
249
259
  subject[:custom] = CustomClass.new('custom')
250
260
  subject[:as] = "As property"
251
261
  subject[:non_property] = "Hello World"
@@ -261,6 +271,7 @@ module MongoModel
261
271
  'array' => [ 123, 'abc', 45.67, true, :bar, { :name => 'custom in array' } ],
262
272
  'date' => "2009/11/15",
263
273
  'time' => Time.local(2008, 5, 14, 1, 2, 3, 4, 0),
274
+ 'rational' => "2/3",
264
275
  'custom' => { :name => 'custom' },
265
276
  '_custom_as' => "As property",
266
277
  'non_property' => "Hello World",
@@ -279,6 +290,7 @@ module MongoModel
279
290
  'array' => [ 123, 'abc', 45.67, true, :bar ],
280
291
  'date' => Time.utc(2009, 11, 15),
281
292
  'time' => Time.local(2008, 5, 14, 1, 2, 3, 4, 0.5),
293
+ 'rational' => "2/3",
282
294
  'custom' => { :name => 'custom' },
283
295
  '_custom_as' => "As property",
284
296
  'custom_non_property' => { :name => 'custom non property' }
@@ -293,6 +305,7 @@ module MongoModel
293
305
  subject[:array].should == [ 123, 'abc', 45.67, true, :bar ]
294
306
  subject[:date].should == Date.civil(2009, 11, 15)
295
307
  subject[:time].should == Time.local(2008, 5, 14, 1, 2, 3, 4, 0)
308
+ subject[:rational].should == Rational(2, 3)
296
309
  subject[:custom].should == CustomClass.new('custom')
297
310
  subject[:as].should == "As property"
298
311
  subject[:custom_non_property].should == { :name => 'custom non property' }
@@ -5,7 +5,7 @@ module MongoModel
5
5
  describe "logging" do
6
6
  define_class(:TestDocument, described_class)
7
7
 
8
- let(:logger) { mock('logger') }
8
+ let(:logger) { mock('logger').as_null_object }
9
9
  before(:all) { MongoModel.logger = logger }
10
10
 
11
11
  it "should have a logger reader on the class" do
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ specs_for(Document) do
5
+ describe "collection modifiers" do
6
+ define_class(:Post, Document) do
7
+ property :hits, Integer
8
+ property :available, Integer
9
+ property :tags, Array
10
+ end
11
+
12
+ let(:collection) { Post.collection }
13
+ subject { Post }
14
+
15
+ def self.should_update_collection(expression, &block)
16
+ it "should update the collection" do
17
+ collection.should_receive(:update).with(selector, expression, :multi => true)
18
+ instance_eval(&block)
19
+ end
20
+ end
21
+
22
+ shared_examples_for "collection modifiers" do
23
+ describe "increase!" do
24
+ should_update_collection('$inc' => { 'hits' => 1, 'available' => -1 }) do
25
+ subject.increase!(:hits => 1, :available => -1)
26
+ end
27
+ end
28
+
29
+ describe "set!" do
30
+ should_update_collection('$set' => { 'hits' => 20, 'available' => 100 }) do
31
+ subject.set!(:hits => 20, :available => 100)
32
+ end
33
+ end
34
+
35
+ describe "unset!" do
36
+ should_update_collection('$unset' => { 'hits' => 1, 'available' => 1 }) do
37
+ subject.unset!(:hits, :available)
38
+ end
39
+ end
40
+
41
+ describe "push!" do
42
+ should_update_collection('$push' => { 'tags' => 'abc' }) do
43
+ subject.push!(:tags => 'abc')
44
+ end
45
+ end
46
+
47
+ describe "push_all!" do
48
+ should_update_collection('$pushAll' => { 'tags' => ['xxx', 'yyy', 'zzz'] }) do
49
+ subject.push_all!(:tags => ['xxx', 'yyy', 'zzz'])
50
+ end
51
+ end
52
+
53
+ describe "add_to_set!" do
54
+ should_update_collection('$addToSet' => { 'tags' => 'xxx' }) do
55
+ subject.add_to_set!(:tags => 'xxx')
56
+ end
57
+ end
58
+
59
+ describe "pull!" do
60
+ should_update_collection('$pull' => { 'tags' => 'abc' }) do
61
+ subject.pull!(:tags => 'abc')
62
+ end
63
+ end
64
+
65
+ describe "pull_all!" do
66
+ should_update_collection('$pullAll' => { 'tags' => ['xxx', 'yyy', 'zzz'] }) do
67
+ subject.pull_all!(:tags => ['xxx', 'yyy', 'zzz'])
68
+ end
69
+ end
70
+
71
+ describe "pop!" do
72
+ should_update_collection('$pop' => { 'tags' => 1 }) do
73
+ subject.pop!(:tags)
74
+ end
75
+ end
76
+
77
+ describe "shift!" do
78
+ should_update_collection('$pop' => { 'tags' => -1 }) do
79
+ subject.shift!(:tags)
80
+ end
81
+ end
82
+
83
+ describe "rename!" do
84
+ should_update_collection('$rename' => { 'tags' => :tag_collection }) do
85
+ subject.rename!(:tags => :tag_collection)
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "class methods" do
91
+ let(:selector) { {} }
92
+ it_should_behave_like "collection modifiers"
93
+ end
94
+
95
+ describe "instance methods" do
96
+ subject { Post.new }
97
+ before { subject.save }
98
+ let(:selector) { { '_id' => subject.id } }
99
+ it_should_behave_like "collection modifiers"
100
+ end
101
+ end
102
+ end
103
+ end
@@ -22,7 +22,7 @@ module MongoModel
22
22
  subject.save
23
23
 
24
24
  doc = User.collection.find_one
25
- doc['_id'].to_s.should == subject.attributes[:id]
25
+ doc['_id'].to_s.should == subject.attributes[:id].to_s
26
26
  doc['name'].should == 'Test'
27
27
  end
28
28
  end
@@ -97,8 +97,8 @@ module MongoModel
97
97
  end
98
98
 
99
99
  describe "#collection" do
100
- it "should be a mongo collection" do
101
- User.collection.should be_a(Mongo::Collection)
100
+ it "should be an instrumented collection" do
101
+ User.collection.should be_a(InstrumentedCollection)
102
102
  end
103
103
 
104
104
  it "should use the correct collection name" do
@@ -42,7 +42,7 @@ describe MongoModel do
42
42
  end
43
43
 
44
44
  it "should have a logger accessor" do
45
- logger = mock('logger')
45
+ logger = mock('logger').as_null_object
46
46
  MongoModel.logger = logger
47
47
  MongoModel.logger.should == logger
48
48
  end
@@ -927,13 +927,17 @@ module MongoModel
927
927
 
928
928
  subject { scoped }
929
929
 
930
+ def truncate_timestamp(time)
931
+ time.change(:usec => (time.usec / 1000.0).floor * 1000)
932
+ end
933
+
930
934
  def model
931
935
  OtherPost
932
936
  end
933
937
 
934
938
  def finder_options
935
939
  {
936
- :conditions => { "author" => "Sam", "published" => true, "date" => { "$lt" => timestamp } },
940
+ :conditions => { "author" => "Sam", "published" => true, "date" => { "$lt" => truncate_timestamp(timestamp.utc) } },
937
941
  :order => [:author.asc, :published.desc],
938
942
  :select => [:author, :published],
939
943
  :offset => 15,
@@ -4,13 +4,13 @@ module DocumentFinderStubs
4
4
  include Spec::Mocks::ExampleMethods
5
5
 
6
6
  def stub_find(result)
7
- find_result = mock('find result', :to_a => result.map { |doc| doc.to_mongo }, :count => result.size)
7
+ find_result = mock('find result', :to_a => result.map { |doc| doc.to_mongo }, :count => result.size).as_null_object
8
8
  collection.stub!(:find).and_return(find_result)
9
9
  end
10
10
 
11
11
  def should_find(expected={}, result=[])
12
12
  selector, options = MongoModel::MongoOptions.new(self, expected).to_a
13
- find_result = mock('find result', :to_a => result.map { |doc| doc.to_mongo })
13
+ find_result = mock('find result', :to_a => result.map { |doc| doc.to_mongo }).as_null_object
14
14
  collection.should_receive(:find).once.with(selector, options).and_return(find_result)
15
15
  yield if block_given?
16
16
  end
@@ -22,7 +22,7 @@ module DocumentFinderStubs
22
22
 
23
23
  def should_count(expected={}, result=[])
24
24
  selector, options = MongoModel::MongoOptions.new(self, expected).to_a
25
- find_result = mock('find result', :count => result)
25
+ find_result = mock('find result', :count => result).as_null_object
26
26
  collection.should_receive(:find).once.with(selector, options).and_return(find_result)
27
27
  yield if block_given?
28
28
  end
@@ -38,7 +38,7 @@ module DocumentFinderStubs
38
38
 
39
39
  def should_delete(conditions={})
40
40
  selector, options = MongoModel::MongoOptions.new(self, :conditions => conditions).to_a
41
- collection.should_receive(:remove).once.with(selector)
41
+ collection.should_receive(:remove).once.with(selector, options)
42
42
  yield if block_given?
43
43
  end
44
44
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongomodel
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 20
10
- version: 0.2.20
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sam Pohlenz
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-08 00:00:00 +10:30
18
+ date: 2011-02-07 00:00:00 +10:30
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -166,6 +166,7 @@ files:
166
166
  - lib/mongomodel/concerns/validations/associated.rb
167
167
  - lib/mongomodel/document.rb
168
168
  - lib/mongomodel/document/callbacks.rb
169
+ - lib/mongomodel/document/collection_modifiers.rb
169
170
  - lib/mongomodel/document/dynamic_finders.rb
170
171
  - lib/mongomodel/document/indexes.rb
171
172
  - lib/mongomodel/document/optimistic_locking.rb
@@ -175,12 +176,15 @@ files:
175
176
  - lib/mongomodel/document/validations/uniqueness.rb
176
177
  - lib/mongomodel/embedded_document.rb
177
178
  - lib/mongomodel/locale/en.yml
179
+ - lib/mongomodel/log_subscriber.rb
178
180
  - lib/mongomodel/railtie.rb
181
+ - lib/mongomodel/railties/controller_runtime.rb
179
182
  - lib/mongomodel/support/collection.rb
180
183
  - lib/mongomodel/support/configuration.rb
181
184
  - lib/mongomodel/support/core_extensions.rb
182
185
  - lib/mongomodel/support/dynamic_finder.rb
183
186
  - lib/mongomodel/support/exceptions.rb
187
+ - lib/mongomodel/support/instrumented_collection.rb
184
188
  - lib/mongomodel/support/map.rb
185
189
  - lib/mongomodel/support/mongo_operator.rb
186
190
  - lib/mongomodel/support/mongo_options.rb
@@ -190,6 +194,7 @@ files:
190
194
  - lib/mongomodel/support/scope/batches.rb
191
195
  - lib/mongomodel/support/scope/dynamic_finders.rb
192
196
  - lib/mongomodel/support/scope/finder_methods.rb
197
+ - lib/mongomodel/support/scope/load_methods.rb
193
198
  - lib/mongomodel/support/scope/pagination.rb
194
199
  - lib/mongomodel/support/scope/query_methods.rb
195
200
  - lib/mongomodel/support/scope/spawn_methods.rb
@@ -202,6 +207,7 @@ files:
202
207
  - lib/mongomodel/support/types/hash.rb
203
208
  - lib/mongomodel/support/types/integer.rb
204
209
  - lib/mongomodel/support/types/object.rb
210
+ - lib/mongomodel/support/types/rational.rb
205
211
  - lib/mongomodel/support/types/set.rb
206
212
  - lib/mongomodel/support/types/string.rb
207
213
  - lib/mongomodel/support/types/symbol.rb
@@ -235,6 +241,7 @@ files:
235
241
  - spec/mongomodel/concerns/translation_spec.rb
236
242
  - spec/mongomodel/concerns/validations_spec.rb
237
243
  - spec/mongomodel/document/callbacks_spec.rb
244
+ - spec/mongomodel/document/collection_modifiers_spec.rb
238
245
  - spec/mongomodel/document/dynamic_finders_spec.rb
239
246
  - spec/mongomodel/document/finders_spec.rb
240
247
  - spec/mongomodel/document/indexes_spec.rb
@@ -298,7 +305,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
305
  requirements: []
299
306
 
300
307
  rubyforge_project: mongomodel
301
- rubygems_version: 1.4.1
308
+ rubygems_version: 1.4.2
302
309
  signing_key:
303
310
  specification_version: 3
304
311
  summary: MongoDB ORM for Ruby/Rails