mongo_mapper 0.6.10 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/README.rdoc +5 -14
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongo_mapper.rb +48 -56
  5. data/lib/mongo_mapper/document.rb +136 -164
  6. data/lib/mongo_mapper/embedded_document.rb +29 -354
  7. data/lib/mongo_mapper/plugins.rb +31 -0
  8. data/lib/mongo_mapper/plugins/associations.rb +105 -0
  9. data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
  10. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  11. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  12. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  13. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  14. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  15. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  16. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  17. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  18. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  19. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  20. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  21. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  22. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  23. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  24. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  25. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  26. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  27. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  28. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  29. data/lib/mongo_mapper/plugins/keys.rb +324 -0
  30. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  31. data/lib/mongo_mapper/plugins/pagination.rb +24 -0
  32. data/lib/mongo_mapper/plugins/pagination/proxy.rb +68 -0
  33. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  34. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  35. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  36. data/lib/mongo_mapper/plugins/validations.rb +46 -0
  37. data/lib/mongo_mapper/query.rb +130 -0
  38. data/lib/mongo_mapper/support.rb +40 -17
  39. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  40. data/lib/mongo_mapper/support/find.rb +77 -0
  41. data/mongo_mapper.gemspec +55 -38
  42. data/performance/read_write.rb +52 -0
  43. data/specs.watchr +23 -2
  44. data/test/functional/associations/test_belongs_to_proxy.rb +12 -10
  45. data/test/functional/associations/test_many_documents_as_proxy.rb +4 -21
  46. data/test/functional/associations/test_many_documents_proxy.rb +2 -8
  47. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +59 -39
  48. data/test/functional/associations/test_many_embedded_proxy.rb +145 -81
  49. data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -40
  50. data/test/functional/associations/test_one_proxy.rb +25 -10
  51. data/test/functional/test_binary.rb +2 -8
  52. data/test/functional/test_callbacks.rb +1 -5
  53. data/test/functional/test_dirty.rb +27 -23
  54. data/test/functional/test_document.rb +224 -165
  55. data/test/functional/test_embedded_document.rb +72 -82
  56. data/test/functional/test_identity_map.rb +508 -0
  57. data/test/functional/test_modifiers.rb +15 -5
  58. data/test/functional/test_pagination.rb +1 -3
  59. data/test/functional/test_protected.rb +155 -0
  60. data/test/functional/test_string_id_compatibility.rb +7 -12
  61. data/test/functional/test_validations.rb +26 -58
  62. data/test/models.rb +0 -39
  63. data/test/test_helper.rb +37 -3
  64. data/test/unit/associations/test_base.rb +5 -5
  65. data/test/unit/associations/test_proxy.rb +8 -6
  66. data/test/unit/test_descendant_appends.rb +71 -0
  67. data/test/unit/test_document.rb +71 -76
  68. data/test/unit/test_dynamic_finder.rb +27 -29
  69. data/test/unit/test_embedded_document.rb +555 -601
  70. data/test/unit/{test_key.rb → test_keys.rb} +2 -5
  71. data/test/unit/test_mongo_mapper.rb +69 -9
  72. data/test/unit/test_pagination.rb +40 -32
  73. data/test/unit/test_plugins.rb +50 -0
  74. data/test/unit/{test_finder_options.rb → test_query.rb} +74 -65
  75. data/test/unit/test_rails.rb +123 -0
  76. data/test/unit/{test_serializations.rb → test_serialization.rb} +1 -2
  77. data/test/unit/test_support.rb +23 -7
  78. data/test/unit/test_time_zones.rb +3 -4
  79. data/test/unit/test_validations.rb +58 -17
  80. metadata +53 -36
  81. data/lib/mongo_mapper/associations.rb +0 -78
  82. data/lib/mongo_mapper/associations/base.rb +0 -119
  83. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +0 -26
  84. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +0 -21
  85. data/lib/mongo_mapper/associations/collection.rb +0 -19
  86. data/lib/mongo_mapper/associations/in_array_proxy.rb +0 -137
  87. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -26
  88. data/lib/mongo_mapper/associations/many_documents_proxy.rb +0 -115
  89. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +0 -31
  90. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +0 -54
  91. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +0 -11
  92. data/lib/mongo_mapper/associations/one_proxy.rb +0 -64
  93. data/lib/mongo_mapper/associations/proxy.rb +0 -116
  94. data/lib/mongo_mapper/callbacks.rb +0 -61
  95. data/lib/mongo_mapper/dirty.rb +0 -117
  96. data/lib/mongo_mapper/dynamic_finder.rb +0 -74
  97. data/lib/mongo_mapper/finder_options.rb +0 -145
  98. data/lib/mongo_mapper/key.rb +0 -36
  99. data/lib/mongo_mapper/mongo_mapper.rb +0 -125
  100. data/lib/mongo_mapper/pagination.rb +0 -66
  101. data/lib/mongo_mapper/rails_compatibility/document.rb +0 -15
  102. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +0 -28
  103. data/lib/mongo_mapper/serialization.rb +0 -54
  104. data/lib/mongo_mapper/serializers/json_serializer.rb +0 -48
  105. data/lib/mongo_mapper/validations.rb +0 -39
  106. data/test/functional/test_rails_compatibility.rb +0 -25
