mongo_mapper-unstable 2009.12.30 → 2010.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -17
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/mongo_mapper/associations/base.rb +19 -10
- data/lib/mongo_mapper/associations/in_array_proxy.rb +137 -0
- data/lib/mongo_mapper/associations/one_proxy.rb +64 -0
- data/lib/mongo_mapper/associations/proxy.rb +7 -4
- data/lib/mongo_mapper/associations.rb +11 -3
- data/lib/mongo_mapper/callbacks.rb +30 -78
- data/lib/mongo_mapper/dirty.rb +5 -24
- data/lib/mongo_mapper/document.rb +117 -144
- data/lib/mongo_mapper/embedded_document.rb +7 -11
- data/lib/mongo_mapper/finder_options.rb +13 -21
- data/lib/mongo_mapper/mongo_mapper.rb +125 -0
- data/lib/mongo_mapper/pagination.rb +12 -1
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +1 -0
- data/lib/mongo_mapper/serialization.rb +2 -2
- data/lib/mongo_mapper/serializers/json_serializer.rb +2 -46
- data/lib/mongo_mapper/support.rb +2 -2
- data/lib/mongo_mapper.rb +8 -2
- data/mongo_mapper.gemspec +14 -8
- data/specs.watchr +3 -5
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +8 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +54 -9
- data/test/functional/associations/test_in_array_proxy.rb +309 -0
- data/test/functional/associations/test_many_documents_proxy.rb +103 -53
- data/test/functional/associations/test_many_embedded_proxy.rb +4 -14
- data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -1
- data/test/functional/associations/test_one_proxy.rb +149 -0
- data/test/functional/test_binary.rb +13 -4
- data/test/functional/test_callbacks.rb +1 -5
- data/test/functional/test_dirty.rb +1 -4
- data/test/functional/test_document.rb +576 -640
- data/test/functional/test_embedded_document.rb +7 -20
- data/test/functional/test_modifiers.rb +238 -0
- data/test/functional/test_pagination.rb +1 -3
- data/test/functional/test_string_id_compatibility.rb +3 -8
- data/test/functional/test_validations.rb +13 -75
- data/test/models.rb +1 -1
- data/test/support/timing.rb +1 -1
- data/test/test_helper.rb +28 -0
- data/test/unit/associations/test_base.rb +54 -13
- data/test/unit/associations/test_proxy.rb +12 -0
- data/test/unit/test_document.rb +36 -26
- data/test/unit/test_embedded_document.rb +14 -51
- data/test/unit/test_finder_options.rb +20 -7
- data/test/unit/test_key.rb +1 -4
- data/test/unit/test_pagination.rb +6 -0
- data/test/unit/test_rails_compatibility.rb +4 -1
- data/test/unit/test_serializations.rb +1 -2
- data/test/unit/test_support.rb +4 -0
- data/test/unit/test_time_zones.rb +1 -2
- data/test/unit/test_validations.rb +3 -14
- metadata +12 -6
- data/lib/mongo_mapper/observing.rb +0 -50
- data/test/unit/test_observing.rb +0 -101
data/README.rdoc
CHANGED
@@ -13,20 +13,9 @@ Releases are tagged on github and released on gemcutter. Master is pushed to whe
|
|
13
13
|
* Send me a pull request. Bonus points for topic branches.
|
14
14
|
|
15
15
|
== Install
|
16
|
-
|
17
|
-
MongoMapper is only released on gemcutter. To install, you can setup gemcutter as your default gem source.
|
18
|
-
|
19
|
-
$ gem install gemcutter
|
20
|
-
$ gem tumble
|
21
|
-
|
22
|
-
Then you can install MM:
|
23
16
|
|
24
17
|
$ gem install mongo_mapper
|
25
18
|
|
26
|
-
You can also just declare the source:
|
27
|
-
|
28
|
-
$ gem install mongo_mapper -s http://gemcutter.org
|
29
|
-
|
30
19
|
== Dependencies
|
31
20
|
|
32
21
|
* ActiveSupport (typically the latest version)
|
@@ -37,16 +26,12 @@ You can also just declare the source:
|
|
37
26
|
|
38
27
|
Documentation is lacking right now because if you can't look through the code right now and feel comfortable, this is probably too young for you to use. Wait til it stabilizes a bit more.
|
39
28
|
|
40
|
-
http://rdoc.info/projects/jnunemaker/mongomapper
|
41
29
|
http://groups.google.com/group/mongomapper
|
42
30
|
|
43
31
|
== More Info
|
44
32
|
|
45
|
-
|
46
|
-
http://
|
47
|
-
|
48
|
-
You can learn more about the mongo ruby driver here:
|
49
|
-
http://github.com/mongodb/mongo-ruby-driver/tree/master
|
33
|
+
* http://www.mongodb.org/
|
34
|
+
* http://github.com/mongodb/mongo-ruby-driver/
|
50
35
|
|
51
36
|
== Copyright
|
52
37
|
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ Jeweler::Tasks.new do |gem|
|
|
12
12
|
gem.authors = ["John Nunemaker"]
|
13
13
|
|
14
14
|
gem.add_dependency('activesupport', '>= 2.3')
|
15
|
-
gem.add_dependency('mongo', '0.18.
|
15
|
+
gem.add_dependency('mongo', '0.18.2')
|
16
16
|
gem.add_dependency('jnunemaker-validatable', '1.8.1')
|
17
17
|
|
18
18
|
gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2010.01.04
|
@@ -1,13 +1,10 @@
|
|
1
1
|
module MongoMapper
|
2
2
|
module Associations
|
3
|
-
# Base class for keeping track of associations.
|
4
|
-
#
|
5
|
-
# @private
|
6
3
|
class Base
|
7
4
|
attr_reader :type, :name, :options, :finder_options
|
8
5
|
|
9
6
|
# Options that should not be considered MongoDB query options/criteria
|
10
|
-
AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :polymorphic]
|
7
|
+
AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic]
|
11
8
|
|
12
9
|
def initialize(type, name, options={}, &extension)
|
13
10
|
@type, @name, @options, @finder_options = type, name, {}, {}
|
@@ -47,6 +44,10 @@ module MongoMapper
|
|
47
44
|
def belongs_to?
|
48
45
|
@belongs_to_type ||= @type == :belongs_to
|
49
46
|
end
|
47
|
+
|
48
|
+
def one?
|
49
|
+
@one_type ||= @type == :one
|
50
|
+
end
|
50
51
|
|
51
52
|
def polymorphic?
|
52
53
|
!!@options[:polymorphic]
|
@@ -55,6 +56,14 @@ module MongoMapper
|
|
55
56
|
def as?
|
56
57
|
!!@options[:as]
|
57
58
|
end
|
59
|
+
|
60
|
+
def in_array?
|
61
|
+
!!@options[:in]
|
62
|
+
end
|
63
|
+
|
64
|
+
def embeddable?
|
65
|
+
many? && klass.embeddable?
|
66
|
+
end
|
58
67
|
|
59
68
|
def type_key_name
|
60
69
|
@type_key_name ||= many? ? '_type' : "#{as}_type"
|
@@ -72,10 +81,6 @@ module MongoMapper
|
|
72
81
|
@ivar ||= "@_#{name}"
|
73
82
|
end
|
74
83
|
|
75
|
-
def embeddable?
|
76
|
-
many? && klass.embeddable?
|
77
|
-
end
|
78
|
-
|
79
84
|
def proxy_class
|
80
85
|
@proxy_class ||= begin
|
81
86
|
if many?
|
@@ -86,15 +91,19 @@ module MongoMapper
|
|
86
91
|
ManyPolymorphicProxy
|
87
92
|
elsif as?
|
88
93
|
ManyDocumentsAsProxy
|
94
|
+
elsif in_array?
|
95
|
+
InArrayProxy
|
89
96
|
else
|
90
97
|
ManyDocumentsProxy
|
91
98
|
end
|
92
99
|
end
|
100
|
+
elsif one?
|
101
|
+
OneProxy
|
93
102
|
else
|
94
103
|
polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
|
95
104
|
end
|
96
|
-
end
|
97
|
-
end
|
105
|
+
end
|
106
|
+
end
|
98
107
|
|
99
108
|
private
|
100
109
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
class InArrayProxy < Collection
|
4
|
+
include ::MongoMapper::Finders
|
5
|
+
|
6
|
+
def find(*args)
|
7
|
+
options = args.extract_options!
|
8
|
+
|
9
|
+
case args.first
|
10
|
+
when :first
|
11
|
+
first(options)
|
12
|
+
when :last
|
13
|
+
last(options)
|
14
|
+
when :all
|
15
|
+
all(options)
|
16
|
+
else
|
17
|
+
klass.find(*scoped_ids(args) << scoped_options(options))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find!(*args)
|
22
|
+
options = args.extract_options!
|
23
|
+
|
24
|
+
case args.first
|
25
|
+
when :first
|
26
|
+
first(options)
|
27
|
+
when :last
|
28
|
+
last(options)
|
29
|
+
when :all
|
30
|
+
all(options)
|
31
|
+
else
|
32
|
+
klass.find!(*scoped_ids(args) << scoped_options(options))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def paginate(options)
|
37
|
+
klass.paginate(scoped_options(options))
|
38
|
+
end
|
39
|
+
|
40
|
+
def all(options={})
|
41
|
+
klass.all(scoped_options(options))
|
42
|
+
end
|
43
|
+
|
44
|
+
def first(options={})
|
45
|
+
klass.first(scoped_options(options))
|
46
|
+
end
|
47
|
+
|
48
|
+
def last(options={})
|
49
|
+
klass.last(scoped_options(options))
|
50
|
+
end
|
51
|
+
|
52
|
+
def count(options={})
|
53
|
+
options.blank? ? ids.size : klass.count(scoped_options(options))
|
54
|
+
end
|
55
|
+
|
56
|
+
def destroy_all(options={})
|
57
|
+
all(options).each do |doc|
|
58
|
+
ids.delete(doc.id)
|
59
|
+
doc.destroy
|
60
|
+
end
|
61
|
+
reset
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete_all(options={})
|
65
|
+
docs = all(options.merge(:select => ['_id']))
|
66
|
+
docs.each { |doc| ids.delete(doc.id) }
|
67
|
+
klass.delete(docs.map(&:id))
|
68
|
+
reset
|
69
|
+
end
|
70
|
+
|
71
|
+
def nullify
|
72
|
+
replace([])
|
73
|
+
reset
|
74
|
+
end
|
75
|
+
|
76
|
+
def create(attrs={})
|
77
|
+
doc = klass.create(attrs)
|
78
|
+
unless doc.new?
|
79
|
+
ids << doc.id
|
80
|
+
owner.save
|
81
|
+
end
|
82
|
+
doc
|
83
|
+
end
|
84
|
+
|
85
|
+
def create!(attrs={})
|
86
|
+
doc = klass.create!(attrs)
|
87
|
+
unless doc.new?
|
88
|
+
ids << doc.id
|
89
|
+
owner.save
|
90
|
+
end
|
91
|
+
doc
|
92
|
+
end
|
93
|
+
|
94
|
+
def <<(*docs)
|
95
|
+
flatten_deeper(docs).each do |doc|
|
96
|
+
doc.save if doc.new?
|
97
|
+
unless ids.include?(doc.id)
|
98
|
+
ids << doc.id
|
99
|
+
end
|
100
|
+
end
|
101
|
+
reset
|
102
|
+
end
|
103
|
+
alias_method :push, :<<
|
104
|
+
alias_method :concat, :<<
|
105
|
+
|
106
|
+
def replace(docs)
|
107
|
+
doc_ids = docs.map do |doc|
|
108
|
+
doc.save if doc.new?
|
109
|
+
doc.id
|
110
|
+
end
|
111
|
+
ids.replace(doc_ids.uniq)
|
112
|
+
reset
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def scoped_conditions
|
117
|
+
{:_id => ids}
|
118
|
+
end
|
119
|
+
|
120
|
+
def scoped_options(options)
|
121
|
+
reflection.finder_options.merge(options).merge(scoped_conditions)
|
122
|
+
end
|
123
|
+
|
124
|
+
def scoped_ids(args)
|
125
|
+
args.flatten.reject { |id| !ids.include?(id) }
|
126
|
+
end
|
127
|
+
|
128
|
+
def find_target
|
129
|
+
ids.blank? ? [] : all
|
130
|
+
end
|
131
|
+
|
132
|
+
def ids
|
133
|
+
owner[options[:in]]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
class OneProxy < Proxy
|
4
|
+
def build(attrs={})
|
5
|
+
instantiate_target(:new, attrs)
|
6
|
+
end
|
7
|
+
|
8
|
+
def create(attrs={})
|
9
|
+
instantiate_target(:create, attrs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create!(attrs={})
|
13
|
+
instantiate_target(:create!, attrs)
|
14
|
+
end
|
15
|
+
|
16
|
+
def replace(doc)
|
17
|
+
load_target
|
18
|
+
|
19
|
+
if !target.nil? && target != doc
|
20
|
+
if options[:dependent] && !target.new?
|
21
|
+
case options[:dependent]
|
22
|
+
when :delete
|
23
|
+
target.delete
|
24
|
+
when :destroy
|
25
|
+
target.destroy
|
26
|
+
when :nullify
|
27
|
+
target[foreign_key] = nil
|
28
|
+
target.save
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
reset
|
34
|
+
|
35
|
+
unless doc.nil?
|
36
|
+
owner.save if owner.new?
|
37
|
+
doc[foreign_key] = owner.id
|
38
|
+
doc.save if doc.new?
|
39
|
+
loaded
|
40
|
+
@target = doc
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def find_target
|
46
|
+
target_class.first(reflection.finder_options.merge(foreign_key => owner.id))
|
47
|
+
end
|
48
|
+
|
49
|
+
def instantiate_target(instantiator, attrs={})
|
50
|
+
@target = target_class.send(instantiator, attrs.update(foreign_key => owner.id))
|
51
|
+
loaded
|
52
|
+
@target
|
53
|
+
end
|
54
|
+
|
55
|
+
def target_class
|
56
|
+
@target_class ||= options[:class] || (options[:class_name] || reflection.name.to_s.camelize).constantize
|
57
|
+
end
|
58
|
+
|
59
|
+
def foreign_key
|
60
|
+
options[:foreign_key] || owner.class.name.foreign_key
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -17,7 +17,7 @@ module MongoMapper
|
|
17
17
|
delegate :collection, :to => :klass
|
18
18
|
|
19
19
|
def initialize(owner, reflection)
|
20
|
-
@owner, @reflection = owner, reflection
|
20
|
+
@owner, @reflection, @loaded = owner, reflection, false
|
21
21
|
Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
|
22
22
|
reset
|
23
23
|
end
|
@@ -45,6 +45,11 @@ module MongoMapper
|
|
45
45
|
target.blank?
|
46
46
|
end
|
47
47
|
|
48
|
+
def present?
|
49
|
+
load_target
|
50
|
+
target.present?
|
51
|
+
end
|
52
|
+
|
48
53
|
def reload
|
49
54
|
reset
|
50
55
|
load_target
|
@@ -57,7 +62,7 @@ module MongoMapper
|
|
57
62
|
|
58
63
|
def reset
|
59
64
|
@loaded = false
|
60
|
-
target = nil
|
65
|
+
@target = nil
|
61
66
|
end
|
62
67
|
|
63
68
|
def respond_to?(*args)
|
@@ -101,8 +106,6 @@ module MongoMapper
|
|
101
106
|
raise NotImplementedError
|
102
107
|
end
|
103
108
|
|
104
|
-
# Array#flatten has problems with recursive arrays. Going one level
|
105
|
-
# deeper solves the majority of the problems.
|
106
109
|
def flatten_deeper(array)
|
107
110
|
array.collect do |element|
|
108
111
|
(element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element
|
@@ -3,12 +3,14 @@ module MongoMapper
|
|
3
3
|
module ClassMethods
|
4
4
|
def belongs_to(association_id, options={}, &extension)
|
5
5
|
create_association(:belongs_to, association_id, options, &extension)
|
6
|
-
self
|
7
6
|
end
|
8
7
|
|
9
8
|
def many(association_id, options={}, &extension)
|
10
9
|
create_association(:many, association_id, options, &extension)
|
11
|
-
|
10
|
+
end
|
11
|
+
|
12
|
+
def one(association_id, options={}, &extension)
|
13
|
+
create_association(:one, association_id, options, &extension)
|
12
14
|
end
|
13
15
|
|
14
16
|
def associations
|
@@ -38,6 +40,12 @@ module MongoMapper
|
|
38
40
|
value
|
39
41
|
end
|
40
42
|
|
43
|
+
if association.one? || association.belongs_to?
|
44
|
+
define_method("#{association.name}?") do
|
45
|
+
get_proxy(association).present?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
41
49
|
if association.options[:dependent] && association.many? && !association.embeddable?
|
42
50
|
after_destroy do |doc|
|
43
51
|
case association.options[:dependent]
|
@@ -61,7 +69,7 @@ module MongoMapper
|
|
61
69
|
def get_proxy(association)
|
62
70
|
unless proxy = self.instance_variable_get(association.ivar)
|
63
71
|
proxy = association.proxy_class.new(self, association)
|
64
|
-
self.instance_variable_set(association.ivar, proxy)
|
72
|
+
self.instance_variable_set(association.ivar, proxy)
|
65
73
|
end
|
66
74
|
proxy
|
67
75
|
end
|
@@ -1,108 +1,60 @@
|
|
1
1
|
module MongoMapper
|
2
|
-
# This module is mixed into the Document module to provide call-backs before
|
3
|
-
# and after the following events:
|
4
|
-
#
|
5
|
-
# * save
|
6
|
-
# * create
|
7
|
-
# * update
|
8
|
-
# * validation
|
9
|
-
# ** every validation
|
10
|
-
# ** validation when created
|
11
|
-
# ** validation when updated
|
12
|
-
# * destruction
|
13
|
-
#
|
14
|
-
# @see ActiveSupport::Callbacks
|
15
2
|
module Callbacks
|
16
|
-
def self.included(model)
|
3
|
+
def self.included(model)
|
17
4
|
model.class_eval do
|
18
|
-
extend Observable
|
19
5
|
include ActiveSupport::Callbacks
|
20
6
|
|
21
|
-
|
22
|
-
before_save
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
after_validation
|
30
|
-
before_validation_on_create
|
31
|
-
after_validation_on_create
|
32
|
-
before_validation_on_update
|
33
|
-
after_validation_on_update
|
34
|
-
before_destroy
|
35
|
-
after_destroy
|
7
|
+
define_callbacks(
|
8
|
+
:before_save, :after_save,
|
9
|
+
:before_create, :after_create,
|
10
|
+
:before_update, :after_update,
|
11
|
+
:before_validation, :after_validation,
|
12
|
+
:before_validation_on_create, :after_validation_on_create,
|
13
|
+
:before_validation_on_update, :after_validation_on_update,
|
14
|
+
:before_destroy, :after_destroy
|
36
15
|
)
|
37
|
-
|
38
|
-
define_callbacks(*callbacks)
|
39
|
-
|
40
|
-
callbacks.each do |callback|
|
41
|
-
define_method(callback.to_sym) {}
|
42
|
-
end
|
43
16
|
end
|
44
17
|
end
|
45
18
|
|
46
|
-
def valid?
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
19
|
+
def valid?
|
20
|
+
action = new? ? 'create' : 'update'
|
21
|
+
|
22
|
+
run_callbacks(:before_validation)
|
23
|
+
run_callbacks("before_validation_on_#{action}".to_sym)
|
51
24
|
result = super
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
25
|
+
run_callbacks("after_validation_on_#{action}".to_sym)
|
26
|
+
run_callbacks(:after_validation)
|
27
|
+
|
28
|
+
result
|
56
29
|
end
|
57
30
|
|
58
|
-
|
59
|
-
|
60
|
-
# if the +before_destroy+ call-back returns +false+.
|
61
|
-
#
|
62
|
-
# @return the result of calling +destroy+ on the document
|
63
|
-
def destroy #:nodoc:
|
64
|
-
return false if callback(:before_destroy) == false
|
31
|
+
def destroy
|
32
|
+
run_callbacks(:before_destroy)
|
65
33
|
result = super
|
66
|
-
|
34
|
+
run_callbacks(:after_destroy)
|
67
35
|
result
|
68
36
|
end
|
69
37
|
|
70
38
|
private
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
if result != false && respond_to?(method)
|
75
|
-
result = send(method)
|
76
|
-
end
|
77
|
-
|
78
|
-
notify(method)
|
79
|
-
return result
|
80
|
-
end
|
81
|
-
|
82
|
-
def notify(method) #:nodoc:
|
83
|
-
self.class.changed
|
84
|
-
self.class.notify_observers(method, self)
|
85
|
-
end
|
86
|
-
|
87
|
-
def create_or_update #:nodoc:
|
88
|
-
return false if callback(:before_save) == false
|
39
|
+
def create_or_update(*args)
|
40
|
+
run_callbacks(:before_save)
|
89
41
|
if result = super
|
90
|
-
|
42
|
+
run_callbacks(:after_save)
|
91
43
|
end
|
92
44
|
result
|
93
45
|
end
|
94
46
|
|
95
|
-
def create
|
96
|
-
|
47
|
+
def create(*args)
|
48
|
+
run_callbacks(:before_create)
|
97
49
|
result = super
|
98
|
-
|
50
|
+
run_callbacks(:after_create)
|
99
51
|
result
|
100
52
|
end
|
101
53
|
|
102
|
-
def update(*args)
|
103
|
-
|
54
|
+
def update(*args)
|
55
|
+
run_callbacks(:before_update)
|
104
56
|
result = super
|
105
|
-
|
57
|
+
run_callbacks(:after_update)
|
106
58
|
result
|
107
59
|
end
|
108
60
|
end
|
data/lib/mongo_mapper/dirty.rb
CHANGED
@@ -30,18 +30,10 @@ module MongoMapper
|
|
30
30
|
!changed_keys.empty?
|
31
31
|
end
|
32
32
|
|
33
|
-
# List of keys with unsaved changes.
|
34
|
-
# person.changed # => []
|
35
|
-
# person.name = 'bob'
|
36
|
-
# person.changed # => ['name']
|
37
33
|
def changed
|
38
34
|
changed_keys.keys
|
39
35
|
end
|
40
36
|
|
41
|
-
# Map of changed attrs => [original value, new value].
|
42
|
-
# person.changes # => {}
|
43
|
-
# person.name = 'bob'
|
44
|
-
# person.changes # => { 'name' => ['bill', 'bob'] }
|
45
37
|
def changes
|
46
38
|
changed.inject({}) { |h, attribute| h[attribute] = key_change(attribute); h }
|
47
39
|
end
|
@@ -51,7 +43,6 @@ module MongoMapper
|
|
51
43
|
changed_keys.clear unless new?
|
52
44
|
end
|
53
45
|
|
54
|
-
# Attempts to +save+ the record and clears changed keys if successful.
|
55
46
|
def save(*args)
|
56
47
|
if status = super
|
57
48
|
changed_keys.clear
|
@@ -59,19 +50,17 @@ module MongoMapper
|
|
59
50
|
status
|
60
51
|
end
|
61
52
|
|
62
|
-
# Attempts to <tt>save!</tt> the record and clears changed keys if successful.
|
63
53
|
def save!(*args)
|
64
54
|
status = super
|
65
55
|
changed_keys.clear
|
66
56
|
status
|
67
57
|
end
|
68
58
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
# end
|
59
|
+
def reload(*args)
|
60
|
+
record = super
|
61
|
+
changed_keys.clear
|
62
|
+
record
|
63
|
+
end
|
75
64
|
|
76
65
|
private
|
77
66
|
def clone_key_value(attribute_name)
|
@@ -81,36 +70,29 @@ module MongoMapper
|
|
81
70
|
value
|
82
71
|
end
|
83
72
|
|
84
|
-
# Map of change <tt>attr => original value</tt>.
|
85
73
|
def changed_keys
|
86
74
|
@changed_keys ||= {}
|
87
75
|
end
|
88
76
|
|
89
|
-
# Handle <tt>*_changed?</tt> for +method_missing+.
|
90
77
|
def key_changed?(attribute)
|
91
78
|
changed_keys.include?(attribute)
|
92
79
|
end
|
93
80
|
|
94
|
-
# Handle <tt>*_change</tt> for +method_missing+.
|
95
81
|
def key_change(attribute)
|
96
82
|
[changed_keys[attribute], __send__(attribute)] if key_changed?(attribute)
|
97
83
|
end
|
98
84
|
|
99
|
-
# Handle <tt>*_was</tt> for +method_missing+.
|
100
85
|
def key_was(attribute)
|
101
86
|
key_changed?(attribute) ? changed_keys[attribute] : __send__(attribute)
|
102
87
|
end
|
103
88
|
|
104
|
-
# Handle <tt>*_will_change!</tt> for +method_missing+.
|
105
89
|
def key_will_change!(attribute)
|
106
90
|
changed_keys[attribute] = clone_key_value(attribute)
|
107
91
|
end
|
108
92
|
|
109
|
-
# Wrap write_attribute to remember original key value.
|
110
93
|
def write_attribute(attribute, value)
|
111
94
|
attribute = attribute.to_s
|
112
95
|
|
113
|
-
# The key already has an unsaved change.
|
114
96
|
if changed_keys.include?(attribute)
|
115
97
|
old = changed_keys[attribute]
|
116
98
|
changed_keys.delete(attribute) unless value_changed?(attribute, old, value)
|
@@ -119,7 +101,6 @@ module MongoMapper
|
|
119
101
|
changed_keys[attribute] = old if value_changed?(attribute, old, value)
|
120
102
|
end
|
121
103
|
|
122
|
-
# Carry on.
|
123
104
|
super(attribute, value)
|
124
105
|
end
|
125
106
|
|