mongoid 0.9.8 → 0.9.9

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/README.textile +5 -4
  2. data/VERSION +1 -1
  3. data/lib/mongoid.rb +3 -28
  4. data/lib/mongoid/associations.rb +63 -44
  5. data/lib/mongoid/associations/{relates_to_one.rb → belongs_to_related.rb} +4 -4
  6. data/lib/mongoid/associations/has_many.rb +26 -12
  7. data/lib/mongoid/associations/has_many_related.rb +100 -0
  8. data/lib/mongoid/associations/has_one.rb +13 -2
  9. data/lib/mongoid/associations/{relates_to_many.rb → has_one_related.rb} +34 -9
  10. data/lib/mongoid/attributes.rb +108 -8
  11. data/lib/mongoid/commands.rb +3 -11
  12. data/lib/mongoid/commands/save.rb +2 -6
  13. data/lib/mongoid/document.rb +6 -70
  14. data/lib/mongoid/errors.rb +34 -0
  15. data/mongoid.gemspec +14 -11
  16. data/spec/integration/mongoid/associations_spec.rb +19 -14
  17. data/spec/spec_helper.rb +6 -3
  18. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +112 -0
  19. data/spec/unit/mongoid/associations/has_many_related_spec.rb +292 -0
  20. data/spec/unit/mongoid/associations/has_many_spec.rb +17 -0
  21. data/spec/unit/mongoid/associations/has_one_related_spec.rb +130 -0
  22. data/spec/unit/mongoid/associations/has_one_spec.rb +53 -0
  23. data/spec/unit/mongoid/associations_spec.rb +36 -8
  24. data/spec/unit/mongoid/attributes_spec.rb +218 -0
  25. data/spec/unit/mongoid/commands/save_spec.rb +4 -2
  26. data/spec/unit/mongoid/commands_spec.rb +4 -17
  27. data/spec/unit/mongoid/criteria_spec.rb +0 -5
  28. data/spec/unit/mongoid/document_spec.rb +26 -156
  29. data/spec/unit/mongoid/errors_spec.rb +87 -0
  30. metadata +14 -11
  31. data/lib/mongoid/commands/quick_save.rb +0 -19
  32. data/spec/unit/mongoid/associations/relates_to_many_spec.rb +0 -76
  33. data/spec/unit/mongoid/associations/relates_to_one_spec.rb +0 -112
  34. data/spec/unit/mongoid/commands/quick_save_spec.rb +0 -24
@@ -44,11 +44,11 @@ Example of a simple domain model:
44
44
  field :title
45
45
  field :age, :type => Integer, :default => 0
46
46
 
47
- has_many :addresses
48
47
  has_one :name
48
+ has_many :addresses
49
49
 
50
- relates_to_one :game
51
- relates_to_many :posts
50
+ has_one_related :game
51
+ has_many_related :posts
52
52
  end
53
53
 
54
54
  class Address < Mongoid::Document
@@ -71,10 +71,11 @@ Example of a simple domain model:
71
71
  field :title
72
72
  field :text
73
73
  field :tags, :type => Array
74
- relates_to_one :person
74
+ belongs_to_related :person
75
75
  end
76
76
 
77
77
  class Game < ActiveRecord::Base
78
+ belongs_to_related :person
78
79
  end
79
80
  </pre>
80
81
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.8
1
+ 0.9.9
@@ -43,6 +43,7 @@ require "mongoid/commands"
43
43
  require "mongoid/criteria"
44
44
  require "mongoid/dynamic_finder"
45
45
  require "mongoid/extensions"
46
+ require "mongoid/errors"
46
47
  require "mongoid/field"
47
48
  require "mongoid/finders"
48
49
  require "mongoid/timestamps"
@@ -51,41 +52,15 @@ require "mongoid/document"
51
52
 
52
53
  module Mongoid
53
54
 
54
- # Raised when the database connection has not been set up.
55
- class InvalidDatabaseError < RuntimeError; end
56
-
57
- # Raised when invalid options are passed into a constructor.
58
- class InvalidOptionsError < RuntimeError; end
59
-
60
- # Raised when an association is defined on the class, but the
61
- # attribute in the hash is not an Array or Hash, or when
62
- # checking equality on objects of different types.
63
- class TypeMismatchError < RuntimeError; end
64
-
65
- # Raised when a persisence method ending in ! fails validation.
66
- class ValidationsError < RuntimeError; end
67
-
68
- # A common case of errors is to instantiate a child document without a
69
- # reference to a parent, which will result in trying to save on a nil
70
- # collection. This error is raised to help debug the issue.
71
- class MissingParentError < RuntimeError
72
- def initialize(doc)
73
- @document = doc
74
- end
75
- def message
76
- "Attempted to save embedded document #{@document.class.name}, but there was no associated parent"
77
- end
78
- end
79
-
80
55
  # Sets the Mongo::DB to be used.
