mongoid-with-auth 1.9.4

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 (104) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +49 -0
  3. data/lib/mongoid.rb +122 -0
  4. data/lib/mongoid/associations.rb +300 -0
  5. data/lib/mongoid/associations/belongs_to_related.rb +58 -0
  6. data/lib/mongoid/associations/embedded_in.rb +72 -0
  7. data/lib/mongoid/associations/embeds_many.rb +254 -0
  8. data/lib/mongoid/associations/embeds_one.rb +96 -0
  9. data/lib/mongoid/associations/has_many_related.rb +181 -0
  10. data/lib/mongoid/associations/has_one_related.rb +85 -0
  11. data/lib/mongoid/associations/meta_data.rb +29 -0
  12. data/lib/mongoid/associations/options.rb +57 -0
  13. data/lib/mongoid/associations/proxy.rb +24 -0
  14. data/lib/mongoid/attributes.rb +204 -0
  15. data/lib/mongoid/callbacks.rb +23 -0
  16. data/lib/mongoid/collection.rb +120 -0
  17. data/lib/mongoid/collections.rb +41 -0
  18. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  19. data/lib/mongoid/collections/master.rb +29 -0
  20. data/lib/mongoid/collections/operations.rb +41 -0
  21. data/lib/mongoid/collections/slaves.rb +45 -0
  22. data/lib/mongoid/components.rb +27 -0
  23. data/lib/mongoid/concern.rb +31 -0
  24. data/lib/mongoid/config.rb +205 -0
  25. data/lib/mongoid/contexts.rb +25 -0
  26. data/lib/mongoid/contexts/enumerable.rb +151 -0
  27. data/lib/mongoid/contexts/ids.rb +25 -0
  28. data/lib/mongoid/contexts/mongo.rb +285 -0
  29. data/lib/mongoid/contexts/paging.rb +50 -0
  30. data/lib/mongoid/criteria.rb +239 -0
  31. data/lib/mongoid/criterion/complex.rb +21 -0
  32. data/lib/mongoid/criterion/exclusion.rb +65 -0
  33. data/lib/mongoid/criterion/inclusion.rb +110 -0
  34. data/lib/mongoid/criterion/optional.rb +136 -0
  35. data/lib/mongoid/cursor.rb +81 -0
  36. data/lib/mongoid/deprecation.rb +22 -0
  37. data/lib/mongoid/dirty.rb +253 -0
  38. data/lib/mongoid/document.rb +311 -0
  39. data/lib/mongoid/errors.rb +108 -0
  40. data/lib/mongoid/extensions.rb +101 -0
  41. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  42. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  43. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  44. data/lib/mongoid/extensions/array/conversions.rb +29 -0
  45. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  46. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  47. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  48. data/lib/mongoid/extensions/boolean/conversions.rb +22 -0
  49. data/lib/mongoid/extensions/date/conversions.rb +24 -0
  50. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  51. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  52. data/lib/mongoid/extensions/hash/accessors.rb +38 -0
  53. data/lib/mongoid/extensions/hash/assimilation.rb +39 -0
  54. data/lib/mongoid/extensions/hash/conversions.rb +45 -0
  55. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  56. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  57. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  58. data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
  59. data/lib/mongoid/extensions/object/conversions.rb +33 -0
  60. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  61. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  62. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  63. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  64. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  65. data/lib/mongoid/extensions/time_conversions.rb +35 -0
  66. data/lib/mongoid/extras.rb +61 -0
  67. data/lib/mongoid/factory.rb +20 -0
  68. data/lib/mongoid/field.rb +59 -0
  69. data/lib/mongoid/fields.rb +65 -0
  70. data/lib/mongoid/finders.rb +136 -0
  71. data/lib/mongoid/identity.rb +39 -0
  72. data/lib/mongoid/indexes.rb +30 -0
  73. data/lib/mongoid/javascript.rb +21 -0
  74. data/lib/mongoid/javascript/functions.yml +37 -0
  75. data/lib/mongoid/matchers.rb +36 -0
  76. data/lib/mongoid/matchers/all.rb +11 -0
  77. data/lib/mongoid/matchers/default.rb +26 -0
  78. data/lib/mongoid/matchers/exists.rb +13 -0
  79. data/lib/mongoid/matchers/gt.rb +11 -0
  80. data/lib/mongoid/matchers/gte.rb +11 -0
  81. data/lib/mongoid/matchers/in.rb +11 -0
  82. data/lib/mongoid/matchers/lt.rb +11 -0
  83. data/lib/mongoid/matchers/lte.rb +11 -0
  84. data/lib/mongoid/matchers/ne.rb +11 -0
  85. data/lib/mongoid/matchers/nin.rb +11 -0
  86. data/lib/mongoid/matchers/size.rb +11 -0
  87. data/lib/mongoid/memoization.rb +33 -0
  88. data/lib/mongoid/named_scope.rb +37 -0
  89. data/lib/mongoid/observable.rb +30 -0
  90. data/lib/mongoid/paths.rb +62 -0
  91. data/lib/mongoid/persistence.rb +222 -0
  92. data/lib/mongoid/persistence/command.rb +39 -0
  93. data/lib/mongoid/persistence/insert.rb +50 -0
  94. data/lib/mongoid/persistence/insert_embedded.rb +38 -0
  95. data/lib/mongoid/persistence/remove.rb +39 -0
  96. data/lib/mongoid/persistence/remove_all.rb +37 -0
  97. data/lib/mongoid/persistence/remove_embedded.rb +50 -0
  98. data/lib/mongoid/persistence/update.rb +63 -0
  99. data/lib/mongoid/scope.rb +75 -0
  100. data/lib/mongoid/state.rb +39 -0
  101. data/lib/mongoid/timestamps.rb +27 -0
  102. data/lib/mongoid/version.rb +4 -0
  103. data/lib/mongoid/versioning.rb +27 -0
  104. metadata +284 -0
