mongoid 1.2.14 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mongoid.rb +10 -3
- data/lib/mongoid/associations.rb +133 -97
- data/lib/mongoid/associations/belongs_to_related.rb +2 -3
- data/lib/mongoid/associations/{belongs_to.rb → embedded_in.rb} +14 -6
- data/lib/mongoid/associations/{has_many.rb → embeds_many.rb} +89 -31
- data/lib/mongoid/associations/{has_one.rb → embeds_one.rb} +8 -7
- data/lib/mongoid/associations/has_many_related.rb +52 -7
- data/lib/mongoid/associations/has_one_related.rb +8 -4
- data/lib/mongoid/associations/meta_data.rb +2 -1
- data/lib/mongoid/associations/options.rb +6 -1
- data/lib/mongoid/associations/proxy.rb +14 -21
- data/lib/mongoid/attributes.rb +27 -12
- data/lib/mongoid/collection.rb +4 -3
- data/lib/mongoid/collections.rb +41 -0
- data/lib/mongoid/collections/master.rb +3 -2
- data/lib/mongoid/collections/slaves.rb +3 -2
- data/lib/mongoid/components.rb +21 -19
- data/lib/mongoid/concern.rb +31 -0
- data/lib/mongoid/config.rb +117 -12
- data/lib/mongoid/contexts.rb +1 -1
- data/lib/mongoid/contexts/enumerable.rb +1 -1
- data/lib/mongoid/contexts/mongo.rb +1 -1
- data/lib/mongoid/contexts/paging.rb +10 -2
- data/lib/mongoid/criterion/inclusion.rb +17 -0
- data/lib/mongoid/criterion/optional.rb +1 -1
- data/lib/mongoid/dirty.rb +253 -0
- data/lib/mongoid/document.rb +81 -52
- data/lib/mongoid/errors.rb +32 -1
- data/lib/mongoid/extensions.rb +11 -9
- data/lib/mongoid/extensions/big_decimal/conversions.rb +2 -2
- data/lib/mongoid/extensions/boolean/conversions.rb +8 -2
- data/lib/mongoid/extensions/date/conversions.rb +13 -4
- data/lib/mongoid/extensions/datetime/conversions.rb +1 -6
- data/lib/mongoid/extensions/float/conversions.rb +5 -1
- data/lib/mongoid/extensions/hash/assimilation.rb +12 -3
- data/lib/mongoid/extensions/hash/conversions.rb +34 -4
- data/lib/mongoid/extensions/integer/conversions.rb +5 -1
- data/lib/mongoid/extensions/nil/assimilation.rb +4 -0
- data/lib/mongoid/extensions/object/conversions.rb +1 -1
- data/lib/mongoid/extensions/string/conversions.rb +1 -1
- data/lib/mongoid/extensions/symbol/inflections.rb +1 -1
- data/lib/mongoid/extensions/time_conversions.rb +35 -0
- data/lib/mongoid/extras.rb +6 -9
- data/lib/mongoid/factory.rb +2 -1
- data/lib/mongoid/field.rb +9 -2
- data/lib/mongoid/fields.rb +1 -0
- data/lib/mongoid/identity.rb +3 -3
- data/lib/mongoid/indexes.rb +3 -3
- data/lib/mongoid/memoization.rb +8 -2
- data/lib/mongoid/named_scope.rb +0 -3
- data/lib/mongoid/observable.rb +30 -0
- data/lib/mongoid/paths.rb +62 -0
- data/lib/mongoid/persistence.rb +222 -0
- data/lib/mongoid/persistence/command.rb +39 -0
- data/lib/mongoid/persistence/insert.rb +50 -0
- data/lib/mongoid/persistence/insert_embedded.rb +38 -0
- data/lib/mongoid/persistence/remove.rb +39 -0
- data/lib/mongoid/persistence/remove_all.rb +37 -0
- data/lib/mongoid/persistence/remove_embedded.rb +50 -0
- data/lib/mongoid/persistence/update.rb +63 -0
- data/lib/mongoid/state.rb +28 -21
- data/lib/mongoid/timestamps.rb +5 -8
- data/lib/mongoid/version.rb +4 -0
- data/lib/mongoid/versioning.rb +6 -7
- metadata +81 -300
- data/.gitignore +0 -6
- data/.watchr +0 -29
- data/Rakefile +0 -53
- data/VERSION +0 -1
- data/caliper.yml +0 -4
- data/lib/mongoid/collections/mimic.rb +0 -46
- data/lib/mongoid/commands.rb +0 -174
- data/lib/mongoid/commands/create.rb +0 -21
- data/lib/mongoid/commands/delete.rb +0 -16
- data/lib/mongoid/commands/delete_all.rb +0 -23
- data/lib/mongoid/commands/deletion.rb +0 -18
- data/lib/mongoid/commands/destroy.rb +0 -19
- data/lib/mongoid/commands/destroy_all.rb +0 -23
- data/lib/mongoid/commands/save.rb +0 -27
- data/lib/mongoid/extensions/time/conversions.rb +0 -18
- data/mongoid.gemspec +0 -395
- data/perf/benchmark.rb +0 -77
- data/spec/integration/mongoid/associations_spec.rb +0 -340
- data/spec/integration/mongoid/attributes_spec.rb +0 -22
- data/spec/integration/mongoid/commands_spec.rb +0 -218
- data/spec/integration/mongoid/contexts/enumerable_spec.rb +0 -33
- data/spec/integration/mongoid/criteria_spec.rb +0 -272
- data/spec/integration/mongoid/document_spec.rb +0 -593
- data/spec/integration/mongoid/extensions_spec.rb +0 -26
- data/spec/integration/mongoid/finders_spec.rb +0 -119
- data/spec/integration/mongoid/inheritance_spec.rb +0 -137
- data/spec/integration/mongoid/named_scope_spec.rb +0 -46
- data/spec/models/address.rb +0 -39
- data/spec/models/animal.rb +0 -6
- data/spec/models/callbacks.rb +0 -18
- data/spec/models/comment.rb +0 -8
- data/spec/models/country_code.rb +0 -6
- data/spec/models/employer.rb +0 -5
- data/spec/models/game.rb +0 -7
- data/spec/models/inheritance.rb +0 -56
- data/spec/models/location.rb +0 -5
- data/spec/models/mixed_drink.rb +0 -4
- data/spec/models/name.rb +0 -13
- data/spec/models/namespacing.rb +0 -11
- data/spec/models/patient.rb +0 -4
- data/spec/models/person.rb +0 -99
- data/spec/models/pet.rb +0 -7
- data/spec/models/pet_owner.rb +0 -6
- data/spec/models/phone.rb +0 -7
- data/spec/models/post.rb +0 -15
- data/spec/models/translation.rb +0 -5
- data/spec/models/vet_visit.rb +0 -5
- data/spec/spec.opts +0 -3
- data/spec/spec_helper.rb +0 -31
- data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +0 -145
- data/spec/unit/mongoid/associations/belongs_to_spec.rb +0 -193
- data/spec/unit/mongoid/associations/has_many_related_spec.rb +0 -420
- data/spec/unit/mongoid/associations/has_many_spec.rb +0 -519
- data/spec/unit/mongoid/associations/has_one_related_spec.rb +0 -179
- data/spec/unit/mongoid/associations/has_one_spec.rb +0 -282
- data/spec/unit/mongoid/associations/meta_data_spec.rb +0 -88
- data/spec/unit/mongoid/associations/options_spec.rb +0 -192
- data/spec/unit/mongoid/associations_spec.rb +0 -595
- data/spec/unit/mongoid/attributes_spec.rb +0 -507
- data/spec/unit/mongoid/callbacks_spec.rb +0 -55
- data/spec/unit/mongoid/collection_spec.rb +0 -187
- data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +0 -75
- data/spec/unit/mongoid/collections/master_spec.rb +0 -41
- data/spec/unit/mongoid/collections/mimic_spec.rb +0 -43
- data/spec/unit/mongoid/collections/slaves_spec.rb +0 -81
- data/spec/unit/mongoid/commands/create_spec.rb +0 -30
- data/spec/unit/mongoid/commands/delete_all_spec.rb +0 -58
- data/spec/unit/mongoid/commands/delete_spec.rb +0 -38
- data/spec/unit/mongoid/commands/destroy_all_spec.rb +0 -23
- data/spec/unit/mongoid/commands/destroy_spec.rb +0 -50
- data/spec/unit/mongoid/commands/save_spec.rb +0 -105
- data/spec/unit/mongoid/commands_spec.rb +0 -275
- data/spec/unit/mongoid/config_spec.rb +0 -172
- data/spec/unit/mongoid/contexts/enumerable_spec.rb +0 -421
- data/spec/unit/mongoid/contexts/mongo_spec.rb +0 -682
- data/spec/unit/mongoid/contexts_spec.rb +0 -25
- data/spec/unit/mongoid/criteria_spec.rb +0 -824
- data/spec/unit/mongoid/criterion/complex_spec.rb +0 -19
- data/spec/unit/mongoid/criterion/exclusion_spec.rb +0 -91
- data/spec/unit/mongoid/criterion/inclusion_spec.rb +0 -219
- data/spec/unit/mongoid/criterion/optional_spec.rb +0 -319
- data/spec/unit/mongoid/cursor_spec.rb +0 -74
- data/spec/unit/mongoid/deprecation_spec.rb +0 -24
- data/spec/unit/mongoid/document_spec.rb +0 -952
- data/spec/unit/mongoid/errors_spec.rb +0 -103
- data/spec/unit/mongoid/extensions/array/accessors_spec.rb +0 -50
- data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +0 -24
- data/spec/unit/mongoid/extensions/array/conversions_spec.rb +0 -35
- data/spec/unit/mongoid/extensions/array/parentization_spec.rb +0 -20
- data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +0 -22
- data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +0 -22
- data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +0 -49
- data/spec/unit/mongoid/extensions/date/conversions_spec.rb +0 -102
- data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +0 -70
- data/spec/unit/mongoid/extensions/float/conversions_spec.rb +0 -61
- data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +0 -184
- data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +0 -46
- data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +0 -21
- data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +0 -17
- data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +0 -14
- data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +0 -61
- data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +0 -24
- data/spec/unit/mongoid/extensions/object/conversions_spec.rb +0 -57
- data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +0 -34
- data/spec/unit/mongoid/extensions/string/conversions_spec.rb +0 -17
- data/spec/unit/mongoid/extensions/string/inflections_spec.rb +0 -208
- data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +0 -91
- data/spec/unit/mongoid/extensions/time/conversions_spec.rb +0 -70
- data/spec/unit/mongoid/extras_spec.rb +0 -102
- data/spec/unit/mongoid/factory_spec.rb +0 -31
- data/spec/unit/mongoid/field_spec.rb +0 -143
- data/spec/unit/mongoid/fields_spec.rb +0 -181
- data/spec/unit/mongoid/finders_spec.rb +0 -404
- data/spec/unit/mongoid/identity_spec.rb +0 -109
- data/spec/unit/mongoid/indexes_spec.rb +0 -93
- data/spec/unit/mongoid/javascript_spec.rb +0 -48
- data/spec/unit/mongoid/matchers/all_spec.rb +0 -27
- data/spec/unit/mongoid/matchers/default_spec.rb +0 -27
- data/spec/unit/mongoid/matchers/exists_spec.rb +0 -56
- data/spec/unit/mongoid/matchers/gt_spec.rb +0 -39
- data/spec/unit/mongoid/matchers/gte_spec.rb +0 -49
- data/spec/unit/mongoid/matchers/in_spec.rb +0 -27
- data/spec/unit/mongoid/matchers/lt_spec.rb +0 -39
- data/spec/unit/mongoid/matchers/lte_spec.rb +0 -49
- data/spec/unit/mongoid/matchers/ne_spec.rb +0 -27
- data/spec/unit/mongoid/matchers/nin_spec.rb +0 -27
- data/spec/unit/mongoid/matchers/size_spec.rb +0 -27
- data/spec/unit/mongoid/matchers_spec.rb +0 -329
- data/spec/unit/mongoid/memoization_spec.rb +0 -75
- data/spec/unit/mongoid/named_scope_spec.rb +0 -123
- data/spec/unit/mongoid/scope_spec.rb +0 -240
- data/spec/unit/mongoid/state_spec.rb +0 -83
- data/spec/unit/mongoid/timestamps_spec.rb +0 -25
- data/spec/unit/mongoid/versioning_spec.rb +0 -41
- data/spec/unit/mongoid_spec.rb +0 -46
@@ -6,7 +6,7 @@ module Mongoid #:nodoc:
|
|
6
6
|
attr_reader :criteria
|
7
7
|
|
8
8
|
delegate :blank?, :empty?, :first, :last, :to => :execute
|
9
|
-
delegate :documents, :options, :selector, :to => :criteria
|
9
|
+
delegate :klass, :documents, :options, :selector, :to => :criteria
|
10
10
|
|
11
11
|
# Return aggregation counts of the grouped documents. This will count by
|
12
12
|
# the first field provided in the fields array.
|
@@ -133,7 +133,7 @@ module Mongoid #:nodoc:
|
|
133
133
|
# <tt>Mongoid::Contexts::Mongo.new(criteria)</tt>
|
134
134
|
def initialize(criteria)
|
135
135
|
@criteria = criteria
|
136
|
-
if klass.hereditary
|
136
|
+
if klass.hereditary
|
137
137
|
criteria.in(:_type => criteria.klass._types)
|
138
138
|
end
|
139
139
|
criteria.enslave if klass.enslaved?
|
@@ -6,12 +6,20 @@ module Mongoid #:nodoc:
|
|
6
6
|
#
|
7
7
|
# Example:
|
8
8
|
#
|
9
|
-
# <tt>context.paginate</tt>
|
9
|
+
# <tt>context.paginate(:page => 6, :per_page => 25)</tt>
|
10
10
|
#
|
11
11
|
# Returns:
|
12
12
|
#
|
13
13
|
# A collection of documents paginated.
|
14
|
-
|
14
|
+
# All previous <tt>limit</tt> and <tt>skip</tt> call will be ignored.
|
15
|
+
def paginate(pager_options={})
|
16
|
+
if pager_options[:per_page]
|
17
|
+
options[:limit] = pager_options[:per_page].to_i
|
18
|
+
if pager_options[:page]
|
19
|
+
options[:skip] = (pager_options[:page].to_i - 1) * pager_options[:per_page].to_i
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
@collection ||= execute(true)
|
16
24
|
WillPaginate::Collection.create(page, per_page, count) do |pager|
|
17
25
|
pager.replace(@collection.to_a)
|
@@ -64,6 +64,23 @@ module Mongoid #:nodoc:
|
|
64
64
|
end
|
65
65
|
alias :any_in :in
|
66
66
|
|
67
|
+
# Adds a criterion to the +Criteria+ that specifies values to do
|
68
|
+
# geospacial searches by. The field must be indexed with the "2d" option.
|
69
|
+
#
|
70
|
+
# Options:
|
71
|
+
#
|
72
|
+
# attributes: A +Hash+ where the keys are the field names and the values are
|
73
|
+
# +Arrays+ of [latitude, longitude] pairs.
|
74
|
+
#
|
75
|
+
# Example:
|
76
|
+
#
|
77
|
+
# <tt>criteria.near(:field1 => [30, -44])</tt>
|
78
|
+
#
|
79
|
+
# Returns: <tt>self</tt>
|
80
|
+
def near(attributes = {})
|
81
|
+
update_selector(attributes, "$near")
|
82
|
+
end
|
83
|
+
|
67
84
|
# Adds a criterion to the +Criteria+ that specifies values that must
|
68
85
|
# be matched in order to return results. This is similar to a SQL "WHERE"
|
69
86
|
# clause. This is the actual selector that will be provided to MongoDB,
|
@@ -0,0 +1,253 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Dirty #:nodoc:
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
module InstanceMethods #:nodoc:
|
6
|
+
# Gets the changes for a specific field.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# person = Person.new(:title => "Sir")
|
11
|
+
# person.title = "Madam"
|
12
|
+
# person.attribute_change("title") # [ "Sir", "Madam" ]
|
13
|
+
#
|
14
|
+
# Returns:
|
15
|
+
#
|
16
|
+
# An +Array+ containing the old and new values.
|
17
|
+
def attribute_change(name)
|
18
|
+
modifications[name]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Determines if a specific field has chaged.
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
#
|
25
|
+
# person = Person.new(:title => "Sir")
|
26
|
+
# person.title = "Madam"
|
27
|
+
# person.attribute_changed?("title") # true
|
28
|
+
#
|
29
|
+
# Returns:
|
30
|
+
#
|
31
|
+
# +true+ if changed, +false+ if not.
|
32
|
+
def attribute_changed?(name)
|
33
|
+
modifications.include?(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Gets the old value for a specific field.
|
37
|
+
#
|
38
|
+
# Example:
|
39
|
+
#
|
40
|
+
# person = Person.new(:title => "Sir")
|
41
|
+
# person.title = "Madam"
|
42
|
+
# person.attribute_was("title") # "Sir"
|
43
|
+
#
|
44
|
+
# Returns:
|
45
|
+
#
|
46
|
+
# The old field value.
|
47
|
+
def attribute_was(name)
|
48
|
+
change = modifications[name]
|
49
|
+
change ? change[0] : nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Gets the names of all the fields that have changed in the document.
|
53
|
+
#
|
54
|
+
# Example:
|
55
|
+
#
|
56
|
+
# person = Person.new(:title => "Sir")
|
57
|
+
# person.title = "Madam"
|
58
|
+
# person.changed # returns [ "title" ]
|
59
|
+
#
|
60
|
+
# Returns:
|
61
|
+
#
|
62
|
+
# An +Array+ of changed field names.
|
63
|
+
def changed
|
64
|
+
modifications.keys
|
65
|
+
end
|
66
|
+
|
67
|
+
# Alerts to whether the document has been modified or not.
|
68
|
+
#
|
69
|
+
# Example:
|
70
|
+
#
|
71
|
+
# person = Person.new(:title => "Sir")
|
72
|
+
# person.title = "Madam"
|
73
|
+
# person.changed? # returns true
|
74
|
+
#
|
75
|
+
# Returns:
|
76
|
+
#
|
77
|
+
# +true+ if changed, +false+ if not.
|
78
|
+
def changed?
|
79
|
+
!modifications.empty?
|
80
|
+
end
|
81
|
+
|
82
|
+
# Gets all the modifications that have happened to the object as a +Hash+
|
83
|
+
# with the keys being the names of the fields, and the values being an
|
84
|
+
# +Array+ with the old value and new value.
|
85
|
+
#
|
86
|
+
# Example:
|
87
|
+
#
|
88
|
+
# person = Person.new(:title => "Sir")
|
89
|
+
# person.title = "Madam"
|
90
|
+
# person.changes # returns { "title" => [ "Sir", "Madam" ] }
|
91
|
+
#
|
92
|
+
# Returns:
|
93
|
+
#
|
94
|
+
# A +Hash+ of changes.
|
95
|
+
def changes
|
96
|
+
modifications
|
97
|
+
end
|
98
|
+
|
99
|
+
# Call this method after save, so the changes can be properly switched.
|
100
|
+
#
|
101
|
+
# Example:
|
102
|
+
#
|
103
|
+
# <tt>person.move_changes</tt>
|
104
|
+
def move_changes
|
105
|
+
@previous_modifications = modifications.dup
|
106
|
+
@modifications = {}
|
107
|
+
end
|
108
|
+
|
109
|
+
# Gets all the new values for each of the changed fields, to be passed to
|
110
|
+
# a MongoDB $set modifier.
|
111
|
+
#
|
112
|
+
# Example:
|
113
|
+
#
|
114
|
+
# person = Person.new(:title => "Sir")
|
115
|
+
# person.title = "Madam"
|
116
|
+
# person.setters # returns { "title" => "Madam" }
|
117
|
+
#
|
118
|
+
# Returns:
|
119
|
+
#
|
120
|
+
# A +Hash+ of new values.
|
121
|
+
def setters
|
122
|
+
modifications.inject({}) do |sets, (field, changes)|
|
123
|
+
key = embedded? ? "#{_position}.#{field}" : field
|
124
|
+
sets[key] = changes[1]; sets
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Gets all the modifications that have happened to the object before the
|
129
|
+
# object was saved.
|
130
|
+
#
|
131
|
+
# Example:
|
132
|
+
#
|
133
|
+
# person = Person.new(:title => "Sir")
|
134
|
+
# person.title = "Madam"
|
135
|
+
# person.save!
|
136
|
+
# person.previous_changes # returns { "title" => [ "Sir", "Madam" ] }
|
137
|
+
#
|
138
|
+
# Returns:
|
139
|
+
#
|
140
|
+
# A +Hash+ of changes before save.
|
141
|
+
def previous_changes
|
142
|
+
@previous_modifications
|
143
|
+
end
|
144
|
+
|
145
|
+
# Resets a changed field back to its old value.
|
146
|
+
#
|
147
|
+
# Example:
|
148
|
+
#
|
149
|
+
# person = Person.new(:title => "Sir")
|
150
|
+
# person.title = "Madam"
|
151
|
+
# person.reset_attribute!("title")
|
152
|
+
# person.title # "Sir"
|
153
|
+
#
|
154
|
+
# Returns:
|
155
|
+
#
|
156
|
+
# The old field value.
|
157
|
+
def reset_attribute!(name)
|
158
|
+
value = attribute_was(name)
|
159
|
+
if value
|
160
|
+
@attributes[name] = value
|
161
|
+
modifications.delete(name)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Sets up the modifications hash. This occurs just after the document is
|
166
|
+
# instantiated.
|
167
|
+
#
|
168
|
+
# Example:
|
169
|
+
#
|
170
|
+
# <tt>document.setup_notifications</tt>
|
171
|
+
def setup_modifications
|
172
|
+
@accessed ||= {}
|
173
|
+
@modifications ||= {}
|
174
|
+
@previous_modifications ||= {}
|
175
|
+
end
|
176
|
+
|
177
|
+
# Reset all modifications for the document. This will wipe all the marked
|
178
|
+
# changes, but not reset the values.
|
179
|
+
#
|
180
|
+
# Example:
|
181
|
+
#
|
182
|
+
# <tt>document.reset_modifications</tt>
|
183
|
+
def reset_modifications
|
184
|
+
@accessed = {}
|
185
|
+
@modifications = {}
|
186
|
+
end
|
187
|
+
|
188
|
+
protected
|
189
|
+
|
190
|
+
# Audit the original value for a field that can be modified in place.
|
191
|
+
#
|
192
|
+
# Example:
|
193
|
+
#
|
194
|
+
# <tt>person.accessed("aliases", [ "007" ])</tt>
|
195
|
+
def accessed(name, value)
|
196
|
+
@accessed ||= {}
|
197
|
+
@accessed[name] = value.dup if (value.is_a?(Array) || value.is_a?(Hash)) && !@accessed.has_key?(name)
|
198
|
+
value
|
199
|
+
end
|
200
|
+
|
201
|
+
# Get all normal modifications plus in place potential changes.
|
202
|
+
#
|
203
|
+
# Example:
|
204
|
+
#
|
205
|
+
# <tt>person.modifications</tt>
|
206
|
+
#
|
207
|
+
# Returns:
|
208
|
+
#
|
209
|
+
# All changes to the document.
|
210
|
+
def modifications
|
211
|
+
@accessed.each_pair do |field, value|
|
212
|
+
current = @attributes[field]
|
213
|
+
@modifications[field] = [ value, current ] if current != value
|
214
|
+
end
|
215
|
+
@accessed.clear
|
216
|
+
@modifications
|
217
|
+
end
|
218
|
+
|
219
|
+
# Audit the change of a field's value.
|
220
|
+
#
|
221
|
+
# Example:
|
222
|
+
#
|
223
|
+
# <tt>person.modify("name", "Jack", "John")</tt>
|
224
|
+
def modify(name, old_value, new_value)
|
225
|
+
@attributes[name] = new_value
|
226
|
+
if @modifications && (old_value != new_value)
|
227
|
+
original = @modifications[name].first if @modifications[name]
|
228
|
+
@modifications[name] = [ (original || old_value), new_value ]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
module ClassMethods #:nodoc:
|
234
|
+
# Add the dynamic dirty methods. These are custom methods defined on a
|
235
|
+
# field by field basis that wrap the dirty attribute methods.
|
236
|
+
#
|
237
|
+
# Example:
|
238
|
+
#
|
239
|
+
# person = Person.new(:title => "Sir")
|
240
|
+
# person.title = "Madam"
|
241
|
+
# person.title_change # [ "Sir", "Madam" ]
|
242
|
+
# person.title_changed? # true
|
243
|
+
# person.title_was # "Sir"
|
244
|
+
# person.reset_title!
|
245
|
+
def add_dirty_methods(name)
|
246
|
+
define_method("#{name}_change") { attribute_change(name) }
|
247
|
+
define_method("#{name}_changed?") { attribute_changed?(name) }
|
248
|
+
define_method("#{name}_was") { attribute_was(name) }
|
249
|
+
define_method("reset_#{name}!") { reset_attribute!(name) }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
data/lib/mongoid/document.rb
CHANGED
@@ -1,23 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Mongoid #:nodoc:
|
3
3
|
module Document
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
extend ClassMethods
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include Mongoid::Components
|
6
|
+
included do
|
7
|
+
include Mongoid::Components
|
9
8
|
|
10
|
-
|
9
|
+
cattr_accessor :primary_key, :hereditary
|
10
|
+
self.hereditary = false
|
11
11
|
|
12
|
-
|
13
|
-
self.hereditary = false
|
14
|
-
self.collection_name = self.name.collectionize
|
12
|
+
attr_accessor :association_name, :_parent
|
15
13
|
|
16
|
-
|
17
|
-
attr_reader :new_record
|
18
|
-
|
19
|
-
delegate :collection, :db, :embedded, :primary_key, :to => "self.class"
|
20
|
-
end
|
14
|
+
delegate :db, :primary_key, :to => "self.class"
|
21
15
|
end
|
22
16
|
|
23
17
|
module ClassMethods
|
@@ -26,17 +20,6 @@ module Mongoid #:nodoc:
|
|
26
20
|
collection.db
|
27
21
|
end
|
28
22
|
|
29
|
-
# Returns the collection associated with this +Document+. If the
|
30
|
-
# document is embedded, there will be no collection associated
|
31
|
-
# with it.
|
32
|
-
#
|
33
|
-
# Returns: <tt>Mongo::Collection</tt>
|
34
|
-
def collection
|
35
|
-
raise Errors::InvalidCollection.new(self) if embedded
|
36
|
-
self._collection ||= Mongoid::Collection.new(self, self.collection_name)
|
37
|
-
add_indexes; self._collection
|
38
|
-
end
|
39
|
-
|
40
23
|
# Perform default behavior but mark the hierarchy as being hereditary.
|
41
24
|
def inherited(subclass)
|
42
25
|
super(subclass)
|
@@ -63,6 +46,7 @@ module Mongoid #:nodoc:
|
|
63
46
|
if attributes["_id"] || allocating
|
64
47
|
document = allocate
|
65
48
|
document.instance_variable_set(:@attributes, attributes)
|
49
|
+
document.setup_modifications
|
66
50
|
return document
|
67
51
|
else
|
68
52
|
return new(attrs)
|
@@ -85,30 +69,31 @@ module Mongoid #:nodoc:
|
|
85
69
|
before_save :identify
|
86
70
|
end
|
87
71
|
|
88
|
-
# Macro for setting the collection name to store in.
|
89
|
-
#
|
90
|
-
# Example:
|
91
|
-
#
|
92
|
-
# <tt>Person.store_in :populdation</tt>
|
93
|
-
def store_in(name)
|
94
|
-
self.collection_name = name.to_s
|
95
|
-
self._collection = Mongoid::Collection.new(self, name.to_s)
|
96
|
-
end
|
97
|
-
|
98
72
|
# Returns all types to query for when using this class as the base.
|
99
73
|
def _types
|
100
74
|
@_type ||= (self.subclasses + [ self.name ])
|
101
75
|
end
|
102
76
|
|
77
|
+
# return the list of subclassses for an object
|
78
|
+
def subclasses_of(*superclasses) #:nodoc:
|
79
|
+
subclasses = []
|
80
|
+
superclasses.each do |sup|
|
81
|
+
ObjectSpace.each_object(class << sup; self; end) do |k|
|
82
|
+
if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
|
83
|
+
subclasses << k
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
subclasses
|
88
|
+
end
|
103
89
|
end
|
104
90
|
|
105
91
|
module InstanceMethods
|
106
|
-
# Performs equality checking on the
|
107
|
-
#
|
92
|
+
# Performs equality checking on the document ids. For more robust
|
93
|
+
# equality checking please override this method.
|
108
94
|
def ==(other)
|
109
95
|
return false unless other.is_a?(Document)
|
110
|
-
|
111
|
-
other.attributes.except(:modified_at).except(:created_at)
|
96
|
+
id == other.id
|
112
97
|
end
|
113
98
|
|
114
99
|
# Delegates to ==
|
@@ -122,6 +107,15 @@ module Mongoid #:nodoc:
|
|
122
107
|
id.hash
|
123
108
|
end
|
124
109
|
|
110
|
+
# Is inheritance in play here?
|
111
|
+
#
|
112
|
+
# Returns:
|
113
|
+
#
|
114
|
+
# <tt>true</tt> if inheritance used, <tt>false</tt> if not.
|
115
|
+
def hereditary?
|
116
|
+
!!self.hereditary
|
117
|
+
end
|
118
|
+
|
125
119
|
# Introduces a child object into the +Document+ object graph. This will
|
126
120
|
# set up the relationships between the parent and child and update the
|
127
121
|
# attributes of the parent +Document+.
|
@@ -155,33 +149,35 @@ module Mongoid #:nodoc:
|
|
155
149
|
# an empty +Hash+.
|
156
150
|
#
|
157
151
|
# If a primary key is defined, the document's id will be set to that key,
|
158
|
-
# otherwise it will be set to a fresh +
|
152
|
+
# otherwise it will be set to a fresh +BSON::ObjectID+ string.
|
159
153
|
#
|
160
154
|
# Options:
|
161
155
|
#
|
162
156
|
# attrs: The attributes +Hash+ to set up the document with.
|
163
157
|
def initialize(attrs = nil)
|
164
|
-
@attributes =
|
158
|
+
@attributes = default_attributes
|
165
159
|
process(attrs)
|
166
|
-
@
|
167
|
-
@new_record = true if id.nil?
|
160
|
+
@new_record = true
|
168
161
|
document = yield self if block_given?
|
169
162
|
identify
|
170
163
|
end
|
171
164
|
|
172
165
|
# Returns the class name plus its attributes.
|
173
166
|
def inspect
|
174
|
-
attrs = fields.map { |name, field| "#{name}: #{@attributes[name].inspect}" }
|
175
|
-
|
167
|
+
attrs = fields.map { |name, field| "#{name}: #{@attributes[name].inspect}" }
|
168
|
+
if Mongoid.allow_dynamic_fields
|
169
|
+
dynamic_keys = @attributes.keys - fields.keys - associations.keys - ["_id", "_type"]
|
170
|
+
attrs += dynamic_keys.map { |name| "#{name}: #{@attributes[name].inspect}" }
|
171
|
+
end
|
172
|
+
"#<#{self.class.name} _id: #{id}, #{attrs * ', '}>"
|
176
173
|
end
|
177
174
|
|
178
|
-
#
|
175
|
+
# Notify observers of an update.
|
179
176
|
#
|
180
177
|
# Example:
|
181
178
|
#
|
182
179
|
# <tt>person.notify</tt>
|
183
180
|
def notify
|
184
|
-
changed(true)
|
185
181
|
notify_observers(self)
|
186
182
|
end
|
187
183
|
|
@@ -210,8 +206,12 @@ module Mongoid #:nodoc:
|
|
210
206
|
|
211
207
|
# Reloads the +Document+ attributes from the database.
|
212
208
|
def reload
|
213
|
-
|
214
|
-
|
209
|
+
reloaded = collection.find_one(:_id => id)
|
210
|
+
if Mongoid.raise_not_found_error
|
211
|
+
raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
|
212
|
+
end
|
213
|
+
@attributes = {}.merge(reloaded || {})
|
214
|
+
self.associations.keys.each { |association_name| unmemoize(association_name) }; self
|
215
215
|
end
|
216
216
|
|
217
217
|
# Remove a child document from this parent +Document+. Will reset the
|
@@ -246,6 +246,30 @@ module Mongoid #:nodoc:
|
|
246
246
|
attributes.to_json(options)
|
247
247
|
end
|
248
248
|
|
249
|
+
# Return an object to be encoded into a JSON string.
|
250
|
+
# Used by Rails 3's object->JSON chain to create JSON
|
251
|
+
# in a backend-agnostic way
|
252
|
+
#
|
253
|
+
# Example:
|
254
|
+
#
|
255
|
+
# <tt>person.as_json</tt>
|
256
|
+
def as_json(options = nil)
|
257
|
+
attributes
|
258
|
+
end
|
259
|
+
|
260
|
+
# Return this document as an object to be encoded as JSON,
|
261
|
+
# with any particular items modified on a per-encoder basis.
|
262
|
+
# Nothing special is required here since Mongoid bubbles up
|
263
|
+
# all the child associations to the parent attribute +Hash+
|
264
|
+
# using observers throughout the +Document+ lifecycle.
|
265
|
+
#
|
266
|
+
# Example:
|
267
|
+
#
|
268
|
+
# <tt>person.encode_json(encoder)</tt>
|
269
|
+
def encode_json(encoder)
|
270
|
+
attributes
|
271
|
+
end
|
272
|
+
|
249
273
|
# Returns the id of the Document, used in Rails compatibility.
|
250
274
|
def to_param
|
251
275
|
id
|
@@ -262,20 +286,25 @@ module Mongoid #:nodoc:
|
|
262
286
|
#
|
263
287
|
# This will also cause the observing +Document+ to notify it's parent if
|
264
288
|
# there is any.
|
265
|
-
def
|
289
|
+
def observe(child, clear = false)
|
266
290
|
name = child.association_name
|
267
291
|
attrs = child.instance_variable_get(:@attributes)
|
268
|
-
clear
|
292
|
+
if clear
|
293
|
+
@attributes.delete(name)
|
294
|
+
else
|
295
|
+
@attributes.insert(name, attrs) unless @attributes[name] && @attributes[name].include?(attrs)
|
296
|
+
end
|
269
297
|
notify
|
270
298
|
end
|
271
299
|
|
272
300
|
protected
|
273
301
|
# apply default values to attributes - calling procs as required
|
274
302
|
def attributes_with_defaults(attributes = {})
|
275
|
-
default_values = defaults
|
303
|
+
default_values = defaults
|
276
304
|
default_values.each_pair do |key, val|
|
277
305
|
default_values[key] = val.call if val.respond_to?(:call)
|
278
306
|
end
|
307
|
+
default_values.merge(attributes)
|
279
308
|
end
|
280
309
|
end
|
281
310
|
end
|