@@ -13,25 +13,16 @@ Releases are tagged on github and released on gemcutter. Master is pushed to whe
13
13
  * Send me a pull request. Bonus points for topic branches.
14
14
 
15
15
  == Install
16
-
17
- $ gem install mongo_mapper
18
-
19
- == Dependencies
20
-
21
- * ActiveSupport (typically the latest version)
22
- * Mongo Ruby Driver (mongo)
23
- * My fork of the validatable gem (jnunemaker-validatable)
24
16
 
25
- == Documentation
17
+ $ gem install mongo_mapper
26
18
 
27
- Documentation is lacking right now because if you can't look through the code right now and feel comfortable, this is probably too young for you to use. Wait til it stabilizes a bit more.
19
+ == Problems or Questions?
28
20
 
21
+ Hit up the google group.
29
22
  http://groups.google.com/group/mongomapper
30
23
 
31
- == More Info
32
-
33
- * http://www.mongodb.org/
34
- * http://github.com/mongodb/mongo-ruby-driver/
24
+ To see if the problem you are having is a verified issue, you can see the MM pivotal tracker project:
25
+ http://www.pivotaltracker.com/projects/33576
35
26
 
36
27
  == Copyright
37
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.18.2')
15
+ gem.add_dependency('mongo', '0.18.3')
16
16
  gem.add_dependency('jnunemaker-validatable', '1.8.1')
17
17
 
18
18
  gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.10
1
+ 0.7.0
@@ -1,10 +1,12 @@
1
+ require 'set'
2
+
1
3
  # if Gem is defined i'll assume you are using rubygems and lock specific versions
2
4
  # call me crazy but a plain old require will just get the latest version you have installed
3
5
  # so i want to make sure that if you are using gems you do in fact have the correct versions
4
6
  # if there is a better way to do this, please enlighten me!
5
7
  if self.class.const_defined?(:Gem)
6
8
  gem 'activesupport', '>= 2.3'
7
- gem 'mongo', '0.18.2'
9
+ gem 'mongo', '0.18.3'
8
10
  gem 'jnunemaker-validatable', '1.8.1'
9
11
  end
10
12
 
@@ -15,112 +17,102 @@ require 'validatable'
15
17
  module MongoMapper
16
18
  # generic MM error
17
19
  class MongoMapperError < StandardError; end
18
-
20
+
19
21
  # raised when key expected to exist but not found
20
22
  class KeyNotFound < MongoMapperError; end
21
-
23
+
22
24
  # raised when document expected but not found
23
25
  class DocumentNotFound < MongoMapperError; end
24
-
26
+
25
27
  # raised when document not valid and using !
26
28
  class DocumentNotValid < MongoMapperError
27
29
  def initialize(document)
28
30
  super("Validation failed: #{document.errors.full_messages.join(", ")}")
29
31
  end
30
32
  end
31
-
33
+
32
34
  # @api public
33
35
  def self.connection
34
36
  @@connection ||= Mongo::Connection.new
