strokedb 0.0.2.1 → 0.0.2.2

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 (192) hide show
  1. data/README +18 -20
  2. data/bench.html +4001 -0
  3. data/bin/strokedb +14 -0
  4. data/examples/movies.rb +105 -0
  5. data/examples/movies2.rb +97 -0
  6. data/examples/strokewiki/README +28 -0
  7. data/examples/strokewiki/view/edit.xhtml +27 -0
  8. data/examples/strokewiki/view/new.xhtml +26 -0
  9. data/examples/strokewiki/view/pages.xhtml +27 -0
  10. data/examples/strokewiki/view/show.xhtml +40 -0
  11. data/examples/strokewiki/view/versions.xhtml +25 -0
  12. data/examples/strokewiki/wiki.rb +106 -0
  13. data/examples/todo.rb +92 -0
  14. data/lib/strokedb.rb +85 -0
  15. data/lib/{config → strokedb}/config.rb +14 -9
  16. data/lib/strokedb/console.rb +87 -0
  17. data/lib/strokedb/core_ext.rb +10 -0
  18. data/lib/{util/ext → strokedb/core_ext}/blank.rb +1 -1
  19. data/lib/{util/ext → strokedb/core_ext}/enumerable.rb +0 -0
  20. data/lib/{util/ext → strokedb/core_ext}/fixnum.rb +0 -0
  21. data/lib/strokedb/core_ext/float.rb +4 -0
  22. data/lib/{util/ext → strokedb/core_ext}/hash.rb +0 -0
  23. data/lib/strokedb/core_ext/infinity.rb +33 -0
  24. data/lib/strokedb/core_ext/kernel.rb +41 -0
  25. data/lib/strokedb/core_ext/object.rb +16 -0
  26. data/lib/{util/ext → strokedb/core_ext}/string.rb +28 -1
  27. data/lib/strokedb/core_ext/symbol.rb +13 -0
  28. data/lib/strokedb/data_structures.rb +5 -0
  29. data/lib/strokedb/data_structures/chunked_skiplist.rb +123 -0
  30. data/lib/{data_structures → strokedb/data_structures}/inverted_list.rb +0 -0
  31. data/lib/{data_structures → strokedb/data_structures}/point_query.rb +0 -0
  32. data/lib/strokedb/data_structures/simple_skiplist.rb +350 -0
  33. data/lib/{data_structures → strokedb/data_structures}/skiplist.rb +1 -1
  34. data/lib/{document → strokedb}/document.rb +180 -71
  35. data/lib/{document → strokedb/document}/callback.rb +0 -0
  36. data/lib/{document → strokedb/document}/delete.rb +2 -2
  37. data/lib/strokedb/document/dsl.rb +4 -0
  38. data/lib/{document → strokedb/document/dsl}/associations.rb +0 -0
  39. data/lib/{document → strokedb/document/dsl}/coercions.rb +0 -0
  40. data/lib/strokedb/document/dsl/meta_dsl.rb +7 -0
  41. data/lib/{document → strokedb/document/dsl}/validations.rb +26 -21
  42. data/lib/{document → strokedb/document/dsl}/virtualize.rb +0 -0
  43. data/lib/{document → strokedb/document}/meta.rb +92 -29
  44. data/lib/{document → strokedb/document}/slot.rb +17 -5
  45. data/lib/{document → strokedb/document}/util.rb +0 -0
  46. data/lib/{document → strokedb/document}/versions.rb +2 -2
  47. data/lib/strokedb/index.rb +2 -0
  48. data/lib/strokedb/nsurl.rb +24 -0
  49. data/lib/strokedb/store.rb +149 -0
  50. data/lib/strokedb/stores.rb +6 -0
  51. data/lib/{stores → strokedb/stores}/chainable_storage.rb +20 -14
  52. data/lib/strokedb/stores/file_storage.rb +118 -0
  53. data/lib/{stores/inverted_list_index → strokedb/stores}/inverted_list_file_storage.rb +50 -0
  54. data/lib/strokedb/stores/memory_storage.rb +80 -0
  55. data/lib/{stores → strokedb/stores}/remote_store.rb +10 -4
  56. data/lib/strokedb/sync.rb +4 -0
  57. data/lib/{sync → strokedb/sync}/chain_sync.rb +0 -0
  58. data/lib/{sync → strokedb/sync}/diff.rb +12 -1
  59. data/lib/{sync/stroke_diff → strokedb/sync/diff}/array.rb +1 -1
  60. data/lib/{sync/stroke_diff → strokedb/sync/diff}/default.rb +0 -0
  61. data/lib/{sync/stroke_diff → strokedb/sync/diff}/hash.rb +1 -1
  62. data/lib/{sync/stroke_diff → strokedb/sync/diff}/string.rb +1 -1
  63. data/lib/{sync → strokedb/sync}/lamport_timestamp.rb +0 -0
  64. data/lib/{sync → strokedb/sync}/store_sync.rb +15 -7
  65. data/lib/strokedb/transaction.rb +78 -0
  66. data/lib/{util → strokedb}/util.rb +14 -7
  67. data/lib/strokedb/util/attach_dsl.rb +29 -0
  68. data/lib/{util → strokedb/util}/blankslate.rb +0 -0
  69. data/lib/strokedb/util/class_optimization.rb +93 -0
  70. data/lib/{util → strokedb/util}/inflect.rb +0 -0
  71. data/lib/strokedb/util/java_util.rb +13 -0
  72. data/lib/{util → strokedb/util}/lazy_array.rb +0 -0
  73. data/lib/{util → strokedb/util}/lazy_mapping_array.rb +4 -0
  74. data/lib/{util → strokedb/util}/lazy_mapping_hash.rb +0 -0
  75. data/lib/{util → strokedb/util}/serialization.rb +21 -0
  76. data/lib/strokedb/util/uuid.rb +159 -0
  77. data/lib/{util → strokedb/util}/xml.rb +0 -0
  78. data/lib/{view → strokedb}/view.rb +2 -2
  79. data/lib/strokedb/volumes.rb +5 -0
  80. data/lib/strokedb/volumes/archive_volume.rb +165 -0
  81. data/lib/strokedb/volumes/block_volume.rb +169 -0
  82. data/lib/strokedb/volumes/distributed_pointer.rb +43 -0
  83. data/lib/strokedb/volumes/fixed_length_skiplist_volume.rb +109 -0
  84. data/lib/strokedb/volumes/map_volume.rb +268 -0
  85. data/meta/MANIFEST +175 -0
  86. data/script/console +2 -70
  87. data/spec/integration/remote_store_spec.rb +70 -0
  88. data/spec/integration/search_spec.rb +76 -0
  89. data/spec/integration/spec_helper.rb +1 -0
  90. data/spec/lib/spec_helper.rb +1 -0
  91. data/spec/lib/strokedb/config_spec.rb +250 -0
  92. data/spec/lib/strokedb/core_ext/blank_spec.rb +20 -0
  93. data/spec/lib/strokedb/core_ext/extract_spec.rb +42 -0
  94. data/spec/lib/strokedb/core_ext/float_spec.rb +62 -0
  95. data/spec/lib/strokedb/core_ext/infinity_spec.rb +40 -0
  96. data/spec/lib/strokedb/core_ext/spec_helper.rb +1 -0
  97. data/spec/lib/strokedb/core_ext/string_spec.rb +25 -0
  98. data/spec/lib/strokedb/core_ext/symbol_spec.rb +8 -0
  99. data/spec/lib/strokedb/data_structures/chunked_skiplist_spec.rb +144 -0
  100. data/spec/lib/strokedb/data_structures/inverted_list_spec.rb +172 -0
  101. data/spec/lib/strokedb/data_structures/simple_skiplist_spec.rb +200 -0
  102. data/spec/lib/strokedb/data_structures/skiplist_spec.rb +253 -0
  103. data/spec/lib/strokedb/data_structures/spec_helper.rb +1 -0
  104. data/spec/lib/strokedb/document/associations_spec.rb +319 -0
  105. data/spec/lib/strokedb/document/callbacks_spec.rb +134 -0
  106. data/spec/lib/strokedb/document/coercions_spec.rb +110 -0
  107. data/spec/lib/strokedb/document/document_spec.rb +1063 -0
  108. data/spec/lib/strokedb/document/meta_meta_spec.rb +30 -0
  109. data/spec/lib/strokedb/document/meta_spec.rb +435 -0
  110. data/spec/lib/strokedb/document/metaslot_spec.rb +43 -0
  111. data/spec/lib/strokedb/document/slot_spec.rb +130 -0
  112. data/spec/lib/strokedb/document/spec_helper.rb +1 -0
  113. data/spec/lib/strokedb/document/validations_spec.rb +1081 -0
  114. data/spec/lib/strokedb/document/virtualize_spec.rb +80 -0
  115. data/spec/lib/strokedb/nsurl_spec.rb +73 -0
  116. data/spec/lib/strokedb/spec_helper.rb +1 -0
  117. data/spec/lib/strokedb/stores/chained_storages_spec.rb +116 -0
  118. data/spec/lib/strokedb/stores/spec_helper.rb +1 -0
  119. data/spec/lib/strokedb/stores/store_spec.rb +201 -0
  120. data/spec/lib/strokedb/stores/transaction_spec.rb +107 -0
  121. data/spec/lib/strokedb/sync/chain_sync_spec.rb +43 -0
  122. data/spec/lib/strokedb/sync/diff_spec.rb +111 -0
  123. data/spec/lib/strokedb/sync/lamport_timestamp_spec.rb +174 -0
  124. data/spec/lib/strokedb/sync/slot_diff_spec.rb +164 -0
  125. data/spec/lib/strokedb/sync/spec_helper.rb +1 -0
  126. data/spec/lib/strokedb/sync/store_sync_spec.rb +181 -0
  127. data/spec/lib/strokedb/sync/stroke_diff/array_spec.rb +97 -0
  128. data/spec/lib/strokedb/sync/stroke_diff/complex_spec.rb +58 -0
  129. data/spec/lib/strokedb/sync/stroke_diff/hash_spec.rb +144 -0
  130. data/spec/lib/strokedb/sync/stroke_diff/scalar_spec.rb +23 -0
  131. data/spec/lib/strokedb/sync/stroke_diff/spec_helper.rb +25 -0
  132. data/spec/lib/strokedb/sync/stroke_diff/string_spec.rb +61 -0
  133. data/spec/lib/strokedb/util/attach_dsl_spec.rb +45 -0
  134. data/spec/lib/strokedb/util/inflect_spec.rb +14 -0
  135. data/spec/lib/strokedb/util/lazy_array_spec.rb +157 -0
  136. data/spec/lib/strokedb/util/lazy_mapping_array_spec.rb +174 -0
  137. data/spec/lib/strokedb/util/lazy_mapping_hash_spec.rb +92 -0
  138. data/spec/lib/strokedb/util/spec_helper.rb +1 -0
  139. data/spec/lib/strokedb/util/uuid_spec.rb +46 -0
  140. data/spec/lib/strokedb/view_spec.rb +228 -0
  141. data/spec/lib/strokedb/volumes/archive_volume_spec.rb +105 -0
  142. data/spec/lib/strokedb/volumes/block_volume_spec.rb +100 -0
  143. data/spec/lib/strokedb/volumes/distributed_pointer_spec.rb +14 -0
  144. data/spec/lib/strokedb/volumes/fixed_length_skiplist_volume_spec.rb +177 -0
  145. data/spec/lib/strokedb/volumes/map_volume_spec.rb +172 -0
  146. data/spec/lib/strokedb/volumes/spec_helper.rb +1 -0
  147. data/spec/regression/docref_spec.rb +94 -0
  148. data/spec/regression/meta_spec.rb +23 -0
  149. data/spec/regression/spec_helper.rb +1 -0
  150. data/spec/regression/sync_spec.rb +36 -0
  151. data/spec/spec.opts +7 -0
  152. data/spec/spec_helper.rb +37 -0
  153. data/spec/temp/storages/TIMESTAMP +1 -0
  154. data/spec/temp/storages/UUID +1 -0
  155. data/spec/temp/storages/database-sync/TIMESTAMP +1 -0
  156. data/spec/temp/storages/database-sync/UUID +1 -0
  157. data/spec/temp/storages/database-sync/config +1 -0
  158. data/spec/temp/storages/database-sync/file/LAST +1 -0
  159. data/spec/temp/storages/database-sync/file/bd/f6/bdf675e5-8a7b-494e-97f2-f74a14ccd95d.av +0 -0
  160. data/spec/temp/storages/database-sync/file/uindex.wal +0 -0
  161. data/spec/temp/storages/database-sync/inverted_list_file/INVERTED_INDEX +1 -0
  162. data/spec/temp/storages/inverted_list_storage/INVERTED_INDEX +0 -0
  163. data/strokedb.gemspec +120 -0
  164. data/task/benchmark.task +9 -0
  165. data/task/ditz.task +30 -0
  166. data/task/echoe.rb +17 -0
  167. data/task/rcov.task +50 -0
  168. data/task/rdoc.task +10 -0
  169. data/task/rspec.task +0 -0
  170. data/vendor/java_inline.rb +106 -0
  171. data/vendor/rbmodexcl/mrimodexcl.rb +82 -0
  172. data/vendor/rbmodexcl/rbmodexcl.rb +5 -0
  173. data/vendor/rbmodexcl/rbxmodexcl.rb +48 -0
  174. data/vendor/rbmodexcl/spec/unextend_spec.rb +50 -0
  175. data/vendor/rbmodexcl/spec/uninclude_spec.rb +26 -0
  176. metadata +271 -79
  177. data/CONTRIBUTORS +0 -7
  178. data/CREDITS +0 -13
  179. data/bin/sdbc +0 -2
  180. data/lib/init.rb +0 -57
  181. data/lib/stores/inverted_list_index/inverted_list_index.rb +0 -49
  182. data/lib/stores/skiplist_store/chunk.rb +0 -119
  183. data/lib/stores/skiplist_store/chunk_storage.rb +0 -21
  184. data/lib/stores/skiplist_store/file_chunk_storage.rb +0 -44
  185. data/lib/stores/skiplist_store/memory_chunk_storage.rb +0 -37
  186. data/lib/stores/skiplist_store/skiplist_store.rb +0 -217
  187. data/lib/stores/store.rb +0 -5
  188. data/lib/sync/stroke_diff/stroke_diff.rb +0 -9
  189. data/lib/util/ext/object.rb +0 -8
  190. data/lib/util/java_util.rb +0 -9
  191. data/lib/util/trigger_partition.rb +0 -136
  192. data/strokedb.rb +0 -75
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Document" do
4
+
5
+ before(:each) do
6
+ setup_default_store
7
+ Object.send!(:remove_const,'SomeMeta') if defined?(SomeMeta)
8
+ SomeMeta = Meta.new
9
+ @document = Document.new
10
+ end
11
+
12
+ it "should assign a uuid-named slot for metaslot" do
13
+ @document[Meta] = SomeMeta
14
+ @document.slotnames.should include(Meta.document.uuid)
15
+ end
16
+
17
+ it "should assign a uuid-named slot for metaslot" do
18
+ @document[SomeMeta] = "some value"
19
+ @document.slotnames.should include(SomeMeta.document.uuid)
20
+ end
21
+
22
+ it "should be able to read metaslot" do
23
+ @document[SomeMeta] = "some value"
24
+ @document[SomeMeta].should == "some value"
25
+ end
26
+
27
+ describe "with metaslot assigned, once saved and reloaded" do
28
+
29
+ before(:each) do
30
+ @document[SomeMeta] = "some value"
31
+ @document.save!
32
+ @document = @document.reload
33
+ end
34
+
35
+ it "should be able to read metaslot" do
36
+ @document[SomeMeta].should == "some value"
37
+ end
38
+
39
+ end
40
+
41
+
42
+
43
+ end
@@ -0,0 +1,130 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Slot" do
4
+
5
+ before(:each) do
6
+ @store = setup_default_store
7
+ @document = Document.new
8
+ @slot = Slot.new(@document)
9
+ end
10
+
11
+ it "should store string value" do
12
+ @slot.value = "some value"
13
+ @slot.value.should == "some value"
14
+ end
15
+
16
+ it "should store numeric value" do
17
+ @slot.value = 1
18
+ @slot.value.should == 1
19
+ @slot.value = 1.1
20
+ @slot.value.should == 1.1
21
+ end
22
+
23
+ it "should store Array value" do
24
+ @slot.value = [1,2,3]
25
+ @slot.value.should == [1,2,3]
26
+ end
27
+
28
+ it "should store Hash value" do
29
+ @slot.value = { "1" => "2"}
30
+ @slot.value.should == { "1" => "2"}
31
+ end
32
+
33
+ it "should store boolean value" do
34
+ @slot.value = true
35
+ @slot.value.should == true
36
+ end
37
+
38
+ it "should store nil value" do
39
+ @slot.value = nil
40
+ @slot.value.should be_nil
41
+ end
42
+
43
+ it "should store symbol value and convert it to string" do
44
+ @slot.value = :a
45
+ @slot.value.should == 'a'
46
+ end
47
+
48
+ it "should store Range value" do
49
+ @slot.value = 1..100
50
+ @slot.value.should == (1..100)
51
+ end
52
+
53
+ it "should store Regexp value" do
54
+ @slot.value = /.+/
55
+ @slot.value.should == /.+/
56
+ end
57
+
58
+ it "should store Time object" do
59
+ t = Time.now
60
+ @slot.value = t
61
+ @slot.value.should be_close(t, 0.000002)
62
+ @slot.to_raw.should match(XMLSCHEMA_TIME_RE)
63
+ end
64
+
65
+ it "should store VersionedDocument reference if value is a saved Document" do
66
+ some_doc = Document.create!(:something => 1)
67
+ some_doc.save!
68
+ @slot.value = some_doc
69
+ @slot.value.should == some_doc
70
+ @slot.to_raw.should match(/@##{UUID_RE}.#{VERSION_RE}/)
71
+ end
72
+
73
+ it "should store VersionedDocument reference if value is a VersionedDocument" do
74
+ some_doc = Document.new(:something => 1)
75
+ some_doc.extend(VersionedDocument)
76
+ some_doc.save!
77
+ @slot.value = some_doc
78
+ @slot.value.should == some_doc
79
+ @slot.to_raw.should match(/@##{UUID_RE}.#{VERSION_RE}/)
80
+ end
81
+
82
+ it "should store Document reference if value is a saved Document" do
83
+ some_doc = Document.new
84
+ @slot.value = some_doc
85
+ @slot.value.should == some_doc
86
+ @slot.to_raw.should match(/@##{UUID_RE}/)
87
+ end
88
+
89
+ it "should raise ArgumentError for non-supported types" do
90
+ lambda { @slot.value = Object.new }.should raise_error(ArgumentError)
91
+ end
92
+
93
+ it "should preserve Ruby object for a complex value" do
94
+ @slot.value = ["some value"]
95
+ @slot.value.object_id.should == @slot.value.object_id
96
+ end
97
+
98
+ end
99
+
100
+ describe "Slot that directly references document" do
101
+
102
+ before(:each) do
103
+ setup_default_store
104
+ @another_doc = Document.create! :some_data => "1"
105
+ @doc = Document.create! :another_doc => @another_doc
106
+ end
107
+
108
+ it "should load the same Ruby object each time" do
109
+ doc = @doc.reload
110
+ doc_obj = doc.another_doc
111
+ doc.another_doc.object_id.should == doc_obj.object_id
112
+ end
113
+
114
+ end
115
+
116
+ describe "Slot that indirectly references document" do
117
+
118
+ before(:each) do
119
+ setup_default_store
120
+ @another_doc = Document.create! :some_data => "1"
121
+ @doc = Document.create! :another_docs => [@another_doc]
122
+ end
123
+
124
+ it "should load the same Ruby object each time" do
125
+ doc = @doc.reload
126
+ doc_obj = doc.another_docs.first
127
+ doc.another_docs.first.object_id.should == doc_obj.object_id
128
+ end
129
+
130
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
@@ -0,0 +1,1081 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ def validations_setup
4
+ setup_default_store
5
+ setup_index
6
+ Object.send!(:remove_const, 'Foo') if defined?(Foo)
7
+ Object.send!(:remove_const, 'Bar') if defined?(Bar)
8
+ Object.send!(:remove_const, 'User') if defined?(User)
9
+ Object.send!(:remove_const, 'Email') if defined?(Email)
10
+ Object.send!(:remove_const, 'Item') if defined?(Item)
11
+ Object.send!(:remove_const, 'OneMoreItem') if defined?(OneMoreItem)
12
+ end
13
+
14
+ def erroneous_stuff
15
+ Meta.new(:name => "ErroneousStuff") do
16
+ on_validation do |doc|
17
+ doc.errors.add(:something, "123")
18
+ doc.errors.add(:other, "456")
19
+ end
20
+ end.new
21
+ end
22
+
23
+ describe "Document validation" do
24
+ before :each do
25
+ validations_setup
26
+ end
27
+
28
+ it "should treat an empty document as valid" do
29
+ Foo = Meta.new
30
+ s = Foo.new
31
+
32
+ s.should be_valid
33
+ s.errors.should be_empty
34
+ end
35
+
36
+ it "should not treat a document with errors as valid" do
37
+ s = erroneous_stuff
38
+
39
+ s.should_not be_valid
40
+ s.errors.messages.sort.should == %w(123 456)
41
+ end
42
+
43
+ it "should raise InvalidDocumentError on a save! call" do
44
+ lambda { erroneous_stuff.save! }.should raise_error(InvalidDocumentError)
45
+ end
46
+
47
+ it "should not raise InvalidDocumentError on a save!(false) call" do
48
+ lambda { erroneous_stuff.save!(false) }.should_not raise_error(InvalidDocumentError)
49
+ end
50
+ end
51
+
52
+ describe "Document with errors" do
53
+ before :each do
54
+ validations_setup
55
+ @erroneous_doc = erroneous_stuff
56
+ end
57
+
58
+ it "should return the number of errors" do
59
+ @erroneous_doc.should_not be_valid
60
+ @erroneous_doc.errors.size.should == 2
61
+ end
62
+
63
+ it "should yield each attribute and associated message per error added" do
64
+ @erroneous_doc.should_not be_valid
65
+ @erroneous_doc.errors.each { }.should == {"something"=>["123"], "other"=>["456"]}
66
+ end
67
+
68
+ end
69
+
70
+ describe "validates_presence_of" do
71
+ before :each do
72
+ validations_setup
73
+ end
74
+
75
+ it "should tell valid if slot is there" do
76
+ Foo = Meta.new { validates_presence_of :name, :on => :save }
77
+ s = Foo.new({:name => "Rick Roll"})
78
+ s.should be_valid
79
+ end
80
+
81
+ it "should tell invalid if slot is absent" do
82
+ Foo = Meta.new { validates_presence_of :name, :on => :save }
83
+ s = Foo.new
84
+
85
+ s.should_not be_valid
86
+ s.errors.messages.should == [ "Foo's name should be present on save" ]
87
+ end
88
+ end
89
+
90
+ describe "Validation helpers" do
91
+ before(:each) { validations_setup }
92
+
93
+ it "should respect :on => :create" do
94
+ Foo = Meta.new { validates_presence_of :name, :on => :create }
95
+ s1 = Foo.new
96
+ bang { s1.save! }
97
+
98
+ s2 = Foo.new(:name => "Rick Roll")
99
+ no_bang { s2.save! }
100
+ s2.remove_slot!(:name)
101
+ no_bang { s2.save! }
102
+ end
103
+
104
+ it "should respect :on => :update" do
105
+ Foo = Meta.new { validates_presence_of :name, :on => :update }
106
+
107
+ s = Foo.new
108
+ no_bang { s.save! }
109
+ bang { s.save! }
110
+ s[:name] = "Rick Roll"
111
+ no_bang { s.save! }
112
+ end
113
+
114
+ it "should respect :on => :save" do
115
+ Foo = Meta.new { validates_presence_of :name, :on => :save }
116
+ s1 = Foo.new
117
+ bang { s1.save! }
118
+
119
+ s2 = Foo.new(:name => "Rick Roll")
120
+ no_bang { s2.save! }
121
+ s2.remove_slot!(:name)
122
+ bang { s2.save! }
123
+ end
124
+
125
+ it "should respect :message" do
126
+ Foo = Meta.new do
127
+ validates_presence_of :name, :on => :save, :message => 'On #{on} Meta #{meta} SlotName #{slotname}'
128
+ end
129
+
130
+ s = Foo.new
131
+ s.valid?.should be_false
132
+ s.errors.messages.should == [ "On save Meta Foo SlotName name" ]
133
+ end
134
+
135
+ it "should allow to use document slot for :if and :unless evaluation" do
136
+ Foo = Meta.new do validates_presence_of :name, :on => :save, :if => :slot end
137
+ bang { Foo.create!(:slot => true) }
138
+ no_bang { Foo.create!(:slot => false) }
139
+
140
+ Bar = Meta.new do validates_presence_of :name, :on => :save, :unless => :slot end
141
+ no_bang { Bar.create!(:slot => true) }
142
+ bang { Bar.create!(:slot => false) }
143
+ end
144
+
145
+ it "should raise an ArgumentError when given something not callable for :if and :unless" do
146
+ lambda do
147
+ Meta.new { validates_presence_of :name, :on => :save, :if => 123 }
148
+ end.should raise_error(ArgumentError)
149
+
150
+ lambda do
151
+ Meta.new { validates_presence_of :name, :on => :save, :if => lambda { } }
152
+ end.should raise_error(ArgumentError)
153
+
154
+ lambda do
155
+ Meta.new { validates_presence_of :name, :on => :save, :unless => 123 }
156
+ end.should raise_error(ArgumentError)
157
+
158
+ lambda do
159
+ Meta.new { validates_presence_of :name, :on => :save, :unless => lambda { } }
160
+ end.should raise_error(ArgumentError)
161
+
162
+ end
163
+
164
+ def bang
165
+ lambda { yield }.should raise_error(InvalidDocumentError)
166
+ end
167
+
168
+ def no_bang
169
+ lambda { yield }.should_not raise_error(InvalidDocumentError)
170
+ end
171
+ end
172
+
173
+ describe "validates_type_of" do
174
+ before :each do
175
+ validations_setup
176
+
177
+ Email = Meta.new
178
+ User = Meta.new { validates_type_of :email, :as => :email }
179
+ end
180
+
181
+ it "should actually check the type" do
182
+ e = Email.create!
183
+ User.new(:email => e).should be_valid
184
+ end
185
+
186
+ it "should treat other types as invalid" do
187
+ OmgEmail = Meta.new
188
+ e = OmgEmail.create!
189
+
190
+ User.new(:email => e).should_not be_valid
191
+ User.new(:email => "name@server.com").should_not be_valid
192
+ u = User.new(:email => nil)
193
+ u.should_not be_valid
194
+ u.errors.messages.should == [ "User's email should be of type Email" ]
195
+ end
196
+
197
+ it "should treat absent slot as valid with :allow_nil => true" do
198
+ Foo = Meta.new { validates_type_of :email, :as => :email, :allow_nil => true }
199
+ Foo.new.should be_valid
200
+ end
201
+
202
+ end
203
+
204
+ describe "validates_format_of" do
205
+ before(:each) do
206
+ validations_setup
207
+ User = Meta.new { validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
208
+ end
209
+
210
+ it "should be valid when :email value match regexp" do
211
+ User.new(:email => 'cool@strokedb.com').should be_valid
212
+ end
213
+
214
+ it "should be invalid when :email value does not match regexp" do
215
+ User.new(:email => 'cool-strokedb.com').should_not be_valid
216
+ end
217
+
218
+ it "should raise an exception if no regex is provided" do
219
+ lambda { Meta.new { validates_format_of :email, :with => "nothing" } }.should raise_error(ArgumentError)
220
+ end
221
+ end
222
+
223
+ describe "validates_uniqueness_of" do
224
+ before :each do
225
+ validations_setup
226
+ User = Meta.new { validates_uniqueness_of :email }
227
+ end
228
+
229
+ it "should treat unique slot values as valid" do
230
+ u1 = User.create!(:email => "name@server.com")
231
+ u2 = User.new(:email => "othername@otherserver.com")
232
+ u2.should be_valid
233
+ end
234
+
235
+ it "should treat duplicate slot values as invalid" do
236
+ u1 = User.create!(:email => "name@server.com")
237
+ u2 = User.new(:email => "name@server.com")
238
+ u2.should_not be_valid
239
+ u2.errors.messages.should == [ "A document with a email of name@server.com already exists" ]
240
+ end
241
+
242
+ it "should respect slot name" do
243
+ u1 = User.create!(:email => "name@server.com")
244
+ u2 = User.new(:otherfield => "name@server.com")
245
+ u2.should be_valid
246
+ end
247
+
248
+ describe "document with multiple metas" do
249
+ before :each do
250
+ validations_setup
251
+ Object.send!(:remove_const,'User') if defined?(User)
252
+ Object.send!(:remove_const,'Admin') if defined?(Admin)
253
+
254
+ User = Meta.new { validates_uniqueness_of :email }
255
+ Admin = Meta.new
256
+ end
257
+
258
+ it "should be valid" do
259
+ u = (User+Admin).create!(:email => 'foo@bar.org')
260
+ u.should be_valid
261
+ end
262
+
263
+ it "should be valid" do
264
+ u = User.create!(:email => 'foo@bar.org')
265
+ u.should be_valid
266
+ u.metas << Admin
267
+ u.should be_valid
268
+ end
269
+
270
+ it "should not be valid" do
271
+ u = User.create!(:email => 'foo@bar.org')
272
+ u.should be_valid
273
+ a = Admin.create!(:email => 'foo@bar.org')
274
+ a.should be_valid
275
+
276
+ u.metas << Admin
277
+ a.should be_valid
278
+ u.should_not be_valid
279
+ end
280
+ end
281
+
282
+ describe "should allow to modify an existing document" do
283
+ it "test 1" do
284
+ u = User.create!(:email => "name@server.com", :status => :newbie)
285
+ u.status = :hacker
286
+ u.should be_valid
287
+ u.save!
288
+ u.status = :hax0r
289
+ u.should be_valid
290
+ u.save!
291
+ u.email = "hax0r@hax0r.com"
292
+ u.should be_valid
293
+ u.save!
294
+ u.email = "name@server.com"
295
+ u.status = :newbie_again
296
+ u.should be_valid
297
+ u.save!
298
+ u.email = "hax0r@hax0r.com"
299
+ u.should be_valid
300
+ end
301
+
302
+ it "test 2" do
303
+ Foo = Meta.new do
304
+ validates_uniqueness_of :login
305
+ virtualizes :blah
306
+ end
307
+
308
+ foo = Foo.create!(:login => "vasya")
309
+
310
+ foo.somefield = 777
311
+ foo.should be_valid
312
+ end
313
+ end
314
+
315
+ it "should respect :allow_nil set to false" do
316
+ Foo = Meta.new { validates_uniqueness_of :slot, :allow_nil => false }
317
+ Foo.create!(:slot => nil)
318
+ Foo.new.should_not be_valid
319
+ end
320
+
321
+ it "should respect :allow_nil set to true" do
322
+ Foo = Meta.new { validates_uniqueness_of :slot, :allow_nil => true }
323
+ Foo.create!(:slot => nil)
324
+ Foo.new.should be_valid
325
+ end
326
+
327
+ it "should default :allow_nil to false" do
328
+ Foo = Meta.new { validates_uniqueness_of :slot }
329
+ Foo.create!(:slot => nil)
330
+ Foo.new.should_not be_valid
331
+ end
332
+
333
+ it "should respect :allow_blank set to false" do
334
+ Foo = Meta.new { validates_uniqueness_of :slot, :allow_blank => false }
335
+ Foo.create!(:slot => "")
336
+ Foo.new.should_not be_valid
337
+ end
338
+
339
+ it "should respect :allow_blank set to true" do
340
+ Foo = Meta.new { validates_uniqueness_of :slot, :allow_blank => true }
341
+ Foo.create!(:slot => "")
342
+ Foo.new.should be_valid
343
+ end
344
+
345
+ it "should default :allow_blank to false" do
346
+ Foo = Meta.new { validates_uniqueness_of :slot }
347
+ Foo.create!(:slot => "")
348
+ Foo.new.should_not be_valid
349
+ end
350
+
351
+ it "should respect :case_sensitive"
352
+ end
353
+
354
+ describe "validates_confirmation_of" do
355
+ before :each do
356
+ validations_setup
357
+ User = Meta.new { validates_confirmation_of :password }
358
+ end
359
+
360
+ it "should be valid when confirmed" do
361
+ User.new(:password => "sekret", :password_confirmation => "sekret").should be_valid
362
+ end
363
+
364
+ it "should be valid when confirmation is not set" do
365
+ User.new(:password => "sekret").should be_valid
366
+ end
367
+
368
+ it "should not be valid when not confirmed" do
369
+ u = User.new(:password => "sekret", :password_confirmation => "invalid_guess")
370
+ u.should_not be_valid
371
+ u.errors.messages.should == [ "User's password doesn't match confirmation" ]
372
+ end
373
+
374
+ it "should not serialize confirmation slot" do
375
+ u = User.create!(:password => "sekret", :password_confirmation => "sekret")
376
+ User.find(u.uuid).has_slot?("password_confirmation").should_not be_true
377
+ end
378
+ end
379
+
380
+ describe "validates_acceptance_of" do
381
+ before :each do
382
+ validations_setup
383
+ end
384
+
385
+ it "should treat accepted value as valid" do
386
+ Meta.new(:name => 'some') { validates_acceptance_of :eula, :accept => "yep" }.new(:eula => "yep").should be_valid
387
+ end
388
+
389
+ it "should treat not accepted value as invalid" do
390
+ Meta.new(:name => 'some') { validates_acceptance_of :eula, :accept => "yep" }.new(:eula => "nope").should_not be_valid
391
+ end
392
+
393
+ it "should respect allow_nil" do
394
+ Meta.new(:name => 'some') { validates_acceptance_of :eula, :accept => "yep", :allow_nil => true }.new.should be_valid
395
+ Meta.new(:name => 'some') { validates_acceptance_of :eula, :accept => "yep", :allow_nil => false }.new.should_not be_valid
396
+ end
397
+
398
+ it "should set :allow_nil to true by default" do
399
+ Meta.new(:name => 'some') { validates_acceptance_of :eula, :accept => "yep" }.new.should be_valid
400
+ end
401
+
402
+ it "should set :accept to \"1\" by default" do
403
+ Meta.new(:name => 'some') { validates_acceptance_of :eula }.new(:eula => "1").should be_valid
404
+ end
405
+
406
+ it "should make a slot virtual" do
407
+ Foo = Meta.new{ validates_acceptance_of :eula, :accept => "yep" }
408
+ f = Foo.create!(:eula => "yep")
409
+ Foo.find(f.uuid).has_slot?("eula").should_not be_true
410
+ end
411
+ end
412
+
413
+ describe "validates_length_of" do
414
+ before :each do
415
+ validations_setup
416
+ end
417
+
418
+ describe "options handling" do
419
+ it "should raise ArgumentError when more than one range option is specified" do
420
+ arg_bang { Meta.new(:name => 'some') { validates_length_of :name, :is => 10, :maximum => 20 } }
421
+ arg_bang { Meta.new(:name => 'some') { validates_length_of :name, :is => 10, :within => 1..20 } }
422
+ end
423
+
424
+ it "should raise ArgumentError when no range option is specified" do
425
+ arg_bang { Meta.new(:name => 'some') { validates_length_of :name } }
426
+ end
427
+
428
+ it "should raise ArgumentError when not Range given to :in or :within" do
429
+ arg_bang { Meta.new(:name => 'some') { validates_length_of :name, :in => 10 } }
430
+ arg_bang { Meta.new(:name => 'some') { validates_length_of :name, :within => "somewhere between one and a million" } }
431
+ end
432
+
433
+ it "should raise ArgumentError when something other than nonnegative Integer is given to :is, :minimum, :maximum" do
434
+ %w(is minimum maximum).each do |arg|
435
+ arg_bang { Meta.new(:name => 'some') { validates_length_of :name, arg => "blah" } }
436
+ arg_bang { Meta.new(:name => 'some') { validates_length_of :name, arg => -1 } }
437
+ end
438
+ end
439
+
440
+ def arg_bang
441
+ lambda { yield }.should raise_error(ArgumentError)
442
+ end
443
+ end
444
+
445
+ %w(within in).each do |within|
446
+ describe ":#{within}" do
447
+ before :each do
448
+ Foo = Meta.new { validates_length_of :bar, within => 10..50 }
449
+ end
450
+
451
+ it "should consider valid when slot is within the range" do
452
+ Foo.new(:bar => "*"*30).should be_valid
453
+ Foo.new(:bar => [1]*30).should be_valid
454
+ end
455
+
456
+ it "should consider invalid when slot is too small" do
457
+ f = Foo.new(:bar => "12345")
458
+ f.should_not be_valid
459
+ f.errors.messages.should == [ "bar is too short (minimum is 10 characters)" ]
460
+ Foo.new(:bar => [1]*5).should_not be_valid
461
+ end
462
+
463
+ it "should consider invalid when slot is too big" do
464
+ f = Foo.new(:bar => "!"*100)
465
+ f.should_not be_valid
466
+ f.errors.messages.should == [ "bar is too long (maximum is 50 characters)" ]
467
+ Foo.new(:bar => [1]*100).should_not be_valid
468
+ end
469
+
470
+ it "should respect :too_short" do
471
+ Bar = Meta.new { validates_length_of :foo, within => 1..5, :too_short => "blah %d" }
472
+ b = Bar.new(:foo => "")
473
+ b.should_not be_valid
474
+ b.errors.messages.should == [ "blah 1" ]
475
+ end
476
+
477
+ it "should respect :too_long" do
478
+ Bar = Meta.new { validates_length_of :foo, within => 1..5, :too_long => "blah %d" }
479
+ b = Bar.new(:foo => "123456")
480
+ b.should_not be_valid
481
+ b.errors.messages.should == [ "blah 5" ]
482
+ end
483
+ end
484
+ end
485
+
486
+ describe ":is" do
487
+ before :each do
488
+ Foo = Meta.new { validates_length_of :bar, :is => 4 }
489
+ end
490
+
491
+ it "should consider valid when slot value has the right length" do
492
+ Foo.new(:bar => "1234").should be_valid
493
+ Foo.new(:bar => %w(ein zwei drei Polizei)).should be_valid
494
+ end
495
+
496
+ it "should consider invalid when slot value has invalid length" do
497
+ f = Foo.new(:bar => "12345")
498
+ f.should_not be_valid
499
+ f.errors.messages.should == [ "bar has the wrong length (should be 4 characters)" ]
500
+
501
+ Foo.new(:bar => %w(ein zwei alles)).should_not be_valid
502
+ end
503
+
504
+ it "should respect :message" do
505
+ Bar = Meta.new { validates_length_of :foo, :is => 66, :message => "fkup %d" }
506
+ b = Bar.new(:foo => "123456")
507
+ b.should_not be_valid
508
+ b.errors.messages.should == [ "fkup 66" ]
509
+ end
510
+ end
511
+
512
+ describe ":minimum" do
513
+ before :each do
514
+ Foo = Meta.new { validates_length_of :bar, :minimum => 4 }
515
+ end
516
+
517
+ it "should consider valid when slot value has the right length" do
518
+ Foo.new(:bar => "1234").should be_valid
519
+ Foo.new(:bar => "12345").should be_valid
520
+ Foo.new(:bar => %w(ein zwei drei vier Polizei)).should be_valid
521
+ end
522
+
523
+ it "should consider invalid when slot value has invalid length" do
524
+ f = Foo.new(:bar => "125")
525
+ f.should_not be_valid
526
+ f.errors.messages.should == [ "bar is too short (minimum is 4 characters)" ]
527
+
528
+ Foo.new(:bar => %w(ein zwei alles)).should_not be_valid
529
+ end
530
+
531
+ it "should respect :message" do
532
+ Bar = Meta.new { validates_length_of :foo, :minimum => 66, :message => "fkup %d" }
533
+ b = Bar.new(:foo => "123456")
534
+ b.should_not be_valid
535
+ b.errors.messages.should == [ "fkup 66" ]
536
+ end
537
+ end
538
+
539
+ describe :maximum do
540
+ before :each do
541
+ Foo = Meta.new { validates_length_of :bar, :maximum => 4 }
542
+ end
543
+
544
+ it "should consider valid when slot value has the right length" do
545
+ Foo.new(:bar => "1234").should be_valid
546
+ Foo.new(:bar => "123").should be_valid
547
+ Foo.new(:bar => %w(ein zwei drei)).should be_valid
548
+ end
549
+
550
+ it "should consider invalid when slot value has invalid length" do
551
+ f = Foo.new(:bar => "123456")
552
+ f.should_not be_valid
553
+ f.errors.messages.should == [ "bar is too long (maximum is 4 characters)" ]
554
+
555
+ Foo.new(:bar => %w(ein zwei drei vier Polizei)).should_not be_valid
556
+ end
557
+
558
+ it "should respect :message" do
559
+ Bar = Meta.new { validates_length_of :foo, :maximum => 66, :message => "fkup %d" }
560
+ b = Bar.new(:foo => "6"*67)
561
+ b.should_not be_valid
562
+ b.errors.messages.should == [ "fkup 66" ]
563
+ end
564
+ end
565
+
566
+ it "should respect :allow_nil" do
567
+ Meta.new(:name => 'some') { validates_length_of :bar, :is => 10, :allow_nil => false }.new.should_not be_valid
568
+ Meta.new(:name => 'some') { validates_length_of :bar, :is => 10, :allow_nil => true }.new.should be_valid
569
+ end
570
+
571
+ it "should respect :allow_blank" do
572
+ Foo = Meta.new { validates_length_of :bar, :is => 10, :allow_blank => false }
573
+ Foo.new.should_not be_valid
574
+ Foo.new(:bar => " ").should_not be_valid
575
+
576
+ Bar = Meta.new { validates_length_of :bar, :is => 10, :allow_blank => true }
577
+ Bar.new.should be_valid
578
+ Bar.new(:bar => " ").should be_valid
579
+ end
580
+ end
581
+
582
+ describe "validates_inclusion_of" do
583
+ before :each do
584
+ validations_setup
585
+ Item = Meta.new do
586
+ validates_inclusion_of :gender, :in => %w( m f )
587
+ validates_inclusion_of :age, :in => 0..99
588
+ end
589
+ end
590
+
591
+ it "should raise ArgumentError unless option :in is suplied" do
592
+ lambda do
593
+ Meta.new { validates_inclusion_of :format }
594
+ end.should raise_error(ArgumentError)
595
+ end
596
+
597
+ it "should raise ArgumentError unless param is an enumerable" do
598
+ lambda do
599
+ Meta.new { validates_inclusion_of :format, :in => 42 }
600
+ end.should raise_error(ArgumentError)
601
+ end
602
+
603
+ it "should be valid" do
604
+ i = Item.new(:gender => 'm', :age => 42)
605
+ i.should be_valid
606
+ i.errors.messages.should be_empty
607
+ end
608
+
609
+ it "should not be valid" do
610
+ i = Item.new(:gender => 'x', :age => 'x')
611
+ i.should_not be_valid
612
+ i.errors.messages.should == [ "Value of gender is not included in the list", "Value of age is not included in the list" ]
613
+ end
614
+
615
+ it "should be invalid without gender and age set" do
616
+ Item.new.should_not be_valid
617
+ end
618
+ end
619
+
620
+ describe "validates_exclusion_of" do
621
+ before :each do
622
+ validations_setup
623
+ Item = Meta.new do
624
+ validates_exclusion_of :gender, :in => %w( m f )
625
+ validates_exclusion_of :age, :in => 30..70
626
+ end
627
+ end
628
+
629
+ it "should raise ArgumentError unless option :in is suplied" do
630
+ lambda do
631
+ Meta.new { validates_exclusion_of :gender }
632
+ end.should raise_error(ArgumentError)
633
+ end
634
+
635
+ it "should raise ArgumentError unless param is an enumerable" do
636
+ lambda do
637
+ Meta.new { validates_inclusion_of :gender, :in => 42 }
638
+ end.should raise_error(ArgumentError)
639
+ end
640
+
641
+ it "should be valid" do
642
+ i = Item.new(:gender => 'x', :age => 25)
643
+ i.should be_valid
644
+ end
645
+
646
+ it "should not be valid" do
647
+ i = Item.new(:gender => 'm', :age => 42)
648
+ i.should_not be_valid
649
+ i.errors.messages.should == [ "Value of gender is reserved", "Value of age is reserved" ]
650
+ end
651
+ end
652
+
653
+ describe "validates_associated" do
654
+ before :each do
655
+ validations_setup
656
+
657
+ Foo = Meta.new { has_many :bars; validates_associated :bars }
658
+ Bar = Meta.new { has_many :items; validates_associated :items }
659
+ Item = Meta.new { validates_associated :associate }
660
+ OneMoreItem = Meta.new { validates_associated :associate; validates_presence_of :something }
661
+ User = Meta.new
662
+ end
663
+
664
+ it "should consider not existing association as valid" do
665
+ Foo.new.should be_valid
666
+ end
667
+
668
+ it "should consider valid when associated document is also valid" do
669
+ perfectly_valid = User.new
670
+ Item.new(:associate => perfectly_valid).should be_valid
671
+ end
672
+
673
+ it "should consider invalid when associated document is invalid" do
674
+ invalid = erroneous_stuff
675
+ invalid.should_not be_valid
676
+ item = Item.new(:associate => invalid)
677
+ item.should_not be_valid
678
+ item.errors.messages.should == [ "associate is invalid" ]
679
+ end
680
+
681
+ it "should work with has_many association" do
682
+ f = Foo.new
683
+ f.bars << Bar.new
684
+ f.should be_valid
685
+ f.bars << Bar.new
686
+ f.should be_valid
687
+
688
+ err = erroneous_stuff
689
+
690
+ lambda { f.bars << err }.should raise_error(InvalidDocumentError)
691
+ f.should be_valid
692
+ end
693
+
694
+ it "should work with a document chain" do
695
+ i3 = erroneous_stuff
696
+ i3.should_not be_valid
697
+
698
+ i2 = Item.new
699
+ i2.should be_valid
700
+ i2.associate = i3
701
+ i2.should_not be_valid
702
+
703
+ i1 = Item.new
704
+ i1.should be_valid
705
+ i1.associate = i2
706
+ i1.should_not be_valid
707
+
708
+ i1.associate = i3
709
+ i1.should_not be_valid
710
+ i1.associate = nil
711
+ i1.should be_valid
712
+ end
713
+
714
+ it "should catch direct circular referenced validations" do
715
+ i = Item.new
716
+ i1 = Item.new(:associate => i)
717
+ i.associate = i1
718
+ lambda { i.valid? }.should_not raise_error(SystemStackError)
719
+ i.should be_valid
720
+ oi = OneMoreItem.new
721
+ oi1 = OneMoreItem.new(:associate => oi)
722
+ oi.associate = oi1
723
+ oi.should_not be_valid
724
+ oi1.should_not be_valid
725
+ end
726
+
727
+ it "should catch circular referenced validations through has_many association" do
728
+ b = Bar.new
729
+ b.items << (i = Item.new(:associate => b))
730
+ lambda { b.valid? }.should_not raise_error(SystemStackError)
731
+ lambda { i.valid? }.should_not raise_error(SystemStackError)
732
+ end
733
+
734
+ end
735
+
736
+ describe "validates_numericality_of" do
737
+ before :each do
738
+ validations_setup
739
+ end
740
+
741
+ describe "general behaviour" do
742
+ before :each do
743
+ Item = Meta.new do
744
+ validates_numericality_of :price
745
+ end
746
+ end
747
+
748
+ it "should raise error on String value" do
749
+ i = Item.new(:price => "A")
750
+ i.should_not be_valid
751
+ i.errors.messages.should == [ "price is not a number" ]
752
+ end
753
+
754
+ it "should treat integer as valid" do
755
+ i = Item.new(:price => 1)
756
+ i.should be_valid
757
+ end
758
+
759
+ it "should treat negative integer as valid" do
760
+ i = Item.new(:price => -1)
761
+ i.should be_valid
762
+ end
763
+
764
+ it "should treat float as valid" do
765
+ i = Item.new(:price => 2.5)
766
+ i.should be_valid
767
+ end
768
+
769
+ it "should treat negative float as valid" do
770
+ i = Item.new(:price => -2.5)
771
+ i.should be_valid
772
+ end
773
+
774
+ it "should treat float in exponential notation as valid" do
775
+ i = Item.new(:price => "1.23456E-3")
776
+ i.should be_valid
777
+ end
778
+ end
779
+
780
+ describe "should respect :only_integer" do
781
+ before :each do
782
+ Item = Meta.new do
783
+ validates_numericality_of :price, :only_integer => true
784
+ end
785
+ end
786
+
787
+ it "should treat integer as valid when :only_integer is specified" do
788
+ Item.new(:price => 123).should be_valid
789
+ end
790
+
791
+ it "should treat integer in string as valid when :only_integer is specified" do
792
+ Item.new(:price => "123").should be_valid
793
+ end
794
+
795
+ it "should treat string as invalid when :only_integer is specified" do
796
+ i = Item.new(:price => "ququ")
797
+ i.should_not be_valid
798
+ i.errors.messages.should == [ "price must be integer" ]
799
+ end
800
+
801
+ it "should treat float as invalid when :only_integer is specified" do
802
+ i = Item.new(:price => 1.5)
803
+ i.should_not be_valid
804
+ i.errors.messages.should == [ "price must be integer" ]
805
+ end
806
+ end
807
+
808
+ describe "should respect :greater_than" do
809
+ before :each do
810
+ Item = Meta.new do
811
+ validates_numericality_of :number, :greater_than => 42
812
+ end
813
+ end
814
+
815
+ it "should raise ArgumentError if value is not numeric" do
816
+ lambda do
817
+ Meta.new { validates_numericality_of :number, :greater_than => "chicken" }
818
+ end.should raise_error(ArgumentError)
819
+ end
820
+
821
+ it "should be valid if value > 42" do
822
+ i = Item.new(:number => 44)
823
+ i.should be_valid
824
+ i.errors.messages.should be_empty
825
+ i.number = 44.3
826
+ i.should be_valid
827
+ i.errors.messages.should be_empty
828
+ end
829
+
830
+ it "should not be valid if value < 42" do
831
+ i = Item.new(:number => 41)
832
+ i.should_not be_valid
833
+ i.errors.messages.should == [ "number must be greater than 42" ]
834
+ end
835
+
836
+ it "should not be valid if value == 42" do
837
+ i = Item.new(:number => 42)
838
+ i.should_not be_valid
839
+ i.errors.messages.should == [ "number must be greater than 42" ]
840
+ end
841
+ end
842
+
843
+ describe "should respect :greater_than_or_equal_to" do
844
+ before :each do
845
+ Item = Meta.new do
846
+ validates_numericality_of :number, :greater_than_or_equal_to => 42
847
+ end
848
+ end
849
+
850
+ it "should raise ArgumentError if value is not numeric" do
851
+ lambda do
852
+ Meta.new { validates_numericality_of :number, :greater_than => "chicken" }
853
+ end.should raise_error(ArgumentError)
854
+ end
855
+
856
+ it "should be valid if value > 42" do
857
+ i = Item.new(:number => 44)
858
+ i.should be_valid
859
+ i.number = 44.3
860
+ i.should be_valid
861
+ end
862
+
863
+ it "should be valid if value == 42" do
864
+ i = Item.new(:number => 42)
865
+ i.should be_valid
866
+ end
867
+
868
+ it "should not be valid if value < 42" do
869
+ i = Item.new(:number => 41)
870
+ i.should_not be_valid
871
+ i.errors.messages.should == [ "number must be greater than or equal to 42" ]
872
+ end
873
+ end
874
+
875
+ describe "should respect :equal_to" do
876
+ before :each do
877
+ Item = Meta.new do
878
+ validates_numericality_of :number, :equal_to => 42
879
+ end
880
+ end
881
+
882
+ it "should raise ArgumentError if value is not numeric" do
883
+ lambda do
884
+ Meta.new { validates_numericality_of :number, :greater_than => "chicken" }
885
+ end.should raise_error(ArgumentError)
886
+ end
887
+
888
+ it "should be valid if value == 42" do
889
+ i = Item.new(:number => 42)
890
+ i.should be_valid
891
+ i.errors.messages.should be_empty
892
+ end
893
+
894
+ it "should not be valid if value > 42" do
895
+ i = Item.new(:number => 44)
896
+ i.should_not be_valid
897
+ i.errors.messages.should == [ "number must be equal to 42" ]
898
+ end
899
+
900
+ it "should not be valid if value < 42" do
901
+ i = Item.new(:number => 41)
902
+ i.should_not be_valid
903
+ i.errors.messages.should == [ "number must be equal to 42" ]
904
+ end
905
+ end
906
+
907
+ describe "should respect :less_than" do
908
+ before :each do
909
+ Item = Meta.new do
910
+ validates_numericality_of :number, :less_than => 42
911
+ end
912
+ end
913
+
914
+ it "should raise ArgumentError if value is not numeric" do
915
+ lambda do
916
+ Meta.new { validates_numericality_of :number, :less_than => "chicken" }
917
+ end.should raise_error(ArgumentError)
918
+ end
919
+
920
+ it "should be valid if value < 42" do
921
+ i = Item.new(:number => 41)
922
+ i.should be_valid
923
+ i.errors.messages.should be_empty
924
+ i.number = 41.3
925
+ i.should be_valid
926
+ i.errors.messages.should be_empty
927
+ end
928
+
929
+ it "should not be valid if value > 42" do
930
+ i = Item.new(:number => 43)
931
+ i.should_not be_valid
932
+ i.errors.messages.should == [ "number must be less than 42" ]
933
+ end
934
+
935
+ it "should not be valid if value == 42" do
936
+ i = Item.new(:number => 42)
937
+ i.should_not be_valid
938
+ i.errors.messages.should == [ "number must be less than 42" ]
939
+ end
940
+ end
941
+
942
+ describe "should respect :less_than_or_equal_to" do
943
+ before :each do
944
+ Item = Meta.new do
945
+ validates_numericality_of :number, :less_than_or_equal_to => 42
946
+ end
947
+ end
948
+
949
+ it "should raise ArgumentError if value is not numeric" do
950
+ lambda do
951
+ Meta.new { validates_numericality_of :number, :less_than_or_equal_to => "chicken" }
952
+ end.should raise_error(ArgumentError)
953
+ end
954
+
955
+ it "should be valid if value < 42" do
956
+ i = Item.new(:number => 41)
957
+ i.should be_valid
958
+ i.errors.messages.should be_empty
959
+ i.number = 41.3
960
+ i.should be_valid
961
+ i.errors.messages.should be_empty
962
+ end
963
+
964
+ it "should be valid if value == 42" do
965
+ i = Item.new(:number => 42)
966
+ i.should be_valid
967
+ i.errors.messages.should be_empty
968
+ end
969
+
970
+ it "should not be valid if value > 42" do
971
+ i = Item.new(:number => 43)
972
+ i.should_not be_valid
973
+ i.errors.messages.should == [ "number must be less than or equal to 42" ]
974
+ end
975
+ end
976
+
977
+ describe "should respect :odd" do
978
+ before :each do
979
+ Item = Meta.new do
980
+ validates_numericality_of :oddnumber, :odd => true
981
+ end
982
+ end
983
+
984
+ it "should raise ArgumentError when argument is not true" do
985
+ lambda do
986
+ Meta.new { validates_numericality_of :number, :odd => :really? }
987
+ end.should raise_error(ArgumentError)
988
+ end
989
+
990
+ it "should be valid when value is odd" do
991
+ i = Item.new(:oddnumber => 1)
992
+ i.should be_valid
993
+ i.errors.messages.should be_empty
994
+ end
995
+
996
+ it "should not be valid when value is even" do
997
+ i = Item.new(:oddnumber => 2)
998
+ i.should_not be_valid
999
+ i.errors.messages.should == [ "oddnumber must be odd" ]
1000
+ end
1001
+ end
1002
+
1003
+ describe "should respect :even" do
1004
+ before :each do
1005
+ Item = Meta.new do
1006
+ validates_numericality_of :evennumber, :even => true
1007
+ end
1008
+ end
1009
+
1010
+ it "should raise ArgumentError when argument is not true" do
1011
+ lambda do
1012
+ Meta.new { validates_numericality_of :number, :even => :really? }
1013
+ end.should raise_error(ArgumentError)
1014
+ end
1015
+
1016
+ it "should be valid when value is even" do
1017
+ i = Item.new(:evennumber => 4)
1018
+ i.should be_valid
1019
+ i.errors.messages.should be_empty
1020
+ end
1021
+
1022
+ it "should not be valid when value is odd" do
1023
+ i = Item.new(:evennumber => 1)
1024
+ i.should_not be_valid
1025
+ i.errors.messages.should == [ "evennumber must be even" ]
1026
+ end
1027
+ end
1028
+
1029
+ it "should respect :allow_nil" do
1030
+ Meta.new(:name => 'some') { validates_numericality_of :number, :allow_nil => true }.new.should be_valid
1031
+ end
1032
+
1033
+ describe "should allow for option combinations" do
1034
+ it ":less_than and :greater_than" do
1035
+ Item = Meta.new do
1036
+ validates_numericality_of :number, :less_than => 100, :greater_than => 50
1037
+ end
1038
+
1039
+ Item.new(:number => 60).should be_valid
1040
+
1041
+ i1 = Item.new(:number => 40)
1042
+ i1.should_not be_valid
1043
+ i1.errors.messages.should == [ "number must be greater than 50" ]
1044
+
1045
+ i2 = Item.new(:number => 150)
1046
+ i2.should_not be_valid
1047
+ i2.errors.messages.should == [ "number must be less than 100" ]
1048
+ end
1049
+
1050
+ it ":even and :less_than_or_equal_to" do
1051
+ Item = Meta.new do
1052
+ validates_numericality_of :number, :less_than_or_equal_to => 100, :even => true
1053
+ end
1054
+
1055
+ Item.new(:number => 60).should be_valid
1056
+
1057
+ i1 = Item.new(:number => 111)
1058
+ i1.should_not be_valid
1059
+ i1.errors.messages.sort.should == ["number must be less than or equal to 100", "number must be even"].sort
1060
+ end
1061
+ end
1062
+ end
1063
+
1064
+ describe "Complex validations" do
1065
+ it "should gather errors for all slots"
1066
+ it "should run all validations for the same slot"
1067
+ it "should run all validations from all metas"
1068
+ it "should somehow deal with the case when different metas contain same validations types for the same slot"
1069
+ end
1070
+
1071
+ describe "Meta with validation enabled" do
1072
+ before(:each) do
1073
+ validations_setup
1074
+ User = Meta.new { validates_uniqueness_of :email }
1075
+ end
1076
+
1077
+ it "should be able to find instances of all documents" do
1078
+ doc = User.create! :email => "yrashk@gmail.com"
1079
+ User.find.should == [doc]
1080
+ end
1081
+ end