mongoid 0.9.6 → 0.9.7

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 (34) hide show
  1. data/VERSION +1 -1
  2. data/lib/mongoid.rb +2 -2
  3. data/lib/mongoid/associations.rb +16 -9
  4. data/lib/mongoid/associations/belongs_to.rb +29 -5
  5. data/lib/mongoid/associations/has_many.rb +12 -0
  6. data/lib/mongoid/associations/has_one.rb +29 -9
  7. data/lib/mongoid/associations/options.rb +5 -0
  8. data/lib/mongoid/associations/relates_to_many.rb +10 -0
  9. data/lib/mongoid/associations/relates_to_one.rb +24 -5
  10. data/lib/mongoid/commands.rb +8 -0
  11. data/lib/mongoid/commands/quick_save.rb +19 -0
  12. data/lib/mongoid/commands/save.rb +1 -1
  13. data/lib/mongoid/criteria.rb +8 -18
  14. data/lib/mongoid/document.rb +27 -59
  15. data/mongoid.gemspec +8 -9
  16. data/spec/integration/mongoid/associations_spec.rb +41 -0
  17. data/spec/integration/mongoid/document_spec.rb +0 -13
  18. data/spec/spec_helper.rb +1 -8
  19. data/spec/unit/mongoid/associations/belongs_to_spec.rb +34 -4
  20. data/spec/unit/mongoid/associations/has_many_spec.rb +9 -0
  21. data/spec/unit/mongoid/associations/has_one_spec.rb +23 -2
  22. data/spec/unit/mongoid/associations/options_spec.rb +12 -1
  23. data/spec/unit/mongoid/associations/relates_to_many_spec.rb +22 -0
  24. data/spec/unit/mongoid/associations/relates_to_one_spec.rb +65 -1
  25. data/spec/unit/mongoid/associations_spec.rb +53 -1
  26. data/spec/unit/mongoid/commands/quick_save_spec.rb +24 -0
  27. data/spec/unit/mongoid/commands/save_spec.rb +2 -2
  28. data/spec/unit/mongoid/commands_spec.rb +107 -102
  29. data/spec/unit/mongoid/criteria_spec.rb +33 -1
  30. metadata +7 -8
  31. data/lib/mongoid/associations/accessor.rb +0 -30
  32. data/lib/mongoid/associations/decorator.rb +0 -27
  33. data/spec/unit/mongoid/associations/accessor_spec.rb +0 -123
  34. data/spec/unit/mongoid/associations/decorator_spec.rb +0 -36
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.6
1
+ 0.9.7
@@ -80,12 +80,12 @@ module Mongoid
80
80
  # Sets the Mongo::DB to be used.
81
81
  def self.database=(db)
82
82
  raise InvalidDatabaseError.new("Database should be a Mongo::DB, not #{db.class.name}") unless db.kind_of?(Mongo::DB)
83
- @@database = db
83
+ @database = db
84
84
  end
85
85
 
86
86
  # Returns the Mongo::DB to use or raise an error if none was set.
87
87
  def self.database
88
- @@database || (raise InvalidDatabaseError.new("No database has been set"))
88
+ @database || (raise InvalidDatabaseError.new("No database has been set"))
89
89
  end
90
90
 
91
91
  end
@@ -1,6 +1,4 @@
1
1
  # encoding: utf-8
2
- require "mongoid/associations/decorator"
3
- require "mongoid/associations/accessor"
4
2
  require "mongoid/associations/belongs_to"
5
3
  require "mongoid/associations/has_many"
6
4
  require "mongoid/associations/has_one"
@@ -22,9 +20,15 @@ module Mongoid # :nodoc:
22
20
  self.class.associations
23
21
  end
24
22
 
25
- # Updates all the relational associations for the document.
23
+ # Updates all the one-to-many relational associations for the name.
26
24
  def update_associations(name)
27
- send(name).each { |doc| doc.save }
25
+ send(name).each { |doc| doc.quick_save }
26
+ end
27
+
28
+ # Update the one-to-one relational association for the name.
29
+ def update_association(name)
30
+ association = send(name)
31
+ association.quick_save if association
28
32
  end
29
33
  end
30
34
 
@@ -138,12 +142,15 @@ module Mongoid # :nodoc:
138
142
  # end
139
143
  #
140
144
  def relates_to_one(name, options = {})
141
- field "#{name.to_s}_id"
142
- index "#{name.to_s}_id"
145
+ key = name.to_s
146
+ field "#{key}_id"
143
147
  add_association(
144
148
  Associations::RelatesToOne,
145
149
  Associations::Options.new(options.merge(:name => name))
146
150
  )
