mongo_mapper 0.9.2 → 0.10.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.
Files changed (40) hide show
  1. data/UPGRADES +6 -0
  2. data/lib/mongo_mapper.rb +1 -2
  3. data/lib/mongo_mapper/document.rb +0 -1
  4. data/lib/mongo_mapper/embedded_document.rb +0 -1
  5. data/lib/mongo_mapper/extensions/float.rb +1 -1
  6. data/lib/mongo_mapper/plugins.rb +2 -8
  7. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +2 -0
  8. data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -4
  9. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +9 -1
  10. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +22 -0
  11. data/lib/mongo_mapper/plugins/associations/one_association.rb +29 -1
  12. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +0 -1
  13. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +43 -16
  14. data/lib/mongo_mapper/plugins/clone.rb +1 -1
  15. data/lib/mongo_mapper/plugins/dirty.rb +6 -15
  16. data/lib/mongo_mapper/plugins/document.rb +5 -5
  17. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +3 -3
  18. data/lib/mongo_mapper/plugins/keys.rb +0 -19
  19. data/lib/mongo_mapper/plugins/keys/key.rb +0 -4
  20. data/lib/mongo_mapper/railtie.rb +1 -2
  21. data/lib/mongo_mapper/version.rb +1 -1
  22. data/test/functional/associations/test_belongs_to_proxy.rb +15 -0
  23. data/test/functional/associations/test_many_documents_proxy.rb +161 -0
  24. data/test/functional/associations/test_one_as_proxy.rb +485 -0
  25. data/test/functional/associations/test_one_proxy.rb +188 -27
  26. data/test/functional/test_dirty.rb +9 -0
  27. data/test/functional/test_document.rb +5 -0
  28. data/test/support/railtie.rb +4 -0
  29. data/test/support/railtie/autoloaded.rb +2 -0
  30. data/test/support/railtie/not_autoloaded.rb +3 -0
  31. data/test/support/railtie/parent.rb +3 -0
  32. data/test/unit/associations/test_one_association.rb +18 -0
  33. data/test/unit/test_extensions.rb +4 -0
  34. data/test/unit/test_keys.rb +0 -22
  35. data/test/unit/test_plugins.rb +1 -46
  36. data/test/unit/test_railtie.rb +61 -0
  37. metadata +18 -14
  38. data/lib/mongo_mapper/support/descendant_appends.rb +0 -45
  39. data/test/functional/test_string_id_compatibility.rb +0 -75
  40. data/test/unit/test_descendant_appends.rb +0 -63
data/UPGRADES CHANGED
@@ -1,3 +1,9 @@
1
+ 0.9 => 0.10
2
+ * Using String IDs are no longer supported. If you are declaring your own ID, ensure it is an ObjectId, and set the default
3
+ key :_id, ObjectId, :default => lambda { BSON::ObjectId.new }
4
+ * The :dependent association option now applies to both when the parent is destroyed and when the association is reassigned (one and many associations)
5
+ * The default :dependent option is now :nullify for both when the parent is destroyed and when the association is reassigned
6
+
1
7
  0.8.6 => 0.9
2
8
  * [attribute]_before_typecast should become [attribute]_before_type_cast (note the extra _)
3
9
  * Change validates_exclusion_of :within option to :in
@@ -72,6 +72,7 @@ module MongoMapper
72
72
  autoload :ManyEmbeddedPolymorphicProxy, 'mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy'
73
73
  autoload :ManyDocumentsAsProxy, 'mongo_mapper/plugins/associations/many_documents_as_proxy'
74
74
  autoload :OneProxy, 'mongo_mapper/plugins/associations/one_proxy'
75
+ autoload :OneAsProxy, 'mongo_mapper/plugins/associations/one_as_proxy'
75
76
  autoload :OneEmbeddedProxy, 'mongo_mapper/plugins/associations/one_embedded_proxy'
76
77
  autoload :InArrayProxy, 'mongo_mapper/plugins/associations/in_array_proxy'
77
78
  end
@@ -84,8 +85,6 @@ Dir[File.join(File.dirname(__FILE__), 'mongo_mapper', 'extensions', '*.rb')].eac
84
85
  require extension
85
86
  end
86
87
 
87
- require 'mongo_mapper/support/descendant_appends'
88
-
89
88
  # FIXME: autoload with proxy is failing, need to investigate
90
89
  require 'mongo_mapper/plugins/associations/proxy'
91
90
 
@@ -3,7 +3,6 @@ module MongoMapper
3
3
  module Document
4
4
  extend ActiveSupport::Concern
5
5
  extend Plugins
6
- extend Support::DescendantAppends
7
6
 
8
7
  include Plugins::ActiveModel
