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.
- data/README.textile +5 -4
- data/VERSION +1 -1
- data/lib/mongoid.rb +3 -28
- data/lib/mongoid/associations.rb +63 -44
- data/lib/mongoid/associations/{relates_to_one.rb → belongs_to_related.rb} +4 -4
- data/lib/mongoid/associations/has_many.rb +26 -12
- data/lib/mongoid/associations/has_many_related.rb +100 -0
- data/lib/mongoid/associations/has_one.rb +13 -2
- data/lib/mongoid/associations/{relates_to_many.rb → has_one_related.rb} +34 -9
- data/lib/mongoid/attributes.rb +108 -8
- data/lib/mongoid/commands.rb +3 -11
- data/lib/mongoid/commands/save.rb +2 -6
- data/lib/mongoid/document.rb +6 -70
- data/lib/mongoid/errors.rb +34 -0
- data/mongoid.gemspec +14 -11
- data/spec/integration/mongoid/associations_spec.rb +19 -14
- data/spec/spec_helper.rb +6 -3
- data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +112 -0
- data/spec/unit/mongoid/associations/has_many_related_spec.rb +292 -0
- data/spec/unit/mongoid/associations/has_many_spec.rb +17 -0
- data/spec/unit/mongoid/associations/has_one_related_spec.rb +130 -0
- data/spec/unit/mongoid/associations/has_one_spec.rb +53 -0
- data/spec/unit/mongoid/associations_spec.rb +36 -8
- data/spec/unit/mongoid/attributes_spec.rb +218 -0
- data/spec/unit/mongoid/commands/save_spec.rb +4 -2
- data/spec/unit/mongoid/commands_spec.rb +4 -17
- data/spec/unit/mongoid/criteria_spec.rb +0 -5
- data/spec/unit/mongoid/document_spec.rb +26 -156
- data/spec/unit/mongoid/errors_spec.rb +87 -0
- metadata +14 -11
- data/lib/mongoid/commands/quick_save.rb +0 -19
- data/spec/unit/mongoid/associations/relates_to_many_spec.rb +0 -76
- data/spec/unit/mongoid/associations/relates_to_one_spec.rb +0 -112
- data/spec/unit/mongoid/commands/quick_save_spec.rb +0 -24
data/README.textile
CHANGED
@@ -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
|
-
|
51
|
-
|
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
|
-
|
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.
|
1
|
+
0.9.9
|
data/lib/mongoid.rb
CHANGED
@@ -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
|
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
|
63
|
+
@database || (raise Errors::InvalidDatabase.new("No database has been set, please use Mongoid.database="))
|
89
64
|
end
|
90
65
|
|
91
66
|
end
|
data/lib/mongoid/associations.rb
CHANGED
@@ -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/
|
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.
|
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.
|
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
|
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
|
-
#
|
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
|
-
#
|
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::
|
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
|
-
#
|
157
|
-
#
|
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:
|
188
|
+
# name: The association name.
|
162
189
|
#
|
163
190
|
# Example:
|
164
191
|
#
|
165
|
-
#
|
166
|
-
|
167
|
-
|
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
|
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 +
|
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
|
-
:
|
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>
|
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 <<(
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
27
|
-
self <<
|
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
|