151
+ before_save do |document|
152
+ document.update_association(name)
153
+ end
147
154
  end
148
155
 
149
156
  # Adds a relational association from the Document to many Documents in
@@ -162,7 +169,7 @@ module Mongoid # :nodoc:
162
169
  def relates_to_many(name, options = {})
163
170
  add_association(
164
171
  Associations::RelatesToMany,
165
- Associations::Options.new(options.merge(:name => name))
172
+ Associations::Options.new(options.merge(:name => name, :parent_key => self.name.foreign_key))
166
173
  )
167
174
  before_save do |document|
168
175
  document.update_associations(name)
@@ -177,11 +184,11 @@ module Mongoid # :nodoc:
177
184
  associations[name] = type
178
185
  define_method(name) do
179
186
  return instance_variable_get("@#{name}") if instance_variable_defined?("@#{name}")
180
- proxy = Associations::Accessor.get(type, self, options)
187
+ proxy = type.instantiate(self, options)
181
188
  instance_variable_set("@#{name}", proxy)
182
189
  end
183
190
  define_method("#{name}=") do |object|
184
- proxy = Associations::Accessor.set(type, self, object, options)
191
+ proxy = type.update(object, self, options)
185
192
  if instance_variable_defined?("@#{name}")
186
193
  remove_instance_variable("@#{name}")
187
194
  else
@@ -2,27 +2,51 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Associations #:nodoc:
4
4
  class BelongsTo #:nodoc:
5
- include Decorator
6
5
 
7
- # Creates the new association by setting the internal
6
+ delegate :==, :to => :document
7
+ attr_reader :document, :options
8
+
9
+ # Creates the new association by setting the internal
8
10
  # document as the passed in Document. This should be the
9
11
  # parent.
10
12
  #
11
13
  # All method calls on this object will then be delegated
12
14
  # to the internal document itself.
15
+ #
16
+ # Options:
17
+ #
18
+ # document: The parent +Document+
19
+ # options: The association options
13
20
  def initialize(document, options)
14
- @document = document.parent
15
- decorate!
21
+ @document, @options = document, options
16
22
  end
17
23
 
18
24
  # Returns the parent document. The id param is present for
19
- # compatibility with rails, however this could be overwritten
25
+ # compatibility with rails, however this could be overwritten
20
26
  # in the future.
21
27
  def find(id)
22
28
  @document
23
29
  end
24
30
 
31
+ # Delegate all missing methods over to the parent +Document+.
32
+ def method_missing(name, *args)
33
+ @document.send(name, *args)
34
+ end
35
+
25
36
  class << self
37
+ # Creates the new association by setting the internal
38
+ # document as the passed in Document. This should be the
39
+ # parent.
40
+ #
41
+ # Options:
42
+ #
43
+ # document: The parent +Document+
44
+ # options: The association options
45
+ def instantiate(document, options)
46
+ parent = document.parent
47
+ parent.nil? ? nil : new(parent, options)
48
+ end
49
+
26
50
  # Returns the macro used to create the association.
27
51
  def macro
28
52
  :belongs_to
@@ -83,6 +83,18 @@ module Mongoid #:nodoc:
83
83
  end
84
84
 
85
85
  class << self
86
+
87
+ # Preferred method of creating a new +HasMany+ association. It will
88
+ # delegate to new.
89
+ #
90
+ # Options:
91
+ #
92
+ # document: The parent +Document+
93
+ # options: The association options
94
+ def instantiate(document, options)
95
+ new(document, options)
96
+ end
97
+
86
98
  # Returns the macro used to create the association.
87
99
  def macro
88
100
  :has_many
@@ -2,16 +2,13 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Associations #:nodoc:
4
4
  class HasOne #:nodoc:
5
- include Decorator
6
5
 
7
- delegate :valid?, :to => :document
8
-
9
- attr_accessor :parent, :options
6
+ delegate :==, :to => :document
7
+ attr_reader :document, :parent, :options
10
8
 
11
9
  # Build a new object for the association.
12
10
  def build(attributes)
13
11
  @document = attributes.assimilate(@parent, @options)
14
- decorate!
15
12
  self
16
13
  end
17
14
 
@@ -28,14 +25,37 @@ module Mongoid #:nodoc:
28
25
  #
29
26
  # All method calls on this object will then be delegated
30
27
  # to the internal document itself.
31
- def initialize(document, options)
28
+ #
29
+ # Options:
30
+ #
31
+ # document: The parent +Document+
32
+ # attributes: The attributes of the decorated object.
33
+ # options: The association options.
34
+ def initialize(document, attributes, options)
32
35
  @parent, @options = document, options