9
8
  include Plugins::Document
@@ -1,7 +1,6 @@
1
1
  # encoding: UTF-8
2
2
  module MongoMapper
3
3
  module EmbeddedDocument
4
- extend Support::DescendantAppends
5
4
  extend ActiveSupport::Concern
6
5
  extend Plugins
7
6
 
@@ -3,7 +3,7 @@ module MongoMapper
3
3
  module Extensions
4
4
  module Float
5
5
  def to_mongo(value)
6
- value.nil? ? nil : value.to_f
6
+ value.blank? ? nil : value.to_f
7
7
  end
8
8
  end
9
9
  end
@@ -8,14 +8,8 @@ module MongoMapper
8
8
  end
9
9
 
10
10
  def plugin(mod)
11
- if ActiveSupport::Concern === mod
12
- include mod
13
- else
14
- warn "[DEPRECATED] Plugins must extend ActiveSupport::Concern"
15
- extend mod::ClassMethods if mod.const_defined?(:ClassMethods)
16
- include mod::InstanceMethods if mod.const_defined?(:InstanceMethods)
17
- mod.configure(self) if mod.respond_to?(:configure)
18
- end
11
+ raise ArgumentError, "Plugins must extend ActiveSupport::Concern" unless ActiveSupport::Concern === mod
12
+ include mod
19
13
  direct_descendants.each {|model| model.send(:include, mod) }
20
14
  plugins << mod
21
15
  end
@@ -13,6 +13,8 @@ module MongoMapper
13
13
  end
14
14
 
15
15
  def setup(model)
16
+ model.key foreign_key, ObjectId unless model.key?(foreign_key)
17
+
16
18
  model.associations_module.module_eval <<-end_eval
17
19
  def #{name}
