mongo_doc_rails2 0.6.1

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 (142) hide show
  1. data/.document +5 -0
  2. data/.gitignore +8 -0
  3. data/HISTORY.md +11 -0
  4. data/LICENSE +20 -0
  5. data/README.textile +185 -0
  6. data/Rakefile +188 -0
  7. data/TODO +40 -0
  8. data/VERSION +1 -0
  9. data/data/.gitignore +2 -0
  10. data/examples/simple_document.rb +46 -0
  11. data/examples/simple_object.rb +34 -0
  12. data/features/collections.feature +9 -0
  13. data/features/embed_hash.feature +16 -0
  14. data/features/finders.feature +76 -0
  15. data/features/indexes.feature +28 -0
  16. data/features/mongodb.yml +7 -0
  17. data/features/mongodoc_base.feature +128 -0
  18. data/features/new_record.feature +36 -0
  19. data/features/partial_updates.feature +95 -0
  20. data/features/removing_documents.feature +68 -0
  21. data/features/saving_an_object.feature +15 -0
  22. data/features/scopes.feature +66 -0
  23. data/features/step_definitions/collection_steps.rb +17 -0
  24. data/features/step_definitions/document_steps.rb +149 -0
  25. data/features/step_definitions/documents.rb +40 -0
  26. data/features/step_definitions/embed_hash_steps.rb +6 -0
  27. data/features/step_definitions/finder_steps.rb +15 -0
  28. data/features/step_definitions/index_steps.rb +10 -0
  29. data/features/step_definitions/json_steps.rb +9 -0
  30. data/features/step_definitions/object_steps.rb +50 -0
  31. data/features/step_definitions/objects.rb +24 -0
  32. data/features/step_definitions/partial_update_steps.rb +31 -0
  33. data/features/step_definitions/query_steps.rb +66 -0
  34. data/features/step_definitions/removing_documents_steps.rb +14 -0
  35. data/features/step_definitions/scope_steps.rb +18 -0
  36. data/features/step_definitions/string_casting_steps.rb +29 -0
  37. data/features/step_definitions/util_steps.rb +7 -0
  38. data/features/string_casting.feature +10 -0
  39. data/features/support/support.rb +10 -0
  40. data/features/using_criteria.feature +142 -0
  41. data/lib/mongo_doc.rb +12 -0
  42. data/lib/mongo_doc/associations.rb +109 -0
  43. data/lib/mongo_doc/associations/collection_proxy.rb +121 -0
  44. data/lib/mongo_doc/associations/document_proxy.rb +65 -0
  45. data/lib/mongo_doc/associations/hash_proxy.rb +102 -0
  46. data/lib/mongo_doc/associations/proxy_base.rb +48 -0
  47. data/lib/mongo_doc/attributes.rb +84 -0
  48. data/lib/mongo_doc/bson.rb +31 -0
  49. data/lib/mongo_doc/collection.rb +82 -0
  50. data/lib/mongo_doc/connection.rb +88 -0
  51. data/lib/mongo_doc/contexts.rb +31 -0
  52. data/lib/mongo_doc/contexts/ids.rb +41 -0
  53. data/lib/mongo_doc/contexts/mongo.rb +272 -0
  54. data/lib/mongo_doc/criteria.rb +70 -0
  55. data/lib/mongo_doc/cursor.rb +32 -0
  56. data/lib/mongo_doc/document.rb +205 -0
  57. data/lib/mongo_doc/ext.rb +16 -0
  58. data/lib/mongo_doc/ext/array.rb +5 -0
  59. data/lib/mongo_doc/ext/binary.rb +7 -0
  60. data/lib/mongo_doc/ext/boolean_class.rb +17 -0
  61. data/lib/mongo_doc/ext/date.rb +19 -0
  62. data/lib/mongo_doc/ext/date_time.rb +17 -0
  63. data/lib/mongo_doc/ext/dbref.rb +7 -0
  64. data/lib/mongo_doc/ext/hash.rb +7 -0
  65. data/lib/mongo_doc/ext/min_max_keys.rb +13 -0
  66. data/lib/mongo_doc/ext/nil_class.rb +5 -0
  67. data/lib/mongo_doc/ext/numeric.rb +17 -0
  68. data/lib/mongo_doc/ext/object.rb +19 -0
  69. data/lib/mongo_doc/ext/object_id.rb +7 -0
  70. data/lib/mongo_doc/ext/regexp.rb +5 -0
  71. data/lib/mongo_doc/ext/string.rb +5 -0
  72. data/lib/mongo_doc/ext/symbol.rb +5 -0
  73. data/lib/mongo_doc/ext/time.rb +9 -0
  74. data/lib/mongo_doc/finders.rb +38 -0
  75. data/lib/mongo_doc/index.rb +46 -0
  76. data/lib/mongo_doc/matchers.rb +35 -0
  77. data/lib/mongo_doc/root.rb +26 -0
  78. data/lib/mongo_doc/scope.rb +64 -0
  79. data/lib/mongo_doc/validations.rb +12 -0
  80. data/lib/mongo_doc/validations/macros.rb +11 -0
  81. data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
  82. data/lib/mongoid/contexts/enumerable.rb +151 -0
  83. data/lib/mongoid/contexts/paging.rb +42 -0
  84. data/lib/mongoid/criteria.rb +239 -0
  85. data/lib/mongoid/criterion/complex.rb +21 -0
  86. data/lib/mongoid/criterion/exclusion.rb +65 -0
  87. data/lib/mongoid/criterion/inclusion.rb +93 -0
  88. data/lib/mongoid/criterion/optional.rb +136 -0
  89. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  90. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  91. data/lib/mongoid/matchers/all.rb +11 -0
  92. data/lib/mongoid/matchers/default.rb +26 -0
  93. data/lib/mongoid/matchers/exists.rb +13 -0
  94. data/lib/mongoid/matchers/gt.rb +11 -0
  95. data/lib/mongoid/matchers/gte.rb +11 -0
  96. data/lib/mongoid/matchers/in.rb +11 -0
  97. data/lib/mongoid/matchers/lt.rb +11 -0
  98. data/lib/mongoid/matchers/lte.rb +11 -0
  99. data/lib/mongoid/matchers/ne.rb +11 -0
  100. data/lib/mongoid/matchers/nin.rb +11 -0
  101. data/lib/mongoid/matchers/size.rb +11 -0
  102. data/mongo_doc_rails2.gemspec +237 -0
  103. data/mongod.example.yml +2 -0
  104. data/mongodb.example.yml +14 -0
  105. data/perf/mongo_doc_object.rb +83 -0
  106. data/perf/mongo_document.rb +84 -0
  107. data/perf/ruby_driver.rb +49 -0
  108. data/script/console +8 -0
  109. data/spec/array_including_argument_matcher.rb +62 -0
  110. data/spec/associations/collection_proxy_spec.rb +233 -0
  111. data/spec/associations/document_proxy_spec.rb +45 -0
  112. data/spec/associations/hash_proxy_spec.rb +181 -0
  113. data/spec/associations/proxy_base_spec.rb +92 -0
  114. data/spec/associations_spec.rb +218 -0
  115. data/spec/attributes_accessor_spec.rb +33 -0
  116. data/spec/attributes_spec.rb +145 -0
  117. data/spec/bson_matchers.rb +54 -0
  118. data/spec/bson_spec.rb +196 -0
  119. data/spec/collection_spec.rb +169 -0
  120. data/spec/connection_spec.rb +147 -0
  121. data/spec/contexts/ids_spec.rb +49 -0
  122. data/spec/contexts/mongo_spec.rb +235 -0
  123. data/spec/contexts_spec.rb +56 -0
  124. data/spec/criteria_spec.rb +69 -0
  125. data/spec/cursor_spec.rb +91 -0
  126. data/spec/document_ext.rb +9 -0
  127. data/spec/document_spec.rb +553 -0
  128. data/spec/embedded_save_spec.rb +73 -0
  129. data/spec/ext_spec.rb +89 -0
  130. data/spec/finders_spec.rb +61 -0
  131. data/spec/hash_matchers.rb +27 -0
  132. data/spec/index_spec.rb +79 -0
  133. data/spec/matchers_spec.rb +342 -0
  134. data/spec/mongodb.yml +6 -0
  135. data/spec/mongodb_pairs.yml +8 -0
  136. data/spec/new_record_spec.rb +128 -0
  137. data/spec/root_spec.rb +41 -0
  138. data/spec/scope_spec.rb +79 -0
  139. data/spec/spec.opts +2 -0
  140. data/spec/spec_helper.rb +14 -0
  141. data/spec/validations_spec.rb +30 -0
  142. metadata +346 -0
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe "MongoDoc::Associations::DocumentProxy" do
4
+ class Parent
5
+ include MongoDoc::Document
6
+ end
7
+
8
+ class Child
9
+ include MongoDoc::Document
10
+ end
11
+
12
+ let(:parent) { Parent.new }
13
+ let(:name) {'association_name'}
14
+
15
+ subject do
16
+ MongoDoc::Associations::DocumentProxy.new(:assoc_name => name, :root => parent, :parent => parent, :assoc_class => Child)
17
+ end
18
+
19
+ describe "#build" do
20
+ it "#build builds a new object" do
21
+ Child.should === subject.build({})
22
+ end
23
+ end
24
+
25
+ context "delegated to the document" do
26
+ %w(id to_bson).each do |method|
27
+ it "delegates #{method} to the document" do
28
+ subject.stub(:document => stub)
29
+ subject.document.should_receive(method)
30
+ subject.send(method)
31
+ end
32
+ end
33
+ end
34
+
35
+ %w(_modifier_path= _selector_path=).each do |setter|
36
+ describe "##{setter}" do
37
+ it "delegates to the document with our assoc name" do
38
+ subject.stub(:document => stub)
39
+ subject.document.should_receive(setter).with("new.path.#{name}")
40
+ MongoDoc::Associations::ProxyBase.stub(:is_document?).and_return(true)
41
+ subject.send("#{setter}", 'new.path')
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,181 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
2
+
3
+ describe MongoDoc::Associations::HashProxy do
4
+ class HashProxyTest
5
+ include MongoDoc::Document
6
+
7
+ attr_accessor :name
8
+ end
9
+
10
+ let(:name) { 'embed_hash_name' }
11
+ let(:root) { HashProxyTest.new }
12
+ let(:proxy) { MongoDoc::Associations::HashProxy.new(:assoc_name => name, :assoc_class => HashProxyTest, :root => root, :parent => root) }
13
+ let(:item) { HashProxyTest.new }
14
+ let(:other_item) {[1,2]}
15
+
16
+ %w(_modifier_path= _selector_path=).each do |setter|
17
+ describe "##{setter}" do
18
+ it "delegates to the document with our assoc name and the key" do
19
+ document = stub
20
+ proxy.stub(:hash).and_return(:key => document)
21
+ document.should_receive(setter).with("new.path.#{name}.key")
22
+ MongoDoc::Associations::ProxyBase.stub(:is_document?).and_return(true)
23
+ proxy.send("#{setter}", 'new.path')
24
+ end
25
+ end
26
+ end
27
+
28
+ context "#[]=" do
29
+ it "adds the item to the hash" do
30
+ proxy['new'] = item
31
+ proxy['new'].should == item
32
+ end
33
+
34
+ context "key names must be a string or symbol constrained by BSON element name" do
35
+ ['$invalid', 'in.valid', :_id, 'query', 1, Object.new].each do |name|
36
+ it "#{name} is invalid" do
37
+ expect do
38
+ proxy[name] = other_item
39
+ end.to raise_error(MongoDoc::InvalidEmbeddedHashKey)
40
+ end
41
+ end
42
+
43
+ [:key, 'key'].each do |name|
44
+ it "#{name} is a valid name" do
45
+ expect do
46
+ proxy[name] = other_item
47
+ end.to_not raise_error(MongoDoc::InvalidEmbeddedHashKey)
48
+ end
49
+ end
50
+ end
51
+
52
+ context "when the item is not a MongoDoc::Document" do
53
+
54
+ it "does not register a save observer" do
55
+ root.should_not_receive(:register_save_observer)
56
+ proxy['new'] = other_item
57
+ end
58
+
59
+ it "does not set the root" do
60
+ other_item.should_not_receive(:_root=)
61
+ proxy['new'] = other_item
62
+ end
63
+ end
64
+
65
+ context "when the item is a MongoDoc::Document" do
66
+ it "registers a save observer" do
67
+ root.should_receive(:register_save_observer)
68
+ proxy['new'] = item
69
+ end
70
+
71
+ it "sets the root" do
72
+ proxy['new'] = item
73
+ item._root.should == root
74
+ end
75
+
76
+ it "adds the item through a document proxy" do
77
+ proxy['new'] = item
78
+ MongoDoc::Associations::DocumentProxy.should === proxy['new']
79
+ end
80
+ end
81
+ end
82
+
83
+ context "#merge!" do
84
+ context "when the key value is not a MongoDoc::Document" do
85
+
86
+ it "does not register a save observer" do
87
+ root.should_not_receive(:register_save_observer)
88
+ proxy.merge!(:new => other_item)
89
+ end
90
+
91
+ it "does not set the root" do
92
+ other_item.should_not_receive(:_root=)
93
+ proxy.merge!(:new => other_item)
94
+ end
95
+ end
96
+
97
+ context "when the key value is a MongoDoc::Document" do
98
+ it "registers a save observer" do
99
+ root.should_receive(:register_save_observer)
100
+ proxy.merge!(:new => item)
101
+ end
102
+
103
+ it "sets the root" do
104
+ proxy.merge!(:new => item)
105
+ item._root.should == root
106
+ end
107
+ end
108
+
109
+ context "with a block" do
110
+ it "calls into the block" do
111
+ proxy.merge!(:new => other_item) {|k, v1, v2| @result = v2}
112
+ @result.should == other_item
113
+ end
114
+
115
+ context "when the key value is not a MongoDoc::Document" do
116
+
117
+ it "does not register a save observer" do
118
+ root.should_not_receive(:register_save_observer)
119
+ proxy.merge!(:new => other_item) {|k, v1, v2| v2}
120
+ end
121
+
122
+ it "does not set the root" do
123
+ other_item.should_not_receive(:_root=)
124
+ proxy.merge!(:new => other_item) {|k, v1, v2| v2}
125
+ end
126
+ end
127
+
128
+ context "when the key value is a MongoDoc::Document" do
129
+ it "registers a save observer" do
130
+ root.should_receive(:register_save_observer)
131
+ proxy.merge!(:new => item) {|k, v1, v2| v2}
132
+ end
133
+
134
+ it "sets the root" do
135
+ proxy.merge!(:new => item) {|k, v1, v2| v2}
136
+ item._root.should == root
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ context "#replace" do
143
+ it "clears any existing data" do
144
+ proxy.should_receive(:clear)
145
+ proxy.replace(:new => other_item)
146
+ end
147
+
148
+ context "when the key value is not a MongoDoc::Document" do
149
+
150
+ it "does not register a save observer" do
151
+ root.should_not_receive(:register_save_observer)
152
+ proxy.replace(:new => other_item)
153
+ end
154
+
155
+ it "does not set the root" do
156
+ other_item.should_not_receive(:_root=)
157
+ proxy.replace(:new => other_item)
158
+ end
159
+ end
160
+
161
+ context "when the key value is a MongoDoc::Document" do
162
+ it "registers a save observer" do
163
+ root.should_receive(:register_save_observer)
164
+ proxy.replace(:new => item)
165
+ end
166
+
167
+ it "sets the root" do
168
+ proxy.replace(:new => item)
169
+ item._root.should == root
170
+ end
171
+ end
172
+
173
+ end
174
+
175
+ context "#build" do
176
+ it "builds an object of the collection class from the hash attrs" do
177
+ name = 'built'
178
+ proxy.build(:key, {:name => name}).name.should == name
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe "MongoDoc::Associations::ProxyBase" do
4
+ class ProxyBaseTest
5
+ include MongoDoc::Associations::ProxyBase
6
+ end
7
+
8
+ let(:_assoc_class) { 'ClassOfAssociation' }
9
+ let(:_assoc_name) { 'name_of_association' }
10
+ let(:_root) { 'root of document' }
11
+ let(:path) { 'root.parent' }
12
+
13
+ subject do
14
+ ProxyBaseTest.new({ :path => path, :assoc_class => _assoc_class, :assoc_name => _assoc_name, :root => _root })
15
+ end
16
+
17
+ %w(_assoc_class _assoc_name _modifier_path _root _selector_path).each do |attr|
18
+ it "defines #{attr}" do
19
+ should respond_to(attr)
20
+ end
21
+ end
22
+
23
+ describe "#initialize" do
24
+ %w(_assoc_class _assoc_name _root).each do |attr|
25
+ its(attr) { should == send(attr) }
26
+ end
27
+ end
28
+
29
+ describe "#_modifier_path" do
30
+ its(:_modifier_path) { should == path + '.' + _assoc_name }
31
+ end
32
+
33
+ describe "#_modifier_path=" do
34
+ it "sets the modifier path to the path + '.' + the assoc name" do
35
+ subject._modifier_path = 'new_path'
36
+ subject._modifier_path.should == "new_path.#{_assoc_name}"
37
+ end
38
+ end
39
+
40
+ describe "#_selector_path" do
41
+ its(:_selector_path) { should == path + '.' + _assoc_name }
42
+ end
43
+
44
+ describe "#_selector_path=" do
45
+ it "sets the selector path to the path + '.' + the assoc name" do
46
+ subject._selector_path = 'new_path'
47
+ subject._selector_path.should == "new_path.#{_assoc_name}"
48
+ end
49
+ end
50
+
51
+ describe ".is_document" do
52
+ class TestIsDocument
53
+ include MongoDoc::Document
54
+ end
55
+
56
+ it "returns false for any non-Document" do
57
+ MongoDoc::Associations::ProxyBase.is_document?(Object.new).should be_false
58
+ end
59
+
60
+ it "returns true for any Document" do
61
+ MongoDoc::Associations::ProxyBase.is_document?(TestIsDocument.new).should be_true
62
+ end
63
+ end
64
+
65
+ describe "#attach" do
66
+ class AttachDocument
67
+ include MongoDoc::Document
68
+ end
69
+
70
+ let(:doc) { AttachDocument.new }
71
+ let(:object) { Object.new }
72
+ let(:proxy) { ProxyBaseTest.new(:assoc_class => _assoc_class, :assoc_name => _assoc_name, :root => _root) }
73
+
74
+ it "returns the attached object" do
75
+ proxy.send(:attach, object).should == object
76
+ end
77
+
78
+ context "when a Document" do
79
+ it "attaches the Document" do
80
+ proxy.should_receive(:attach_document)
81
+ proxy.send(:attach, doc)
82
+ end
83
+ end
84
+
85
+ context "when not a Document" do
86
+ it "does not attach the Document" do
87
+ proxy.should_not_receive(:attach_document)
88
+ proxy.send(:attach, object)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ describe "MongoDoc::Associations" do
4
+
5
+ context ".embed" do
6
+ class TestDoc
7
+ include MongoDoc::Document
8
+
9
+ embed :sub_doc
10
+ end
11
+
12
+ class SubDoc
13
+ include MongoDoc::Document
14
+
15
+ attr_accessor :data
16
+ end
17
+
18
+ class TestHasOne
19
+ include MongoDoc::Document
20
+
21
+ has_one :sub_doc
22
+ end
23
+
24
+ let(:subdoc) { SubDoc.new }
25
+ let(:doc) { TestDoc.new(:sub_doc => subdoc) }
26
+
27
+ it "uses a proxy" do
28
+ MongoDoc::Associations::DocumentProxy.should === doc.sub_doc
29
+ end
30
+
31
+ it ".has_one is an alias for embed" do
32
+ MongoDoc::Associations::DocumentProxy.should === TestHasOne.new(:sub_doc => SubDoc.new).sub_doc
33
+ end
34
+
35
+ it "set the subdocuments root" do
36
+ doc.should == subdoc._root
37
+ end
38
+
39
+ context "validations" do
40
+ class EmbedValidationTest
41
+ include MongoDoc::Document
42
+
43
+ attr_accessor :data
44
+ validates_presence_of :data
45
+ end
46
+
47
+ it "cascades validations down" do
48
+ invalid = EmbedValidationTest.new
49
+ TestDoc.new(:sub_doc => invalid).should have(1).error_on(:sub_doc)
50
+ end
51
+ end
52
+ end
53
+
54
+ context ".embed_many" do
55
+
56
+ class SubEmbedManyDoc
57
+ include MongoDoc::Document
58
+
59
+ attr_accessor :data
60
+ end
61
+
62
+ class TestEmbedManyDoc
63
+ include MongoDoc::Document
64
+
65
+ embed_many :sub_docs, :class_name => 'SubEmbedManyDoc'
66
+ end
67
+
68
+ class TestEmbedManyDoc2
69
+ include MongoDoc::Document
70
+
71
+ embed_many :sub_docs, :class_name => :sub_embed_many_doc
72
+ end
73
+
74
+ class TestImplicitEmbedManyDoc
75
+ include MongoDoc::Document
76
+
77
+ embed_many :sub_embed_many_docs
78
+ end
79
+
80
+ class TestHasManyDoc
81
+ include MongoDoc::Document
82
+
83
+ has_many :sub_docs, :class_name => 'SubEmbedManyDoc'
84
+ end
85
+
86
+ let(:subdoc) { SubEmbedManyDoc.new }
87
+ let(:doc) { TestEmbedManyDoc.new(:sub_docs => [subdoc]) }
88
+
89
+ it "uses a proxy" do
90
+ MongoDoc::Associations::CollectionProxy.should === TestEmbedManyDoc.new.sub_docs
91
+ end
92
+
93
+ it ".has_many is an alias for .embed_many" do
94
+ MongoDoc::Associations::CollectionProxy.should === TestHasManyDoc.new.sub_docs
95
+ end
96
+
97
+ it "set the subdocuments root to the root" do
98
+ doc.should == subdoc._root
99
+ end
100
+
101
+ it "uses the association name to find the children's class name" do
102
+ TestImplicitEmbedManyDoc.new.sub_embed_many_docs._assoc_class.should == SubEmbedManyDoc
103
+ end
104
+
105
+ it "uses class_name attribute for the children's class name" do
106
+ TestEmbedManyDoc.new.sub_docs._assoc_class.should == SubEmbedManyDoc
107
+ end
108
+
109
+ it "uses class_name attribute for the children's class name" do
110
+ TestEmbedManyDoc2.new.sub_docs._assoc_class.should == SubEmbedManyDoc
111
+ end
112
+
113
+ context "validations" do
114
+ class EmbedManyValidationChild
115
+ include MongoDoc::Document
116
+
117
+ attr_accessor :data
118
+ validates_presence_of :data
119
+ end
120
+
121
+ class EmbedManyValidationTest
122
+ include MongoDoc::Document
123
+
124
+ embed_many :subdocs, :class_name => 'HasManyValidationChild'
125
+ end
126
+
127
+ let(:invalid_child) { EmbedManyValidationChild.new }
128
+ let(:doc) { EmbedManyValidationTest.new(:subdocs => [invalid_child]) }
129
+
130
+ it "cascades validations and marks it in the parent" do
131
+ doc.should have(1).error_on(:subdocs)
132
+ end
133
+
134
+ it "cascades validations and marks it in the child" do
135
+ invalid_child.should have(1).error_on(:data)
136
+ end
137
+
138
+ it "ignores non-document children" do
139
+ EmbedManyValidationTest.new(:subdocs => ['not a doc']).should be_valid
140
+ end
141
+ end
142
+ end
143
+
144
+ context ".embed_hash" do
145
+ class SubEmbedHashDoc
146
+ include MongoDoc::Document
147
+
148
+ attr_accessor :data
149
+ end
150
+
151
+ class TestEmbedHashDoc
152
+ include MongoDoc::Document
153
+
154
+ embed_hash :sub_docs, :class_name => 'SubEmbedHashDoc'
155
+ end
156
+
157
+ class TestImplicitEmbedHashDoc
158
+ include MongoDoc::Document
159
+
160
+ embed_hash :sub_embed_hash_docs
161
+ end
162
+
163
+ class TestHasHashDoc
164
+ include MongoDoc::Document
165
+
166
+ has_hash :sub_embed_hash_docs
167
+ end
168
+
169
+ let(:subdoc) { SubEmbedHashDoc.new }
170
+ let(:doc) { TestEmbedHashDoc.new(:sub_docs => {:key => subdoc}) }
171
+
172
+ it "uses a proxy" do
173
+ MongoDoc::Associations::HashProxy.should === TestEmbedHashDoc.new.sub_docs
174
+ end
175
+
176
+ it ".has_hash is an alias for embed_hash" do
177
+ MongoDoc::Associations::HashProxy.should === TestEmbedHashDoc.new.sub_docs
178
+ end
179
+
180
+ it "set the subdocuments root to the root" do
181
+ doc.should == subdoc._root
182
+ end
183
+
184
+ it "uses the association name to find the children's class name" do
185
+ TestImplicitEmbedHashDoc.new.sub_embed_hash_docs._assoc_class.should == SubEmbedHashDoc
186
+ end
187
+
188
+ context "validations" do
189
+ class EmbedHashValidationChild
190
+ include MongoDoc::Document
191
+
192
+ attr_accessor :data
193
+ validates_presence_of :data
194
+ end
195
+
196
+ class EmbedHashValidationTest
197
+ include MongoDoc::Document
198
+
199
+ embed_hash :subdocs, :class_name => 'EmbedHashValidationChild'
200
+ end
201
+
202
+ let(:invalid_child) { EmbedHashValidationChild.new }
203
+ let(:doc) { EmbedHashValidationTest.new(:subdocs => {:key => invalid_child}) }
204
+
205
+ it "cascades validations and marks it in the parent" do
206
+ doc.should have(1).error_on(:subdocs)
207
+ end
208
+
209
+ it "cascades validations and marks it in the child" do
210
+ invalid_child.should have(1).error_on(:data)
211
+ end
212
+
213
+ it "ignores non-document children" do
214
+ EmbedHashValidationTest.new(:subdocs => {:key => 'data'}).should be_valid
215
+ end
216
+ end
217
+ end
218
+ end