81
56
  def self.database=(db)
82
- raise InvalidDatabaseError.new("Database should be a Mongo::DB, not #{db.class.name}") unless db.kind_of?(Mongo::DB)
57
+ raise Errors::InvalidDatabase.new("Database should be a Mongo::DB, not #{db.class.name}") unless db.kind_of?(Mongo::DB)
83
58
  @database = db
84
59
  end
85
60
 
86
61
  # Returns the Mongo::DB to use or raise an error if none was set.
87
62
  def self.database
88
- @database || (raise InvalidDatabaseError.new("No database has been set"))
63
+ @database || (raise Errors::InvalidDatabase.new("No database has been set, please use Mongoid.database="))
89
64
  end
90
65
 
91
66
  end
@@ -1,9 +1,10 @@
1
1
  # encoding: utf-8
2
2
  require "mongoid/associations/belongs_to"
3
+ require "mongoid/associations/belongs_to_related"
3
4
  require "mongoid/associations/has_many"
5
+ require "mongoid/associations/has_many_related"
4
6
  require "mongoid/associations/has_one"
5
- require "mongoid/associations/relates_to_many"
6
- require "mongoid/associations/relates_to_one"
7
+ require "mongoid/associations/has_one_related"
7
8
 
8
9
  module Mongoid # :nodoc:
9
10
  module Associations #:nodoc:
@@ -22,13 +23,13 @@ module Mongoid # :nodoc:
22
23
 
23
24
  # Updates all the one-to-many relational associations for the name.
24
25
  def update_associations(name)
25
- send(name).each { |doc| doc.quick_save }
26
+ send(name).each { |doc| doc.save }
26
27
  end
27
28
 
28
29
  # Update the one-to-one relational association for the name.
29
30
  def update_association(name)
30
31
  association = send(name)
31
- association.quick_save if association
32
+ association.save unless association.nil?
32
33
  end
33
34
  end
34
35
 
@@ -56,7 +57,7 @@ module Mongoid # :nodoc:
56
57
  # end
57
58
  def belongs_to(name, options = {})
58
59
  unless options.has_key?(:inverse_of)
59
- raise InvalidOptionsError.new("Options for belongs_to association must include :inverse_of")
60
+ raise Errors::InvalidOptions.new("Options for belongs_to association must include :inverse_of")
60
61
  end
61
62
  @embedded = true
62
63
  add_association(
@@ -65,6 +66,27 @@ module Mongoid # :nodoc:
65
66
  )
66
67
  end
67
68
 
69
+ # Adds a relational association from the child Document to a Document in
70
+ # another database or collection.
71
+ #
72
+ # Options:
73
+ #
74
+ # name: A +Symbol+ that is the related class name.
75
+ #
76
+ # Example:
77
+ #
78
+ # class Game < Mongoid::Document
79
+ # belongs_to_related :person
80
+ # end
81
+ #
82
+ def belongs_to_related(name, options = {})
83
+ field "#{name.to_s}_id"
84
+ add_association(
85
+ Associations::BelongsToRelated,
86
+ Associations::Options.new(options.merge(:name => name))
87
+ )
88
+ end
89
+
68
90
  # Adds the association from a parent document to its children. The name
69
91
  # of the association needs to be a pluralized form of the child class
70
92
  # name.
@@ -89,6 +111,29 @@ module Mongoid # :nodoc:
89
111
  )
90
112
  end
91
113
 
114
+ # Adds a relational association from the Document to many Documents in
115
+ # another database or collection.
116
+ #
117
+ # Options:
118
+ #
119
+ # name: A +Symbol+ that is the related class name pluralized.
120
+ #
121
+ # Example:
122
+ #
123
+ # class Person < Mongoid::Document
124
+ # has_many_related :posts
125
+ # end
126
+ #
127
+ def has_many_related(name, options = {})
128
+ add_association(
129
+ Associations::HasManyRelated,
130
+ Associations::Options.new(options.merge(:name => name, :parent_key => self.name.foreign_key))
131
+ )
132
+ before_save do |document|
133
+ document.update_associations(name)
134
+ end
135
+ end
136
+
92
137
  # Adds the association from a parent document to its child. The name
93
138
  # of the association needs to be a singular form of the child class
