sequel 2.11.0 → 2.12.0

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 (162) hide show
  1. data/CHANGELOG +168 -0
  2. data/README.rdoc +77 -95
  3. data/Rakefile +100 -80
  4. data/bin/sequel +2 -1
  5. data/doc/advanced_associations.rdoc +23 -32
  6. data/doc/cheat_sheet.rdoc +23 -40
  7. data/doc/dataset_filtering.rdoc +6 -6
  8. data/doc/prepared_statements.rdoc +22 -22
  9. data/doc/release_notes/2.12.0.txt +534 -0
  10. data/doc/schema.rdoc +3 -1
  11. data/doc/sharding.rdoc +8 -8
  12. data/doc/virtual_rows.rdoc +65 -0
  13. data/lib/sequel.rb +1 -1
  14. data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
  15. data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
  16. data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
  17. data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
  18. data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
  19. data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
  20. data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
  21. data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
  22. data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
  23. data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
  24. data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
  25. data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
  26. data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
  27. data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
  28. data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
  29. data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
  30. data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
  31. data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
  32. data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
  33. data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
  34. data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
  35. data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
  36. data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
  37. data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
  38. data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
  39. data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
  40. data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
  41. data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
  42. data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
  43. data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
  44. data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
  45. data/lib/sequel/core.rb +221 -0
  46. data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
  47. data/lib/{sequel_core → sequel}/database.rb +264 -149
  48. data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
  49. data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
  50. data/lib/sequel/database/schema_sql.rb +224 -0
  51. data/lib/{sequel_core → sequel}/dataset.rb +78 -236
  52. data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
  53. data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
  54. data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
  55. data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
  56. data/lib/sequel/deprecated.rb +593 -0
  57. data/lib/sequel/deprecated_migration.rb +91 -0
  58. data/lib/sequel/exceptions.rb +48 -0
  59. data/lib/sequel/extensions/blank.rb +42 -0
  60. data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
  61. data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
  62. data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
  63. data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
  64. data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
  65. data/lib/sequel/extensions/string_date_time.rb +47 -0
  66. data/lib/sequel/metaprogramming.rb +43 -0
  67. data/lib/sequel/model.rb +110 -0
  68. data/lib/sequel/model/associations.rb +1300 -0
  69. data/lib/sequel/model/base.rb +937 -0
  70. data/lib/sequel/model/deprecated.rb +204 -0
  71. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  72. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  73. data/lib/sequel/model/deprecated_validations.rb +388 -0
  74. data/lib/sequel/model/errors.rb +39 -0
  75. data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
  76. data/lib/sequel/model/inflections.rb +208 -0
  77. data/lib/sequel/model/plugins.rb +76 -0
  78. data/lib/sequel/plugins/caching.rb +122 -0
  79. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  80. data/lib/sequel/plugins/schema.rb +53 -0
  81. data/lib/sequel/plugins/serialization.rb +117 -0
  82. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  83. data/lib/sequel/plugins/validation_class_methods.rb +384 -0
  84. data/lib/sequel/plugins/validation_helpers.rb +150 -0
  85. data/lib/{sequel_core → sequel}/sql.rb +125 -190
  86. data/lib/{sequel_core → sequel}/version.rb +2 -1
  87. data/lib/sequel_core.rb +1 -172
  88. data/lib/sequel_model.rb +1 -91
  89. data/spec/adapters/firebird_spec.rb +5 -5
  90. data/spec/adapters/informix_spec.rb +1 -1
  91. data/spec/adapters/mysql_spec.rb +128 -42
  92. data/spec/adapters/oracle_spec.rb +47 -19
  93. data/spec/adapters/postgres_spec.rb +64 -52
  94. data/spec/adapters/spec_helper.rb +1 -1
  95. data/spec/adapters/sqlite_spec.rb +12 -17
  96. data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
  97. data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
  98. data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
  99. data/spec/{sequel_core → core}/database_spec.rb +135 -99
  100. data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
  101. data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
  102. data/spec/core/migration_spec.rb +263 -0
  103. data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
  104. data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
  105. data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
  106. data/spec/{sequel_core → core}/schema_spec.rb +8 -10
  107. data/spec/{sequel_core → core}/spec_helper.rb +29 -2
  108. data/spec/{sequel_core → core}/version_spec.rb +0 -0
  109. data/spec/extensions/blank_spec.rb +67 -0
  110. data/spec/extensions/caching_spec.rb +201 -0
  111. data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
  112. data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
  113. data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
  114. data/spec/extensions/pagination_spec.rb +99 -0
  115. data/spec/extensions/pretty_table_spec.rb +91 -0
  116. data/spec/extensions/query_spec.rb +85 -0
  117. data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
  118. data/spec/extensions/serialization_spec.rb +109 -0
  119. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  120. data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
  121. data/spec/extensions/string_date_time_spec.rb +93 -0
  122. data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
  123. data/spec/extensions/validation_helpers_spec.rb +291 -0
  124. data/spec/integration/dataset_test.rb +31 -0
  125. data/spec/integration/eager_loader_test.rb +17 -30
  126. data/spec/integration/schema_test.rb +8 -5
  127. data/spec/integration/spec_helper.rb +17 -0
  128. data/spec/integration/transaction_test.rb +68 -0
  129. data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
  130. data/spec/{sequel_model → model}/associations_spec.rb +23 -10
  131. data/spec/{sequel_model → model}/base_spec.rb +29 -20
  132. data/spec/{sequel_model → model}/caching_spec.rb +16 -14
  133. data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
  134. data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
  135. data/spec/model/hooks_spec.rb +472 -0
  136. data/spec/model/inflector_spec.rb +126 -0
  137. data/spec/{sequel_model → model}/model_spec.rb +25 -20
  138. data/spec/model/plugins_spec.rb +142 -0
  139. data/spec/{sequel_model → model}/record_spec.rb +121 -62
  140. data/spec/model/schema_spec.rb +92 -0
  141. data/spec/model/spec_helper.rb +124 -0
  142. data/spec/model/validations_spec.rb +1080 -0
  143. metadata +136 -107
  144. data/lib/sequel_core/core_ext.rb +0 -217
  145. data/lib/sequel_core/dataset/callback.rb +0 -13
  146. data/lib/sequel_core/dataset/schema.rb +0 -15
  147. data/lib/sequel_core/deprecated.rb +0 -26
  148. data/lib/sequel_core/exceptions.rb +0 -44
  149. data/lib/sequel_core/schema.rb +0 -2
  150. data/lib/sequel_core/schema/sql.rb +0 -325
  151. data/lib/sequel_model/association_reflection.rb +0 -267
  152. data/lib/sequel_model/associations.rb +0 -499
  153. data/lib/sequel_model/base.rb +0 -539
  154. data/lib/sequel_model/caching.rb +0 -82
  155. data/lib/sequel_model/dataset_methods.rb +0 -26
  156. data/lib/sequel_model/eager_loading.rb +0 -370
  157. data/lib/sequel_model/hooks.rb +0 -101
  158. data/lib/sequel_model/plugins.rb +0 -62
  159. data/lib/sequel_model/record.rb +0 -568
  160. data/lib/sequel_model/schema.rb +0 -49
  161. data/lib/sequel_model/validations.rb +0 -429
  162. data/spec/sequel_model/plugins_spec.rb +0 -80
