mongo_mapper 0.8.6 → 0.9.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 (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
@@ -3,47 +3,26 @@ module MongoMapper
3
3
  module Plugins
4
4
  module Associations
5
5
  class Base
6
- attr_reader :type, :name, :options, :query_options
6
+ attr_reader :name, :options, :query_options
7
7
 
8
8
  # Options that should not be considered MongoDB query options/criteria
9
- AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic]
9
+ AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic, :autosave]
10
10
 
11
- def initialize(type, name, options={}, &extension)
12
- @type, @name, @options, @query_options, @original_options = type, name, {}, {}, options
11
+ def initialize(name, options={}, &extension)
12
+ @name, @options, @query_options, @original_options = name.to_sym, {}, {}, options
13
13
  options.symbolize_keys!
14
14
  options[:extend] = modularized_extensions(extension, options[:extend])
15
15
  separate_options_and_conditions
16
16
  end
17
17
 
18
18
  def class_name
19
- return @class_name if defined?(@class_name)
20
-
21
- @class_name =
22
- if cn = options[:class_name]
23
- cn
24
- elsif many?
25
- name.to_s.singularize.camelize
26
- else
27
- name.to_s.camelize
28
- end
19
+ @class_name ||= options[:class_name] || name.to_s.camelize
29
20
  end
30
21
 
31
22
  def klass
32
23
  @klass ||= options[:class] || class_name.constantize
33
24
  end
34
25
 
35
- def many?
36
- @type == :many
37
- end
38
-
39
- def belongs_to?
40
- @type == :belongs_to
41
- end
42
-
43
- def one?
44
- @type == :one
45
- end
46
-
47
26
  def polymorphic?
48
27
  !!@options[:polymorphic]
49
28
  end
@@ -57,11 +36,11 @@ module MongoMapper
57
36
  end
58
37
 
59
38
  def embeddable?
60
- (one? || many?) && klass.embeddable?
39
+ klass.embeddable?
61
40
  end
62
41
 
63
42
  def type_key_name
64
- many? ? '_type' : "#{as}_type"
43
+ "#{as}_type"
65
44
  end
66
45
 
67
46
  def as
@@ -76,30 +55,15 @@ module MongoMapper
76
55
  @ivar ||= "@_#{name}"
77
56
  end
78
57
 
79
- # hate this, need to revisit
80
58
  def proxy_class
81
- return @proxy_class if defined?(@proxy_class)
59
+ raise NotImplementedError
60
+ end
82
61
 
83
- @proxy_class =
84
- if many?
85
- if klass.embeddable?
86
- polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
87
- else
88
- if polymorphic?
89
- ManyPolymorphicProxy
90
- elsif as?
91
- ManyDocumentsAsProxy
92
- elsif in_array?
93
- InArrayProxy
94
- else
95
- ManyDocumentsProxy
96
- end
97
- end
98
- elsif one?
99
- klass.embeddable? ? OneEmbeddedProxy : OneProxy
100
- else
101
- polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
102
- end
62
+ def setup(model)
63
+ end
64
+
65
+ def autosave?
66
+ raise NotImplementedError
103
67
  end
104
68
 
105
69
  private
@@ -0,0 +1,58 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class BelongsToAssociation < Base
6
+
7
+ def embeddable?
8
+ false
9
+ end
10
+
11
+ def proxy_class
12
+ @proxy_class ||= polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
13
+ end
14
+
15
+ def setup(model)
16
+ model.associations_module.module_eval <<-end_eval
17
+ def #{name}
18
+ proxy = get_proxy(associations[#{name.inspect}])
19
+ proxy.nil? ? nil : proxy
20
+ end
21
+
22
+ def #{name}=(value)
23
+ association = associations[#{name.inspect}]
24
+ proxy = get_proxy(association)
25
+
26
+ if proxy.nil? || proxy.target != value
27
+ proxy = build_proxy(association)
28
+ end
29
+
30
+ proxy.replace(value)
31
+ value
32
+ end
33
+
34
+ def #{name}?
35
+ get_proxy(associations[#{name.inspect}]).present?
36
+ end
37
+
38
+ def build_#{name}(attrs={})
39
+ get_proxy(associations[#{name.inspect}]).build(attrs)
40
+ end
41
+
42
+ def create_#{name}(attrs={})
43
+ get_proxy(associations[#{name.inspect}]).create(attrs)
44
+ end
45
+
46
+ def create_#{name}!(attrs={})
47
+ get_proxy(associations[#{name.inspect}]).create!(attrs)
48
+ end
49
+ end_eval
50
+ end
51
+
52
+ def autosave?
53
+ options.fetch(:autosave, false)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -5,13 +5,18 @@ module MongoMapper
5
5
  class BelongsToPolymorphicProxy < Proxy
6
6
  def replace(doc)
7
7
  if doc
8
- doc.save if doc.new?
8
+ doc.save unless doc.persisted?
9
9
  id, type = doc.id, doc.class.name
10
10
  end
11
11
 
12
12
  proxy_owner[association.foreign_key] = id
13
13
  proxy_owner[association.type_key_name] = type
14
14
  reset
15
+ unless doc.nil?
16
+ loaded
17
+ @target = doc
18
+ end
19
+ @target
15
20
  end
16
21
 
17
22
  protected
@@ -5,12 +5,33 @@ module MongoMapper
5
5
  class BelongsToProxy < Proxy
6
6
  def replace(doc)
7
7
  if doc
8
- doc.save if doc.new?
8
+ doc.save if !doc.persisted?
9
9
  id = doc.id
10
10
  end
11
11
 
12
+ reset
12
13
  proxy_owner[association.foreign_key] = id
13
- reload
14
+ unless doc.nil?
15
+ loaded
16
+ @target = doc
17
+ end
18
+ @target
19
+ end
20
+
21
+ def build(attrs={})
22
+ instantiate_target(:new, attrs)
23
+ end
24
+
25
+ def create(attrs={})
26
+ instantiate_target(:create, attrs)
27
+ end
28
+
29
+ def create!(attrs={})
30
+ instantiate_target(:create!, attrs)
31
+ end
32
+
33
+ def save_to_collection(options={})
34
+ @target.save(options) if @target
14
35
  end
15
36
 
16
37
  protected
@@ -18,6 +39,13 @@ module MongoMapper
18
39
  return nil if proxy_owner[association.foreign_key].nil?
19
40
  klass.find_by_id(proxy_owner[association.foreign_key])
20
41
  end
42
+
43
+ def instantiate_target(instantiator, attrs={})
44
+ @target = klass.send(instantiator, attrs)
45
+ proxy_owner[association.foreign_key] = @target.id
46
+ loaded
47
+ @target
48
+ end
21
49
  end
22
50
  end
23
51
  end
@@ -30,6 +30,10 @@ module MongoMapper
30
30
  alias_method :push, :<<
31
31
  alias_method :concat, :<<
32
32
 
33
+ def save_to_collection(options={})
34
+ @target.each { |doc| doc.persist(options) } if @target
35
+ end
36
+
33
37
  private
34
38
  def assign_references(*docs)
35
39
  docs.each { |doc| doc._parent_document = proxy_owner }
@@ -14,22 +14,27 @@ module MongoMapper
14
14
  end
15
15
 
16
16
  def paginate(options)
17
+ return [] if ids.blank?
17
18
  query.paginate(options)
18
19
  end
19
20
 
20
21
  def all(options={})
22
+ return [] if ids.blank?
21
23
  query(options).all
22
24
  end
23
25
 
24
26
  def first(options={})
27
+ return nil if ids.blank?
25
28
  query(options).first
26
29
  end
27
30
 
28
31
  def last(options={})
32
+ return nil if ids.blank?
29
33
  query(options).last
30
34
  end
31
35
 
32
36
  def count(options={})
37
+ return 0 if ids.blank?
33
38
  options.blank? ? ids.size : query(options).count
34
39
  end
35
40
 
@@ -44,7 +49,7 @@ module MongoMapper
44
49
  def delete_all(options={})
45
50
  docs = query(options).fields(:_id).all
46
51
  docs.each { |doc| ids.delete(doc.id) }
47
- klass.delete(docs.map(&:id))
52
+ klass.delete(docs.map { |d| d.id })
48
53
  reset
49
54
  end
50
55
 
@@ -55,7 +60,7 @@ module MongoMapper
55
60
 
56
61
  def create(attrs={})
57
62
  doc = klass.create(attrs)
58
- unless doc.new?
63
+ if doc.persisted?
59
64
  ids << doc.id
60
65
  proxy_owner.save
61
66
  reset
@@ -65,7 +70,7 @@ module MongoMapper
65
70
 
66
71
  def create!(attrs={})
67
72
  doc = klass.create!(attrs)
68
- unless doc.new?
73
+ if doc.persisted?
69
74
  ids << doc.id
70
75
  proxy_owner.save
71
76
  reset
@@ -75,7 +80,7 @@ module MongoMapper
75
80
 
76
81
  def <<(*docs)
77
82
  flatten_deeper(docs).each do |doc|
78
- doc.save if doc.new?
83
+ doc.save unless doc.persisted?
79
84
  unless ids.include?(doc.id)
80
85
  ids << doc.id
81
86
  end
@@ -87,7 +92,7 @@ module MongoMapper
87
92
 
88
93
  def replace(docs)
89
94
  doc_ids = docs.map do |doc|
90
- doc.save if doc.new?
95
+ doc.save unless doc.persisted?
91
96
  doc.id
92
97
  end
93
98
  ids.replace(doc_ids.uniq)
@@ -115,7 +120,8 @@ module MongoMapper
115
120
  end
116
121
 
117
122
  def find_target
118
- ids.blank? ? [] : all
123
+ return [] if ids.blank?
124
+ all
119
125
  end
120
126
 
121
127
  def ids
@@ -0,0 +1,67 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class ManyAssociation < Base
6
+
7
+ def class_name
8
+ @class_name ||= options[:class_name] || name.to_s.singularize.camelize
9
+ end
10
+
11
+ def type_key_name
12
+ "_type"
13
+ end
14
+
15
+ # hate this, need to revisit
16
+ def proxy_class
17
+ @proxy_class ||= if klass.embeddable?
18
+ polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
19
+ else
20
+ if polymorphic?
21
+ ManyPolymorphicProxy
22
+ elsif as?
23
+ ManyDocumentsAsProxy
24
+ elsif in_array?
25
+ InArrayProxy
26
+ else
27
+ ManyDocumentsProxy
28
+ end
29
+ end
30
+ end
31
+
32
+ def setup(model)
33
+ model.associations_module.module_eval <<-end_eval
34
+ def #{name}
35
+ get_proxy(associations[#{name.inspect}])
36
+ end
37
+
38
+ def #{name}=(value)
39
+ get_proxy(associations[#{name.inspect}]).replace(value)
40
+ value
41
+ end
42
+ end_eval
43
+
44
+ if options[:dependent] && !embeddable?
45
+ association = self
46
+ options = self.options
47
+
48
+ model.after_destroy do
49
+ case options[:dependent]
50
+ when :destroy
51
+ self.get_proxy(association).destroy_all
52
+ when :delete_all
53
+ self.get_proxy(association).delete_all
54
+ when :nullify
55
+ self.get_proxy(association).nullify
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def autosave?
62
+ options.fetch(:autosave, true)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -8,7 +8,7 @@ module MongoMapper
8
8
 
9
9
  def replace(docs)
10
10
  load_target
11
- target.map(&:destroy)
11
+ target.each { |t| t.destroy }
12
12
  docs.each { |doc| prepare(doc).save }
13
13
  reset
14
14
  end
@@ -44,7 +44,7 @@ module MongoMapper
44
44
  end
45
45
 
46
46
  def destroy_all(options={})
47
- all(options).map(&:destroy)
47
+ all(options).each { |doc| doc.destroy }
48
48
  reset
49
49
  end
50
50
 
@@ -72,7 +72,7 @@ module MongoMapper
72
72
  def method_missing(method, *args, &block)
73
73
  if klass.respond_to?(method)
74
74
  result = klass.send(method, *args, &block)
75
- result.is_a?(Plucky::Query) ?
75
+ result.is_a?(Plucky::Query) ?
76
76
  query.merge(result) : super
77
77
  else
78
78
  super
@@ -88,7 +88,7 @@ module MongoMapper
88
88
  end
89
89
 
90
90
  def ensure_owner_saved
91
- proxy_owner.save if proxy_owner.new?
91
+ proxy_owner.save unless proxy_owner.persisted?
92
92
  end
93
93
 
94
94
  def prepare(doc)
@@ -101,7 +101,7 @@ module MongoMapper
101
101
  end
102
102
 
103
103
  def foreign_key
104
- options[:foreign_key] || proxy_owner.class.name.to_s.underscore.gsub("/", "_") + "_id"
104
+ options[:foreign_key] || proxy_owner.class.name.foreign_key
105
105
  end
106
106
  end
107
107
  end
@@ -4,7 +4,7 @@ module MongoMapper
4
4
  module Associations
5
5
  class ManyEmbeddedProxy < EmbeddedCollection
6
6
  def replace(values)
7
- @_values = values.map do |v|
7
+ @_values = (values || []).map do |v|
8
8
  v.respond_to?(:attributes) ? v.attributes : v
9
9
  end
10
10
  reset