mongoid 1.2.7 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/VERSION +1 -1
  2. data/lib/mongoid.rb +2 -1
  3. data/lib/mongoid/associations.rb +18 -14
  4. data/lib/mongoid/associations/has_many_related.rb +1 -1
  5. data/lib/mongoid/associations/meta_data.rb +28 -0
  6. data/lib/mongoid/associations/options.rb +1 -6
  7. data/lib/mongoid/attributes.rb +1 -1
  8. data/lib/mongoid/caching.rb +41 -0
  9. data/lib/mongoid/collection.rb +1 -0
  10. data/lib/mongoid/components.rb +1 -0
  11. data/lib/mongoid/config.rb +2 -0
  12. data/lib/mongoid/contexts/mongo.rb +8 -13
  13. data/lib/mongoid/criteria.rb +7 -2
  14. data/lib/mongoid/criterion/inclusion.rb +1 -0
  15. data/lib/mongoid/criterion/optional.rb +10 -2
  16. data/lib/mongoid/finders.rb +5 -23
  17. data/lib/mongoid/identity.rb +1 -1
  18. data/lib/mongoid/javascript.rb +21 -0
  19. data/lib/mongoid/javascript/functions.yml +37 -0
  20. data/mongoid.gemspec +12 -2
  21. data/spec/integration/mongoid/attributes_spec.rb +2 -2
  22. data/spec/integration/mongoid/commands_spec.rb +5 -3
  23. data/spec/integration/mongoid/criteria_spec.rb +27 -0
  24. data/spec/models/game.rb +2 -1
  25. data/spec/models/post.rb +1 -1
  26. data/spec/unit/mongoid/associations/has_many_related_spec.rb +5 -1
  27. data/spec/unit/mongoid/associations/meta_data_spec.rb +88 -0
  28. data/spec/unit/mongoid/associations/options_spec.rb +20 -19
  29. data/spec/unit/mongoid/associations_spec.rb +41 -7
  30. data/spec/unit/mongoid/caching_spec.rb +63 -0
  31. data/spec/unit/mongoid/collection_spec.rb +20 -4
  32. data/spec/unit/mongoid/config_spec.rb +7 -0
  33. data/spec/unit/mongoid/contexts/mongo_spec.rb +51 -12
  34. data/spec/unit/mongoid/criteria_spec.rb +23 -1
  35. data/spec/unit/mongoid/criterion/inclusion_spec.rb +8 -0
  36. data/spec/unit/mongoid/criterion/optional_spec.rb +0 -10
  37. data/spec/unit/mongoid/identity_spec.rb +24 -3
  38. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  39. metadata +12 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.7
1
+ 1.2.8
@@ -38,8 +38,8 @@ require "active_support/time_with_zone"
38
38
  require "will_paginate/collection"
39
39
  require "mongo"
40
40
  require "mongoid/associations"
41
- require "mongoid/associations/options"
42
41
  require "mongoid/attributes"
42
+ require "mongoid/caching"
43
43
  require "mongoid/callbacks"
44
44
  require "mongoid/collection"
45
45
  require "mongoid/commands"
@@ -56,6 +56,7 @@ require "mongoid/fields"
56
56
  require "mongoid/finders"
57
57
  require "mongoid/identity"
58
58
  require "mongoid/indexes"
59
+ require "mongoid/javascript"
59
60
  require "mongoid/matchers"
60
61
  require "mongoid/memoization"
61
62
  require "mongoid/named_scope"
@@ -6,6 +6,8 @@ require "mongoid/associations/has_many"
6
6
  require "mongoid/associations/has_many_related"
7
7
  require "mongoid/associations/has_one"
8
8
  require "mongoid/associations/has_one_related"
9
+ require "mongoid/associations/options"
10
+ require "mongoid/associations/meta_data"
9
11
 
10
12
  module Mongoid # :nodoc:
11
13
  module Associations #:nodoc:
@@ -28,13 +30,13 @@ module Mongoid # :nodoc:
28
30
 
29
31
  # Updates all the one-to-many relational associations for the name.
30
32
  def update_associations(name)
31
- send(name).each { |doc| doc.save }
33
+ send(name).each { |doc| doc.save } if new_record?
32
34
  end
33
35
 