94
139
  # name.
@@ -113,67 +158,41 @@ module Mongoid # :nodoc:
113
158
  )
114
159
  end
115
160
 
116
- # Returns the macro associated with the supplied association name. This
117
- # will return has_one, has_many, belongs_to or nil.
118
- #
119
- # Options:
120
- #
121
- # name: The association name.
122
- #
123
- # Example:
124
- #
125
- # <tt>Person.reflect_on_association(:addresses)</tt>
126
- def reflect_on_association(name)
127
- association = associations[name]
128
- association ? association.macro : nil
129
- end
130
-
131
- # Adds a relational association from the Document to a Document in
161
+ # Adds a relational association from the Document to one Document in
132
162
  # another database or collection.
133
163
  #
134
164
  # Options:
135
165
  #
136
- # name: A +Symbol+ that is the related class name.
166
+ # name: A +Symbol+ that is the related class name pluralized.
137
167
  #
138
168
  # Example:
139
169
  #
140
170
  # class Person < Mongoid::Document
141
- # relates_to_one :game
171
+ # has_one_related :game
142
172
  # end
143
- #
144
- def relates_to_one(name, options = {})
145
- key = name.to_s
146
- field "#{key}_id"
173
+ def has_one_related(name, options = {})
147
174
  add_association(
148
- Associations::RelatesToOne,
149
- Associations::Options.new(options.merge(:name => name))
175
+ Associations::HasOneRelated,
176
+ Associations::Options.new(options.merge(:name => name, :parent_key => self.name.foreign_key))
150
177
  )
151
178
  before_save do |document|
152
179
  document.update_association(name)
153
180
  end
154
181
  end
155
182
 
156
- # Adds a relational association from the Document to many Documents in
157
- # another database or collection.
183
+ # Returns the macro associated with the supplied association name. This
184
+ # will return has_one, has_many, belongs_to or nil.
158
185
  #
159
186
  # Options:
160
187
  #
161
- # name: A +Symbol+ that is the related class name pluralized.
188
+ # name: The association name.
162
189
  #
163
190
  # Example:
164
191
  #
165
- # class Person < Mongoid::Document
166
- # relates_to_many :posts
167
- # end
168
- #
169
- def relates_to_many(name, options = {})
170
- add_association(
171
- Associations::RelatesToMany,
172
- Associations::Options.new(options.merge(:name => name, :parent_key => self.name.foreign_key))
173
- )
174
- before_save do |document|
175
- document.update_associations(name)
176
- end
192
+ # <tt>Person.reflect_on_association(:addresses)</tt>
193
+ def reflect_on_association(name)
194
+ association = associations[name]
195
+ association ? association.macro : nil
177
196
  end
178
197
 
179
198
  protected
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Associations #:nodoc:
4
- class RelatesToOne #:nodoc:
4
+ class BelongsToRelated #:nodoc:
5
5
 
6
6
  delegate :==, :to => :document
7
7
  attr_reader :document
@@ -23,7 +23,7 @@ module Mongoid #:nodoc:
23
23
  end
24
24
 
25
25
  class << self
26
- # Instantiate a new +RelatesToOne+ or return nil if the foreign key is
26
+ # Instantiate a new +BelongsToRelated+ or return nil if the foreign key is
27
27
  # nil. It is preferrable to use this method over the traditional call
28
28
  # to new.
29
29
  #
@@ -38,7 +38,7 @@ module Mongoid #:nodoc:
38
38
 
39
39
  # Returns the macro used to create the association.
40
40
  def macro
41
- :relates_to_one
41
+ :belongs_to_related
42
42
  end
43
43
 
44
44
  # Perform an update of the relationship of the parent and child. This
@@ -52,7 +52,7 @@ module Mongoid #:nodoc:
52
52
  #
53
53
  # Example:
54
54
  #
55
- # <tt>RelatesToOne.update(game, person, options)</tt>
55
+ # <tt>BelongsToRelated.update(game, person, options)</tt>
56
56
  def update(related, parent, options)
57
57
  parent.send("#{options.foreign_key}=", related.id); related
58
58
  end
@@ -7,10 +7,12 @@ module Mongoid #:nodoc:
7
7
 
8
8
  # Appends the object to the +Array+, setting its parent in
9
9
  # the process.
10
- def <<(object)
11
- object.parentize(@parent, @association_name)
12
- @documents << object
13
- object.is_a?(Array) ? object.each(&:notify) : object.notify
10
+ def <<(*objects)
11
+ objects.flatten.each do |object|
12
+ object.parentize(@parent, @association_name)
13
+ @documents << object
14
+ object.notify
15
+ end
14
16
  end
