mongoid 2.0.2 → 2.1.0
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/README.rdoc +3 -1
- data/Rakefile +3 -2
- data/lib/config/locales/bg.yml +6 -0
- data/lib/config/locales/de.yml +6 -0
- data/lib/config/locales/en-GB.yml +48 -0
- data/lib/config/locales/en.yml +6 -3
- data/lib/config/locales/es.yml +6 -0
- data/lib/config/locales/fr.yml +6 -0
- data/lib/config/locales/hi.yml +39 -0
- data/lib/config/locales/hu.yml +13 -7
- data/lib/config/locales/id.yml +3 -0
- data/lib/config/locales/it.yml +7 -1
- data/lib/config/locales/ja.yml +4 -1
- data/lib/config/locales/kr.yml +9 -34
- data/lib/config/locales/nl.yml +6 -0
- data/lib/config/locales/pl.yml +6 -0
- data/lib/config/locales/pt-BR.yml +6 -0
- data/lib/config/locales/pt.yml +6 -0
- data/lib/config/locales/ro.yml +6 -0
- data/lib/config/locales/ru.yml +6 -0
- data/lib/config/locales/sv.yml +6 -0
- data/lib/config/locales/vi.yml +3 -0
- data/lib/config/locales/zh-CN.yml +6 -0
- data/lib/mongoid.rb +51 -45
- data/lib/mongoid/atomic.rb +145 -0
- data/lib/mongoid/atomic/modifiers.rb +109 -0
- data/lib/mongoid/atomic/paths.rb +3 -0
- data/lib/mongoid/atomic/paths/embedded.rb +43 -0
- data/lib/mongoid/atomic/paths/embedded/many.rb +44 -0
- data/lib/mongoid/atomic/paths/embedded/one.rb +43 -0
- data/lib/mongoid/atomic/paths/root.rb +40 -0
- data/lib/mongoid/attributes.rb +12 -23
- data/lib/mongoid/attributes/processing.rb +5 -5
- data/lib/mongoid/callbacks.rb +2 -0
- data/lib/mongoid/collection.rb +12 -59
- data/lib/mongoid/collections.rb +23 -20
- data/lib/mongoid/collections/master.rb +6 -4
- data/lib/mongoid/collections/operations.rb +1 -0
- data/lib/mongoid/collections/retry.rb +7 -0
- data/lib/mongoid/components.rb +2 -2
- data/lib/mongoid/config.rb +42 -55
- data/lib/mongoid/config/database.rb +6 -2
- data/lib/mongoid/config/replset_database.rb +7 -3
- data/lib/mongoid/contexts.rb +9 -3
- data/lib/mongoid/contexts/enumerable.rb +7 -3
- data/lib/mongoid/contexts/mongo.rb +139 -101
- data/lib/mongoid/criteria.rb +86 -69
- data/lib/mongoid/criterion/complex.rb +32 -5
- data/lib/mongoid/criterion/inclusion.rb +4 -2
- data/lib/mongoid/criterion/optional.rb +111 -86
- data/lib/mongoid/criterion/selector.rb +8 -4
- data/lib/mongoid/cursor.rb +27 -27
- data/lib/mongoid/dirty.rb +54 -214
- data/lib/mongoid/document.rb +37 -39
- data/lib/mongoid/errors/document_not_found.rb +3 -4
- data/lib/mongoid/errors/invalid_collection.rb +2 -3
- data/lib/mongoid/errors/invalid_database.rb +2 -3
- data/lib/mongoid/errors/invalid_field.rb +2 -3
- data/lib/mongoid/errors/invalid_options.rb +19 -7
- data/lib/mongoid/errors/invalid_type.rb +2 -3
- data/lib/mongoid/errors/mongoid_error.rb +5 -6
- data/lib/mongoid/errors/too_many_nested_attribute_records.rb +2 -3
- data/lib/mongoid/errors/unsupported_version.rb +2 -3
- data/lib/mongoid/errors/validations.rb +2 -3
- data/lib/mongoid/extensions.rb +8 -62
- data/lib/mongoid/extensions/array/deletion.rb +29 -0
- data/lib/mongoid/extensions/false_class/equality.rb +14 -1
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +21 -10
- data/lib/mongoid/extensions/hash/scoping.rb +14 -1
- data/lib/mongoid/extensions/nil/collectionization.rb +12 -1
- data/lib/mongoid/extensions/object/reflections.rb +33 -2
- data/lib/mongoid/extensions/object_id/conversions.rb +2 -36
- data/lib/mongoid/extensions/proc/scoping.rb +14 -1
- data/lib/mongoid/extensions/string/conversions.rb +4 -16
- data/lib/mongoid/extensions/string/inflections.rb +35 -14
- data/lib/mongoid/extensions/symbol/inflections.rb +38 -12
- data/lib/mongoid/extensions/true_class/equality.rb +14 -1
- data/lib/mongoid/extras.rb +11 -30
- data/lib/mongoid/factory.rb +1 -1
- data/lib/mongoid/fields.rb +121 -29
- data/lib/mongoid/fields/mappings.rb +36 -0
- data/lib/mongoid/fields/serializable.rb +131 -0
- data/lib/mongoid/fields/serializable/array.rb +64 -0
- data/lib/mongoid/fields/serializable/big_decimal.rb +42 -0
- data/lib/mongoid/fields/serializable/bignum.rb +10 -0
- data/lib/mongoid/fields/serializable/binary.rb +11 -0
- data/lib/mongoid/fields/serializable/boolean.rb +44 -0
- data/lib/mongoid/fields/serializable/date.rb +51 -0
- data/lib/mongoid/fields/serializable/date_time.rb +28 -0
- data/lib/mongoid/fields/serializable/fixnum.rb +10 -0
- data/lib/mongoid/fields/serializable/float.rb +33 -0
- data/lib/mongoid/fields/serializable/foreign_keys/array.rb +56 -0
- data/lib/mongoid/fields/serializable/foreign_keys/object.rb +43 -0
- data/lib/mongoid/fields/serializable/hash.rb +25 -0
- data/lib/mongoid/fields/serializable/integer.rb +33 -0
- data/lib/mongoid/fields/serializable/object.rb +11 -0
- data/lib/mongoid/fields/serializable/object_id.rb +32 -0
- data/lib/mongoid/fields/serializable/range.rb +42 -0
- data/lib/mongoid/fields/serializable/set.rb +42 -0
- data/lib/mongoid/fields/serializable/string.rb +28 -0
- data/lib/mongoid/fields/serializable/symbol.rb +28 -0
- data/lib/mongoid/fields/serializable/time.rb +12 -0
- data/lib/mongoid/fields/serializable/time_with_zone.rb +12 -0
- data/lib/mongoid/fields/serializable/timekeeping.rb +102 -0
- data/lib/mongoid/finders.rb +61 -37
- data/lib/mongoid/hierarchy.rb +43 -8
- data/lib/mongoid/identity_map.rb +106 -0
- data/lib/mongoid/indexes.rb +17 -1
- data/lib/mongoid/javascript.rb +2 -3
- data/lib/mongoid/keys.rb +10 -21
- data/lib/mongoid/logger.rb +22 -1
- data/lib/mongoid/matchers/all.rb +10 -0
- data/lib/mongoid/matchers/default.rb +1 -1
- data/lib/mongoid/matchers/exists.rb +10 -0
- data/lib/mongoid/matchers/gt.rb +10 -0
- data/lib/mongoid/matchers/gte.rb +10 -0
- data/lib/mongoid/matchers/in.rb +10 -0
- data/lib/mongoid/matchers/lt.rb +10 -0
- data/lib/mongoid/matchers/lte.rb +10 -0
- data/lib/mongoid/matchers/ne.rb +10 -0
- data/lib/mongoid/matchers/nin.rb +10 -0
- data/lib/mongoid/matchers/or.rb +7 -4
- data/lib/mongoid/matchers/size.rb +10 -0
- data/lib/mongoid/multi_database.rb +26 -6
- data/lib/mongoid/multi_parameter_attributes.rb +40 -17
- data/lib/mongoid/named_scope.rb +1 -2
- data/lib/mongoid/nested_attributes.rb +4 -1
- data/lib/mongoid/observer.rb +108 -5
- data/lib/mongoid/paranoia.rb +26 -26
- data/lib/mongoid/persistence.rb +15 -21
- data/lib/mongoid/persistence/atomic.rb +135 -0
- data/lib/mongoid/persistence/atomic/add_to_set.rb +11 -8
- data/lib/mongoid/persistence/atomic/bit.rb +37 -0
- data/lib/mongoid/persistence/atomic/inc.rb +9 -6
- data/lib/mongoid/persistence/atomic/operation.rb +48 -7
- data/lib/mongoid/persistence/atomic/pop.rb +34 -0
- data/lib/mongoid/persistence/atomic/pull.rb +34 -0
- data/lib/mongoid/persistence/atomic/pull_all.rb +10 -9
- data/lib/mongoid/persistence/atomic/push.rb +8 -5
- data/lib/mongoid/persistence/atomic/push_all.rb +31 -0
- data/lib/mongoid/persistence/atomic/rename.rb +31 -0
- data/lib/mongoid/persistence/atomic/set.rb +30 -0
- data/lib/mongoid/persistence/atomic/unset.rb +28 -0
- data/lib/mongoid/persistence/deletion.rb +32 -0
- data/lib/mongoid/persistence/insertion.rb +41 -0
- data/lib/mongoid/persistence/modification.rb +37 -0
- data/lib/mongoid/persistence/operations.rb +214 -0
- data/lib/mongoid/persistence/operations/embedded/insert.rb +42 -0
- data/lib/mongoid/persistence/operations/embedded/remove.rb +40 -0
- data/lib/mongoid/persistence/operations/insert.rb +34 -0
- data/lib/mongoid/persistence/operations/remove.rb +33 -0
- data/lib/mongoid/persistence/operations/update.rb +53 -0
- data/lib/mongoid/railtie.rb +21 -33
- data/lib/mongoid/railties/database.rake +12 -12
- data/lib/mongoid/relations.rb +9 -5
- data/lib/mongoid/relations/accessors.rb +15 -36
- data/lib/mongoid/relations/auto_save.rb +2 -2
- data/lib/mongoid/relations/binding.rb +28 -1
- data/lib/mongoid/relations/bindings/embedded/in.rb +17 -30
- data/lib/mongoid/relations/bindings/embedded/many.rb +16 -21
- data/lib/mongoid/relations/bindings/embedded/one.rb +11 -16
- data/lib/mongoid/relations/bindings/referenced/in.rb +31 -32
- data/lib/mongoid/relations/bindings/referenced/many.rb +19 -61
- data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +15 -63
- data/lib/mongoid/relations/bindings/referenced/one.rb +18 -26
- data/lib/mongoid/relations/builder.rb +4 -2
- data/lib/mongoid/relations/builders.rb +21 -2
- data/lib/mongoid/relations/builders/embedded/in.rb +5 -1
- data/lib/mongoid/relations/builders/embedded/many.rb +12 -4
- data/lib/mongoid/relations/builders/embedded/one.rb +5 -1
- data/lib/mongoid/relations/builders/nested_attributes/many.rb +2 -2
- data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
- data/lib/mongoid/relations/builders/referenced/in.rb +2 -5
- data/lib/mongoid/relations/builders/referenced/many.rb +2 -3
- data/lib/mongoid/relations/builders/referenced/many_to_many.rb +14 -5
- data/lib/mongoid/relations/builders/referenced/one.rb +2 -3
- data/lib/mongoid/relations/embedded/atomic.rb +2 -2
- data/lib/mongoid/relations/embedded/in.rb +72 -41
- data/lib/mongoid/relations/embedded/many.rb +116 -120
- data/lib/mongoid/relations/embedded/one.rb +59 -41
- data/lib/mongoid/relations/embedded/sort.rb +31 -0
- data/lib/mongoid/relations/macros.rb +28 -24
- data/lib/mongoid/relations/many.rb +10 -103
- data/lib/mongoid/relations/metadata.rb +335 -38
- data/lib/mongoid/relations/one.rb +7 -32
- data/lib/mongoid/relations/options.rb +47 -0
- data/lib/mongoid/relations/proxy.rb +29 -28
- data/lib/mongoid/relations/referenced/batch.rb +2 -3
- data/lib/mongoid/relations/referenced/in.rb +66 -53
- data/lib/mongoid/relations/referenced/many.rb +216 -143
- data/lib/mongoid/relations/referenced/many_to_many.rb +132 -163
- data/lib/mongoid/relations/referenced/one.rb +76 -58
- data/lib/mongoid/relations/synchronization.rb +113 -0
- data/lib/mongoid/relations/targets.rb +2 -0
- data/lib/mongoid/relations/targets/enumerable.rb +329 -0
- data/lib/mongoid/safety.rb +24 -156
- data/lib/mongoid/serialization.rb +21 -0
- data/lib/mongoid/state.rb +34 -0
- data/lib/mongoid/threaded.rb +175 -0
- data/lib/mongoid/timestamps/updated.rb +1 -1
- data/lib/mongoid/validations.rb +3 -7
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/versioning.rb +61 -7
- data/lib/rack/mongoid.rb +2 -0
- data/lib/rack/mongoid/middleware/identity_map.rb +38 -0
- data/lib/rails/generators/mongoid/model/model_generator.rb +1 -1
- data/lib/rails/generators/mongoid/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/mongoid/observer/observer_generator.rb +1 -1
- data/lib/rails/generators/mongoid/observer/templates/{observer.rb → observer.rb.tt} +0 -0
- data/lib/rails/mongoid.rb +17 -17
- metadata +136 -102
- data/lib/mongoid/atomicity.rb +0 -111
- data/lib/mongoid/collections/cyclic_iterator.rb +0 -34
- data/lib/mongoid/collections/slaves.rb +0 -61
- data/lib/mongoid/extensions/array/conversions.rb +0 -23
- data/lib/mongoid/extensions/array/parentization.rb +0 -13
- data/lib/mongoid/extensions/big_decimal/conversions.rb +0 -19
- data/lib/mongoid/extensions/binary/conversions.rb +0 -17
- data/lib/mongoid/extensions/boolean/conversions.rb +0 -27
- data/lib/mongoid/extensions/date/conversions.rb +0 -25
- data/lib/mongoid/extensions/datetime/conversions.rb +0 -12
- data/lib/mongoid/extensions/float/conversions.rb +0 -20
- data/lib/mongoid/extensions/hash/conversions.rb +0 -19
- data/lib/mongoid/extensions/integer/conversions.rb +0 -20
- data/lib/mongoid/extensions/object/conversions.rb +0 -25
- data/lib/mongoid/extensions/range/conversions.rb +0 -25
- data/lib/mongoid/extensions/set/conversions.rb +0 -20
- data/lib/mongoid/extensions/symbol/conversions.rb +0 -21
- data/lib/mongoid/extensions/time_conversions.rb +0 -38
- data/lib/mongoid/field.rb +0 -162
- data/lib/mongoid/paths.rb +0 -61
- data/lib/mongoid/persistence/command.rb +0 -71
- data/lib/mongoid/persistence/insert.rb +0 -53
- data/lib/mongoid/persistence/insert_embedded.rb +0 -43
- data/lib/mongoid/persistence/remove.rb +0 -44
- data/lib/mongoid/persistence/remove_all.rb +0 -40
- data/lib/mongoid/persistence/remove_embedded.rb +0 -48
- data/lib/mongoid/persistence/update.rb +0 -77
- data/lib/mongoid/safe.rb +0 -23
- data/lib/mongoid/validations/referenced.rb +0 -58
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid # :nodoc:
|
|
3
|
+
module Relations #:nodoc:
|
|
4
|
+
|
|
5
|
+
# This module handles the behaviour for synchronizing foreign keys between
|
|
6
|
+
# both sides of a many to many relations.
|
|
7
|
+
module Synchronization
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
# Is the document able to be synced on the inverse side? This is only if
|
|
11
|
+
# the key has changed and the relation bindings have not been run.
|
|
12
|
+
#
|
|
13
|
+
# @example Are the foreign keys syncable?
|
|
14
|
+
# document.syncable?(metadata)
|
|
15
|
+
#
|
|
16
|
+
# @param [ Metadata ] metadata The relation metadata.
|
|
17
|
+
#
|
|
18
|
+
# @return [ true, false ] If we can sync.
|
|
19
|
+
#
|
|
20
|
+
# @since 2.1.0
|
|
21
|
+
def syncable?(metadata)
|
|
22
|
+
!synced?(metadata.foreign_key) && send(metadata.foreign_key_check)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get the synced foreign keys.
|
|
26
|
+
#
|
|
27
|
+
# @example Get the synced foreign keys.
|
|
28
|
+
# document.synced
|
|
29
|
+
#
|
|
30
|
+
# @return [ Hash ] The synced foreign keys.
|
|
31
|
+
#
|
|
32
|
+
# @since 2.1.0
|
|
33
|
+
def synced
|
|
34
|
+
@synced ||= {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Has the document been synced for the foreign key?
|
|
38
|
+
#
|
|
39
|
+
# @todo Change the sync to be key based.
|
|
40
|
+
#
|
|
41
|
+
# @example Has the document been synced?
|
|
42
|
+
# document.synced?
|
|
43
|
+
#
|
|
44
|
+
# @param [ String ] foreign_key The foreign key.
|
|
45
|
+
#
|
|
46
|
+
# @return [ true, false ] If we can sync.
|
|
47
|
+
#
|
|
48
|
+
# @since 2.1.0
|
|
49
|
+
def synced?(foreign_key)
|
|
50
|
+
!!synced[foreign_key]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Update the inverse keys for the relation.
|
|
54
|
+
#
|
|
55
|
+
# @example Update the inverse keys
|
|
56
|
+
# document.update_inverse_keys(metadata)
|
|
57
|
+
#
|
|
58
|
+
# @param [ Metadata ] meta The document metadata.
|
|
59
|
+
#
|
|
60
|
+
# @return [ Object ] The updated values.
|
|
61
|
+
#
|
|
62
|
+
# @since 2.1.0
|
|
63
|
+
def update_inverse_keys(meta)
|
|
64
|
+
old, new = changes[meta.foreign_key]
|
|
65
|
+
meta.criteria(new - old).add_to_set(meta.inverse_foreign_key, id)
|
|
66
|
+
meta.criteria(old - new).pull(meta.inverse_foreign_key, id)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
module ClassMethods #:nodoc:
|
|
70
|
+
|
|
71
|
+
# Set up the syncing of many to many foreign keys.
|
|
72
|
+
#
|
|
73
|
+
# @example Set up the syncing.
|
|
74
|
+
# Person.synced(metadata)
|
|
75
|
+
#
|
|
76
|
+
# @param [ Metadata ] metadata The relation metadata.
|
|
77
|
+
#
|
|
78
|
+
# @since 2.1.0
|
|
79
|
+
def synced(metadata)
|
|
80
|
+
synced_save(metadata)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# Set up the sync of inverse keys that needs to happen on a save.
|
|
86
|
+
#
|
|
87
|
+
# If the foreign key field has changed and the document is not
|
|
88
|
+
# synced, $addToSet the new ids, $pull the ones no longer in the
|
|
89
|
+
# array from the inverse side.
|
|
90
|
+
#
|
|
91
|
+
# @example Set up the save syncing.
|
|
92
|
+
# Person.synced_save(metadata)
|
|
93
|
+
#
|
|
94
|
+
# @param [ Metadata ] metadata The relation metadata.
|
|
95
|
+
#
|
|
96
|
+
# @return [ Class ] The class getting set up.
|
|
97
|
+
#
|
|
98
|
+
# @since 2.1.0
|
|
99
|
+
def synced_save(metadata)
|
|
100
|
+
tap do
|
|
101
|
+
set_callback(
|
|
102
|
+
:save,
|
|
103
|
+
:after,
|
|
104
|
+
:if => lambda { |doc| doc.syncable?(metadata) }
|
|
105
|
+
) do |doc|
|
|
106
|
+
doc.update_inverse_keys(metadata)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
module Relations #:nodoc:
|
|
4
|
+
module Targets #:nodoc:
|
|
5
|
+
|
|
6
|
+
# This class is the wrapper for all relational associations that have a
|
|
7
|
+
# target that can be a criteria or array of loaded documents. This
|
|
8
|
+
# handles both cases or a combination of the two.
|
|
9
|
+
class Enumerable
|
|
10
|
+
include ::Enumerable
|
|
11
|
+
|
|
12
|
+
# The three main instance variables are collections of documents.
|
|
13
|
+
#
|
|
14
|
+
# @attribute [rw] added Documents that have been appended.
|
|
15
|
+
# @attribute [rw] loaded Persisted documents that have been loaded.
|
|
16
|
+
# @attribute [rw] unloaded A criteria representing persisted docs.
|
|
17
|
+
attr_accessor :added, :loaded, :unloaded
|
|
18
|
+
|
|
19
|
+
# Check if the enumerable is equal to the other object.
|
|
20
|
+
#
|
|
21
|
+
# @example Check equality.
|
|
22
|
+
# enumerable == []
|
|
23
|
+
#
|
|
24
|
+
# @param [ Enumerable ] other The other enumerable.
|
|
25
|
+
#
|
|
26
|
+
# @return [ true, false ] If the objects are equal.
|
|
27
|
+
#
|
|
28
|
+
# @since 2.1.0
|
|
29
|
+
def ==(other)
|
|
30
|
+
return false unless other.respond_to?(:entries)
|
|
31
|
+
entries == other.entries
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Append a document to the enumerable.
|
|
35
|
+
#
|
|
36
|
+
# @example Append the document.
|
|
37
|
+
# enumerable << document
|
|
38
|
+
#
|
|
39
|
+
# @param [ Document ] document The document to append.
|
|
40
|
+
#
|
|
41
|
+
# @return [ Document ] The document.
|
|
42
|
+
#
|
|
43
|
+
# @since 2.1.0
|
|
44
|
+
def <<(document)
|
|
45
|
+
added << document
|
|
46
|
+
end
|
|
47
|
+
alias :push :<<
|
|
48
|
+
|
|
49
|
+
# Clears out all the documents in this enumerable. If passed a block it
|
|
50
|
+
# will yield to each document that is in memory.
|
|
51
|
+
#
|
|
52
|
+
# @example Clear out the enumerable.
|
|
53
|
+
# enumerable.clear
|
|
54
|
+
#
|
|
55
|
+
# @example Clear out the enumerable with a block.
|
|
56
|
+
# enumerable.clear do |doc|
|
|
57
|
+
# doc.unbind
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# @return [ Array<Document> ] The cleared out added docs.
|
|
61
|
+
#
|
|
62
|
+
# @since 2.1.0
|
|
63
|
+
def clear
|
|
64
|
+
if block_given?
|
|
65
|
+
in_memory { |doc| yield(doc) }
|
|
66
|
+
end
|
|
67
|
+
loaded.clear and added.clear
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Delete the supplied document from the enumerable.
|
|
71
|
+
#
|
|
72
|
+
# @example Delete the document.
|
|
73
|
+
# enumerable.delete(document)
|
|
74
|
+
#
|
|
75
|
+
# @param [ Document ] document The document to delete.
|
|
76
|
+
#
|
|
77
|
+
# @return [ Document ] The deleted document.
|
|
78
|
+
#
|
|
79
|
+
# @since 2.1.0
|
|
80
|
+
def delete(document)
|
|
81
|
+
(loaded.delete(document) || added.delete(document)).tap do |doc|
|
|
82
|
+
unless doc
|
|
83
|
+
if unloaded && unloaded.where(:_id => document.id).exists?
|
|
84
|
+
yield(document) if block_given?
|
|
85
|
+
return document
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
yield(doc) if block_given?
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Deletes every document in the enumerable for where the block returns
|
|
93
|
+
# true.
|
|
94
|
+
#
|
|
95
|
+
# @note This operation loads all documents from the database.
|
|
96
|
+
#
|
|
97
|
+
# @example Delete all matching documents.
|
|
98
|
+
# enumerable.delete_if do |doc|
|
|
99
|
+
# dod.id == id
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# @return [ Array<Document> ] The remaining docs.
|
|
103
|
+
#
|
|
104
|
+
# @since 2.1.0
|
|
105
|
+
def delete_if(&block)
|
|
106
|
+
load_all!
|
|
107
|
+
tap do
|
|
108
|
+
loaded.delete_if(&block)
|
|
109
|
+
added.delete_if(&block)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Iterating over this enumerable has to handle a few different
|
|
114
|
+
# scenarios.
|
|
115
|
+
#
|
|
116
|
+
# If the enumerable has its criteria loaded into memory then it yields
|
|
117
|
+
# to all the loaded docs and all the added docs.
|
|
118
|
+
#
|
|
119
|
+
# If the enumerable has not loaded the criteria then it iterates over
|
|
120
|
+
# the cursor while loading the documents and then iterates over the
|
|
121
|
+
# added docs.
|
|
122
|
+
#
|
|
123
|
+
# @example Iterate over the enumerable.
|
|
124
|
+
# enumerable.each do |doc|
|
|
125
|
+
# puts doc
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
# @return [ true ] That the enumerable is now loaded.
|
|
129
|
+
#
|
|
130
|
+
# @since 2.1.0
|
|
131
|
+
def each
|
|
132
|
+
if loaded?
|
|
133
|
+
loaded.each do |doc|
|
|
134
|
+
yield(doc)
|
|
135
|
+
end
|
|
136
|
+
else
|
|
137
|
+
unloaded.each do |doc|
|
|
138
|
+
loaded.push(doc)
|
|
139
|
+
yield(doc)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
added.each do |doc|
|
|
143
|
+
next unless doc.new?
|
|
144
|
+
yield(doc)
|
|
145
|
+
end
|
|
146
|
+
@executed = true
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Is the enumerable empty? Will determine if the count is zero based on
|
|
150
|
+
# whether or not it is loaded.
|
|
151
|
+
#
|
|
152
|
+
# @example Is the enumerable empty?
|
|
153
|
+
# enumerable.empty?
|
|
154
|
+
#
|
|
155
|
+
# @return [ true, false ] If the enumerable is empty.
|
|
156
|
+
#
|
|
157
|
+
# @since 2.1.0
|
|
158
|
+
def empty?
|
|
159
|
+
if loaded?
|
|
160
|
+
in_memory.count == 0
|
|
161
|
+
else
|
|
162
|
+
unloaded.count + added.count == 0
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Get the first document in the enumerable. Will check the persisted
|
|
167
|
+
# documents first. Does not load the entire enumerable.
|
|
168
|
+
#
|
|
169
|
+
# @example Get the first document.
|
|
170
|
+
# enumerable.first
|
|
171
|
+
#
|
|
172
|
+
# @return [ Document ] The first document found.
|
|
173
|
+
#
|
|
174
|
+
# @since 2.1.0
|
|
175
|
+
def first
|
|
176
|
+
(loaded? ? loaded.first : unloaded.first) || added.first
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Initialize the new enumerable either with a criteria or an array.
|
|
180
|
+
#
|
|
181
|
+
# @example Initialize the enumerable with a criteria.
|
|
182
|
+
# Enumberable.new(Post.where(:person_id => id))
|
|
183
|
+
#
|
|
184
|
+
# @example Initialize the enumerable with an array.
|
|
185
|
+
# Enumerable.new([ post ])
|
|
186
|
+
#
|
|
187
|
+
# @param [ Criteria, Array<Document> ] target The wrapped object.
|
|
188
|
+
#
|
|
189
|
+
# @since 2.1.0
|
|
190
|
+
def initialize(target)
|
|
191
|
+
if target.is_a?(Criteria)
|
|
192
|
+
@added, @loaded, @unloaded = [], [], target
|
|
193
|
+
else
|
|
194
|
+
@added, @executed, @loaded = [], true, target
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Inspection will just inspect the entries for nice array-style
|
|
199
|
+
# printing.
|
|
200
|
+
#
|
|
201
|
+
# @example Inspect the enumerable.
|
|
202
|
+
# enumerable.inspect
|
|
203
|
+
#
|
|
204
|
+
# @return [ String ] The inspected enum.
|
|
205
|
+
#
|
|
206
|
+
# @since 2.1.0
|
|
207
|
+
def inspect
|
|
208
|
+
entries.inspect
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Return all the documents in the enumerable that have been loaded or
|
|
212
|
+
# added.
|
|
213
|
+
#
|
|
214
|
+
# @note When passed a block it yields to each document.
|
|
215
|
+
#
|
|
216
|
+
# @example Get the in memory docs.
|
|
217
|
+
# enumerable.in_memory
|
|
218
|
+
#
|
|
219
|
+
# @return [ Array<Document> ] The in memory docs.
|
|
220
|
+
#
|
|
221
|
+
# @since 2.1.0
|
|
222
|
+
def in_memory
|
|
223
|
+
(loaded + added).tap do |docs|
|
|
224
|
+
docs.each { |doc| yield(doc) } if block_given?
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Get the last document in the enumerable. Will check the new
|
|
229
|
+
# documents first. Does not load the entire enumerable.
|
|
230
|
+
#
|
|
231
|
+
# @example Get the last document.
|
|
232
|
+
# enumerable.last
|
|
233
|
+
#
|
|
234
|
+
# @return [ Document ] The last document found.
|
|
235
|
+
#
|
|
236
|
+
# @since 2.1.0
|
|
237
|
+
def last
|
|
238
|
+
added.last || (loaded? ? loaded.last : unloaded.last)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Loads all the documents in the enumerable from the database.
|
|
242
|
+
#
|
|
243
|
+
# @example Load all the documents.
|
|
244
|
+
# enumerable.load_all!
|
|
245
|
+
#
|
|
246
|
+
# @return [ true ] That the enumerable is loaded.
|
|
247
|
+
#
|
|
248
|
+
# @since 2.1.0
|
|
249
|
+
alias :load_all! :entries
|
|
250
|
+
|
|
251
|
+
# Has the enumerable been loaded? This will be true if the criteria has
|
|
252
|
+
# been executed or we manually load the entire thing.
|
|
253
|
+
#
|
|
254
|
+
# @example Is the enumerable loaded?
|
|
255
|
+
# enumerable.loaded?
|
|
256
|
+
#
|
|
257
|
+
# @return [ true, false ] If the enumerable has been loaded.
|
|
258
|
+
#
|
|
259
|
+
# @since 2.1.0
|
|
260
|
+
def loaded?
|
|
261
|
+
!!@executed
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Reset the enumerable back to it's persisted state.
|
|
265
|
+
#
|
|
266
|
+
# @example Reset the enumerable.
|
|
267
|
+
# enumerable.reset
|
|
268
|
+
#
|
|
269
|
+
# @return [ false ] Always false.
|
|
270
|
+
#
|
|
271
|
+
# @since 2.1.0
|
|
272
|
+
def reset
|
|
273
|
+
loaded.clear and added.clear
|
|
274
|
+
@executed = false
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Does this enumerable respond to the provided method?
|
|
278
|
+
#
|
|
279
|
+
# @example Does the enumerable respond to the method?
|
|
280
|
+
# enumerable.respond_to?(:sum)
|
|
281
|
+
#
|
|
282
|
+
# @param [ String, Symbol ] name The name of the method.
|
|
283
|
+
# @param [ true, false ] include_private Whether to include private
|
|
284
|
+
# methods.
|
|
285
|
+
#
|
|
286
|
+
# @return [ true, false ] Whether the enumerable responds.
|
|
287
|
+
#
|
|
288
|
+
# @since 2.1.0
|
|
289
|
+
def respond_to?(name, include_private = false)
|
|
290
|
+
[].respond_to?(name, include_private) || super
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Gets the total size of this enumerable. This is a combination of all
|
|
294
|
+
# the persisted and unpersisted documents.
|
|
295
|
+
#
|
|
296
|
+
# @example Get the size.
|
|
297
|
+
# enumerable.size
|
|
298
|
+
#
|
|
299
|
+
# @return [ Integer ] The size of the enumerable.
|
|
300
|
+
#
|
|
301
|
+
# @since 2.1.0
|
|
302
|
+
def size
|
|
303
|
+
(loaded? ? loaded.count : unloaded.count) + added.count{ |d| d.new? }
|
|
304
|
+
end
|
|
305
|
+
alias :length :size
|
|
306
|
+
|
|
307
|
+
# Return all the unique documents in the enumerable.
|
|
308
|
+
#
|
|
309
|
+
# @note This operation loads all documents from the database.
|
|
310
|
+
#
|
|
311
|
+
# @example Get all the unique documents.
|
|
312
|
+
# enumerable.uniq
|
|
313
|
+
#
|
|
314
|
+
# @return [ Array<Document> ] The unique documents.
|
|
315
|
+
#
|
|
316
|
+
# @since 2.1.0
|
|
317
|
+
def uniq
|
|
318
|
+
entries.uniq
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
private
|
|
322
|
+
|
|
323
|
+
def method_missing(name, *args, &block)
|
|
324
|
+
entries.send(name, *args, &block)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
data/lib/mongoid/safety.rb
CHANGED
|
@@ -24,7 +24,29 @@ module Mongoid #:nodoc:
|
|
|
24
24
|
#
|
|
25
25
|
# @return [ Proxy ] The safety proxy.
|
|
26
26
|
def safely(safety = true)
|
|
27
|
-
|
|
27
|
+
tap { Threaded.safety_options = safety }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
|
|
32
|
+
# Static class method of easily getting the desired safe mode options
|
|
33
|
+
# from anywhere in the framework.
|
|
34
|
+
#
|
|
35
|
+
# @example Get the options with safe mode included.
|
|
36
|
+
# Safety.merge_safety_options({ :safe => false })
|
|
37
|
+
#
|
|
38
|
+
# @param [ Hash ] options The persistence options.
|
|
39
|
+
#
|
|
40
|
+
# @return [ Hash ] The options hash.
|
|
41
|
+
#
|
|
42
|
+
# @since 2.1.0
|
|
43
|
+
def merge_safety_options(options = {})
|
|
44
|
+
options ||= {}
|
|
45
|
+
return options if options[:safe]
|
|
46
|
+
options.merge!(
|
|
47
|
+
{ :safe => Threaded.safety_options || Mongoid.persist_in_safe_mode }
|
|
48
|
+
)
|
|
49
|
+
end
|
|
28
50
|
end
|
|
29
51
|
|
|
30
52
|
module ClassMethods #:nodoc:
|
|
@@ -46,161 +68,7 @@ module Mongoid #:nodoc:
|
|
|
46
68
|
#
|
|
47
69
|
# @return [ Proxy ] The safety proxy.
|
|
48
70
|
def safely(safety = true)
|
|
49
|
-
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# When this class proxies a document or class, the next persistence
|
|
54
|
-
# operation executed on it will query in safe mode.
|
|
55
|
-
#
|
|
56
|
-
# Operations that took a hash of attributes had to be somewhat duplicated
|
|
57
|
-
# here since we do not want to allow a :safe attribute to be included in
|
|
58
|
-
# the args. This is because safe could be a common attribute name and we
|
|
59
|
-
# don't want the collision between the attribute and determining whether or
|
|
60
|
-
# not safe mode is allowed.
|
|
61
|
-
class Proxy
|
|
62
|
-
|
|
63
|
-
attr_reader :target, :safety_options
|
|
64
|
-
|
|
65
|
-
# Create a new +Document+. This will instantiate a new document and
|
|
66
|
-
# insert it in a single call. Will always return the document
|
|
67
|
-
# whether save passed or not.
|
|
68
|
-
#
|
|
69
|
-
# @example Safely create a document.
|
|
70
|
-
# Person.safely.create(:title => "Mr")
|
|
71
|
-
#
|
|
72
|
-
# @param [ Hash ] attributes The attributes to create with.
|
|
73
|
-
#
|
|
74
|
-
# @return [ Document ] The new document.
|
|
75
|
-
def create(attributes = {})
|
|
76
|
-
target.new(attributes).tap { |doc| doc.insert(:safe => safety_options) }
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Create a new +Document+. This will instantiate a new document and
|
|
80
|
-
# insert it in a single call. Will always return the document
|
|
81
|
-
# whether save passed or not, and if validation fails an error will be
|
|
82
|
-
# raise.
|
|
83
|
-
#
|
|
84
|
-
# @example Safely create a document.
|
|
85
|
-
# Person.safely.create!(:title => "Mr")
|
|
86
|
-
#
|
|
87
|
-
# @param [ Hash ] attributes The attributes to create with.
|
|
88
|
-
#
|
|
89
|
-
# @raise [ Errors::Validations ] If validation failed.
|
|
90
|
-
#
|
|
91
|
-
# @return [ Document ] If validation passed.
|
|
92
|
-
def create!(attributes = {})
|
|
93
|
-
target.new(attributes).tap do |document|
|
|
94
|
-
fail_validate!(document) if document.insert(:safe => safety_options).errors.any?
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Delete all documents given the supplied conditions. If no conditions
|
|
99
|
-
# are passed, the entire collection will be dropped for performance
|
|
100
|
-
# benefits. Does not fire any callbacks.
|
|
101
|
-
#
|
|
102
|
-
# @example Delete all documents.
|
|
103
|
-
# Person.safely.delete_all
|
|
104
|
-
#
|
|
105
|
-
# @example Conditionally delete all documents.
|
|
106
|
-
# Person.safely.delete_all(:conditions => { :title => "Sir" })
|
|
107
|
-
#
|
|
108
|
-
# @param [ Hash ] conditions The conditions to delete with.
|
|
109
|
-
#
|
|
110
|
-
# @return [ Integer ] The number of documents deleted.
|
|
111
|
-
def delete_all(conditions = {})
|
|
112
|
-
Mongoid::Persistence::RemoveAll.new(
|
|
113
|
-
target,
|
|
114
|
-
{ :validate => false, :safe => safety_options },
|
|
115
|
-
conditions[:conditions] || {}
|
|
116
|
-
).persist
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# destroy all documents given the supplied conditions. If no conditions
|
|
120
|
-
# are passed, the entire collection will be dropped for performance
|
|
121
|
-
# benefits. Fires the destroy callbacks if conditions were passed.
|
|
122
|
-
#
|
|
123
|
-
# @example destroy all documents.
|
|
124
|
-
# Person.safely.destroy_all
|
|
125
|
-
#
|
|
126
|
-
# @example Conditionally destroy all documents.
|
|
127
|
-
# Person.safely.destroy_all(:conditions => { :title => "Sir" })
|
|
128
|
-
#
|
|
129
|
-
# @param [ Hash ] conditions The conditions to destroy with.
|
|
130
|
-
#
|
|
131
|
-
# @return [ Integer ] The number of documents destroyd.
|
|
132
|
-
def destroy_all(conditions = {})
|
|
133
|
-
documents = target.all(conditions)
|
|
134
|
-
documents.count.tap do |count|
|
|
135
|
-
documents.each { |doc| doc.destroy(:safe => safety_options) }
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Increment the field by the provided value, else if it doesn't exists set
|
|
140
|
-
# it to that value.
|
|
141
|
-
#
|
|
142
|
-
# @example Safely increment a field.
|
|
143
|
-
# person.safely.inc(:age, 1)
|
|
144
|
-
#
|
|
145
|
-
# @param [ Symbol, String ] field The field to increment.
|
|
146
|
-
# @param [ Integer ] value The value to increment by.
|
|
147
|
-
# @param [ Hash ] options Options to pass through to the driver.
|
|
148
|
-
def inc(field, value, options = {})
|
|
149
|
-
target.inc(field, value, :safe => safety_options)
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# Create the new +Proxy+.
|
|
153
|
-
#
|
|
154
|
-
# @example Create the proxy.
|
|
155
|
-
# Proxy.new(document, :w => 3)
|
|
156
|
-
#
|
|
157
|
-
# @param [ Document, Class ] target Either the class or the instance.
|
|
158
|
-
# @param [ true, Hash ] safety_options The options.
|
|
159
|
-
def initialize(target, safety_options)
|
|
160
|
-
@target = target
|
|
161
|
-
@safety_options = safety_options
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# We will use method missing to proxy calls to the target.
|
|
165
|
-
#
|
|
166
|
-
# @example Save safely.
|
|
167
|
-
# person.safely.save
|
|
168
|
-
#
|
|
169
|
-
# @param [ Array ] *args The arguments to pass on.
|
|
170
|
-
def method_missing(*args)
|
|
171
|
-
name = args[0]
|
|
172
|
-
attributes = args[1] || {}
|
|
173
|
-
target.send(name, attributes.merge(:safe => safety_options))
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Update the +Document+ attributes in the datbase.
|
|
177
|
-
#
|
|
178
|
-
# @example Safely update attributes.
|
|
179
|
-
# person.safely.update_attributes(:title => "Sir")
|
|
180
|
-
#
|
|
181
|
-
# @param [ Hash ] attributes The attributes to update.
|
|
182
|
-
#
|
|
183
|
-
# @return [ true, false ] Whether the document was saved.
|
|
184
|
-
def update_attributes(attributes = {})
|
|
185
|
-
target.write_attributes(attributes)
|
|
186
|
-
target.update(:safe => safety_options)
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
# Update the +Document+ attributes in the datbase.
|
|
190
|
-
#
|
|
191
|
-
# @example Safely update attributes.
|
|
192
|
-
# person.safely.update_attributes(:title => "Sir")
|
|
193
|
-
#
|
|
194
|
-
# @param [ Hash ] attributes The attributes to update.
|
|
195
|
-
#
|
|
196
|
-
# @raise [ Errors::Validations ] If validation failed.
|
|
197
|
-
#
|
|
198
|
-
# @return [ true ] If the document was saved.
|
|
199
|
-
def update_attributes!(attributes = {})
|
|
200
|
-
target.write_attributes(attributes)
|
|
201
|
-
update(:safe => safety_options).tap do |result|
|
|
202
|
-
target.class.fail_validate!(target) unless result
|
|
203
|
-
end
|
|
71
|
+
tap { Threaded.safety_options = safety }
|
|
204
72
|
end
|
|
205
73
|
end
|
|
206
74
|
end
|