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,153 @@
1
+ require 'spec_helper'
2
+
3
+ describe MarkMapper::Plugins::Keys::Static do
4
+ before do
5
+ @klass = Class.new do
6
+ include MarkMapper::Document
7
+ end
8
+ end
9
+
10
+ it "is off by default" do
11
+ @klass.static_keys.should == false
12
+ end
13
+
14
+ it "can be turned on" do
15
+ @klass.static_keys = true
16
+ @klass.static_keys.should == true
17
+ end
18
+
19
+ describe "a class with static keys" do
20
+ before do
21
+ @klass = Class.new do
22
+ include MarkMapper::Document
23
+
24
+ self.static_keys = true
25
+
26
+ attr_accessor :an_accessor
27
+
28
+ key "valid_key", String
29
+ end
30
+
31
+ @obj = @klass.new
32
+ end
33
+
34
+ it "should be able to assign to a valid key" do
35
+ @obj.valid_key = "foo"
36
+ @obj.valid_key.should == "foo"
37
+ end
38
+
39
+ it "should raise a MissingKeyError if trying to assign to a field that does not exist" do
40
+ lambda {
41
+ @obj['foo'] = "foo"
42
+ }.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
43
+ end
44
+
45
+ it "should raise a MissingKeyError if trying to read from a field that does not exist" do
46
+ lambda {
47
+ @obj['foo']
48
+ }.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
49
+ end
50
+
51
+ it "should raise a MissingKeyError if trying to set attributes= to a field that does not exist" do
52
+ lambda {
53
+ @obj.attributes = {:foo => "foo"}
54
+ }.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
55
+ end
56
+
57
+ it "should be able to call a method" do
58
+ @obj.an_accessor = :bar
59
+ @obj.an_accessor.should == :bar
60
+ end
61
+
62
+ it "should raise a NoMethodError if trying to call the non-existant key method" do
63
+ lambda {
64
+ @obj.foo
65
+ }.should raise_error(NoMethodError)
66
+ end
67
+
68
+ it "should raise for read_key" do
69
+ lambda {
70
+ @obj.read_key('foo')
71
+ }.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
72
+ end
73
+
74
+ it "should raise for attribute" do
75
+ lambda {
76
+ @obj.attribute('foo')
77
+ }.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
78
+ end
79
+
80
+ it "should not raise if trying to assign to a field that exists (but with a symbol instead of a string)" do
81
+ lambda {
82
+ @obj.write_key(:valid_key, "foo")
83
+ }.should_not raise_error
84
+
85
+ @obj.valid_key.should == "foo"
86
+ end
87
+
88
+ it "should not raise a MissingKeyError if trying to read from a field that exists (but with a symbol instead of a string)" do
89
+ lambda {
90
+ @obj[:valid_key]
91
+ }.should_not raise_error
92
+ end
93
+
94
+ it "should not blow up when loading if there is a key defined in the db that has not been defined (but it should not load it)" do
95
+ @klass.collection.insert({ :foo => "bar", :valid_key => "something" })
96
+ @obj = @klass.first
97
+ @obj.valid_key.should == "something"
98
+
99
+ lambda {
100
+ @obj.foo
101
+ }.should raise_error(NoMethodError)
102
+ end
103
+ end
104
+
105
+ describe "a class without static keys" do
106
+ before do
107
+ @klass = Class.new do
108
+ include MarkMapper::Document
109
+ key :valid_key, String
110
+ end
111
+
112
+ @obj = @klass.new
113
+ end
114
+
115
+ it "should be able to assign to a valid key" do
116
+ @obj.valid_key = "foo"
117
+ @obj.valid_key.should == "foo"
118
+ end
119
+
120
+ it "should be able to read and assign a dynamic key" do
121
+ @obj['foo'].should be_nil
122
+ @obj['foo'] = "bar"
123
+ @obj['foo'].should == "bar"
124
+ end
125
+ end
126
+
127
+ describe "with mixed static + non static models" do
128
+ before do
129
+ @static_model = Class.new do
130
+ include MarkMapper::Document
131
+ self.static_keys = true
132
+ end
133
+
134
+ @non_static_model = Class.new do
135
+ include MarkMapper::Document
136
+ self.static_keys = false
137
+ end
138
+
139
+ @static_key_object = @static_model.new
140
+ @non_static_key_object = @non_static_model.new
141
+ end
142
+
143
+ it "should allow non-static key assignment to one, only static assignment to the other" do
144
+ @non_static_key_object['foo'] = 'bar'
145
+ @non_static_key_object.foo.should == 'bar'
146
+ @non_static_key_object['foo'].should == 'bar'
147
+
148
+ lambda {
149
+ @static_key_object['foo'] = 'bar'
150
+ }.should raise_error
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Timestamps" do
4
+ context "included" do
5
+ before do
6
+ @klass = Doc do
7
+ key :first_name, String
8
+ end
9
+ @klass.timestamps!
10
+ end
11
+
12
+ it "should set record_timestamps to true" do
13
+ @klass.record_timestamps.should be(true)
14
+ end
15
+ end
16
+
17
+ context "timestamping" do
18
+ before do
19
+ @klass = Doc do
20
+ key :first_name, String
21
+ key :last_name, String
22
+ key :age, Integer
23
+ key :date, Date
24
+ end
25
+ @klass.timestamps!
26
+ end
27
+
28
+ context "when #record_timestamps is set to true" do
29
+ it "should set created_at and updated_at on create" do
30
+ doc = @klass.new(:first_name => 'John', :age => 27)
31
+ doc.created_at.should be(nil)
32
+ doc.updated_at.should be(nil)
33
+ doc.save
34
+ doc.created_at.should_not be(nil)
35
+ doc.updated_at.should_not be(nil)
36
+ end
37
+
38
+ it "should not overwrite created_at if it already exists" do
39
+ original_created_at = 1.month.ago
40
+ doc = @klass.new(:first_name => 'John', :age => 27, :created_at => original_created_at)
41
+ doc.created_at.to_i.should == original_created_at.to_i
42
+ doc.updated_at.should be_nil
43
+ doc.save
44
+ doc.created_at.to_i.should == original_created_at.to_i
45
+ doc.updated_at.should_not be_nil
46
+ end
47
+
48
+ it "should set updated_at on field update but leave created_at alone" do
49
+ doc = @klass.create(:first_name => 'John', :age => 27)
50
+ old_created_at = doc.created_at
51
+ old_updated_at = doc.updated_at
52
+ doc.first_name = 'Johnny'
53
+
54
+ Timecop.freeze(Time.now + 5.seconds) do
55
+ doc.save
56
+ end
57
+
58
+ doc.created_at.should == old_created_at
59
+ doc.updated_at.should_not == old_updated_at
60
+ end
61
+
62
+ it "should set updated_at on document update but leave created_at alone" do
63
+ doc = @klass.create(:first_name => 'John', :age => 27)
64
+ old_created_at = doc.created_at.to_i
65
+
66
+ new_updated_at = Time.at(Time.now.to_i + 5.seconds)
67
+ Timecop.freeze(new_updated_at) do
68
+ @klass.update(doc._id, { :first_name => 'Johnny' })
69
+ end
70
+
71
+ doc = doc.reload
72
+ doc.created_at.to_i.should be_within(1).of(old_created_at.to_i)
73
+ doc.updated_at.to_i.should be_within(1).of(new_updated_at.to_i)
74
+ end
75
+ end
76
+
77
+ context "when #record_timestamps is set to false" do
78
+ before do
79
+ @klass.record_timestamps = false
80
+ end
81
+
82
+ after do
83
+ @klass.record_timestamps = true
84
+ end
85
+
86
+ it "should not set created_at on document create" do
87
+ doc = @klass.create(:first_name => "John")
88
+ doc.created_at.should be_nil
89
+ end
90
+
91
+ it "should not set updated_at on document create" do
92
+ doc = @klass.create(:first_name => "John")
93
+ doc.updated_at.should be_nil
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Touch" do
4
+ context "touch" do
5
+ context "document" do
6
+ before do
7
+ @document = Doc { timestamps! }
8
+ end
9
+
10
+ it "should update the updated_at timestamp" do
11
+ doc = @document.create
12
+ old_updated_at = doc.updated_at
13
+
14
+ Timecop.freeze(Time.now + 1.day) do
15
+ doc.touch
16
+ end
17
+
18
+ doc.reload
19
+ doc.updated_at.should_not == old_updated_at
20
+ end
21
+ end
22
+
23
+ context "embedded document" do
24
+ it "should update the updated_at timestamp" do
25
+ Doc = Doc("Document") { timestamps!}
26
+ Emdoc = EDoc("EmbeddedDocument") { timestamps! }
27
+ Doc.has_many :emdocs, :class => Emdoc
28
+
29
+ doc = Doc.create
30
+ emdoc = Emdoc.new
31
+ doc.emdocs << emdoc
32
+ doc.save
33
+
34
+ old_updated_at = emdoc.updated_at
35
+ document_old_updated_at = doc.updated_at
36
+
37
+ Timecop.freeze(Time.now + 1.day) do
38
+ emdoc.touch
39
+ end
40
+
41
+ doc.reload
42
+ emdoc.updated_at.should_not == old_updated_at
43
+ doc.updated_at.should_not == document_old_updated_at
44
+ end
45
+ end
46
+
47
+ context "association" do
48
+ before do
49
+ @post_class = Doc("Post") do
50
+ key :touched_at, DateTime
51
+ timestamps!
52
+ end
53
+ @comment_class = Doc("Comment") do
54
+ key :post_id, ObjectId
55
+ key :text, String
56
+ timestamps!
57
+ end
58
+
59
+ @post_class.many :comments, :class => @comment_class
60
+ end
61
+
62
+ it 'should not be true by default' do
63
+ @comment_class.belongs_to :post, :class => @post_class
64
+ @comment_class.associations[:post].touch?.should_not be_truthy
65
+ end
66
+
67
+ context 'touch the parent when true' do
68
+ before do
69
+ @comment_class.belongs_to :post, :class => @post_class, :touch => true
70
+ @post = @post_class.create(:title => 'Hello, world!')
71
+ @comment = @post.comments.build
72
+ end
73
+
74
+ it "should when the child is created" do
75
+ orig_updated_at = @post.updated_at
76
+ Timecop.freeze(Time.now + 1.day) do
77
+ @comment.save
78
+ end
79
+
80
+ @post.reload.updated_at.should_not == orig_updated_at
81
+ end
82
+
83
+ it "should when the child is updated" do
84
+ @comment.save
85
+ old_updated_at = @post.updated_at
86
+ Timecop.freeze(Time.now + 2.day) do
87
+ @comment.update_attributes(:text => "Something")
88
+ end
89
+ @post.reload.updated_at.should_not == old_updated_at
90
+ end
91
+
92
+ it "should when the child is touched" do
93
+ @comment.save
94
+ old_updated_at = @post.updated_at
95
+ Timecop.freeze(Time.now + 3.day) do
96
+ @comment.touch
97
+ end
98
+ @post.reload.updated_at.should_not == old_updated_at
99
+ end
100
+ end
101
+
102
+ context "when set to a symbol that is a key of parent" do
103
+ it "should set that key on touch events" do
104
+ @comment_class.belongs_to :post, :class => @post_class, :touch => :touched_at
105
+ post = @post_class.create(:title => 'Hello, world!')
106
+ post.touched_at.should be_nil
107
+
108
+ comment = post.comments.build
109
+ comment.save
110
+ post.reload.touched_at.should_not be_nil
111
+ end
112
+ end
113
+
114
+ it 'should not touch the parent when false' do
115
+ post = @post_class.create(:title => 'Hello, world!')
116
+ comment = post.comments.build
117
+ Timecop.freeze(Time.now + 1.day) do
118
+ comment.save
119
+ end
120
+
121
+ post.reload.updated_at.should == post.created_at
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ module Userstamps
4
+ describe "Userstamps" do
5
+ class AltUser
6
+ include MarkMapper::Document
7
+ end
8
+
9
+ context "userstamping" do
10
+ before do
11
+ @document = Doc do
12
+ userstamps!
13
+ end
14
+ @document_alt_user = Doc do
15
+ userstamps! :class_name => 'Userstamps::AltUser'
16
+ end
17
+ @document_alt_user_class = Doc do
18
+ userstamps! :class => Userstamps::AltUser
19
+ end
20
+ @docs = [@document, @document_alt_user, @document_alt_user_class]
21
+ end
22
+
23
+ it "should add creator_id key" do
24
+ @docs.each{ |d| d.keys.should include('creator_id') }
25
+ end
26
+
27
+ it "should add updater_id key" do
28
+ @docs.each{ |d| d.keys.should include('updater_id') }
29
+ end
30
+
31
+ it "should add belongs_to creator" do
32
+ @docs.each{ |d| d.associations.keys.should include(:creator) }
33
+ end
34
+
35
+ it "should add belongs_to updater" do
36
+ @docs.each{ |d| d.associations.keys.should include(:updater) }
37
+ end
38
+
39
+ it "should properly set class names" do
40
+ @document.associations[:creator].class_name.should == 'User'
41
+ @document_alt_user.associations[:creator].class_name.should == 'Userstamps::AltUser'
42
+ @document_alt_user_class.associations[:creator].class_name.should == 'Userstamps::AltUser'
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,416 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Validations" do
4
+ context "Saving a new document that is invalid" do
5
+ before do
6
+ @document = Doc do
7
+ key :name, String, :required => true
8
+ end
9
+ end
10
+
11
+ it "should not insert document" do
12
+ doc = @document.new
13
+ doc.save
14
+ @document.count.should == 0
15
+ end
16
+
17
+ it "should populate document's errors" do
18
+ doc = @document.new
19
+ doc.errors.size.should == 0
20
+ doc.save
21
+ doc.errors.full_messages.should == ["Name can't be blank"]
22
+ end
23
+ end
24
+
25
+ context "Saving a document that is invalid (destructive)" do
26
+ before do
27
+ @document = Doc do
28
+ key :name, String, :required => true
29
+ end
30
+ end
31
+
32
+ it "should raise error" do
33
+ doc = @document.new
34
+ lambda { doc.save! }.should raise_error(MarkMapper::DocumentNotValid)
35
+ end
36
+
37
+ it "should set document on exception" do
38
+ doc = @document.new
39
+ begin
40
+ doc.save!
41
+ rescue MarkMapper::DocumentNotValid => e
42
+ e.document.should == doc
43
+ end
44
+ end
45
+ end
46
+
47
+ context "Creating a document that is invalid (destructive)" do
48
+ before do
49
+ @document = Doc do
50
+ key :name, String, :required => true
51
+ end
52
+ end
53
+
54
+ it "should raise error" do
55
+ lambda { @document.create! }.should raise_error(MarkMapper::DocumentNotValid)
56
+ end
57
+
58
+ it "should create a new document" do
59
+ instance = @document.create!(:name => "James")
60
+ instance.new_record?.should be_falsey
61
+ end
62
+ end
63
+
64
+ context "Saving an existing document that is invalid" do
65
+ before do
66
+ @document = Doc do
67
+ key :name, String, :required => true
68
+ end
69
+
70
+ @doc = @document.create(:name => 'John Nunemaker')
71
+ end
72
+
73
+ it "should not update document" do
74
+ @doc.name = nil
75
+ @doc.save
76
+ @doc.reload.name.should == 'John Nunemaker'
77
+ end
78
+
79
+ it "should populate document's errors" do
80
+ @doc.name = nil
81
+ @doc.save
82
+ @doc.errors.full_messages.should == ["Name can't be blank"]
83
+ end
84
+ end
85
+
86
+ context "Adding validation errors" do
87
+ before do
88
+ @document = Doc do
89
+ key :action, String
90
+ def action_present
91
+ errors.add(:action, 'is invalid') if action.blank?
92
+ end
93
+ end
94
+ end
95
+
96
+ it "should work with validate :on => :create callback" do
97
+ @document.validate :action_present, :on => :create
98
+
99
+ doc = @document.create(:action => nil)
100
+ doc.should have_error_on(:action)
101
+
102
+ doc.action = 'kick'
103
+ doc.save
104
+ doc.should_not have_error_on(:action)
105
+ end
106
+
107
+ it "should work with validate :on => :update callback" do
108
+ @document.validate :action_present, :on => :update
109
+
110
+ doc = @document.new
111
+ doc.action = nil
112
+ doc.should_not have_error_on(:action)
113
+ doc.save
114
+
115
+ doc.action = nil
116
+ doc.should have_error_on(:action)
117
+
118
+ doc.action = 'kick'
119
+ doc.should_not have_error_on(:action)
120
+ end
121
+ end
122
+
123
+ context "validating uniqueness of" do
124
+ before do
125
+ @document = Doc do
126
+ key :name, String
127
+ validates_uniqueness_of :name
128
+ end
129
+ end
130
+
131
+ it "should not fail if object is new" do
132
+ doc = @document.new
133
+ doc.should_not have_error_on(:name)
134
+ end
135
+
136
+ it "should not fail when new object is out of scope" do
137
+ document = Doc do
138
+ key :name
139
+ key :adult
140
+ validates_uniqueness_of :name, :scope => :adult
141
+ end
142
+ doc = document.new("name" => "joe", :adult => true)
143
+ doc.save.should be_truthy
144
+
145
+ doc2 = document.new("name" => "joe", :adult => false)
146
+ doc2.should be_valid
147
+ end
148
+
149
+ it "should work with i18n taken message" do
150
+ @document.create(:name => 'joe')
151
+ doc = @document.create(:name => 'joe')
152
+ doc.should have_error_on(:name)
153
+ doc.errors[:name].should == ['has already been taken']
154
+ end
155
+
156
+ it "should allow to update an object" do
157
+ doc = @document.new("name" => "joe")
158
+ doc.save.should be_truthy
159
+
160
+ allow(@document).to \
161
+ receive(:first).
162
+ with(:name => 'joe').
163
+ and_return(doc)
164
+
165
+ doc.name = "joe"
166
+ doc.valid?.should be_truthy
167
+ doc.should_not have_error_on(:name)
168
+ end
169
+
170
+ it "should fail if object name is not unique" do
171
+ doc = @document.new("name" => "joe")
172
+ doc.save.should be_truthy
173
+
174
+ allow(@document).to \
175
+ receive(:first).
176
+ with(:name => 'joe').
177
+ and_return(doc)
178
+
179
+ doc2 = @document.new("name" => "joe")
180
+ doc2.should have_error_on(:name)
181
+ end
182
+
183
+ it "should allow multiple blank entries if :allow_blank => true" do
184
+ document = Doc do
185
+ key :name
186
+ validates_uniqueness_of :name, :allow_blank => :true
187
+ end
188
+
189
+ doc = document.new("name" => "")
190
+ doc.save.should be_truthy
191
+
192
+ allow(@document).to \
193
+ receive(:first).
194
+ with(:name => '').
195
+ and_return(doc)
196
+
197
+ doc2 = document.new("name" => "")
198
+ doc2.should_not have_error_on(:name)
199
+ end
200
+
201
+ it "should allow multiple nil entries if :allow_nil => true" do
202
+ document = Doc do
203
+ key :name
204
+ validates_uniqueness_of :name, :allow_nil => :true
205
+ end
206
+
207
+ doc = document.new('name' => nil)
208
+ doc.save.should be_truthy
209
+
210
+ doc2 = document.new('name' => nil)
211
+ doc2.should_not have_error_on(:name)
212
+ end
213
+
214
+ it "should allow entries that differ only in case by default" do
215
+ document = Doc do
216
+ key :name
217
+ validates_uniqueness_of :name
218
+ end
219
+
220
+ doc = document.new("name" => "BLAMMO")
221
+ doc.save.should be_truthy
222
+
223
+ doc2 = document.new("name" => "Blammo")
224
+ doc2.should_not have_error_on(:name)
225
+ end
226
+
227
+ context "with :case_sensitive => false" do
228
+ before do
229
+ @document = Doc do
230
+ key :name
231
+ validates_uniqueness_of :name, :case_sensitive => false
232
+ end
233
+ end
234
+
235
+ it "should fail on entries that differ only in case" do
236
+ doc = @document.new("name" => "BLAMMO")
237
+ doc.save.should be_truthy
238
+
239
+ doc2 = @document.new("name" => "blammo")
240
+ doc2.should have_error_on(:name)
241
+ end
242
+
243
+ it "should not raise an error if value is nil" do
244
+ doc = @document.new("name" => nil)
245
+ lambda { doc.valid? }.should_not raise_error
246
+ end
247
+
248
+ it "should not raise an error if special Regexp characters used" do
249
+ doc = @document.new("name" => '?')
250
+ lambda { doc.valid? }.should_not raise_error
251
+ end
252
+
253
+ it "should check for uniqueness using entire string" do
254
+ doc = @document.new("name" => "John Doe")
255
+ doc.save.should be_truthy
256
+
257
+ doc2 = @document.new("name" => "John")
258
+ doc2.valid?.should be_truthy
259
+ end
260
+ end
261
+
262
+ context "scoped by a single attribute" do
263
+ before do
264
+ @document = Doc do
265
+ key :name, String
266
+ key :scope, String
267
+ validates_uniqueness_of :name, :scope => :scope
268
+ end
269
+ end
270
+
271
+ it "should fail if the same name exists in the scope" do
272
+ doc = @document.new("name" => "joe", "scope" => "one")
273
+ doc.save.should be_truthy
274
+
275
+ allow(@document).to \
276
+ receive(:first).
277
+ with(:name => 'joe', :scope => "one").
278
+ and_return(doc)
279
+
280
+ doc2 = @document.new("name" => "joe", "scope" => "one")
281
+ doc2.should have_error_on(:name)
282
+ end
283
+
284
+ it "should pass if the same name exists in a different scope" do
285
+ doc = @document.new("name" => "joe", "scope" => "one")
286
+ doc.save.should be_truthy
287
+
288
+ allow(@document).to \
289
+ receive(:first).
290
+ with(:name => 'joe', :scope => 'two').
291
+ and_return(nil)
292
+
293
+ doc2 = @document.new("name" => "joe", "scope" => "two")
294
+ doc2.should_not have_error_on(:name)
295
+ end
296
+ end
297
+
298
+ context "scoped by a multiple attributes" do
299
+ before do
300
+ @document = Doc do
301
+ key :name, String
302
+ key :first_scope, String
303
+ key :second_scope, String
304
+ validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
305
+ end
306
+ end
307
+
308
+ it "should fail if the same name exists in the scope" do
309
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
310
+ doc.save.should be_truthy
311
+
312
+ allow(@document).to \
313
+ receive(:first).
314
+ with(:name => 'joe', :first_scope => 'one', :second_scope => 'two').
315
+ and_return(doc)
316
+
317
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
318
+ doc2.should have_error_on(:name)
319
+ end
320
+
321
+ it "should pass if the same name exists in a different scope" do
322
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
323
+ doc.save.should be_truthy
324
+
325
+ allow(@document).to \
326
+ receive(:first).
327
+ with(:name => 'joe', :first_scope => 'one', :second_scope => 'one').
328
+ and_return(nil)
329
+
330
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
331
+ doc2.should_not have_error_on(:name)
332
+ end
333
+ end
334
+ end
335
+
336
+ context "validating associated docs" do
337
+ before do
338
+ @child_class = EDoc do
339
+ key :name, :required => true
340
+ end
341
+
342
+ @root_class = Doc { }
343
+ @root_class.many :children, :class => @child_class
344
+ @root_class.validates_associated :children, :message => 'are invalid'
345
+ end
346
+
347
+ it "should pass if there are no associated docs" do
348
+ doc = @root_class.new
349
+ doc.save.should be_truthy
350
+ end
351
+
352
+ it "should pass if the associated doc is valid" do
353
+ doc = @root_class.new
354
+ doc.children.build(:name => 'Joe')
355
+ doc.save.should be_truthy
356
+ end
357
+
358
+ it "should fail if the associated doc is invalid" do
359
+ doc = @root_class.new
360
+ doc.children.build
361
+ doc.should have_error_on(:children)
362
+ doc.errors[:children].should == ['are invalid']
363
+ end
364
+ end
365
+
366
+ context "validating associated docs with custom context" do
367
+ before do
368
+ @child_class = EDoc do
369
+ key :name
370
+
371
+ validates_length_of :name, :minimum => 5, :on => :custom_context
372
+ end
373
+
374
+ @root_class = Doc { }
375
+ @root_class.many :children, :class => @child_class
376
+ @root_class.validates_associated :children, :context => :custom_context
377
+ end
378
+
379
+ it "should pass if there are no associated docs" do
380
+ doc = @root_class.new
381
+ doc.valid?(:custom_context).should be_truthy
382
+ end
383
+
384
+ it "should pass if the associated doc is valid" do
385
+ doc = @root_class.new
386
+ doc.children.build(:name => 'George')
387
+ doc.valid?(:custom_context).should be_truthy
388
+ end
389
+
390
+ it "should fail if the associated doc is invalid" do
391
+ doc = @root_class.new
392
+ doc.children.build(:name => 'Bob')
393
+ doc.valid?(:custom_context).should_not be_truthy
394
+ end
395
+ end
396
+
397
+ # context "validates uniqueness of with :unique shortcut" do
398
+ # it "should work" do
399
+ # @document = Doc do
400
+ # key :name, String, :unique => true
401
+ # end
402
+ #
403
+ # doc = @document.create(:name => 'John')
404
+ # doc.should_not have_error_on(:name)
405
+ #
406
+ # allow(@document).to \
407
+ # receive(:first).
408
+ # with(:name => 'John').
409
+ # and_return(doc)
410
+ #
411
+ # second_john = @document.create(:name => 'John')
412
+ # second_john.should have_error_on(:name)
413
+ # second_john.errors[:name].should == ['has already been taken']
414
+ # end
415
+ # end
416
+ end