mongoid 2.0.0.beta.5 → 2.0.0.beta.7
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mongoid.rb +10 -2
- data/lib/mongoid/associations.rb +82 -58
- data/lib/mongoid/associations/embeds_one.rb +6 -6
- data/lib/mongoid/associations/foreign_key.rb +35 -0
- data/lib/mongoid/associations/meta_data.rb +9 -0
- data/lib/mongoid/associations/options.rb +1 -1
- data/lib/mongoid/associations/proxy.rb +9 -0
- data/lib/mongoid/associations/{belongs_to_related.rb → referenced_in.rb} +6 -5
- data/lib/mongoid/associations/{has_many_related.rb → references_many.rb} +69 -26
- data/lib/mongoid/associations/references_many_as_array.rb +78 -0
- data/lib/mongoid/associations/{has_one_related.rb → references_one.rb} +16 -2
- data/lib/mongoid/atomicity.rb +42 -0
- data/lib/mongoid/attributes.rb +148 -146
- data/lib/mongoid/callbacks.rb +5 -1
- data/lib/mongoid/collections.rb +31 -1
- data/lib/mongoid/components.rb +4 -1
- data/lib/mongoid/config.rb +2 -1
- data/lib/mongoid/criteria.rb +9 -0
- data/lib/mongoid/dirty.rb +211 -212
- data/lib/mongoid/document.rb +126 -185
- data/lib/mongoid/extensions.rb +5 -0
- data/lib/mongoid/extensions/array/conversions.rb +3 -5
- data/lib/mongoid/extensions/hash/conversions.rb +19 -22
- data/lib/mongoid/extensions/object/conversions.rb +3 -5
- data/lib/mongoid/extensions/set/conversions.rb +20 -0
- data/lib/mongoid/field.rb +11 -0
- data/lib/mongoid/finders.rb +8 -0
- data/lib/mongoid/hierarchy.rb +76 -0
- data/lib/mongoid/identity.rb +37 -29
- data/lib/mongoid/paths.rb +46 -47
- data/lib/mongoid/persistence.rb +111 -113
- data/lib/mongoid/persistence/insert.rb +1 -1
- data/lib/mongoid/persistence/insert_embedded.rb +10 -5
- data/lib/mongoid/persistence/remove_all.rb +3 -2
- data/lib/mongoid/persistence/update.rb +8 -3
- data/lib/mongoid/railtie.rb +3 -0
- data/lib/mongoid/railties/database.rake +33 -18
- data/lib/mongoid/timestamps.rb +9 -12
- data/lib/mongoid/validations/uniqueness.rb +16 -4
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/versioning.rb +10 -11
- data/lib/rails/generators/mongoid/config/config_generator.rb +0 -16
- metadata +64 -24
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Extensions #:nodoc:
|
4
|
+
module Set #:nodoc:
|
5
|
+
# This module converts set into mongoid related objects.
|
6
|
+
module Conversions #:nodoc:
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods #:nodoc:
|
10
|
+
def get(value)
|
11
|
+
::Set.new(value)
|
12
|
+
end
|
13
|
+
def set(value)
|
14
|
+
value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/mongoid/field.rb
CHANGED
@@ -12,6 +12,15 @@ module Mongoid #:nodoc:
|
|
12
12
|
!!@accessible
|
13
13
|
end
|
14
14
|
|
15
|
+
# Get the declared options for this field
|
16
|
+
#
|
17
|
+
# Returns:
|
18
|
+
#
|
19
|
+
# a hash of options
|
20
|
+
def options
|
21
|
+
@options
|
22
|
+
end
|
23
|
+
|
15
24
|
# Get the default value for the field.
|
16
25
|
#
|
17
26
|
# Returns:
|
@@ -38,6 +47,8 @@ module Mongoid #:nodoc:
|
|
38
47
|
@copyable = (@default.is_a?(Array) || @default.is_a?(Hash))
|
39
48
|
@type = options[:type] || String
|
40
49
|
@accessible = options.has_key?(:accessible) ? options[:accessible] : true
|
50
|
+
|
51
|
+
@options = options
|
41
52
|
end
|
42
53
|
|
43
54
|
# Used for setting an object in the attributes hash. If nil is provided the
|
data/lib/mongoid/finders.rb
CHANGED
@@ -30,6 +30,14 @@ module Mongoid #:nodoc:
|
|
30
30
|
Criteria.translate(self, *args).count
|
31
31
|
end
|
32
32
|
|
33
|
+
# Returns true if there are on document in database based on the
|
34
|
+
# provided arguments.
|
35
|
+
#
|
36
|
+
# <tt>Person.exists?(:first, :conditions => { :attribute => "value" })</tt>
|
37
|
+
def exists?(*args)
|
38
|
+
Criteria.translate(self, *args).limit(1).count == 1
|
39
|
+
end
|
40
|
+
|
33
41
|
# Helper to initialize a new +Criteria+ object for this class.
|
34
42
|
#
|
35
43
|
# Example:
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc
|
3
|
+
module Hierarchy #:nodoc
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
cattr_accessor :hereditary
|
7
|
+
self.hereditary = false
|
8
|
+
|
9
|
+
attr_accessor :_parent
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods #:nodoc:
|
13
|
+
|
14
|
+
# Get all child +Documents+ to this +Document+, going n levels deep if
|
15
|
+
# necessary. This is used when calling update persistence operations from
|
16
|
+
# the root document, where changes in the entire tree need to be
|
17
|
+
# determined. Note that persistence from the embedded documents will
|
18
|
+
# always be preferred, since they are optimized calls... This operation
|
19
|
+
# can get expensive in domains with large hierarchies.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
#
|
23
|
+
# <tt>person._children</tt>
|
24
|
+
#
|
25
|
+
# Returns:
|
26
|
+
#
|
27
|
+
# All child +Documents+ to this +Document+ in the entire hierarchy.
|
28
|
+
def _children
|
29
|
+
associations.inject([]) do |children, (name, metadata)|
|
30
|
+
if metadata.embedded? && name != "versions"
|
31
|
+
child = send(name)
|
32
|
+
child.to_a.each do |doc|
|
33
|
+
children.push(doc).concat(doc._children)
|
34
|
+
end unless child.blank?
|
35
|
+
end
|
36
|
+
children
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Is inheritance in play here?
|
41
|
+
#
|
42
|
+
# Returns:
|
43
|
+
#
|
44
|
+
# <tt>true</tt> if inheritance used, <tt>false</tt> if not.
|
45
|
+
def hereditary?
|
46
|
+
!!self.hereditary
|
47
|
+
end
|
48
|
+
|
49
|
+
# Sets up a child/parent association. This is used for newly created
|
50
|
+
# objects so they can be properly added to the graph and have the parent
|
51
|
+
# observers set up properly.
|
52
|
+
#
|
53
|
+
# Options:
|
54
|
+
#
|
55
|
+
# abject: The parent object that needs to be set for the child.
|
56
|
+
# association_name: The name of the association for the child.
|
57
|
+
#
|
58
|
+
# Example:
|
59
|
+
#
|
60
|
+
# <tt>address.parentize(person, :addresses)</tt>
|
61
|
+
def parentize(object, association_name)
|
62
|
+
self._parent = object
|
63
|
+
self.association_name = association_name.to_s
|
64
|
+
add_observer(object)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return the root +Document+ in the object graph. If the current +Document+
|
68
|
+
# is the root object in the graph it will return self.
|
69
|
+
def _root
|
70
|
+
object = self
|
71
|
+
while (object._parent) do object = object._parent; end
|
72
|
+
object || self
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/mongoid/identity.rb
CHANGED
@@ -1,39 +1,47 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Mongoid #:nodoc:
|
3
3
|
class Identity #:nodoc:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
# Create the identity for the +Document+.
|
5
|
+
#
|
6
|
+
# The id will be set in either in the form of a Mongo
|
7
|
+
# +ObjectID+ or a composite key set up by defining a key on the document.
|
8
|
+
#
|
9
|
+
# The _type will be set to the document's class name.
|
10
|
+
def create
|
11
|
+
identify!; type!
|
12
|
+
end
|
13
|
+
|
14
|
+
# Create the new identity generator - this will be expanded in the future
|
15
|
+
# to support pk generators.
|
16
|
+
#
|
17
|
+
# Options:
|
18
|
+
#
|
19
|
+
# document: The document to generate an id for.
|
20
|
+
def initialize(document)
|
21
|
+
@document = document
|
22
|
+
end
|
14
23
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
24
|
+
protected
|
25
|
+
# Return the proper id for the document.
|
26
|
+
def generate_id
|
27
|
+
id = BSON::ObjectID.new
|
28
|
+
Mongoid.use_object_ids ? id : id.to_s
|
29
|
+
end
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
# Set the id for the document.
|
32
|
+
def identify!
|
33
|
+
@document.id = compose.join(" ").identify if @document.primary_key
|
34
|
+
@document.id = generate_id if @document.id.blank?
|
35
|
+
end
|
27
36
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
37
|
+
# Set the _type field on the @document.ment.
|
38
|
+
def type!
|
39
|
+
@document._type = @document.class.name if @document.hereditary?
|
40
|
+
end
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
42
|
+
# Generates the composite key for a @document.ment.
|
43
|
+
def compose
|
44
|
+
@document.primary_key.collect { |key| @document.attributes[key] }.reject { |val| val.nil? }
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
data/lib/mongoid/paths.rb
CHANGED
@@ -6,57 +6,56 @@ module Mongoid #:nodoc:
|
|
6
6
|
cattr_accessor :__path
|
7
7
|
attr_accessor :_index
|
8
8
|
end
|
9
|
-
module InstanceMethods
|
10
|
-
# Get the insertion modifier for the document. Will be nil on root
|
11
|
-
# documents, $set on embeds_one, $push on embeds_many.
|
12
|
-
#
|
13
|
-
# Example:
|
14
|
-
#
|
15
|
-
# <tt>name.inserter</tt>
|
16
|
-
def _inserter
|
17
|
-
embedded? ? (embedded_many? ? "$push" : "$set") : nil
|
18
|
-
end
|
19
9
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
alias :_pull :_path
|
10
|
+
# Get the insertion modifier for the document. Will be nil on root
|
11
|
+
# documents, $set on embeds_one, $push on embeds_many.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# <tt>name.inserter</tt>
|
16
|
+
def _inserter
|
17
|
+
embedded? ? (embedded_many? ? "$push" : "$set") : nil
|
18
|
+
end
|
30
19
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
20
|
+
# Return the path to this +Document+ in JSON notation, used for atomic
|
21
|
+
# updates via $set in MongoDB.
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
#
|
25
|
+
# <tt>address.path # returns "addresses"</tt>
|
26
|
+
def _path
|
27
|
+
_position.sub!(/\.\d+$/, '') || _position
|
28
|
+
end
|
29
|
+
alias :_pull :_path
|
40
30
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
31
|
+
# Returns the positional operator of this document for modification.
|
32
|
+
#
|
33
|
+
# Example:
|
34
|
+
#
|
35
|
+
# <tt>address.position</tt>
|
36
|
+
def _position
|
37
|
+
locator = _index ? (new_record? ? "" : ".#{_index}") : ""
|
38
|
+
embedded? ? "#{_parent._position}#{"." unless _parent._position.blank?}#{@association_name}#{locator}" : ""
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get the removal modifier for the document. Will be nil on root
|
42
|
+
# documents, $unset on embeds_one, $set on embeds_many.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
#
|
46
|
+
# <tt>name.remover</tt>
|
47
|
+
def _remover
|
48
|
+
embedded? ? (_index ? "$pull" : "$unset") : nil
|
49
|
+
end
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
51
|
+
# Return the selector for this document to be matched exactly for use
|
52
|
+
# with MongoDB's $ operator.
|
53
|
+
#
|
54
|
+
# Example:
|
55
|
+
#
|
56
|
+
# <tt>address.selector</tt>
|
57
|
+
def _selector
|
58
|
+
embedded? ? _parent._selector.merge("#{_path}._id" => id) : { "_id" => id }
|
60
59
|
end
|
61
60
|
end
|
62
61
|
end
|
data/lib/mongoid/persistence.rb
CHANGED
@@ -19,129 +19,127 @@ module Mongoid #:nodoc:
|
|
19
19
|
# <tt>document.upsert</tt>
|
20
20
|
module Persistence
|
21
21
|
extend ActiveSupport::Concern
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
# Insert a new +Document+ into the database. Will return the document
|
35
|
-
# itself whether or not the save was successful.
|
36
|
-
#
|
37
|
-
# Example:
|
38
|
-
#
|
39
|
-
# <tt>document.insert</tt>
|
40
|
-
def insert(validate = true)
|
41
|
-
Insert.new(self, validate).persist
|
42
|
-
end
|
22
|
+
# Remove the +Document+ from the datbase with callbacks.
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
#
|
26
|
+
# <tt>document.destroy</tt>
|
27
|
+
#
|
28
|
+
# TODO: Will get rid of other #destroy once new persistence complete.
|
29
|
+
def destroy
|
30
|
+
run_callbacks(:destroy) { self.destroyed = true if _remove }
|
31
|
+
end
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
33
|
+
# Insert a new +Document+ into the database. Will return the document
|
34
|
+
# itself whether or not the save was successful.
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# <tt>document.insert</tt>
|
39
|
+
def insert(validate = true)
|
40
|
+
Insert.new(self, validate).persist
|
41
|
+
end
|
54
42
|
|
55
|
-
|
43
|
+
# Remove the +Document+ from the datbase.
|
44
|
+
#
|
45
|
+
# Example:
|
46
|
+
#
|
47
|
+
# <tt>document._remove</tt>
|
48
|
+
#
|
49
|
+
# TODO: Will get rid of other #remove once observable pattern killed.
|
50
|
+
def _remove
|
51
|
+
Remove.new(self).persist
|
52
|
+
end
|
56
53
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
54
|
+
alias :delete :_remove
|
55
|
+
|
56
|
+
# Save the document - will perform an insert if the document is new, and
|
57
|
+
# update if not. If a validation error occurs a
|
58
|
+
# Mongoid::Errors::Validations error will get raised.
|
59
|
+
#
|
60
|
+
# Example:
|
61
|
+
#
|
62
|
+
# <tt>document.save!</tt>
|
63
|
+
#
|
64
|
+
# Returns:
|
65
|
+
#
|
66
|
+
# +true+ if validation passed, will raise error otherwise.
|
67
|
+
def save!
|
68
|
+
self.class.fail_validate!(self) unless upsert; true
|
69
|
+
end
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
71
|
+
# Update the +Document+ in the datbase.
|
72
|
+
#
|
73
|
+
# Example:
|
74
|
+
#
|
75
|
+
# <tt>document.update</tt>
|
76
|
+
def update(validate = true)
|
77
|
+
Update.new(self, validate).persist
|
78
|
+
end
|
80
79
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
80
|
+
# Update the +Document+ attributes in the datbase.
|
81
|
+
#
|
82
|
+
# Example:
|
83
|
+
#
|
84
|
+
# <tt>document.update_attributes(:title => "Sir")</tt>
|
85
|
+
#
|
86
|
+
# Returns:
|
87
|
+
#
|
88
|
+
# +true+ if validation passed, +false+ if not.
|
89
|
+
def update_attributes(attributes = {})
|
90
|
+
write_attributes(attributes); update
|
91
|
+
end
|
93
92
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
93
|
+
# Update the +Document+ attributes in the datbase.
|
94
|
+
#
|
95
|
+
# Example:
|
96
|
+
#
|
97
|
+
# <tt>document.update_attributes(:title => "Sir")</tt>
|
98
|
+
#
|
99
|
+
# Returns:
|
100
|
+
#
|
101
|
+
# +true+ if validation passed, raises an error if not
|
102
|
+
def update_attributes!(attributes = {})
|
103
|
+
write_attributes(attributes)
|
104
|
+
result = update
|
105
|
+
self.class.fail_validate!(self) unless result
|
106
|
+
result
|
107
|
+
end
|
109
108
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
109
|
+
# Upsert the document - will perform an insert if the document is new, and
|
110
|
+
# update if not.
|
111
|
+
#
|
112
|
+
# Example:
|
113
|
+
#
|
114
|
+
# <tt>document.upsert</tt>
|
115
|
+
#
|
116
|
+
# Returns:
|
117
|
+
#
|
118
|
+
# A +Boolean+ for updates.
|
119
|
+
def upsert(validate = true)
|
120
|
+
validate = parse_validate(validate)
|
121
|
+
if new_record?
|
122
|
+
insert(validate).persisted?
|
123
|
+
else
|
124
|
+
update(validate)
|
127
125
|
end
|
126
|
+
end
|
128
127
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
end
|
143
|
-
validate
|
128
|
+
# Save is aliased so that users familiar with active record can have some
|
129
|
+
# semblance of a familiar API.
|
130
|
+
#
|
131
|
+
# Example:
|
132
|
+
#
|
133
|
+
# <tt>document.save</tt>
|
134
|
+
alias :save :upsert
|
135
|
+
|
136
|
+
protected
|
137
|
+
# Alternative validation params.
|
138
|
+
def parse_validate(validate)
|
139
|
+
if validate.is_a?(Hash) && validate.has_key?(:validate)
|
140
|
+
validate = validate[:validate]
|
144
141
|
end
|
142
|
+
validate
|
145
143
|
end
|
146
144
|
|
147
145
|
module ClassMethods #:nodoc:
|