mongo_mapper 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/UPGRADES +10 -0
  2. data/bin/mmconsole +0 -1
  3. data/examples/identity_map/automatic.rb +1 -7
  4. data/examples/plugins.rb +9 -9
  5. data/examples/safe.rb +43 -0
  6. data/lib/mongo_mapper.rb +46 -33
  7. data/lib/mongo_mapper/document.rb +33 -32
  8. data/lib/mongo_mapper/embedded_document.rb +22 -22
  9. data/lib/mongo_mapper/locale/en.yml +5 -0
  10. data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
  11. data/lib/mongo_mapper/plugins.rb +16 -3
  12. data/lib/mongo_mapper/plugins/accessible.rb +2 -0
  13. data/lib/mongo_mapper/plugins/active_model.rb +18 -0
  14. data/lib/mongo_mapper/plugins/associations.rb +37 -42
  15. data/lib/mongo_mapper/plugins/associations/base.rb +14 -50
  16. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +58 -0
  17. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +6 -1
  18. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +30 -2
  19. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -0
  20. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +12 -6
  21. data/lib/mongo_mapper/plugins/associations/many_association.rb +67 -0
  22. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +5 -5
  23. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +1 -1
  24. data/lib/mongo_mapper/plugins/associations/one_association.rb +20 -0
  25. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +5 -0
  26. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +7 -7
  27. data/lib/mongo_mapper/plugins/associations/proxy.rb +2 -2
  28. data/lib/mongo_mapper/plugins/caching.rb +3 -1
  29. data/lib/mongo_mapper/plugins/callbacks.rb +12 -221
  30. data/lib/mongo_mapper/plugins/clone.rb +3 -1
  31. data/lib/mongo_mapper/plugins/dirty.rb +38 -91
  32. data/lib/mongo_mapper/plugins/document.rb +4 -2
  33. data/lib/mongo_mapper/plugins/dynamic_querying.rb +2 -0
  34. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +43 -0
  35. data/lib/mongo_mapper/plugins/embedded_document.rb +16 -9
  36. data/lib/mongo_mapper/plugins/equality.rb +2 -0
  37. data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
  38. data/lib/mongo_mapper/plugins/indexes.rb +2 -0
  39. data/lib/mongo_mapper/plugins/inspect.rb +3 -1
  40. data/lib/mongo_mapper/plugins/keys.rb +28 -22
  41. data/lib/mongo_mapper/plugins/keys/key.rb +12 -6
  42. data/lib/mongo_mapper/plugins/logger.rb +2 -0
  43. data/lib/mongo_mapper/plugins/modifiers.rb +3 -1
  44. data/lib/mongo_mapper/plugins/pagination.rb +2 -0
  45. data/lib/mongo_mapper/plugins/persistence.rb +2 -0
  46. data/lib/mongo_mapper/plugins/protected.rb +2 -0
  47. data/lib/mongo_mapper/plugins/querying.rb +5 -4
  48. data/lib/mongo_mapper/plugins/rails.rb +3 -5
  49. data/lib/mongo_mapper/plugins/safe.rb +2 -0
  50. data/lib/mongo_mapper/plugins/sci.rb +2 -0
  51. data/lib/mongo_mapper/plugins/scopes.rb +2 -0
  52. data/lib/mongo_mapper/plugins/serialization.rb +67 -46
  53. data/lib/mongo_mapper/plugins/timestamps.rb +3 -1
  54. data/lib/mongo_mapper/plugins/userstamps.rb +2 -0
  55. data/lib/mongo_mapper/plugins/validations.rb +40 -24
  56. data/lib/mongo_mapper/railtie.rb +49 -0
  57. data/lib/mongo_mapper/railtie/database.rake +60 -0
  58. data/lib/mongo_mapper/support/descendant_appends.rb +11 -11
  59. data/lib/mongo_mapper/translation.rb +10 -0
  60. data/lib/mongo_mapper/version.rb +1 -1
  61. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
  62. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
  63. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
  64. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +11 -0
  65. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +1 -0
  66. data/test/functional/associations/test_belongs_to_proxy.rb +131 -1
  67. data/test/functional/associations/test_in_array_proxy.rb +30 -0
  68. data/test/functional/associations/test_many_documents_proxy.rb +30 -2
  69. data/test/functional/associations/test_many_embedded_proxy.rb +33 -0
  70. data/test/functional/associations/test_many_polymorphic_proxy.rb +1 -0
  71. data/test/functional/associations/test_one_embedded_proxy.rb +21 -2
  72. data/test/functional/associations/test_one_proxy.rb +49 -9
  73. data/test/functional/test_associations.rb +2 -0
  74. data/test/functional/test_caching.rb +3 -2
  75. data/test/functional/test_callbacks.rb +25 -18
  76. data/test/functional/test_dirty.rb +123 -1
  77. data/test/functional/test_document.rb +26 -2
  78. data/test/functional/test_embedded_document.rb +68 -2
  79. data/test/functional/test_identity_map.rb +3 -4
  80. data/test/functional/test_querying.rb +11 -0
  81. data/test/functional/test_userstamps.rb +2 -2
  82. data/test/functional/test_validations.rb +31 -29
  83. data/test/models.rb +10 -0
  84. data/test/test_active_model_lint.rb +1 -1
  85. data/test/test_helper.rb +9 -10
  86. data/test/unit/associations/test_base.rb +24 -100
  87. data/test/unit/associations/test_belongs_to_association.rb +29 -0
  88. data/test/unit/associations/test_many_association.rb +63 -0
  89. data/test/unit/associations/test_one_association.rb +18 -0
  90. data/test/unit/serializers/test_json_serializer.rb +0 -1
  91. data/test/unit/test_descendant_appends.rb +8 -16
  92. data/test/unit/test_document.rb +4 -9
  93. data/test/unit/test_dynamic_finder.rb +1 -1
  94. data/test/unit/test_embedded_document.rb +51 -18
  95. data/test/unit/test_identity_map_middleware.rb +34 -0
  96. data/test/unit/test_inspect.rb +22 -0
  97. data/test/unit/test_key.rb +21 -1
  98. data/test/unit/test_keys.rb +0 -2
  99. data/test/unit/test_plugins.rb +106 -20
  100. data/test/unit/test_rails.rb +8 -8
  101. data/test/unit/test_serialization.rb +116 -1
  102. data/test/unit/test_translation.rb +27 -0
  103. data/test/unit/test_validations.rb +66 -81
  104. metadata +103 -43
  105. data/examples/identity_map/middleware.rb +0 -14
  106. data/lib/mongo_mapper/plugins/descendants.rb +0 -17
  107. data/rails/init.rb +0 -19
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class OneAssociation < BelongsToAssociation
6
+ def embeddable?
7
+ klass.embeddable?
8
+ end
9
+
10
+ def proxy_class
11
+ @proxy_class ||= klass.embeddable? ? OneEmbeddedProxy : OneProxy
12
+ end
13
+
14
+ def autosave?
15
+ options.fetch(:autosave, embeddable?)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -16,11 +16,16 @@ module MongoMapper
16
16
  else
