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.
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