35
37
  end
36
-
38
+
37
39
  # @api public
38
40
  def self.connection=(new_connection)
39
41
  @@connection = new_connection
40
42
  end
41
-
43
+
42
44
  # @api public
43
45
  def self.logger
44
46
  connection.logger
45
47
  end
46
-
48
+
47
49
  # @api public
48
50
  def self.database=(name)
49
51
  @@database = nil
50
52
  @@database_name = name
51
53
  end
52
-
54
+
53
55
  # @api public
54
56
  def self.database
55
57
  if @@database_name.blank?
56
58
  raise 'You forgot to set the default database name: MongoMapper.database = "foobar"'
57
59
  end
58
-
60
+
59
61
  @@database ||= MongoMapper.connection.db(@@database_name)
60
62
  end
61
-
62
- # @api private
63
- def self.ensured_indexes
64
- @@ensured_indexes ||= []
63
+
64
+ def self.config=(hash)
65
+ @@config = hash
65
66
  end
66
-
67
- # @api private
68
- def self.ensured_indexes=(value)
69
- @@ensured_indexes = value
67
+
68
+ def self.config
69
+ raise 'Set config before connecting. MongoMapper.config = {...}' unless defined?(@@config)
70
+ @@config
70
71
  end
71
-
72
- # @api private
73
- def self.ensure_index(klass, keys, options={})
74
- ensured_indexes << {:klass => klass, :keys => keys, :options => options}
72
+
73
+ def self.connect(environment, options={})
74
+ raise 'Set config before connecting. MongoMapper.config = {...}' if config.blank?
75
+ MongoMapper.connection = Mongo::Connection.new(config[environment]['host'], config[environment]['port'], options)
76
+ MongoMapper.database = config[environment]['database']
77
+ if config[environment]['username'].present? && config[environment]['password'].present?
78
+ MongoMapper.database.authenticate(config[environment]['username'], config[environment]['password'])
79
+ end
75
80
  end
76
-
77
- # @api public
78
- def self.ensure_indexes!
79
- ensured_indexes.each do |index|
80
- unique = index[:options].delete(:unique)
81
- index[:klass].collection.create_index(index[:keys], unique)
81
+
82
+ def self.setup(config, environment, options={})
83
+ using_passenger = options.delete(:passenger)
84
+ handle_passenger_forking if using_passenger
85
+ self.config = config
86
+ connect(environment, options)
87
+ end
88
+
89
+ def self.handle_passenger_forking
90
+ if defined?(PhusionPassenger)
91
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
92
+ connection.connect_to_master if forked
93
+ end
82
94
  end
83
95
  end
84
-
96
+
85
97
  # @api private
86
98
  def self.use_time_zone?
87
99
  Time.respond_to?(:zone) && Time.zone ? true : false
88
100
  end
89
-
101
+
90
102
  # @api private
91
103
  def self.time_class
92
104
  use_time_zone? ? Time.zone : Time
93
105
  end
94
-
106
+
95
107
  # @api private
96
108
  def self.normalize_object_id(value)
97
109
  value.is_a?(String) ? Mongo::ObjectID.from_string(value) : value
98
110
  end
111
+
112
+ autoload :Query, 'mongo_mapper/query'
113
+ autoload :Document, 'mongo_mapper/document'
114
+ autoload :EmbeddedDocument, 'mongo_mapper/embedded_document'
99
115
  end
100
116
 
101
117
  require 'mongo_mapper/support'
102
- require 'mongo_mapper/callbacks'
103
- require 'mongo_mapper/finder_options'
104
- require 'mongo_mapper/dirty'
105
- require 'mongo_mapper/dynamic_finder'
106
- require 'mongo_mapper/key'
107
- require 'mongo_mapper/pagination'
108
- require 'mongo_mapper/serialization'
109
- require 'mongo_mapper/validations'
110
- require 'mongo_mapper/rails_compatibility/document'
111
- require 'mongo_mapper/rails_compatibility/embedded_document'
112
- require 'mongo_mapper/embedded_document'
113
- require 'mongo_mapper/document'
114
- require 'mongo_mapper/associations'
115
- require 'mongo_mapper/associations/base'
116
- require 'mongo_mapper/associations/proxy'
117
- require 'mongo_mapper/associations/collection'
118
- require 'mongo_mapper/associations/many_documents_proxy'
119
- require 'mongo_mapper/associations/belongs_to_proxy'
120
- require 'mongo_mapper/associations/belongs_to_polymorphic_proxy'
121
- require 'mongo_mapper/associations/many_polymorphic_proxy'
122
- require 'mongo_mapper/associations/many_embedded_proxy'
123
- require 'mongo_mapper/associations/many_embedded_polymorphic_proxy'
124
- require 'mongo_mapper/associations/many_documents_as_proxy'
125
- require 'mongo_mapper/associations/one_proxy'
126
- require 'mongo_mapper/associations/in_array_proxy'
118
+ require 'mongo_mapper/plugins'
@@ -1,66 +1,39 @@
1
- require 'set'
2
-
3
1
  module MongoMapper
4
2
  module Document
3
+ extend Support::DescendantAppends
4
+
5
5
  def self.included(model)
6
6
  model.class_eval do
7
- include EmbeddedDocument
8
7
  include InstanceMethods
9
- include Callbacks
10
- include Dirty
11
- include RailsCompatibility::Document
12
- extend Validations::Macros
13
- extend ClassMethods
14
- extend Finders
15
-
16
- def self.per_page
17
- 25
18
- end unless respond_to?(:per_page)
19
- end
20
-
21
- extra_extensions.each { |extension| model.extend(extension) }
22
- extra_inclusions.each { |inclusion| model.send(:include, inclusion) }
23
-
24
- descendants << model
25
- end
26
-
27
- def self.descendants
28
- @descendants ||= Set.new
29
- end
30
-
31
- def self.append_extensions(*extensions)
32
- extra_extensions.concat extensions
33
-
34
- # Add the extension to existing descendants
35
- descendants.each do |model|
36
- extensions.each { |extension| model.extend(extension) }
37
- end
8
+ extend Support::Find
9
+ extend ClassMethods
10
+ extend Plugins
11
+
12
+ plugin Plugins::Associations
13
+ plugin Plugins::Clone
14
+ plugin Plugins::Descendants
15
+ plugin Plugins::Equality
16
+ plugin Plugins::Inspect
17
+ plugin Plugins::Keys
18
+ plugin Plugins::Dirty # for now dirty needs to be after keys
19
+ plugin Plugins::Logger
20
+ plugin Plugins::Pagination
21
+ plugin Plugins::Protected
22
+ plugin Plugins::Rails
23
+ plugin Plugins::Serialization
24
+ plugin Plugins::Validations
25
+ plugin Plugins::Callbacks # for now callbacks needs to be after validations
26
+
27
+ extend Plugins::Validations::DocumentMacros
28
+ end
29
+
30
+ super
38
31
  end
39
32
 
40
- # @api private
41
- def self.extra_extensions
42
- @extra_extensions ||= []
43
- end
44
-
45
- def self.append_inclusions(*inclusions)
46
- extra_inclusions.concat inclusions
47
-
48
- # Add the inclusion to existing descendants
49
- descendants.each do |model|
50
- inclusions.each { |inclusion| model.send :include, inclusion }
51
- end
52
- end
53
-
54
- # @api private
55
- def self.extra_inclusions
56
- @extra_inclusions ||= []
57
- end
58
-
59
33
  module ClassMethods
60
- def key(*args)
61
- key = super
62
- create_indexes_for(key)
63
- key
34
+ def inherited(subclass)
35
+ subclass.set_collection_name(collection_name)
36
+ super
64
37
  end
65
38
 
66
39
  def ensure_index(name_or_array, options={})
@@ -70,43 +43,50 @@ module MongoMapper
70
43
  name_or_array
71
44
  end
72
45
 
73
- MongoMapper.ensure_index(self, keys_to_index, options)
46
+ collection.create_index(keys_to_index, options[:unique])
47
+ end
48
+
49
+ def find(*args)
50
+ assert_no_first_last_or_all(args)
51
+ options = args.extract_options!
52
+ return nil if args.size == 0
53
+
54
+ if args.first.is_a?(Array) || args.size > 1
55
+ find_some(args, options)
56
+ else
57
+ find_one(options.merge({:_id => args[0]}))
58
+ end
74
59
  end