18
20
  proxy = get_proxy(associations[#{name.inspect}])
@@ -41,11 +41,11 @@ module MongoMapper
41
41
  end
42
42
  end_eval
43
43
 
44
- if options[:dependent] && !embeddable?
45
- association = self
46
- options = self.options
44
+ association = self
45
+ options = self.options
47
46
 
48
- model.after_destroy do
47
+ model.before_destroy do
48
+ if !association.embeddable?
49
49
  case options[:dependent]
50
50
  when :destroy
51
51
  self.get_proxy(association).destroy_all
@@ -8,7 +8,15 @@ module MongoMapper
8
8
 
9
9
  def replace(docs)
10
10
  load_target
11
- target.each { |t| t.destroy }
11
+
12
+ (target - docs).each do |t|
13
+ case options[:dependent]
14
+ when :destroy then t.destroy
15
+ when :delete_all then t.delete
16
+ else t.update_attributes(self.foreign_key => nil)
17
+ end
18
+ end
19
+
12
20
  docs.each { |doc| prepare(doc).save }
13
21
  reset
14
22
  end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class OneAsProxy < OneProxy
6
+ protected
7
+ def criteria
8
+ {type_key_name => proxy_owner.class.name, id_key_name => proxy_owner.id}
9
+ end
10
+
11
+ private
12
+ def type_key_name
13
+ "#{options[:as]}_type"
14
+ end
15
+
16
+ def id_key_name
17
+ "#{options[:as]}_id"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -8,7 +8,35 @@ module MongoMapper
8
8
  end
9
9
 
10
10
  def proxy_class
11
- @proxy_class ||= klass.embeddable? ? OneEmbeddedProxy : OneProxy
11
+ @proxy_class ||=
12
+ if klass.embeddable?
13
+ OneEmbeddedProxy
14
+ elsif as?
15
+ OneAsProxy
16
+ else
17
+ OneProxy
18
+ end
19
+ end
20
+
21
+ def setup(model)
22
+ super
23
+
24
+ association = self
25
+ options = self.options
26
+
27
+ model.before_destroy do
28
+ if !association.embeddable?
29
+ proxy = self.get_proxy(association)
30
+
31
+ unless proxy.nil?
32
+ case options[:dependent]
33
+ when :destroy then proxy.destroy
34
+ when :delete then proxy.delete
35
+ else proxy.nullify
36
+ end
37
+ end
38
+ end
39
+ end
12
40
  end
13
41
 
14
42
  def autosave?
@@ -16,7 +16,6 @@ module MongoMapper
16
16
  else
17
17
  @target = klass.load(doc)
18
18
  end
19
- @target.default_id_value if @target && @target.id.nil?
20
19
  assign_references(@target)
21
20
  loaded
22
21
  @target
@@ -19,38 +19,51 @@ module MongoMapper
19
19
  load_target
20
20
 
21
21
  if !target.nil? && target != doc
22
- if options[:dependent] && target.persisted?
22
+ if target.persisted?
23
23
  case options[:dependent]
24
- when :delete
25
- target.delete
26
- when :destroy
27
- target.destroy
28
- when :nullify
29
- target[foreign_key] = nil
24
+ when :delete then target.delete
25
+ when :destroy then target.destroy
26
+ else
27
+ nullify_scope(target)
30
28
  target.save
31
29
  end
32
30
  end
33
31
  end
34
-
35
- if doc.nil?
36
- target.update_attributes(foreign_key => nil) unless target.nil?
37
- else
32
+
33
+ unless doc.nil?
38
34
  proxy_owner.save unless proxy_owner.persisted?
39
35
  doc = klass.new(doc) unless doc.is_a?(klass)
40
- doc[foreign_key] = proxy_owner.id
36
+ apply_scope(doc)
41
37
  doc.save unless doc.persisted?
42
- loaded
43
- @target = doc
44
38
  end
39
+
40
+ loaded
41
+ @target = doc
42
+ end
43
+
44
+ def destroy
45
+ target.destroy
46
+ reset
47
+ end
48
+
49
+ def delete
50
+ target.delete
51
+ reset
52
+ end
53
+
54
+ def nullify
55
+ nullify_scope(target)
56
+ target.save
57
+ reset
45
58
  end
46
59
 
47
60
  protected
48
61
  def find_target
49
- target_class.first(association.query_options.merge(foreign_key => proxy_owner.id))
62
+ target_class.first(association.query_options.merge(criteria))
50
63
  end
51
64
 
52
65
  def instantiate_target(instantiator, attrs={})
53
- @target = target_class.send(instantiator, attrs.update(foreign_key => proxy_owner.id))
66
+ @target = target_class.send(instantiator, attrs.update(criteria))
54
67
  loaded
55
68
  @target
56
69
  end
@@ -62,6 +75,20 @@ module MongoMapper
62
75
  def foreign_key
63
76
  options[:foreign_key] || proxy_owner.class.name.foreign_key
64
77
  end
78
+
79
+ def criteria
80
+ {self.foreign_key => proxy_owner.id}
81
+ end
82
+
83
+ def nullify_scope(doc)
84
+ criteria.each { |key, value| doc[key] = nil }
85
+ doc
86
+ end
87
+
88
+ def apply_scope(doc)
89
+ criteria.each { |key, value| doc[key] = value }
90
+ doc
91
+ end
65
92
  end
66
93
  end
67
94
  end
@@ -8,7 +8,7 @@ module MongoMapper
8
8
  def initialize_copy(other)
9
9
  @_new = true
10
10
  @_destroyed = false
11
- default_id_value
11
+ @_id = nil
12
12
  associations.each do |name, association|
13
13
  instance_variable_set(association.ivar, nil)
14
14
  end
@@ -20,10 +20,6 @@ module MongoMapper
20
20
  clear_changes { super }
21
21
  end
22
22
 
23
- def save!(*)
24
- clear_changes { super }
25
- end
26
-
27
23
  def reload(*)
28
24
  super.tap { clear_changes }
29
25
  end
@@ -51,19 +47,14 @@ module MongoMapper
51
47
 
52
48
  def write_key(key, value)
53
49
  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 attribute_value_changed?(key, attribute_was(key), value)
57
- super(key, value)
58
- end
59
-
60
- def attribute_should_change?(key, old, value)
61
- attribute_changed?(key) == false && attribute_value_changed?(key, old, value)
50
+ attribute_will_change!(key) unless attribute_changed?(key)
51
+ super(key, value).tap do
52
+ changed_attributes.delete(key) unless attribute_value_changed?(key)
53
+ end
62
54
  end
63
55
 
64
- def attribute_value_changed?(key_name, old, value)
65
- value = nil if keys[key_name.to_s].number? && value.blank?
66
- old != value
56
+ def attribute_value_changed?(key_name)
57
+ attribute_was(key_name) != read_key(key_name)
67
58
  end
68
59
  end
69
60
  end
@@ -21,12 +21,12 @@ module MongoMapper
21
21
 
22
22
  def reload
23
23
  if doc = collection.find_one(:_id => id)
24
- tap do |instance|
25
- instance.class.associations.each_value do |association|
26
- get_proxy(association).reset
27
- end
28
- instance.attributes = doc
24
+ self.class.associations.each_value do |association|
25
+ get_proxy(association).reset
29
26
  end
27
+ instance_variables.each { |ivar| instance_variable_set(ivar, nil) }
28
+ self.attributes = doc
29
+ self
30
30
  else
31
31
  raise DocumentNotFound, "Document match #{_id.inspect} does not exist in #{collection.name} collection"
32
32
  end
@@ -11,7 +11,7 @@ module MongoMapper
11
11
  end
12
12
 
13
13
  module InstanceMethods
14
- def run_callbacks(callback, &block)
14
+ def run_callbacks(callback, *args, &block)
15
15
  embedded_docs = []
16
16
 
17
17
  embedded_associations.each do |association|
@@ -20,12 +20,12 @@ module MongoMapper
20
20
 
21
21
  block = embedded_docs.inject(block) do |chain, doc|
22
22
  if doc.class.respond_to?("_#{callback}_callbacks")
23
- lambda { doc.run_callbacks(callback, &chain) }
23
+ lambda { doc.run_callbacks(callback, *args, &chain) }
24
24
  else
25
25
  chain
26
26
  end
27
27
  end
28
- super callback, &block
28
+ super callback, *args, &block
29
29
  end
30
30
  end
31
31
  end
@@ -47,11 +47,6 @@ module MongoMapper
47
47
  object_id_keys.include?(name.to_sym)
48
48
  end
49
49
 
50
- # API Private
51
- def can_default_id?
52
- keys['_id'].can_default_id?
53
- end
54
-
55
50
  def to_mongo(instance)
56
51
  return nil if instance.nil?
57
52
  instance.to_mongo
@@ -163,7 +158,6 @@ module MongoMapper
163
158
 
164
159
  module InstanceMethods
165
160
  def initialize(attrs={})
166
- default_id_value(attrs)
167
161
  @_new = true
168
162
  assign(attrs)
169
163
  end
@@ -171,7 +165,6 @@ module MongoMapper
171
165
  def initialize_from_database(attrs={})
172
166
  @_new = false
173
167
  load_from_database(attrs)
174
- default_id_value(attrs)
175
168
  self
176
169
  end
177
170
 
@@ -266,18 +259,6 @@ module MongoMapper
266
259
  keys.values.select { |key| key.embeddable? }
267
260
  end
268
261
 
269
- def default_id_value(attrs={})
270
- id_provided = !attrs.nil? && attrs.keys.map { |k| k.to_s }.detect { |k| k == 'id' || k == '_id' }
271
- if !id_provided && self.class.can_default_id?
272
- unless keys['_id'].default_value
273
- warn "[DEPRECATED] Custom IDs will no longer be automatically populated. If you definte your own :_id key, set a default:\n key :_id, String, :default => lambda { BSON::ObjectId.new }"
274
- warn caller.grep(/test/).join("\n")
275
- end
276
-
277
- write_key :_id, BSON::ObjectId.new
278
- end
279
- end
280
-
281
262
  private
282
263
  def load_from_database(attrs)
283
264
  return if attrs.blank?
@@ -21,10 +21,6 @@ module MongoMapper
21
21
  type.embeddable?
22
22
  end
23
23
 
24
- def can_default_id?
25
- type && (type == ObjectId || type == BSON::ObjectId || type == String)
26
- end
27
-
28
24
  def number?
29
25
  type == Integer || type == Float
30
26
  end
@@ -37,10 +37,9 @@ module MongoMapper
37
37
  initializer "mongo_mapper.prepare_dispatcher" do |app|
38
38
  # See http://groups.google.com/group/mongomapper/browse_thread/thread/68f62e8eda43b43a/4841dba76938290c
39
39
  # to_prepare is called before each request in development mode and the first request in production.
40
+
40
41
  ActionDispatch::Callbacks.to_prepare do
41
42
  unless app.config.cache_classes
42
- # Rails reloading was making descendants fill up and leak memory, these make sure they get cleared
43
- ActiveSupport::DescendantsTracker.clear
44
43
  MongoMapper::Plugins::IdentityMap.models.clear
45
44
  end
46
45
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: UTF-8
2
2
  module MongoMapper
3
- Version = '0.9.2'
3
+ Version = '0.10.0'
4
4
  end
@@ -102,6 +102,21 @@ class BelongsToProxyTest < Test::Unit::TestCase
102
102
  @comment_class.new(:name => 'Foo', :post_id => id).post.nil?.should be_true
103
103
  end
104
104
 
105
+ should "define foreign key if it doesn't exist" do
106
+ @category_class = Doc()
107
+ @post_class.belongs_to :category, :class => @category_class
108
+
109
+ @post_class.key?(:category_id).should be_true
110
+ end
111
+
112
+ should "not define foreign key if it already exists" do
113
+ @category_class = Doc()
114
+ @post_class.key :category_id, String
115
+ @post_class.belongs_to :category, :class => @category_class
116
+
117
+ @post_class.keys['category_id'].type.should == String
118
+ end
119
+
105
120
  context ":dependent" do
106
121
  setup do
107
122
  # FIXME: make use of already defined models