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
@@ -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