mongo_mapper-unstable 2009.11.8 → 2009.11.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/Rakefile +3 -3
  2. data/VERSION +1 -1
  3. data/bin/mmconsole +10 -5
  4. data/lib/mongo_mapper.rb +28 -5
  5. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +1 -1
  6. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +1 -1
  7. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +2 -2
  8. data/lib/mongo_mapper/associations/many_documents_proxy.rb +2 -2
  9. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +2 -1
  10. data/lib/mongo_mapper/document.rb +3 -12
  11. data/lib/mongo_mapper/embedded_document.rb +37 -19
  12. data/lib/mongo_mapper/finder_options.rb +17 -11
  13. data/lib/mongo_mapper/rails_compatibility/document.rb +4 -0
  14. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +4 -0
  15. data/lib/mongo_mapper/serializers/json_serializer.rb +2 -2
  16. data/lib/mongo_mapper/support.rb +16 -44
  17. data/lib/mongo_mapper/types.rb +64 -0
  18. data/lib/mongo_mapper/validations.rb +1 -1
  19. data/mongo_mapper.gemspec +15 -12
  20. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +10 -10
  21. data/test/functional/associations/test_belongs_to_proxy.rb +2 -1
  22. data/test/functional/associations/test_many_documents_as_proxy.rb +13 -12
  23. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +34 -34
  24. data/test/functional/associations/test_many_embedded_proxy.rb +22 -22
  25. data/test/functional/associations/test_many_polymorphic_proxy.rb +10 -10
  26. data/test/functional/associations/test_many_proxy.rb +14 -15
  27. data/test/functional/test_associations.rb +4 -4
  28. data/test/functional/test_binary.rb +1 -1
  29. data/test/functional/test_dirty.rb +6 -6
  30. data/test/functional/test_document.rb +64 -65
  31. data/test/functional/test_embedded_document.rb +34 -14
  32. data/test/functional/test_string_id_compatibility.rb +72 -0
  33. data/test/functional/test_validations.rb +1 -1
  34. data/test/models.rb +24 -24
  35. data/test/unit/test_document.rb +7 -4
  36. data/test/unit/test_embedded_document.rb +47 -5
  37. data/test/unit/test_finder_options.rb +22 -3
  38. data/test/unit/test_mongo_mapper.rb +65 -0
  39. data/test/unit/test_rails_compatibility.rb +14 -0
  40. data/test/unit/test_support.rb +45 -0
  41. metadata +8 -5
  42. data/test/unit/test_mongomapper.rb +0 -28
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ Jeweler::Tasks.new do |gem|
12
12
  gem.authors = ["John Nunemaker"]
13
13
 
14
14
  gem.add_dependency('activesupport', '>= 2.3')
15
- gem.add_dependency('mongo', '0.16')
15
+ gem.add_dependency('mongo', '0.17.1')
16
16
  gem.add_dependency('jnunemaker-validatable', '1.8.1')
17
17
 
18
18
  gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
@@ -51,5 +51,5 @@ task :default => :test
51
51
  task :test => :check_dependencies
52
52
 
53
53
  YARD::Rake::YardocTask.new(:doc) do |t|
54
- t.options = ["--legacy"]
55
- end
54
+ t.options = ["--legacy"] if RUBY_VERSION < "1.9.0"
55
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2009.11.08
1
+ 2009.11.18
data/bin/mmconsole CHANGED
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
- $:.unshift File.dirname(__FILE__)+"/../lib"
3
-
4
- require 'mongo_mapper'
5
- require 'irb'
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
3
+
4
+ begin
5
+ require 'mongo_mapper'
6
+ require 'irb'
7
+ rescue LoadError
8
+ require 'rubygems'
9
+ retry
10
+ end
6
11
 
7
12
  IRB.setup(nil)
8
13
  irb = IRB::Irb.new
@@ -43,12 +48,12 @@ Example 2:
43
48
 
44
49
  all_things = Thing.all
45
50
  puts all_things.map { |object| object.name }.inspect
46
-
47
51
  @
48
52
 
49
53
  trap("SIGINT") do
50
54
  irb.signal_handle
51
55
  end
56
+
52
57
  catch(:IRB_EXIT) do
53
58
  irb.eval_input
