djsun-mongo_mapper 0.5.5.3 → 0.5.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -220,7 +220,6 @@ module MongoMapper
220
220
  attrs = HashWithIndifferentAccess.new
221
221
 
222
222
  embedded_keys.each do |key|
223
- puts key.inspect
224
223
  attrs[key.name] = read_attribute(key.name).try(:attributes)
225
224
  end
226
225
 
@@ -1,54 +1,14 @@
1
1
  module MongoMapper
2
2
  class FinderOptions
3
- attr_reader :options
4
-
5
- def self.to_mongo_criteria(conditions, parent_key=nil)
6
- criteria = {}
7
- conditions.each_pair do |field, value|
8
- field = field_normalized(field)
9
- case value
10
- when Array
11
- operator_present = field.to_s =~ /^\$/
12
- criteria[field] = if operator_present
13
- value
14
- else
15
- {'$in' => value}
16
- end
17
- when Hash
18
- criteria[field] = to_mongo_criteria(value, field)
19
- else
20
- criteria[field] = value
21
- end
22
- end
23
-
24
- criteria
25
- end
26
-
27
- def self.to_mongo_options(options)
28
- options = options.dup
29
- {
30
- :fields => to_mongo_fields(options.delete(:fields) || options.delete(:select)),
31
- :skip => (options.delete(:skip) || options.delete(:offset) || 0).to_i,
32
- :limit => (options.delete(:limit) || 0).to_i,
33
- :sort => options.delete(:sort) || to_mongo_sort(options.delete(:order))
34
- }
35
- end
36
-
37
- def self.field_normalized(field)
38
- if field.to_s == 'id'
39
- :_id
40
- else
41
- field
42
- end
43
- end
44
-
45
3
  OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
46
4
 
47
- def initialize(options)
5
+ attr_reader :model, :options
6
+
7
+ def initialize(model, options)
48
8
  raise ArgumentError, "FinderOptions must be a hash" unless options.is_a?(Hash)
9
+ options.symbolize_keys!
49
10
 
50
- options = options.symbolize_keys
51
- @options, @conditions = {}, options.delete(:conditions) || {}
11
+ @model, @options, @conditions = model, {}, options.delete(:conditions) || {}
52
12
 
53
13
  options.each_pair do |key, value|
54
14
  if OptionKeys.include?(key)
@@ -60,11 +20,11 @@ module MongoMapper
60
20
  end
61
21
 
62
22
  def criteria
63
- self.class.to_mongo_criteria(@conditions)
23
+ to_mongo_criteria(model, @conditions)
64
24
  end
65
25
 
66
26
  def options
67
- self.class.to_mongo_options(@options)
27
+ to_mongo_options(model, @options)
68
28
  end
69
29
 
70
30
  def to_a
@@ -72,7 +32,56 @@ module MongoMapper
72
32
  end
73
33
 
74
34
  private
75
- def self.to_mongo_fields(fields)
35
+ def to_mongo_criteria(model, conditions, parent_key=nil)
36
+ criteria = {}
37
+ add_sci_scope(model, criteria)
38
+
39
+ conditions.each_pair do |field, value|
40
+ field = field_normalized(field)
41
+ case value
42
+ when Array
43
+ operator_present = field.to_s =~ /^\$/
44
+ criteria[field] = if operator_present
45
+ value
46
+ else
47
+ {'$in' => value}
48
+ end
49
+ when Hash
50
+ criteria[field] = to_mongo_criteria(model, value, field)
51
+ else
52
+ criteria[field] = value
53
+ end
54
+ end
55
+
56
+ criteria
57
+ end
58
+
59
+ def field_normalized(field)
60
+ if field.to_s == 'id'
61
+ :_id
62
+ else
63
+ field
64
+ end
65
+ end
66
+
67
+ # adds _type single collection inheritance scope for models that need it
68
+ def add_sci_scope(model, criteria)
69
+ if model.single_collection_inherited?
70
+ criteria[:_type] = model.to_s
71
+ end
72
+ end
73
+
74
+ def to_mongo_options(model, options)
75
+ options = options.dup
76
+ {
77
+ :fields => to_mongo_fields(options.delete(:fields) || options.delete(:select)),
78
+ :skip => (options.delete(:skip) || options.delete(:offset) || 0).to_i,
79
+ :limit => (options.delete(:limit) || 0).to_i,
80
+ :sort => options.delete(:sort) || to_mongo_sort(options.delete(:order))
81
+ }
82
+ end
83
+
84
+ def to_mongo_fields(fields)
76
85
  return if fields.blank?