@@ -0,0 +1,92 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model, "table_exists?" do
4
+
5
+ before(:each) do
6
+ MODEL_DB.reset
7
+ @model = Class.new(Sequel::Model(:items))
8
+ end
9
+
10
+ it "should get the table name and question the model's db if table_exists?" do
11
+ @model.should_receive(:table_name).and_return(:items)
12
+ @model.db.should_receive(:table_exists?)
13
+ @model.table_exists?
14
+ end
15
+
16
+ end
17
+
18
+ describe Sequel::Model, "create_table and schema" do
19
+
20
+ before(:each) do
21
+ MODEL_DB.reset
22
+ deprec do
23
+ @model = Class.new(Sequel::Model) do
24
+ set_schema(:items) do
25
+ text :name
26
+ float :price, :null => false
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ deprec_specify "should get the create table SQL list from the db and execute it line by line" do
33
+ @model.create_table
34
+ MODEL_DB.sqls.should == ['CREATE TABLE items (name text, price float NOT NULL)']
35
+ end
36
+
37
+ deprec_specify "should reload the schema from the database" do
38
+ schem = {:name=>{:type=>:string}, :price=>{:type=>:float}}
39
+ @model.db.should_receive(:schema).with(:items, :reload=>true).and_return(schem.to_a.sort_by{|x| x[0].to_s})
40
+ @model.create_table
41
+ @model.db_schema.should == schem
42
+ @model.instance_variable_get(:@columns).should == [:name, :price]
43
+ end
44
+
45
+ deprec_specify "should return the schema generator via schema" do
46
+ @model.schema.should be_a_kind_of(Sequel::Schema::Generator)
47
+ end
48
+
49
+ deprec_specify "should use the superclasses schema if it exists" do
50
+ @submodel = Class.new(@model)
51
+ @submodel.schema.should be_a_kind_of(Sequel::Schema::Generator)
52
+ end
53
+
54
+ deprec_specify "should return nil if no schema is present" do
55
+ @model = Class.new(Sequel::Model)
56
+ @model.schema.should == nil
57
+ @submodel = Class.new(@model)
58
+ @submodel.schema.should == nil
59
+ end
60
+ end
61
+
62
+ describe Sequel::Model, "drop_table" do
63
+
64
+ before(:each) do
65
+ MODEL_DB.reset
66
+ @model = Class.new(Sequel::Model(:items))
67
+ end
68
+
69
+ deprec_specify "should get the drop table SQL for the associated table and then execute the SQL." do
70
+ @model.should_receive(:table_name).and_return(:items)
71
+ @model.db.should_receive(:drop_table_sql).with(:items)
72
+ @model.db.should_receive(:execute).and_return(:true)
73
+ @model.drop_table
74
+ end
75
+
76
+ end
77
+
78
+ describe Sequel::Model, "create_table!" do
79
+
80
+ before(:each) do
81
+ MODEL_DB.reset
82
+ @model = Class.new(Sequel::Model(:items))
83
+ end
84
+
85
+ deprec_specify "should drop table if it exists and then create the table" do
86
+ @model.should_receive(:drop_table).and_return(true)
87
+ @model.should_receive(:create_table).and_return(true)
88
+
89
+ @model.create_table!
90
+ end
91
+
92
+ end
@@ -0,0 +1,124 @@
1
+ require 'rubygems'
2
+ unless Object.const_defined?('Sequel')
3
+ $:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
4
+ require 'sequel/core'
5
+ end
6
+ unless Sequel.const_defined?('Model')
7
+ $:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
8
+ require 'sequel/model'
9
+ end
10
+
11
+ Sequel.virtual_row_instance_eval = true
12
+
13
+ module Spec::Example::ExampleMethods
14
+ def deprec
15
+ output = Sequel::Deprecation.output = nil
16
+ begin
17
+ yield
18
+ ensure
19
+ Sequel::Deprecation.output = output
20
+ end
21
+ end
22
+ end
23
+
24
+ module Spec::Example::ExampleGroupMethods
25
+ def deprec_specify(*args, &block)
26
+ specify(*args) do
27
+ output = Sequel::Deprecation.output
28
+ Sequel::Deprecation.output = nil
29
+ begin
30
+ instance_eval(&block)
31
+ ensure
32
+ Sequel::Deprecation.output = output
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ class MockDataset < Sequel::Dataset
39
+ def insert(*args)
40
+ @db.execute insert_sql(*args)
41
+ end
42
+
43
+ def update(*args)
44
+ @db.execute update_sql(*args)
45
+ end
46
+
47
+ def delete(*args)
48
+ @db.execute delete_sql(*args)
49
+ end
50
+
51
+ def fetch_rows(sql)
52
+ return if sql =~ /information_schema/
53
+ @db.execute(sql)
54
+ yield({:id => 1, :x => 1})
55
+ end
56
+
57
+ def quoted_identifier(c)
58
+ "\"#{c}\""
59
+ end
60
+ end
61
+
62
+ class MockDatabase < Sequel::Database
63
+ @@quote_identifiers = false
64
+ self.identifier_input_method = nil
65
+ self.identifier_output_method = nil
66
+ attr_reader :sqls
67
+
68
+ def execute(sql, opts={})
69
+ @sqls ||= []
70
+ @sqls << sql
71
+ end
72
+
73
+ def reset
74
+ @sqls = []
75
+ end
76
+
77
+ def schema(table_name, opts)
78
+ if table_name
79
+ [[:id, {:primary_key=>true}]]
80
+ else
81
+ {table_name=>[[:id, {:primary_key=>true}]]}
82
+ end
83
+ end
84
+
85
+ def transaction(opts={})
86
+ return yield if @transactions.include?(Thread.current)
87
+ execute('BEGIN')
88
+ begin
89
+ @transactions << Thread.current
90
+ yield
91
+ rescue Exception => e
92
+ execute('ROLLBACK')
93
+ transaction_error(e)
94
+ ensure
95
+ unless e
96
+ execute('COMMIT')
97
+ end
98
+ @transactions.delete(Thread.current)
99
+ end
100
+ end
101
+
102
+ def dataset(opts=nil); MockDataset.new(self, opts); end
103
+ end
104
+
105
+ class << Sequel::Model
106
+ alias orig_columns columns
107
+ alias orig_str_columns str_columns
108
+ def columns(*cols)
109
+ return if cols.empty?
110
+ define_method(:columns){cols}
111
+ @dataset.instance_variable_set(:@columns, cols) if @dataset
112
+ define_method(:str_columns){cols.map{|x|x.to_s.freeze}}
113
+ def_column_accessor(*cols)
114
+ @columns = cols
115
+ @db_schema = {}
116
+ cols.each{|c| @db_schema[c] = {}}
117
+ end
118
+ def simple_table
119
+ nil
120
+ end
121
+ end
122
+
123
+ Sequel::Model.use_transactions = false
124
+ Sequel::Model.db = MODEL_DB = MockDatabase.new
@@ -0,0 +1,1080 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model::Errors do
4
+ before do
5
+ @errors = Sequel::Model::Errors.new
6
+ end
7
+
8
+ specify "should be clearable using #clear" do
9
+ @errors.add(:a, 'b')
10
+ @errors.should == {:a=>['b']}
11
+ @errors.clear
12
+ @errors.should == {}
13
+ end
14
+
15
+ specify "should be empty if no errors are added" do
16
+ @errors.should be_empty
17
+ @errors[:blah] << "blah"
18
+ @errors.should_not be_empty
19
+ end
20
+
21
+ specify "should return errors for a specific attribute using #on or #[]" do
22
+ @errors[:blah].should == []
23
+ @errors.on(:blah).should == []
24
+
25
+ @errors[:blah] << 'blah'
26
+ @errors[:blah].should == ['blah']
27
+ @errors.on(:blah).should == ['blah']
28
+
29
+ @errors[:bleu].should == []
30
+ @errors.on(:bleu).should == []
31
+ end
32
+
33
+ specify "should accept errors using #[] << or #add" do
34
+ @errors[:blah] << 'blah'
35
+ @errors[:blah].should == ['blah']
36
+
37
+ @errors.add :blah, 'zzzz'
38
+ @errors[:blah].should == ['blah', 'zzzz']
39
+ end
40
+
41
+ specify "should return full messages using #full_messages" do
42
+ @errors.full_messages.should == []
43
+
44
+ @errors[:blow] << 'blieuh'
45
+ @errors[:blow] << 'blich'
46
+ @errors[:blay] << 'bliu'
47
+ msgs = @errors.full_messages
48
+ msgs.size.should == 3
49
+ msgs.should include('blow blieuh', 'blow blich', 'blay bliu')
50
+ end
51
+
52
+ specify "should return the number of error messages using #count" do
53
+ @errors.count.should == 0
54
+ @errors.add(:a, 'b')
55
+ @errors.count.should == 1
56
+ @errors.add(:a, 'c')
57
+ @errors.count.should == 2
58
+ @errors.add(:b, 'c')
59
+ @errors.count.should == 3
60
+ end
61
+
62
+ specify "should return the array of error messages for a given attribute using #on" do
63
+ @errors.add(:a, 'b')
64
+ @errors.on(:a).should == ['b']
65
+ @errors.add(:a, 'c')
66
+ @errors.on(:a).should == ['b', 'c']
67
+ @errors.add(:b, 'c')
68
+ @errors.on(:a).should == ['b', 'c']
69
+ end
70
+
71
+ specify "should return nil if there are no error messages for a given attribute using #on" do
72
+ @errors.on(:a).should == nil
73
+ @errors.add(:b, 'b')
74
+ @errors.on(:a).should == nil
75
+ end
76
+ end
77
+
78
+ describe Sequel::Model do
79
+ before do
80
+ deprec do
81
+ @c = Class.new(Sequel::Model) do
82
+ def self.validates_coolness_of(attr)
83
+ validates_each(attr) {|o, a, v| o.errors[a] << 'is not cool' if v != :cool}
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ deprec_specify "should respond to validates, validations, has_validations?" do
90
+ @c.should respond_to(:validations)
91
+ @c.should respond_to(:has_validations?)
92
+ end
93
+
94
+ deprec_specify "should acccept validation definitions using validates_each" do
95
+ @c.validates_each(:xx, :yy) {|o, a, v| o.errors[a] << 'too low' if v < 50}
96
+ o = @c.new
97
+ o.should_receive(:xx).once.and_return(40)
98
+ o.should_receive(:yy).once.and_return(60)
99
+ o.valid?.should == false
100
+ o.errors.full_messages.should == ['xx too low']
101
+ end
102
+
103
+ deprec_specify "should return true/false for has_validations?" do
104
+ @c.has_validations?.should == false
105
+ @c.validates_each(:xx) {1}
106
+ @c.has_validations?.should == true
107
+ end
108
+
109
+ deprec_specify "should validate multiple attributes at once" do
110
+ o = @c.new
111
+ def o.xx
112
+ 1
113
+ end
114
+ def o.yy
115
+ 2
116
+ end
117
+ vals = nil
118
+ atts = nil
119
+ @c.validates_each([:xx, :yy]){|obj,a,v| atts=a; vals=v}
120
+ o.valid?
121
+ vals.should == [1,2]
122
+ atts.should == [:xx, :yy]
123
+ end
124
+
125
+ deprec_specify "should respect allow_missing option when using multiple attributes" do
126
+ o = @c.new
127
+ def o.xx
128
+ self[:xx]
129
+ end
130
+ def o.yy
131
+ self[:yy]
132
+ end
133
+ vals = nil
134
+ atts = nil
135
+ @c.validates_each([:xx, :yy], :allow_missing=>true){|obj,a,v| atts=a; vals=v}
136
+
137
+ o.values[:xx] = 1
138
+ o.valid?
139
+ vals.should == [1,nil]
140
+ atts.should == [:xx, :yy]
141
+
142
+ vals = nil
143
+ atts = nil
144
+ o.values.clear
145
+ o.values[:yy] = 2
146
+ o.valid?
147
+ vals.should == [nil, 2]
148
+ atts.should == [:xx, :yy]
149
+
150
+ vals = nil
151
+ atts = nil
152
+ o.values.clear
153
+ o.valid?.should == true
154
+ vals.should == nil
155
+ atts.should == nil
156
+ end
157
+
158
+ deprec_specify "should overwrite existing validation with the same tag and attribute" do
159
+ @c.validates_each(:xx, :xx, :tag=>:low) {|o, a, v| o.xxx; o.errors[a] << 'too low' if v < 50}
160
+ @c.validates_each(:yy, :yy) {|o, a, v| o.yyy; o.errors[a] << 'too low' if v < 50}
161
+ @c.validates_presence_of(:zz, :zz)
162
+ @c.validates_length_of(:aa, :aa, :tag=>:blah)
163
+ o = @c.new
164
+ def o.zz
165
+ @a ||= 0
166
+ @a += 1
167
+ end
168
+ def o.aa
169
+ @b ||= 0
170
+ @b += 1
171
+ end
172
+ o.should_receive(:xx).once.and_return(40)
173
+ o.should_receive(:yy).once.and_return(60)
174
+ o.should_receive(:xxx).once
175
+ o.should_receive(:yyy).twice
176
+ o.valid?.should == false
177
+ o.zz.should == 2
178
+ o.aa.should == 2
179
+ o.errors.full_messages.should == ['xx too low']
180
+ end
181
+
182
+ deprec_specify "should provide a validates method that takes block with validation definitions" do
183
+ @c.validates do
184
+ coolness_of :blah
185
+ end
186
+ @c.validations[:blah].should_not be_empty
187
+ o = @c.new
188
+ o.should_receive(:blah).once.and_return(nil)
189
+ o.valid?.should == false
190
+ o.errors.full_messages.should == ['blah is not cool']
191
+ end
192
+ end
193
+
194
+ describe Sequel::Model do
195
+ before do
196
+ @c = Class.new(Sequel::Model) do
197
+ columns :score
198
+ def validate
199
+ errors[:score] << 'too low' if score < 87
200
+ end
201
+ end
202
+
203
+ @o = @c.new
204
+ end
205
+
206
+ specify "should supply a #valid? method that returns true if validations pass" do
207
+ @o.score = 50
208
+ @o.should_not be_valid
209
+ @o.score = 100
210
+ @o.should be_valid
211
+ end
212
+
213
+ specify "should provide an errors object" do
214
+ @o.score = 100
215
+ @o.should be_valid
216
+ @o.errors.should be_empty
217
+
218
+ @o.score = 86
219
+ @o.should_not be_valid
220
+ @o.errors[:score].should == ['too low']
221
+ @o.errors[:blah].should be_empty
222
+ end
223
+ end
224
+
225
+ describe 'Sequel::Model::Validation::Generator' do
226
+ before do
227
+ $testit = nil
228
+
229
+ @c = Class.new(Sequel::Model) do
230
+ def self.validates_blah
231
+ $testit = 1324
232
+ end
233
+ end
234
+ end
235
+
236
+ deprec_specify "should instance_eval the block, sending everything to its receiver" do
237
+ @c.class_eval do
238
+ validates do
239
+ blah
240
+ end
241
+ end
242
+ $testit.should == 1324
243
+ end
244
+ end
245
+
246
+ describe Sequel::Model do
247
+ before do
248
+ @c = Class.new(Sequel::Model) do
249
+ columns :value
250
+
251
+ def self.filter(*args)
252
+ o = Object.new
253
+ def o.count; 2; end
254
+ o
255
+ end
256
+
257
+ def skip; false; end
258
+ def dont_skip; true; end
259
+ end
260
+ @m = @c.new
261
+ end
262
+
263
+ deprec_specify "should validate acceptance_of" do
264
+ @c.validates_acceptance_of :value
265
+ @m.should be_valid
266
+ @m.value = '1'
267
+ @m.should be_valid
268
+ end
269
+
270
+ deprec_specify "should validate acceptance_of with accept" do
271
+ @c.validates_acceptance_of :value, :accept => 'true'
272
+ @m.value = '1'
273
+ @m.should_not be_valid
274
+ @m.value = 'true'
275
+ @m.should be_valid
276
+ end
277
+
278
+ deprec_specify "should validate acceptance_of with allow_nil => false" do
279
+ @c.validates_acceptance_of :value, :allow_nil => false
280
+ @m.should_not be_valid
281
+ end
282
+
283
+ deprec_specify "should validate acceptance_of with allow_missing => true" do
284
+ @c.validates_acceptance_of :value, :allow_missing => true
285
+ @m.should be_valid
286
+ end
287
+
288
+ deprec_specify "should validate acceptance_of with allow_missing => true and allow_nil => false" do
289
+ @c.validates_acceptance_of :value, :allow_missing => true, :allow_nil => false
290
+ @m.should be_valid
291
+ @m.value = nil
292
+ @m.should_not be_valid
293
+ end
294
+
295
+ deprec_specify "should validate acceptance_of with if => true" do
296
+ @c.validates_acceptance_of :value, :if => :dont_skip
297
+ @m.value = '0'
298
+ @m.should_not be_valid
299
+ end
300
+
301
+ deprec_specify "should validate acceptance_of with if => false" do
302
+ @c.validates_acceptance_of :value, :if => :skip
303
+ @m.value = '0'
304
+ @m.should be_valid
305
+ end
306
+
307
+ deprec_specify "should validate acceptance_of with if proc that evaluates to true" do
308
+ @c.validates_acceptance_of :value, :if => proc{true}
309
+ @m.value = '0'
310
+ @m.should_not be_valid
311
+ end
312
+
313
+ deprec_specify "should validate acceptance_of with if proc that evaluates to false" do
314
+ @c.validates_acceptance_of :value, :if => proc{false}
315
+ @m.value = '0'
316
+ @m.should be_valid
317
+ end
318
+
319
+ deprec_specify "should raise an error if :if option is not a Symbol, Proc, or nil" do
320
+ @c.validates_acceptance_of :value, :if => 1
321
+ @m.value = '0'
322
+ proc{@m.valid?}.should raise_error(Sequel::Error)
323
+ end
324
+
325
+ deprec_specify "should validate confirmation_of" do
326
+ @c.send(:attr_accessor, :value_confirmation)
327
+ @c.validates_confirmation_of :value
328
+
329
+ @m.value = 'blah'
330
+ @m.should_not be_valid
331
+
332
+ @m.value_confirmation = 'blah'
333
+ @m.should be_valid
334
+ end
335
+
336
+ deprec_specify "should validate confirmation_of with if => true" do
337
+ @c.send(:attr_accessor, :value_confirmation)
338
+ @c.validates_confirmation_of :value, :if => :dont_skip
339
+
340
+ @m.value = 'blah'
341
+ @m.should_not be_valid
342
+ end
343
+
344
+ deprec_specify "should validate confirmation_of with if => false" do
345
+ @c.send(:attr_accessor, :value_confirmation)
346
+ @c.validates_confirmation_of :value, :if => :skip
347
+
348
+ @m.value = 'blah'
349
+ @m.should be_valid
350
+ end
351
+
352
+ deprec_specify "should validate confirmation_of with allow_missing => true" do
353
+ @c.send(:attr_accessor, :value_confirmation)
354
+ @c.validates_acceptance_of :value, :allow_missing => true
355
+ @m.should be_valid
356
+ @m.value_confirmation = 'blah'
357
+ @m.should be_valid
358
+ @m.value = nil
359
+ @m.should_not be_valid
360
+ end
361
+
362
+ deprec_specify "should validate format_of" do
363
+ @c.validates_format_of :value, :with => /.+_.+/
364
+ @m.value = 'abc_'
365
+ @m.should_not be_valid
366
+ @m.value = 'abc_def'
367
+ @m.should be_valid
368
+ end
369
+
370
+ deprec_specify "should raise for validate_format_of without regexp" do
371
+ proc {@c.validates_format_of :value}.should raise_error(ArgumentError)
372
+ proc {@c.validates_format_of :value, :with => :blah}.should raise_error(ArgumentError)
373
+ end
374
+
375
+ deprec_specify "should validate format_of with if => true" do
376
+ @c.validates_format_of :value, :with => /_/, :if => :dont_skip
377
+
378
+ @m.value = 'a'
379
+ @m.should_not be_valid
380
+ end
381
+
382
+ deprec_specify "should validate format_of with if => false" do
383
+ @c.validates_format_of :value, :with => /_/, :if => :skip
384
+
385
+ @m.value = 'a'
386
+ @m.should be_valid
387
+ end
388
+
389
+ deprec_specify "should validate format_of with allow_missing => true" do
390
+ @c.validates_format_of :value, :allow_missing => true, :with=>/./
391
+ @m.should be_valid
392
+ @m.value = nil
393
+ @m.should_not be_valid
394
+ end
395
+
396
+ deprec_specify "should validate length_of with maximum" do
397
+ @c.validates_length_of :value, :maximum => 5
398
+ @m.should_not be_valid
399
+ @m.value = '12345'
400
+ @m.should be_valid
401
+ @m.value = '123456'
402
+ @m.should_not be_valid
403
+ end
404
+
405
+ deprec_specify "should validate length_of with minimum" do
406
+ @c.validates_length_of :value, :minimum => 5
407
+ @m.should_not be_valid
408
+ @m.value = '12345'
409
+ @m.should be_valid
410
+ @m.value = '1234'
411
+ @m.should_not be_valid
412
+ end
413
+
414
+ deprec_specify "should validate length_of with within" do
415
+ @c.validates_length_of :value, :within => 2..5
416
+ @m.should_not be_valid
417
+ @m.value = '12345'
418
+ @m.should be_valid
419
+ @m.value = '1'
420
+ @m.should_not be_valid
421
+ @m.value = '123456'
422
+ @m.should_not be_valid
423
+ end
424
+
425
+ deprec_specify "should validate length_of with is" do
426
+ @c.validates_length_of :value, :is => 3
427
+ @m.should_not be_valid
428
+ @m.value = '123'
429
+ @m.should be_valid
430
+ @m.value = '12'
431
+ @m.should_not be_valid
432
+ @m.value = '1234'
433
+ @m.should_not be_valid
434
+ end
435
+
436
+ deprec_specify "should validate length_of with allow_nil" do
437
+ @c.validates_length_of :value, :is => 3, :allow_nil => true
438
+ @m.should be_valid
439
+ end
440
+
441
+ deprec_specify "should validate length_of with if => true" do
442
+ @c.validates_length_of :value, :is => 3, :if => :dont_skip
443
+
444
+ @m.value = 'a'
445
+ @m.should_not be_valid
446
+ end
447
+
448
+ deprec_specify "should validate length_of with if => false" do
449
+ @c.validates_length_of :value, :is => 3, :if => :skip
450
+
451
+ @m.value = 'a'
452
+ @m.should be_valid
453
+ end
454
+
455
+ deprec_specify "should validate length_of with allow_missing => true" do
456
+ @c.validates_length_of :value, :allow_missing => true, :minimum => 5
457
+ @m.should be_valid
458
+ @m.value = nil
459
+ @m.should_not be_valid
460
+ end
461
+
462
+ deprec_specify "should allow multiple calls to validates_length_of with different options without overwriting" do
463
+ @c.validates_length_of :value, :maximum => 5
464
+ @c.validates_length_of :value, :minimum => 5
465
+ @m.should_not be_valid
466
+ @m.value = '12345'
467
+ @m.should be_valid
468
+ @m.value = '123456'
469
+ @m.should_not be_valid
470
+ @m.value = '12345'
471
+ @m.should be_valid
472
+ @m.value = '1234'
473
+ @m.should_not be_valid
474
+ end
475
+
476
+ deprec_specify "should validate numericality_of" do
477
+ @c.validates_numericality_of :value
478
+ @m.value = 'blah'
479
+ @m.should_not be_valid
480
+ @m.value = '123'
481
+ @m.should be_valid
482
+ @m.value = '123.1231'
483
+ @m.should be_valid
484
+ @m.value = '+1'
485
+ @m.should be_valid
486
+ @m.value = '-1'
487
+ @m.should be_valid
488
+ @m.value = '+1.123'
489
+ @m.should be_valid
490
+ @m.value = '-0.123'
491
+ @m.should be_valid
492
+ @m.value = '-0.123E10'
493
+ @m.should be_valid
494
+ @m.value = '32.123e10'
495
+ @m.should be_valid
496
+ @m.value = '+32.123E10'
497
+ @m.should be_valid
498
+ @m.should be_valid
499
+ @m.value = '.0123'
500
+ end
501
+
502
+ deprec_specify "should validate numericality_of with only_integer" do
503
+ @c.validates_numericality_of :value, :only_integer => true
504
+ @m.value = 'blah'
505
+ @m.should_not be_valid
506
+ @m.value = '123'
507
+ @m.should be_valid
508
+ @m.value = '123.1231'
509
+ @m.should_not be_valid
510
+ end
511
+
512
+ deprec_specify "should validate numericality_of with if => true" do
513
+ @c.validates_numericality_of :value, :if => :dont_skip
514
+
515
+ @m.value = 'a'
516
+ @m.should_not be_valid
517
+ end
518
+
519
+ deprec_specify "should validate numericality_of with if => false" do
520
+ @c.validates_numericality_of :value, :if => :skip
521
+
522
+ @m.value = 'a'
523
+ @m.should be_valid
524
+ end
525
+
526
+ deprec_specify "should validate numericality_of with allow_missing => true" do
527
+ @c.validates_numericality_of :value, :allow_missing => true
528
+ @m.should be_valid
529
+ @m.value = nil
530
+ @m.should_not be_valid
531
+ end
532
+
533
+ deprec_specify "should validate presence_of" do
534
+ @c.validates_presence_of :value
535
+ @m.should_not be_valid
536
+ @m.value = ''
537
+ @m.should_not be_valid
538
+ @m.value = 1234
539
+ @m.should be_valid
540
+ @m.value = nil
541
+ @m.should_not be_valid
542
+ @m.value = true
543
+ @m.should be_valid
544
+ @m.value = false
545
+ @m.should be_valid
546
+ end
547
+
548
+ deprec_specify "should validate inclusion_of with an array" do
549
+ @c.validates_inclusion_of :value, :in => [1,2]
550
+ @m.should_not be_valid
551
+ @m.value = 1
552
+ @m.should be_valid
553
+ @m.value = 1.5
554
+ @m.should_not be_valid
555
+ @m.value = 2
556
+ @m.should be_valid
557
+ @m.value = 3
558
+ @m.should_not be_valid
559
+ end
560
+
561
+ deprec_specify "should validate inclusion_of with a range" do
562
+ @c.validates_inclusion_of :value, :in => 1..4
563
+ @m.should_not be_valid
564
+ @m.value = 1
565
+ @m.should be_valid
566
+ @m.value = 1.5
567
+ @m.should be_valid
568
+ @m.value = 0
569
+ @m.should_not be_valid
570
+ @m.value = 5
571
+ @m.should_not be_valid
572
+ end
573
+
574
+ deprec_specify "should raise an error if inclusion_of doesn't receive a valid :in option" do
575
+ lambda {
576
+ @c.validates_inclusion_of :value
577
+ }.should raise_error(ArgumentError)
578
+
579
+ lambda {
580
+ @c.validates_inclusion_of :value, :in => 1
581
+ }.should raise_error(ArgumentError)
582
+ end
583
+
584
+ deprec_specify "should raise an error if inclusion_of handles :allow_nil too" do
585
+ @c.validates_inclusion_of :value, :in => 1..4, :allow_nil => true
586
+ @m.value = nil
587
+ @m.should be_valid
588
+ @m.value = 0
589
+ @m.should_not be_valid
590
+ end
591
+
592
+ deprec_specify "should validate presence_of with if => true" do
593
+ @c.validates_presence_of :value, :if => :dont_skip
594
+ @m.should_not be_valid
595
+ end
596
+
597
+ deprec_specify "should validate presence_of with if => false" do
598
+ @c.validates_presence_of :value, :if => :skip
599
+ @m.should be_valid
600
+ end
601
+
602
+ deprec_specify "should validate presence_of with allow_missing => true" do
603
+ @c.validates_presence_of :value, :allow_missing => true
604
+ @m.should be_valid
605
+ @m.value = nil
606
+ @m.should_not be_valid
607
+ end
608
+
609
+ deprec_specify "should validate uniqueness_of with if => true" do
610
+ @c.validates_uniqueness_of :value, :if => :dont_skip
611
+
612
+ @m.value = 'a'
613
+ @m.should_not be_valid
614
+ end
615
+
616
+ deprec_specify "should validate uniqueness_of with if => false" do
617
+ @c.validates_uniqueness_of :value, :if => :skip
618
+
619
+ @m.value = 'a'
620
+ @m.should be_valid
621
+ end
622
+
623
+ deprec_specify "should validate uniqueness_of with allow_missing => true" do
624
+ @c.validates_uniqueness_of :value, :allow_missing => true
625
+ @m.should be_valid
626
+ @m.value = nil
627
+ @m.should_not be_valid
628
+ end
629
+ end
630
+
631
+ context "Superclass validations" do
632
+ before do
633
+ deprec do
634
+ @c1 = Class.new(Sequel::Model) do
635
+ columns :value
636
+ validates_length_of :value, :minimum => 5
637
+ end
638
+
639
+ @c2 = Class.new(@c1) do
640
+ columns :value
641
+ validates_format_of :value, :with => /^[a-z]+$/
642
+ end
643
+ end
644
+ end
645
+
646
+ deprec_specify "should be checked when validating" do
647
+ o = @c2.new
648
+ o.value = 'ab'
649
+ o.valid?.should == false
650
+ o.errors.full_messages.should == [
651
+ 'value is too short'
652
+ ]
653
+
654
+ o.value = '12'
655
+ o.valid?.should == false
656
+ o.errors.full_messages.should == [
657
+ 'value is too short',
658
+ 'value is invalid'
659
+ ]
660
+
661
+ o.value = 'abcde'
662
+ o.valid?.should be_true
663
+ end
664
+
665
+ deprec_specify "should be skipped if skip_superclass_validations is called" do
666
+ @c2.skip_superclass_validations
667
+
668
+ o = @c2.new
669
+ o.value = 'ab'
670
+ o.valid?.should be_true
671
+
672
+ o.value = '12'
673
+ o.valid?.should == false
674
+ o.errors.full_messages.should == [
675
+ 'value is invalid'
676
+ ]
677
+
678
+ o.value = 'abcde'
679
+ o.valid?.should be_true
680
+ end
681
+ end
682
+
683
+ context ".validates with block" do
684
+ deprec_specify "should support calling .each" do
685
+ @c = Class.new(Sequel::Model) do
686
+ columns :vvv
687
+ validates do
688
+ each :vvv do |o, a, v|
689
+ o.errors[a] << "is less than zero" if v.to_i < 0
690
+ end
691
+ end
692
+ end
693
+
694
+ o = @c.new
695
+ o.vvv = 1
696
+ o.should be_valid
697
+ o.vvv = -1
698
+ o.should_not be_valid
699
+ end
700
+ end
701
+
702
+ describe Sequel::Model, "Validations" do
703
+
704
+ before(:all) do
705
+ class ::Person < Sequel::Model
706
+ columns :id,:name,:first_name,:last_name,:middle_name,:initials,:age, :terms
707
+ end
708
+
709
+ class ::Smurf < Person
710
+ end
711
+
712
+ class ::Cow < Sequel::Model
713
+ columns :id, :name, :got_milk
714
+ end
715
+
716
+ class ::User < Sequel::Model
717
+ columns :id, :username, :password
718
+ end
719
+
720
+ class ::Address < Sequel::Model
721
+ columns :id, :zip_code
722
+ end
723
+ end
724
+
725
+ deprec_specify "should validate the acceptance of a column" do
726
+ class ::Cow < Sequel::Model
727
+ validations.clear
728
+ validates_acceptance_of :got_milk, :accept => 'blah', :allow_nil => false
729
+ end
730
+
731
+ @cow = Cow.new
732
+ @cow.should_not be_valid
733
+ @cow.errors.full_messages.should == ["got_milk is not accepted"]
734
+
735
+ @cow.got_milk = "blah"
736
+ @cow.should be_valid
737
+ end
738
+
739
+ deprec_specify "should validate the confirmation of a column" do
740
+ class ::User < Sequel::Model
741
+ def password_confirmation
742
+ "test"
743
+ end
744
+
745
+ validations.clear
746
+ validates_confirmation_of :password
747
+ end
748
+
749
+ @user = User.new
750
+ @user.should_not be_valid
751
+ @user.errors.full_messages.should == ["password is not confirmed"]
752
+
753
+ @user.password = "test"
754
+ @user.should be_valid
755
+ end
756
+
757
+ deprec_specify "should validate format of column" do
758
+ class ::Person < Sequel::Model
759
+ validates_format_of :first_name, :with => /^[a-zA-Z]+$/
760
+ end
761
+
762
+ @person = Person.new :first_name => "Lancelot99"
763
+ @person.valid?.should be_false
764
+ @person = Person.new :first_name => "Anita"
765
+ @person.valid?.should be_true
766
+ end
767
+
768
+ deprec_specify "should validate length of column" do
769
+ class ::Person < Sequel::Model
770
+ validations.clear
771
+ validates_length_of :first_name, :maximum => 30
772
+ validates_length_of :last_name, :minimum => 30
773
+ validates_length_of :middle_name, :within => 1..5
774
+ validates_length_of :initials, :is => 2
775
+ end
776
+
777
+ @person = Person.new(
778
+ :first_name => "Anamethatiswaytofreakinglongandwayoverthirtycharacters",
779
+ :last_name => "Alastnameunderthirtychars",
780
+ :initials => "LGC",
781
+ :middle_name => "danger"
782
+ )
783
+
784
+ @person.should_not be_valid
785
+ @person.errors.full_messages.size.should == 4
786
+ @person.errors.full_messages.should include(
787
+ 'first_name is too long',
788
+ 'last_name is too short',
789
+ 'middle_name is the wrong length',
790
+ 'initials is the wrong length'
791
+ )
792
+
793
+ @person.first_name = "Lancelot"
794
+ @person.last_name = "1234567890123456789012345678901"
795
+ @person.initials = "LC"
796
+ @person.middle_name = "Will"
797
+ @person.should be_valid
798
+ end
799
+
800
+ deprec_specify "should validate that a column doesn't have a string value" do
801
+ p = Class.new(Sequel::Model)
802
+ p.class_eval do
803
+ columns :age, :price, :confirmed
804
+ self.raise_on_typecast_failure = false
805
+ validates_not_string :age
806
+ validates_not_string :confirmed
807
+ validates_not_string :price, :message=>'is not valid'
808
+ @db_schema = {:age=>{:type=>:integer}}
809
+ end
810
+
811
+ @person = p.new
812
+ @person.should be_valid
813
+
814
+ @person.confirmed = 't'
815
+ @person.should_not be_valid
816
+ @person.errors.full_messages.should == ['confirmed is a string']
817
+ @person.confirmed = true
818
+ @person.should be_valid
819
+
820
+ @person.age = 'a'
821
+ @person.should_not be_valid
822
+ @person.errors.full_messages.should == ['age is not a valid integer']
823
+ @person.db_schema[:age][:type] = :datetime
824
+ @person.should_not be_valid
825
+ @person.errors.full_messages.should == ['age is not a valid datetime']
826
+ @person.age = 20
827
+ @person.should be_valid
828
+
829
+ @person.price = 'a'
830
+ @person.should_not be_valid
831
+ @person.errors.full_messages.should == ['price is not valid']
832
+ @person.price = 20
833
+ @person.should be_valid
834
+ end
835
+
836
+ deprec_specify "should validate numericality of column" do
837
+ class ::Person < Sequel::Model
838
+ validations.clear
839
+ validates_numericality_of :age
840
+ end
841
+
842
+ @person = Person.new :age => "Twenty"
843
+ @person.should_not be_valid
844
+ @person.errors.full_messages.should == ['age is not a number']
845
+
846
+ @person.age = 20
847
+ @person.should be_valid
848
+ end
849
+
850
+ deprec_specify "should validate the presence of a column" do
851
+ class ::Cow < Sequel::Model
852
+ validations.clear
853
+ validates_presence_of :name
854
+ end
855
+
856
+ @cow = Cow.new
857
+ @cow.should_not be_valid
858
+ @cow.errors.full_messages.should == ['name is not present']
859
+
860
+ @cow.name = "Betsy"
861
+ @cow.should be_valid
862
+ end
863
+
864
+ deprec_specify "should validate the uniqueness of a column" do
865
+ class ::User < Sequel::Model
866
+ validations.clear
867
+ validates do
868
+ uniqueness_of :username
869
+ end
870
+ end
871
+ User.dataset.extend(Module.new {
872
+ def fetch_rows(sql)
873
+ @db << sql
874
+
875
+ case sql
876
+ when /COUNT.*username = '0records'/
877
+ yield({:v => 0})
878
+ when /COUNT.*username = '2records'/
879
+ yield({:v => 2})
880
+ when /COUNT.*username = '1record'/
881
+ yield({:v => 1})
882
+ when /username = '1record'/
883
+ yield({:id => 3, :username => "1record", :password => "test"})
884
+ end
885
+ end
886
+ })
887
+
888
+ @user = User.new(:username => "2records", :password => "anothertest")
889
+ @user.should_not be_valid
890
+ @user.errors.full_messages.should == ['username is already taken']
891
+
892
+ @user = User.new(:username => "1record", :password => "anothertest")
893
+ @user.should_not be_valid
894
+ @user.errors.full_messages.should == ['username is already taken']
895
+
896
+ @user = User.load(:id=>4, :username => "1record", :password => "anothertest")
897
+ @user.should_not be_valid
898
+ @user.errors.full_messages.should == ['username is already taken']
899
+
900
+ @user = User.load(:id=>3, :username => "1record", :password => "anothertest")
901
+ @user.should be_valid
902
+ @user.errors.full_messages.should == []
903
+
904
+ @user = User.new(:username => "0records", :password => "anothertest")
905
+ @user.should be_valid
906
+ @user.errors.full_messages.should == []
907
+ end
908
+
909
+ deprec_specify "should validate the uniqueness of multiple columns" do
910
+ class ::User < Sequel::Model
911
+ validations.clear
912
+ validates do
913
+ uniqueness_of [:username, :password]
914
+ end
915
+ end
916
+ User.dataset.extend(Module.new {
917
+ def fetch_rows(sql)
918
+ @db << sql
919
+
920
+ case sql
921
+ when /COUNT.*username = '0records'/
922
+ yield({:v => 0})
923
+ when /COUNT.*username = '2records'/
924
+ yield({:v => 2})
925
+ when /COUNT.*username = '1record'/
926
+ yield({:v => 1})
927
+ when /username = '1record'/
928
+ if sql =~ /password = 'anothertest'/
929
+ yield({:id => 3, :username => "1record", :password => "anothertest"})
930
+ else
931
+ yield({:id => 4, :username => "1record", :password => "test"})
932
+ end
933
+ end
934
+ end
935
+ })
936
+
937
+ @user = User.new(:username => "2records", :password => "anothertest")
938
+ @user.should_not be_valid
939
+ @user.errors.full_messages.should == ['username and password is already taken']
940
+
941
+ @user = User.new(:username => "1record", :password => "anothertest")
942
+ @user.should_not be_valid
943
+ @user.errors.full_messages.should == ['username and password is already taken']
944
+
945
+ @user = User.load(:id=>4, :username => "1record", :password => "anothertest")
946
+ @user.should_not be_valid
947
+ @user.errors.full_messages.should == ['username and password is already taken']
948
+
949
+ @user = User.load(:id=>3, :username => "1record", :password => "test")
950
+ @user.should_not be_valid
951
+ @user.errors.full_messages.should == ['username and password is already taken']
952
+
953
+ @user = User.load(:id=>3, :username => "1record", :password => "anothertest")
954
+ @user.should be_valid
955
+ @user.errors.full_messages.should == []
956
+
957
+ @user = User.new(:username => "0records", :password => "anothertest")
958
+ @user.should be_valid
959
+ @user.errors.full_messages.should == []
960
+ end
961
+
962
+ deprec_specify "should have a validates block that contains multiple validations" do
963
+ class ::Person < Sequel::Model
964
+ validations.clear
965
+ validates do
966
+ format_of :first_name, :with => /^[a-zA-Z]+$/
967
+ length_of :first_name, :maximum => 30
968
+ end
969
+ end
970
+
971
+ Person.validations[:first_name].size.should == 2
972
+
973
+ @person = Person.new :first_name => "Lancelot99"
974
+ @person.valid?.should be_false
975
+
976
+ @person2 = Person.new :first_name => "Wayne"
977
+ @person2.valid?.should be_true
978
+ end
979
+
980
+ deprec_specify "should allow 'longhand' validations direcly within the model." do
981
+ lambda {
982
+ class ::Person < Sequel::Model
983
+ validations.clear
984
+ validates_length_of :first_name, :maximum => 30
985
+ end
986
+ }.should_not raise_error
987
+ Person.validations.length.should eql(1)
988
+ end
989
+
990
+ deprec_specify "should define a has_validations? method which returns true if the model has validations, false otherwise" do
991
+ class ::Person < Sequel::Model
992
+ validations.clear
993
+ validates do
994
+ format_of :first_name, :with => /\w+/
995
+ length_of :first_name, :maximum => 30
996
+ end
997
+ end
998
+
999
+ class ::Smurf < Person
1000
+ validations.clear
1001
+ end
1002
+
1003
+ Person.should have_validations
1004
+ Smurf.should_not have_validations
1005
+ end
1006
+
1007
+ deprec_specify "should validate correctly instances initialized with string keys" do
1008
+ class ::Can < Sequel::Model
1009
+ columns :id, :name
1010
+
1011
+ validates_length_of :name, :minimum => 4
1012
+ end
1013
+
1014
+ Can.new('name' => 'ab').should_not be_valid
1015
+ Can.new('name' => 'abcd').should be_valid
1016
+ end
1017
+
1018
+ end
1019
+
1020
+ describe "Model#save!" do
1021
+ before do
1022
+ @c = Class.new(Sequel::Model(:people)) do
1023
+ def columns; [:id]; end
1024
+
1025
+ validates_each :id do |o, a, v|
1026
+ o.errors[a] << 'blah' unless v == 5
1027
+ end
1028
+ end
1029
+ @m = @c.load(:id => 4)
1030
+ MODEL_DB.reset
1031
+ end
1032
+
1033
+ deprec_specify "should save regardless of validations" do
1034
+ @m.should_not be_valid
1035
+ @m.save!
1036
+ MODEL_DB.sqls.should == ['UPDATE people SET id = 4 WHERE (id = 4)']
1037
+ end
1038
+ end
1039
+
1040
+ describe "Model#save" do
1041
+ before do
1042
+ @c = Class.new(Sequel::Model(:people)) do
1043
+ columns :id
1044
+
1045
+ def validate
1046
+ errors[:id] << 'blah' unless id == 5
1047
+ end
1048
+ end
1049
+ @m = @c.load(:id => 4)
1050
+ MODEL_DB.reset
1051
+ end
1052
+
1053
+ specify "should save only if validations pass" do
1054
+ @m.raise_on_save_failure = false
1055
+ @m.should_not be_valid
1056
+ @m.save
1057
+ MODEL_DB.sqls.should be_empty
1058
+
1059
+ @m.id = 5
1060
+ @m.should be_valid
1061
+ @m.save.should_not be_false
1062
+ MODEL_DB.sqls.should == ['UPDATE people SET id = 5 WHERE (id = 5)']
1063
+ end
1064
+
1065
+ specify "should skip validations if the :validate=>false option is used" do
1066
+ @m.raise_on_save_failure = false
1067
+ @m.should_not be_valid
1068
+ @m.save(:validate=>false)
1069
+ MODEL_DB.sqls.should == ['UPDATE people SET id = 4 WHERE (id = 4)']
1070
+ end
1071
+
1072
+ specify "should raise error if validations fail and raise_on_save_faiure is true" do
1073
+ proc{@m.save}.should raise_error(Sequel::ValidationFailed)
1074
+ end
1075
+
1076
+ specify "should return nil if validations fail and raise_on_save_faiure is false" do
1077
+ @m.raise_on_save_failure = false
1078
+ @m.save.should == nil
1079
+ end
1080
+ end