75
60
 
76
61
  def find!(*args)
62
+ assert_no_first_last_or_all(args)
77
63
  options = args.extract_options!
78
- case args.first
79
- when :first then first(options)
80
- when :last then last(options)
81
- when :all then find_every(options)
82
- when Array then find_some(args, options)
83
- else
84
- case args.size
85
- when 0
86
- raise DocumentNotFound, "Couldn't find without an ID"
87
- when 1
88
- find_one!(options.merge({:_id => args[0]}))
89
- else
90
- find_some(args, options)
91
- end
64
+ raise DocumentNotFound, "Couldn't find without an ID" if args.size == 0
65
+
66
+ if args.first.is_a?(Array) || args.size > 1
67
+ find_some!(args, options)
68
+ else
69
+ find_one(options.merge({:_id => args[0]})) || raise(DocumentNotFound, "Document match #{options.inspect} does not exist in #{collection.name} collection")
92
70
  end
93
71
  end
94
-
95
- def find(*args)
96
- find!(*args)
97
- rescue DocumentNotFound
98
- nil
72
+
73
+ def find_each(options={})
74
+ criteria, options = to_query(options)
75
+ collection.find(criteria, options).each do |doc|
76
+ yield load(doc)
77
+ end
99
78
  end
100
79
 
101
- def paginate(options)
102
- per_page = options.delete(:per_page) || self.per_page
103
- page = options.delete(:page)
104
- total_entries = count(options)
105
- pagination = Pagination::PaginationProxy.new(total_entries, page, per_page)
80
+ def find_by_id(id)
81
+ find(id)
82
+ end
106
83
 
107
- options.merge!(:limit => pagination.limit, :skip => pagination.skip)
108
- pagination.subject = find_every(options)
109
- pagination
84
+ def first_or_create(arg)
85
+ first(arg) || create(arg)
86
+ end
87
+
88
+ def first_or_new(arg)
89
+ first(arg) || new(arg)
110
90
  end
111
91
 
112
92
  def first(options={})
@@ -119,11 +99,7 @@ module MongoMapper
119
99
  end
120
100
 
121
101
  def all(options={})
122
- find_every(options)
123
- end
124
-
125
- def find_by_id(id)
126
- find(id)
102
+ find_many(options)
127
103
  end
128
104
 
129
105
  def count(options={})
@@ -160,63 +136,57 @@ module MongoMapper
160
136
  end
161
137
 
162
138
  def destroy(*ids)
163
- find_some(ids.flatten).each(&:destroy)
139
+ find_some!(ids.flatten).each(&:destroy)
164
140
  end
165
141
 
166
142
  def destroy_all(options={})
167
- all(options).each(&:destroy)
143
+ find_each(options) { |document| document.destroy }
168
144
  end
169
-
145
+
170
146
  def increment(*args)
171
147
  modifier_update('$inc', args)
172
148
  end
173
-
149
+
174
150
  def decrement(*args)
175
151
  criteria, keys = criteria_and_keys_from_args(args)
176
152
  values, to_decrement = keys.values, {}
177
153
  keys.keys.each_with_index { |k, i| to_decrement[k] = -values[i].abs }
178
154
  collection.update(criteria, {'$inc' => to_decrement}, :multi => true)
179
155
  end
180
-
156
+
181
157
  def set(*args)
182
158
  modifier_update('$set', args)
183
159
  end
184
-
160
+
185
161
  def push(*args)
186
162
  modifier_update('$push', args)
187
163
  end
188
-
164
+
189
165
  def push_all(*args)
190
166
  modifier_update('$pushAll', args)
191
167
  end
192
-
168
+
193
169
  def push_uniq(*args)
194
170
  criteria, keys = criteria_and_keys_from_args(args)
195
171
  keys.each { |key, value | criteria[key] = {'$ne' => value} }
196
172
  collection.update(criteria, {'$push' => keys}, :multi => true)