33
- attributes = @parent.attributes[options.name]
34
- @document = (attributes || {}).assimilate(@parent, @options)
35
- decorate!
36
+ unless attributes.nil?
37
+ @document = attributes.assimilate(@parent, @options)
38
+ end
39
+ end
40
+
41
+ # Delegate all missing methods over to the +Document+.
42
+ def method_missing(name, *args)
43
+ @document.send(name, *args)
36
44
  end
37
45
 
38
46
  class << self
47
+ # Preferred method of instantiating a new +HasOne+, since nil values
48
+ # will be handled properly.
49
+ #
50
+ # Options:
51
+ #
52
+ # document: The parent +Document+
53
+ # options: The association options.
54
+ def instantiate(document, options)
55
+ attributes = document.attributes[options.name]
56
+ new(document, attributes, options)
57
+ end
58
+
39
59
  # Returns the macro used to create the association.
40
60
  def macro
41
61
  :has_one
@@ -32,6 +32,11 @@ module Mongoid #:nodoc:
32
32
  @attributes[:name]
33
33
  end
34
34
 
35
+ # Returns the parent foreign key association name.
36
+ def parent_key
37
+ @attributes[:parent_key]
38
+ end
39
+
35
40
  # Returns whether or not this association is polymorphic.
36
41
  def polymorphic
37
42
  @attributes[:polymorphic] == true
@@ -17,6 +17,16 @@ module Mongoid #:nodoc:
17
17
  end
18
18
 
19
19
  class << self
20
+ # Preferred method for creating the new +RelatesToMany+ association.
21
+ #
22
+ # Options:
23
+ #
24
+ # document: The +Document+ that contains the relationship.
25
+ # options: The association +Options+.
26
+ def instantiate(document, options)
27
+ new(document, options)
28
+ end
29
+
20
30
  # Returns the macro used to create the association.
21
31
  def macro
22
32
  :relates_to_many
@@ -2,7 +2,9 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Associations #:nodoc:
4
4
  class RelatesToOne #:nodoc:
5
- include Decorator
5
+
6
+ delegate :==, :to => :document
7
+ attr_reader :document
6
8
 
7
9
  # Initializing a related association only requires looking up the object
8
10
  # by its id.
@@ -11,12 +13,29 @@ module Mongoid #:nodoc:
11
13
  #
12
14
  # document: The +Document+ that contains the relationship.
13
15
  # options: The association +Options+.
14
- def initialize(document, options)
15
- @document = options.klass.find(document.send(options.foreign_key))
16
- decorate!
16
+ def initialize(document, foreign_key, options)
17
+ @document = options.klass.find(foreign_key)
18
+ end
19
+
20
+ # Delegate all missing methods over to the +Document+.
21
+ def method_missing(name, *args)
22
+ @document.send(name, *args)
17
23
  end
18
24
 
19
25
  class << self
26
+ # Instantiate a new +RelatesToOne+ or return nil if the foreign key is
27
+ # nil. It is preferrable to use this method over the traditional call
28
+ # to new.
29
+ #
30
+ # Options:
31
+ #
32
+ # document: The +Document+ that contains the relationship.
33
+ # options: The association +Options+.
34
+ def instantiate(document, options)
35
+ foreign_key = document.send(options.foreign_key)
36
+ foreign_key.nil? ? nil : new(document, foreign_key, options)
37
+ end
38
+
20
39
  # Returns the macro used to create the association.
21
40
  def macro
22
41
  :relates_to_one
@@ -35,7 +54,7 @@ module Mongoid #:nodoc:
35
54
  #
36
55
  # <tt>RelatesToOne.update(game, person, options)</tt>
37
56
  def update(related, parent, options)
38
- parent.send("#{options.foreign_key}=", related.id)
57
+ parent.send("#{options.foreign_key}=", related.id); related
39
58
  end
40
59
  end
41
60
 
@@ -5,6 +5,7 @@ require "mongoid/commands/delete_all"
5
5
  require "mongoid/commands/destroy"
6
6
  require "mongoid/commands/destroy_all"
7
7
  require "mongoid/commands/save"
8
+ require "mongoid/commands/quick_save"
8
9
  require "mongoid/commands/validate"
9
10
 
10
11
  module Mongoid #:nodoc:
@@ -61,6 +62,13 @@ module Mongoid #:nodoc:
61
62
  end
62
63
  end
63
64
 
65
+ # Performs a save with no validations and no callbacks. This is used in
66
+ # relational association saves, and is not recommended for use by
67
+ # anything else.
68
+ def quick_save
69
+ QuickSave.execute(self)
70
+ end
71
+
64
72
  # Update the attributes of the +Document+. Will call save after the
