mongo_doc_rails2 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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