197
173
  end
198
-
174
+
199
175
  def pull(*args)
200
176
  modifier_update('$pull', args)
201
177
  end
202
-
178
+
203
179
  def pull_all(*args)
204
180
  modifier_update('$pullAll', args)
205
181
  end
206
-
207
- def modifier_update(modifier, args)
208
- criteria, keys = criteria_and_keys_from_args(args)
209
- modifiers = {modifier => keys}
210
- collection.update(criteria, modifiers, :multi => true)
182
+
183
+ def pop(*args)
184
+ modifier_update('$pop', args)
211
185
  end
212
- private :modifier_update
213
-
214
- def criteria_and_keys_from_args(args)
215
- keys = args.pop
216
- criteria = args[0].is_a?(Hash) ? args[0] : {:id => args}
217
- [to_criteria(criteria), keys]
186
+
187
+ def embeddable?
188
+ false
218
189
  end
219
- private :criteria_and_keys_from_args
220
190
 
221
191
  def connection(mongo_connection=nil)
222
192
  if mongo_connection.nil?
@@ -269,39 +239,51 @@ module MongoMapper
269
239
  end
270
240
 
271
241
  def single_collection_inherited?
272
- keys.has_key?('_type') && single_collection_inherited_superclass?
242
+ keys.key?(:_type) && single_collection_inherited_superclass?
273
243
  end
274
244
 
275
245
  def single_collection_inherited_superclass?
276
- superclass.respond_to?(:keys) && superclass.keys.has_key?('_type')
246
+ superclass.respond_to?(:keys) && superclass.keys.key?(:_type)
277
247
  end
278
248
 
279
249
  private
280
- def create_indexes_for(key)
281
- ensure_index key.name if key.options[:index]
282
- end
283
-
284
250
  def initialize_each(*docs)
285
251
  instances = []
286
252
  docs = [{}] if docs.blank?
287
253
  docs.flatten.each do |attrs|
288
- doc = initialize_doc(attrs)
254
+ doc = new(attrs)
289
255
  yield(doc)
290
256
  instances << doc
291
257
  end
292
258
  instances.size == 1 ? instances[0] : instances
293
259
  end
294
260
 
295
- def find_every(options)
296
- criteria, options = to_finder_options(options)
297
- collection.find(criteria, options).to_a.map do |doc|
298
- initialize_doc(doc)
261
+ def modifier_update(modifier, args)
262
+ criteria, keys = criteria_and_keys_from_args(args)
263
+ modifiers = {modifier => keys}
264
+ collection.update(criteria, modifiers, :multi => true)
265
+ end
266
+
267
+ def criteria_and_keys_from_args(args)
268
+ keys = args.pop
269
+ criteria = args[0].is_a?(Hash) ? args[0] : {:id => args}
270
+ [to_criteria(criteria), keys]
271
+ end
272
+
273
+ def assert_no_first_last_or_all(args)
274
+ if args[0] == :first || args[0] == :last || args[0] == :all
275
+ raise ArgumentError, "#{self}.find(:#{args}) is no longer supported, use #{self}.#{args} instead."
299
276
  end
300
277
  end
301
278
 
302
279
  def find_some(ids, options={})
303
- ids = ids.flatten.compact.uniq
304
- documents = find_every(options.merge(:_id => ids))
280
+ ids = ids.flatten.compact.uniq
281
+ find_many(options.merge(:_id => ids)).compact
282
+ end
283
+
284
+ def find_some!(ids, options={})
285
+ ids = ids.flatten.compact.uniq
286
+ documents = find_some(ids, options)
305
287
 
306
288
  if ids.size == documents.size
307
289
  documents
@@ -310,15 +292,20 @@ module MongoMapper
310
292
  end
311
293
  end
312
294
 
295
+ # All query methods that load documents pass through find_one or find_many
313
296
  def find_one(options={})
314
- criteria, options = to_finder_options(options)
297
+ criteria, options = to_query(options)
315
298
  if doc = collection.find_one(criteria, options)
316
- initialize_doc(doc)
299
+ load(doc)
317
300
  end
318
301
  end