@@ -0,0 +1,181 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # Represents an relational one-to-many association with an object in a
5
+ # separate collection or database.
6
+ class HasManyRelated < Proxy
7
+
8
+ # Appends the object to the +Array+, setting its parent in
9
+ # the process.
10
+ def <<(*objects)
11
+ load_target
12
+ objects.flatten.each do |object|
13
+ object.send("#{@foreign_key}=", @parent.id)
14
+ @target << object
15
+ object.save unless @parent.new_record?
16
+ end
17
+ end
18
+
19
+ # Builds a new Document and adds it to the association collection. The
20
+ # document created will be of the same class as the others in the
21
+ # association, and the attributes will be passed into the constructor.
22
+ #
23
+ # Returns the newly created object.
24
+ def build(attributes = {})
25
+ load_target
26
+ name = @parent.class.to_s.underscore
27
+ object = @klass.instantiate(attributes.merge(name => @parent))
28
+ @target << object
29
+ object
30
+ end
31
+
32
+ # Delegates to <<
33
+ def concat(*objects)
34
+ self << objects
35
+ end
36
+
37
+ # Creates a new Document and adds it to the association collection. The
38
+ # document created will be of the same class as the others in the
39
+ # association, and the attributes will be passed into the constructor and
40
+ # the new object will then be saved.
41
+ #
42
+ # Returns the newly created object.
43
+ def create(attributes)
44
+ object = build(attributes)
45
+ object.save; object
46
+ end
47
+
48
+ # Creates a new Document and adds it to the association collection. If
49
+ # validation fails an error is raised.
50
+ #
51
+ # Returns the newly created object.
52
+ def create!(attributes)
53
+ object = build(attributes)
54
+ object.save!; object
55
+ end
56
+
57
+ # Delete all the associated objects.
58
+ #
59
+ # Example:
60
+ #
61
+ # <tt>person.posts.delete_all</tt>
62
+ #
63
+ # Returns:
64
+ #
65
+ # The number of objects deleted.
66
+ def delete_all(conditions = {})
67
+ remove(:delete_all, conditions[:conditions])
68
+ end
69
+
70
+ # Destroy all the associated objects.
71
+ #
72
+ # Example:
73
+ #
74
+ # <tt>person.posts.destroy_all</tt>
75
+ #
76
+ # Returns:
77
+ #
78
+ # The number of objects destroyed.
79
+ def destroy_all(conditions = {})
80
+ remove(:destroy_all, conditions[:conditions])
81
+ end
82
+
83
+ # Finds a document in this association.
84
+ # If an id is passed, will return the document for that id.
85
+ def find(*args)
86
+ args[1][:conditions].merge!(@foreign_key.to_sym => @parent.id) if args.size > 1
87
+ @klass.find(*args)
88
+ end
89
+
90
+ # Initializing a related association only requires looking up the objects
91
+ # by their ids.
92
+ #
93
+ # Options:
94
+ #
95
+ # document: The +Document+ that contains the relationship.
96
+ # options: The association +Options+.
97
+ def initialize(document, options, target = nil)
98
+ @parent, @klass, @options = document, options.klass, options
99
+ @foreign_key = options.foreign_key
100
+ @base = lambda { @klass.all(:conditions => { @foreign_key => document.id }) }
101
+ @target = target || @base.call
102
+ extends(options)
103
+ end
104
+
105
+ # Override the default behavior to allow the criteria to get reset on
106
+ # each call into the association.
107
+ #
108
+ # Example:
109
+ #
110
+ # person.posts.where(:title => "New")
111
+ # person.posts # resets the criteria
112
+ #
113
+ # Returns:
114
+ #
115
+ # A Criteria object or Array.
116
+ def method_missing(name, *args, &block)
117
+ @target = @base.call unless @target.is_a?(Array)
118
+ @target.send(name, *args, &block)
119
+ end
120
+
121
+ # Delegates to <<
122
+ def push(*objects)
123
+ self << objects
124
+ end
125
+
126
+ protected
127
+ # Load the target entries if the document is new.
128
+ def load_target
129
+ @target = @target.entries if @parent.new_record?
130
+ end
131
+
132
+ # Remove the objects based on conditions.
133
+ def remove(method, conditions)
134
+ selector = { @foreign_key => @parent.id }.merge(conditions || {})
135
+ removed = @klass.send(method, :conditions => selector)
136
+ reset; removed
137
+ end
138
+
139
+ # Reset the memoized association on the parent.
140
+ def reset
141
+ @parent.send(:reset, @options.name) { @base.call }
142
+ end
143
+
144
+ class << self
145
+ # Preferred method for creating the new +HasManyRelated+ association.
146
+ #
147
+ # Options:
148
+ #
149
+ # document: The +Document+ that contains the relationship.
150
+ # options: The association +Options+.
151
+ def instantiate(document, options, target = nil)
152
+ new(document, options, target)
153
+ end
154
+
155
+ # Returns the macro used to create the association.
156
+ def macro
157
+ :has_many_related
158
+ end
159
+
160
+ # Perform an update of the relationship of the parent and child. This
161
+ # will assimilate the child +Document+ into the parent's object graph.
162
+ #
163
+ # Options:
164
+ #
165
+ # related: The related object
166
+ # parent: The parent +Document+ to update.
167
+ # options: The association +Options+
168
+ #
169
+ # Example:
170
+ #
171
+ # <tt>RelatesToOne.update(game, person, options)</tt>
172
+ def update(target, document, options)
173
+ name = document.class.to_s.underscore
174
+ target.each { |child| child.send("#{name}=", document) }
175
+ instantiate(document, options, target)
176
+ end
177
+ end
178
+
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # Represents an relational one-to-one association with an object in a
5
+ # separate collection or database.
6
+ class HasOneRelated < Proxy
7
+
8
+ delegate :nil?, :to => :target
9
+
10
+ # Builds a new Document and sets it as the association.
11
+ #
12
+ # Returns the newly created object.
13
+ def build(attributes = {})
14
+ @target = @klass.instantiate(attributes)
15
+ inverse = @target.associations.values.detect do |metadata|
16
+ metadata.options.klass == @parent.class
17
+ end
18
+ name = inverse.name
19
+ @target.send("#{name}=", @parent)
20
+ @target
21
+ end
22
+
23
+ # Builds a new Document and sets it as the association, then saves the
24
+ # newly created document.
25
+ #
26
+ # Returns the newly created object.
27
+ def create(attributes)
28
+ build(attributes); @target.save; @target
29
+ end
30
+
31
+ # Initializing a related association only requires looking up the objects
32
+ # by their ids.
33
+ #
34
+ # Options:
35
+ #
36
+ # document: The +Document+ that contains the relationship.
37
+ # options: The association +Options+.
38
+ def initialize(document, options, target = nil)
39
+ @parent, @klass = document, options.klass
40
+ @foreign_key = options.foreign_key
41
+ @target = target || @klass.first(:conditions => { @foreign_key => @parent.id })
42
+ extends(options)
43
+ end
44
+
45
+ class << self
46
+ # Preferred method for creating the new +RelatesToMany+ association.
47
+ #
48
+ # Options:
49
+ #
50
+ # document: The +Document+ that contains the relationship.
51
+ # options: The association +Options+.
52
+ def instantiate(document, options, target = nil)
53
+ new(document, options, target)
54
+ end
55
+
56
+ # Returns the macro used to create the association.
57
+ def macro
58
+ :has_one_related
59
+ end
60
+
61
+ # Perform an update of the relationship of the parent and child. This
62
+ # will assimilate the child +Document+ into the parent's object graph.
63
+ #
64
+ # Options:
65
+ #
66
+ # related: The related object to update.
67
+ # document: The parent +Document+.
68
+ # options: The association +Options+
69
+ #
70
+ # Example:
71
+ #
72
+ # <tt>HasOneToRelated.update(game, person, options)</tt>
73
+ def update(target, document, options)
74
+ if target
75
+ name = document.class.to_s.underscore
76
+ target.send("#{name}=", document)
77
+ return instantiate(document, options, target)
78
+ end
79
+ target
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # This class contains metadata about association proxies.
5
+ class MetaData
6
+
7
+ attr_reader :association, :options
8
+
9
+ delegate :macro, :to => :association
10
+
11
+ # Delegate all methods on +Options+ to the options instance.
12
+ Associations::Options.public_instance_methods(false).each do |name|
13
+ define_method(name) { |*args| @options.send(name) }
14
+ end
15
+
16
+ # Create the new associations MetaData object, which holds the type of
17
+ # the association and its options, with convenience methods for getting
18
+ # that information.
19
+ #
20
+ # Options:
21
+ #
22
+ # association: The association type as a class instance.
23
+ # options: The association options
24
+ def initialize(association, options)
25
+ @association, @options = association, options
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class Options #:nodoc:
5
+
6
+ # Create the new +Options+ object, which provides convenience methods for
7
+ # accessing values out of an options +Hash+.
8
+ def initialize(attributes = {})
9
+ @attributes = attributes
10
+ end
11
+
12
+ # Returns the extension if it exists, nil if not.
13
+ def extension
14
+ @attributes[:extend]
15
+ end
16
+
17
+ # Returns true is the options have extensions.
18
+ def extension?
19
+ !extension.nil?
20
+ end
21
+
22
+ # Return the foreign key based off the association name.
23
+ def foreign_key
24
+ key = @attributes[:foreign_key] || klass.name.to_s.foreign_key
25
+ key.to_s
26
+ end
27
+
28
+ # Returns the name of the inverse_of association
29
+ def inverse_of
30
+ @attributes[:inverse_of]
31
+ end
32
+
33
+ # Return a +Class+ for the options. If a class_name was provided, then the
34
+ # constantized class_name will be returned. If not, a constant based on the
35
+ # association name will be returned.
36
+ def klass
37
+ class_name = @attributes[:class_name]
38
+ class_name ? class_name.constantize : name.to_s.classify.constantize
39
+ end
40
+
41
+ # Returns the association name of the options.
42
+ def name
43
+ @attributes[:name].to_s
44
+ end
45
+
46
+ # Returns whether or not this association is polymorphic.
47
+ def polymorphic
48
+ @attributes[:polymorphic] == true
49
+ end
50
+
51
+ # Used with has_many_related to save as array of ids.
52
+ def stored_as
53
+ @attributes[:stored_as]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Associations #:nodoc
4
+ class Proxy #:nodoc
5
+ instance_methods.each do |method|
6
+ undef_method(method) unless method =~ /(^__|^nil\?$|^send$|^object_id$|^extend$)/
7
+ end
8
+ attr_reader \
9
+ :options,
10
+ :target
11
+
12
+ # Default behavior of method missing should be to delegate all calls
13
+ # to the target of the proxy. This can be overridden in special cases.
14
+ def method_missing(name, *args, &block)
15
+ @target.send(name, *args, &block)
16
+ end
17
+
18
+ # If anonymous extensions are added this will take care of them.
19
+ def extends(options)
20
+ extend Module.new(&options.extension) if options.extension?
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,204 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Attributes
4
+ def self.included(base)
5
+ base.class_eval do
6
+ include InstanceMethods
7
+ extend ClassMethods
8
+ end
9
+ end
10
+ module InstanceMethods
11
+ # Get the id associated with this object. This will pull the _id value out
12
+ # of the attributes +Hash+.
13
+ def id
14
+ @attributes["_id"]
15
+ end
16
+
17
+ # Set the id of the +Document+ to a new one.
18
+ def id=(new_id)
19
+ @attributes["_id"] = new_id
20
+ end
21
+
22
+ alias :_id :id
23
+ alias :_id= :id=
24
+
25
+ # Used for allowing accessor methods for dynamic attributes.
26
+ def method_missing(name, *args)
27
+ attr = name.to_s
28
+ return super unless @attributes.has_key?(attr.reader)
29
+ if attr.writer?
30
+ # "args.size > 1" allows to simulate 1.8 behavior of "*args"
31
+ @attributes[attr.reader] = (args.size > 1) ? args : args.first
32
+ else
33
+ @attributes[attr.reader]
34
+ end
35
+ end
36
+
37
+ # Process the provided attributes casting them to their proper values if a
38
+ # field exists for them on the +Document+. This will be limited to only the
39
+ # attributes provided in the suppied +Hash+ so that no extra nil values get
40
+ # put into the document's attributes.
41
+ def process(attrs = nil)
42
+ (attrs || {}).each_pair do |key, value|
43
+ if set_allowed?(key)
44
+ @attributes[key.to_s] = value
45
+ elsif write_allowed?(key)
46
+ send("#{key}=", value)
47
+ end
48
+ end
49
+ setup_modifications
50
+ end
51
+
52
+ # Read a value from the +Document+ attributes. If the value does not exist
53
+ # it will return nil.
54
+ #
55
+ # Options:
56
+ #
57
+ # name: The name of the attribute to get.
58
+ #
59
+ # Example:
60
+ #
61
+ # <tt>person.read_attribute(:title)</tt>
62
+ def read_attribute(name)
63
+ access = name.to_s
64
+ accessed(access, fields[access].get(@attributes[access]))
65
+ end
66
+
67
+ # Remove a value from the +Document+ attributes. If the value does not exist
68
+ # it will fail gracefully.
69
+ #
70
+ # Options:
71
+ #
72
+ # name: The name of the attribute to remove.
73
+ #
74
+ # Example:
75
+ #
76
+ # <tt>person.remove_attribute(:title)</tt>
77
+ def remove_attribute(name)
78
+ access = name.to_s
79
+ modify(access, @attributes.delete(access), nil)
80
+ end
81
+
82
+ # Returns the object type. This corresponds to the name of the class that
83
+ # this +Document+ is, which is used in determining the class to
84
+ # instantiate in various cases.
85
+ def _type
86
+ @attributes["_type"]
87
+ end
88
+
89
+ # Set the type of the +Document+. This should be the name of the class.
90
+ def _type=(new_type)
91
+ @attributes["_type"] = new_type
92
+ end
93
+
94
+ # Write a single attribute to the +Document+ attribute +Hash+. This will
95
+ # also fire the before and after update callbacks, and perform any
96
+ # necessary typecasting.
97
+ #
98
+ # Options:
99
+ #
100
+ # name: The name of the attribute to update.
101
+ # value: The value to set for the attribute.
102
+ #
103
+ # Example:
104
+ #
105
+ # <tt>person.write_attribute(:title, "Mr.")</tt>
106
+ #
107
+ # This will also cause the observing +Document+ to notify it's parent if
108
+ # there is any.
109
+ def write_attribute(name, value)
110
+ access = name.to_s
111
+ modify(access, @attributes[access], fields[access].set(value))
112
+ notify if !id.blank? && new_record?
113
+ end
114
+
115
+ # Writes the supplied attributes +Hash+ to the +Document+. This will only
116
+ # overwrite existing attributes if they are present in the new +Hash+, all
117
+ # others will be preserved.
118
+ #
119
+ # Options:
120
+ #
121
+ # attrs: The +Hash+ of new attributes to set on the +Document+
122
+ #
123
+ # Example:
124
+ #
125
+ # <tt>person.write_attributes(:title => "Mr.")</tt>
126
+ #
127
+ # This will also cause the observing +Document+ to notify it's parent if
128
+ # there is any.
129
+ def write_attributes(attrs = nil)
130
+ process(attrs || {})
131
+ identified = !id.blank?
132
+ if new_record? && !identified
133
+ identify; notify
134
+ end
135
+ end
136
+ alias :attributes= write_attributes
137
+
138
+ protected
139
+ # apply default values to attributes - calling procs as required
140
+ def default_attributes
141
+ default_values = defaults
142
+ default_values.each_pair do |key, val|
143
+ default_values[key] = val.call if val.respond_to?(:call)
144
+ end
145
+ default_values || {}
146
+ end
147
+
148
+ # Return true if dynamic field setting is enabled.
149
+ def set_allowed?(key)
150
+ Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
151
+ end
152
+
153
+ # Used when supplying a :reject_if block as an option to
154
+ # accepts_nested_attributes_for
155
+ def reject(attributes, options)
156
+ rejector = options[:reject_if]
157
+ if rejector
158
+ attributes.delete_if do |key, value|
159
+ rejector.call(value)
160
+ end
161
+ end
162
+ end
163
+
164
+ # Return true if writing to the given field is allowed
165
+ def write_allowed?(key)
166
+ name = key.to_s
167
+ existing = fields[name]
168
+ return true unless existing
169
+ existing.accessible?
170
+ end
171
+ end
172
+
173
+ module ClassMethods
174
+ # Defines attribute setters for the associations specified by the names.
175
+ # This will work for a has one or has many association.
176
+ #
177
+ # Example:
178
+ #
179
+ # class Person
180
+ # include Mongoid::Document
181
+ # embeds_one :name
182
+ # embeds_many :addresses
183
+ #
184
+ # accepts_nested_attributes_for :name, :addresses
185
+ # end
186
+ def accepts_nested_attributes_for(*args)
187
+ associations = args.flatten
188
+ options = associations.last.is_a?(Hash) ? associations.pop : {}
189
+ associations.each do |name|
190
+ define_method("#{name}_attributes=") do |attrs|
191
+ reject(attrs, options)
192
+ association = send(name)
193
+ if association
194
+ observe(association, true)
195
+ association.nested_build(attrs)
196
+ else
197
+ send("build_#{name}", attrs)
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end