mark_mapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.rdoc +39 -0
  4. data/examples/attr_accessible.rb +24 -0
  5. data/examples/attr_protected.rb +24 -0
  6. data/examples/cache_key.rb +26 -0
  7. data/examples/custom_types.rb +26 -0
  8. data/examples/identity_map.rb +30 -0
  9. data/examples/identity_map/automatic.rb +2 -0
  10. data/examples/keys.rb +42 -0
  11. data/examples/modifiers/set.rb +27 -0
  12. data/examples/plugins.rb +40 -0
  13. data/examples/querying.rb +39 -0
  14. data/examples/sample_app.rb +43 -0
  15. data/examples/scopes.rb +56 -0
  16. data/examples/validating/embedded_docs.rb +31 -0
  17. data/lib/mark_mapper.rb +125 -0
  18. data/lib/mark_mapper/config.rb +90 -0
  19. data/lib/mark_mapper/connection.rb +60 -0
  20. data/lib/mark_mapper/criteria_hash.rb +194 -0
  21. data/lib/mark_mapper/document.rb +46 -0
  22. data/lib/mark_mapper/embedded_document.rb +32 -0
  23. data/lib/mark_mapper/exceptions.rb +33 -0
  24. data/lib/mark_mapper/extensions/array.rb +27 -0
  25. data/lib/mark_mapper/extensions/boolean.rb +45 -0
  26. data/lib/mark_mapper/extensions/date.rb +29 -0
  27. data/lib/mark_mapper/extensions/duplicable.rb +86 -0
  28. data/lib/mark_mapper/extensions/float.rb +18 -0
  29. data/lib/mark_mapper/extensions/hash.rb +26 -0
  30. data/lib/mark_mapper/extensions/integer.rb +27 -0
  31. data/lib/mark_mapper/extensions/kernel.rb +11 -0
  32. data/lib/mark_mapper/extensions/nil_class.rb +18 -0
  33. data/lib/mark_mapper/extensions/object.rb +30 -0
  34. data/lib/mark_mapper/extensions/object_id.rb +18 -0
  35. data/lib/mark_mapper/extensions/set.rb +20 -0
  36. data/lib/mark_mapper/extensions/string.rb +31 -0
  37. data/lib/mark_mapper/extensions/symbol.rb +87 -0
  38. data/lib/mark_mapper/extensions/time.rb +29 -0
  39. data/lib/mark_mapper/locale/en.yml +5 -0
  40. data/lib/mark_mapper/middleware/identity_map.rb +41 -0
  41. data/lib/mark_mapper/normalizers/criteria_hash_key.rb +17 -0
  42. data/lib/mark_mapper/normalizers/criteria_hash_value.rb +66 -0
  43. data/lib/mark_mapper/normalizers/fields_value.rb +26 -0
  44. data/lib/mark_mapper/normalizers/hash_key.rb +19 -0
  45. data/lib/mark_mapper/normalizers/integer.rb +19 -0
  46. data/lib/mark_mapper/normalizers/options_hash_value.rb +83 -0
  47. data/lib/mark_mapper/normalizers/sort_value.rb +55 -0
  48. data/lib/mark_mapper/options_hash.rb +103 -0
  49. data/lib/mark_mapper/pagination.rb +6 -0
  50. data/lib/mark_mapper/pagination/collection.rb +32 -0
  51. data/lib/mark_mapper/pagination/paginator.rb +46 -0
  52. data/lib/mark_mapper/plugins.rb +22 -0
  53. data/lib/mark_mapper/plugins/accessible.rb +61 -0
  54. data/lib/mark_mapper/plugins/active_model.rb +18 -0
  55. data/lib/mark_mapper/plugins/associations.rb +96 -0
  56. data/lib/mark_mapper/plugins/associations/base.rb +98 -0
  57. data/lib/mark_mapper/plugins/associations/belongs_to_association.rb +63 -0
  58. data/lib/mark_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +35 -0
  59. data/lib/mark_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  60. data/lib/mark_mapper/plugins/associations/collection.rb +29 -0
  61. data/lib/mark_mapper/plugins/associations/embedded_collection.rb +44 -0
  62. data/lib/mark_mapper/plugins/associations/in_array_proxy.rb +133 -0
  63. data/lib/mark_mapper/plugins/associations/many_association.rb +63 -0
  64. data/lib/mark_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  65. data/lib/mark_mapper/plugins/associations/many_documents_proxy.rb +142 -0
  66. data/lib/mark_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  67. data/lib/mark_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  68. data/lib/mark_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  69. data/lib/mark_mapper/plugins/associations/one_as_proxy.rb +22 -0
  70. data/lib/mark_mapper/plugins/associations/one_association.rb +48 -0
  71. data/lib/mark_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  72. data/lib/mark_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  73. data/lib/mark_mapper/plugins/associations/one_proxy.rb +95 -0
  74. data/lib/mark_mapper/plugins/associations/proxy.rb +138 -0
  75. data/lib/mark_mapper/plugins/associations/single_association.rb +46 -0
  76. data/lib/mark_mapper/plugins/caching.rb +21 -0
  77. data/lib/mark_mapper/plugins/callbacks.rb +42 -0
  78. data/lib/mark_mapper/plugins/clone.rb +24 -0
  79. data/lib/mark_mapper/plugins/counter_cache.rb +97 -0
  80. data/lib/mark_mapper/plugins/dirty.rb +61 -0
  81. data/lib/mark_mapper/plugins/document.rb +41 -0
  82. data/lib/mark_mapper/plugins/dumpable.rb +22 -0
  83. data/lib/mark_mapper/plugins/dynamic_querying.rb +45 -0
  84. data/lib/mark_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  85. data/lib/mark_mapper/plugins/embedded_callbacks.rb +81 -0
  86. data/lib/mark_mapper/plugins/embedded_document.rb +53 -0
  87. data/lib/mark_mapper/plugins/equality.rb +23 -0
  88. data/lib/mark_mapper/plugins/identity_map.rb +144 -0
  89. data/lib/mark_mapper/plugins/indexable.rb +86 -0
  90. data/lib/mark_mapper/plugins/inspect.rb +16 -0
  91. data/lib/mark_mapper/plugins/keys.rb +470 -0
  92. data/lib/mark_mapper/plugins/keys/key.rb +134 -0
  93. data/lib/mark_mapper/plugins/keys/static.rb +45 -0
  94. data/lib/mark_mapper/plugins/logger.rb +18 -0
  95. data/lib/mark_mapper/plugins/modifiers.rb +140 -0
  96. data/lib/mark_mapper/plugins/pagination.rb +16 -0
  97. data/lib/mark_mapper/plugins/partial_updates.rb +77 -0
  98. data/lib/mark_mapper/plugins/persistence.rb +79 -0
  99. data/lib/mark_mapper/plugins/protected.rb +45 -0
  100. data/lib/mark_mapper/plugins/querying.rb +173 -0
  101. data/lib/mark_mapper/plugins/querying/decorated_markmapper_query.rb +75 -0
  102. data/lib/mark_mapper/plugins/rails.rb +79 -0
  103. data/lib/mark_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  104. data/lib/mark_mapper/plugins/sci.rb +82 -0
  105. data/lib/mark_mapper/plugins/scopes.rb +28 -0
  106. data/lib/mark_mapper/plugins/serialization.rb +109 -0
  107. data/lib/mark_mapper/plugins/timestamps.rb +29 -0
  108. data/lib/mark_mapper/plugins/touch.rb +18 -0
  109. data/lib/mark_mapper/plugins/userstamps.rb +18 -0
  110. data/lib/mark_mapper/plugins/validations.rb +96 -0
  111. data/lib/mark_mapper/query.rb +278 -0
  112. data/lib/mark_mapper/railtie.rb +52 -0
  113. data/lib/mark_mapper/railtie/database.rake +65 -0
  114. data/lib/mark_mapper/translation.rb +10 -0
  115. data/lib/mark_mapper/version.rb +4 -0
  116. data/lib/rails/generators/mark_mapper/config/config_generator.rb +37 -0
  117. data/lib/rails/generators/mark_mapper/config/templates/marklogic.yml +19 -0
  118. data/lib/rails/generators/mark_mapper/model/model_generator.rb +40 -0
  119. data/lib/rails/generators/mark_mapper/model/templates/model.rb +17 -0
  120. data/spec/config/mark_mapper.yml +6 -0
  121. data/spec/examples_spec.rb +25 -0
  122. data/spec/functional/accessible_spec.rb +198 -0
  123. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +64 -0
  124. data/spec/functional/associations/belongs_to_proxy_spec.rb +255 -0
  125. data/spec/functional/associations/in_array_proxy_spec.rb +349 -0
  126. data/spec/functional/associations/many_documents_as_proxy_spec.rb +230 -0
  127. data/spec/functional/associations/many_documents_proxy_spec.rb +968 -0
  128. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +238 -0
  129. data/spec/functional/associations/many_embedded_proxy_spec.rb +288 -0
  130. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +302 -0
  131. data/spec/functional/associations/one_as_proxy_spec.rb +489 -0
  132. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +207 -0
  133. data/spec/functional/associations/one_embedded_proxy_spec.rb +100 -0
  134. data/spec/functional/associations/one_proxy_spec.rb +406 -0
  135. data/spec/functional/associations_spec.rb +48 -0
  136. data/spec/functional/caching_spec.rb +75 -0
  137. data/spec/functional/callbacks_spec.rb +330 -0
  138. data/spec/functional/counter_cache_spec.rb +235 -0
  139. data/spec/functional/dirty_spec.rb +316 -0
  140. data/spec/functional/document_spec.rb +310 -0
  141. data/spec/functional/dumpable_spec.rb +24 -0
  142. data/spec/functional/dynamic_querying_spec.rb +75 -0
  143. data/spec/functional/embedded_document_spec.rb +316 -0
  144. data/spec/functional/equality_spec.rb +20 -0
  145. data/spec/functional/extensions_spec.rb +16 -0
  146. data/spec/functional/identity_map_spec.rb +483 -0
  147. data/spec/functional/keys_spec.rb +339 -0
  148. data/spec/functional/logger_spec.rb +20 -0
  149. data/spec/functional/modifiers_spec.rb +446 -0
  150. data/spec/functional/options_hash_spec.rb +41 -0
  151. data/spec/functional/pagination_spec.rb +89 -0
  152. data/spec/functional/partial_updates_spec.rb +530 -0
  153. data/spec/functional/protected_spec.rb +199 -0
  154. data/spec/functional/querying_spec.rb +984 -0
  155. data/spec/functional/rails_spec.rb +55 -0
  156. data/spec/functional/sci_spec.rb +374 -0
  157. data/spec/functional/scopes_spec.rb +204 -0
  158. data/spec/functional/static_keys_spec.rb +153 -0
  159. data/spec/functional/timestamps_spec.rb +97 -0
  160. data/spec/functional/touch_spec.rb +125 -0
  161. data/spec/functional/userstamps_spec.rb +46 -0
  162. data/spec/functional/validations_spec.rb +416 -0
  163. data/spec/quality_spec.rb +51 -0
  164. data/spec/spec_helper.rb +150 -0
  165. data/spec/support/matchers.rb +15 -0
  166. data/spec/support/models.rb +256 -0
  167. data/spec/symbol_operator_spec.rb +70 -0
  168. data/spec/symbol_spec.rb +9 -0
  169. data/spec/unit/associations/base_spec.rb +146 -0
  170. data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
  171. data/spec/unit/associations/many_association_spec.rb +64 -0
  172. data/spec/unit/associations/one_association_spec.rb +48 -0
  173. data/spec/unit/associations/proxy_spec.rb +103 -0
  174. data/spec/unit/clone_spec.rb +79 -0
  175. data/spec/unit/config_generator_spec.rb +24 -0
  176. data/spec/unit/criteria_hash_spec.rb +218 -0
  177. data/spec/unit/document_spec.rb +251 -0
  178. data/spec/unit/dynamic_finder_spec.rb +125 -0
  179. data/spec/unit/embedded_document_spec.rb +676 -0
  180. data/spec/unit/equality_spec.rb +38 -0
  181. data/spec/unit/exceptions_spec.rb +12 -0
  182. data/spec/unit/extensions_spec.rb +368 -0
  183. data/spec/unit/identity_map_middleware_spec.rb +134 -0
  184. data/spec/unit/inspect_spec.rb +47 -0
  185. data/spec/unit/key_spec.rb +276 -0
  186. data/spec/unit/keys_spec.rb +155 -0
  187. data/spec/unit/mark_mapper_spec.rb +37 -0
  188. data/spec/unit/model_generator_spec.rb +45 -0
  189. data/spec/unit/normalizers/criteria_hash_key_spec.rb +37 -0
  190. data/spec/unit/normalizers/criteria_hash_value_spec.rb +200 -0
  191. data/spec/unit/normalizers/fields_value_spec.rb +45 -0
  192. data/spec/unit/normalizers/hash_key_spec.rb +15 -0
  193. data/spec/unit/normalizers/integer_spec.rb +24 -0
  194. data/spec/unit/normalizers/options_hash_value_spec.rb +99 -0
  195. data/spec/unit/normalizers/sort_value_spec.rb +98 -0
  196. data/spec/unit/options_hash_spec.rb +64 -0
  197. data/spec/unit/pagination/collection_spec.rb +30 -0
  198. data/spec/unit/pagination/paginator_spec.rb +118 -0
  199. data/spec/unit/pagination_spec.rb +11 -0
  200. data/spec/unit/plugins_spec.rb +89 -0
  201. data/spec/unit/query_spec.rb +837 -0
  202. data/spec/unit/rails_compatibility_spec.rb +40 -0
  203. data/spec/unit/rails_reflect_on_association_spec.rb +118 -0
  204. data/spec/unit/rails_spec.rb +188 -0
  205. data/spec/unit/serialization_spec.rb +169 -0
  206. data/spec/unit/serializers/json_serializer_spec.rb +218 -0
  207. data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
  208. data/spec/unit/time_zones_spec.rb +44 -0
  209. data/spec/unit/translation_spec.rb +27 -0
  210. data/spec/unit/validations_spec.rb +588 -0
  211. metadata +307 -0
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'A document with protected attributes' do
4
+ before do
5
+ @doc_class = Doc do
6
+ key :name, String
7
+ key :admin, Boolean, :default => false
8
+
9
+ attr_protected :admin
10
+ end
11
+
12
+ @doc = @doc_class.create(:name => 'Steve Sloan')
13
+ end
14
+
15
+ it "should have protected attributes class method" do
16
+ @doc_class.protected_attributes.should == [:admin].to_set
17
+ end
18
+
19
+ it "should default protected attributes to nil" do
20
+ Doc().protected_attributes.should be_nil
21
+ end
22
+
23
+ it "should have protected attributes instance method" do
24
+ @doc.protected_attributes.should equal(@doc_class.protected_attributes)
25
+ end
26
+
27
+ it "should raise error if there are accessible attributes" do
28
+ doc = Doc('Post')
29
+ doc.attr_accessible :name
30
+ lambda { doc.attr_protected :admin }.
31
+ should raise_error(/Declare either attr_protected or attr_accessible for Post/)
32
+ end
33
+
34
+ it "should know if using protected attributes" do
35
+ @doc_class.protected_attributes?.should be(true)
36
+ Doc().protected_attributes?.should be(false)
37
+ end
38
+
39
+ it "should work with :protected shortcut when defining key" do
40
+ Doc() do
41
+ key :user_id, ObjectId, :protected => true
42
+ end.protected_attributes.should == [:user_id].to_set
43
+ end
44
+
45
+ it "should assign protected attribute through accessor" do
46
+ @doc.admin = true
47
+ @doc.admin.should be_truthy
48
+ end
49
+
50
+ it "should ignore protected attribute on #initialize" do
51
+ doc = @doc_class.new(:name => 'John', :admin => true)
52
+ doc.admin.should be_falsey
53
+ doc.name.should == 'John'
54
+ end
55
+
56
+ it "should not ignore protected attributes on #initialize from the database" do
57
+ doc = @doc_class.new(:name => 'John')
58
+ doc.admin = true
59
+ doc.save!
60
+
61
+ doc = @doc_class.first(:name => 'John')
62
+ doc.admin.should be_truthy
63
+ doc.name.should == 'John'
64
+ end
65
+
66
+ it "should not ignore protected attributes on #reload" do
67
+ doc = @doc_class.new(:name => 'John')
68
+ doc.admin = true
69
+ doc.save!
70
+
71
+ doc.reload
72
+ doc.admin.should be_truthy
73
+ doc.name.should == 'John'
74
+ end
75
+
76
+ it "should ignore protected attribute on #update_attribute" do
77
+ @doc.update_attribute('admin', true)
78
+ @doc.admin.should be_truthy
79
+ end
80
+
81
+ it "should ignore protected attribute on #update_attributes" do
82
+ @doc.update_attributes(:name => 'Ren Hoek', :admin => true)
83
+ @doc.name.should == 'Ren Hoek'
84
+ @doc.admin.should be_falsey
85
+ end
86
+
87
+ it "should ignore protected attribute on #update_attributes!" do
88
+ @doc.update_attributes!(:name => 'Stimpson J. Cat', :admin => true)
89
+ @doc.name.should == 'Stimpson J. Cat'
90
+ @doc.admin.should be_falsey
91
+ end
92
+
93
+ it "should ignore protecteds attribute on #attributes=" do
94
+ @doc.attributes = {:name => 'Stimpson J. Cat', :admin => true}
95
+ @doc.name.should == 'Stimpson J. Cat'
96
+ @doc.admin.should be_falsey
97
+ end
98
+
99
+ it "should be indifferent to whether the protected keys are strings or symbols" do
100
+ @doc.update_attributes!("name" => 'Stimpson J. Cat', "admin" => true)
101
+ @doc.name.should == 'Stimpson J. Cat'
102
+ @doc.admin.should be_falsey
103
+ end
104
+
105
+ it "should accept nil as constructor's argument without raising exception" do
106
+ lambda { @doc_class.new(nil) }.should_not raise_error
107
+ end
108
+ end
109
+
110
+ describe "Single collection inherited protected attributes" do
111
+ before do
112
+ class ::GrandParent
113
+ include MarkMapper::Document
114
+
115
+ key :site_id, ObjectId
116
+ attr_protected :site_id
117
+ end
118
+ GrandParent.collection.remove
119
+
120
+ class ::Child < ::GrandParent
121
+ key :position, Integer
122
+
123
+ attr_protected :position
124
+ end
125
+
126
+ class ::GrandChild < ::Child; end
127
+
128
+ class ::OtherChild < ::GrandParent
129
+ key :blog_id, ObjectId
130
+
131
+ attr_protected :blog_id
132
+ end
133
+ end
134
+
135
+ after do
136
+ Object.send :remove_const, 'GrandParent' if defined?(::GrandParent)
137
+ Object.send :remove_const, 'Child' if defined?(::Child)
138
+ Object.send :remove_const, 'GrandChild' if defined?(::GrandChild)
139
+ Object.send :remove_const, 'OtherChild' if defined?(::OtherChild)
140
+ end
141
+
142
+ it "should share keys down the inheritance trail" do
143
+ GrandParent.protected_attributes.should == [:site_id].to_set
144
+ Child.protected_attributes.should == [:site_id, :position].to_set
145
+ GrandChild.protected_attributes.should == [:site_id, :position].to_set
146
+ OtherChild.protected_attributes.should == [:site_id, :blog_id].to_set
147
+ end
148
+ end
149
+
150
+ describe 'An embedded document with protected attributes' do
151
+ before do
152
+ @doc_class = Doc('Project')
153
+ @edoc_class = EDoc('Person') do
154
+ key :name, String
155
+ key :admin, Boolean, :default => false
156
+
157
+ attr_protected :admin
158
+ end
159
+ @doc_class.many :people, :class => @edoc_class
160
+
161
+ @doc = @doc_class.create(:title => 'MarkMapper')
162
+ @edoc = @edoc_class.new(:name => 'Steve Sloan')
163
+ @doc.people << @edoc
164
+ end
165
+
166
+ it "should have protected attributes class method" do
167
+ @edoc_class.protected_attributes.should == [:admin].to_set
168
+ end
169
+
170
+ it "should default protected attributes to nil" do
171
+ EDoc().protected_attributes.should be_nil
172
+ end
173
+
174
+ it "should have protected attributes instance method" do
175
+ @edoc.protected_attributes.should equal(@edoc_class.protected_attributes)
176
+ end
177
+
178
+ it "should assign protected attribute through accessor" do
179
+ @edoc.admin = true
180
+ @edoc.admin.should be_truthy
181
+ end
182
+
183
+ it "should not ignore protected attribute on #update_attribute" do
184
+ @edoc.update_attribute('admin', true)
185
+ @edoc.admin.should be_truthy
186
+ end
187
+
188
+ it "should ignore protected attribute on #update_attributes" do
189
+ @edoc.update_attributes(:name => 'Ren Hoek', :admin => true)
190
+ @edoc.name.should == 'Ren Hoek'
191
+ @edoc.admin.should be_falsey
192
+ end
193
+
194
+ it "should ignore protected attribute on #update_attributes!" do
195
+ @edoc.update_attributes!(:name => 'Stimpson J. Cat', :admin => true)
196
+ @edoc.name.should == 'Stimpson J. Cat'
197
+ @edoc.admin.should be_falsey
198
+ end
199
+ end
@@ -0,0 +1,984 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Querying" do
4
+ let(:document) {
5
+ Doc do
6
+ key :first_name, String
7
+ key :last_name, String
8
+ key :age, Integer
9
+ key :date, Date
10
+ end
11
+ }
12
+
13
+ context ".query" do
14
+ let(:query) { document.query }
15
+
16
+ it "should set model to self" do
17
+ query.model.should == document
18
+ end
19
+
20
+ it "should always return new instance" do
21
+ document.query.should_not equal(query)
22
+ end
23
+
24
+ it "should apply options" do
25
+ document.query(:foo => 'bar')[:foo].should == 'bar'
26
+ end
27
+ end
28
+
29
+ context ".criteria_hash" do
30
+ let(:hash) { document.criteria_hash }
31
+
32
+ it "should set object id keys on hash" do
33
+ hash.object_ids.should == [:_id]
34
+ end
35
+
36
+ it "should always return new instance" do
37
+ document.criteria_hash.should_not equal(hash)
38
+ end
39
+
40
+ it "should apply provided criteria" do
41
+ document.criteria_hash(:foo => 'bar')[:foo].should == 'bar'
42
+ end
43
+ end
44
+
45
+ context ".create (single document)" do
46
+ let!(:doc) { document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'}) }
47
+
48
+ it "should create a document in correct collection" do
49
+ document.count.should == 1
50
+ end
51
+
52
+ it "should automatically set id" do
53
+ doc.id.should be_instance_of(MarkLogic::ObjectId)
54
+ doc._id.should be_instance_of(MarkLogic::ObjectId)
55
+ end
56
+
57
+ it "should no longer be new?" do
58
+ doc.new?.should be_falsey
59
+ end
60
+
61
+ it "should return instance of document" do
62
+ doc.should be_instance_of(document)
63
+ doc.first_name.should == 'John'
64
+ doc.last_name.should == 'Nunemaker'
65
+ doc.age.should == 27
66
+ end
67
+
68
+ it "should not fail if no attributes provided" do
69
+ document = Doc()
70
+ lambda { document.create }.should change { document.count }.by(1)
71
+ end
72
+ end
73
+
74
+ context ".create (multiple documents)" do
75
+ before do
76
+ @docs = document.create([
77
+ {:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
78
+ {:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
79
+ ])
80
+ end
81
+
82
+ it "should create multiple documents" do
83
+ document.count.should == 2
84
+ end
85
+
86
+ it "should return an array of doc instances" do
87
+ @docs.map do |doc|
88
+ doc.should be_instance_of(document)
89
+ end
90
+ end
91
+ end
92
+
93
+ context ".update (single document)" do
94
+ before do
95
+ doc = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
96
+ @doc = document.update(doc._id, {:age => 40})
97
+ end
98
+
99
+ it "should update attributes provided" do
100
+ @doc.age.should == 40
101
+ end
102
+
103
+ it "should not update existing attributes that were not set to update" do
104
+ @doc.first_name.should == 'John'
105
+ @doc.last_name.should == 'Nunemaker'
106
+ end
107
+
108
+ it "should not create new document" do
109
+ document.count.should == 1
110
+ end
111
+
112
+ it "should raise error if not provided id" do
113
+ doc = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
114
+ lambda { document.update }.should raise_error(ArgumentError)
115
+ end
116
+
117
+ it "should raise error if not provided attributes" do
118
+ doc = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
119
+ lambda { document.update(doc._id) }.should raise_error(ArgumentError)
120
+ lambda { document.update(doc._id, [1]) }.should raise_error(ArgumentError)
121
+ end
122
+ end
123
+
124
+ context ".update (multiple documents)" do
125
+ before do
126
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
127
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
128
+
129
+ @docs = document.update({
130
+ @doc1._id => {:age => 30},
131
+ @doc2._id => {:age => 30},
132
+ })
133
+ end
134
+
135
+ it "should not create any new documents" do
136
+ document.count.should == 2
137
+ end
138
+
139
+ it "should should return an array of doc instances" do
140
+ @docs.map do |doc|
141
+ doc.should be_instance_of(document)
142
+ end
143
+ end
144
+
145
+ it "should update the documents" do
146
+ document.find(@doc1._id).age.should == 30
147
+ document.find(@doc2._id).age.should == 30
148
+ end
149
+
150
+ it "should raise error if not a hash" do
151
+ lambda { document.update([1, 2]) }.should raise_error(ArgumentError)
152
+ end
153
+ end
154
+
155
+ context ".find" do
156
+ before do
157
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
158
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
159
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
160
+ end
161
+
162
+ it "should return nil if nothing provided for find" do
163
+ document.find.should be_nil
164
+ end
165
+
166
+ it "should raise document not found if nothing provided for find!" do
167
+ expect { document.find! }.to raise_error(MarkMapper::DocumentNotFound)
168
+ end
169
+
170
+ context "(with a single id)" do
171
+ it "should work" do
172
+ document.find(@doc1._id).should == @doc1
173
+ end
174
+
175
+ it "should return nil if document not found with find" do
176
+ document.find(123).should be_nil
177
+ end
178
+
179
+ it "should raise error if document not found with find!" do
180
+ expect { document.find!(123) }.to raise_error(MarkMapper::DocumentNotFound)
181
+ end
182
+ end
183
+
184
+ context "(with multiple id's)" do
185
+ it "should work as arguments" do
186
+ document.find(@doc1._id, @doc2._id).should =~ [@doc1, @doc2]
187
+ end
188
+
189
+ it "should work as arguments with string ids" do
190
+ document.find(@doc1._id.to_s, @doc2._id.to_s).should =~ [@doc1, @doc2]
191
+ end
192
+
193
+ it "should work as array" do
194
+ document.find([@doc1._id, @doc2._id]).should =~ [@doc1, @doc2]
195
+ end
196
+
197
+ it "should work as array with string ids" do
198
+ document.find([@doc1._id.to_s, @doc2._id.to_s]).should =~ [@doc1, @doc2]
199
+ end
200
+
201
+ it "should compact not found when using find" do
202
+ document.find(@doc1._id, MarkLogic::ObjectId.new.to_s).should =~ [@doc1]
203
+ end
204
+
205
+ it "should raise error if not all found when using find!" do
206
+ expect {
207
+ document.find!(@doc1._id, MarkLogic::ObjectId.new.to_s)
208
+ }.to raise_error(MarkMapper::DocumentNotFound)
209
+ end
210
+
211
+ it "should raise error if not all found when using find!" do
212
+ expect {
213
+ document.find!([@doc1._id, MarkLogic::ObjectId.new.to_s])
214
+ }.to raise_error(MarkMapper::DocumentNotFound)
215
+ end
216
+
217
+ it "should return array if array with one element" do
218
+ document.find([@doc1._id]).should == [@doc1]
219
+ end
220
+ end
221
+
222
+ it "should be able to find using condition auto-detection" do
223
+ document.first(:first_name => 'John').should == @doc1
224
+ document.all(:last_name => 'Nunemaker', :order => 'age desc').should =~ [@doc1, @doc3]
225
+ end
226
+
227
+ context "#all" do
228
+ it "should find all documents with options" do
229
+ document.all(:order => 'first_name').should =~ [@doc1, @doc3, @doc2]
230
+ document.all(:last_name => 'Nunemaker', :order => 'age desc').should =~ [@doc1, @doc3]
231
+ end
232
+ end
233
+
234
+ context "#first" do
235
+ it "should find first document with options" do
236
+ document.first(:order => 'first_name').should == @doc1
237
+ document.first(:age => 28).should == @doc2
238
+ end
239
+ end
240
+
241
+ context "#last" do
242
+ it "should find last document with options" do
243
+ document.last(:order => 'age').should == @doc2
244
+ document.last(:order => 'age', :age => 28).should == @doc2
245
+ end
246
+ end
247
+
248
+ context "#find_each" do
249
+ it "should yield all documents found based on options" do
250
+ yield_documents = []
251
+ document.find_each(:order => "first_name") {|doc| yield_documents << doc }
252
+ yield_documents.should =~ [@doc1, @doc3, @doc2]
253
+
254
+ yield_documents = []
255
+ document.find_each(:last_name => 'Nunemaker', :order => 'age desc') {|doc| yield_documents << doc }
256
+ yield_documents.should =~ [@doc1, @doc3]
257
+ end
258
+ end
259
+ end # finding documents
260
+
261
+ context ".find_by_id" do
262
+ before do
263
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
264
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
265
+ end
266
+
267
+ it "should be able to find by id" do
268
+ document.find_by_id(@doc1._id).should == @doc1
269
+ document.find_by_id(@doc2._id).should == @doc2
270
+ end
271
+
272
+ it "should return nil if document not found" do
273
+ document.find_by_id(1234).should be_nil
274
+ end
275
+ end
276
+
277
+ context ".first_or_create" do
278
+ it "should find if exists" do
279
+ created = document.create(:first_name => 'John', :last_name => 'Nunemaker')
280
+ lambda {
281
+ found = document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
282
+ found.should == created
283
+ }.should_not change { document.count }
284
+ end
285
+
286
+ it "should create if not found" do
287
+ lambda {
288
+ created = document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
289
+ created.first_name.should == 'John'
290
+ created.last_name.should == 'Nunemaker'
291
+ }.should change { document.count }.by(1)
292
+ end
293
+
294
+ it "should disregard non-keys when creating, but use them in the query" do
295
+ expect {
296
+ document.create(:first_name => 'John', :age => 9)
297
+ lambda {
298
+ document.first_or_create(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
299
+ }.should change { document.count }.by(1)
300
+ }.to_not raise_error
301
+ end
302
+ end
303
+
304
+ context ".first_or_new" do
305
+ it "should find if exists" do
306
+ created = document.create(:first_name => 'John', :last_name => 'Nunemaker')
307
+ lambda {
308
+ found = document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
309
+ found.should == created
310
+ }.should_not change { document.count }
311
+ end
312
+
313
+ it "should initialize if not found" do
314
+ lambda {
315
+ created = document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
316
+ created.first_name.should == 'John'
317
+ created.last_name.should == 'Nunemaker'
318
+ created.should be_new
319
+ }.should_not change { document.count }
320
+ end
321
+
322
+ it "should disregard non-keys when initializing, but use them in the query" do
323
+ expect {
324
+ document.create(:first_name => 'John', :age => 9)
325
+ document.first_or_new(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
326
+ }.to_not raise_error
327
+ end
328
+ end
329
+
330
+ context ".delete (single document)" do
331
+ before do
332
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
333
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
334
+ document.delete(@doc1._id)
335
+ end
336
+
337
+ it "should remove document from collection" do
338
+ document.count.should == 1
339
+ end
340
+
341
+ it "should not remove other documents" do
342
+ document.find(@doc2._id).should_not be(nil)
343
+ end
344
+ end
345
+
346
+ context ".delete (multiple documents)" do
347
+ it "should work with multiple arguments" do
348
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
349
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
350
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
351
+ document.delete(@doc1._id, @doc2._id)
352
+
353
+ document.count.should == 1
354
+ end
355
+
356
+ it "should work with array as argument" do
357
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
358
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
359
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
360
+ document.delete([@doc1._id, @doc2._id])
361
+
362
+ document.count.should == 1
363
+ end
364
+ end
365
+
366
+ context ".delete_all" do
367
+ before do
368
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
369
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
370
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
371
+ end
372
+
373
+ it "should remove all documents when given no conditions" do
374
+ document.delete_all
375
+ document.count.should == 0
376
+ end
377
+
378
+ it "should only remove matching documents when given conditions" do
379
+ document.delete_all({:first_name => 'John'})
380
+ document.count.should == 2
381
+ end
382
+
383
+ it "should convert the conditions to marklogic criteria" do
384
+ document.delete_all(:age => [26, 27])
385
+ document.count.should == 1
386
+ end
387
+ end
388
+
389
+ context ".destroy (single document)" do
390
+ before do
391
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
392
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
393
+ document.destroy(@doc1._id)
394
+ end
395
+
396
+ it "should remove document from collection" do
397
+ document.count.should == 1
398
+ end
399
+
400
+ it "should not remove other documents" do
401
+ document.find(@doc2._id).should_not be(nil)
402
+ end
403
+ end
404
+
405
+ context ".destroy (multiple documents)" do
406
+ it "should work with multiple arguments" do
407
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
408
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
409
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
410
+ document.destroy(@doc1._id, @doc2._id)
411
+
412
+ document.count.should == 1
413
+ end
414
+
415
+ it "should work with array as argument" do
416
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
417
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
418
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
419
+ document.destroy([@doc1._id, @doc2._id])
420
+
421
+ document.count.should == 1
422
+ end
423
+ end
424
+
425
+ context ".destroy_all" do
426
+ before do
427
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
428
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
429
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
430
+ end
431
+
432
+ it "should remove all documents when given no conditions" do
433
+ document.destroy_all
434
+ document.count.should == 0
435
+ end
436
+
437
+ it "should only remove matching documents when given conditions" do
438
+ document.destroy_all(:first_name => 'John')
439
+ document.count.should == 2
440
+ document.destroy_all(:age => 26)
441
+ document.count.should == 1
442
+ end
443
+
444
+ it "should convert the conditions to marklogic criteria" do
445
+ document.destroy_all(:age => [26, 27])
446
+ document.count.should == 1
447
+ end
448
+ end
449
+
450
+ context ".count" do
451
+ before do
452
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
453
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
454
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
455
+ end
456
+
457
+ it "should count all with no arguments" do
458
+ document.count.should == 3
459
+ end
460
+
461
+ it "should return 0 if there are no documents in the collection" do
462
+ document.delete_all
463
+ document.count.should == 0
464
+ end
465
+
466
+ it "should return 0 if the collection does not exist" do
467
+ klass = Doc do
468
+ set_collection_name 'foobarbazwickdoesnotexist'
469
+ end
470
+
471
+ klass.count.should == 0
472
+ end
473
+
474
+ it "should return count for matching documents if conditions provided" do
475
+ document.count(:age => 27).should == 1
476
+ end
477
+
478
+ it "should convert the conditions to marklogic criteria" do
479
+ document.count(:age => [26, 27]).should == 2
480
+ end
481
+ end
482
+
483
+ context ".size" do
484
+ it "should return 0 if no documents" do
485
+ document.count.should == 0
486
+ end
487
+
488
+ it "should return the number of documents" do
489
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
490
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
491
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
492
+ document.count.should == 3
493
+ end
494
+ end
495
+
496
+ context ".empty?" do
497
+ it "should be true if no documents" do
498
+ document.empty?.should be_truthy
499
+ end
500
+
501
+ it "should be false if documents present" do
502
+ @doc = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
503
+ document.empty?.should be_falsey
504
+ end
505
+ end
506
+
507
+ context ".exists?" do
508
+ before do
509
+ @doc = document.create(:first_name => "James", :age => 27)
510
+ end
511
+
512
+ it "should be true when at least one document exists" do
513
+ document.exists?.should == true
514
+ end
515
+
516
+ it "should be false when no documents exist" do
517
+ @doc.destroy
518
+ document.exists?.should == false
519
+ end
520
+
521
+ it "should be true when at least one document exists that matches the conditions" do
522
+ document.exists?(:first_name => "James").should == true
523
+ end
524
+
525
+ it "should be false when no documents exist with the provided conditions" do
526
+ document.exists?(:first_name => "Jean").should == false
527
+ end
528
+ end
529
+
530
+ context "to_a" do
531
+ it "should return an array" do
532
+ document.to_a.class.should == Array
533
+ end
534
+
535
+ it "should return everything" do
536
+ @doc2 = document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
537
+ @doc1 = document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
538
+ @doc3 = document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
539
+ document.to_a.size.should == 3
540
+ end
541
+ end
542
+
543
+ context ".where" do
544
+ let!(:query) { document.where(:last_name => 'Nunemaker') }
545
+ let!(:doc1) { document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27') }
546
+ let!(:doc2) { document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28') }
547
+ let!(:doc3) { document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26') }
548
+
549
+ it "should fetch documents when kicker called" do
550
+ docs = query.all
551
+ docs.should include(doc1)
552
+ docs.should include(doc3)
553
+ docs.should_not include(doc2)
554
+ end
555
+
556
+ it "should be chainable" do
557
+ query.sort(:age).first.should == doc3
558
+ end
559
+
560
+ context "with methods from MarkMapper::Plugins::Querying" do
561
+ it "should delete" do
562
+ lambda do
563
+ document.where(:first_name => 'Steve').delete(doc1.id, doc2.id)
564
+ end.should change { document.count }.by(-1)
565
+ document.all(:order => 'first_name').should =~ [doc1, doc3]
566
+ end
567
+
568
+ it "should delete_all" do
569
+ lambda do
570
+ document.where(:first_name => 'Steph').delete_all(:last_name => "Nunemaker")
571
+ end.should change { document.count }.by(-1)
572
+ document.all(:order => 'first_name').should =~ [doc1, doc2]
573
+ end
574
+
575
+ it "should destroy" do
576
+ lambda do
577
+ document.where(:first_name => 'Steve').destroy(doc1.id, doc2.id)
578
+ end.should raise_error(MarkMapper::DocumentNotFound)
579
+ document.count.should == 3
580
+
581
+ lambda do
582
+ document.where(:last_name => 'Nunemaker').destroy(doc1.id, doc3.id)
583
+ end.should change { document.count }.by(-2)
584
+ document.all.should == [doc2]
585
+ end
586
+
587
+ it "should destroy_all" do
588
+ lambda do
589
+ document.where(:first_name => 'Steph').destroy_all(:last_name => "Nunemaker")
590
+ end.should change { document.count }.by(-1)
591
+ document.all(:order => 'first_name').should =~ [doc1, doc2]
592
+ end
593
+ end
594
+ end
595
+
596
+ context ".fields" do
597
+ before do
598
+ @doc1 = document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
599
+ @doc2 = document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
600
+ @doc3 = document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
601
+ @query = document.fields(:age)
602
+ end
603
+
604
+ it "should fetch documents when kicker called" do
605
+ docs = @query.all
606
+ docs.should include(@doc1)
607
+ docs.should include(@doc3)
608
+ docs.should include(@doc2)
609
+ docs.each do |doc|
610
+ doc.age.should_not be_nil
611
+ doc.first_name.should be_nil # key was not loaded
612
+ doc.last_name.should be_nil # key was not loaded
613
+ end
614
+ end
615
+
616
+ it "should be chainable" do
617
+ @query.sort(:age).all.map(&:age).should == [26, 27, 28]
618
+ end
619
+ end
620
+
621
+ context ".limit" do
622
+ before do
623
+ @doc1 = document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
624
+ @doc2 = document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
625
+ @doc3 = document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
626
+ @query = document.limit(2)
627
+ end
628
+
629
+ it "should fetch documents when kicker called" do
630
+ docs = @query.all
631
+ docs.size.should == 2
632
+ end
633
+
634
+ it "should be chainable" do
635
+ result = [26, 27]
636
+ @query.sort(:age).all.map(&:age).should == result
637
+ @query.count.should > result.size
638
+ end
639
+ end
640
+
641
+ context ".skip" do
642
+ before do
643
+ @doc1 = document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
644
+ @doc2 = document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
645
+ @doc3 = document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
646
+ @query = document.skip(1)
647
+ end
648
+
649
+ it "should fetch documents when kicker called" do
650
+ docs = @query.all
651
+ docs.size.should == 2 # skipping 1 out of 3
652
+ end
653
+
654
+ it "should be chainable" do
655
+ result = [27, 28]
656
+ @query.sort(:age).all.map(&:age).should == result
657
+ @query.count.should > result.size
658
+ end
659
+ end
660
+
661
+ context ".sort" do
662
+ before do
663
+ @doc1 = document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
664
+ @doc2 = document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
665
+ @doc3 = document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
666
+ @query = document.sort(:age)
667
+ end
668
+
669
+ it "should fetch documents when kicker called" do
670
+ @query.all.should =~ [@doc3, @doc1, @doc2]
671
+ end
672
+
673
+ it "should be chainable" do
674
+ result = [28]
675
+ @query.skip(2).all.map(&:age).should == result
676
+ @query.count.should > result.size
677
+ end
678
+ end
679
+
680
+ context "#update_attributes (new document)" do
681
+ before do
682
+ @doc = document.new(:first_name => 'John', :age => '27')
683
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
684
+ end
685
+
686
+ it "should insert document into the collection" do
687
+ document.count.should == 1
688
+ end
689
+
690
+ it "should assign an id for the document" do
691
+ @doc.id.should be_instance_of(MarkLogic::ObjectId)
692
+ end
693
+
694
+ it "should save attributes" do
695
+ @doc.first_name.should == 'Johnny'
696
+ @doc.age.should == 30
697
+ end
698
+
699
+ it "should update attributes in the database" do
700
+ doc = @doc.reload
701
+ doc.should == @doc
702
+ doc.first_name.should == 'Johnny'
703
+ doc.age.should == 30
704
+ end
705
+
706
+ it "should allow updating custom attributes" do
707
+ @doc.update_attributes(:gender => 'mALe')
708
+ @doc.reload.gender.should == 'mALe'
709
+ end
710
+ end
711
+
712
+ context "#update_attributes (existing document)" do
713
+ before do
714
+ @doc = document.create(:first_name => 'John', :age => '27')
715
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
716
+ end
717
+
718
+ it "should not insert document into collection" do
719
+ document.count.should == 1
720
+ end
721
+
722
+ it "should update attributes" do
723
+ @doc.first_name.should == 'Johnny'
724
+ @doc.age.should == 30
725
+ end
726
+
727
+ it "should update attributes in the database" do
728
+ doc = @doc.reload
729
+ doc.first_name.should == 'Johnny'
730
+ doc.age.should == 30
731
+ end
732
+ end
733
+
734
+ context "#update_attributes (return value)" do
735
+ before do
736
+ document.key :foo, String, :required => true
737
+ end
738
+
739
+ it "should be true if document valid" do
740
+ document.new.update_attributes(:foo => 'bar').should be_truthy
741
+ end
742
+
743
+ it "should be false if document not valid" do
744
+ document.new.update_attributes({}).should be_falsey
745
+ end
746
+ end
747
+
748
+ context "#update_attribute" do
749
+ before do
750
+ @doc = document.create(:first_name => 'John', :age => '27')
751
+ end
752
+
753
+ it "should accept symbols as keys" do
754
+ @doc.update_attribute(:first_name, 'Chris').should be_truthy
755
+ @doc.reload.first_name.should == 'Chris'
756
+ end
757
+
758
+ it "should update the attribute" do
759
+ @doc.update_attribute('first_name', 'Chris').should be_truthy
760
+ @doc.reload.first_name.should == 'Chris'
761
+ end
762
+
763
+ it "should update the attribute without invoking validations" do
764
+ document.key :name, String, :required => true
765
+
766
+ expect(@doc).to receive(:valid?).never
767
+ @doc.update_attribute('name', '').should be_truthy
768
+
769
+ @doc.reload.name.should == ''
770
+ document.count.should == 1
771
+ end
772
+ end
773
+
774
+ context "#save (new document)" do
775
+ before do
776
+ @doc = document.new(:first_name => 'John', :age => '27')
777
+ @doc.save
778
+ end
779
+
780
+ it "should insert document into the collection" do
781
+ document.count.should == 1
782
+ end
783
+
784
+ it "should assign an id for the document" do
785
+ @doc.id.should be_instance_of(MarkLogic::ObjectId)
786
+ end
787
+
788
+ it "should save attributes" do
789
+ @doc.first_name.should == 'John'
790
+ @doc.age.should == 27
791
+ end
792
+
793
+ it "should update attributes in the database" do
794
+ doc = @doc.reload
795
+ doc.should == @doc
796
+ doc.first_name.should == 'John'
797
+ doc.age.should == 27
798
+ end
799
+
800
+ it "should allow to add custom attributes to the document" do
801
+ @doc = document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
802
+ @doc.save
803
+ doc = @doc.reload
804
+ doc.gender.should == 'male'
805
+ doc.tags.should == [1, "2"]
806
+ end
807
+
808
+ it "should allow to use custom methods to assign properties" do
809
+ klass = Doc do
810
+ key :name, String
811
+
812
+ def realname=(value)
813
+ self.name = value
814
+ end
815
+ end
816
+
817
+ person = klass.new(:realname => 'David')
818
+ person.save
819
+ person.reload.name.should == 'David'
820
+ end
821
+
822
+ context "with key of type date" do
823
+ it "should save the date value as a Time object" do
824
+ doc = document.new(:first_name => 'John', :age => '27', :date => "2009-12-01")
825
+ doc.save
826
+ doc.date.should == Date.new(2009, 12, 1)
827
+ end
828
+ end
829
+ end
830
+
831
+ context "#save (existing document)" do
832
+ before do
833
+ @doc = document.create(:first_name => 'John', :age => '27')
834
+ @doc.first_name = 'Johnny'
835
+ @doc.age = 30
836
+ @doc.save
837
+ end
838
+
839
+ it "should not insert document into collection" do
840
+ document.count.should == 1
841
+ end
842
+
843
+ it "should update attributes" do
844
+ @doc.first_name.should == 'Johnny'
845
+ @doc.age.should == 30
846
+ end
847
+
848
+ it "should update attributes in the database" do
849
+ doc = @doc.reload
850
+ doc.first_name.should == 'Johnny'
851
+ doc.age.should == 30
852
+ end
853
+
854
+ it "should allow updating custom attributes" do
855
+ @doc = document.new(:first_name => 'David', :age => '26', :gender => 'male')
856
+ @doc.gender = 'Male'
857
+ @doc.save
858
+ @doc.reload.gender.should == 'Male'
859
+ end
860
+ end
861
+
862
+ context "#save (with validations off)" do
863
+ before do
864
+ document = Doc do
865
+ key :name, String, :required => true
866
+ end
867
+ end
868
+
869
+ it "should insert invalid document" do
870
+ doc = document.new
871
+ expect(doc).to receive(:valid?).never
872
+ doc.save(:validate => false)
873
+ document.count.should == 1
874
+ end
875
+ end
876
+
877
+ context "#save (with options)" do
878
+ before do
879
+ @document = Doc do
880
+ key :name, String
881
+ end
882
+ # @document.ensure_index :name, :unique => true
883
+ end
884
+ # after { drop_indexes(@document) }
885
+
886
+ it "should raise argument error if options has unsupported key" do
887
+ expect {
888
+ @document.new.save(:foo => true)
889
+ }.to raise_error(ArgumentError)
890
+ end
891
+ end
892
+
893
+ context "#save! (with options)" do
894
+ before do
895
+ @document = Doc { key :name, String }
896
+ # @document.ensure_index :name, :unique => true
897
+ end
898
+ # after { drop_indexes(@document) }
899
+
900
+ it "should raise argument error if options has unsupported key" do
901
+ expect {
902
+ @document.new.save!(:foo => true)
903
+ }.to raise_error(ArgumentError)
904
+ end
905
+ end
906
+
907
+ context "#destroy" do
908
+ before do
909
+ @doc = document.create(:first_name => 'John', :age => '27')
910
+ @doc.destroy
911
+ end
912
+
913
+ it "should remove the document from the collection" do
914
+ document.count.should == 0
915
+ end
916
+ end
917
+
918
+ context "#delete" do
919
+ before do
920
+ @doc1 = document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
921
+ @doc2 = document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
922
+
923
+ document.class_eval do
924
+ before_destroy :before_destroy_callback
925
+ after_destroy :after_destroy_callback
926
+
927
+ def history; @history ||= [] end
928
+ def before_destroy_callback; history << :after_destroy end
929
+ def after_destroy_callback; history << :after_destroy end
930
+ end
931
+
932
+ @doc1.delete
933
+ end
934
+
935
+ it "should remove document from collection" do
936
+ document.count.should == 1
937
+ end
938
+
939
+ it "should not remove other documents" do
940
+ document.find(@doc2.id).should_not be(nil)
941
+ end
942
+
943
+ it "should not call before/after destroy callbacks" do
944
+ @doc1.history.should == []
945
+ end
946
+ end
947
+
948
+ context "#new" do
949
+ it "should accept a block" do
950
+ user = document.new do |doc|
951
+ doc.first_name = "John"
952
+ end
953
+
954
+ user.first_name.should == "John"
955
+ end
956
+ end
957
+
958
+ context "#create" do
959
+ it "should accept a block" do
960
+ user = document.create do |doc|
961
+ doc.first_name = "John"
962
+ end
963
+
964
+ user.first_name.should == "John"
965
+ end
966
+ end
967
+
968
+ context "#create!" do
969
+ it "should accept a block" do
970
+ user = document.create! do |doc|
971
+ doc.first_name = "John"
972
+ end
973
+
974
+ user.first_name.should == "John"
975
+ end
976
+ end
977
+
978
+ context "#scoped" do
979
+ it "should return a Query" do
980
+ document.scoped.should be_a MarkMapper::Plugins::Querying::DecoratedMarkMapperQuery
981
+ document.scoped.criteria_hash.should be_empty
982
+ end
983
+ end
984
+ end