mongoid 1.2.7 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
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