mark_mapper 0.0.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.rdoc +39 -0
- data/examples/attr_accessible.rb +24 -0
- data/examples/attr_protected.rb +24 -0
- data/examples/cache_key.rb +26 -0
- data/examples/custom_types.rb +26 -0
- data/examples/identity_map.rb +30 -0
- data/examples/identity_map/automatic.rb +2 -0
- data/examples/keys.rb +42 -0
- data/examples/modifiers/set.rb +27 -0
- data/examples/plugins.rb +40 -0
- data/examples/querying.rb +39 -0
- data/examples/sample_app.rb +43 -0
- data/examples/scopes.rb +56 -0
- data/examples/validating/embedded_docs.rb +31 -0
- data/lib/mark_mapper.rb +125 -0
- data/lib/mark_mapper/config.rb +90 -0
- data/lib/mark_mapper/connection.rb +60 -0
- data/lib/mark_mapper/criteria_hash.rb +194 -0
- data/lib/mark_mapper/document.rb +46 -0
- data/lib/mark_mapper/embedded_document.rb +32 -0
- data/lib/mark_mapper/exceptions.rb +33 -0
- data/lib/mark_mapper/extensions/array.rb +27 -0
- data/lib/mark_mapper/extensions/boolean.rb +45 -0
- data/lib/mark_mapper/extensions/date.rb +29 -0
- data/lib/mark_mapper/extensions/duplicable.rb +86 -0
- data/lib/mark_mapper/extensions/float.rb +18 -0
- data/lib/mark_mapper/extensions/hash.rb +26 -0
- data/lib/mark_mapper/extensions/integer.rb +27 -0
- data/lib/mark_mapper/extensions/kernel.rb +11 -0
- data/lib/mark_mapper/extensions/nil_class.rb +18 -0
- data/lib/mark_mapper/extensions/object.rb +30 -0
- data/lib/mark_mapper/extensions/object_id.rb +18 -0
- data/lib/mark_mapper/extensions/set.rb +20 -0
- data/lib/mark_mapper/extensions/string.rb +31 -0
- data/lib/mark_mapper/extensions/symbol.rb +87 -0
- data/lib/mark_mapper/extensions/time.rb +29 -0
- data/lib/mark_mapper/locale/en.yml +5 -0
- data/lib/mark_mapper/middleware/identity_map.rb +41 -0
- data/lib/mark_mapper/normalizers/criteria_hash_key.rb +17 -0
- data/lib/mark_mapper/normalizers/criteria_hash_value.rb +66 -0
- data/lib/mark_mapper/normalizers/fields_value.rb +26 -0
- data/lib/mark_mapper/normalizers/hash_key.rb +19 -0
- data/lib/mark_mapper/normalizers/integer.rb +19 -0
- data/lib/mark_mapper/normalizers/options_hash_value.rb +83 -0
- data/lib/mark_mapper/normalizers/sort_value.rb +55 -0
- data/lib/mark_mapper/options_hash.rb +103 -0
- data/lib/mark_mapper/pagination.rb +6 -0
- data/lib/mark_mapper/pagination/collection.rb +32 -0
- data/lib/mark_mapper/pagination/paginator.rb +46 -0
- data/lib/mark_mapper/plugins.rb +22 -0
- data/lib/mark_mapper/plugins/accessible.rb +61 -0
- data/lib/mark_mapper/plugins/active_model.rb +18 -0
- data/lib/mark_mapper/plugins/associations.rb +96 -0
- data/lib/mark_mapper/plugins/associations/base.rb +98 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_association.rb +63 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +35 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
- data/lib/mark_mapper/plugins/associations/collection.rb +29 -0
- data/lib/mark_mapper/plugins/associations/embedded_collection.rb +44 -0
- data/lib/mark_mapper/plugins/associations/in_array_proxy.rb +133 -0
- data/lib/mark_mapper/plugins/associations/many_association.rb +63 -0
- data/lib/mark_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
- data/lib/mark_mapper/plugins/associations/many_documents_proxy.rb +142 -0
- data/lib/mark_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
- data/lib/mark_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
- data/lib/mark_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
- data/lib/mark_mapper/plugins/associations/one_as_proxy.rb +22 -0
- data/lib/mark_mapper/plugins/associations/one_association.rb +48 -0
- data/lib/mark_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
- data/lib/mark_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
- data/lib/mark_mapper/plugins/associations/one_proxy.rb +95 -0
- data/lib/mark_mapper/plugins/associations/proxy.rb +138 -0
- data/lib/mark_mapper/plugins/associations/single_association.rb +46 -0
- data/lib/mark_mapper/plugins/caching.rb +21 -0
- data/lib/mark_mapper/plugins/callbacks.rb +42 -0
- data/lib/mark_mapper/plugins/clone.rb +24 -0
- data/lib/mark_mapper/plugins/counter_cache.rb +97 -0
- data/lib/mark_mapper/plugins/dirty.rb +61 -0
- data/lib/mark_mapper/plugins/document.rb +41 -0
- data/lib/mark_mapper/plugins/dumpable.rb +22 -0
- data/lib/mark_mapper/plugins/dynamic_querying.rb +45 -0
- data/lib/mark_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
- data/lib/mark_mapper/plugins/embedded_callbacks.rb +81 -0
- data/lib/mark_mapper/plugins/embedded_document.rb +53 -0
- data/lib/mark_mapper/plugins/equality.rb +23 -0
- data/lib/mark_mapper/plugins/identity_map.rb +144 -0
- data/lib/mark_mapper/plugins/indexable.rb +86 -0
- data/lib/mark_mapper/plugins/inspect.rb +16 -0
- data/lib/mark_mapper/plugins/keys.rb +470 -0
- data/lib/mark_mapper/plugins/keys/key.rb +134 -0
- data/lib/mark_mapper/plugins/keys/static.rb +45 -0
- data/lib/mark_mapper/plugins/logger.rb +18 -0
- data/lib/mark_mapper/plugins/modifiers.rb +140 -0
- data/lib/mark_mapper/plugins/pagination.rb +16 -0
- data/lib/mark_mapper/plugins/partial_updates.rb +77 -0
- data/lib/mark_mapper/plugins/persistence.rb +79 -0
- data/lib/mark_mapper/plugins/protected.rb +45 -0
- data/lib/mark_mapper/plugins/querying.rb +173 -0
- data/lib/mark_mapper/plugins/querying/decorated_markmapper_query.rb +75 -0
- data/lib/mark_mapper/plugins/rails.rb +79 -0
- data/lib/mark_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
- data/lib/mark_mapper/plugins/sci.rb +82 -0
- data/lib/mark_mapper/plugins/scopes.rb +28 -0
- data/lib/mark_mapper/plugins/serialization.rb +109 -0
- data/lib/mark_mapper/plugins/timestamps.rb +29 -0
- data/lib/mark_mapper/plugins/touch.rb +18 -0
- data/lib/mark_mapper/plugins/userstamps.rb +18 -0
- data/lib/mark_mapper/plugins/validations.rb +96 -0
- data/lib/mark_mapper/query.rb +278 -0
- data/lib/mark_mapper/railtie.rb +52 -0
- data/lib/mark_mapper/railtie/database.rake +65 -0
- data/lib/mark_mapper/translation.rb +10 -0
- data/lib/mark_mapper/version.rb +4 -0
- data/lib/rails/generators/mark_mapper/config/config_generator.rb +37 -0
- data/lib/rails/generators/mark_mapper/config/templates/marklogic.yml +19 -0
- data/lib/rails/generators/mark_mapper/model/model_generator.rb +40 -0
- data/lib/rails/generators/mark_mapper/model/templates/model.rb +17 -0
- data/spec/config/mark_mapper.yml +6 -0
- data/spec/examples_spec.rb +25 -0
- data/spec/functional/accessible_spec.rb +198 -0
- data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +64 -0
- data/spec/functional/associations/belongs_to_proxy_spec.rb +255 -0
- data/spec/functional/associations/in_array_proxy_spec.rb +349 -0
- data/spec/functional/associations/many_documents_as_proxy_spec.rb +230 -0
- data/spec/functional/associations/many_documents_proxy_spec.rb +968 -0
- data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +238 -0
- data/spec/functional/associations/many_embedded_proxy_spec.rb +288 -0
- data/spec/functional/associations/many_polymorphic_proxy_spec.rb +302 -0
- data/spec/functional/associations/one_as_proxy_spec.rb +489 -0
- data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +207 -0
- data/spec/functional/associations/one_embedded_proxy_spec.rb +100 -0
- data/spec/functional/associations/one_proxy_spec.rb +406 -0
- data/spec/functional/associations_spec.rb +48 -0
- data/spec/functional/caching_spec.rb +75 -0
- data/spec/functional/callbacks_spec.rb +330 -0
- data/spec/functional/counter_cache_spec.rb +235 -0
- data/spec/functional/dirty_spec.rb +316 -0
- data/spec/functional/document_spec.rb +310 -0
- data/spec/functional/dumpable_spec.rb +24 -0
- data/spec/functional/dynamic_querying_spec.rb +75 -0
- data/spec/functional/embedded_document_spec.rb +316 -0
- data/spec/functional/equality_spec.rb +20 -0
- data/spec/functional/extensions_spec.rb +16 -0
- data/spec/functional/identity_map_spec.rb +483 -0
- data/spec/functional/keys_spec.rb +339 -0
- data/spec/functional/logger_spec.rb +20 -0
- data/spec/functional/modifiers_spec.rb +446 -0
- data/spec/functional/options_hash_spec.rb +41 -0
- data/spec/functional/pagination_spec.rb +89 -0
- data/spec/functional/partial_updates_spec.rb +530 -0
- data/spec/functional/protected_spec.rb +199 -0
- data/spec/functional/querying_spec.rb +984 -0
- data/spec/functional/rails_spec.rb +55 -0
- data/spec/functional/sci_spec.rb +374 -0
- data/spec/functional/scopes_spec.rb +204 -0
- data/spec/functional/static_keys_spec.rb +153 -0
- data/spec/functional/timestamps_spec.rb +97 -0
- data/spec/functional/touch_spec.rb +125 -0
- data/spec/functional/userstamps_spec.rb +46 -0
- data/spec/functional/validations_spec.rb +416 -0
- data/spec/quality_spec.rb +51 -0
- data/spec/spec_helper.rb +150 -0
- data/spec/support/matchers.rb +15 -0
- data/spec/support/models.rb +256 -0
- data/spec/symbol_operator_spec.rb +70 -0
- data/spec/symbol_spec.rb +9 -0
- data/spec/unit/associations/base_spec.rb +146 -0
- data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
- data/spec/unit/associations/many_association_spec.rb +64 -0
- data/spec/unit/associations/one_association_spec.rb +48 -0
- data/spec/unit/associations/proxy_spec.rb +103 -0
- data/spec/unit/clone_spec.rb +79 -0
- data/spec/unit/config_generator_spec.rb +24 -0
- data/spec/unit/criteria_hash_spec.rb +218 -0
- data/spec/unit/document_spec.rb +251 -0
- data/spec/unit/dynamic_finder_spec.rb +125 -0
- data/spec/unit/embedded_document_spec.rb +676 -0
- data/spec/unit/equality_spec.rb +38 -0
- data/spec/unit/exceptions_spec.rb +12 -0
- data/spec/unit/extensions_spec.rb +368 -0
- data/spec/unit/identity_map_middleware_spec.rb +134 -0
- data/spec/unit/inspect_spec.rb +47 -0
- data/spec/unit/key_spec.rb +276 -0
- data/spec/unit/keys_spec.rb +155 -0
- data/spec/unit/mark_mapper_spec.rb +37 -0
- data/spec/unit/model_generator_spec.rb +45 -0
- data/spec/unit/normalizers/criteria_hash_key_spec.rb +37 -0
- data/spec/unit/normalizers/criteria_hash_value_spec.rb +200 -0
- data/spec/unit/normalizers/fields_value_spec.rb +45 -0
- data/spec/unit/normalizers/hash_key_spec.rb +15 -0
- data/spec/unit/normalizers/integer_spec.rb +24 -0
- data/spec/unit/normalizers/options_hash_value_spec.rb +99 -0
- data/spec/unit/normalizers/sort_value_spec.rb +98 -0
- data/spec/unit/options_hash_spec.rb +64 -0
- data/spec/unit/pagination/collection_spec.rb +30 -0
- data/spec/unit/pagination/paginator_spec.rb +118 -0
- data/spec/unit/pagination_spec.rb +11 -0
- data/spec/unit/plugins_spec.rb +89 -0
- data/spec/unit/query_spec.rb +837 -0
- data/spec/unit/rails_compatibility_spec.rb +40 -0
- data/spec/unit/rails_reflect_on_association_spec.rb +118 -0
- data/spec/unit/rails_spec.rb +188 -0
- data/spec/unit/serialization_spec.rb +169 -0
- data/spec/unit/serializers/json_serializer_spec.rb +218 -0
- data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
- data/spec/unit/time_zones_spec.rb +44 -0
- data/spec/unit/translation_spec.rb +27 -0
- data/spec/unit/validations_spec.rb +588 -0
- metadata +307 -0
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ManyDocumentsAsProxy" do
|
4
|
+
before do
|
5
|
+
Post.collection.remove
|
6
|
+
PostComment.collection.remove
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should default reader to empty array" do
|
10
|
+
Post.new.comments.should == []
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should add type and id key to polymorphic class base" do
|
14
|
+
PostComment.keys.keys.should include('commentable_type')
|
15
|
+
PostComment.keys['commentable_type'].type.should == String
|
16
|
+
PostComment.keys.keys.should include('commentable_id')
|
17
|
+
PostComment.keys['commentable_id'].type.should == ObjectId
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow adding to association like it was an array" do
|
21
|
+
post = Post.new
|
22
|
+
post.comments << PostComment.new(:body => 'foo bar')
|
23
|
+
post.comments << PostComment.new(:body => 'baz')
|
24
|
+
post.comments.concat PostComment.new(:body => 'baz')
|
25
|
+
|
26
|
+
post.comments.size.should == 3
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should be able to replace the association" do
|
30
|
+
post = Post.new
|
31
|
+
|
32
|
+
lambda {
|
33
|
+
post.comments = [
|
34
|
+
PostComment.new(:body => 'foo'),
|
35
|
+
PostComment.new(:body => 'bar'),
|
36
|
+
PostComment.new(:body => 'baz')
|
37
|
+
]
|
38
|
+
}.should change { PostComment.count }.by(3)
|
39
|
+
|
40
|
+
post = post.reload
|
41
|
+
post.comments.size.should == 3
|
42
|
+
bodies = post.comments.collect(&:body)
|
43
|
+
bodies.should include('foo')
|
44
|
+
bodies.should include('bar')
|
45
|
+
bodies.should include('baz')
|
46
|
+
end
|
47
|
+
|
48
|
+
context "build" do
|
49
|
+
it "should assign foreign key" do
|
50
|
+
post = Post.new
|
51
|
+
comment = post.comments.build
|
52
|
+
comment.commentable_id.should == post._id
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should assign _type" do
|
56
|
+
post = Post.new
|
57
|
+
comment = post.comments.build
|
58
|
+
comment.commentable_type.should == "Post"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should allow assigning attributes" do
|
62
|
+
post = Post.new
|
63
|
+
comment = post.comments.build(:body => 'foo bar')
|
64
|
+
comment.body.should == 'foo bar'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "create" do
|
69
|
+
it "should assign foreign key" do
|
70
|
+
post = Post.new
|
71
|
+
comment = post.comments.create
|
72
|
+
comment.commentable_id.should == post._id
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should assign _type" do
|
76
|
+
post = Post.new
|
77
|
+
comment = post.comments.create
|
78
|
+
comment.commentable_type.should == "Post"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should save record" do
|
82
|
+
post = Post.new
|
83
|
+
lambda {
|
84
|
+
post.comments.create(:body => 'baz')
|
85
|
+
}.should change { PostComment.count }
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should allow passing attributes" do
|
89
|
+
post = Post.create
|
90
|
+
comment = post.comments.create(:body => 'foo bar')
|
91
|
+
comment.body.should == 'foo bar'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "count" do
|
96
|
+
it "should work scoped to association" do
|
97
|
+
post = Post.create
|
98
|
+
3.times { post.comments.create(:body => 'foo bar') }
|
99
|
+
|
100
|
+
other_post = Post.create
|
101
|
+
2.times { other_post.comments.create(:body => 'baz') }
|
102
|
+
|
103
|
+
post.comments.count.should == 3
|
104
|
+
other_post.comments.count.should == 2
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should work with conditions" do
|
108
|
+
post = Post.create
|
109
|
+
post.comments.create(:body => 'foo bar')
|
110
|
+
post.comments.create(:body => 'baz')
|
111
|
+
post.comments.create(:body => 'foo bar')
|
112
|
+
|
113
|
+
post.comments.count(:body => 'foo bar').should == 2
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "Finding scoped to association" do
|
118
|
+
before do
|
119
|
+
@post = Post.new
|
120
|
+
|
121
|
+
@comment1 = PostComment.create(:body => 'comment1', :name => 'John')
|
122
|
+
@comment2 = PostComment.create(:body => 'comment2', :name => 'Steve')
|
123
|
+
@comment3 = PostComment.create(:body => 'comment3', :name => 'John')
|
124
|
+
@post.comments = [@comment1, @comment2]
|
125
|
+
@post.save
|
126
|
+
|
127
|
+
@post2 = Post.create(:body => "post #2")
|
128
|
+
@comment4 = PostComment.create(:body => 'comment1', :name => 'Chas')
|
129
|
+
@comment5 = PostComment.create(:body => 'comment2', :name => 'Dan')
|
130
|
+
@comment6 = PostComment.create(:body => 'comment3', :name => 'Ed')
|
131
|
+
@post2.comments = [@comment4, @comment5, @comment6]
|
132
|
+
@post2.save
|
133
|
+
end
|
134
|
+
|
135
|
+
context "with #all" do
|
136
|
+
it "should work" do
|
137
|
+
@post.comments.all.should include(@comment1)
|
138
|
+
@post.comments.all.should include(@comment2)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should work with conditions" do
|
142
|
+
comments = @post.comments.all(:body => 'comment1')
|
143
|
+
comments.should == [@comment1]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should work with order" do
|
147
|
+
comments = @post.comments.all(:order => 'body desc')
|
148
|
+
comments.should == [@comment2, @comment1]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "with one id" do
|
153
|
+
it "should work for id in association" do
|
154
|
+
@post.comments.find(@comment2._id).should == @comment2
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should not work for id not in association" do
|
158
|
+
expect {
|
159
|
+
@post.comments.find!(@comment5._id)
|
160
|
+
}.to raise_error(MarkMapper::DocumentNotFound)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "with multiple ids" do
|
165
|
+
it "should work for ids in association" do
|
166
|
+
posts = @post.comments.find!(@comment1._id, @comment2._id)
|
167
|
+
posts.should =~ [@comment1, @comment2]
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should not work for ids not in association" do
|
171
|
+
expect {
|
172
|
+
@post.comments.find!(@comment1._id, @comment2._id, @comment4._id)
|
173
|
+
}.to raise_error(MarkMapper::DocumentNotFound)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "dynamic finders" do
|
178
|
+
it "should work with single key" do
|
179
|
+
@post.comments.find_by_body('comment1').should == @comment1
|
180
|
+
@post2.comments.find_by_body('comment1').should == @comment4
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should work with multiple keys" do
|
184
|
+
@post.comments.find_by_body_and_name('comment1', 'John').should == @comment1
|
185
|
+
@post.comments.find_by_body_and_name('comment1', 'Frank').should be_nil
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should raise error when using !" do
|
189
|
+
lambda {
|
190
|
+
@post.comments.find_by_body!('asdf')
|
191
|
+
}.should raise_error(MarkMapper::DocumentNotFound)
|
192
|
+
end
|
193
|
+
|
194
|
+
context "find_or_create_by" do
|
195
|
+
it "should not create document if found" do
|
196
|
+
lambda {
|
197
|
+
comment = @post.comments.find_or_create_by_name('Steve')
|
198
|
+
comment.commentable.should == @post
|
199
|
+
comment.should == @comment2
|
200
|
+
}.should_not change { PostComment.count }
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should create document if not found" do
|
204
|
+
lambda {
|
205
|
+
@post.comments.find_or_create_by_name('Chas')
|
206
|
+
}.should change { PostComment.count }.by(1)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "with #paginate" do
|
212
|
+
before do
|
213
|
+
@comments = @post2.comments.paginate(:per_page => 2, :page => 1, :order => 'name')
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should return total pages" do
|
217
|
+
@comments.total_pages.should == 2
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should return total entries" do
|
221
|
+
@comments.total_entries.should == 3
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should return the subject" do
|
225
|
+
@comments.should include(@comment4)
|
226
|
+
@comments.should include(@comment5)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,968 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe "ManyDocumentsProxy" do
|
4
|
+
before do
|
5
|
+
Project.collection.remove
|
6
|
+
Status.collection.remove
|
7
|
+
|
8
|
+
@pet_class = Doc do
|
9
|
+
key :name, String
|
10
|
+
key :owner_id, ObjectId
|
11
|
+
end
|
12
|
+
|
13
|
+
@owner_class = Doc do
|
14
|
+
key :name, String
|
15
|
+
end
|
16
|
+
@owner_class.many :pets, :class => @pet_class, :foreign_key => :owner_id, :order => 'name'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return results if found via method_missing" do
|
20
|
+
@pet_class.class_eval do
|
21
|
+
def self.from_param(name)
|
22
|
+
find_by_name(name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.all_from_param(names)
|
26
|
+
where(:name => names).all
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
instance = @owner_class.new
|
31
|
+
instance.pets.build(:name => "Foo")
|
32
|
+
instance.pets.build(:name => "Bar")
|
33
|
+
instance.save
|
34
|
+
|
35
|
+
instance.reload.pets.from_param("Foo").tap do |pet|
|
36
|
+
pet.should be_a @pet_class
|
37
|
+
pet.name.should == "Foo"
|
38
|
+
end
|
39
|
+
|
40
|
+
instance.reload.pets.all_from_param(["Foo", "Bar"]).tap do |pet|
|
41
|
+
pet.should be_a Array
|
42
|
+
pet.map(&:name).should =~ %w(Foo Bar)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should default reader to empty array" do
|
47
|
+
project = Project.new
|
48
|
+
project.statuses.should == []
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should allow overriding association methods" do
|
52
|
+
@owner_class.class_eval do
|
53
|
+
def pets
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
instance = @owner_class.new
|
59
|
+
instance.pets.should == []
|
60
|
+
instance.pets.build
|
61
|
+
expect(instance.pets.empty?).to eq(false)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should be able to iterate associated documents in a callback" do
|
65
|
+
@owner_class.class_eval do
|
66
|
+
before_save :search_pets
|
67
|
+
|
68
|
+
def search_pets
|
69
|
+
pets.each { |p| p.name = "Animal" }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
owner = @owner_class.new
|
74
|
+
sophie = owner.pets.build(:name => "Sophie")
|
75
|
+
pippa = owner.pets.build(:name => "Pippa")
|
76
|
+
|
77
|
+
owner.save
|
78
|
+
owner.reload
|
79
|
+
owner.pets.reload
|
80
|
+
|
81
|
+
pets = []
|
82
|
+
owner.pets.each { |p| pets << p }
|
83
|
+
|
84
|
+
pets.size.should == 2
|
85
|
+
pets.should include(sophie)
|
86
|
+
pets.should include(pippa)
|
87
|
+
|
88
|
+
sophie.reload.name.should == "Animal"
|
89
|
+
pippa.reload.name.should == "Animal"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should allow assignment of many associated documents using a hash" do
|
93
|
+
person_attributes = {
|
94
|
+
'name' => 'Mr. Pet Lover',
|
95
|
+
'pets' => [
|
96
|
+
{'name' => 'Jimmy', 'species' => 'Cocker Spainel'},
|
97
|
+
{'name' => 'Sasha', 'species' => 'Siberian Husky'},
|
98
|
+
]
|
99
|
+
}
|
100
|
+
|
101
|
+
owner = @owner_class.new(person_attributes)
|
102
|
+
owner.name.should == 'Mr. Pet Lover'
|
103
|
+
owner.pets[0].name.should == 'Jimmy'
|
104
|
+
owner.pets[0].species.should == 'Cocker Spainel'
|
105
|
+
owner.pets[1].name.should == 'Sasha'
|
106
|
+
owner.pets[1].species.should == 'Siberian Husky'
|
107
|
+
|
108
|
+
owner.save.should be_truthy
|
109
|
+
owner.reload
|
110
|
+
|
111
|
+
owner.name.should == 'Mr. Pet Lover'
|
112
|
+
owner.pets[0].name.should == 'Jimmy'
|
113
|
+
owner.pets[0].species.should == 'Cocker Spainel'
|
114
|
+
owner.pets[1].name.should == 'Sasha'
|
115
|
+
owner.pets[1].species.should == 'Siberian Husky'
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should allow adding to association like it was an array" do
|
119
|
+
project = Project.new
|
120
|
+
project.statuses << Status.new(:name => 'Foo1!')
|
121
|
+
project.statuses.push Status.new(:name => 'Foo2!')
|
122
|
+
project.statuses.concat Status.new(:name => 'Foo3!')
|
123
|
+
project.statuses.size.should == 3
|
124
|
+
end
|
125
|
+
|
126
|
+
context "replacing the association" do
|
127
|
+
context "with objects of the class" do
|
128
|
+
it "should work" do
|
129
|
+
project = Project.new
|
130
|
+
project.statuses = [Status.new(:name => "ready")]
|
131
|
+
project.save.should be_truthy
|
132
|
+
|
133
|
+
project.reload
|
134
|
+
project.statuses.size.should == 1
|
135
|
+
project.statuses[0].name.should == "ready"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "with Hashes" do
|
140
|
+
it "should convert to objects of the class and work" do
|
141
|
+
project = Project.new
|
142
|
+
project.statuses = [{ 'name' => 'ready' }]
|
143
|
+
project.save.should be_truthy
|
144
|
+
|
145
|
+
project.reload
|
146
|
+
project.statuses.size.should == 1
|
147
|
+
project.statuses[0].name.should == "ready"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "with :dependent" do
|
152
|
+
before do
|
153
|
+
@broker_class = Doc('Broker')
|
154
|
+
@property_class = Doc('Property') do
|
155
|
+
key :broker_id, ObjectId
|
156
|
+
belongs_to :broker
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "=> destroy" do
|
161
|
+
before do
|
162
|
+
@broker_class.many :properties, :class => @property_class, :dependent => :destroy
|
163
|
+
|
164
|
+
@broker = @broker_class.create(:name => "Bob")
|
165
|
+
@property1 = @property_class.create
|
166
|
+
@property2 = @property_class.create
|
167
|
+
@property3 = @property_class.create
|
168
|
+
@broker.properties << @property1
|
169
|
+
@broker.properties << @property2
|
170
|
+
@broker.properties << @property3
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should call destroy the existing documents" do
|
174
|
+
expect(@broker.properties[0]).to receive(:destroy).once
|
175
|
+
expect(@broker.properties[1]).to receive(:destroy).once
|
176
|
+
expect(@broker.properties[2]).to receive(:destroy).once
|
177
|
+
@broker.properties = [@property_class.new]
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should remove the existing document from the database" do
|
181
|
+
@property_class.count.should == 3
|
182
|
+
@broker.properties = []
|
183
|
+
@property_class.count.should == 0
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should skip over documents that are the same" do
|
187
|
+
@broker.properties.each do |property|
|
188
|
+
if [@property3.id, @property1.id].include? property.id
|
189
|
+
expect(property).to receive(:destroy).never
|
190
|
+
else
|
191
|
+
expect(property).to receive(:destroy).once
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# expect(@broker.properties[0]).to receive(:destroy).never
|
196
|
+
# expect(@broker.properties[1]).to receive(:destroy).once
|
197
|
+
# expect(@broker.properties[2]).to receive(:destroy).never
|
198
|
+
@broker.properties = [@property3, @property1]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "=> delete_all" do
|
203
|
+
before do
|
204
|
+
@broker_class.many :properties, :class => @property_class, :dependent => :delete_all
|
205
|
+
|
206
|
+
@broker = @broker_class.create(:name => "Bob")
|
207
|
+
@property1 = @property_class.create
|
208
|
+
@property2 = @property_class.create
|
209
|
+
@property3 = @property_class.create
|
210
|
+
@broker.properties << @property1
|
211
|
+
@broker.properties << @property2
|
212
|
+
@broker.properties << @property3
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should call delete the existing documents" do
|
216
|
+
expect(@broker.properties[0]).to receive(:delete).once
|
217
|
+
expect(@broker.properties[1]).to receive(:delete).once
|
218
|
+
expect(@broker.properties[2]).to receive(:delete).once
|
219
|
+
@broker.properties = [@property_class.new]
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should remove the existing document from the database" do
|
223
|
+
@property_class.count.should == 3
|
224
|
+
@broker.properties = []
|
225
|
+
@property_class.count.should == 0
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should skip over documents that are the same" do
|
229
|
+
@broker.properties.each do |property|
|
230
|
+
if [@property3.id, @property1.id].include? property.id
|
231
|
+
expect(property).to receive(:delete).never
|
232
|
+
else
|
233
|
+
expect(property).to receive(:delete).once
|
234
|
+
end
|
235
|
+
end
|
236
|
+
# expect(@broker.properties[0]).to receive(:delete).never
|
237
|
+
# expect(@broker.properties[1]).to receive(:delete).once
|
238
|
+
# expect(@broker.properties[2]).to receive(:delete).never
|
239
|
+
@broker.properties = [@property3, @property1]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
context "=> nullify" do
|
244
|
+
before do
|
245
|
+
@broker_class.many :properties, :class => @property_class, :dependent => :nullify
|
246
|
+
|
247
|
+
@broker = @broker_class.create(:name => "Bob")
|
248
|
+
@property1 = @property_class.create
|
249
|
+
@property2 = @property_class.create
|
250
|
+
@property3 = @property_class.create
|
251
|
+
@broker.properties << @property1
|
252
|
+
@broker.properties << @property2
|
253
|
+
@broker.properties << @property3
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should nullify the existing documents" do
|
257
|
+
@property1.reload.broker_id.should == @broker.id
|
258
|
+
@property2.reload.broker_id.should == @broker.id
|
259
|
+
@property3.reload.broker_id.should == @broker.id
|
260
|
+
|
261
|
+
@broker.properties = [@property_class.new]
|
262
|
+
|
263
|
+
@property1.reload.broker_id.should be_nil
|
264
|
+
@property2.reload.broker_id.should be_nil
|
265
|
+
@property3.reload.broker_id.should be_nil
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should skip over documents that are the same" do
|
269
|
+
@broker.properties = [@property3, @property1]
|
270
|
+
|
271
|
+
@property1.reload.broker_id.should == @broker.id
|
272
|
+
@property2.reload.broker_id.should be_nil
|
273
|
+
@property3.reload.broker_id.should == @broker.id
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should work" do
|
277
|
+
old_properties = @broker.properties
|
278
|
+
@broker.properties = [@property1, @property2, @property3]
|
279
|
+
old_properties.should == @broker.properties
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
context "unspecified" do
|
284
|
+
it "should nullify the existing documents" do
|
285
|
+
@broker_class.many :properties, :class => @property_class
|
286
|
+
|
287
|
+
@broker = @broker_class.create(:name => "Bob")
|
288
|
+
@property1 = @property_class.create
|
289
|
+
@property2 = @property_class.create
|
290
|
+
@property3 = @property_class.create
|
291
|
+
@broker.properties << @property1
|
292
|
+
@broker.properties << @property2
|
293
|
+
@broker.properties << @property3
|
294
|
+
|
295
|
+
@broker.properties = [@property_class.new]
|
296
|
+
|
297
|
+
@property1.reload.broker_id.should be_nil
|
298
|
+
@property2.reload.broker_id.should be_nil
|
299
|
+
@property3.reload.broker_id.should be_nil
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context "using <<, push and concat" do
|
306
|
+
context "with objects of the class" do
|
307
|
+
it "should correctly assign foreign key" do
|
308
|
+
project = Project.new
|
309
|
+
project.statuses << Status.new(:name => '<<')
|
310
|
+
project.statuses.push Status.new(:name => 'push')
|
311
|
+
project.statuses.concat Status.new(:name => 'concat')
|
312
|
+
|
313
|
+
project.reload
|
314
|
+
project.statuses[0].project_id.should == project.id
|
315
|
+
project.statuses[1].project_id.should == project.id
|
316
|
+
project.statuses[2].project_id.should == project.id
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
context "with Hashes" do
|
321
|
+
it "should correctly convert to objects and assign foreign key" do
|
322
|
+
project = Project.new
|
323
|
+
project.statuses << { 'name' => '<<' }
|
324
|
+
project.statuses.push( { 'name' => 'push' })
|
325
|
+
project.statuses.concat({ 'name' => 'concat' })
|
326
|
+
|
327
|
+
project.reload
|
328
|
+
project.statuses[0].project_id.should == project.id
|
329
|
+
project.statuses[1].project_id.should == project.id
|
330
|
+
project.statuses[2].project_id.should == project.id
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
context "#build" do
|
336
|
+
it "should assign foreign key" do
|
337
|
+
project = Project.create
|
338
|
+
status = project.statuses.build
|
339
|
+
status.project_id.should == project.id
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should allow assigning attributes" do
|
343
|
+
project = Project.create
|
344
|
+
status = project.statuses.build(:name => 'Foo')
|
345
|
+
status.name.should == 'Foo'
|
346
|
+
end
|
347
|
+
|
348
|
+
it "should reset cache" do
|
349
|
+
project = Project.create
|
350
|
+
project.statuses.size.should == 0
|
351
|
+
status = project.statuses.build(:name => 'Foo')
|
352
|
+
status.save!
|
353
|
+
project.statuses.size.should == 1
|
354
|
+
end
|
355
|
+
|
356
|
+
it "should update collection without save" do
|
357
|
+
project = Project.create
|
358
|
+
project.statuses.build(:name => 'Foo')
|
359
|
+
project.statuses.size.should == 1
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should save built document when saving parent" do
|
363
|
+
project = Project.create
|
364
|
+
status = project.statuses.build(:name => 'Foo')
|
365
|
+
project.save!
|
366
|
+
status.should_not be_new
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should not save the parent when building associations" do
|
370
|
+
project = Project.new
|
371
|
+
status = project.statuses.build(:name => 'Foo')
|
372
|
+
project.should be_new
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should not save the built object" do
|
376
|
+
project = Project.new
|
377
|
+
status = project.statuses.build(:name => 'Foo')
|
378
|
+
status.should be_new
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should accept a block" do
|
382
|
+
project = Project.new
|
383
|
+
status = project.statuses.build do |doc|
|
384
|
+
doc.name = "Foo"
|
385
|
+
end
|
386
|
+
project.statuses[0].name.should == "Foo"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
context "#create" do
|
391
|
+
it "should assign foreign key" do
|
392
|
+
project = Project.create
|
393
|
+
status = project.statuses.create(:name => 'Foo!')
|
394
|
+
status.project_id.should == project.id
|
395
|
+
end
|
396
|
+
|
397
|
+
it "should save record" do
|
398
|
+
project = Project.create
|
399
|
+
lambda {
|
400
|
+
project.statuses.create(:name => 'Foo!')
|
401
|
+
}.should change { Status.count }
|
402
|
+
end
|
403
|
+
|
404
|
+
it "should allow passing attributes" do
|
405
|
+
project = Project.create
|
406
|
+
status = project.statuses.create(:name => 'Foo!')
|
407
|
+
status.name.should == 'Foo!'
|
408
|
+
end
|
409
|
+
|
410
|
+
it "should reset cache" do
|
411
|
+
project = Project.create
|
412
|
+
project.statuses.size.should == 0
|
413
|
+
project.statuses.create(:name => 'Foo!')
|
414
|
+
project.statuses.size.should == 1
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should accept a block" do
|
418
|
+
project = Project.new
|
419
|
+
status = project.statuses.create do |doc|
|
420
|
+
doc.name = "Foo"
|
421
|
+
end
|
422
|
+
project.statuses.first.name.should == "Foo"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context "#create!" do
|
427
|
+
it "should assign foreign key" do
|
428
|
+
project = Project.create
|
429
|
+
status = project.statuses.create!(:name => 'Foo!')
|
430
|
+
status.project_id.should == project.id
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should save record" do
|
434
|
+
project = Project.create
|
435
|
+
lambda {
|
436
|
+
project.statuses.create!(:name => 'Foo!')
|
437
|
+
}.should change { Status.count }
|
438
|
+
end
|
439
|
+
|
440
|
+
it "should allow passing attributes" do
|
441
|
+
project = Project.create
|
442
|
+
status = project.statuses.create!(:name => 'Foo!')
|
443
|
+
status.name.should == 'Foo!'
|
444
|
+
end
|
445
|
+
|
446
|
+
it "should raise exception if not valid" do
|
447
|
+
project = Project.create
|
448
|
+
expect {
|
449
|
+
project.statuses.create!(:name => nil)
|
450
|
+
}.to raise_error(MarkMapper::DocumentNotValid)
|
451
|
+
end
|
452
|
+
|
453
|
+
it "should reset cache" do
|
454
|
+
project = Project.create
|
455
|
+
project.statuses.size.should == 0
|
456
|
+
project.statuses.create!(:name => 'Foo!')
|
457
|
+
project.statuses.size.should == 1
|
458
|
+
end
|
459
|
+
|
460
|
+
it "should accept a block" do
|
461
|
+
project = Project.new
|
462
|
+
status = project.statuses.create! do |doc|
|
463
|
+
doc.name = "Foo"
|
464
|
+
end
|
465
|
+
status.name.should == "Foo"
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
context "count" do
|
470
|
+
it "should work scoped to association" do
|
471
|
+
project = Project.create
|
472
|
+
3.times { project.statuses.create(:name => 'Foo!') }
|
473
|
+
|
474
|
+
other_project = Project.create
|
475
|
+
2.times { other_project.statuses.create(:name => 'Foo!') }
|
476
|
+
|
477
|
+
project.statuses.count.should == 3
|
478
|
+
other_project.statuses.count.should == 2
|
479
|
+
end
|
480
|
+
|
481
|
+
it "should work with conditions" do
|
482
|
+
project = Project.create
|
483
|
+
project.statuses.create(:name => 'Foo')
|
484
|
+
project.statuses.create(:name => 'Other 1')
|
485
|
+
project.statuses.create(:name => 'Other 2')
|
486
|
+
|
487
|
+
project.statuses.count(:name => 'Foo').should == 1
|
488
|
+
end
|
489
|
+
|
490
|
+
it "should ignore unpersisted documents" do
|
491
|
+
project = Project.create
|
492
|
+
project.statuses.build(:name => 'Foo')
|
493
|
+
project.statuses.count.should == 0
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
context "size" do
|
498
|
+
it "should reflect both persisted and new documents" do
|
499
|
+
project = Project.create
|
500
|
+
3.times { project.statuses.create(:name => 'Foo!') }
|
501
|
+
2.times { project.statuses.build(:name => 'Foo!') }
|
502
|
+
project.statuses.size.should == 5
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
context "empty?" do
|
507
|
+
it "should be true with no associated docs" do
|
508
|
+
project = Project.create
|
509
|
+
project.statuses.empty?.should be_truthy
|
510
|
+
end
|
511
|
+
|
512
|
+
it "should be false if a document is built" do
|
513
|
+
project = Project.create
|
514
|
+
project.statuses.build(:name => 'Foo!')
|
515
|
+
project.statuses.empty?.should be_falsey
|
516
|
+
end
|
517
|
+
|
518
|
+
it "should be false if a document is created" do
|
519
|
+
project = Project.create
|
520
|
+
project.statuses.create(:name => 'Foo!')
|
521
|
+
project.statuses.empty?.should be_falsey
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
context "#to_a" do
|
526
|
+
it "should include persisted and new documents" do
|
527
|
+
project = Project.create
|
528
|
+
3.times { project.statuses.create(:name => 'Foo!') }
|
529
|
+
2.times { project.statuses.build(:name => 'Foo!') }
|
530
|
+
project.statuses.to_a.size.should == 5
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
context "#map" do
|
535
|
+
it "should include persisted and new documents" do
|
536
|
+
project = Project.create
|
537
|
+
3.times { project.statuses.create(:name => 'Foo!') }
|
538
|
+
2.times { project.statuses.build(:name => 'Foo!') }
|
539
|
+
project.statuses.map(&:name).size.should == 5
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
context "to_json" do
|
544
|
+
it "should work on association" do
|
545
|
+
project = Project.create
|
546
|
+
3.times { |i| project.statuses.create(:name => i.to_s) }
|
547
|
+
|
548
|
+
JSON.parse(project.statuses.to_json).collect{|status| status["name"] }.sort.should == ["0","1","2"]
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
context "as_json" do
|
553
|
+
it "should work on association" do
|
554
|
+
project = Project.create
|
555
|
+
3.times { |i| project.statuses.create(:name => i.to_s) }
|
556
|
+
|
557
|
+
project.statuses.as_json.collect{|status| status["name"] }.sort.should == ["0","1","2"]
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
context "Unassociating documents" do
|
562
|
+
before do
|
563
|
+
@project = Project.create
|
564
|
+
@project.statuses << Status.create(:name => '1')
|
565
|
+
@project.statuses << Status.create(:name => '2')
|
566
|
+
|
567
|
+
@project2 = Project.create
|
568
|
+
@project2.statuses << Status.create(:name => '1')
|
569
|
+
@project2.statuses << Status.create(:name => '2')
|
570
|
+
end
|
571
|
+
|
572
|
+
it "should work with destroy all" do
|
573
|
+
@project.statuses.count.should == 2
|
574
|
+
@project.statuses.destroy_all
|
575
|
+
@project.statuses.count.should == 0
|
576
|
+
|
577
|
+
@project2.statuses.count.should == 2
|
578
|
+
Status.count.should == 2
|
579
|
+
end
|
580
|
+
|
581
|
+
it "should work with destroy all and conditions" do
|
582
|
+
@project.statuses.count.should == 2
|
583
|
+
@project.statuses.destroy_all(:name => '1')
|
584
|
+
@project.statuses.count.should == 1
|
585
|
+
|
586
|
+
@project2.statuses.count.should == 2
|
587
|
+
Status.count.should == 3
|
588
|
+
end
|
589
|
+
|
590
|
+
it "should work with delete all" do
|
591
|
+
@project.statuses.count.should == 2
|
592
|
+
@project.statuses.delete_all
|
593
|
+
@project.statuses.count.should == 0
|
594
|
+
|
595
|
+
@project2.statuses.count.should == 2
|
596
|
+
Status.count.should == 2
|
597
|
+
end
|
598
|
+
|
599
|
+
it "should work with delete all and conditions" do
|
600
|
+
@project.statuses.count.should == 2
|
601
|
+
@project.statuses.delete_all(:name => '1')
|
602
|
+
@project.statuses.count.should == 1
|
603
|
+
|
604
|
+
@project2.statuses.count.should == 2
|
605
|
+
Status.count.should == 3
|
606
|
+
end
|
607
|
+
|
608
|
+
it "should work with nullify" do
|
609
|
+
@project.statuses.count.should == 2
|
610
|
+
@project.statuses.nullify
|
611
|
+
@project.statuses.count.should == 0
|
612
|
+
|
613
|
+
@project2.statuses.count.should == 2
|
614
|
+
Status.count.should == 4
|
615
|
+
Status.count(:name => '1').should == 2
|
616
|
+
Status.count(:name => '2').should == 2
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
context "Finding scoped to association" do
|
621
|
+
before do
|
622
|
+
@project1 = Project.new(:name => 'Project 1')
|
623
|
+
@brand_new = Status.create(:name => 'New', :position => 1 )
|
624
|
+
@complete = Status.create(:name => 'Complete', :position => 2)
|
625
|
+
@project1.statuses = [@brand_new, @complete]
|
626
|
+
@project1.save
|
627
|
+
|
628
|
+
@project2 = Project.create(:name => 'Project 2')
|
629
|
+
@in_progress = Status.create(:name => 'In Progress')
|
630
|
+
@archived = Status.create(:name => 'Archived')
|
631
|
+
@another_complete = Status.create(:name => 'Complete')
|
632
|
+
@project2.statuses = [@in_progress, @archived, @another_complete]
|
633
|
+
@project2.save
|
634
|
+
end
|
635
|
+
|
636
|
+
context "include?" do
|
637
|
+
it "should return true if in association" do
|
638
|
+
expect(@project1.statuses.include?(@brand_new)).to eq(true)
|
639
|
+
end
|
640
|
+
|
641
|
+
it "should return false if not in association" do
|
642
|
+
expect(@project1.statuses.include?(@in_progress)).to eq(false)
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
context "dynamic finders" do
|
647
|
+
it "should work with single key" do
|
648
|
+
@project1.statuses.find_by_name('New').should == @brand_new
|
649
|
+
@project1.statuses.find_by_name!('New').should == @brand_new
|
650
|
+
@project2.statuses.find_by_name('In Progress').should == @in_progress
|
651
|
+
@project2.statuses.find_by_name!('In Progress').should == @in_progress
|
652
|
+
end
|
653
|
+
|
654
|
+
it "should work with multiple keys" do
|
655
|
+
@project1.statuses.find_by_name_and_position('New', 1).should == @brand_new
|
656
|
+
@project1.statuses.find_by_name_and_position!('New', 1).should == @brand_new
|
657
|
+
@project1.statuses.find_by_name_and_position('New', 2).should be_nil
|
658
|
+
end
|
659
|
+
|
660
|
+
it "should raise error when using !" do
|
661
|
+
lambda {
|
662
|
+
@project1.statuses.find_by_name!('Fake')
|
663
|
+
}.should raise_error(MarkMapper::DocumentNotFound)
|
664
|
+
end
|
665
|
+
|
666
|
+
context "find_or_create_by" do
|
667
|
+
it "should not create document if found" do
|
668
|
+
lambda {
|
669
|
+
status = @project1.statuses.find_or_create_by_name('New')
|
670
|
+
status.project.should == @project1
|
671
|
+
status.should == @brand_new
|
672
|
+
}.should_not change { Status.count }
|
673
|
+
end
|
674
|
+
|
675
|
+
it "should create document if not found" do
|
676
|
+
lambda {
|
677
|
+
status = @project1.statuses.find_or_create_by_name('Delivered')
|
678
|
+
status.project.should == @project1
|
679
|
+
}.should change { Status.count }
|
680
|
+
end
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
context "sexy querying" do
|
685
|
+
it "should work with where" do
|
686
|
+
@project1.statuses.where(:name => 'New').all.should == [@brand_new]
|
687
|
+
end
|
688
|
+
|
689
|
+
it "should work with sort" do
|
690
|
+
@project1.statuses.sort(:name).all.should == [@complete, @brand_new]
|
691
|
+
end
|
692
|
+
|
693
|
+
it "should work with limit" do
|
694
|
+
@project1.statuses.sort(:name).limit(1).all.should == [@complete]
|
695
|
+
end
|
696
|
+
|
697
|
+
it "should work with skip" do
|
698
|
+
@project1.statuses.sort(:name).skip(1).all.should == [@brand_new]
|
699
|
+
end
|
700
|
+
|
701
|
+
it "should work with fields" do
|
702
|
+
@project1.statuses.fields(:position).all.each do |status|
|
703
|
+
status.position.should_not be_nil
|
704
|
+
status.name.should be_nil
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
it "should work with scopes" do
|
709
|
+
@project1.statuses.complete.all.should == [@complete]
|
710
|
+
end
|
711
|
+
|
712
|
+
it "should work with methods on class that return query" do
|
713
|
+
@project1.statuses.by_position(1).first.should == @brand_new
|
714
|
+
end
|
715
|
+
|
716
|
+
it "should not work with methods on class that do not return query" do
|
717
|
+
Status.class_eval { def self.foo; 'foo' end }
|
718
|
+
lambda { @project1.statuses.foo }.
|
719
|
+
should raise_error(NoMethodError)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
context "all" do
|
724
|
+
it "should work" do
|
725
|
+
@project1.statuses.all(:order => "position asc").should == [@brand_new, @complete]
|
726
|
+
end
|
727
|
+
|
728
|
+
it "should work with conditions" do
|
729
|
+
@project1.statuses.all(:name => 'Complete').should == [@complete]
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
context "first" do
|
734
|
+
it "should work" do
|
735
|
+
@project1.statuses.first(:order => 'name').should == @complete
|
736
|
+
end
|
737
|
+
|
738
|
+
it "should work with conditions" do
|
739
|
+
@project1.statuses.first(:name => 'Complete').should == @complete
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
context "last" do
|
744
|
+
it "should work" do
|
745
|
+
@project1.statuses.last(:order => "position asc").should == @complete
|
746
|
+
end
|
747
|
+
|
748
|
+
it "should work with conditions" do
|
749
|
+
@project1.statuses.last(:order => 'position', :name => 'New').should == @brand_new
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
context "with one id" do
|
754
|
+
it "should work for id in association" do
|
755
|
+
@project1.statuses.find(@complete.id).should == @complete
|
756
|
+
end
|
757
|
+
|
758
|
+
it "should not work for id not in association" do
|
759
|
+
lambda {
|
760
|
+
@project1.statuses.find!(@archived.id)
|
761
|
+
}.should raise_error(MarkMapper::DocumentNotFound)
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
context "with multiple ids" do
|
766
|
+
it "should work for ids in association" do
|
767
|
+
statuses = @project1.statuses.find(@brand_new.id, @complete.id)
|
768
|
+
statuses.should == [@brand_new, @complete]
|
769
|
+
end
|
770
|
+
|
771
|
+
it "should not work for ids not in association" do
|
772
|
+
expect {
|
773
|
+
@project1.statuses.find!(@brand_new.id, @complete.id, @archived.id)
|
774
|
+
}.to raise_error(MarkMapper::DocumentNotFound)
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
context "with #paginate" do
|
779
|
+
before do
|
780
|
+
@statuses = @project2.statuses.paginate(:per_page => 2, :page => 1, :order => 'name asc')
|
781
|
+
end
|
782
|
+
|
783
|
+
it "should return total pages" do
|
784
|
+
@statuses.total_pages.should == 2
|
785
|
+
end
|
786
|
+
|
787
|
+
it "should return total entries" do
|
788
|
+
@statuses.total_entries.should == 3
|
789
|
+
end
|
790
|
+
|
791
|
+
it "should return the subject" do
|
792
|
+
@statuses.collect(&:name).should == %w(Archived Complete)
|
793
|
+
end
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
context "extending the association" do
|
798
|
+
it "should work using a block passed to many" do
|
799
|
+
project = Project.new(:name => "Some Project")
|
800
|
+
status1 = Status.new(:name => "New")
|
801
|
+
status2 = Status.new(:name => "Assigned")
|
802
|
+
status3 = Status.new(:name => "Closed")
|
803
|
+
project.statuses = [status1, status2, status3]
|
804
|
+
project.save
|
805
|
+
|
806
|
+
open_statuses = project.statuses.open
|
807
|
+
open_statuses.should include(status1)
|
808
|
+
open_statuses.should include(status2)
|
809
|
+
open_statuses.should_not include(status3)
|
810
|
+
end
|
811
|
+
|
812
|
+
it "should work using many's :extend option" do
|
813
|
+
project = Project.new(:name => "Some Project")
|
814
|
+
collaborator1 = Collaborator.new(:name => "zing")
|
815
|
+
collaborator2 = Collaborator.new(:name => "zang")
|
816
|
+
project.collaborators = [collaborator1, collaborator2]
|
817
|
+
project.save
|
818
|
+
[collaborator1, collaborator2].include?(project.collaborators.top).should be true# == collaborator1
|
819
|
+
end
|
820
|
+
end
|
821
|
+
|
822
|
+
context ":dependent" do
|
823
|
+
before do
|
824
|
+
# FIXME: make use of already defined models
|
825
|
+
class ::Property
|
826
|
+
include MarkMapper::Document
|
827
|
+
end
|
828
|
+
Property.collection.remove
|
829
|
+
|
830
|
+
class ::Thing
|
831
|
+
include MarkMapper::Document
|
832
|
+
key :name, String
|
833
|
+
end
|
834
|
+
Thing.collection.remove
|
835
|
+
end
|
836
|
+
|
837
|
+
after do
|
838
|
+
Object.send :remove_const, 'Property' if defined?(::Property)
|
839
|
+
Object.send :remove_const, 'Thing' if defined?(::Thing)
|
840
|
+
end
|
841
|
+
|
842
|
+
context "=> destroy" do
|
843
|
+
before do
|
844
|
+
Property.key :thing_id, ObjectId
|
845
|
+
Property.belongs_to :thing, :dependent => :destroy
|
846
|
+
Thing.many :properties, :dependent => :destroy
|
847
|
+
|
848
|
+
@thing = Thing.create(:name => "Tree")
|
849
|
+
@property1 = Property.create
|
850
|
+
@property2 = Property.create
|
851
|
+
@property3 = Property.create
|
852
|
+
@thing.properties << @property1
|
853
|
+
@thing.properties << @property2
|
854
|
+
@thing.properties << @property3
|
855
|
+
end
|
856
|
+
|
857
|
+
it "should should destroy the associated documents" do
|
858
|
+
@thing.properties.count.should == 3
|
859
|
+
@thing.destroy
|
860
|
+
@thing.properties.count.should == 0
|
861
|
+
Property.count.should == 0
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
context "=> delete_all" do
|
866
|
+
before do
|
867
|
+
Property.key :thing_id, ObjectId
|
868
|
+
Property.belongs_to :thing
|
869
|
+
Thing.has_many :properties, :dependent => :delete_all
|
870
|
+
|
871
|
+
@thing = Thing.create(:name => "Tree")
|
872
|
+
@property1 = Property.create
|
873
|
+
@property2 = Property.create
|
874
|
+
@property3 = Property.create
|
875
|
+
@thing.properties << @property1
|
876
|
+
@thing.properties << @property2
|
877
|
+
@thing.properties << @property3
|
878
|
+
end
|
879
|
+
|
880
|
+
it "should should delete associated documents" do
|
881
|
+
@thing.properties.count.should == 3
|
882
|
+
@thing.destroy
|
883
|
+
@thing.properties.count.should == 0
|
884
|
+
Property.count.should == 0
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
context "=> nullify" do
|
889
|
+
before do
|
890
|
+
Property.key :thing_id, ObjectId
|
891
|
+
Property.belongs_to :thing
|
892
|
+
Thing.has_many :properties, :dependent => :nullify
|
893
|
+
|
894
|
+
@thing = Thing.create(:name => "Tree")
|
895
|
+
@property1 = Property.create
|
896
|
+
@property2 = Property.create
|
897
|
+
@property3 = Property.create
|
898
|
+
@thing.properties << @property1
|
899
|
+
@thing.properties << @property2
|
900
|
+
@thing.properties << @property3
|
901
|
+
end
|
902
|
+
|
903
|
+
it "should should nullify relationship but not destroy associated documents" do
|
904
|
+
@thing.properties.count.should == 3
|
905
|
+
@thing.destroy
|
906
|
+
@thing.properties.count.should == 0
|
907
|
+
Property.count.should == 3
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
context "unspecified" do
|
912
|
+
before do
|
913
|
+
Property.key :thing_id, ObjectId
|
914
|
+
Property.belongs_to :thing
|
915
|
+
Thing.has_many :properties, :dependent => :nullify
|
916
|
+
|
917
|
+
@thing = Thing.create(:name => "Tree")
|
918
|
+
@property1 = Property.create
|
919
|
+
@property2 = Property.create
|
920
|
+
@property3 = Property.create
|
921
|
+
@thing.properties << @property1
|
922
|
+
@thing.properties << @property2
|
923
|
+
@thing.properties << @property3
|
924
|
+
end
|
925
|
+
|
926
|
+
it "should should nullify relationship but not destroy associated documents" do
|
927
|
+
@thing.properties.count.should == 3
|
928
|
+
@thing.destroy
|
929
|
+
@thing.properties.count.should == 0
|
930
|
+
Property.count.should == 3
|
931
|
+
end
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
context "namespaced foreign keys" do
|
936
|
+
before do
|
937
|
+
News::Paper.many :articles, :class_name => 'News::Article'
|
938
|
+
News::Article.belongs_to :paper, :class_name => 'News::Paper'
|
939
|
+
|
940
|
+
@paper = News::Paper.create
|
941
|
+
end
|
942
|
+
|
943
|
+
it "should properly infer the foreign key" do
|
944
|
+
article = @paper.articles.create
|
945
|
+
article.should respond_to(:paper_id)
|
946
|
+
article.paper_id.should == @paper.id
|
947
|
+
end
|
948
|
+
end
|
949
|
+
|
950
|
+
context "criteria" do
|
951
|
+
before do
|
952
|
+
News::Paper.many :articles, :class_name => 'News::Article'
|
953
|
+
News::Article.belongs_to :paper, :class_name => 'News::Paper'
|
954
|
+
|
955
|
+
@paper = News::Paper.create
|
956
|
+
end
|
957
|
+
|
958
|
+
it "should should find associated instances by an object ID" do
|
959
|
+
article = News::Article.create(:paper_id => @paper.id)
|
960
|
+
expect(@paper.articles.include?(article)).to eq(true)
|
961
|
+
end
|
962
|
+
|
963
|
+
it "should should find associated instances by a string" do
|
964
|
+
article = News::Article.create(:paper_id => @paper.id.to_s)
|
965
|
+
expect(@paper.articles.include?(article)).to eq(true)
|
966
|
+
end
|
967
|
+
end
|
968
|
+
end
|