mongoid 2.0.0.beta.5 → 2.0.0.beta.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 (43) hide show
  1. data/lib/mongoid.rb +10 -2
  2. data/lib/mongoid/associations.rb +82 -58
  3. data/lib/mongoid/associations/embeds_one.rb +6 -6
  4. data/lib/mongoid/associations/foreign_key.rb +35 -0
  5. data/lib/mongoid/associations/meta_data.rb +9 -0
  6. data/lib/mongoid/associations/options.rb +1 -1
  7. data/lib/mongoid/associations/proxy.rb +9 -0
  8. data/lib/mongoid/associations/{belongs_to_related.rb → referenced_in.rb} +6 -5
  9. data/lib/mongoid/associations/{has_many_related.rb → references_many.rb} +69 -26
  10. data/lib/mongoid/associations/references_many_as_array.rb +78 -0
  11. data/lib/mongoid/associations/{has_one_related.rb → references_one.rb} +16 -2
  12. data/lib/mongoid/atomicity.rb +42 -0
  13. data/lib/mongoid/attributes.rb +148 -146
  14. data/lib/mongoid/callbacks.rb +5 -1
  15. data/lib/mongoid/collections.rb +31 -1
  16. data/lib/mongoid/components.rb +4 -1
  17. data/lib/mongoid/config.rb +2 -1
  18. data/lib/mongoid/criteria.rb +9 -0
  19. data/lib/mongoid/dirty.rb +211 -212
  20. data/lib/mongoid/document.rb +126 -185
  21. data/lib/mongoid/extensions.rb +5 -0
  22. data/lib/mongoid/extensions/array/conversions.rb +3 -5
  23. data/lib/mongoid/extensions/hash/conversions.rb +19 -22
  24. data/lib/mongoid/extensions/object/conversions.rb +3 -5
  25. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  26. data/lib/mongoid/field.rb +11 -0
  27. data/lib/mongoid/finders.rb +8 -0
  28. data/lib/mongoid/hierarchy.rb +76 -0
  29. data/lib/mongoid/identity.rb +37 -29
  30. data/lib/mongoid/paths.rb +46 -47
  31. data/lib/mongoid/persistence.rb +111 -113
  32. data/lib/mongoid/persistence/insert.rb +1 -1
  33. data/lib/mongoid/persistence/insert_embedded.rb +10 -5
  34. data/lib/mongoid/persistence/remove_all.rb +3 -2
  35. data/lib/mongoid/persistence/update.rb +8 -3
  36. data/lib/mongoid/railtie.rb +3 -0
  37. data/lib/mongoid/railties/database.rake +33 -18
  38. data/lib/mongoid/timestamps.rb +9 -12
  39. data/lib/mongoid/validations/uniqueness.rb +16 -4
  40. data/lib/mongoid/version.rb +1 -1
  41. data/lib/mongoid/versioning.rb +10 -11
  42. data/lib/rails/generators/mongoid/config/config_generator.rb +0 -16
  43. 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
@@ -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
@@ -1,39 +1,47 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  class Identity #:nodoc:
4
- class << self
5
- # Create the identity for the +Document+.
6
- #
7
- # The id will be set in either in the form of a Mongo
8
- # +ObjectID+ or a composite key set up by defining a key on the document.
9
- #
10
- # The _type will be set to the document's class name.
11
- def create(doc)
12
- identify(doc); type(doc); doc
13
- end
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
- protected
16
- # Return the proper id for the document.
17
- def generate_id
18
- id = BSON::ObjectID.new
19
- Mongoid.use_object_ids ? id : id.to_s
20
- end
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
- # Set the id for the document.
23
- def identify(doc)
24
- doc.id = compose(doc).join(" ").identify if doc.primary_key
25
- doc.id = generate_id if doc.id.blank?
26
- end
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
- # Set the _type field on the document.
29
- def type(doc)
30
- doc._type = doc.class.name if doc.hereditary?
31
- end
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
- # Generates the composite key for a document.
34
- def compose(doc)
35
- doc.primary_key.collect { |key| doc.attributes[key] }.reject { |val| val.nil? }
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
- # 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
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
- # 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
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
- # 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
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
- # 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 }
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
@@ -19,129 +19,127 @@ module Mongoid #:nodoc:
19
19
  # <tt>document.upsert</tt>
20
20
  module Persistence
21
21
  extend ActiveSupport::Concern
22
- module InstanceMethods #:nodoc:
23
- # Remove the +Document+ from the datbase with callbacks.
24
- #
25
- # Example:
26
- #
27
- # <tt>document.destroy</tt>
28
- #
29
- # TODO: Will get rid of other #destroy once new persistence complete.
30
- def destroy
31
- run_callbacks(:destroy) { self.destroyed = true if _remove }
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
- # Remove the +Document+ from the datbase.
45
- #
46
- # Example:
47
- #
48
- # <tt>document._remove</tt>
49
- #
50
- # TODO: Will get rid of other #remove once observable pattern killed.
51
- def _remove
52
- Remove.new(self).persist
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
- alias :delete :_remove
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
- # Save the document - will perform an insert if the document is new, and
58
- # update if not. If a validation error occurs a
59
- # Mongoid::Errors::Validations error will get raised.
60
- #
61
- # Example:
62
- #
63
- # <tt>document.save!</tt>
64
- #
65
- # Returns:
66
- #
67
- # +true+ if validation passed, will raise error otherwise.
68
- def save!
69
- self.class.fail_validate!(self) unless upsert; true
70
- end
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
- # Update the +Document+ in the datbase.
73
- #
74
- # Example:
75
- #
76
- # <tt>document.update</tt>
77
- def update(validate = true)
78
- Update.new(self, validate).persist
79
- end
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
- # Update the +Document+ attributes in the datbase.
82
- #
83
- # Example:
84
- #
85
- # <tt>document.update_attributes(:title => "Sir")</tt>
86
- #
87
- # Returns:
88
- #
89
- # +true+ if validation passed, +false+ if not.
90
- def update_attributes(attributes = {})
91
- write_attributes(attributes); update
92
- end
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
- # Update the +Document+ attributes in the datbase.
95
- #
96
- # Example:
97
- #
98
- # <tt>document.update_attributes(:title => "Sir")</tt>
99
- #
100
- # Returns:
101
- #
102
- # +true+ if validation passed, raises an error if not
103
- def update_attributes!(attributes = {})
104
- write_attributes(attributes)
105
- result = update
106
- self.class.fail_validate!(self) unless result
107
- result
108
- end
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
- # Upsert the document - will perform an insert if the document is new, and
111
- # update if not.
112
- #
113
- # Example:
114
- #
115
- # <tt>document.upsert</tt>
116
- #
117
- # Returns:
118
- #
119
- # A +Boolean+ for updates.
120
- def upsert(validate = true)
121
- validate = parse_validate(validate)
122
- if new_record?
123
- insert(validate).persisted?
124
- else
125
- update(validate)
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
- # Save is aliased so that users familiar with active record can have some
130
- # semblance of a familiar API.
131
- #
132
- # Example:
133
- #
134
- # <tt>document.save</tt>
135
- alias :save :upsert
136
-
137
- protected
138
- # Alternative validation params.
139
- def parse_validate(validate)
140
- if validate.is_a?(Hash) && validate.has_key?(:validate)
141
- validate = validate[:validate]
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: