mongoid 1.2.6 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/VERSION +1 -1
  2. data/lib/mongoid.rb +1 -0
  3. data/lib/mongoid/associations.rb +1 -1
  4. data/lib/mongoid/attributes.rb +1 -0
  5. data/lib/mongoid/collection.rb +1 -1
  6. data/lib/mongoid/commands.rb +1 -1
  7. data/lib/mongoid/commands/delete_all.rb +2 -1
  8. data/lib/mongoid/commands/destroy_all.rb +1 -1
  9. data/lib/mongoid/components.rb +1 -0
  10. data/lib/mongoid/config.rb +3 -1
  11. data/lib/mongoid/contexts.rb +21 -0
  12. data/lib/mongoid/contexts/enumerable.rb +15 -12
  13. data/lib/mongoid/contexts/ids.rb +25 -0
  14. data/lib/mongoid/contexts/mongo.rb +25 -23
  15. data/lib/mongoid/contexts/paging.rb +2 -2
  16. data/lib/mongoid/criteria.rb +5 -43
  17. data/lib/mongoid/document.rb +1 -0
  18. data/lib/mongoid/enslavement.rb +38 -0
  19. data/lib/mongoid/fields.rb +5 -2
  20. data/lib/mongoid/identity.rb +7 -1
  21. data/lib/mongoid/named_scope.rb +2 -0
  22. data/mongoid.gemspec +8 -2
  23. data/spec/integration/mongoid/commands_spec.rb +2 -2
  24. data/spec/integration/mongoid/contexts/enumerable_spec.rb +13 -0
  25. data/spec/integration/mongoid/criteria_spec.rb +2 -2
  26. data/spec/integration/mongoid/document_spec.rb +5 -1
  27. data/spec/integration/mongoid/finders_spec.rb +85 -28
  28. data/spec/models/person.rb +1 -0
  29. data/spec/unit/mongoid/associations_spec.rb +12 -0
  30. data/spec/unit/mongoid/attributes_spec.rb +60 -51
  31. data/spec/unit/mongoid/collection_spec.rb +30 -0
  32. data/spec/unit/mongoid/commands/delete_all_spec.rb +3 -3
  33. data/spec/unit/mongoid/commands_spec.rb +16 -0
  34. data/spec/unit/mongoid/config_spec.rb +7 -0
  35. data/spec/unit/mongoid/contexts/enumerable_spec.rb +151 -11
  36. data/spec/unit/mongoid/contexts/mongo_spec.rb +168 -42
  37. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  38. data/spec/unit/mongoid/criteria_spec.rb +49 -75
  39. data/spec/unit/mongoid/criterion/exclusion_spec.rb +3 -13
  40. data/spec/unit/mongoid/criterion/inclusion_spec.rb +17 -19
  41. data/spec/unit/mongoid/criterion/optional_spec.rb +25 -8
  42. data/spec/unit/mongoid/document_spec.rb +4 -0
  43. data/spec/unit/mongoid/enslavement_spec.rb +63 -0
  44. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +2 -2
  45. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +2 -2
  46. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +1 -1
  47. data/spec/unit/mongoid/fields_spec.rb +10 -0
  48. data/spec/unit/mongoid/finders_spec.rb +1 -1
  49. data/spec/unit/mongoid/identity_spec.rb +23 -3
  50. data/spec/unit/mongoid/named_scope_spec.rb +15 -2
  51. data/spec/unit/mongoid/scope_spec.rb +1 -1
  52. metadata +8 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.6
1
+ 1.2.7
@@ -47,6 +47,7 @@ require "mongoid/config"
47
47
  require "mongoid/contexts"
48
48
  require "mongoid/criteria"
49
49
  require "mongoid/cursor"
50
+ require "mongoid/enslavement"
50
51
  require "mongoid/extensions"
51
52
  require "mongoid/errors"
52
53
  require "mongoid/factory"
@@ -240,7 +240,7 @@ module Mongoid # :nodoc:
240
240
  def add_builder(type, options)
241
241
  name = options.name.to_s
242
242
  define_method("build_#{name}") do |attrs|
243
- reset(name) { type.new(self, attrs.stringify_keys, options) }
243
+ reset(name) { type.new(self, (attrs || {}).stringify_keys, options) }
244
244
  end
245
245
  end
246
246
 
@@ -129,6 +129,7 @@ module Mongoid #:nodoc:
129
129
  identify if id.blank?
130
130
  notify
131
131
  end
