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