mongomodel 0.2.20 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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