132
+ alias :attributes= write_attributes
132
133
 
133
134
  protected
134
135
  # Return true is dynamic field setting is enabled.
@@ -30,7 +30,7 @@ module Mongoid #:nodoc
30
30
  #
31
31
  # Either a +Master+ or +Slaves+ collection.
32
32
  def directed(options = {})
33
- enslave = options.delete(:enslave)
33
+ enslave = options.delete(:enslave) || @klass.enslaved?
34
34
  enslave ? master_or_slaves : master
35
35
  end
36
36
 
@@ -67,7 +67,7 @@ module Mongoid #:nodoc:
67
67
  rescue Mongo::OperationFailure => e
68
68
  errors.add(:mongoid, e.message)
69
69
  end
70
- run_callbacks(:after_create) if new
70
+ run_callbacks(:after_create) if new && saved
71
71
  saved
72
72
  end
73
73
 
@@ -14,8 +14,9 @@ module Mongoid #:nodoc:
14
14
  #
15
15
  # <tt>DeleteAll.execute(Person, :conditions => { :field => "value" })</tt>
16
16
  def self.execute(klass, params = {})
17
+ safe = Mongoid.persist_in_safe_mode
17
18
  collection = klass.collection
18
- collection.remove((params[:conditions] || {}).merge(:_type => klass.name))
19
+ collection.remove((params[:conditions] || {}).merge(:_type => klass.name), :safe => safe)
19
20
  end
20
21
  end
21
22
  end
@@ -16,7 +16,7 @@ module Mongoid #:nodoc:
16
16
  def self.execute(klass, params)
17
17
  conditions = params[:conditions] || {}
18
18
  params[:conditions] = conditions.merge(:_type => klass.name)
19
- klass.find(:all, params).each { |doc| Destroy.execute(doc) }
19
+ klass.find(:all, params).each { |doc| Destroy.execute(doc) }; true
20
20
  end
21
21
  end
22
22
  end
@@ -9,6 +9,7 @@ module Mongoid #:nodoc
9
9
  include Attributes
10
10
  include Callbacks
11
11
  include Commands
12
+ include Enslavement
12
13
  include Fields
13
14
  include Indexes
14
15
  include Matchers
@@ -8,7 +8,8 @@ module Mongoid #:nodoc
8
8
  :reconnect_time,
9
9
  :parameterize_keys,
10
10
  :persist_in_safe_mode,
11
- :raise_not_found_error
11
+ :raise_not_found_error,
12
+ :use_object_ids
12
13
 
13
14
  # Defaults the configuration options to true.
14
15
  def initialize
@@ -17,6 +18,7 @@ module Mongoid #:nodoc
17
18
  @persist_in_safe_mode = true
18
19
  @raise_not_found_error = true
19
20
  @reconnect_time = 3
21
+ @use_object_ids = false
20
22
  end
21
23
 
22
24
  # Sets the Mongo::DB master database to be used. If the object trying to me
@@ -1,4 +1,25 @@
1
1
  # encoding: utf-8
2
+ require "mongoid/contexts/ids"
2
3
  require "mongoid/contexts/paging"
3
4
  require "mongoid/contexts/enumerable"
4
5
  require "mongoid/contexts/mongo"
6
+
7
+ module Mongoid
8
+ module Contexts
9
+ # Determines the context to be used for this criteria. If the class is an
10
+ # embedded document, then the context will be the array in the has_many
11
+ # association it is in. If the class is a root, then the database itself
12
+ # will be the context.
13
+ #
14
+ # Example:
15
+ #
16
+ # <tt>Contexts.context_for(criteria)</tt>
17
+ def self.context_for(criteria)
18
+ if criteria.klass.embedded
19
+ return Contexts::Enumerable.new(criteria)
20
+ end
21
+ Contexts::Mongo.new(criteria)
22
+ end
23
+
24
+ end
25
+ end
@@ -2,10 +2,11 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Contexts #:nodoc:
4
4
  class Enumerable
5
- include Paging
6
- attr_reader :selector, :options, :documents
5
+ include Ids, Paging
6
+ attr_reader :criteria
7
7
 
8
8
  delegate :first, :last, :to => :execute
9
+ delegate :documents, :options, :selector, :to => :criteria
9
10
 
10
11
  # Return aggregation counts of the grouped documents. This will count by
11
12
  # the first field provided in the fields array.
@@ -21,7 +22,7 @@ module Mongoid #:nodoc:
21
22
 
22
23
  # Gets the number of documents in the array. Delegates to size.