77
86
 
78
87
  if fields.is_a?(String)
@@ -82,13 +91,13 @@ module MongoMapper
82
91
  end
83
92
  end
84
93
 
85
- def self.to_mongo_sort(sort)
94
+ def to_mongo_sort(sort)
86
95
  return if sort.blank?
87
96
  pieces = sort.split(',')
88
97
  pieces.map { |s| to_mongo_sort_piece(s) }
89
98
  end
90
99
 
91
- def self.to_mongo_sort_piece(str)
100
+ def to_mongo_sort_piece(str)
92
101
  field, direction = str.strip.split(' ')
93
102
  direction ||= 'ASC'
94
103
  direction = direction.upcase == 'ASC' ? 1 : -1
@@ -51,5 +51,4 @@ module MongoMapper #:nodoc:
51
51
  end
52
52
  end
53
53
 
54
- dir = Pathname(__FILE__).dirname.expand_path + 'serializers'
55
- require dir + 'json_serializer'
54
+ require 'mongo_mapper/serializers/json_serializer'
@@ -18,7 +18,9 @@ module MongoMapper
18
18
  option :scope
19
19
 
20
20
  def valid?(instance)
21
- doc = instance.class.find(:first, :conditions => {self.attribute => instance[attribute]}.merge(scope_conditions(instance)), :limit => 1)
21
+ value = instance[attribute]
22
+ return true if allow_blank && value.blank?
23
+ doc = instance.class.first({self.attribute => value}.merge(scope_conditions(instance)))
22
24
  doc.nil? || instance.id == doc.id
23
25
  end
24
26
 
data/mongo_mapper.gemspec CHANGED
@@ -1,16 +1,17 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongo_mapper}
8
- s.version = "0.5.5"
8
+ s.version = "0.5.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Nunemaker"]
12
- s.date = %q{2009-10-16}
12
+ s.date = %q{2009-10-22}
13
13
  s.default_executable = %q{mmconsole}
14
+ s.description = %q{Awesome gem for modeling your domain and storing it in mongo}
14
15
  s.email = %q{nunemaker@gmail.com}
15
16
  s.executables = ["mmconsole"]