319
302
 
320
- def find_one!(options={})
321
- find_one(options) || raise(DocumentNotFound, "Document match #{options.inspect} does not exist in #{collection.name} collection")
303
+ # All query methods that load documents pass through find_one or find_many
304
+ def find_many(options)
305
+ criteria, options = to_query(options)
306
+ collection.find(criteria, options).to_a.map do |doc|
307
+ load(doc)
308
+ end
322
309
  end
323
310
 
324
311
  def invert_order_clause(order)
@@ -354,11 +341,11 @@ module MongoMapper
354
341
  end
355
342
 
356
343
  def to_criteria(options={})
357
- FinderOptions.new(self, options).criteria
344
+ Query.new(self, options).criteria
358
345
  end
359
346
 
360
- def to_finder_options(options={})
361
- FinderOptions.new(self, options).to_a
347
+ def to_query(options={})
348
+ Query.new(self, options).to_a
362
349
  end
363
350
  end
364
351
 
@@ -366,42 +353,38 @@ module MongoMapper
366
353
  def collection
367
354
  self.class.collection
368
355
  end
369
-
356
+
370
357
  def database
371
358
  self.class.database
372
359
  end
373
360
 
374
- def new?
375
- read_attribute('_id').blank? || using_custom_id?
376
- end
377
-
378
361
  def save(options={})
379
- if options === false
380
- ActiveSupport::Deprecation.warn "save with true/false is deprecated. You should now use :validate => true/false."
381
- options = {:validate => false}
382
- end
362
+ options.assert_valid_keys(:validate, :safe)
383
363
  options.reverse_merge!(:validate => true)
384
- perform_validations = options.delete(:validate)
385
- !perform_validations || valid? ? create_or_update(options) : false
364
+ !options[:validate] || valid? ? create_or_update(options) : false
386
365
  end
387
366
 
388
- def save!
389
- save || raise(DocumentNotValid.new(self))
367
+ def save!(options={})
368
+ options.assert_valid_keys(:safe)
369
+ save(options) || raise(DocumentNotValid.new(self))
390
370
  end
391
371
 
392
372
  def destroy
393
- self.class.delete(id) unless new?
373
+ delete
394
374
  end
395
-
375
+
396
376
  def delete
397
377
  self.class.delete(id) unless new?
398
378
  end
399
379
 
400
380
  def reload
401
- doc = self.class.find(_id)
402
- self.class.associations.each { |name, assoc| send(name).reset if respond_to?(name) }
403
- self.attributes = doc.attributes
404
- self
381
+ if attrs = collection.find_one({:_id => _id})
382
+ self.class.associations.each { |name, assoc| send(name).reset if respond_to?(name) }
383
+ self.attributes = attrs
384
+ self
385
+ else
386
+ raise DocumentNotFound, "Document match #{_id.inspect} does not exist in #{collection.name} collection"
387
+ end
405
388
  end
406
389
 
407
390
  private
@@ -411,34 +394,23 @@ module MongoMapper
411
394
  end
412
395
 
413
396
  def create(options={})
414
- assign_id
415
397
  save_to_collection(options)
416
398
  end
417
399
 
418
- def assign_id
419
- if read_attribute(:_id).blank?
420
- write_attribute :_id, Mongo::ObjectID.new
421
- end
422
- end
423
-
424
400
  def update(options={})
425
401
  save_to_collection(options)
426
402
  end
427
403
 
428
404
  def save_to_collection(options={})
429
- clear_custom_id_flag
430
- safe = options.delete(:safe) || false
405
+ safe = options[:safe] || false
406
+ @new = false
431
407
  collection.save(to_mongo, :safe => safe)
432
408
  end
433
409
 
434
410
  def update_timestamps
435
411
  now = Time.now.utc
436
- write_attribute('created_at', now) if new? && read_attribute('created_at').blank?
437
- write_attribute('updated_at', now)
438
- end
439
-
440
- def clear_custom_id_flag
441
- @using_custom_id = nil
412
+ self[:created_at] = now if new? && !created_at?
413
+ self[:updated_at] = now
442
414
  end
443
415
  end
444
416
  end # Document