17
17
  @target = klass.load(doc)
18
18
  end
19
+ @target.default_id_value if @target && @target.id.nil?
19
20
  assign_references(@target)
20
21
  loaded
21
22
  @target
22
23
  end
23
24
 
25
+ def save_to_collection(options={})
26
+ @target.persist(options) if @target
27
+ end
28
+
24
29
  protected
25
30
 
26
31
  def find_target
@@ -19,7 +19,7 @@ module MongoMapper
19
19
  load_target
20
20
 
21
21
  if !target.nil? && target != doc
22
- if options[:dependent] && !target.new?
22
+ if options[:dependent] && target.persisted?
23
23
  case options[:dependent]
24
24
  when :delete
25
25
  target.delete
@@ -32,13 +32,13 @@ module MongoMapper
32
32
  end
33
33
  end
34
34
 
35
- reset
36
-
37
- unless doc.nil?
38
- proxy_owner.save if proxy_owner.new?
39
- doc = klass.new(doc) unless klass === doc
35
+ if doc.nil?
36
+ target.update_attributes(foreign_key => nil) unless target.nil?
37
+ else
38
+ proxy_owner.save unless proxy_owner.persisted?
39
+ doc = klass.new(doc) unless doc.is_a?(klass)
40
40
  doc[foreign_key] = proxy_owner.id
41
- doc.save if doc.new?
41
+ doc.save unless doc.persisted?
42
42
  loaded
43
43
  @target = doc
44
44
  end
@@ -9,7 +9,7 @@ module MongoMapper
9
9
  alias :proxy_respond_to? :respond_to?
10
10
  alias :proxy_extend :extend
11
11
 
12
- instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
12
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^respond_to_missing\?$|^object_id$)/ }
13
13
 
14
14
  attr_reader :proxy_owner, :association, :target
15
15
 