16
17
  s.extra_rdoc_files = [
@@ -94,7 +95,6 @@ Gem::Specification.new do |s|
94
95
  s.homepage = %q{http://github.com/jnunemaker/mongomapper}
95
96
  s.rdoc_options = ["--charset=UTF-8"]
96
97
  s.require_paths = ["lib"]
97
- s.rubyforge_project = %q{mongomapper}
98
98
  s.rubygems_version = %q{1.3.5}
99
99
  s.summary = %q{Awesome gem for modeling your domain and storing it in mongo}
100
100
  s.test_files = [
@@ -143,7 +143,7 @@ Gem::Specification.new do |s|
143
143
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
144
144
  s.add_runtime_dependency(%q<activesupport>, [">= 2.3"])
145
145
  s.add_runtime_dependency(%q<mongo>, ["= 0.15.1"])
146
- s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.7.4"])
146
+ s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.8.0"])
147
147
  s.add_development_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
148
148
  s.add_development_dependency(%q<shoulda>, ["= 2.10.2"])
149
149
  s.add_development_dependency(%q<timecop>, ["= 0.3.1"])
@@ -151,7 +151,7 @@ Gem::Specification.new do |s|
151
151
  else
152
152
  s.add_dependency(%q<activesupport>, [">= 2.3"])
153
153
  s.add_dependency(%q<mongo>, ["= 0.15.1"])
154
- s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.4"])
154
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.8.0"])
155
155
  s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
156
156
  s.add_dependency(%q<shoulda>, ["= 2.10.2"])
157
157
  s.add_dependency(%q<timecop>, ["= 0.3.1"])
@@ -160,10 +160,11 @@ Gem::Specification.new do |s|
160
160
  else
161
161
  s.add_dependency(%q<activesupport>, [">= 2.3"])
162
162
  s.add_dependency(%q<mongo>, ["= 0.15.1"])
163
- s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.4"])
163
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.8.0"])
164
164
  s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
165
165
  s.add_dependency(%q<shoulda>, ["= 2.10.2"])
166
166
  s.add_dependency(%q<timecop>, ["= 0.3.1"])
167
167
  s.add_dependency(%q<mocha>, ["= 0.9.4"])
168
168
  end
169
169
  end
170
+
data/specs.watchr CHANGED
@@ -4,7 +4,7 @@ def run(cmd)
4
4
  end
5
5
 
6
6
  def run_test_file(file)
7
- run "ruby -Itest #{file}"
7
+ run %Q(ruby -I"lib:test" -rubygems #{file})
8
8
  end
9
9
 
10
10
  def run_all_tests
@@ -12,7 +12,7 @@ def run_all_tests
12
12
  end
13
13
 
14
14
  def related_test_files(path)
15
- Dir['test/**/*.rb'].select { |file| file =~ /#{File.basename(path)}/ }
15
+ Dir['test/**/*.rb'].select { |file| file =~ /test_#{File.basename(path)}/ }
16
16
  end
17
17
 
18
18
  watch('test/test_helper\.rb') { run_all_tests }
@@ -137,7 +137,7 @@ class ManyDocumentsAsProxyTest < Test::Unit::TestCase
137
137
  end
138
138
 
139
139
  should "work with conditions" do
140
- comments = @post.comments.find(:all, :conditions => {:body => 'comment1'})
140
+ comments = @post.comments.find(:all, :body => 'comment1')
141
141
  comments.should == [@comment1]
142
142
  end
143
143
 
@@ -154,7 +154,7 @@ class ManyDocumentsAsProxyTest < Test::Unit::TestCase
154
154
  end
155
155
 
156
156
  should "work with conditions" do
157
- comments = @post.comments.all(:conditions => {:body => 'comment1'})
157
+ comments = @post.comments.all(:body => 'comment1')
158
158
  comments.should == [@comment1]
159
159
  end
160
160
 
@@ -181,7 +181,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
181
181
  end
182
182
 
183
183
  should "work with conditions" do
184
- messages = @lounge.messages.find(:all, :conditions => {:body => 'Loungin!'}, :order => "position")
184
+ messages = @lounge.messages.find(:all, :body => 'Loungin!', :order => "position")
185
185
  messages.should == [@lm1]
186
186
  end
187
187
 
@@ -197,7 +197,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
197
197
  end
198
198
 
199
199
  should "work with conditions" do
200
- messages = @lounge.messages.all(:conditions => {:body => 'Loungin!'}, :order => "position")
200
+ messages = @lounge.messages.all(:body => 'Loungin!', :order => "position")
201
201
  messages.should == [@lm1]
202
202
  end
203
203
 
@@ -213,7 +213,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
213
213
  end
214
214
 
215
215
  should "work with conditions" do
216
- message = @lounge.messages.find(:first, :conditions => {:body => 'I love loungin!'}, :order => "position asc")
216
+ message = @lounge.messages.find(:first, :body => 'I love loungin!', :order => "position asc")
217
217
  message.should == @lm2
218
218
  end
219
219
  end
@@ -224,7 +224,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
224
224
  end
225
225
 
226
226
  should "work with conditions" do
227
- message = @lounge.messages.first(:conditions => {:body => 'I love loungin!'}, :order => "position asc")
227
+ message = @lounge.messages.first(:body => 'I love loungin!', :order => "position asc")
228
228
  message.should == @lm2
229
229
  end
230
230
  end
@@ -235,7 +235,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
235
235
  end
236
236
 
237
237
  should "work with conditions" do
238
- message = @lounge.messages.find(:last, :conditions => {:body => 'Loungin!'}, :order => "position asc")
238
+ message = @lounge.messages.find(:last, :body => 'Loungin!', :order => "position asc")
239
239
  message.should == @lm1
240
240
  end
241
241
  end
@@ -246,7 +246,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
246
246
  end
247
247
 
248
248
  should "work with conditions" do
249
- message = @lounge.messages.last(:conditions => {:body => 'Loungin!'}, :order => "position asc")
249
+ message = @lounge.messages.last(:body => 'Loungin!', :order => "position asc")
250
250
  message.should == @lm1
251
251
  end
252
252
  end
@@ -215,7 +215,7 @@ class ManyProxyTest < Test::Unit::TestCase
215
215
  end
216
216
 
217
217
  should "work with conditions" do
218
- statuses = @project1.statuses.find(:all, :conditions => {'name' => 'Complete'})
218
+ statuses = @project1.statuses.find(:all, :name => 'Complete')
219
219
  statuses.should == [@complete]
220
220
  end
221
221
 
@@ -231,7 +231,7 @@ class ManyProxyTest < Test::Unit::TestCase
231
231
  end
232
232
 
233
233
  should "work with conditions" do
234
- statuses = @project1.statuses.all(:conditions => {'name' => 'Complete'})
234
+ statuses = @project1.statuses.all(:name => 'Complete')
235
235
  statuses.should == [@complete]
236
236
  end
237
237
 
@@ -247,7 +247,7 @@ class ManyProxyTest < Test::Unit::TestCase
247
247
  end
248
248
 
249
249
  should "work with conditions" do
250
- status = @project1.statuses.find(:first, :conditions => {:name => 'Complete'})
250
+ status = @project1.statuses.find(:first, :name => 'Complete')
251
251
  status.should == @complete
252
252
  end
253
253
  end
@@ -258,7 +258,7 @@ class ManyProxyTest < Test::Unit::TestCase
258
258
  end
259
259
 
260
260
  should "work with conditions" do
261
- status = @project1.statuses.first(:conditions => {:name => 'Complete'})
261
+ status = @project1.statuses.first(:name => 'Complete')
262
262
  status.should == @complete
263
263
  end
264
264
  end
@@ -269,7 +269,7 @@ class ManyProxyTest < Test::Unit::TestCase
269
269
  end
270
270
 
271
271
  should "work with conditions" do
272
- status = @project1.statuses.find(:last, :order => 'position', :conditions => {:name => 'New'})
272
+ status = @project1.statuses.find(:last, :order => 'position', :name => 'New')
273
273
  status.should == @brand_new
274
274
  end
275
275
  end
@@ -280,7 +280,7 @@ class ManyProxyTest < Test::Unit::TestCase
280
280
  end
281
281
 
282
282
  should "work with conditions" do
283
- status = @project1.statuses.last(:order => 'position', :conditions => {:name => 'New'})
283
+ status = @project1.statuses.last(:order => 'position', :name => 'New')
284
284
  status.should == @brand_new
285
285
  end
286
286
  end
@@ -24,7 +24,7 @@ class DocumentTest < Test::Unit::TestCase
24
24
  doc.using_custom_id?.should be_false
25
25
  end
26
26
  end
27
-
27
+
28
28
  context "Saving a document with a blank binary value" do
29
29
  setup do
30
30
  @document.key :file, Binary
@@ -41,9 +41,9 @@ class DocumentTest < Test::Unit::TestCase
41
41
  @id = Mongo::ObjectID.new.to_s
42
42
  @document.collection.insert({
43
43
  :_id => @id,
44
- :first_name => 'John',
45
- :last_name => 'Nunemaker',
46
- :age => 27,
44
+ :first_name => 'John',
45
+ :last_name => 'Nunemaker',
46
+ :age => 27,
47
47
  :favorite_color => 'red',
48
48
  :skills => ['ruby', 'rails', 'javascript', 'xhtml', 'css']
49
49
  })
@@ -58,7 +58,7 @@ class DocumentTest < Test::Unit::TestCase
58
58
  doc.skills.should == ['ruby', 'rails', 'javascript', 'xhtml', 'css']
59
59
  end
60
60
  end
61
-
61
+
62
62
  context "Document Class Methods" do
63
63
  context "Using key with type Array" do
64
64
  setup do
@@ -141,7 +141,7 @@ class DocumentTest < Test::Unit::TestCase
141
141
  doc.foo['baz'].should == 'bar'
142
142
  end
143
143
  end
144
-
144
+
145
145
  context "Using key with custom type with default" do
146
146
  setup do
147
147
  @document.key :window, WindowSize, :default => WindowSize.new(600, 480)
@@ -150,19 +150,19 @@ class DocumentTest < Test::Unit::TestCase
150
150
  should "default to default" do
151
151
  doc = @document.new
152
152
  doc.window.should == WindowSize.new(600, 480)
153
-
153
+
154
154
  end
155
-
155
+
156
156
  should "save and load from mongo" do
157
157
  doc = @document.new
158
158
  doc.save
159
-
159
+
160
160
  from_db = @document.find(doc.id)
161
161
  from_db.window.should == WindowSize.new(600, 480)
162
162
  end
163
163
  end
164
-
165
-
164
+
165
+
166
166
  context "Creating a single document" do
167
167
  setup do
168
168
  @doc_instance = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
@@ -299,7 +299,9 @@ class DocumentTest < Test::Unit::TestCase
299
299
  end
300
300
 
301
301
  should "raise error if document not found" do
302
- lambda { @document.find(123) }.should raise_error(MongoMapper::DocumentNotFound)
302
+ lambda {
303
+ @document.find(123)
304
+ }.should raise_error(MongoMapper::DocumentNotFound)
303
305
  end
304
306
  end
305
307
 
@@ -311,31 +313,31 @@ class DocumentTest < Test::Unit::TestCase
311
313
  should "work as array" do
312
314
  @document.find([@doc1.id, @doc2.id]).should == [@doc1, @doc2]
313
315
  end
314
-
316
+
315
317
  should "return array if array only has one element" do
316
318
  @document.find([@doc1.id]).should == [@doc1]
317
319
  end
318
320
  end
319
-
321
+
320
322
  should "be able to find using condition auto-detection" do
321
323
  @document.first(:first_name => 'John').should == @doc1
322
324
  @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
323
325
  end
324
-
326
+
325
327
  context "with :all" do
326
328
  should "find all documents" do
327
329
  @document.find(:all, :order => 'first_name').should == [@doc1, @doc3, @doc2]
328
330
  end
329
331
 
330
332
  should "be able to add conditions" do
331
- @document.find(:all, :conditions => {:first_name => 'John'}).should == [@doc1]
333
+ @document.find(:all, :first_name => 'John').should == [@doc1]
332
334
  end
333
335
  end
334
336
 
335
337
  context "with #all" do
336
338
  should "find all documents based on criteria" do
337
339
  @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
338
- @document.all(:conditions => {:last_name => 'Nunemaker'}, :order => 'age desc').should == [@doc1, @doc3]
340
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
339
341
  end
340
342
  end
341
343
 
@@ -348,7 +350,7 @@ class DocumentTest < Test::Unit::TestCase
348
350
  context "with #first" do
349
351
  should "find first document based on criteria" do
350
352
  @document.first(:order => 'first_name').should == @doc1
351
- @document.first(:conditions => {:age => 28}).should == @doc2
353
+ @document.first(:age => 28).should == @doc2
352
354
  end
353
355
  end
354
356
 
@@ -361,13 +363,13 @@ class DocumentTest < Test::Unit::TestCase
361
363
  context "with #last" do
362
364
  should "find last document based on criteria" do
363
365
  @document.last(:order => 'age').should == @doc2
364
- @document.last(:order => 'age', :conditions => {:age => 28}).should == @doc2
366
+ @document.last(:order => 'age', :age => 28).should == @doc2
365
367
  end
366
-
368
+
367
369
  should "raise error if no order provided" do
368
370
  lambda { @document.last() }.should raise_error
369
371
  end
370
- end
372
+ end
371
373
 
372
374
  context "with :find_by" do
373
375
  should "find document based on argument" do
@@ -669,11 +671,11 @@ class DocumentTest < Test::Unit::TestCase
669
671
  @thing.properties << @property3
670
672
  end
671
673
 
672
- should_eventually "destroy the thing" do
674
+ should "not execute on a belongs_to association" do
673
675
  Thing.count.should == 1
674
676
  @property1.destroy
675
- Thing.count.should == 0
676
- @property1.thing.should be_frozen
677
+ Thing.count.should == 1
678
+ @property1.should be_frozen
677
679
  end
678
680
  end
679
681
  end
@@ -722,14 +724,14 @@ class DocumentTest < Test::Unit::TestCase
722
724
  should "allow creating index for a key" do
723
725
  @document.ensure_index :first_name
724
726
  MongoMapper.ensure_indexes!
725
-
726
- @document.should have_index('first_name_1')
727
+
728
+ @document.should have_index('first_name_1')
727
729
  end
728
730
 
729
731
  should "allow creating unique index for a key" do
730
732
  @document.ensure_index :first_name, :unique => true
731
733
  MongoMapper.ensure_indexes!
732
-
734
+
733
735
  @document.should have_index('first_name_1')
734
736
  end
735
737
 
@@ -737,13 +739,19 @@ class DocumentTest < Test::Unit::TestCase
737
739
  @document.ensure_index [[:first_name, 1], [:last_name, -1]]
738
740
  MongoMapper.ensure_indexes!
739
741
 
740
- @document.should have_index('last_name_-1_first_name_1')
742
+ # order is different for different versions of ruby so instead of
743
+ # just checking have_index('first_name_1_last_name_-1') I'm checking
744
+ # the values of the indexes to make sure the index creation was successful
745
+ @document.collection.index_information.detect do |index|
746
+ keys = index[1]
747
+ keys.include?(['first_name', 1]) && keys.include?(['last_name', -1])
748
+ end.should_not be_nil
741
749
  end
742
750
 
743
751
  should "work with :index shortcut when defining key" do
744
752
  @document.key :father, String, :index => true
745
753
  MongoMapper.ensure_indexes!
746
-
754
+
747
755
  @document.should have_index('father_1')
748
756
  end
749
757
  end
@@ -794,7 +802,7 @@ class DocumentTest < Test::Unit::TestCase
794
802
  from_db = RealPerson.find(person.id)
795
803
  from_db.name.should == "David"
796
804
  end
797
-
805
+
798
806
  context "with key of type date" do
799
807
  should "save the date value as a Time object" do
800
808
  doc = @document.new(:first_name => 'John', :age => '27', :date => "12/01/2009")
@@ -891,16 +899,16 @@ class DocumentTest < Test::Unit::TestCase
891
899
  from_db.age.should == 30
892
900
  end
893
901
  end
894
-
902
+
895
903
  context "update_attributes" do
896
904
  setup do
897
905
  @document.key :foo, String, :required => true
898
906
  end
899
-
907
+
900
908
  should "return true if document valid" do
901
909
  @document.new.update_attributes(:foo => 'bar').should be_true
902
910
  end
903
-
911
+
904
912
  should "return false if document not valid" do
905
913
  @document.new.update_attributes({}).should be_false
906
914
  end
@@ -941,63 +949,158 @@ class DocumentTest < Test::Unit::TestCase
941
949
  end
942
950
  end
943
951
  end
944
-
952
+
945
953
  context "Single collection inheritance" do
946
954
  setup do
947
955
  class ::DocParent
948
956
  include MongoMapper::Document
949
957
  key :_type, String
958
+ key :name, String
950
959
  end
951
-
952
- class ::DocChild < ::DocParent; end
953
960
  DocParent.collection.clear
954
961
 
962
+ class ::DocDaughter < ::DocParent; end
963
+ class ::DocSon < ::DocParent; end
964
+ class ::DocGrandSon < ::DocSon; end
965
+
955
966
  @parent = DocParent.new({:name => "Daddy Warbucks"})
956
- @child = DocChild.new({:name => "Little Orphan Annie"})
967
+ @daughter = DocDaughter.new({:name => "Little Orphan Annie"})
957
968
  end
958
969
 
959
970
  teardown do
960
- Object.send :remove_const, 'DocParent' if defined?(::DocParent)
961
- Object.send :remove_const, 'DocChild' if defined?(::DocChild)
971
+ Object.send :remove_const, 'DocParent' if defined?(::DocParent)
972
+ Object.send :remove_const, 'DocDaughter' if defined?(::DocDaughter)
973
+ Object.send :remove_const, 'DocSon' if defined?(::DocSon)
974
+ Object.send :remove_const, 'DocGrandSon' if defined?(::DocGrandSon)
962
975
  end
963
976
 
964
977
  should "use the same collection in the subclass" do
965
- DocChild.collection.name.should == DocParent.collection.name
978
+ DocDaughter.collection.name.should == DocParent.collection.name
966
979
  end
967
980
 
968
981
  should "assign the class name into the _type property" do
969
982
  @parent._type.should == 'DocParent'
970
- @child._type.should == 'DocChild'
983
+ @daughter._type.should == 'DocDaughter'
971
984
  end
972
985
 
973
986
  should "load the document with the assigned type" do
974
987
  @parent.save
975
- @child.save
976
-
988
+ @daughter.save
989
+
977
990
  collection = DocParent.find(:all)
978
991
  collection.size.should == 2
979
992
  collection.first.should be_kind_of(DocParent)
980
993
  collection.first.name.should == "Daddy Warbucks"
981
- collection.last.should be_kind_of(DocChild)
994
+ collection.last.should be_kind_of(DocDaughter)
982
995
  collection.last.name.should == "Little Orphan Annie"
983
996
  end
984
-
997
+
985
998
  should "gracefully handle when the type can't be constantized" do
986
999
  doc = DocParent.new(:name => 'Nunes')
987
1000
  doc._type = 'FoobarBaz'
988
1001
  doc.save
989
-
1002
+
990
1003
  collection = DocParent.all
991
1004
  collection.last.should == doc
992
1005
  collection.last.should be_kind_of(DocParent)
993
1006
  end
1007
+
1008
+ should "find scoped to class" do
1009
+ john = DocSon.create(:name => 'John')
1010
+ steve = DocSon.create(:name => 'Steve')
1011
+ steph = DocDaughter.create(:name => 'Steph')
1012
+ carrie = DocDaughter.create(:name => 'Carrie')
1013
+
1014
+ DocGrandSon.all(:order => 'name').should == []
1015
+ DocSon.all(:order => 'name').should == [john, steve]
1016
+ DocDaughter.all(:order => 'name').should == [carrie, steph]
1017
+ DocParent.all(:order => 'name').should == [carrie, john, steph, steve]
1018
+ end
1019
+
1020
+ should "raise error if not found scoped to class" do
1021
+ john = DocSon.create(:name => 'John')
1022
+ steph = DocDaughter.create(:name => 'Steph')
1023
+
1024
+ lambda {
1025
+ DocSon.find(steph.id)
1026
+ }.should raise_error(MongoMapper::DocumentNotFound)
1027
+ end
1028
+
1029
+ should "not raise error for find with parent" do
1030
+ john = DocSon.create(:name => 'John')
1031
+
1032
+ DocParent.find(john.id).should == john
1033
+ end
1034
+
1035
+ should "count scoped to class" do
1036
+ john = DocSon.create(:name => 'John')
1037
+ steve = DocSon.create(:name => 'Steve')
1038
+ steph = DocDaughter.create(:name => 'Steph')
1039
+ carrie = DocDaughter.create(:name => 'Carrie')
1040
+
1041
+ DocGrandSon.count.should == 0
1042
+ DocSon.count.should == 2
1043
+ DocDaughter.count.should == 2
1044
+ DocParent.count.should == 4
1045
+ end
1046
+
1047
+ should "know if it is single_collection_inherited?" do
1048
+ DocParent.single_collection_inherited?.should be_false
1049
+
1050
+ DocDaughter.single_collection_inherited?.should be_true
1051
+ DocSon.single_collection_inherited?.should be_true
1052
+ end
1053
+
1054
+ should "know if single_collection_inherited_superclass?" do
1055
+ DocParent.single_collection_inherited_superclass?.should be_false
1056
+
1057
+ DocDaughter.single_collection_inherited_superclass?.should be_true
1058
+ DocSon.single_collection_inherited_superclass?.should be_true
1059
+ DocGrandSon.single_collection_inherited_superclass?.should be_true
1060
+ end
1061
+
1062
+ should "not be able to destroy each other" do
1063
+ john = DocSon.create(:name => 'John')
1064
+ steph = DocDaughter.create(:name => 'Steph')
1065
+
1066
+ lambda {
1067
+ DocSon.destroy(steph.id)
1068
+ }.should raise_error(MongoMapper::DocumentNotFound)
1069
+ end
1070
+
1071
+ should "not be able to delete each other" do
1072
+ john = DocSon.create(:name => 'John')
1073
+ steph = DocDaughter.create(:name => 'Steph')
1074
+
1075
+ lambda {
1076
+ DocSon.delete(steph.id)
1077
+ }.should_not change { DocParent.count }
1078
+ end
1079
+
1080
+ should "be able to destroy using parent" do
1081
+ john = DocSon.create(:name => 'John')
1082
+ steph = DocDaughter.create(:name => 'Steph')
1083
+
1084
+ lambda {
1085
+ DocParent.destroy_all
1086
+ }.should change { DocParent.count }.by(-2)
1087
+ end
1088
+
1089
+ should "be able to delete using parent" do
1090
+ john = DocSon.create(:name => 'John')
1091
+ steph = DocDaughter.create(:name => 'Steph')
1092
+
1093
+ lambda {
1094
+ DocParent.delete_all
1095
+ }.should change { DocParent.count }.by(-2)
1096
+ end
994
1097
  end
995
1098
 
996
1099
  context "timestamping" do
997
1100
  setup do
998
1101
  @document.timestamps!
999
1102
  end
1000
-
1103
+
1001
1104
  should "set created_at and updated_at on create" do
1002
1105
  doc = @document.new(:first_name => 'John', :age => 27)
1003
1106
  doc.created_at.should be(nil)
@@ -1012,11 +1115,11 @@ class DocumentTest < Test::Unit::TestCase
1012
1115
  old_created_at = doc.created_at
1013
1116
  old_updated_at = doc.updated_at
1014
1117
  doc.first_name = 'Johnny'
1015
-
1118
+
1016
1119
  Timecop.freeze(Time.now + 5.seconds) do
1017
1120
  doc.save
1018
1121
  end
1019
-
1122
+
1020
1123
  doc.created_at.should == old_created_at
1021
1124
  doc.updated_at.should_not == old_updated_at
1022
1125
  end
@@ -1025,7 +1128,7 @@ class DocumentTest < Test::Unit::TestCase
1025
1128
  doc = @document.create(:first_name => 'John', :age => 27)
1026
1129
  old_created_at = doc.created_at
1027
1130
  old_updated_at = doc.updated_at
1028
-
1131
+
1029
1132
  Timecop.freeze(Time.now + 5.seconds) do
1030
1133
  @document.update(doc._id, { :first_name => 'Johnny' })
1031
1134
  end