54
59
  end
data/lib/mongo_mapper.rb CHANGED
@@ -15,28 +15,32 @@ module MongoMapper
15
15
  # raised when document not valid and using !
16
16
  class DocumentNotValid < MongoMapperError
17
17
  def initialize(document)
18
- @document = document
19
- super("Validation failed: #{@document.errors.full_messages.join(", ")}")
18
+ super("Validation failed: #{document.errors.full_messages.join(", ")}")
20
19
  end
21
20
  end
22
21
 
22
+ # @api public
23
23
  def self.connection
24
24
  @@connection ||= Mongo::Connection.new
25
25
  end
26
-
26
+
27
+ # @api public
27
28
  def self.connection=(new_connection)
28
29
  @@connection = new_connection
29
30
  end
30
31
 
32
+ # @api public
31
33
  def self.logger
32
34
  connection.logger
33
35
  end
34
-
36
+
37
+ # @api public
35
38
  def self.database=(name)
36
39
  @@database = nil
37
40
  @@database_name = name
38
41
  end
39
-
42
+
43
+ # @api public
40
44
  def self.database
41
45
  if @@database_name.blank?
42
46
  raise 'You forgot to set the default database name: MongoMapper.database = "foobar"'
@@ -45,14 +49,17 @@ module MongoMapper
45
49
  @@database ||= MongoMapper.connection.db(@@database_name)
46
50
  end
47
51
 
52
+ # @api private
48
53
  def self.ensured_indexes
49
54
  @@ensured_indexes ||= []
50
55
  end
51
56
 
57
+ # @api private
52
58
  def self.ensure_index(klass, keys, options={})
53
59
  ensured_indexes << {:klass => klass, :keys => keys, :options => options}
54
60
  end
55
61
 
62
+ # @api public
56
63
  def self.ensure_indexes!
57
64
  ensured_indexes.each do |index|
58
65
  unique = index[:options].delete(:unique)
@@ -60,6 +67,7 @@ module MongoMapper
60
67
  end
61
68
  end
62
69
 
70
+ # @api private
63
71
  module Finders
64
72
  def dynamic_find(finder, args)
65
73
  attributes = {}
@@ -83,9 +91,24 @@ module MongoMapper
83
91
  end
84
92
  end
85
93
  end
94
+
95
+ # @api private
96
+ def self.use_time_zone?
97
+ Time.respond_to?(:zone) && Time.zone ? true : false
98
+ end
99
+
100
+ # @api private
101
+ def self.time_class
102
+ use_time_zone? ? Time.zone : Time
103
+ end
104
+
105
+ def self.normalize_object_id(value)
106
+ value.is_a?(String) ? Mongo::ObjectID.from_string(value) : value
107
+ end
86
108
  end
87
109
 
88
110
  require 'mongo_mapper/support'
111
+ require 'mongo_mapper/types'
89
112
  require 'mongo_mapper/associations'
90
113
  require 'mongo_mapper/associations/base'
91
114
  require 'mongo_mapper/associations/proxy'
@@ -4,7 +4,7 @@ module MongoMapper
4
4
  def replace(doc)
5
5
  if doc
6
6
  doc.save if doc.new?
7
- id, type = doc.id, doc.class.name
7
+ id, type = doc._id, doc.class.name
8
8
  end
9
9
 
10
10
  @owner.send("#{@association.foreign_key}=", id)
@@ -4,7 +4,7 @@ module MongoMapper
4
4
  def replace(doc)
5
5
  if doc
6
6
  doc.save if doc.new?
7
- id = doc.id
7
+ id = doc._id
8
8
  end
9
9
 
10
10
  @owner.send("#{@association.foreign_key}=", id)
@@ -3,13 +3,13 @@ module MongoMapper
3
3
  class ManyDocumentsAsProxy < ManyDocumentsProxy
4
4
  protected
5
5
  def scoped_conditions
6
- {as_type_name => @owner.class.name, as_id_name => @owner.id}
6
+ {as_type_name => @owner.class.name, as_id_name => @owner._id}
7
7
  end
8
8
 
9
9
  def apply_scope(doc)
10
10
  ensure_owner_saved
11
11
  doc.send("#{as_type_name}=", @owner.class.name)
