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,235 @@
1
+ require 'spec_helper'
2
+
3
+ module CounterCacheFixtureModels
4
+ class User
5
+ include MarkMapper::Document
6
+
7
+ key :posts_count, Integer, :default => 0
8
+
9
+ has_many :posts,
10
+ :class_name => "CounterCacheFixtureModels::Post"
11
+ end
12
+
13
+ class Post
14
+ include MarkMapper::Document
15
+
16
+ key :comments_count, Integer, :default => 0
17
+ key :some_custom_comments_count, Integer, :default => 0
18
+ key :commentable_count, Integer, :default => 0
19
+
20
+ has_many :comments,
21
+ :class_name => "CounterCacheFixtureModels::Comment"
22
+
23
+ belongs_to :user,
24
+ :counter_cache => true,
25
+ :class_name => "CounterCacheFixtureModels::User"
26
+
27
+ many :polymorphic_comments,
28
+ :as => :commentable,
29
+ :class_name => "CounterCacheFixtureModels::Comment"
30
+ end
31
+
32
+ class Article
33
+ include MarkMapper::Document
34
+
35
+ key :commentable_count, Integer, :default => 0
36
+
37
+ many :polymorphic_comments,
38
+ :as => :commentable,
39
+ :class_name => "CounterCacheFixtureModels::Comment"
40
+ end
41
+
42
+ class Comment
43
+ include MarkMapper::Document
44
+
45
+ belongs_to :post,
46
+ :counter_cache => true,
47
+ :class_name => "CounterCacheFixtureModels::Post"
48
+
49
+ belongs_to :commentable,
50
+ :polymorphic => true,
51
+ :counter_cache => :commentable_count
52
+ end
53
+
54
+ class CustomComment
55
+ include MarkMapper::Document
56
+
57
+ belongs_to :post,
58
+ :counter_cache => :some_custom_comments_count,
59
+ :class_name => "CounterCacheFixtureModels::Post"
60
+ end
61
+ end
62
+
63
+ describe MarkMapper::Plugins::CounterCache do
64
+ before do
65
+ @post_class = CounterCacheFixtureModels::Post
66
+ @comment_class = CounterCacheFixtureModels::Comment
67
+ @user_class = CounterCacheFixtureModels::User
68
+ @custom_comment_class = CounterCacheFixtureModels::CustomComment
69
+ end
70
+
71
+ it "should have a key with posts_count defaulting to 0" do
72
+ @post_class.new.comments_count.should == 0
73
+ end
74
+
75
+ it "should update the count when a new object is created" do
76
+ post = @post_class.new
77
+ comment = @comment_class.new
78
+
79
+ post.save!
80
+
81
+ comment.post = post
82
+ comment.save!
83
+
84
+ post.reload
85
+ post.comments_count.should == 1
86
+
87
+ second_comment = @comment_class.new
88
+ second_comment.post = post
89
+ second_comment.save!
90
+
91
+ post.reload
92
+ post.comments_count.should == 2
93
+ end
94
+
95
+ it "should decrease the count by one when an object is destroyed" do
96
+ post = @post_class.new
97
+ comment = @comment_class.new
98
+
99
+ post.save!
100
+
101
+ comment.post = post
102
+ comment.save!
103
+
104
+ post.reload
105
+ post.comments_count.should == 1
106
+
107
+ comment.destroy
108
+ post.reload
109
+ post.comments_count.should == 0
110
+ end
111
+
112
+ it "should use the correct association name" do
113
+ @user = @user_class.new
114
+ @post = @post_class.new
115
+
116
+ @user.save!
117
+ @post.user = @user
118
+ @post.save!
119
+
120
+ @user.reload
121
+ @user.posts_count.should == 1
122
+ end
123
+
124
+ it "should be able to use a custom field name" do
125
+ @post = @post_class.new
126
+ @custom_comment = @custom_comment_class.new
127
+
128
+ @post.save!
129
+ @custom_comment.post = @post
130
+ @custom_comment.save!
131
+
132
+ @post.reload
133
+ @post.some_custom_comments_count.should == 1
134
+ end
135
+
136
+ it "should thrown an error if there is no association" do
137
+ lambda {
138
+ CounterCacheFixtureModels.module_eval do
139
+ class CommentWithInvalidAssociation
140
+ include MarkMapper::Document
141
+
142
+ belongs_to :post,
143
+ :class_name => "CounterCacheFixtureModels::Post"
144
+
145
+ counter_cache :foo
146
+ end
147
+ end
148
+ }.should raise_error(MarkMapper::Plugins::CounterCache::InvalidCounterCacheError, "You must define an association with name `foo' on model CommentWithInvalidAssociation")
149
+ end
150
+
151
+ it "should thown a sensible error if the field is not defined on the target object" do
152
+ lambda {
153
+ CounterCacheFixtureModels.module_eval do
154
+ class CommentWithBadRefenceField
155
+ include MarkMapper::Document
156
+
157
+ belongs_to :post,
158
+ :class_name => "CounterCacheFixtureModels::Post"
159
+
160
+ counter_cache :post, :field => :invalid_field
161
+ end
162
+ end
163
+ }.should raise_error(MarkMapper::Plugins::CounterCache::InvalidCounterCacheError, "Missing `key :invalid_field, Integer, :default => 0' on model CounterCacheFixtureModels::Post")
164
+ end
165
+
166
+ describe "with polymorphic associations" do
167
+ before do
168
+ @article = CounterCacheFixtureModels::Article.new
169
+ @comment = CounterCacheFixtureModels::Comment.new
170
+ @comment.commentable = @article
171
+ end
172
+
173
+ it "should update the counter cache on save" do
174
+ expect {
175
+ @comment.save!
176
+ @article.reload
177
+ }.to change(@article, :commentable_count).by(1)
178
+ end
179
+
180
+ it "should increment with a second object" do
181
+ @comment.save!
182
+
183
+ expect {
184
+ second_comment = CounterCacheFixtureModels::Comment.new
185
+ second_comment.commentable = @article
186
+ second_comment.save!
187
+ @article.reload
188
+ }.to change(@article, :commentable_count).by(1)
189
+ end
190
+
191
+ it "should decrement the counter cache on destroy" do
192
+ @comment.save!
193
+
194
+ expect {
195
+ @comment.destroy
196
+ @article.reload
197
+ }.to change(@article, :commentable_count).by(-1)
198
+ end
199
+
200
+ it "should increment with a different type of object" do
201
+ @comment.save!
202
+
203
+ expect {
204
+ second_comment = CounterCacheFixtureModels::Comment.new
205
+ second_comment.commentable = @article
206
+ second_comment.save!
207
+
208
+ @article.reload
209
+ }.to change(@article, :commentable_count).by(1)
210
+ end
211
+
212
+ describe "without a counter cache field" do
213
+ before do
214
+ @comment = CounterCacheFixtureModels::Comment.new
215
+ @klass = Class.new do
216
+ include MarkMapper::Document
217
+
218
+ many :polymorphic_comments,
219
+ :as => :commentable,
220
+ :class_name => "CounterCacheFixtureModels::Comment"
221
+ end
222
+
223
+ @obj = @klass.new
224
+ end
225
+
226
+ it "should raise at save (runtime) if there is no counter cache field" do
227
+ @comment.commentable = @obj
228
+
229
+ expect {
230
+ @comment.save!
231
+ }.to raise_error(MarkMapper::Plugins::CounterCache::InvalidCounterCacheError)
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,316 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Dirty" do
4
+ before do
5
+ @document = Doc {
6
+ key :phrase, String
7
+ key :paragraph, String, :alias => :para
8
+ }
9
+ end
10
+
11
+ context "marking changes" do
12
+ it "should not happen if there are none" do
13
+ doc = @document.new
14
+ doc.phrase_changed?.should be_falsey
15
+ doc.phrase_change.should be_nil
16
+ end
17
+
18
+ it "should happen when change happens" do
19
+ doc = @document.new
20
+ doc.phrase = 'Golly Gee Willikers Batman'
21
+ doc.phrase_changed?.should be_truthy
22
+ doc.phrase_was.should be_nil
23
+ doc.phrase_change.should == [nil, 'Golly Gee Willikers Batman']
24
+ end
25
+
26
+ it "should happen when initializing" do
27
+ doc = @document.new(:phrase => 'Foo')
28
+ doc.changed?.should be_truthy
29
+ end
30
+
31
+ it "should clear changes on save" do
32
+ doc = @document.new
33
+ doc.phrase = 'Golly Gee Willikers Batman'
34
+ doc.phrase_changed?.should be_truthy
35
+ doc.save
36
+ doc.phrase_changed?.should_not be_truthy
37
+ doc.phrase_change.should be_nil
38
+ end
39
+
40
+ it "should clear changes on save!" do
41
+ doc = @document.new
42
+ doc.phrase = 'Golly Gee Willikers Batman'
43
+ doc.phrase_changed?.should be_truthy
44
+ doc.save!
45
+ doc.phrase_changed?.should_not be_truthy
46
+ doc.phrase_change.should be_nil
47
+ end
48
+
49
+ it "should not happen when loading from database" do
50
+ doc = @document.create(:phrase => 'Foo')
51
+ expect_any_instance_of(@document).to receive(:attribute_will_change!).never
52
+ expect_any_instance_of(@document).to receive(:attribute_changed?).never
53
+ doc = @document.find(doc.id)
54
+ doc.changed?.should be_falsey
55
+ end
56
+
57
+ it "should not happen when reloading from database" do
58
+ doc = @document.create(:phrase => 'Foo')
59
+ doc = @document.find(doc.id)
60
+
61
+ doc.changed?.should be_falsey
62
+ doc.phrase = 'Fart'
63
+ doc.changed?.should be_truthy
64
+ doc.reload
65
+ doc.changed?.should be_falsey
66
+ end
67
+
68
+ it "should happen if changed after loading from database" do
69
+ doc = @document.create(:phrase => 'Foo')
70
+ doc.reload
71
+ doc.changed?.should be_falsey
72
+ doc.phrase = 'Bar'
73
+ doc.changed?.should be_truthy
74
+ end
75
+
76
+ it "should happen with aliased keys" do
77
+ doc = @document.create(:paragraph => 'Wibbly')
78
+ doc.paragraph = "Wobbly"
79
+ doc.changed?.should be_truthy
80
+ doc.paragraph_changed?.should be_truthy
81
+ end
82
+ end
83
+
84
+ context "blank new value and type integer" do
85
+ it "should not mark changes" do
86
+ @document.key :age, Integer
87
+
88
+ [nil, ''].each do |value|
89
+ doc = @document.new
90
+ doc.age = value
91
+ doc.age_changed?.should be_falsey
92
+ doc.age_change.should be_nil
93
+ end
94
+ end
95
+ end
96
+
97
+ context "blank new value and type float" do
98
+ it "should not mark changes" do
99
+ @document.key :amount, Float
100
+
101
+ [nil, ''].each do |value|
102
+ doc = @document.new
103
+ doc.amount = value
104
+ doc.amount_changed?.should be_falsey
105
+ doc.amount_change.should be_nil
106
+ end
107
+ end
108
+ end
109
+
110
+ context "changed?" do
111
+ it "should be true if key changed" do
112
+ doc = @document.new
113
+ doc.phrase = 'A penny saved is a penny earned.'
114
+ doc.changed?.should be_truthy
115
+ end
116
+
117
+ it "should be false if no keys changed" do
118
+ @document.new.changed?.should be_falsey
119
+ end
120
+
121
+ it "should not raise when key name is 'value'" do
122
+ @document.key :value, Integer
123
+
124
+ doc = @document.new
125
+ doc.value_changed?.should be_falsey
126
+ end
127
+
128
+ it "should be false if the same ObjectId was assigned in String format" do
129
+ @document.key :doc_id, ObjectId
130
+
131
+ doc = @document.create!(:doc_id => MarkLogic::ObjectId.new)
132
+ doc.changed?.should be_falsey
133
+ doc.doc_id = doc.doc_id.to_s
134
+ doc.changed?.should be_falsey
135
+ end
136
+ end
137
+
138
+ context "changes" do
139
+ it "should be empty hash if no changes" do
140
+ @document.new.changes.should == {}
141
+ end
142
+
143
+ it "should be hash of keys with values of changes if there are changes" do
144
+ doc = @document.new
145
+ doc.phrase = 'A penny saved is a penny earned.'
146
+ doc.changes['phrase'].should == [nil, 'A penny saved is a penny earned.']
147
+ end
148
+ end
149
+
150
+ context "changed" do
151
+ it "should be empty array if no changes" do
152
+ @document.new.changed.should == []
153
+ end
154
+
155
+ it "should be array of keys that have changed if there are changes" do
156
+ doc = @document.new
157
+ doc.phrase = 'A penny saved is a penny earned.'
158
+ doc.changed.should == ['phrase']
159
+ end
160
+ end
161
+
162
+ context "will_change!" do
163
+ it "should mark changes" do
164
+ doc = @document.create(:phrase => 'Foo')
165
+
166
+ doc.phrase << 'bar'
167
+ doc.phrase_changed?.should be_falsey
168
+
169
+ doc.phrase_will_change!
170
+ doc.phrase_changed?.should be_truthy
171
+ doc.phrase_change.should == ['Foobar', 'Foobar']
172
+
173
+ doc.phrase << '!'
174
+ doc.phrase_changed?.should be_truthy
175
+ doc.phrase_change.should == ['Foobar', 'Foobar!']
176
+ end
177
+ end
178
+
179
+ context "changing a foreign key through association" do
180
+ it "should mark changes" do
181
+ project_class = Doc do
182
+ key :name, String
183
+ end
184
+
185
+ milestone_class = Doc do
186
+ key :project_id, ObjectId
187
+ key :name, String
188
+ end
189
+ milestone_class.belongs_to :project, :class => project_class
190
+
191
+ milestone = milestone_class.create(:name => 'Launch')
192
+ milestone.project = project_class.create(:name => 'Harmony')
193
+ milestone.changed?.should be_truthy
194
+ milestone.changed.should == %w(project_id)
195
+ end
196
+ end
197
+
198
+ context "save with an invalid document" do
199
+ it "should not clear changes" do
200
+ validated_class = Doc do
201
+ key :name, String
202
+ key :required, String, :required=>true
203
+ end
204
+ validated_doc = validated_class.new
205
+ validated_doc.name = "I'm a changin"
206
+ validated_doc.save
207
+ validated_doc.changed?.should be_truthy
208
+
209
+ validated_doc.required = 1
210
+ validated_doc.save
211
+ validated_doc.changed?.should be_falsey
212
+ end
213
+ end
214
+
215
+ context "changing an already changed attribute" do
216
+ it "should preserve the original value" do
217
+ doc = @document.create(:a=>"b")
218
+ doc.a = "c"
219
+ doc.a_change.should == ["b","c"]
220
+ doc.a = "d"
221
+ doc.a_change.should == ["b","d"]
222
+ end
223
+ it "should reset changes when set back to the original value" do
224
+ doc = @document.create(:a=>"b")
225
+ doc.a = "c"
226
+ doc.a = "b"
227
+ doc.changed?.should be_falsey
228
+ end
229
+ end
230
+
231
+ context "reset_attribute!" do
232
+ it "should reset the attribute back to the previous value" do
233
+ doc = @document.create(:a=>"b")
234
+ doc.a = "c"
235
+ doc.reset_a!
236
+ doc.changed?.should be_falsey
237
+ doc.a.should == "b"
238
+ end
239
+ it "should reset the attribute back to the original value after several changes" do
240
+ doc = @document.create(:a=>"b")
241
+ doc.a = "c"
242
+ doc.a = "d"
243
+ doc.a = "e"
244
+ doc.reset_a!
245
+ doc.changed?.should be_falsey
246
+ doc.a.should == "b"
247
+ end
248
+ end
249
+
250
+ context "previous_changes" do
251
+ it "should reflect previously committed change" do
252
+ doc = @document.create(:a=>"b")
253
+ doc.a = "c"
254
+ changes = doc.changes
255
+ doc.save!
256
+ doc.previous_changes.should == changes
257
+ doc.previous_changes["a"].should == ["b", "c"]
258
+ end
259
+
260
+ it "should not include attributes loaded from db" do
261
+ doc = @document.create(:a => "b")
262
+ @document.find(doc.id).previous_changes.should be_blank
263
+ end
264
+ end
265
+
266
+ context "Embedded documents" do
267
+ before do
268
+ @edoc = EDoc('Duck') { key :name, String }
269
+ @edoc.plugin MarkMapper::Plugins::Dirty
270
+ @document = Doc('Long') { key :name, String }
271
+ @document.many :ducks, :class=>@edoc
272
+ @doc = @document.new
273
+ @duck = @doc.ducks.build
274
+ end
275
+
276
+ it "should track changes" do
277
+ @duck.name = "hi"
278
+ @duck.changed?.should be_truthy
279
+ end
280
+
281
+ it "should clear changes when saved" do
282
+ @duck.name = "hi"
283
+ @duck.changed?.should be_truthy
284
+ @duck.save!
285
+ @duck.changed?.should_not be_truthy
286
+ end
287
+
288
+ it "should clear changes when the parent is saved" do
289
+ @duck.name = "hi"
290
+ @duck.changed?.should be_truthy
291
+ @doc.save!
292
+ @duck.changed?.should_not be_truthy
293
+ end
294
+
295
+ context "with nested embedded documents" do
296
+ before do
297
+ @inner_edoc = EDoc('Dong') {key :name, String}
298
+ @inner_edoc.plugin MarkMapper::Plugins::Dirty
299
+ @edoc.many :dongs, :class=>@inner_edoc
300
+ @dong = @duck.dongs.build
301
+ end
302
+
303
+ it "should track changes" do
304
+ @dong.name = "hi"
305
+ @dong.changed?.should be_truthy
306
+ end
307
+
308
+ it "should clear changes when the root saves" do
309
+ @dong.name = "hi"
310
+ @dong.changed?.should be_truthy
311
+ @doc.save!
312
+ @dong.changed?.should be_falsey
313
+ end
314
+ end
315
+ end
316
+ end