lookout-mongo_mapper 0.11.3

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 (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +33 -0
  3. data/UPGRADES +26 -0
  4. data/bin/mmconsole +59 -0
  5. data/examples/attr_accessible.rb +22 -0
  6. data/examples/attr_protected.rb +22 -0
  7. data/examples/cache_key.rb +24 -0
  8. data/examples/custom_types.rb +24 -0
  9. data/examples/identity_map.rb +33 -0
  10. data/examples/identity_map/automatic.rb +2 -0
  11. data/examples/keys.rb +40 -0
  12. data/examples/modifiers/set.rb +25 -0
  13. data/examples/plugins.rb +38 -0
  14. data/examples/querying.rb +35 -0
  15. data/examples/safe.rb +43 -0
  16. data/examples/scopes.rb +52 -0
  17. data/examples/validating/embedded_docs.rb +29 -0
  18. data/lib/mongo_mapper.rb +94 -0
  19. data/lib/mongo_mapper/connection.rb +96 -0
  20. data/lib/mongo_mapper/document.rb +42 -0
  21. data/lib/mongo_mapper/embedded_document.rb +32 -0
  22. data/lib/mongo_mapper/exceptions.rb +30 -0
  23. data/lib/mongo_mapper/extensions/array.rb +19 -0
  24. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  25. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  26. data/lib/mongo_mapper/extensions/date.rb +25 -0
  27. data/lib/mongo_mapper/extensions/float.rb +14 -0
  28. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  29. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  30. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  31. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  32. data/lib/mongo_mapper/extensions/object.rb +26 -0
  33. data/lib/mongo_mapper/extensions/object_id.rb +32 -0
  34. data/lib/mongo_mapper/extensions/set.rb +20 -0
  35. data/lib/mongo_mapper/extensions/string.rb +18 -0
  36. data/lib/mongo_mapper/extensions/time.rb +28 -0
  37. data/lib/mongo_mapper/locale/en.yml +5 -0
  38. data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
  39. data/lib/mongo_mapper/plugins.rb +22 -0
  40. data/lib/mongo_mapper/plugins/accessible.rb +52 -0
  41. data/lib/mongo_mapper/plugins/active_model.rb +18 -0
  42. data/lib/mongo_mapper/plugins/associations.rb +90 -0
  43. data/lib/mongo_mapper/plugins/associations/base.rb +92 -0
  44. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +54 -0
  45. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +34 -0
  46. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  47. data/lib/mongo_mapper/plugins/associations/collection.rb +27 -0
  48. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +44 -0
  49. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +133 -0
  50. data/lib/mongo_mapper/plugins/associations/many_association.rb +63 -0
  51. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  52. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +118 -0
  53. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  54. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  55. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  56. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +22 -0
  57. data/lib/mongo_mapper/plugins/associations/one_association.rb +48 -0
  58. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  59. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  60. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +95 -0
  61. data/lib/mongo_mapper/plugins/associations/proxy.rb +134 -0
  62. data/lib/mongo_mapper/plugins/associations/single_association.rb +46 -0
  63. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  64. data/lib/mongo_mapper/plugins/callbacks.rb +29 -0
  65. data/lib/mongo_mapper/plugins/clone.rb +22 -0
  66. data/lib/mongo_mapper/plugins/dirty.rb +60 -0
  67. data/lib/mongo_mapper/plugins/document.rb +41 -0
  68. data/lib/mongo_mapper/plugins/dynamic_querying.rb +45 -0
  69. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  70. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +56 -0
  71. data/lib/mongo_mapper/plugins/embedded_document.rb +53 -0
  72. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  73. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  74. data/lib/mongo_mapper/plugins/indexes.rb +13 -0
  75. data/lib/mongo_mapper/plugins/inspect.rb +16 -0
  76. data/lib/mongo_mapper/plugins/keys.rb +313 -0
  77. data/lib/mongo_mapper/plugins/keys/key.rb +61 -0
  78. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  79. data/lib/mongo_mapper/plugins/modifiers.rb +134 -0
  80. data/lib/mongo_mapper/plugins/pagination.rb +16 -0
  81. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  82. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  83. data/lib/mongo_mapper/plugins/querying.rb +165 -0
  84. data/lib/mongo_mapper/plugins/querying/decorator.rb +36 -0
  85. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  86. data/lib/mongo_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  87. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  88. data/lib/mongo_mapper/plugins/sci.rb +36 -0
  89. data/lib/mongo_mapper/plugins/scopes.rb +27 -0
  90. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  91. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  92. data/lib/mongo_mapper/plugins/touch.rb +18 -0
  93. data/lib/mongo_mapper/plugins/userstamps.rb +18 -0
  94. data/lib/mongo_mapper/plugins/validations.rb +86 -0
  95. data/lib/mongo_mapper/railtie.rb +48 -0
  96. data/lib/mongo_mapper/railtie/database.rake +65 -0
  97. data/lib/mongo_mapper/translation.rb +10 -0
  98. data/lib/mongo_mapper/version.rb +4 -0
  99. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
  100. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
  101. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
  102. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +13 -0
  103. data/test/_NOTE_ON_TESTING +1 -0
  104. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +64 -0
  105. data/test/functional/associations/test_belongs_to_proxy.rb +238 -0
  106. data/test/functional/associations/test_in_array_proxy.rb +349 -0
  107. data/test/functional/associations/test_many_documents_as_proxy.rb +231 -0
  108. data/test/functional/associations/test_many_documents_proxy.rb +866 -0
  109. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +239 -0
  110. data/test/functional/associations/test_many_embedded_proxy.rb +289 -0
  111. data/test/functional/associations/test_many_polymorphic_proxy.rb +303 -0
  112. data/test/functional/associations/test_one_as_proxy.rb +491 -0
  113. data/test/functional/associations/test_one_embedded_polymorphic_proxy.rb +208 -0
  114. data/test/functional/associations/test_one_embedded_proxy.rb +100 -0
  115. data/test/functional/associations/test_one_proxy.rb +383 -0
  116. data/test/functional/test_accessible.rb +198 -0
  117. data/test/functional/test_associations.rb +46 -0
  118. data/test/functional/test_binary.rb +27 -0
  119. data/test/functional/test_caching.rb +77 -0
  120. data/test/functional/test_callbacks.rb +232 -0
  121. data/test/functional/test_dirty.rb +301 -0
  122. data/test/functional/test_document.rb +282 -0
  123. data/test/functional/test_dynamic_querying.rb +75 -0
  124. data/test/functional/test_embedded_document.rb +288 -0
  125. data/test/functional/test_equality.rb +20 -0
  126. data/test/functional/test_identity_map.rb +513 -0
  127. data/test/functional/test_indexes.rb +50 -0
  128. data/test/functional/test_logger.rb +20 -0
  129. data/test/functional/test_modifiers.rb +537 -0
  130. data/test/functional/test_pagination.rb +91 -0
  131. data/test/functional/test_protected.rb +201 -0
  132. data/test/functional/test_querying.rb +935 -0
  133. data/test/functional/test_safe.rb +76 -0
  134. data/test/functional/test_sci.rb +240 -0
  135. data/test/functional/test_scopes.rb +171 -0
  136. data/test/functional/test_timestamps.rb +62 -0
  137. data/test/functional/test_touch.rb +125 -0
  138. data/test/functional/test_userstamps.rb +44 -0
  139. data/test/functional/test_validations.rb +414 -0
  140. data/test/models.rb +261 -0
  141. data/test/support/railtie.rb +4 -0
  142. data/test/support/railtie/autoloaded.rb +2 -0
  143. data/test/support/railtie/not_autoloaded.rb +3 -0
  144. data/test/support/railtie/parent.rb +3 -0
  145. data/test/test_active_model_lint.rb +18 -0
  146. data/test/test_helper.rb +93 -0
  147. data/test/unit/associations/test_base.rb +146 -0
  148. data/test/unit/associations/test_belongs_to_association.rb +29 -0
  149. data/test/unit/associations/test_many_association.rb +63 -0
  150. data/test/unit/associations/test_one_association.rb +47 -0
  151. data/test/unit/associations/test_proxy.rb +100 -0
  152. data/test/unit/serializers/test_json_serializer.rb +216 -0
  153. data/test/unit/serializers/test_xml_serializer.rb +196 -0
  154. data/test/unit/test_clone.rb +69 -0
  155. data/test/unit/test_document.rb +249 -0
  156. data/test/unit/test_dynamic_finder.rb +125 -0
  157. data/test/unit/test_embedded_document.rb +682 -0
  158. data/test/unit/test_equality.rb +38 -0
  159. data/test/unit/test_exceptions.rb +12 -0
  160. data/test/unit/test_extensions.rb +380 -0
  161. data/test/unit/test_identity_map_middleware.rb +34 -0
  162. data/test/unit/test_inspect.rb +47 -0
  163. data/test/unit/test_key.rb +205 -0
  164. data/test/unit/test_keys.rb +65 -0
  165. data/test/unit/test_mongo_mapper.rb +143 -0
  166. data/test/unit/test_pagination.rb +11 -0
  167. data/test/unit/test_plugins.rb +89 -0
  168. data/test/unit/test_rails.rb +183 -0
  169. data/test/unit/test_rails_compatibility.rb +38 -0
  170. data/test/unit/test_rails_reflect_on_association.rb +118 -0
  171. data/test/unit/test_railtie.rb +66 -0
  172. data/test/unit/test_serialization.rb +166 -0
  173. data/test/unit/test_time_zones.rb +44 -0
  174. data/test/unit/test_translation.rb +27 -0
  175. data/test/unit/test_validations.rb +562 -0
  176. metadata +285 -0
@@ -0,0 +1,125 @@
1
+ require 'test_helper'
2
+
3
+ class DynamicFinderTest < Test::Unit::TestCase
4
+ DynamicFinder = MongoMapper::Plugins::DynamicQuerying::DynamicFinder
5
+
6
+ should "initialize with method" do
7
+ finder = DynamicFinder.new(:foobar)
8
+ finder.method.should == :foobar
9
+ end
10
+
11
+ context "found?" do
12
+ should "be true for find_by" do
13
+ DynamicFinder.new(:find_by_foo).found?.should be_true
14
+ end
15
+
16
+ should "be true for find_by with !" do
17
+ DynamicFinder.new(:find_by_foo!).found?.should be_true
18
+ end
19
+
20
+ should "be true for find_all_by" do
21
+ DynamicFinder.new(:find_all_by_foo).found?.should be_true
22
+ end
23
+
24
+ should "be true for find_or_initialize_by" do
25
+ DynamicFinder.new(:find_or_initialize_by_foo).found?.should be_true
26
+ end
27
+
28
+ should "be true for find_or_create_by" do
29
+ DynamicFinder.new(:find_or_create_by_foo).found?.should be_true
30
+ end
31
+
32
+ should "be false for anything else" do
33
+ [:foobar, :bazwick].each do |method|
34
+ DynamicFinder.new(method).found?.should be_false
35
+ end
36
+ end
37
+ end
38
+
39
+ context "find_all_by" do
40
+ should "parse one attribute" do
41
+ DynamicFinder.new(:find_all_by_foo).attributes.should == %w(foo)
42
+ end
43
+
44
+ should "parse multiple attributes" do
45
+ DynamicFinder.new(:find_all_by_foo_and_bar).attributes.should == %w(foo bar)
46
+ DynamicFinder.new(:find_all_by_foo_and_bar_and_baz).attributes.should == %w(foo bar baz)
47
+ end
48
+
49
+ should "set finder to :all" do
50
+ DynamicFinder.new(:find_all_by_foo_and_bar).finder.should == :all
51
+ end
52
+ end
53
+
54
+ context "find_by" do
55
+ should "parse one attribute" do
56
+ DynamicFinder.new(:find_by_foo).attributes.should == %w(foo)
57
+ end
58
+
59
+ should "parse multiple attributes" do
60
+ DynamicFinder.new(:find_by_foo_and_bar).attributes.should == %w(foo bar)
61
+ end
62
+
63
+ should "set finder to :first" do
64
+ DynamicFinder.new(:find_by_foo).finder.should == :first
65
+ end
66
+
67
+ should "set bang to false" do
68
+ DynamicFinder.new(:find_by_foo).bang.should be_false
69
+ end
70
+ end
71
+
72
+ context "find_by with !" do
73
+ should "parse one attribute" do
74
+ DynamicFinder.new(:find_by_foo!).attributes.should == %w(foo)
75
+ end
76
+
77
+ should "parse multiple attributes" do
78
+ DynamicFinder.new(:find_by_foo_and_bar!).attributes.should == %w(foo bar)
79
+ end
80
+
81
+ should "set finder to :first" do
82
+ DynamicFinder.new(:find_by_foo!).finder.should == :first
83
+ end
84
+
85
+ should "set bang to true" do
86
+ DynamicFinder.new(:find_by_foo!).bang.should be_true
87
+ end
88
+ end
89
+
90
+ context "find_or_initialize_by" do
91
+ should "parse one attribute" do
92
+ DynamicFinder.new(:find_or_initialize_by_foo).attributes.should == %w(foo)
93
+ end
94
+
95
+ should "parse multiple attributes" do
96
+ DynamicFinder.new(:find_or_initialize_by_foo_and_bar).attributes.should == %w(foo bar)
97
+ end
98
+
99
+ should "set finder to :first" do
100
+ DynamicFinder.new(:find_or_initialize_by_foo).finder.should == :first
101
+ end
102
+
103
+ should "set instantiator to new" do
104
+ DynamicFinder.new(:find_or_initialize_by_foo).instantiator.should == :new
105
+ end
106
+ end
107
+
108
+ context "find_or_create_by" do
109
+ should "parse one attribute" do
110
+ DynamicFinder.new(:find_or_create_by_foo).attributes.should == %w(foo)
111
+ end
112
+
113
+ should "parse multiple attributes" do
114
+ DynamicFinder.new(:find_or_create_by_foo_and_bar).attributes.should == %w(foo bar)
115
+ end
116
+
117
+ should "set finder to :first" do
118
+ DynamicFinder.new(:find_or_create_by_foo).finder.should == :first
119
+ end
120
+
121
+ should "set instantiator to new" do
122
+ DynamicFinder.new(:find_or_create_by_foo).instantiator.should == :create
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,682 @@
1
+ require 'test_helper'
2
+
3
+ module KeyOverride
4
+ def other_child
5
+ self[:other_child] || "special result"
6
+ end
7
+
8
+ def other_child=(value)
9
+ super(value + " modified")
10
+ end
11
+ end
12
+
13
+ class EmbeddedDocumentTest < Test::Unit::TestCase
14
+ context "EmbeddedDocuments" do
15
+ setup do
16
+ class ::Grandparent
17
+ include MongoMapper::EmbeddedDocument
18
+ key :grandparent, String
19
+ end
20
+
21
+ class ::Parent < ::Grandparent
22
+ include MongoMapper::EmbeddedDocument
23
+ key :parent, String
24
+ end
25
+
26
+ class ::Child < ::Parent
27
+ include MongoMapper::EmbeddedDocument
28
+ key :child, String
29
+ end
30
+
31
+ class ::OtherChild < ::Parent
32
+ include MongoMapper::EmbeddedDocument
33
+ include KeyOverride
34
+
35
+ key :other_child, String
36
+ end
37
+
38
+ class ::EDocWithAValidation
39
+ include MongoMapper::EmbeddedDocument
40
+ key :name, String, :required => true
41
+ end
42
+
43
+ class ::DocWithAValidation
44
+ include MongoMapper::Document
45
+ key :name, String, :required => true
46
+ many :e_doc_with_a_validations
47
+ end
48
+ end
49
+
50
+ teardown do
51
+ Object.send :remove_const, 'Grandparent' if defined?(::Grandparent)
52
+ Object.send :remove_const, 'Parent' if defined?(::Parent)
53
+ Object.send :remove_const, 'Child' if defined?(::Child)
54
+ Object.send :remove_const, 'OtherChild' if defined?(::OtherChild)
55
+ Object.send :remove_const, 'EDocWithAValidation' if defined?(::EDocWithAValidation)
56
+ Object.send :remove_const, 'DocWithAValidation' if defined?(::DocWithAValidation)
57
+ end
58
+
59
+ context "Including MongoMapper::EmbeddedDocument in a class" do
60
+ setup do
61
+ @klass = EDoc()
62
+ end
63
+
64
+ should "add _id key" do
65
+ @klass.keys['_id'].should_not be_nil
66
+ end
67
+
68
+ should "know it is using object id" do
69
+ @klass.using_object_id?.should be_true
70
+ end
71
+
72
+ should "know it is not using object id if _id type is changed" do
73
+ @klass.key :_id, String
74
+ @klass.using_object_id?.should be_false
75
+ end
76
+ end
77
+
78
+ context "Class Methods" do
79
+ should "include logger" do
80
+ @klass = EDoc()
81
+ @klass.logger.should == MongoMapper.logger
82
+ @klass.logger.should be_instance_of(Logger)
83
+ end
84
+
85
+ should "return false for embeddable" do
86
+ EDoc().embeddable?.should be_true
87
+ end
88
+
89
+ context "#to_mongo" do
90
+ setup { @klass = EDoc() }
91
+
92
+ should "be nil if nil" do
93
+ @klass.to_mongo(nil).should be_nil
94
+ end
95
+
96
+ should "convert to_mongo for other values" do
97
+ doc = @klass.new(:foo => 'bar')
98
+ to_mongo = @klass.to_mongo(doc)
99
+ to_mongo.is_a?(Hash).should be_true
100
+ to_mongo['foo'].should == 'bar'
101
+ end
102
+ end
103
+
104
+ context "#from_mongo" do
105
+ setup { @klass = EDoc() }
106
+
107
+ should "be nil if nil" do
108
+ @klass.from_mongo(nil).should be_nil
109
+ end
110
+
111
+ should "be instance if instance of class" do
112
+ doc = @klass.new
113
+ @klass.from_mongo(doc).should == doc
114
+ end
115
+
116
+ should "be instance if hash of attributes" do
117
+ doc = @klass.from_mongo({:foo => 'bar'})
118
+ doc.instance_of?(@klass).should be_true
119
+ doc.foo.should == 'bar'
120
+ end
121
+ end
122
+
123
+ context "defining a key" do
124
+ setup do
125
+ @document = EDoc()
126
+ end
127
+
128
+ should "work with name" do
129
+ key = @document.key(:name)
130
+ key.name.should == 'name'
131
+ end
132
+
133
+ should "work with name and type" do
134
+ key = @document.key(:name, String)
135
+ key.name.should == 'name'
136
+ key.type.should == String
137
+ end
138
+
139
+ should "work with name, type and options" do
140
+ key = @document.key(:name, String, :required => true)
141
+ key.name.should == 'name'
142
+ key.type.should == String
143
+ key.options[:required].should be_true
144
+ end
145
+
146
+ should "work with name and options" do
147
+ key = @document.key(:name, :required => true)
148
+ key.name.should == 'name'
149
+ key.options[:required].should be_true
150
+ end
151
+
152
+ should "be tracked per document" do
153
+ @document.key(:name, String)
154
+ @document.key(:age, Integer)
155
+ @document.keys['name'].name.should == 'name'
156
+ @document.keys['name'].type.should == String
157
+ @document.keys['age'].name.should == 'age'
158
+ @document.keys['age'].type.should == Integer
159
+ end
160
+
161
+ should "be redefinable" do
162
+ @document.key(:foo, String)
163
+ @document.keys['foo'].type.should == String
164
+ @document.key(:foo, Integer)
165
+ @document.keys['foo'].type.should == Integer
166
+ end
167
+
168
+ should "create reader method" do
169
+ @document.new.should_not respond_to(:foo)
170
+ @document.key(:foo, String)
171
+ @document.new.should respond_to(:foo)
172
+ end
173
+
174
+ should "create reader before type cast method" do
175
+ @document.new.should_not respond_to(:foo_before_type_cast)
176
+ @document.key(:foo, String)
177
+ @document.new.should respond_to(:foo_before_type_cast)
178
+ end
179
+
180
+ should "create writer method" do
181
+ @document.new.should_not respond_to(:foo=)
182
+ @document.key(:foo, String)
183
+ @document.new.should respond_to(:foo=)
184
+ end
185
+
186
+ should "create boolean method" do
187
+ @document.new.should_not respond_to(:foo?)
188
+ @document.key(:foo, String)
189
+ @document.new.should respond_to(:foo?)
190
+ end
191
+ end
192
+
193
+ context "keys" do
194
+ should "be inherited" do
195
+ Grandparent.keys.keys.sort.should == ['_id', '_type', 'grandparent']
196
+ Parent.keys.keys.sort.should == ['_id', '_type', 'grandparent', 'parent']
197
+ Child.keys.keys.sort.should == ['_id', '_type', 'child', 'grandparent', 'parent']
198
+ end
199
+
200
+ should "propogate to descendants if key added after class definition" do
201
+ Grandparent.key :foo, String
202
+
203
+ Grandparent.keys.keys.sort.should == ['_id', '_type', 'foo', 'grandparent']
204
+ Parent.keys.keys.sort.should == ['_id', '_type', 'foo', 'grandparent', 'parent']
205
+ Child.keys.keys.sort.should == ['_id', '_type', 'child', 'foo', 'grandparent', 'parent']
206
+ end
207
+
208
+ should "not add anonymous objects to the ancestor tree" do
209
+ OtherChild.ancestors.any? { |a| a.name.blank? }.should be_false
210
+ end
211
+
212
+ should "not include descendant keys" do
213
+ lambda { Parent.new.other_child }.should raise_error
214
+ end
215
+ end
216
+
217
+ context "descendants" do
218
+ should "default to an empty array" do
219
+ Child.descendants.should == []
220
+ end
221
+
222
+ should "be recorded" do
223
+ Grandparent.direct_descendants.should == [Parent]
224
+ Grandparent.descendants.to_set.should == [Parent, Child, OtherChild].to_set
225
+
226
+ Parent.descendants.should == [Child, OtherChild]
227
+ end
228
+ end
229
+ end
230
+
231
+ context "An instance of an embedded document" do
232
+ setup do
233
+ @document = EDoc do
234
+ key :name, String
235
+ key :age, Integer
236
+ end
237
+ end
238
+
239
+ should "respond to cache_key" do
240
+ @document.new.should respond_to(:cache_key)
241
+ end
242
+
243
+ should "have access to class logger" do
244
+ doc = @document.new
245
+ doc.logger.should == @document.logger
246
+ doc.logger.should be_instance_of(Logger)
247
+ end
248
+
249
+ should "automatically have an _id key" do
250
+ @document.keys.keys.should include('_id')
251
+ end
252
+
253
+ should "create id during initialization" do
254
+ @document.new._id.should be_instance_of(BSON::ObjectId)
255
+ end
256
+
257
+ should "have id method returns _id" do
258
+ id = BSON::ObjectId.new
259
+ doc = @document.new(:_id => id)
260
+ doc.id.should == id
261
+ end
262
+
263
+ should "convert string object id to mongo object id when assigning id with _id object id type" do
264
+ id = BSON::ObjectId.new
265
+ doc = @document.new(:id => id.to_s)
266
+ doc._id.should == id
267
+ doc.id.should == id
268
+ doc = @document.new(:_id => id.to_s)
269
+ doc._id.should == id
270
+ doc.id.should == id
271
+ end
272
+
273
+ context "_parent_document" do
274
+ should "default to nil" do
275
+ @document.new._parent_document.should be_nil
276
+ @document.new._root_document.should be_nil
277
+ end
278
+
279
+ should "set _root_document when setting _parent_document" do
280
+ root = Doc().new
281
+ doc = @document.new(:_parent_document => root)
282
+ doc._parent_document.should be(root)
283
+ doc._root_document.should be(root)
284
+ end
285
+
286
+ should "set _root_document when setting _parent_document on embedded many" do
287
+ root = Doc().new
288
+ klass = EDoc { many :children }
289
+ parent = klass.new(:_parent_document => root, :children => [{}])
290
+ child = parent.children.first
291
+ child._parent_document.should be(parent)
292
+ child._root_document.should be(root)
293
+ end
294
+ end
295
+
296
+ context "being initialized" do
297
+ should "accept a hash that sets keys and values" do
298
+ doc = @document.new(:name => 'John', :age => 23)
299
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
300
+ doc.attributes['name'].should == 'John'
301
+ doc.attributes['age'].should == 23
302
+ end
303
+
304
+ should "be able to assign keys dynamically" do
305
+ doc = @document.new(:name => 'John', :skills => ['ruby', 'rails'])
306
+ doc.name.should == 'John'
307
+ doc.skills.should == ['ruby', 'rails']
308
+ end
309
+
310
+ should "not throw error if initialized with nil" do
311
+ assert_nothing_raised { @document.new(nil) }
312
+ end
313
+ end
314
+
315
+ context "initialized when _type key present" do
316
+ setup do
317
+ @klass = EDoc('FooBar') { key :_type, String }
318
+ end
319
+
320
+ should "set _type to class name" do
321
+ @klass.new._type.should == 'FooBar'
322
+ end
323
+
324
+ should "ignore _type attribute and always use class" do
325
+ @klass.new(:_type => 'Foo')._type.should == 'FooBar'
326
+ end
327
+ end
328
+
329
+ context "attributes=" do
330
+ should "update values for keys provided" do
331
+ doc = @document.new(:name => 'foobar', :age => 10)
332
+ doc.attributes = {:name => 'new value', :age => 5}
333
+ doc.attributes[:name].should == 'new value'
334
+ doc.attributes[:age].should == 5
335
+ end
336
+
337
+ should "not update values for keys that were not provided" do
338
+ doc = @document.new(:name => 'foobar', :age => 10)
339
+ doc.attributes = {:name => 'new value'}
340
+ doc.attributes[:name].should == 'new value'
341
+ doc.attributes[:age].should == 10
342
+ end
343
+
344
+ should "work with pre-defined methods" do
345
+ @document.class_eval do
346
+ attr_writer :password
347
+
348
+ def passwd
349
+ @password
350
+ end
351
+ end
352
+
353
+ doc = @document.new(:name => 'foobar', :password => 'secret')
354
+ doc.passwd.should == 'secret'
355
+ end
356
+
357
+ should "type cast key values" do
358
+ doc = @document.new(:name => 1234, :age => '21')
359
+ doc.name.should == '1234'
360
+ doc.age.should == 21
361
+ end
362
+ end
363
+
364
+ context "attributes" do
365
+ should "default to hash with all keys" do
366
+ doc = @document.new
367
+ doc.keys.keys.sort.should == ['_id', 'age', 'name']
368
+ doc.attributes.keys.sort.should == ['_id']
369
+ end
370
+
371
+ should "return all keys with values" do
372
+ doc = @document.new(:name => 'string', :age => nil)
373
+ doc.attributes.keys.sort.should == ['_id', 'name']
374
+ doc.keys.keys.sort.should == ['_id', 'age', 'name']
375
+ doc.attributes.values.should include('string')
376
+ doc.attributes.values.should_not include(nil)
377
+ end
378
+
379
+ should "have indifferent access" do
380
+ doc = @document.new(:name => 'string')
381
+ doc.attributes[:name].should == 'string'
382
+ doc.attributes['name'].should == 'string'
383
+ end
384
+ end
385
+
386
+ context "to_mongo" do
387
+ should "default to hash with _id key" do
388
+ doc = @document.new
389
+ doc.to_mongo.keys.sort.should == ['_id']
390
+ doc.keys.keys.sort.should == ['_id', 'age', 'name']
391
+ end
392
+
393
+ should "return all keys" do
394
+ doc = @document.new(:name => 'string', :age => nil)
395
+ doc.keys.keys.sort.should == ['_id', 'age', 'name']
396
+ doc.to_mongo.keys.sort.should == ['_id','name']
397
+ doc.to_mongo.values.should include('string')
398
+ doc.to_mongo.values.should_not include(nil)
399
+ end
400
+ end
401
+
402
+ context "key shorcut access" do
403
+ context "[]" do
404
+ should "work when key found" do
405
+ doc = @document.new(:name => 'string')
406
+ doc[:name].should == 'string'
407
+ end
408
+
409
+ should "return nil when not found" do
410
+ doc = @document.new(:name => 'string')
411
+ doc[:not_here].should be_nil
412
+ end
413
+ end
414
+
415
+ context "[]=" do
416
+ should "write key value for existing key" do
417
+ doc = @document.new
418
+ doc[:name] = 'string'
419
+ doc[:name].should == 'string'
420
+ end
421
+
422
+ should "create key and write value for missing key" do
423
+ doc = @document.new
424
+ doc[:foo] = 'string'
425
+ doc.class.keys.include?('foo').should be_true
426
+ doc[:foo].should == 'string'
427
+ end
428
+
429
+ should "share the new key with the class" do
430
+ doc = @document.new
431
+ doc[:foo] = 'string'
432
+ @document.keys.should include('foo')
433
+ end
434
+ end
435
+ end
436
+
437
+ context "reading a key" do
438
+ should "work for defined keys" do
439
+ doc = @document.new(:name => 'string')
440
+ doc.name.should == 'string'
441
+ end
442
+
443
+ should "raise no method error for undefined keys" do
444
+ doc = @document.new
445
+ lambda { doc.fart }.should raise_error(NoMethodError)
446
+ end
447
+
448
+ should "be accessible for use in the model" do
449
+ @document.class_eval do
450
+ def name_and_age
451
+ "#{self[:name]} (#{self[:age]})"
452
+ end
453
+ end
454
+
455
+ doc = @document.new(:name => 'John', :age => 27)
456
+ doc.name_and_age.should == 'John (27)'
457
+ end
458
+
459
+ should "set instance variable" do
460
+ @document.key :foo, Array
461
+ doc = @document.new
462
+ doc.instance_variable_get("@foo").should be_nil
463
+ doc.foo
464
+ doc.instance_variable_get("@foo").should == []
465
+ end
466
+
467
+ should "be overrideable by modules" do
468
+ @document = Doc do
469
+ key :other_child, String
470
+ end
471
+
472
+ child = @document.new
473
+ child.other_child.should be_nil
474
+
475
+ @document.send :include, KeyOverride
476
+
477
+ overriden_child = @document.new
478
+ overriden_child.other_child.should == 'special result'
479
+ end
480
+ end
481
+
482
+ context "reading a key before typcasting" do
483
+ should "work for defined keys" do
484
+ doc = @document.new(:name => 12)
485
+ doc.name_before_type_cast.should == 12
486
+ end
487
+
488
+ should "raise no method error for undefined keys" do
489
+ doc = @document.new
490
+ lambda { doc.foo_before_type_cast }.should raise_error(NoMethodError)
491
+ end
492
+
493
+ should "be accessible for use in a document" do
494
+ @document.class_eval do
495
+ def untypcasted_name
496
+ read_key_before_type_cast(:name)
497
+ end
498
+ end
499
+
500
+ doc = @document.new(:name => 12)
501
+ doc.name.should == '12'
502
+ doc.untypcasted_name.should == 12
503
+ end
504
+ end
505
+
506
+ context "writing a key" do
507
+ should "work for defined keys" do
508
+ doc = @document.new
509
+ doc.name = 'John'
510
+ doc.name.should == 'John'
511
+ end
512
+
513
+ should "raise no method error for undefined keys" do
514
+ doc = @document.new
515
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
516
+ end
517
+
518
+ should "type cast value" do
519
+ doc = @document.new
520
+ doc.name = 1234
521
+ doc.name.should == '1234'
522
+ doc.age = '21'
523
+ doc.age.should == 21
524
+ end
525
+
526
+ should "be accessible for use in the model" do
527
+ @document.class_eval do
528
+ def name_and_age=(new_value)
529
+ new_value.match(/([^\(\s]+) \((.*)\)/)
530
+ write_key :name, $1
531
+ write_key :age, $2
532
+ end
533
+ end
534
+
535
+ doc = @document.new
536
+ doc.name_and_age = 'Frank (62)'
537
+ doc.name.should == 'Frank'
538
+ doc.age.should == 62
539
+ end
540
+
541
+ should "be overrideable by modules" do
542
+ @document = Doc do
543
+ key :other_child, String
544
+ end
545
+
546
+ child = @document.new(:other_child => 'foo')
547
+ child.other_child.should == 'foo'
548
+
549
+ @document.send :include, KeyOverride
550
+
551
+ overriden_child = @document.new(:other_child => 'foo')
552
+ overriden_child.other_child.should == 'foo modified'
553
+ end
554
+ end # writing a key
555
+
556
+ context "checking if a keys value is present" do
557
+ should "work for defined keys" do
558
+ doc = @document.new
559
+ doc.name?.should be_false
560
+ doc.name = 'John'
561
+ doc.name?.should be_true
562
+ end
563
+
564
+ should "raise no method error for undefined keys" do
565
+ doc = @document.new
566
+ lambda { doc.fart? }.should raise_error(NoMethodError)
567
+ end
568
+ end
569
+
570
+ context "equality" do
571
+ setup do
572
+ @oid = BSON::ObjectId.new
573
+ end
574
+
575
+ should "delegate hash to _id" do
576
+ doc = @document.new
577
+ doc.hash.should == doc._id.hash
578
+ end
579
+
580
+ should "delegate eql to ==" do
581
+ doc = @document.new
582
+ other = @document.new
583
+ doc.eql?(other).should == (doc == other)
584
+ doc.eql?(doc).should == (doc == doc)
585
+ end
586
+
587
+ should "know if same object as another" do
588
+ doc = @document.new
589
+ doc.should equal(doc)
590
+ doc.should_not equal(@document.new)
591
+ end
592
+
593
+ should "allow set operations on array of documents" do
594
+ doc = @document.new
595
+ ([doc] & [doc]).should == [doc]
596
+ end
597
+
598
+ should "be equal if id and class are the same" do
599
+ (@document.new('_id' => @oid) == @document.new('_id' => @oid)).should be_true
600
+ end
601
+
602
+ should "not be equal if class same but id different" do
603
+ (@document.new('_id' => @oid) == @document.new('_id' => BSON::ObjectId.new)).should be_false
604
+ end
605
+
606
+ should "not be equal if id same but class different" do
607
+ another_document = Doc()
608
+ (@document.new('_id' => @oid) == another_document.new('_id' => @oid)).should be_false
609
+ end
610
+ end
611
+
612
+ context "reading keys with default values" do
613
+ setup do
614
+ @document = EDoc do
615
+ key :name, String, :default => 'foo'
616
+ key :age, Integer, :default => 20
617
+ key :net_worth, Float, :default => 100.00
618
+ key :active, Boolean, :default => true
619
+ key :smart, Boolean, :default => false
620
+ key :skills, Array, :default => [1]
621
+ key :options, Hash, :default => {'foo' => 'bar'}
622
+ end
623
+
624
+ @doc = @document.new
625
+ end
626
+
627
+ should "work for strings" do
628
+ @doc.name.should == 'foo'
629
+ end
630
+
631
+ should "work for integers" do
632
+ @doc.age.should == 20
633
+ end
634
+
635
+ should "work for floats" do
636
+ @doc.net_worth.should == 100.00
637
+ end
638
+
639
+ should "work for booleans" do
640
+ @doc.active.should == true
641
+ @doc.smart.should == false
642
+ end
643
+
644
+ should "work for arrays" do
645
+ @doc.skills.should == [1]
646
+ @doc.skills << 2
647
+ @doc.skills.should == [1, 2]
648
+ end
649
+
650
+ should "work for hashes" do
651
+ @doc.options['foo'].should == 'bar'
652
+ @doc.options['baz'] = 'wick'
653
+ @doc.options['baz'].should == 'wick'
654
+ end
655
+ end
656
+
657
+ context "#save!" do
658
+ setup do
659
+ @root = DocWithAValidation.create(:name => "Root")
660
+ @doc = @root.e_doc_with_a_validations.build :name => "Embedded"
661
+ end
662
+
663
+ should "should save when valid" do
664
+ @doc.save!
665
+ @root.reload.e_doc_with_a_validations.first.should == @doc
666
+ end
667
+
668
+ should "should raise errors when invalid" do
669
+ @doc.name = ''
670
+ lambda{ @doc.save! }.should raise_error(MongoMapper::DocumentNotValid, "Validation failed: Name can't be empty")
671
+ end
672
+
673
+ should "should raise errors when root document is invalid" do
674
+ @root.name = ''
675
+ @root.save(:validate => false)
676
+ lambda{ @doc.save! }.should raise_error(MongoMapper::DocumentNotValid, "Foo")
677
+ end
678
+ end
679
+ end # instance of a embedded document
680
+ end
681
+ end
682
+