65
73
  # attributes have been updated.
66
74
  def update_attributes(attrs = {})
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Commands
4
+ class QuickSave
5
+ # Performs a save of the supplied +Document+ without any validations or
6
+ # callbacks. This is a dangerous command only intended for internal use
7
+ # with saving relational associations.
8
+ #
9
+ # Options:
10
+ #
11
+ # doc: A +Document+ that is going to be persisted.
12
+ #
13
+ # Returns: true
14
+ def self.execute(doc)
15
+ doc.collection.save(doc.attributes)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -21,7 +21,7 @@ module Mongoid #:nodoc:
21
21
  collection ? collection.save(doc.attributes) : raise(MissingParentError.new(doc))
22
22
  end
23
23
  doc.run_callbacks :after_save
24
- return doc
24
+ return true
25
25
  end
26
26
  end
27
27
  end
@@ -110,13 +110,9 @@ module Mongoid #:nodoc:
110
110
  # Example:
111
111
  #
112
112
  # <tt>criteria.each { |doc| p doc }</tt>
113
- def each
113
+ def each(&block)
114
114
  @collection ||= execute
115
- if block_given?
116
- @collection.each { |doc| yield doc }
117
- else
118
- @collection.each
119
- end
115
+ block_given? ? @collection.each { |doc| yield doc } : self
120
116
  end
121
117
 
122
118
  # Adds a criterion to the +Criteria+ that specifies values that are not allowed
@@ -152,9 +148,7 @@ module Mongoid #:nodoc:
152
148
  #
153
149
  # Returns: <tt>self</tt>
154
150
  def extras(extras)
155
- @options = extras
156
- filter_options
157
- self
151
+ @options = extras; filter_options; self
158
152
  end
159
153
 
160
154
  # Return the first result for the +Criteria+.
@@ -176,8 +170,7 @@ module Mongoid #:nodoc:
176
170
  # Example:
177
171
  #
178
172
  # <tt>criteria.select(:field1).where(:field1 => "Title").group(Person)</tt>