34
36
  # Update the one-to-one relational association for the name.
35
37
  def update_association(name)
36
38
  association = send(name)
37
- association.save unless association.nil?
39
+ association.save if new_record? && !association.nil?
38
40
  end
39
41
  end
40
42
 
@@ -87,14 +89,12 @@ module Mongoid # :nodoc:
87
89
  # end
88
90
  #
89
91
  def belongs_to_related(name, options = {}, &block)
90
- field "#{name.to_s}_id"
91
- index "#{name.to_s}_id" unless self.embedded
92
- add_association(
93
- Associations::BelongsToRelated,
94
- Associations::Options.new(
95
- options.merge(:name => name, :extend => block)
92
+ opts = Associations::Options.new(
93
+ options.merge(:name => name, :extend => block, :foreign_key => foreign_key(name, options))
96
94
  )
97
- )
95
+ add_association(Associations::BelongsToRelated, opts)
96
+ field opts.foreign_key
97
+ index opts.foreign_key unless self.embedded
98
98
  end
99
99
 
100
100
  # Adds the association from a parent document to its children. The name
@@ -140,10 +140,9 @@ module Mongoid # :nodoc:
140
140
  # end
141
141
  #
142
142
  def has_many_related(name, options = {}, &block)
143
- add_association(
144
- Associations::HasManyRelated,
143
+ add_association(Associations::HasManyRelated,
145
144
  Associations::Options.new(
146
- options.merge(:name => name, :parent_key => self.name.foreign_key, :extend => block)
145
+ options.merge(:name => name, :foreign_key => foreign_key(self.name, options), :extend => block)
147
146
  )
148
147
  )
149
148
  before_save do |document|
@@ -197,7 +196,7 @@ module Mongoid # :nodoc:
197
196
  add_association(
198
197
  Associations::HasOneRelated,
199
198
  Associations::Options.new(
200
- options.merge(:name => name, :parent_key => self.name.foreign_key, :extend => block)
199
+ options.merge(:name => name, :foreign_key => foreign_key(name, options), :extend => block)
201
200
  )
202
201
  )
203
202
  before_save do |document|
@@ -226,7 +225,7 @@ module Mongoid # :nodoc:
226
225
  # getters for the associations will perform the necessary memoization.
227
226
  def add_association(type, options)
228
227
  name = options.name.to_s
229
- associations[name] = type
228
+ associations[name] = MetaData.new(type, options)
230
229
  define_method(name) do
231
230
  memoized(name) { type.instantiate(self, options) }
232
231
  end
@@ -253,6 +252,11 @@ module Mongoid # :nodoc:
253
252
  document.save; document
254
253
  end
255
254
  end
255
+
256
+ # Find the foreign key.
257
+ def foreign_key(name, options)
258
+ options[:foreign_key] || name.to_s.foreign_key
259
+ end
256
260
  end
257
261
  end
258
262
  end
@@ -59,7 +59,7 @@ module Mongoid #:nodoc:
59
59
  # options: The association +Options+.
60
60
  def initialize(document, options, target = nil)
61
61
  @parent, @klass = document, options.klass
62
- @foreign_key = document.class.to_s.foreign_key
62
+ @foreign_key = options.foreign_key
63
63
  @target = target || @klass.all(:conditions => { @foreign_key => document.id })
64
64
  extends(options)
65
65
  end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class MetaData #:nodoc:
5
+
6
+ attr_reader :association, :options
7
+
8
+ delegate :macro, :to => :association
9
+
10
+ # Delegate all methods on +Options+ to the options instance.
11
+ Associations::Options.public_instance_methods(false).each do |name|
12
+ define_method(name) { |*args| @options.send(name) }
13
+ end
14
+
15
+ # Create the new associations MetaData object, which holds the type of
16
+ # the association and its options, with convenience methods for getting
17
+ # that information.
18
+ #
19
+ # Options:
20
+ #
21
+ # association: The association type as a class instance.
22
+ # options: The association options
23
+ def initialize(association, options)
24
+ @association, @options = association, options
25
+ end
26
+ end
27
+ end
28
+ end
@@ -21,7 +21,7 @@ module Mongoid #:nodoc:
21
21
 
22
22
  # Return the foreign key based off the association name.
23
23
  def foreign_key
24
- name.to_s.foreign_key
24
+ @attributes[:foreign_key] || klass.name.to_s.foreign_key
25
25
  end
26
26
 
27
27
  # Returns the name of the inverse_of association
@@ -42,11 +42,6 @@ module Mongoid #:nodoc:
42
42
  @attributes[:name].to_s
43
43
  end
44
44
 
45
- # Returns the parent foreign key association name.
46
- def parent_key
47
- @attributes[:parent_key]
48
- end
49
-
50
45
  # Returns whether or not this association is polymorphic.
51
46
  def polymorphic
52
47
  @attributes[:polymorphic] == true
@@ -43,7 +43,7 @@ module Mongoid #:nodoc:
43
43
  if set_allowed?(key)
44
44
  @attributes[key.to_s] = value
45
45
  else
46
- send("#{key}=", value) if value
46
+ send("#{key}=", value)
47
47
  end
48
48
  end
49
49
  end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Caching #:nodoc:
4
+ def self.included(base)
5
+ base.class_eval do
6
+ extend ClassMethods
7
+ class_inheritable_accessor :cached
8
+ self.cached = false
9
+
10
+ delegate :cached?, :to => "self.class"
11
+ end
12
+ end
13
+
14
+ module ClassMethods #:nodoc
15
+ # Sets caching on for this class. This class level configuration will
16
+ # default all queries to cache the results of the first iteration over
17
+ # the cursor into an internal array. This should only be used for queries
18
+ # that return a small number of results or have small documents, as after
19
+ # the first iteration the entire results will be stored in memory.
20
+ #
21
+ # Example:
22
+ #
23
+ # class Person
24
+ # include Mongoid::Document
25
+ # cache
26
+ # end
27
+ def cache
28
+ self.cached = true
29
+ end
30
+
31
+ # Determines if the class is cached or not.
32
+ #
33
+ # Returns:
34
+ #
35
+ # True if cached, false if not.
36
+ def cached?
37
+ self.cached == true
38
+ end
39
+ end
40
+ end
41
+ end
@@ -30,6 +30,7 @@ module Mongoid #:nodoc
30
30
  #
31
31
  # Either a +Master+ or +Slaves+ collection.
32
32
  def directed(options = {})
33
+ options.delete(:cache)
33
34
  enslave = options.delete(:enslave) || @klass.enslaved?
34
35
  enslave ? master_or_slaves : master
35
36
  end
@@ -7,6 +7,7 @@ module Mongoid #:nodoc
7
7
  # module, to keep the document class from getting too cluttered.
8
8
  include Associations
9
9
  include Attributes
10
+ include Caching
10
11
  include Callbacks
11
12
  include Commands
12
13
  include Enslavement
@@ -8,6 +8,7 @@ module Mongoid #:nodoc
8
8
  :reconnect_time,
9
9
  :parameterize_keys,
10
10
  :persist_in_safe_mode,
11
+ :persist_types,
11
12
  :raise_not_found_error,
12
13
  :use_object_ids
13
14
 
@@ -16,6 +17,7 @@ module Mongoid #:nodoc
16
17
  @allow_dynamic_fields = true
17
18
  @parameterize_keys = true
18
19
  @persist_in_safe_mode = true
20
+ @persist_types = true
19
21
  @raise_not_found_error = true
20
22
  @reconnect_time = 3
21
23
  @use_object_ids = false
@@ -7,7 +7,6 @@ module Mongoid #:nodoc:
7
7
 
8
8
  delegate :klass, :options, :selector, :to => :criteria
9
9
 
10
- AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
11
10
  # Aggregate the context. This will take the internally built selector and options
12
11
  # and pass them on to the Ruby driver's +group()+ method on the collection. The
13
12
  # collection itself will be retrieved from the class provided, and once the
@@ -21,7 +20,7 @@ module Mongoid #:nodoc:
21
20
  #
22
21
  # A +Hash+ with field values as keys, counts as values
23
22
  def aggregate
24
- klass.collection.group(options[:fields], selector, { :count => 0 }, AGGREGATE_REDUCE, true)
23
+ klass.collection.group(options[:fields], selector, { :count => 0 }, Javascript.aggregate, true)
25
24
  end
26
25
 
27
26
  # Get the count of matching documents in the database for the context.
@@ -59,7 +58,6 @@ module Mongoid #:nodoc:
59
58
  end
60
59
  end
61
60
 
62
- GROUP_REDUCE = "function(obj, prev) { prev.group.push(obj); }"
63
61
  # Groups the context. This will take the internally built selector and options
64
62
  # and pass them on to the Ruby driver's +group()+ method on the collection. The
65
63
  # collection itself will be retrieved from the class provided, and once the
@@ -77,7 +75,7 @@ module Mongoid #:nodoc:
77
75
  options[:fields],
78
76
  selector,
79
77
  { :group => [] },
80
- GROUP_REDUCE,
78
+ Javascript.group,
81
79
  true
82
80
  ).collect do |docs|
83
81
  docs["group"] = docs["group"].collect do |attrs|
@@ -95,9 +93,11 @@ module Mongoid #:nodoc:
95
93
  # <tt>Mongoid::Contexts::Mongo.new(criteria)</tt>
96
94
  def initialize(criteria)
97
95
  @criteria = criteria
98
- if criteria.klass.hereditary
96
+ if klass.hereditary && Mongoid.persist_types
99
97
  criteria.in(:_type => criteria.klass._types)
100
98
  end
99
+ criteria.enslave if klass.enslaved?
100
+ criteria.cache if klass.cached?
101
101
  end
102
102
 
103
103
  # Return the last result for the +Context+. Essentially does a find_one on
@@ -120,8 +120,6 @@ module Mongoid #:nodoc:
120
120
  attributes ? Mongoid::Factory.build(klass, attributes) : nil
121
121
  end
122
122
 
123
- MAX_REDUCE = "function(obj, prev) { if (prev.max == 'start') { prev.max = obj.[field]; } " +
124
- "if (prev.max < obj.[field]) { prev.max = obj.[field]; } }"
125
123
  # Return the max value for a field.
126
124
  #
127
125
  # This will take the internally built selector and options
@@ -137,11 +135,9 @@ module Mongoid #:nodoc:
137
135
  #
138
136
  # A numeric max value.
139
137
  def max(field)
140
- grouped(:max, field.to_s, MAX_REDUCE)
138
+ grouped(:max, field.to_s, Javascript.max)
141
139
  end
142
140
 
143
- MIN_REDUCE = "function(obj, prev) { if (prev.min == 'start') { prev.min = obj.[field]; } " +
144
- "if (prev.min > obj.[field]) { prev.min = obj.[field]; } }"
145
141
  # Return the min value for a field.
146
142
  #
147
143
  # This will take the internally built selector and options
@@ -157,7 +153,7 @@ module Mongoid #:nodoc:
157
153
  #
158
154
  # A numeric minimum value.
159
155
  def min(field)
160
- grouped(:min, field.to_s, MIN_REDUCE)
156
+ grouped(:min, field.to_s, Javascript.min)
161
157
  end
162
158
 
163
159
  # Return the first result for the +Context+.
@@ -176,7 +172,6 @@ module Mongoid #:nodoc:
176
172
 
177
173
  alias :first :one
178
174
 
179
- SUM_REDUCE = "function(obj, prev) { if (prev.sum == 'start') { prev.sum = 0; } prev.sum += obj.[field]; }"
180
175
  # Sum the context.
181
176
  #
182
177
  # This will take the internally built selector and options
@@ -192,7 +187,7 @@ module Mongoid #:nodoc:
192
187
  #
193
188
  # A numeric value that is the sum.
194
189
  def sum(field)
195
- grouped(:sum, field.to_s, SUM_REDUCE)
190
+ grouped(:sum, field.to_s, Javascript.sum)
196
191
  end
197
192
 
198
193
  # Common functionality for grouping operations. Currently used by min, max
@@ -165,7 +165,7 @@ module Mongoid #:nodoc:
165
165
  # Returns: <tt>Criteria</tt>
166
166
  def method_missing(name, *args)
167
167
  if @klass.respond_to?(name)
168
- new_scope = @klass.send(name)
168
+ new_scope = @klass.send(name, *args)
169
169
  new_scope.merge(self)
170
170
  return new_scope
171
171
  else
@@ -203,7 +203,12 @@ module Mongoid #:nodoc:
203
203
  unless params.is_a?(Hash)
204
204
  return new(klass).id_criteria(params)
205
205
  end
206
- return new(klass).where(params.delete(:conditions) || {}).extras(params)
206
+ conditions = params.delete(:conditions) || {}
207
+ if conditions.include?(:id)
208
+ conditions[:_id] = conditions[:id]
209
+ conditions.delete(:id)
210
+ end
211
+ return new(klass).where(conditions).extras(params)
207
212
  end
208
213
 
209
214
  protected
@@ -61,6 +61,7 @@ module Mongoid #:nodoc:
61
61
  def in(attributes = {})
62
62
  update_selector(attributes, "$in")
63
63
  end
64
+ alias any_in in
64
65
 
65
66
  # Adds a criterion to the +Criteria+ that specifies values that must
66
67
  # be matched in order to return results. This is similar to a SQL "WHERE"
@@ -20,8 +20,7 @@ module Mongoid #:nodoc:
20
20
  #
21
21
  # <tt>criteria.cached?</tt>
22
22
  def cached?
23
- @cached ||= @options.delete(:cache)
24
- @cached == true
23
+ @options[:cache] == true
25
24
  end
26
25
 
27
26
  # Flags the criteria to execute against a read-only slave in the pool
@@ -34,6 +33,15 @@ module Mongoid #:nodoc:
34
33
  @options.merge!(:enslave => true); self
35
34
  end
36
35
 
36
+ # Will return true if the criteria is enslaved.
37
+ #
38
+ # Example:
39
+ #
40
+ # <tt>criteria.enslaved?</tt>
41
+ def enslaved?
42
+ @options[:enslave] == true
43
+ end
44
+
37
45
  # Adds a criterion to the +Criteria+ that specifies additional options
38
46
  # to be passed to the Ruby driver, in the exact format for the driver.
39
47
  #
@@ -112,27 +112,9 @@ module Mongoid #:nodoc:
112
112
  #
113
113
  # Returns: <tt>Float</tt> max value.
114
114
  def max(field)
115
- Criteria.new(self).max(field)
115
+ criteria.max(field)
116
116
  end
117
117
 
118
- # Will execute a +Criteria+ based on the +DynamicFinder+ that gets
119
- # generated.
120
- #
121
- # Options:
122
- #
123
- # name: The finder method name
124
- # args: The arguments to pass to the method.
125
- #
126
- # Example:
127
- #
128
- # <tt>Person.find_all_by_title_and_age("Sir", 30)</tt>
129
- # def method_missing(name, *args)
130
- # dyna = DynamicFinder.new(name, *args)
131
- # finder, conditions = dyna.finder, dyna.conditions
132
- # results = find(finder, :conditions => conditions)
133
- # results ? results : dyna.create(self)
134
- # end
135
-
136
118
  # Convenience method for returning the min value of a field.
137
119
  #
138
120
  # Options:
@@ -145,7 +127,7 @@ module Mongoid #:nodoc:
145
127
  #
146
128
  # Returns: <tt>Float</tt> min value.
147
129
  def min(field)
148
- Criteria.new(self).min(field)
130
+ criteria.min(field)
149
131
  end
150
132
 
151
133
  # Find all documents in paginated fashion given the supplied arguments.
@@ -179,7 +161,7 @@ module Mongoid #:nodoc:
179
161
  #
180
162
  # Returns: <tt>Criteria</tt>
181
163
  def only(*args)
182
- Criteria.new(self).only(*args)
164
+ criteria.only(*args)
183
165
  end
184
166
 
185
167
  # Convenience method for returning the sum of a specified field for all
@@ -195,7 +177,7 @@ module Mongoid #:nodoc:
195
177
  #
196
178
  # Returns: <tt>Float</tt> of the sum.
197
179
  def sum(field)
198
- Criteria.new(self).sum(field)
180
+ criteria.sum(field)
199
181
  end
200
182
 
201
183
  # Entry point for creating a new criteria from a Document. This will
@@ -212,7 +194,7 @@ module Mongoid #:nodoc:
212
194
  #
213
195
  # Returns: <tt>Criteria</tt>
214
196
  def where(selector = nil)
215
- Criteria.new(self).where(selector)
197
+ criteria.where(selector)
216
198
  end
217
199
 
218
200
  protected