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.
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: