mongo_mapper 0.6.10 → 0.7.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 (106) hide show
  1. data/README.rdoc +5 -14
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongo_mapper.rb +48 -56
  5. data/lib/mongo_mapper/document.rb +136 -164
  6. data/lib/mongo_mapper/embedded_document.rb +29 -354
  7. data/lib/mongo_mapper/plugins.rb +31 -0
  8. data/lib/mongo_mapper/plugins/associations.rb +105 -0
  9. data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
  10. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  11. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  12. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  13. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  14. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  15. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  16. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  17. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  18. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  19. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  20. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  21. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  22. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  23. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  24. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  25. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  26. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  27. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  28. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  29. data/lib/mongo_mapper/plugins/keys.rb +324 -0
  30. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  31. data/lib/mongo_mapper/plugins/pagination.rb +24 -0
  32. data/lib/mongo_mapper/plugins/pagination/proxy.rb +68 -0
  33. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  34. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  35. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  36. data/lib/mongo_mapper/plugins/validations.rb +46 -0
  37. data/lib/mongo_mapper/query.rb +130 -0
  38. data/lib/mongo_mapper/support.rb +40 -17
  39. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  40. data/lib/mongo_mapper/support/find.rb +77 -0
  41. data/mongo_mapper.gemspec +55 -38
  42. data/performance/read_write.rb +52 -0
  43. data/specs.watchr +23 -2
  44. data/test/functional/associations/test_belongs_to_proxy.rb +12 -10
  45. data/test/functional/associations/test_many_documents_as_proxy.rb +4 -21
  46. data/test/functional/associations/test_many_documents_proxy.rb +2 -8
  47. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +59 -39
  48. data/test/functional/associations/test_many_embedded_proxy.rb +145 -81
  49. data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -40
  50. data/test/functional/associations/test_one_proxy.rb +25 -10
  51. data/test/functional/test_binary.rb +2 -8
  52. data/test/functional/test_callbacks.rb +1 -5
  53. data/test/functional/test_dirty.rb +27 -23
  54. data/test/functional/test_document.rb +224 -165
  55. data/test/functional/test_embedded_document.rb +72 -82
  56. data/test/functional/test_identity_map.rb +508 -0
  57. data/test/functional/test_modifiers.rb +15 -5
  58. data/test/functional/test_pagination.rb +1 -3
  59. data/test/functional/test_protected.rb +155 -0
  60. data/test/functional/test_string_id_compatibility.rb +7 -12
  61. data/test/functional/test_validations.rb +26 -58
  62. data/test/models.rb +0 -39
  63. data/test/test_helper.rb +37 -3
  64. data/test/unit/associations/test_base.rb +5 -5
  65. data/test/unit/associations/test_proxy.rb +8 -6
  66. data/test/unit/test_descendant_appends.rb +71 -0
  67. data/test/unit/test_document.rb +71 -76
  68. data/test/unit/test_dynamic_finder.rb +27 -29
  69. data/test/unit/test_embedded_document.rb +555 -601
  70. data/test/unit/{test_key.rb → test_keys.rb} +2 -5
  71. data/test/unit/test_mongo_mapper.rb +69 -9
  72. data/test/unit/test_pagination.rb +40 -32
  73. data/test/unit/test_plugins.rb +50 -0
  74. data/test/unit/{test_finder_options.rb → test_query.rb} +74 -65
  75. data/test/unit/test_rails.rb +123 -0
  76. data/test/unit/{test_serializations.rb → test_serialization.rb} +1 -2
  77. data/test/unit/test_support.rb +23 -7
  78. data/test/unit/test_time_zones.rb +3 -4
  79. data/test/unit/test_validations.rb +58 -17
  80. metadata +53 -36
  81. data/lib/mongo_mapper/associations.rb +0 -78
  82. data/lib/mongo_mapper/associations/base.rb +0 -119
  83. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +0 -26
  84. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +0 -21
  85. data/lib/mongo_mapper/associations/collection.rb +0 -19
  86. data/lib/mongo_mapper/associations/in_array_proxy.rb +0 -137
  87. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -26
  88. data/lib/mongo_mapper/associations/many_documents_proxy.rb +0 -115
  89. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +0 -31
  90. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +0 -54
  91. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +0 -11
  92. data/lib/mongo_mapper/associations/one_proxy.rb +0 -64
  93. data/lib/mongo_mapper/associations/proxy.rb +0 -116
  94. data/lib/mongo_mapper/callbacks.rb +0 -61
  95. data/lib/mongo_mapper/dirty.rb +0 -117
  96. data/lib/mongo_mapper/dynamic_finder.rb +0 -74
  97. data/lib/mongo_mapper/finder_options.rb +0 -145
  98. data/lib/mongo_mapper/key.rb +0 -36
  99. data/lib/mongo_mapper/mongo_mapper.rb +0 -125
  100. data/lib/mongo_mapper/pagination.rb +0 -66
  101. data/lib/mongo_mapper/rails_compatibility/document.rb +0 -15
  102. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +0 -28
  103. data/lib/mongo_mapper/serialization.rb +0 -54
  104. data/lib/mongo_mapper/serializers/json_serializer.rb +0 -48
  105. data/lib/mongo_mapper/validations.rb +0 -39
  106. data/test/functional/test_rails_compatibility.rb +0 -25
@@ -0,0 +1,123 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class Base
5
+ attr_reader :type, :name, :options, :query_options
6
+
7
+ # Options that should not be considered MongoDB query options/criteria
8
+ AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic]
9
+
10
+ def initialize(type, name, options={}, &extension)
11
+ @type, @name, @options, @query_options, @original_options = type, name, {}, {}, options
12
+ options.symbolize_keys!
13
+ options[:extend] = modularized_extensions(extension, options[:extend])
14
+ separate_options_and_conditions
15
+ end
16
+
17
+ def class_name
18
+ return @class_name if defined?(@class_name)
19
+
20
+ @class_name =
21
+ if cn = options[:class_name]
22
+ cn
23
+ elsif many?
24
+ name.to_s.singularize.camelize
25
+ else
26
+ name.to_s.camelize
27
+ end
28
+ end
29
+
30
+ def klass
31
+ @klass ||= options[:class] || class_name.constantize
32
+ end
33
+
34
+ def many?
35
+ @type == :many
36
+ end
37
+
38
+ def belongs_to?
39
+ @type == :belongs_to
40
+ end
41
+
42
+ def one?
43
+ @type == :one
44
+ end
45
+
46
+ def polymorphic?
47
+ !!@options[:polymorphic]
48
+ end
49
+
50
+ def as?
51
+ !!@options[:as]
52
+ end
53
+
54
+ def in_array?
55
+ !!@options[:in]
56
+ end
57
+
58
+ def embeddable?
59
+ many? && klass.embeddable?
60
+ end
61
+
62
+ def type_key_name
63
+ many? ? '_type' : "#{as}_type"
64
+ end
65
+
66
+ def as
67
+ @options[:as] || self.name
68
+ end
69
+
70
+ def foreign_key
71
+ @options[:foreign_key] || "#{name}_id"
72
+ end
73
+
74
+ def ivar
75
+ @ivar ||= "@_#{name}"
76
+ end
77
+
78
+ # hate this, need to revisit
79
+ def proxy_class
80
+ return @proxy_class if defined?(@proxy_class)
81
+
82
+ @proxy_class =
83
+ if many?
84
+ if klass.embeddable?
85
+ polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
86
+ else
87
+ if polymorphic?
88
+ ManyPolymorphicProxy
89
+ elsif as?
90
+ ManyDocumentsAsProxy
91
+ elsif in_array?
92
+ InArrayProxy
93
+ else
94
+ ManyDocumentsProxy
95
+ end
96
+ end
97
+ elsif one?
98
+ OneProxy
99
+ else
100
+ polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
101
+ end
102
+ end
103
+
104
+ private
105
+ def separate_options_and_conditions
106
+ @original_options.each_pair do |key, value|
107
+ if AssociationOptions.include?(key)
108
+ @options[key] = value
109
+ else
110
+ @query_options[key] = value
111
+ end
112
+ end
113
+ end
114
+
115
+ def modularized_extensions(*extensions)
116
+ extensions.flatten.compact.map do |extension|
117
+ Proc === extension ? Module.new(&extension) : extension
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,30 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class BelongsToPolymorphicProxy < Proxy
5
+ undef_method :object_id
6
+
7
+ def replace(doc)
8
+ if doc
9
+ doc.save if doc.new?
10
+ id, type = doc.id, doc.class.name
11
+ end
12
+
13
+ owner[association.foreign_key] = id
14
+ owner[association.type_key_name] = type
15
+ reset
16
+ end
17
+
18
+ protected
19
+ def find_target
20
+ return nil if association_class.nil? || owner[association.foreign_key].nil?
21
+ association_class.find_by_id(owner[association.foreign_key])
22
+ end
23
+
24
+ def association_class
25
+ proxy_owner[association.type_key_name] ? proxy_owner[association.type_key_name].constantize : nil
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,25 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class BelongsToProxy < Proxy
5
+ undef_method :object_id
6
+
7
+ def replace(doc)
8
+ if doc
9
+ doc.save if doc.new?
10
+ id = doc.id
11
+ end
12
+
13
+ owner[association.foreign_key] = id
14
+ reset
15
+ end
16
+
17
+ protected
18
+ def find_target
19
+ return nil if owner[association.foreign_key].nil?
20
+ klass.find_by_id(owner[association.foreign_key])
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class Collection < Proxy
5
+ def to_ary
6
+ load_target
7
+ if target.is_a?(Array)
8
+ target.to_ary
9
+ else
10
+ Array(target)
11
+ end
12
+ end
13
+
14
+ def reset
15
+ super
16
+ target = []
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class EmbeddedCollection < Collection
5
+ def build(attributes={})
6
+ doc = klass.new(attributes)
7
+ assign_references(doc)
8
+ self << doc
9
+ doc
10
+ end
11
+
12
+ def find(id)
13
+ load_target
14
+ target.detect { |item| item.id.to_s == id || item.id == id }
15
+ end
16
+
17
+ def count
18
+ load_target
19
+ target.size
20
+ end
21
+
22
+ def <<(*docs)
23
+ load_target
24
+ docs.each do |doc|
25
+ assign_references(doc)
26
+ target << doc
27
+ end
28
+ end
29
+ alias_method :push, :<<
30
+ alias_method :concat, :<<
31
+
32
+ private
33
+ def _root_document
34
+ if owner.respond_to?(:_root_document)
35
+ owner._root_document
36
+ else
37
+ owner
38
+ end
39
+ end
40
+
41
+ def assign_references(*docs)
42
+ docs.each do |doc|
43
+ doc._root_document = _root_document
44
+ doc._parent_document = owner
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,139 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class InArrayProxy < Collection
5
+ include Support::Find
6
+
7
+ def find(*args)
8
+ options = args.extract_options!
9
+
10
+ case args.first
11
+ when :first
12
+ first(options)
13
+ when :last
14
+ last(options)
15
+ when :all
16
+ all(options)
17
+ else
18
+ klass.find(*scoped_ids(args) << scoped_options(options))
19
+ end
20
+ end
21
+
22
+ def find!(*args)
23
+ options = args.extract_options!
24
+
25
+ case args.first
26
+ when :first
27
+ first(options)
28
+ when :last
29
+ last(options)
30
+ when :all
31
+ all(options)
32
+ else
33
+ klass.find!(*scoped_ids(args) << scoped_options(options))
34
+ end
35
+ end
36
+
37
+ def paginate(options)
38
+ klass.paginate(scoped_options(options))
39
+ end
40
+
41
+ def all(options={})
42
+ klass.all(scoped_options(options))
43
+ end
44
+
45
+ def first(options={})
46
+ klass.first(scoped_options(options))
47
+ end
48
+
49
+ def last(options={})
50
+ klass.last(scoped_options(options))
51
+ end
52
+
53
+ def count(options={})
54
+ options.blank? ? ids.size : klass.count(scoped_options(options))
55
+ end
56
+
57
+ def destroy_all(options={})
58
+ all(options).each do |doc|
59
+ ids.delete(doc.id)
60
+ doc.destroy
61
+ end
62
+ reset
63
+ end
64
+
65
+ def delete_all(options={})
66
+ docs = all(options.merge(:select => ['_id']))
67
+ docs.each { |doc| ids.delete(doc.id) }
68
+ klass.delete(docs.map(&:id))
69
+ reset
70
+ end
71
+
72
+ def nullify
73
+ replace([])
74
+ reset
75
+ end
76
+
77
+ def create(attrs={})
78
+ doc = klass.create(attrs)
79
+ unless doc.new?
80
+ ids << doc.id
81
+ owner.save
82
+ end
83
+ doc
84
+ end
85
+
86
+ def create!(attrs={})
87
+ doc = klass.create!(attrs)
88
+ unless doc.new?
89
+ ids << doc.id
90
+ owner.save
91
+ end
92
+ doc
93
+ end
94
+
95
+ def <<(*docs)
96
+ flatten_deeper(docs).each do |doc|
97
+ doc.save if doc.new?
98
+ unless ids.include?(doc.id)
99
+ ids << doc.id
100
+ end
101
+ end
102
+ reset
103
+ end
104
+ alias_method :push, :<<
105
+ alias_method :concat, :<<
106
+
107
+ def replace(docs)
108
+ doc_ids = docs.map do |doc|
109
+ doc.save if doc.new?
110
+ doc.id
111
+ end
112
+ ids.replace(doc_ids.uniq)
113
+ reset
114
+ end
115
+
116
+ private
117
+ def scoped_conditions
118
+ {:_id => ids}
119
+ end
120
+
121
+ def scoped_options(options)
122
+ association.query_options.merge(options).merge(scoped_conditions)
123
+ end
124
+
125
+ def scoped_ids(args)
126
+ args.flatten.reject { |id| !ids.include?(id) }
127
+ end
128
+
129
+ def find_target
130
+ ids.blank? ? [] : all
131
+ end
132
+
133
+ def ids
134
+ owner[options[:in]]
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,28 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class ManyDocumentsAsProxy < ManyDocumentsProxy
5
+ protected
6
+ def scoped_conditions
7
+ {type_key_name => owner.class.name, id_key_name => owner.id}
8
+ end
9
+
10
+ def apply_scope(doc)
11
+ ensure_owner_saved
12
+ doc[type_key_name] = owner.class.name
13
+ doc[id_key_name] = owner.id
14
+ doc
15
+ end
16
+
17
+ private
18
+ def type_key_name
19
+ "#{options[:as]}_type"
20
+ end
21
+
22
+ def id_key_name
23
+ "#{options[:as]}_id"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,117 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class ManyDocumentsProxy < Collection
5
+ include Support::Find
6
+
7
+ def find(*args)
8
+ options = args.extract_options!
9
+ klass.find(*args << scoped_options(options))
10
+ end
11
+
12
+ def find!(*args)
13
+ options = args.extract_options!
14
+ klass.find!(*args << scoped_options(options))
15
+ end
16
+
17
+ def paginate(options)
18
+ klass.paginate(scoped_options(options))
19
+ end
20
+
21
+ def all(options={})
22
+ klass.all(scoped_options(options))
23
+ end
24
+
25
+ def first(options={})
26
+ klass.first(scoped_options(options))
27
+ end
28
+
29
+ def last(options={})
30
+ klass.last(scoped_options(options))
31
+ end
32
+
33
+ def count(options={})
34
+ klass.count(scoped_options(options))
35
+ end
36
+
37
+ def replace(docs)
38
+ load_target
39
+ target.map(&:destroy)
40
+ docs.each { |doc| apply_scope(doc).save }
41
+ reset
42
+ end
43
+
44
+ def <<(*docs)
45
+ ensure_owner_saved
46
+ flatten_deeper(docs).each { |doc| apply_scope(doc).save }
47
+ reset
48
+ end
49
+ alias_method :push, :<<
50
+ alias_method :concat, :<<
51
+
52
+ def build(attrs={})
53
+ doc = klass.new(attrs)
54
+ apply_scope(doc)
55
+ doc
56
+ end
57
+
58
+ def create(attrs={})
59
+ doc = klass.new(attrs)
60
+ apply_scope(doc).save
61
+ doc
62
+ end
63
+
64
+ def create!(attrs={})
65
+ doc = klass.new(attrs)
66
+ apply_scope(doc).save!
67
+ doc
68
+ end
69
+
70
+ def destroy_all(options={})
71
+ all(options).map(&:destroy)
72
+ reset
73
+ end
74
+
75
+ def delete_all(options={})
76
+ klass.delete_all(options.merge(scoped_conditions))
77
+ reset
78
+ end
79
+
80
+ def nullify
81
+ criteria = Query.new(klass, scoped_conditions).criteria
82
+ all(criteria).each do |doc|
83
+ doc.update_attributes(self.foreign_key => nil)
84
+ end
85
+ reset
86
+ end
87
+
88
+ protected
89
+ def scoped_conditions
90
+ {self.foreign_key => owner.id}
91
+ end
92
+
93
+ def scoped_options(options)
94
+ association.query_options.merge(options).merge(scoped_conditions)
95
+ end
96
+
97
+ def find_target
98
+ all
99
+ end
100
+
101
+ def ensure_owner_saved
102
+ owner.save if owner.new?
103
+ end
104
+
105
+ def apply_scope(doc)
106
+ ensure_owner_saved
107
+ doc[foreign_key] = owner.id
108
+ doc
109
+ end
110
+
111
+ def foreign_key
112
+ options[:foreign_key] || owner.class.name.underscore.gsub("/", "_") + "_id"
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end