@@ -113,7 +113,7 @@ module MongoMapper
113
113
  def load_target
114
114
  unless loaded?
115
115
  if @target.is_a?(Array) && @target.any?
116
- @target = find_target + @target.find_all { |record| record.new? }
116
+ @target = find_target + @target.find_all { |record| !record.persisted? }
117
117
  else
118
118
  @target = find_target
119
119
  end
@@ -2,10 +2,12 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Caching
5
+ extend ActiveSupport::Concern
6
+
5
7
  module InstanceMethods
6
8
  def cache_key(*suffixes)
7
9
  cache_key = case
8
- when new?
10
+ when !persisted?
9
11
  "#{self.class.name}/new"
10
12
  when timestamp = self[:updated_at]
11
13
  "#{self.class.name}/#{id}-#{timestamp.to_s(:number)}"
@@ -1,240 +1,31 @@
1
1
  # encoding: UTF-8
2
- # Almost all of this callback stuff is pulled directly from ActiveSupport
3
- # in the interest of support rails 2 and 3 at the same time and is the
4
- # same copyright as rails.
5
2
  module MongoMapper
6
3
  module Plugins
7
4
  module Callbacks
8
- def self.configure(model)
9
- model.class_eval do
10
- define_callbacks(
11
- :before_save, :after_save,
12
- :before_create, :after_create,
13
- :before_update, :after_update,
14
- :before_validation, :after_validation,
15
- :before_validation_on_create, :after_validation_on_create,
16
- :before_validation_on_update, :after_validation_on_update,
17
- :before_destroy, :after_destroy,
18
- :validate_on_create, :validate_on_update,
19
- :validate
20
- )
21
- end
22
- end
23
-
24
- module ClassMethods
25
- def define_callbacks(*callbacks)
26
- callbacks.each do |callback|
27
- class_eval <<-"end_eval"
28
- def self.#{callback}(*methods, &block)
29
- callbacks = CallbackChain.build(:#{callback}, *methods, &block)
30
- @#{callback}_callbacks ||= CallbackChain.new
31
- @#{callback}_callbacks.concat callbacks
32
- end
33
-
34
- def self.#{callback}_callback_chain
35
- @#{callback}_callbacks ||= CallbackChain.new
36
-
37
- if superclass.respond_to?(:#{callback}_callback_chain)
38
- CallbackChain.new(
39
- superclass.#{callback}_callback_chain +
40
- @#{callback}_callbacks
41
- )
42
- else
43
- @#{callback}_callbacks
44
- end
45
- end
46
- end_eval
47
- end
48
- end
49
- end
5
+ extend ActiveSupport::Concern
50
6
 
51
7
  module InstanceMethods
52
- def valid?
53
- action = new? ? 'create' : 'update'
54
- run_callbacks(:before_validation)
55
- run_callbacks("before_validation_on_#{action}".to_sym)
56
- result = super
57
- run_callbacks("after_validation_on_#{action}".to_sym)
58
- run_callbacks(:after_validation)
59
- result
60
- end
61
-
62
- # Overriding validatable's valid_for_group? to integrate validation callbacks
63
- def valid_for_group?(group) #:nodoc:
64
- errors.clear
65
- run_before_validations
66
- run_callbacks(:validate)
67
- new? ? run_callbacks(:validate_on_create) : run_callbacks(:validate_on_update)
68
- self.class.validate_children(self, group)
69
- self.validate_group(group)
70
- errors.empty?
8
+ def valid?(context = nil)
9
+ context ||= (new_record? ? :create : :update)
10
+ super(context) && errors.empty?
71
11
  end
72
12
 
73
13
  def destroy
74
- run_callbacks(:before_destroy)
75
- result = super
76
- run_callbacks(:after_destroy)
77
- result
78
- end
79
-
80
- def run_callbacks(kind, options={}, &block)
81
- callback_chain_method = "#{kind}_callback_chain"
82
- return unless self.class.respond_to?(callback_chain_method)
83
- self.class.send(callback_chain_method).run(self, options, &block)
84
- self.embedded_associations.each do |association|
85
- if association.one?
86
- self.send(association.name).run_callbacks(kind, options, &block)
87
- else
88
- self.send(association.name).each do |document|
89
- document.run_callbacks(kind, options, &block)
90
- end
91
- end
92
- end
93
- end
94
-
95
- private
96
- def create_or_update(*args)
97
- run_callbacks(:before_save)
98
- if result = super
99
- run_callbacks(:after_save)
100
- end
101
- result
102
- end
103
-
104
- def create(*args)
105
- run_callbacks(:before_create)
106
- result = super
107
- run_callbacks(:after_create)
108
- result
109
- end
110
-
111
- def update(*args)
112
- run_callbacks(:before_update)
113
- result = super
114
- run_callbacks(:after_update)
115
- result
116
- end
117
- end
118
-
119
- class CallbackChain < Array
120
- def self.build(kind, *methods, &block)
121
- methods, options = extract_options(*methods, &block)
122
- methods.map! { |method| Callback.new(kind, method, options) }
123
- new(methods)
124
- end
125
-
126
- def run(object, options={}, &terminator)
127
- enumerator = options[:enumerator] || :each
128
-
129
- unless block_given?
130
- send(enumerator) { |callback| callback.call(object) }
131
- else
132
- send(enumerator) do |callback|
133
- result = callback.call(object)
134
- break result if terminator.call(result, object)
135
- end
136
- end
137
- end
138
-
139
- # TODO: Decompose into more Array like behavior
140
- def replace_or_append!(chain)
141
- if index = index(chain)
142
- self[index] = chain
143
- else
144
- self << chain
145
- end
146
- self
147
- end
148
-
149
- def find(callback, &block)
150
- select { |c| c == callback && (!block_given? || yield(c)) }.first
14
+ run_callbacks(:destroy) { super }
151
15
  end
152
16
 
153
- def delete(callback)
154
- super(callback.is_a?(Callback) ? callback : find(callback))
17
+ private
18
+ def create_or_update(*)
19
+ run_callbacks(:save) { super }
155
20
  end
156
21
 
157
- private
158
- def self.extract_options(*methods, &block)
159
- methods.flatten!
160
- options = methods.extract_options!
161
- methods << block if block_given?
162
- return methods, options
163
- end
164
-
165
- def extract_options(*methods, &block)
166
- self.class.extract_options(*methods, &block)
167
- end
168
- end
169
-
170
- class Callback
171
- attr_reader :kind, :method, :identifier, :options
172
-
173
- def initialize(kind, method, options={})
174
- @kind = kind
175
- @method = method
176
- @identifier = options[:identifier]
177
- @options = options
22
+ def create(*)
23
+ run_callbacks(:create) { super }
178
24
  end
179
25
 
180
- def ==(other)
181
- case other
182
- when Callback
183
- (self.identifier && self.identifier == other.identifier) || self.method == other.method
184
- else
185
- (self.identifier && self.identifier == other) || self.method == other
186
- end
26
+ def update(*)
27
+ run_callbacks(:update) { super }
187
28
  end
188
-
189
- def eql?(other)
190
- self == other
191
- end
192
-
193
- def dup
194
- self.class.new(@kind, @method, @options.dup)
195
- end
196
-
197
- def hash
198
- if @identifier
199
- @identifier.hash
200
- else
201
- @method.hash
202
- end
203
- end
204
-
205
- def call(*args, &block)
206
- evaluate_method(method, *args, &block) if should_run_callback?(*args)
207
- rescue LocalJumpError
208
- raise ArgumentError,
209
- "Cannot yield from a Proc type filter. The Proc must take two " +
210
- "arguments and execute #call on the second argument."
211
- end
212
-
213
- private
214
- def evaluate_method(method, *args, &block)
215
- case method
216
- when Symbol
217
- object = args.shift
218
- object.send(method, *args, &block)
219
- when String
220
- eval(method, args.first.instance_eval { binding })
221
- when Proc, Method
222
- method.call(*args, &block)
223
- else
224
- if method.respond_to?(kind)
225
- method.send(kind, *args, &block)
226
- else
227
- raise ArgumentError,
228
- "Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
229
- "a block to be invoked, or an object responding to the callback method."
230
- end
231
- end
232
- end
233
-
234
- def should_run_callback?(*args)
235
- [options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
236
- ![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
237
- end
238
29
  end
239
30
  end
240
31
  end
@@ -2,11 +2,13 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Clone
5
+ extend ActiveSupport::Concern
6
+
5
7
  module InstanceMethods
6
8
  def initialize_copy(other)
7
9
  @_new = true
8
10
  @_destroyed = false
9
- default_id_value({})
11
+ default_id_value
10
12
  associations.each do |name, association|
11
13
  instance_variable_set(association.ivar, nil)
12
14
  end
@@ -2,122 +2,69 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Dirty
5
- module InstanceMethods
6
- def method_missing(method, *args, &block)
7
- if method.to_s =~ /(_changed\?|_change|_will_change!|_was)$/
8
- method_suffix = $1
9
- key = method.to_s.gsub(method_suffix, '')
5
+ extend ActiveSupport::Concern
10
6
 
11
- if key_names.include?(key)
12
- case method_suffix
13
- when '_changed?'
14
- key_changed?(key)
15
- when '_change'
16
- key_change(key)
17
- when '_will_change!'
18
- key_will_change!(key)
19
- when '_was'
20
- key_was(key)
21
- end
22
- else
23
- super
24
- end
25
- else
26
- super
27
- end
28
- end
29
-
30
- def changed?
31
- !changed_keys.empty?
32
- end
33
-
34
- def changed
35
- changed_keys.keys
36
- end
37
-
38
- def changes
39
- changed.inject({}) { |h, key| h[key] = key_change(key); h }
40
- end
7
+ include ::ActiveModel::Dirty
41
8
 
9
+ module InstanceMethods
42
10
  def initialize(*)
43
11
  # never register initial id assignment as a change
44
- super.tap { changed_keys.delete('_id') }
12
+ super.tap { changed_attributes.delete('_id') }
45
13
  end
46
14
 
47
15
  def initialize_from_database(*)
48
- super.tap { changed_keys.clear }
16
+ super.tap { changed_attributes.clear }
49
17
  end
50
18
 
51
19
  def save(*)
52
- if status = super
53
- changed_keys.clear
54
- end
55
- status
20
+ clear_changes { super }
56
21
  end
57
22
 
58
23
  def save!(*)
59
- status = super
60
- changed_keys.clear
61
- status
24
+ clear_changes { super }
62
25
  end
63
26
 
64
27
  def reload(*)
65
- document = super
66
- changed_keys.clear
67
- document
28
+ super.tap { clear_changes }
68
29
  end
69
30
 
70
- private
71
- def clone_key_value(key)
72
- value = read_key(key)
73
- value.duplicable? ? value.clone : value
74
- rescue TypeError, NoMethodError
75
- value
76
- end
77
-
78
- def changed_keys
79
- @changed_keys ||= {}
80
- end
81
-
82
- def key_changed?(key)
83
- changed_keys.include?(key)
84
- end
85
-
86
- def key_change(key)
87
- [changed_keys[key], __send__(key)] if key_changed?(key)
88
- end
89
-
90
- def key_was(key)
91
- key_changed?(key) ? changed_keys[key] : __send__(key)
92
- end
31
+ protected
93
32
 
94
- def key_will_change!(key)
95
- changed_keys[key] = clone_key_value(key)
96
- end
97
-
98
- def write_key(key, value)
99
- key = key.to_s
33
+ def attribute_method?(attr)
34
+ # This overrides ::ActiveSupport::Dirty#attribute_method? to allow attributes to be any key
35
+ # in the attributes hash ( default ) or any key defined on the model that may not yet have
36
+ # had a value stored in the attributes collection.
37
+ super || key_names.include?(attr)
38
+ end
100
39
 
101
- if changed_keys.include?(key)
102
- old = changed_keys[key]
103
- changed_keys.delete(key) unless value_changed?(key, old, value)
104
- else
105
- old = clone_key_value(key)
106
- changed_keys[key] = old if value_changed?(key, old, value)
40
+ def clear_changes
41
+ previous = changes
42
+ (block_given? ? yield : true).tap do |result|
43
+ unless result == false #failed validation; nil is OK.
44
+ @previously_changed = previous
45
+ changed_attributes.clear
107
46
  end
108
-
109
- super(key, value)
110
47
  end
48
+ end
111
49
 
112
- def value_changed?(key_name, old, value)
113
- key = keys[key_name]
50
+ private
114
51
 
115
- if key.number? && value.blank?
116
- value = nil
117
- end
52
+ def write_key(key, value)
53
+ key = key.to_s
54
+ old = read_key(key)
55
+ attribute_will_change!(key) if attribute_should_change?(key, old, value)
56
+ changed_attributes.delete(key) unless value_changed?(key, attribute_was(key), value)
57
+ super(key, value)
58
+ end
118
59
 
119
- old != value
120
- end
60
+ def attribute_should_change?(key, old, value)
61
+ attribute_changed?(key) == false && value_changed?(key, old, value)
62
+ end
63
+
64
+ def value_changed?(key_name, old, value)
65
+ value = nil if keys[key_name.to_s].number? && value.blank?
66
+ old != value
67
+ end
121
68
  end
122
69
  end
123
70
  end