12
- doc.send("#{as_id_name}=", @owner.id)
12
+ doc.send("#{as_id_name}=", @owner._id)
13
13
  doc
14
14
  end
15
15
 
@@ -98,7 +98,7 @@ module MongoMapper
98
98
 
99
99
  protected
100
100
  def scoped_conditions
101
- {self.foreign_key => @owner.id}
101
+ {self.foreign_key => @owner._id}
102
102
  end
103
103
 
104
104
  def scoped_options(options)
@@ -115,7 +115,7 @@ module MongoMapper
115
115
 
116
116
  def apply_scope(doc)
117
117
  ensure_owner_saved
118
- doc.send("#{self.foreign_key}=", @owner.id)
118
+ doc.send("#{self.foreign_key}=", @owner._id)
119
119
  doc
120
120
  end
121
121
 
@@ -13,9 +13,10 @@ module MongoMapper
13
13
  doc
14
14
  end
15
15
 
16
+ # TODO: test that both string and oid version work
16
17
  def find(id)
17
18
  load_target
18
- @target.detect { |item| item.id == id }
19
+ @target.detect { |item| item.id == id || item._id == id }
19
20
  end
20
21
 
21
22
  def <<(*docs)
@@ -344,15 +344,6 @@ module MongoMapper
344
344
  instances.size == 1 ? instances[0] : instances
345
345
  end
346
346
 
347
- def initialize_doc(doc)
348
- begin
349
- klass = doc['_type'].present? ? doc['_type'].constantize : self
350
- klass.new(doc)
351
- rescue NameError
352
- new(doc)
353
- end
354
- end
355
-
356
347
  def find_every(options)
357
348
  criteria, options = to_finder_options(options)
358
349
  collection.find(criteria, options).to_a.map do |doc|
@@ -442,12 +433,12 @@ module MongoMapper
442
433
 
443
434
  def destroy
444
435
  return false if frozen?
445
- self.class.delete(id) unless new?
436
+ self.class.delete(_id) unless new?
446
437
  freeze
447
438
  end
448
439
 
449
440
  def reload
450
- self.class.find(id)
441
+ self.class.find(_id)
451
442
  end
452
443
 
453
444
  private
@@ -463,7 +454,7 @@ module MongoMapper
463
454
 
464
455
  def assign_id
465
456
  if read_attribute(:_id).blank?
466
- write_attribute(:_id, Mongo::ObjectID.new.to_s)
457
+ write_attribute :_id, Mongo::ObjectID.new
467
458
  end
468
459
  end
469
460
 
@@ -16,7 +16,7 @@ module MongoMapper
16
16
 
17
17
  extend Validations::Macros
18
18
 
19
- key :_id, String
19
+ key :_id, ObjectId
20
20
  attr_accessor :_root_document
21
21
  end
22
22
  end
@@ -25,7 +25,7 @@ module MongoMapper
25
25
  def logger
26
26
  MongoMapper.logger
27
27
  end
28
-
28
+
29
29
  def inherited(subclass)
30
30
  unless subclass.embeddable?
31
31
  subclass.set_collection_name(collection_name)
@@ -48,19 +48,23 @@ module MongoMapper
48
48
 
49
49
  def key(*args)
50
50
  key = Key.new(*args)
51
+ keys[key.name] = key
51
52
 
52
- if keys[key.name].blank?
53
- keys[key.name] = key
53
+ create_accessors_for(key)
54
+ create_key_in_subclasses(*args)
55
+ create_validations_for(key)
54
56
 
55
- create_accessors_for(key)
56
- create_key_in_subclasses(*args)
57
- create_validations_for(key)
58
-
59
- key
60
- end
61
-
62
57
  key
63
58
  end
59
+
60
+ def using_object_id?
61
+ object_id_key?(:_id)
62
+ end
63
+
64
+ def object_id_key?(name)
65
+ key = keys[name.to_s]
66
+ key && key.type == ObjectId
67
+ end
64
68
 
65
69
  def embeddable?
66
70
  !self.ancestors.include?(Document)
@@ -83,11 +87,20 @@ module MongoMapper
83
87
  if instance_or_hash.is_a?(self)
84
88
  instance_or_hash
85
89
  else