179
- def group(klass = nil)
180
- @klass = klass if klass
173
+ def group
181
174
  @klass.collection.group(
182
175
  @options[:fields],
183
176
  @selector,
@@ -367,11 +360,8 @@ module Mongoid #:nodoc:
367
360
  # Either returns the page option and removes it from the options, or
368
361
  # returns a default value of 1.
369
362
  def page
370
- if @options[:skip] && @options[:limit]
371
- (@options[:skip].to_i + @options[:limit].to_i) / @options[:limit].to_i
372
- else
373
- 1
374
- end
363
+ skips, limits = @options[:skip], @options[:limit]
364
+ (skips && limits) ? (skips + limits) / limits : 1
375
365
  end
376
366
 
377
367
  # Executes the +Criteria+ and paginates the results.
@@ -505,8 +495,8 @@ module Mongoid #:nodoc:
505
495
  page_num = @options.delete(:page)
506
496
  per_page_num = @options.delete(:per_page)
507
497
  if (page_num || per_page_num)
508
- @options[:limit] = (per_page_num || 20).to_i
509
- @options[:skip] = (page_num || 1).to_i * @options[:limit] - @options[:limit]
498
+ @options[:limit] = limits = (per_page_num || 20).to_i
499
+ @options[:skip] = (page_num || 1).to_i * limits - limits
510
500
  end
511
501
  end
512
502
 
@@ -8,17 +8,10 @@ module Mongoid #:nodoc:
8
8
  attr_accessor :association_name, :parent
9
9
  attr_reader :attributes, :new_record
10
10
 
11
- define_callbacks \
12
- :after_create,
13
- :after_destroy,
14
- :after_save,
15
- :after_update,
16
- :after_validation,
17
- :before_create,
18
- :before_destroy,
19
- :before_save,
20
- :before_update,
21
- :before_validation
11
+ delegate :collection, :defaults, :embedded?, :fields, :primary_key, :to => :klass
12
+
13
+ define_callbacks :before_create, :before_destroy, :before_save, :before_update, :before_validation
14
+ define_callbacks :after_create, :after_destroy, :after_save, :after_update, :after_validation
22
15
 
23
16
  class << self
24
17
 
@@ -29,7 +22,7 @@ module Mongoid #:nodoc:
29
22
  # Returns: <tt>Mongo::Collection</tt>
30
23
  def collection
31
24
  return nil if embedded?
32
- @collection_name = self.to_s.demodulize.tableize
25
+ @collection_name ||= self.to_s.demodulize.tableize
33
26
  @collection ||= Mongoid.database.collection(@collection_name)
34
27
  end
35
28
 
@@ -56,11 +49,8 @@ module Mongoid #:nodoc:
56
49
  #
57
50
  # <tt>field :score, :default => 0</tt>
58
51
  def field(name, options = {})
59
- @fields ||= {}.with_indifferent_access
60
- @defaults ||= {}.with_indifferent_access
61
- @fields[name] = Field.new(name.to_s, options)
62
- @defaults[name] = options[:default] if options[:default]
63
- define_field_methods(name, options)
52
+ define(name, options)
53
+ default(name, options)
64
54
  end
65
55
 
66
56
  # Returns all the fields for the Document as a +Hash+ with names as keys.
@@ -107,12 +97,23 @@ module Mongoid #:nodoc:
107
97
  end
108
98
 
109
99
  protected
110
- def define_field_methods(name, options)
100
+
101
+ # Define a field attribute for the +Document+.
102
+ def define(name, options = {})
103
+ @fields ||= {}.with_indifferent_access
104
+ @fields[name] = Field.new(name.to_s, options)
111
105
  define_method(name) { read_attribute(name) }
112
106
  define_method("#{name}=") { |value| write_attribute(name, value) }
113
107
  define_method("#{name}?") { read_attribute(name) == true } if options[:type] == Boolean
114
108
  end
115
109
 
110
+ # Set up a default value for a field.
111
+ def default(name, options = {})
112
+ value = options[:default]
113
+ @defaults ||= {}.with_indifferent_access
114
+ @defaults[name] = value if value
115
+ end
116
+
116
117
  end
117
118
 
118
119
  # Performs equality checking on the attributes. For now we chack against
@@ -138,9 +139,7 @@ module Mongoid #:nodoc:
138
139
  #
139
140
  # Returns: The child +Document+.
140
141
  def assimilate(parent, options)
141
- parentize(parent, options.name)
142
- notify
143
- self
142
+ parentize(parent, options.name); notify; self
144
143
  end
145
144
 
146
145
  # Clone the current +Document+. This will return all attributes with the
@@ -149,26 +148,6 @@ module Mongoid #:nodoc:
149
148
  self.class.instantiate(@attributes.except(:_id).except(:versions).dup, true)
150
149
  end
151
150
 
152
- # Get the Mongo::Collection associated with this Document.
153
- def collection
154
- self.class.collection
155
- end
156
-
157
- # Returns the class defaults
158
- def defaults
159
- self.class.defaults
160
- end
161
-
162
- # Return true if the +Document+ is embedded in another +Document+.
163
- def embedded?
164
- self.class.embedded?
165
- end
166
-
167
- # Get the fields for the Document class.
168
- def fields
169
- self.class.fields
170
- end
171
-
172
151
  # Get the id associated with this object. This will pull the _id value out
173
152
  # of the attributes +Hash+.
174
153
  def id
@@ -209,22 +188,6 @@ module Mongoid #:nodoc:
209
188
  "#{self.class.name} : #{@attributes.inspect}"
210
189
  end
211
190
 
212
- # Return the +Document+ primary key. This will only exist if a key has been
213
- # set up on the +Document+ and will return an array of fields.
214
- #
215
- # Example:
216
- #
217
- # class Person < Mongoid::Document
218
- # field :first_name
219
- # field :last_name
220
- # key :first_name, :last_name
221
- # end
222
- #
223
- # <tt>person.primary_key #[:first_name, :last_name]</tt>
224
- def primary_key
225
- self.class.primary_key
226
- end
227
-
228
191
  # Returns true is the +Document+ has not been persisted to the database,
229
192
  # false if it has. This is determined by the instance variable @new_record
230
193
  # and NOT if the object has an id.
@@ -322,8 +285,8 @@ module Mongoid #:nodoc:
322
285
  # This will also cause the observing +Document+ to notify it's parent if
323
286
  # there is any.
324
287
  def update(child, clear = false)
325
- @attributes.insert(child.association_name, child.attributes) unless clear
326
- @attributes.delete(child.association_name) if clear
288
+ name = child.association_name
289
+ clear ? @attributes.delete(name) : @attributes.insert(name, child.attributes)
327
290
  notify
328
291
  end
329
292
 
@@ -377,5 +340,10 @@ module Mongoid #:nodoc:
377
340
  @attributes[:_id] = Mongo::ObjectID.new.to_s unless id
378
341
  end
379
342
  end
343
+
344
+ # Convenience method to get the document's class
345
+ def klass
346
+ self.class
347
+ end
380
348
  end
381
349
  end