23
24
  def count
24
- @count ||= @documents.size
25
+ @count ||= documents.size
25
26
  end
26
27
 
27
28
  # Groups the documents by the first field supplied in the field options.
@@ -30,8 +31,8 @@ module Mongoid #:nodoc:
30
31
  #
31
32
  # A +Hash+ with field values as keys, arrays of documents as values.
32
33
  def group
33
- field = @options[:fields].first
34
- @documents.group_by { |doc| doc.send(field) }
34
+ field = options[:fields].first
35
+ documents.group_by { |doc| doc.send(field) }
35
36
  end
36
37
 
37
38
  # Enumerable implementation of execute. Returns matching documents for
@@ -41,7 +42,7 @@ module Mongoid #:nodoc:
41
42
  #
42
43
  # An +Array+ of documents that matched the selector.
43
44
  def execute(paginating = false)
44
- limit(@documents.select { |document| document.matches?(@selector) })
45
+ limit(documents.select { |document| document.matches?(selector) })
45
46
  end
46
47
 
47
48
  # Create the new enumerable context. This will need the selector and
@@ -50,9 +51,9 @@ module Mongoid #:nodoc:
50
51
  #
51
52
  # Example:
52
53
  #
53
- # <tt>Mongoid::Contexts::Enumerable.new(selector, options, docs)</tt>
54
- def initialize(selector, options, documents)
55
- @selector, @options, @documents = selector, options, documents
54
+ # <tt>Mongoid::Contexts::Enumerable.new(criteria)</tt>
55
+ def initialize(criteria)
56
+ @criteria = criteria
56
57
  end
57
58
 
58
59
  # Get the largest value for the field in all the documents.
@@ -86,7 +87,7 @@ module Mongoid #:nodoc:
86
87
  #
87
88
  # The numerical sum of all the document field values.
88
89
  def sum(field)
89
- sum = @documents.inject(nil) do |memo, doc|
90
+ sum = documents.inject(nil) do |memo, doc|
90
91
  value = doc.send(field)
91
92
  memo ? memo += value : value
92
93
  end
@@ -95,7 +96,7 @@ module Mongoid #:nodoc:
95
96
  protected
96
97
  # If the field exists, perform the comparison and set if true.
97
98
  def determine(field, operator)
98
- matching = @documents.inject(nil) do |memo, doc|
99
+ matching = documents.inject(nil) do |memo, doc|
99
100
  value = doc.send(field)
100
101
  (memo && memo.send(operator, value)) ? memo : value
101
102
  end
@@ -103,9 +104,11 @@ module Mongoid #:nodoc:
103
104
 
104
105
  # Limits the result set if skip and limit options.
105
106
  def limit(documents)
106
- skip, limit = @options[:skip], @options[:limit]
107
+ skip, limit = options[:skip], options[:limit]
107
108
  if skip && limit
108
109
  return documents.slice(skip, limit)
110
+ elsif limit
111
+ return documents.first(limit)
109
112
  end
110
113
  documents
111
114
  end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Ids
5
+ # Return documents based on an id search. Will handle if a single id has
6
+ # been passed or mulitple ids.
7
+ #
8
+ # Example:
9
+ #
10
+ # context.id_criteria([1, 2, 3])
11
+ #
12
+ # Returns:
13
+ #
14
+ # The single or multiple documents.
15
+ def id_criteria(params)
16
+ criteria.id(params)
17
+ result = params.is_a?(Array) ? criteria.entries : one
18
+ if Mongoid.raise_not_found_error
19
+ raise Errors::DocumentNotFound.new(klass, params) if result.blank?
20
+ end
21
+ return result
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,8 +2,10 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Contexts #:nodoc:
4
4
  class Mongo
5
- include Paging
6
- attr_reader :selector, :options, :klass
5
+ include Ids, Paging
6
+ attr_reader :criteria
7
+
8
+ delegate :klass, :options, :selector, :to => :criteria
7
9
 
8
10
  AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
9
11
  # Aggregate the context. This will take the internally built selector and options
@@ -19,7 +21,7 @@ module Mongoid #:nodoc:
19
21
  #
20
22
  # A +Hash+ with field values as keys, counts as values
21
23
  def aggregate
22
- @klass.collection.group(@options[:fields], @selector, { :count => 0 }, AGGREGATE_REDUCE, true)
24
+ klass.collection.group(options[:fields], selector, { :count => 0 }, AGGREGATE_REDUCE, true)
23
25
  end
