mongoid-locomotive 2.0.0.beta9
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/MIT_LICENSE +20 -0
- data/README.rdoc +47 -0
- data/lib/mongoid.rb +141 -0
- data/lib/mongoid/associations.rb +306 -0
- data/lib/mongoid/associations/embedded_in.rb +74 -0
- data/lib/mongoid/associations/embeds_many.rb +280 -0
- data/lib/mongoid/associations/embeds_one.rb +97 -0
- data/lib/mongoid/associations/foreign_key.rb +35 -0
- data/lib/mongoid/associations/meta_data.rb +38 -0
- data/lib/mongoid/associations/options.rb +62 -0
- data/lib/mongoid/associations/proxy.rb +33 -0
- data/lib/mongoid/associations/referenced_in.rb +59 -0
- data/lib/mongoid/associations/references_many.rb +245 -0
- data/lib/mongoid/associations/references_many_as_array.rb +78 -0
- data/lib/mongoid/associations/references_one.rb +99 -0
- data/lib/mongoid/atomicity.rb +55 -0
- data/lib/mongoid/attributes.rb +242 -0
- data/lib/mongoid/callbacks.rb +21 -0
- data/lib/mongoid/collection.rb +120 -0
- data/lib/mongoid/collections.rb +71 -0
- data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
- data/lib/mongoid/collections/master.rb +29 -0
- data/lib/mongoid/collections/operations.rb +41 -0
- data/lib/mongoid/collections/slaves.rb +45 -0
- data/lib/mongoid/components.rb +34 -0
- data/lib/mongoid/config.rb +263 -0
- data/lib/mongoid/contexts.rb +24 -0
- data/lib/mongoid/contexts/enumerable.rb +156 -0
- data/lib/mongoid/contexts/ids.rb +25 -0
- data/lib/mongoid/contexts/mongo.rb +285 -0
- data/lib/mongoid/contexts/paging.rb +50 -0
- data/lib/mongoid/criteria.rb +248 -0
- data/lib/mongoid/criterion/complex.rb +21 -0
- data/lib/mongoid/criterion/exclusion.rb +65 -0
- data/lib/mongoid/criterion/inclusion.rb +110 -0
- data/lib/mongoid/criterion/optional.rb +189 -0
- data/lib/mongoid/cursor.rb +81 -0
- data/lib/mongoid/deprecation.rb +21 -0
- data/lib/mongoid/dirty.rb +252 -0
- data/lib/mongoid/document.rb +210 -0
- data/lib/mongoid/errors.rb +131 -0
- data/lib/mongoid/extensions.rb +115 -0
- data/lib/mongoid/extensions/array/accessors.rb +17 -0
- data/lib/mongoid/extensions/array/assimilation.rb +26 -0
- data/lib/mongoid/extensions/array/conversions.rb +23 -0
- data/lib/mongoid/extensions/array/parentization.rb +13 -0
- data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
- data/lib/mongoid/extensions/binary/conversions.rb +17 -0
- data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
- data/lib/mongoid/extensions/date/conversions.rb +24 -0
- data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
- data/lib/mongoid/extensions/false_class/equality.rb +13 -0
- data/lib/mongoid/extensions/float/conversions.rb +20 -0
- data/lib/mongoid/extensions/hash/accessors.rb +42 -0
- data/lib/mongoid/extensions/hash/assimilation.rb +40 -0
- data/lib/mongoid/extensions/hash/conversions.rb +42 -0
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
- data/lib/mongoid/extensions/hash/scoping.rb +12 -0
- data/lib/mongoid/extensions/integer/conversions.rb +20 -0
- data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
- data/lib/mongoid/extensions/object/conversions.rb +21 -0
- data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
- data/lib/mongoid/extensions/proc/scoping.rb +12 -0
- data/lib/mongoid/extensions/set/conversions.rb +20 -0
- data/lib/mongoid/extensions/string/conversions.rb +15 -0
- data/lib/mongoid/extensions/string/inflections.rb +97 -0
- data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
- data/lib/mongoid/extensions/time_conversions.rb +35 -0
- data/lib/mongoid/extensions/true_class/equality.rb +13 -0
- data/lib/mongoid/extras.rb +61 -0
- data/lib/mongoid/factory.rb +20 -0
- data/lib/mongoid/field.rb +83 -0
- data/lib/mongoid/fields.rb +62 -0
- data/lib/mongoid/finders.rb +145 -0
- data/lib/mongoid/hierarchy.rb +74 -0
- data/lib/mongoid/identity.rb +47 -0
- data/lib/mongoid/indexes.rb +27 -0
- data/lib/mongoid/javascript.rb +21 -0
- data/lib/mongoid/javascript/functions.yml +37 -0
- data/lib/mongoid/logger.rb +19 -0
- data/lib/mongoid/matchers.rb +35 -0
- data/lib/mongoid/matchers/all.rb +11 -0
- data/lib/mongoid/matchers/default.rb +26 -0
- data/lib/mongoid/matchers/exists.rb +13 -0
- data/lib/mongoid/matchers/gt.rb +11 -0
- data/lib/mongoid/matchers/gte.rb +11 -0
- data/lib/mongoid/matchers/in.rb +11 -0
- data/lib/mongoid/matchers/lt.rb +11 -0
- data/lib/mongoid/matchers/lte.rb +11 -0
- data/lib/mongoid/matchers/ne.rb +11 -0
- data/lib/mongoid/matchers/nin.rb +11 -0
- data/lib/mongoid/matchers/size.rb +11 -0
- data/lib/mongoid/memoization.rb +33 -0
- data/lib/mongoid/named_scope.rb +37 -0
- data/lib/mongoid/paranoia.rb +106 -0
- data/lib/mongoid/paths.rb +61 -0
- data/lib/mongoid/persistence.rb +216 -0
- data/lib/mongoid/persistence/command.rb +39 -0
- data/lib/mongoid/persistence/insert.rb +48 -0
- data/lib/mongoid/persistence/insert_embedded.rb +44 -0
- data/lib/mongoid/persistence/remove.rb +39 -0
- data/lib/mongoid/persistence/remove_all.rb +38 -0
- data/lib/mongoid/persistence/remove_embedded.rb +50 -0
- data/lib/mongoid/persistence/update.rb +71 -0
- data/lib/mongoid/railtie.rb +67 -0
- data/lib/mongoid/railties/database.rake +60 -0
- data/lib/mongoid/scope.rb +75 -0
- data/lib/mongoid/state.rb +32 -0
- data/lib/mongoid/timestamps.rb +27 -0
- data/lib/mongoid/validations.rb +51 -0
- data/lib/mongoid/validations/associated.rb +32 -0
- data/lib/mongoid/validations/locale/en.yml +5 -0
- data/lib/mongoid/validations/uniqueness.rb +56 -0
- data/lib/mongoid/version.rb +4 -0
- data/lib/mongoid/versioning.rb +26 -0
- data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +24 -0
- data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
- data/lib/rails/generators/mongoid/model/templates/model.rb +15 -0
- data/lib/rails/generators/mongoid_generator.rb +61 -0
- data/spec/integration/mongoid/association_attributes_spec.rb +71 -0
- data/spec/integration/mongoid/associations_spec.rb +768 -0
- data/spec/integration/mongoid/attributes_spec.rb +59 -0
- data/spec/integration/mongoid/callback_spec.rb +33 -0
- data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
- data/spec/integration/mongoid/criteria_spec.rb +281 -0
- data/spec/integration/mongoid/dirty_spec.rb +85 -0
- data/spec/integration/mongoid/document_spec.rb +741 -0
- data/spec/integration/mongoid/extensions_spec.rb +22 -0
- data/spec/integration/mongoid/finders_spec.rb +119 -0
- data/spec/integration/mongoid/inheritance_spec.rb +171 -0
- data/spec/integration/mongoid/named_scope_spec.rb +58 -0
- data/spec/integration/mongoid/paranoia_spec.rb +44 -0
- data/spec/integration/mongoid/persistence/update_spec.rb +46 -0
- data/spec/integration/mongoid/persistence_spec.rb +311 -0
- data/spec/integration/mongoid/validations/uniqueness_spec.rb +206 -0
- data/spec/models/account.rb +5 -0
- data/spec/models/address.rb +40 -0
- data/spec/models/agent.rb +7 -0
- data/spec/models/animal.rb +15 -0
- data/spec/models/answer.rb +4 -0
- data/spec/models/callbacks.rb +47 -0
- data/spec/models/category.rb +13 -0
- data/spec/models/comment.rb +10 -0
- data/spec/models/country_code.rb +6 -0
- data/spec/models/employer.rb +5 -0
- data/spec/models/favorite.rb +8 -0
- data/spec/models/game.rb +9 -0
- data/spec/models/inheritance.rb +72 -0
- data/spec/models/location.rb +5 -0
- data/spec/models/login.rb +6 -0
- data/spec/models/mixed_drink.rb +4 -0
- data/spec/models/name.rb +13 -0
- data/spec/models/namespacing.rb +11 -0
- data/spec/models/paranoid_post.rb +18 -0
- data/spec/models/parents.rb +32 -0
- data/spec/models/patient.rb +15 -0
- data/spec/models/person.rb +106 -0
- data/spec/models/pet.rb +7 -0
- data/spec/models/pet_owner.rb +6 -0
- data/spec/models/phone.rb +7 -0
- data/spec/models/post.rb +25 -0
- data/spec/models/preference.rb +7 -0
- data/spec/models/question.rb +8 -0
- data/spec/models/survey.rb +6 -0
- data/spec/models/translation.rb +5 -0
- data/spec/models/user.rb +6 -0
- data/spec/models/user_accout.rb +5 -0
- data/spec/models/vet_visit.rb +5 -0
- data/spec/models/video.rb +5 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
- data/spec/unit/mongoid/associations/embeds_many_spec.rb +626 -0
- data/spec/unit/mongoid/associations/embeds_one_spec.rb +287 -0
- data/spec/unit/mongoid/associations/foreign_key_spec.rb +90 -0
- data/spec/unit/mongoid/associations/meta_data_spec.rb +110 -0
- data/spec/unit/mongoid/associations/options_spec.rb +215 -0
- data/spec/unit/mongoid/associations/referenced_in_spec.rb +145 -0
- data/spec/unit/mongoid/associations/references_many_as_array_spec.rb +424 -0
- data/spec/unit/mongoid/associations/references_many_spec.rb +502 -0
- data/spec/unit/mongoid/associations/references_one_spec.rb +204 -0
- data/spec/unit/mongoid/associations_spec.rb +688 -0
- data/spec/unit/mongoid/atomicity_spec.rb +164 -0
- data/spec/unit/mongoid/attributes_spec.rb +646 -0
- data/spec/unit/mongoid/callbacks_spec.rb +85 -0
- data/spec/unit/mongoid/collection_spec.rb +187 -0
- data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
- data/spec/unit/mongoid/collections/master_spec.rb +41 -0
- data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
- data/spec/unit/mongoid/collections_spec.rb +98 -0
- data/spec/unit/mongoid/config_spec.rb +298 -0
- data/spec/unit/mongoid/contexts/enumerable_spec.rb +447 -0
- data/spec/unit/mongoid/contexts/mongo_spec.rb +703 -0
- data/spec/unit/mongoid/contexts_spec.rb +25 -0
- data/spec/unit/mongoid/criteria_spec.rb +873 -0
- data/spec/unit/mongoid/criterion/complex_spec.rb +17 -0
- data/spec/unit/mongoid/criterion/exclusion_spec.rb +121 -0
- data/spec/unit/mongoid/criterion/inclusion_spec.rb +274 -0
- data/spec/unit/mongoid/criterion/optional_spec.rb +483 -0
- data/spec/unit/mongoid/cursor_spec.rb +80 -0
- data/spec/unit/mongoid/deprecation_spec.rb +24 -0
- data/spec/unit/mongoid/dirty_spec.rb +430 -0
- data/spec/unit/mongoid/document_spec.rb +623 -0
- data/spec/unit/mongoid/errors_spec.rb +154 -0
- data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
- data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
- data/spec/unit/mongoid/extensions/array/conversions_spec.rb +52 -0
- data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
- data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +36 -0
- data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
- data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
- data/spec/unit/mongoid/extensions/date/conversions_spec.rb +145 -0
- data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +14 -0
- data/spec/unit/mongoid/extensions/false_class/equality_spec.rb +35 -0
- data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
- data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
- data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +59 -0
- data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +35 -0
- data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
- data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
- data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
- data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +29 -0
- data/spec/unit/mongoid/extensions/object/conversions_spec.rb +44 -0
- data/spec/unit/mongoid/extensions/objectid/conversions_spec.rb +22 -0
- data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
- data/spec/unit/mongoid/extensions/set/conversions_spec.rb +21 -0
- data/spec/unit/mongoid/extensions/string/conversions_spec.rb +28 -0
- data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
- data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +107 -0
- data/spec/unit/mongoid/extensions/time_conversions_spec.rb +186 -0
- data/spec/unit/mongoid/extensions/true_class/equality_spec.rb +35 -0
- data/spec/unit/mongoid/extras_spec.rb +102 -0
- data/spec/unit/mongoid/factory_spec.rb +31 -0
- data/spec/unit/mongoid/field_spec.rb +169 -0
- data/spec/unit/mongoid/fields_spec.rb +181 -0
- data/spec/unit/mongoid/finders_spec.rb +439 -0
- data/spec/unit/mongoid/hierarchy_spec.rb +68 -0
- data/spec/unit/mongoid/identity_spec.rb +109 -0
- data/spec/unit/mongoid/indexes_spec.rb +99 -0
- data/spec/unit/mongoid/javascript_spec.rb +48 -0
- data/spec/unit/mongoid/logger_spec.rb +38 -0
- data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
- data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
- data/spec/unit/mongoid/matchers_spec.rb +329 -0
- data/spec/unit/mongoid/memoization_spec.rb +75 -0
- data/spec/unit/mongoid/named_scope_spec.rb +123 -0
- data/spec/unit/mongoid/paranoia_spec.rb +108 -0
- data/spec/unit/mongoid/paths_spec.rb +272 -0
- data/spec/unit/mongoid/persistence/insert_embedded_spec.rb +154 -0
- data/spec/unit/mongoid/persistence/insert_spec.rb +144 -0
- data/spec/unit/mongoid/persistence/remove_all_spec.rb +82 -0
- data/spec/unit/mongoid/persistence/remove_embedded_spec.rb +152 -0
- data/spec/unit/mongoid/persistence/remove_spec.rb +89 -0
- data/spec/unit/mongoid/persistence/update_spec.rb +177 -0
- data/spec/unit/mongoid/persistence_spec.rb +452 -0
- data/spec/unit/mongoid/scope_spec.rb +240 -0
- data/spec/unit/mongoid/serialization_spec.rb +43 -0
- data/spec/unit/mongoid/state_spec.rb +94 -0
- data/spec/unit/mongoid/timestamps_spec.rb +30 -0
- data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
- data/spec/unit/mongoid/validations/uniqueness_spec.rb +201 -0
- data/spec/unit/mongoid/validations_spec.rb +43 -0
- data/spec/unit/mongoid/versioning_spec.rb +41 -0
- data/spec/unit/mongoid_spec.rb +46 -0
- metadata +433 -0
|
@@ -0,0 +1,78 @@
|
|
|
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, stored as an array of ids on the parent
|
|
6
|
+
# document.
|
|
7
|
+
class ReferencesManyAsArray < ReferencesMany
|
|
8
|
+
|
|
9
|
+
# Append a document to this association. This will also set the appended
|
|
10
|
+
# document's id on the inverse association as well.
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
#
|
|
14
|
+
# <tt>person.preferences << Preference.new(:name => "VGA")</tt>
|
|
15
|
+
def <<(*objects)
|
|
16
|
+
@target = @target.entries
|
|
17
|
+
objects.flatten.each do |object|
|
|
18
|
+
# First set the documents id on the parent array of ids.
|
|
19
|
+
@parent.send(@foreign_key) << object.id
|
|
20
|
+
# Then we need to set the parent's id on the documents array of ids
|
|
21
|
+
# to get the inverse side of the association as well. Note, need a
|
|
22
|
+
# clean way to handle this with new documents - we want to set the
|
|
23
|
+
# actual objects as well, but dont want to get in an infinite loop
|
|
24
|
+
# while doing so.
|
|
25
|
+
object.send(reverse_key(object)) << @parent.id
|
|
26
|
+
@target << object
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
alias :concat :<<
|
|
31
|
+
alias :push :<<
|
|
32
|
+
|
|
33
|
+
# Builds a new Document and adds it to the association collection. The
|
|
34
|
+
# document created will be of the same class as the others in the
|
|
35
|
+
# association, and the attributes will be passed into the constructor.
|
|
36
|
+
#
|
|
37
|
+
# Returns the newly created object.
|
|
38
|
+
def build(attributes = nil)
|
|
39
|
+
load_target
|
|
40
|
+
document = @klass.instantiate(attributes || {})
|
|
41
|
+
push(document); document
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
protected
|
|
45
|
+
# Find the inverse key for the supplied document.
|
|
46
|
+
def reverse_key(document)
|
|
47
|
+
document.send(@options.inverse_of).options.foreign_key
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The default query used for retrieving the documents from the database.
|
|
51
|
+
def query
|
|
52
|
+
@query ||= lambda { @klass.any_in(:_id => @parent.send(@foreign_key)) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class << self
|
|
56
|
+
# Perform an update of the relationship of the parent and child. This
|
|
57
|
+
# will assimilate the child +Document+ into the parent's object graph.
|
|
58
|
+
#
|
|
59
|
+
# Options:
|
|
60
|
+
#
|
|
61
|
+
# related: The related object
|
|
62
|
+
# parent: The parent +Document+ to update.
|
|
63
|
+
# options: The association +Options+
|
|
64
|
+
#
|
|
65
|
+
# Example:
|
|
66
|
+
#
|
|
67
|
+
# <tt>RelatesToManyAsArray.update(preferences, person, options)</tt>
|
|
68
|
+
def update(target, document, options)
|
|
69
|
+
target.each do |child|
|
|
70
|
+
name = child.associations[options.inverse_of.to_s].options.name
|
|
71
|
+
child.send(name) << document
|
|
72
|
+
end
|
|
73
|
+
instantiate(document, options, target)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
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 ReferencesOne < 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).tap(&:save)
|
|
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
|
+
# Used for setting the association via a nested attributes setter on the
|
|
46
|
+
# parent +Document+. Called when using accepts_nested_attributes_for.
|
|
47
|
+
#
|
|
48
|
+
# Options:
|
|
49
|
+
#
|
|
50
|
+
# attributes: The attributes for the new association
|
|
51
|
+
#
|
|
52
|
+
# Returns:
|
|
53
|
+
#
|
|
54
|
+
# A new target document.
|
|
55
|
+
def nested_build(attributes, options = nil)
|
|
56
|
+
build(attributes) unless @target.blank? && options[:update_only]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class << self
|
|
60
|
+
# Preferred method for creating the new +RelatesToMany+ association.
|
|
61
|
+
#
|
|
62
|
+
# Options:
|
|
63
|
+
#
|
|
64
|
+
# document: The +Document+ that contains the relationship.
|
|
65
|
+
# options: The association +Options+.
|
|
66
|
+
def instantiate(document, options, target = nil)
|
|
67
|
+
new(document, options, target)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns the macro used to create the association.
|
|
71
|
+
def macro
|
|
72
|
+
:references_one
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Perform an update of the relationship of the parent and child. This
|
|
76
|
+
# will assimilate the child +Document+ into the parent's object graph.
|
|
77
|
+
#
|
|
78
|
+
# Options:
|
|
79
|
+
#
|
|
80
|
+
# related: The related object to update.
|
|
81
|
+
# document: The parent +Document+.
|
|
82
|
+
# options: The association +Options+
|
|
83
|
+
#
|
|
84
|
+
# Example:
|
|
85
|
+
#
|
|
86
|
+
# <tt>HasOneToRelated.update(game, person, options)</tt>
|
|
87
|
+
def update(target, document, options)
|
|
88
|
+
if target
|
|
89
|
+
name = document.class.to_s.underscore
|
|
90
|
+
target.send("#{name}=", document)
|
|
91
|
+
return instantiate(document, options, target)
|
|
92
|
+
end
|
|
93
|
+
target
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
module Atomicity #:nodoc:
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
# Get all the atomic updates that need to happen for the current
|
|
7
|
+
# +Document+. This includes all changes that need to happen in the
|
|
8
|
+
# entire hierarchy that exists below where the save call was made.
|
|
9
|
+
#
|
|
10
|
+
# Example:
|
|
11
|
+
#
|
|
12
|
+
# <tt>person.save</tt> # Saves entire tree
|
|
13
|
+
#
|
|
14
|
+
# Returns:
|
|
15
|
+
#
|
|
16
|
+
# A +Hash+ of all atomic updates that need to occur.
|
|
17
|
+
def _updates
|
|
18
|
+
processed = {}
|
|
19
|
+
|
|
20
|
+
_children.inject({ "$set" => _sets, "$pushAll" => {}, :other => {} }) do |updates, child|
|
|
21
|
+
changes = child._sets
|
|
22
|
+
updates["$set"].update(changes)
|
|
23
|
+
processed[child.class] = true unless changes.empty?
|
|
24
|
+
|
|
25
|
+
target = processed.has_key?(child.class) ? :other : "$pushAll"
|
|
26
|
+
|
|
27
|
+
child._pushes.each do |attr, val|
|
|
28
|
+
if updates[target].has_key?(attr)
|
|
29
|
+
updates[target][attr] << val
|
|
30
|
+
else
|
|
31
|
+
updates[target].update({attr => [val]})
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
updates
|
|
35
|
+
end.delete_if do |key, value|
|
|
36
|
+
value.empty?
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
protected
|
|
41
|
+
# Get all the push attributes that need to occur.
|
|
42
|
+
def _pushes
|
|
43
|
+
(new_record? && embedded_many? && !_parent.new_record?) ? { _path => raw_attributes } : {}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get all the attributes that need to be set.
|
|
47
|
+
def _sets
|
|
48
|
+
if changed? && !new_record?
|
|
49
|
+
setters
|
|
50
|
+
else
|
|
51
|
+
embedded_one? && new_record? ? { _path => raw_attributes } : {}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
module Attributes
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
included do
|
|
6
|
+
class_inheritable_accessor :_protected_fields
|
|
7
|
+
self._protected_fields = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Get the id associated with this object. This will pull the _id value out
|
|
11
|
+
# of the attributes +Hash+.
|
|
12
|
+
def id
|
|
13
|
+
@attributes["_id"]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Set the id of the +Document+ to a new one.
|
|
17
|
+
def id=(new_id)
|
|
18
|
+
@attributes["_id"] = new_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias :_id :id
|
|
22
|
+
alias :_id= :id=
|
|
23
|
+
|
|
24
|
+
# Used for allowing accessor methods for dynamic attributes.
|
|
25
|
+
def method_missing(name, *args)
|
|
26
|
+
attr = name.to_s
|
|
27
|
+
return super unless @attributes.has_key?(attr.reader)
|
|
28
|
+
if attr.writer?
|
|
29
|
+
# "args.size > 1" allows to simulate 1.8 behavior of "*args"
|
|
30
|
+
write_attribute(attr.reader, (args.size > 1) ? args : args.first)
|
|
31
|
+
else
|
|
32
|
+
read_attribute(attr.reader)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Process the provided attributes casting them to their proper values if a
|
|
37
|
+
# field exists for them on the +Document+. This will be limited to only the
|
|
38
|
+
# attributes provided in the suppied +Hash+ so that no extra nil values get
|
|
39
|
+
# put into the document's attributes.
|
|
40
|
+
def process(attrs = nil)
|
|
41
|
+
(attrs || {}).each_pair do |key, value|
|
|
42
|
+
if set_allowed?(key)
|
|
43
|
+
write_attribute(key, value)
|
|
44
|
+
elsif write_allowed?(key)
|
|
45
|
+
if associations.include?(key.to_s) and associations[key.to_s].embedded? and value.is_a?(Hash)
|
|
46
|
+
if association = send(key)
|
|
47
|
+
association.nested_build(value)
|
|
48
|
+
else
|
|
49
|
+
send("build_#{key}", value)
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
send("#{key}=", value)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
setup_modifications
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Read a value from the +Document+ attributes. If the value does not exist
|
|
60
|
+
# it will return nil.
|
|
61
|
+
#
|
|
62
|
+
# Options:
|
|
63
|
+
#
|
|
64
|
+
# name: The name of the attribute to get.
|
|
65
|
+
#
|
|
66
|
+
# Example:
|
|
67
|
+
#
|
|
68
|
+
# <tt>person.read_attribute(:title)</tt>
|
|
69
|
+
def read_attribute(name)
|
|
70
|
+
access = name.to_s
|
|
71
|
+
value = @attributes[access]
|
|
72
|
+
typed_value = fields.has_key?(access) ? fields[access].get(value) : value
|
|
73
|
+
accessed(access, typed_value)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Remove a value from the +Document+ attributes. If the value does not exist
|
|
77
|
+
# it will fail gracefully.
|
|
78
|
+
#
|
|
79
|
+
# Options:
|
|
80
|
+
#
|
|
81
|
+
# name: The name of the attribute to remove.
|
|
82
|
+
#
|
|
83
|
+
# Example:
|
|
84
|
+
#
|
|
85
|
+
# <tt>person.remove_attribute(:title)</tt>
|
|
86
|
+
def remove_attribute(name)
|
|
87
|
+
access = name.to_s
|
|
88
|
+
modify(access, @attributes.delete(name.to_s), nil)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns true when attribute is present.
|
|
92
|
+
#
|
|
93
|
+
# Options:
|
|
94
|
+
#
|
|
95
|
+
# name: The name of the attribute to request presence on.
|
|
96
|
+
def attribute_present?(name)
|
|
97
|
+
value = read_attribute(name)
|
|
98
|
+
!value.blank?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Returns the object type. This corresponds to the name of the class that
|
|
102
|
+
# this +Document+ is, which is used in determining the class to
|
|
103
|
+
# instantiate in various cases.
|
|
104
|
+
def _type
|
|
105
|
+
@attributes["_type"]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Set the type of the +Document+. This should be the name of the class.
|
|
109
|
+
def _type=(new_type)
|
|
110
|
+
@attributes["_type"] = new_type
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Write a single attribute to the +Document+ attribute +Hash+. This will
|
|
114
|
+
# also fire the before and after update callbacks, and perform any
|
|
115
|
+
# necessary typecasting.
|
|
116
|
+
#
|
|
117
|
+
# Options:
|
|
118
|
+
#
|
|
119
|
+
# name: The name of the attribute to update.
|
|
120
|
+
# value: The value to set for the attribute.
|
|
121
|
+
#
|
|
122
|
+
# Example:
|
|
123
|
+
#
|
|
124
|
+
# <tt>person.write_attribute(:title, "Mr.")</tt>
|
|
125
|
+
#
|
|
126
|
+
# This will also cause the observing +Document+ to notify it's parent if
|
|
127
|
+
# there is any.
|
|
128
|
+
def write_attribute(name, value)
|
|
129
|
+
access = name.to_s
|
|
130
|
+
typed_value = fields.has_key?(access) ? fields[access].set(value) : value
|
|
131
|
+
modify(access, @attributes[access], typed_value)
|
|
132
|
+
notify if !id.blank? && new_record?
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Writes the supplied attributes +Hash+ to the +Document+. This will only
|
|
136
|
+
# overwrite existing attributes if they are present in the new +Hash+, all
|
|
137
|
+
# others will be preserved.
|
|
138
|
+
#
|
|
139
|
+
# Options:
|
|
140
|
+
#
|
|
141
|
+
# attrs: The +Hash+ of new attributes to set on the +Document+
|
|
142
|
+
#
|
|
143
|
+
# Example:
|
|
144
|
+
#
|
|
145
|
+
# <tt>person.write_attributes(:title => "Mr.")</tt>
|
|
146
|
+
#
|
|
147
|
+
# This will also cause the observing +Document+ to notify it's parent if
|
|
148
|
+
# there is any.
|
|
149
|
+
def write_attributes(attrs = nil)
|
|
150
|
+
process(attrs || {})
|
|
151
|
+
identified = !id.blank?
|
|
152
|
+
if new_record? && !identified
|
|
153
|
+
identify; notify
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
alias :attributes= :write_attributes
|
|
157
|
+
|
|
158
|
+
protected
|
|
159
|
+
# apply default values to attributes - calling procs as required
|
|
160
|
+
def default_attributes
|
|
161
|
+
default_values = defaults
|
|
162
|
+
default_values.each_pair do |key, val|
|
|
163
|
+
default_values[key] = val.call if val.respond_to?(:call)
|
|
164
|
+
end
|
|
165
|
+
default_values || {}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Return true if dynamic field setting is enabled.
|
|
169
|
+
def set_allowed?(key)
|
|
170
|
+
Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Used when supplying a :reject_if block as an option to
|
|
174
|
+
# accepts_nested_attributes_for
|
|
175
|
+
def reject(attributes, options)
|
|
176
|
+
rejector = options[:reject_if]
|
|
177
|
+
if rejector
|
|
178
|
+
attributes.delete_if do |key, value|
|
|
179
|
+
rejector.call(value)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Used when supplying a :limit as an option to accepts_nested_attributes_for
|
|
185
|
+
def limit(attributes, name, options)
|
|
186
|
+
if options[:limit] && attributes.size > options[:limit]
|
|
187
|
+
raise Mongoid::Errors::TooManyNestedAttributeRecords.new(name, options[:limit])
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Return true if writing to the given field is allowed
|
|
192
|
+
def write_allowed?(key)
|
|
193
|
+
name = key.to_s
|
|
194
|
+
!self._protected_fields.include?(name)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
module ClassMethods
|
|
198
|
+
# Defines attribute setters for the associations specified by the names.
|
|
199
|
+
# This will work for a has one or has many association.
|
|
200
|
+
#
|
|
201
|
+
# Example:
|
|
202
|
+
#
|
|
203
|
+
# class Person
|
|
204
|
+
# include Mongoid::Document
|
|
205
|
+
# embeds_one :name
|
|
206
|
+
# embeds_many :addresses
|
|
207
|
+
#
|
|
208
|
+
# accepts_nested_attributes_for :name, :addresses
|
|
209
|
+
# end
|
|
210
|
+
def accepts_nested_attributes_for(*args)
|
|
211
|
+
associations = args.flatten
|
|
212
|
+
options = associations.last.is_a?(Hash) ? associations.pop : {}
|
|
213
|
+
associations.each do |name|
|
|
214
|
+
define_method("#{name}_attributes=") do |attrs|
|
|
215
|
+
reject(attrs, options)
|
|
216
|
+
limit(attrs, name, options)
|
|
217
|
+
association = send(name)
|
|
218
|
+
if association
|
|
219
|
+
# observe(association, true)
|
|
220
|
+
association.nested_build(attrs, options)
|
|
221
|
+
else
|
|
222
|
+
send("build_#{name}", attrs, options)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Defines fields that cannot be set via mass assignment.
|
|
229
|
+
#
|
|
230
|
+
# Example:
|
|
231
|
+
#
|
|
232
|
+
# class Person
|
|
233
|
+
# include Mongoid::Document
|
|
234
|
+
# field :security_code
|
|
235
|
+
# attr_protected :security_code
|
|
236
|
+
# end
|
|
237
|
+
def attr_protected(*names)
|
|
238
|
+
_protected_fields.concat(names.flatten.map(&:to_s))
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|