15
17
 
16
18
  # Clears the association, and notifies the parents of the removal.
@@ -23,14 +25,8 @@ module Mongoid #:nodoc:
23
25
 
24
26
  # Appends the object to the +Array+, setting its parent in
25
27
  # the process.
26
- def push(object)
27
- self << object
28
- end
29
-
30
- # Appends the object to the +Array+, setting its parent in
31
- # the process.
32
- def concat(object)
33
- self << object
28
+ def concat(*objects)
29
+ self << objects
34
30
  end
35
31
 
36
32
  # Builds a new Document and adds it to the association collection. The
@@ -82,6 +78,24 @@ module Mongoid #:nodoc:
82
78
  super(@documents)
83
79
  end
84
80
 
81
+ # Used for setting associations via a nested attributes setter from the
82
+ # parent +Document+.
83
+ #
84
+ # Options:
85
+ #
86
+ # attributes: A +Hash+ of integer keys and +Hash+ values.
87
+ def nested_build(attributes)
88
+ attributes.values.each do |attrs|
89
+ build(attrs)
90
+ end
91
+ end
92
+
93
+ # Appends the object to the +Array+, setting its parent in
94
+ # the process.
95
+ def push(*objects)
96
+ self << objects
97
+ end
98
+
85
99
  class << self
86
100
 
87
101
  # Preferred method of creating a new +HasMany+ association. It will
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class HasManyRelated < DelegateClass(Array) #:nodoc:
5
+
6
+ attr_reader :klass
7
+
8
+ # Appends the object to the +Array+, setting its parent in
9
+ # the process.
10
+ def <<(*objects)
11
+ objects.flatten.each do |object|
12
+ object.send("#{@foreign_key}=", @parent.id)
13
+ @documents << object
14
+ object.save unless @parent.new_record?
15
+ end
16
+ end
17
+
18
+ # Builds a new Document and adds it to the association collection. The
19
+ # document created will be of the same class as the others in the
20
+ # association, and the attributes will be passed into the constructor.
21
+ #
22
+ # Returns the newly created object.
23
+ def build(attributes)
24
+ object = @klass.instantiate(attributes.merge(@foreign_key => @parent.id))
25
+ @documents << object
26
+ object
27
+ end
28
+
29
+ # Delegates to <<
30
+ def concat(*objects)
31
+ self << objects
32
+ end
33
+
34
+ # Creates a new Document and adds it to the association collection. The
35
+ # document created will be of the same class as the others in the
36
+ # association, and the attributes will be passed into the constructor and
37
+ # the new object will then be saved.
38
+ #
39
+ # Returns the newly created object.
40
+ def create(attributes)
41
+ object = build(attributes)
42
+ object.save
43
+ end
44
+
45
+ # Initializing a related association only requires looking up the objects
46
+ # by their ids.
47
+ #
48
+ # Options:
49
+ #
50
+ # document: The +Document+ that contains the relationship.
51
+ # options: The association +Options+.
52
+ def initialize(document, options)
53
+ @parent, @klass = document, options.klass
54
+ @foreign_key = document.class.to_s.foreign_key
55
+ @documents = @klass.all(:conditions => { @foreign_key => document.id })
56
+ super(@documents)
57
+ end
58
+
59
+ # Delegates to <<
60
+ def push(*objects)
61
+ self << objects
62
+ end
63
+
64
+ class << self
65
+ # Preferred method for creating the new +HasManyRelated+ association.
66
+ #
67
+ # Options:
68
+ #
69
+ # document: The +Document+ that contains the relationship.
70
+ # options: The association +Options+.
71
+ def instantiate(document, options)
72
+ new(document, options)
73
+ end
74
+
75
+ # Returns the macro used to create the association.
76
+ def macro
77
+ :has_many_related
78
+ end
79
+
80
+ # Perform an update of the relationship of the parent and child. This
81
+ # will assimilate the child +Document+ into the parent's object graph.
82
+ #
83
+ # Options:
84
+ #
85
+ # related: The related object
86
+ # parent: The parent +Document+ to update.
87
+ # options: The association +Options+
88
+ #
89
+ # Example:
90
+ #
91
+ # <tt>RelatesToOne.update(game, person, options)</tt>
92
+ def update(related, document, options)
93
+ name = document.class.to_s.underscore
94
+ related.each { |child| child.send("#{name}=", document) }
95
+ end
96
+ end
97
+
98
+ end
99
+ end
100
+ end