24
26
 
25
27
  # Get the count of matching documents in the database for the context.
@@ -32,7 +34,7 @@ module Mongoid #:nodoc:
32
34
  #
33
35
  # An +Integer+ count of documents.
34
36
  def count
35
- @count ||= @klass.collection.find(@selector, process_options).count
37
+ @count ||= klass.collection.find(selector, process_options).count
36
38
  end
37
39
 
38
40
  # Execute the context. This will take the selector and options
@@ -48,7 +50,7 @@ module Mongoid #:nodoc:
48
50
  #
49
51
  # An enumerable +Cursor+.
50
52
  def execute(paginating = false)
51
- cursor = @klass.collection.find(@selector, process_options)
53
+ cursor = klass.collection.find(selector, process_options)
52
54
  if cursor
53
55
  @count = cursor.count if paginating
54
56
  cursor
@@ -71,15 +73,15 @@ module Mongoid #:nodoc:
71
73
  #
72
74
  # A +Hash+ with field values as keys, arrays of documents as values.
73
75
  def group
74
- @klass.collection.group(
75
- @options[:fields],
76
- @selector,
76
+ klass.collection.group(
77
+ options[:fields],
78
+ selector,
77
79
  { :group => [] },
78
80
  GROUP_REDUCE,
79
81
  true
80
82
  ).collect do |docs|
81
83
  docs["group"] = docs["group"].collect do |attrs|
82
- Mongoid::Factory.build(@klass, attrs)
84
+ Mongoid::Factory.build(klass, attrs)
83
85
  end
84
86
  docs
85
87
  end
@@ -90,11 +92,11 @@ module Mongoid #:nodoc:
90
92
  #
91
93
  # Example:
92
94
  #
93
- # <tt>Mongoid::Contexts::Mongo.new(selector, options, klass)</tt>
94
- def initialize(selector, options, klass)
95
- @selector, @options, @klass = selector, options, klass
96
- if klass.hereditary
97
- @hereditary = true
95
+ # <tt>Mongoid::Contexts::Mongo.new(criteria)</tt>
96
+ def initialize(criteria)
97
+ @criteria = criteria
98
+ if criteria.klass.hereditary
99
+ criteria.in(:_type => criteria.klass._types)
98
100
  end
99
101
  end
100
102
 
@@ -114,8 +116,8 @@ module Mongoid #:nodoc:
114
116
  sorting = opts[:sort]
115
117
  sorting = [[:_id, :asc]] unless sorting
116
118
  opts[:sort] = sorting.collect { |option| [ option[0], option[1].invert ] }
117
- attributes = @klass.collection.find_one(@selector, opts)
118
- attributes ? Mongoid::Factory.build(@klass, attributes) : nil
119
+ attributes = klass.collection.find_one(selector, opts)
120
+ attributes ? Mongoid::Factory.build(klass, attributes) : nil
119
121
  end
120
122
 
121
123
  MAX_REDUCE = "function(obj, prev) { if (prev.max == 'start') { prev.max = obj.[field]; } " +
@@ -168,8 +170,8 @@ module Mongoid #:nodoc:
168
170
  #
169
171
  # The first document in the collection.
170
172
  def one
171
- attributes = @klass.collection.find_one(@selector, process_options)
172
- attributes ? Mongoid::Factory.build(@klass, attributes) : nil
173
+ attributes = klass.collection.find_one(selector, process_options)
174
+ attributes ? Mongoid::Factory.build(klass, attributes) : nil
173
175
  end
174
176
 
175
177
  alias :first :one
@@ -196,9 +198,9 @@ module Mongoid #:nodoc:
196
198
  # Common functionality for grouping operations. Currently used by min, max
197
199
  # and sum. Will gsub the field name in the supplied reduce function.
198
200
  def grouped(start, field, reduce)
199
- collection = @klass.collection.group(
201
+ collection = klass.collection.group(
200
202
  nil,
201
- @selector,
203
+ selector,
202
204
  { start => "start" },
203
205
  reduce.gsub("[field]", field),
204
206
  true
@@ -209,12 +211,12 @@ module Mongoid #:nodoc:
209
211
  # Filters the field list. If no fields have been supplied, then it will be
210
212
  # empty. If fields have been defined then _type will be included as well.
211
213
  def process_options
212
- fields = @options[:fields]
214
+ fields = options[:fields]
213
215
  if fields && fields.size > 0 && !fields.include?(:_type)
214
216
  fields << :_type
215
- @options[:fields] = fields
217
+ options[:fields] = fields
216
218
  end
217
- @options.dup
219
+ options.dup
218
220
  end
219
221
 
220
222
  end
@@ -25,7 +25,7 @@ module Mongoid #:nodoc:
25
25
  #
26
26
  # An +Integer+ page number.
27
27
  def page
28
- skips, limits = @options[:skip], @options[:limit]
28
+ skips, limits = options[:skip], options[:limit]
29
29
  (skips && limits) ? (skips + limits) / limits : 1
30
30
  end
31
31
 
@@ -35,7 +35,7 @@ module Mongoid #:nodoc:
35
35
  #
36
36
  # The +Integer+ number of documents in each page.
37
37
  def per_page
38
- (@options[:limit] || 20).to_i
38
+ (options[:limit] || 20).to_i
39
39
  end
40
40
  end
41
41
  end
@@ -35,6 +35,7 @@ module Mongoid #:nodoc:
35
35
  :execute,
36
36
  :first,
37
37
  :group,
38
+ :id_criteria,
38
39
  :last,
39
40
  :max,
40
41
  :min,
@@ -91,7 +92,7 @@ module Mongoid #:nodoc:
91
92
  # This will return an Enumerable context if the class is embedded,
92
93
  # otherwise it will return a Mongo context for root classes.
93
94
  def context
94
- @context ||= determine_context
95
+ @context ||= Contexts.context_for(self)
95
96
  end
96
97
 
97
98
  # Iterate over each +Document+ in the results. This can take an optional
@@ -101,7 +102,7 @@ module Mongoid #:nodoc:
101
102
  #
102
103
  # <tt>criteria.each { |doc| p doc }</tt>
103
104
  def each(&block)
104
- return each_cached(&block) if cached?
105
+ return caching(&block) if cached?
105
106
  if block_given?
106
107
  execute.each { |doc| yield doc }
107
108
  end
@@ -134,10 +135,6 @@ module Mongoid #:nodoc:
134
135
  # klass: The class to execute on.
135
136
  def initialize(klass)
136
137
  @selector, @options, @klass, @documents = {}, {}, klass, []
137
- if klass.hereditary
138
- @selector = { :_type => { "$in" => klass._types } }
139
- @hereditary = true
140
- end
141
138
  end
142
139
 
143
140
  # Merges another object into this +Criteria+. The other object may be a
@@ -204,29 +201,15 @@ module Mongoid #:nodoc:
204
201
  klass = args[0]
205
202
  params = args[1] || {}
206
203
  unless params.is_a?(Hash)
207
- return id_criteria(klass, params)
204
+ return new(klass).id_criteria(params)
208
205
  end
209
206
  return new(klass).where(params.delete(:conditions) || {}).extras(params)
210
207
  end
211
208
 
212
209
  protected
213
- # Determines the context to be used for this criteria. If the class is an
214
- # embedded document, then thw context will be the array in the has_many
215
- # association it is in. If the class is a root, then the database itself
216
- # will be the context.
217
- #
218
- # Example:
219
- #
220
- # <tt>criteria#determine_context</tt>
221
- def determine_context
222
- if @klass.embedded
223
- return Contexts::Enumerable.new(@selector, @options, @documents)
224
- end
225
- Contexts::Mongo.new(@selector, @options, @klass)
226
- end
227
210
 
228
211
  # Iterate over each +Document+ in the results and cache the collection.
229
- def each_cached(&block)
212
+ def caching(&block)
230
213
  @collection ||= execute
231
214
  if block_given?
232
215
  docs = []
@@ -272,26 +255,5 @@ module Mongoid #:nodoc:
272
255
  def update_selector(attributes, operator)
273
256
  attributes.each { |key, value| @selector[key] = { operator => value } }; self
274
257
  end
275
-
276
- class << self
277
- # Create a criteria or single document based on an id search. Will handle
278
- # if a single id has been passed or mulitple ids.
279
- #
280
- # Example:
281
- #
282
- # Criteria.id_criteria(Person, [1, 2, 3])
283
- #
284
- # Returns:
285
- #
286
- # The single or multiple documents.
287
- def id_criteria(klass, params)
288
- criteria = new(klass).id(params)
289
- result = params.is_a?(String) ? criteria.one : criteria.entries
290
- if Mongoid.raise_not_found_error
291
- raise Errors::DocumentNotFound.new(klass, params) if result.blank?
292
- end
293
- return result
294
- end
295
- end
296
258
  end
297
259
  end