86
- new(instance_or_hash)
90
+ initialize_doc(instance_or_hash)
87
91
  end
88
92
  end
89
93
 
90
94
  private
95
+ def initialize_doc(doc)
96
+ begin
97
+ klass = doc['_type'].present? ? doc['_type'].constantize : self
98
+ klass.new(doc)
99
+ rescue NameError
100
+ new(doc)
101
+ end
102
+ end
103
+
91
104
  def accessors_module
92
105
  module_defined = if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
93
106
  const_defined?('MongoMapperKeys')
@@ -191,16 +204,16 @@ module MongoMapper
191
204
  end
192
205
  end
193
206
 
194
- if self.class.embeddable?
207
+ if self.class.embeddable?
195
208
  if read_attribute(:_id).blank?
196
- write_attribute :_id, Mongo::ObjectID.new.to_s
209
+ write_attribute :_id, Mongo::ObjectID.new
197
210
  @new_document = true
198
211
  else
199
212
  @new_document = false
200
213
  end
201
214
  end
202
215
  end
203
-
216
+
204
217
  def new?
205
218
  !!@new_document
206
219
  end
@@ -275,15 +288,20 @@ module MongoMapper
275
288
  end
276
289
 
277
290
  def ==(other)
278
- other.is_a?(self.class) && id == other.id
291
+ other.is_a?(self.class) && _id == other._id
279
292
  end
280
293
 
281
294
  def id
282
- read_attribute(:_id)
295
+ read_attribute(:_id).to_s
283
296
  end
284
297
 
285
298
  def id=(value)
286
- @using_custom_id = true
299
+ if self.class.using_object_id?
300
+ value = MongoMapper.normalize_object_id(value)
301
+ else
302
+ @using_custom_id = true
303
+ end
304
+
287
305
  write_attribute :_id, value
288
306
  end
289
307
 
@@ -353,7 +371,7 @@ module MongoMapper
353
371
  def write_attribute(name, value)
354
372
  key = _keys[name]
355
373
  instance_variable_set "@#{name}_before_typecast", value
356
- instance_variable_set "@#{name}", key.set(value)
374
+ instance_variable_set "@#{name}", key.set(value)
357
375
  end
358
376
 
359
377
  def embedded_associations
@@ -8,17 +8,7 @@ module MongoMapper
8
8
  # useful for understanding how MongoMapper handles the parsing of finder
9
9
  # conditions and options.
10
10
  #
11
- # @private
12
- class FinderOperator
13
- def initialize(field, operator)
14
- @field, @operator = field, operator
15
- end
16
-
17
- def to_criteria(value)
18
- {@field => {@operator => value}}
19
- end
20
- end
21
-
11
+ # @private
22
12
  class FinderOptions
23
13
  OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
24
14
 
@@ -72,10 +62,16 @@ module MongoMapper
72
62
 
73
63
  conditions.each_pair do |field, value|
74
64
  field = normalized_field(field)
65
+
66
+ if @model.object_id_key?(field) && value.is_a?(String)
67
+ value = Mongo::ObjectID.from_string(value)
68
+ end
69
+
75
70
  if field.is_a?(FinderOperator)
76
71
  criteria.merge!(field.to_criteria(value))
77
72
  next
78
73
  end
74
+
79
75
  case value
80
76
  when Array
81
77
  operator_present = field.to_s =~ /^\$/
@@ -128,4 +124,14 @@ module MongoMapper
128
124
  [field, direction]
129
125
  end
130
126
  end
127
+
128
+ class FinderOperator
129
+ def initialize(field, operator)
130
+ @field, @operator = field, operator
131
+ end
132
+
133
+ def to_criteria(value)
134
+ {@field => {@operator => value}}
135
+ end
136
+ end
131
137
  end
@@ -4,6 +4,10 @@ module MongoMapper
4
4
  def self.included(model)
5
5
  model.class_eval do
6
6
  alias_method :new_record?, :new?
7
+
8
+ def human_name
9
+ self.name.demodulize.titleize
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -17,6 +17,10 @@ module MongoMapper
17
17
  def column_names
18
18
  keys.keys
19
19
  end
20
+
21
+ def human_name
22
+ self.name.demodulize.titleize
23
+ end
20
24
  end
21
25
  end
22
26
  end