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