sequel 3.8.0 → 3.9.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.
- data/CHANGELOG +48 -0
- data/Rakefile +6 -28
- data/bin/sequel +7 -2
- data/doc/release_notes/3.9.0.txt +233 -0
- data/lib/sequel/adapters/ado.rb +4 -8
- data/lib/sequel/adapters/amalgalite.rb +1 -1
- data/lib/sequel/adapters/dbi.rb +3 -3
- data/lib/sequel/adapters/do.rb +7 -13
- data/lib/sequel/adapters/jdbc.rb +10 -16
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/mysql.rb +10 -23
- data/lib/sequel/adapters/odbc.rb +6 -10
- data/lib/sequel/adapters/postgres.rb +0 -5
- data/lib/sequel/adapters/shared/mssql.rb +17 -9
- data/lib/sequel/adapters/shared/mysql.rb +16 -7
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/adapters/sqlite.rb +2 -1
- data/lib/sequel/connection_pool.rb +67 -349
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +46 -15
- data/lib/sequel/database.rb +11 -9
- data/lib/sequel/dataset/convenience.rb +23 -0
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/query.rb +9 -5
- data/lib/sequel/dataset/sql.rb +87 -12
- data/lib/sequel/extensions/inflector.rb +8 -1
- data/lib/sequel/extensions/schema_dumper.rb +3 -4
- data/lib/sequel/model/associations.rb +5 -43
- data/lib/sequel/model/base.rb +9 -2
- data/lib/sequel/model/default_inflections.rb +1 -1
- data/lib/sequel/model/exceptions.rb +11 -1
- data/lib/sequel/model/inflections.rb +8 -1
- data/lib/sequel/model/plugins.rb +2 -12
- data/lib/sequel/plugins/active_model.rb +5 -0
- data/lib/sequel/plugins/association_dependencies.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/optimistic_locking.rb +65 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +14 -3
- data/lib/sequel/plugins/validation_helpers.rb +2 -2
- data/lib/sequel/sql.rb +2 -2
- data/lib/sequel/timezones.rb +2 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +19 -0
- data/spec/adapters/mysql_spec.rb +4 -0
- data/spec/adapters/postgres_spec.rb +180 -0
- data/spec/adapters/spec_helper.rb +15 -1
- data/spec/core/connection_pool_spec.rb +119 -78
- data/spec/core/database_spec.rb +41 -50
- data/spec/core/dataset_spec.rb +115 -4
- data/spec/extensions/active_model_spec.rb +40 -34
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/migration_spec.rb +43 -38
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/schema_dumper_spec.rb +4 -4
- data/spec/extensions/single_table_inheritance_spec.rb +19 -11
- data/spec/integration/dataset_test.rb +44 -1
- data/spec/integration/plugin_test.rb +39 -0
- data/spec/integration/prepared_statement_test.rb +58 -7
- data/spec/integration/spec_helper.rb +4 -0
- data/spec/model/eager_loading_spec.rb +24 -0
- data/spec/model/validations_spec.rb +5 -1
- metadata +114 -106
data/spec/core/dataset_spec.rb
CHANGED
@@ -267,6 +267,33 @@ context "A dataset with multiple tables in its FROM clause" do
|
|
267
267
|
end
|
268
268
|
end
|
269
269
|
|
270
|
+
context "Dataset#unused_table_alias" do
|
271
|
+
before do
|
272
|
+
@ds = Sequel::Dataset.new(nil).from(:test)
|
273
|
+
end
|
274
|
+
|
275
|
+
specify "should return given symbol if it hasn't already been used" do
|
276
|
+
@ds.unused_table_alias(:blah).should == :blah
|
277
|
+
end
|
278
|
+
|
279
|
+
specify "should return a symbol specifying an alias that hasn't already been used if it has already been used" do
|
280
|
+
@ds.unused_table_alias(:test).should == :test_0
|
281
|
+
@ds.from(:test, :test_0).unused_table_alias(:test).should == :test_1
|
282
|
+
@ds.from(:test, :test_0).cross_join(:test_1).unused_table_alias(:test).should == :test_2
|
283
|
+
end
|
284
|
+
|
285
|
+
specify "should return an appropriate symbol if given other forms of identifiers" do
|
286
|
+
@ds.unused_table_alias('test').should == :test_0
|
287
|
+
@ds.unused_table_alias(:b__t___test).should == :test_0
|
288
|
+
@ds.unused_table_alias(:b__test).should == :test_0
|
289
|
+
@ds.unused_table_alias(:test.qualify(:b)).should == :test_0
|
290
|
+
@ds.unused_table_alias(:b.as(:test)).should == :test_0
|
291
|
+
@ds.unused_table_alias(:b.as(:test.identifier)).should == :test_0
|
292
|
+
@ds.unused_table_alias(:b.as('test')).should == :test_0
|
293
|
+
@ds.unused_table_alias(:test.identifier).should == :test_0
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
270
297
|
context "Dataset#exists" do
|
271
298
|
before do
|
272
299
|
@ds1 = Sequel::Dataset.new(nil).from(:test)
|
@@ -419,9 +446,62 @@ context "Dataset#where" do
|
|
419
446
|
specify "should accept a subquery" do
|
420
447
|
@dataset.filter('gdp > ?', @d1.select(:avg.sql_function(:gdp))).sql.should ==
|
421
448
|
"SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
|
422
|
-
|
423
|
-
|
424
|
-
|
449
|
+
end
|
450
|
+
|
451
|
+
specify "should handle all types of IN/NOT IN queries" do
|
452
|
+
@dataset.filter(:id => @d1.select(:id)).sql.should == "SELECT * FROM test WHERE (id IN (SELECT id FROM test WHERE (region = 'Asia')))"
|
453
|
+
@dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (id != id)"
|
454
|
+
@dataset.filter(:id => [1, 2]).sql.should == "SELECT * FROM test WHERE (id IN (1, 2))"
|
455
|
+
@dataset.filter([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
|
456
|
+
@dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
|
457
|
+
@dataset.filter([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
|
458
|
+
|
459
|
+
@dataset.exclude(:id => @d1.select(:id)).sql.should == "SELECT * FROM test WHERE (id NOT IN (SELECT id FROM test WHERE (region = 'Asia')))"
|
460
|
+
@dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
461
|
+
@dataset.exclude(:id => [1, 2]).sql.should == "SELECT * FROM test WHERE (id NOT IN (1, 2))"
|
462
|
+
@dataset.exclude([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
|
463
|
+
@dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
464
|
+
@dataset.exclude([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
|
465
|
+
end
|
466
|
+
|
467
|
+
specify "should handle IN/NOT IN queries with multiple columns and an array where the database doesn't support it" do
|
468
|
+
@dataset.meta_def(:supports_multiple_column_in?){false}
|
469
|
+
@dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
|
470
|
+
@dataset.filter([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
|
471
|
+
@dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
472
|
+
@dataset.exclude([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
|
473
|
+
end
|
474
|
+
|
475
|
+
specify "should handle IN/NOT IN queries with multiple columns and a dataset where the database doesn't support it" do
|
476
|
+
@dataset.meta_def(:supports_multiple_column_in?){false}
|
477
|
+
d1 = @d1.select(:id1, :id2)
|
478
|
+
def d1.fetch_rows(sql)
|
479
|
+
@sql_used = sql
|
480
|
+
@columns = [:id1, :id2]
|
481
|
+
yield(:id1=>1, :id2=>2)
|
482
|
+
yield(:id1=>3, :id2=>4)
|
483
|
+
end
|
484
|
+
d1.instance_variable_get(:@sql_used).should == nil
|
485
|
+
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
|
486
|
+
d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
|
487
|
+
d1.instance_variable_set(:@sql_used, nil)
|
488
|
+
@dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
|
489
|
+
d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
|
490
|
+
end
|
491
|
+
|
492
|
+
specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it" do
|
493
|
+
@dataset.meta_def(:supports_multiple_column_in?){false}
|
494
|
+
d1 = @d1.select(:id1, :id2)
|
495
|
+
def d1.fetch_rows(sql)
|
496
|
+
@sql_used = sql
|
497
|
+
@columns = [:id1, :id2]
|
498
|
+
end
|
499
|
+
d1.instance_variable_get(:@sql_used).should == nil
|
500
|
+
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
|
501
|
+
d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
|
502
|
+
d1.instance_variable_set(:@sql_used, nil)
|
503
|
+
@dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
504
|
+
d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
|
425
505
|
end
|
426
506
|
|
427
507
|
specify "should accept a subquery for an EXISTS clause" do
|
@@ -832,6 +912,13 @@ context "Dataset#literal" do
|
|
832
912
|
@dataset.literal(d).should == s
|
833
913
|
end
|
834
914
|
|
915
|
+
specify "should literalize Date properly, even if to_s is overridden" do
|
916
|
+
d = Date.today
|
917
|
+
def d.to_s; "adsf" end
|
918
|
+
s = d.strftime("'%Y-%m-%d'")
|
919
|
+
@dataset.literal(d).should == s
|
920
|
+
end
|
921
|
+
|
835
922
|
specify "should literalize Time, DateTime, Date properly if SQL standard format is required" do
|
836
923
|
@dataset.meta_def(:requires_sql_standard_datetimes?){true}
|
837
924
|
|
@@ -1276,6 +1363,21 @@ context "Dataset#limit" do
|
|
1276
1363
|
specify "should include an offset if a second argument is given" do
|
1277
1364
|
@dataset.limit(6, 10).sql.should ==
|
1278
1365
|
'SELECT * FROM test LIMIT 6 OFFSET 10'
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
specify "should convert regular strings to integers" do
|
1369
|
+
@dataset.limit('6', 'a() - 1').sql.should ==
|
1370
|
+
'SELECT * FROM test LIMIT 6 OFFSET 0'
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
specify "should not convert literal strings to integers" do
|
1374
|
+
@dataset.limit('6'.lit, 'a() - 1'.lit).sql.should ==
|
1375
|
+
'SELECT * FROM test LIMIT 6 OFFSET a() - 1'
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
specify "should not convert other objects" do
|
1379
|
+
@dataset.limit(6, :a.sql_function - 1).sql.should ==
|
1380
|
+
'SELECT * FROM test LIMIT 6 OFFSET (a() - 1)'
|
1279
1381
|
end
|
1280
1382
|
|
1281
1383
|
specify "should work with fixed sql datasets" do
|
@@ -1852,6 +1954,11 @@ context "Dataset#join_table" do
|
|
1852
1954
|
proc{@d.join(:categories, :a=>:d).delete_sql}.should raise_error(Sequel::InvalidOperation)
|
1853
1955
|
proc{@d.join(:categories, :a=>:d).truncate_sql}.should raise_error(Sequel::InvalidOperation)
|
1854
1956
|
end
|
1957
|
+
|
1958
|
+
specify "should raise an error if an invalid option is passed" do
|
1959
|
+
proc{@d.join(:c, [:id], nil)}.should raise_error(Sequel::Error)
|
1960
|
+
proc{@d.join(:c, [:id], :c.qualify(:d))}.should raise_error(Sequel::Error)
|
1961
|
+
end
|
1855
1962
|
end
|
1856
1963
|
|
1857
1964
|
context "Dataset#[]=" do
|
@@ -2683,6 +2790,10 @@ context "Dataset#update_sql" do
|
|
2683
2790
|
@ds.update_sql("a = b").should == "UPDATE items SET a = b"
|
2684
2791
|
end
|
2685
2792
|
|
2793
|
+
specify "should handle implicitly qualified symbols" do
|
2794
|
+
@ds.update_sql(:items__a=>:b).should == "UPDATE items SET items.a = b"
|
2795
|
+
end
|
2796
|
+
|
2686
2797
|
specify "should accept hash with string keys" do
|
2687
2798
|
@ds.update_sql('c' => 'd').should == "UPDATE items SET c = 'd'"
|
2688
2799
|
end
|
@@ -3036,7 +3147,7 @@ context Sequel::Dataset::UnnumberedArgumentMapper do
|
|
3036
3147
|
end
|
3037
3148
|
|
3038
3149
|
specify "should submitted the SQL to the database with placeholders and bind variables" do
|
3039
|
-
@ps.each{|p| p.call(:n=>1)}
|
3150
|
+
@ps.each{|p| p.prepared_sql; p.call(:n=>1)}
|
3040
3151
|
@db.sqls.should == [["SELECT * FROM items WHERE (num = ?)", 1],
|
3041
3152
|
["SELECT * FROM items WHERE (num = ?)", 1],
|
3042
3153
|
["SELECT * FROM items WHERE (num = ?) LIMIT 1", 1],
|
@@ -1,47 +1,53 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
-
if (
|
3
|
-
require 'active_model'
|
4
|
-
true
|
5
|
-
rescue LoadError
|
6
|
-
end)
|
2
|
+
if RUBY_PLATFORM !~ /(win|w)32|java$/
|
7
3
|
describe "ActiveModel plugin" do
|
8
|
-
before do
|
9
|
-
@c = Class.new(Sequel::Model) do
|
10
|
-
def delete; end
|
11
|
-
end
|
12
|
-
@c.plugin :active_model
|
13
|
-
@m = @c.new
|
14
|
-
@o = @c.load({})
|
15
|
-
end
|
16
|
-
|
17
4
|
specify "should be compliant to the ActiveModel spec" do
|
18
5
|
s = ''
|
19
6
|
IO.popen('-') do |f|
|
20
7
|
if f
|
21
8
|
s = f.read
|
22
9
|
else
|
23
|
-
|
10
|
+
begin
|
11
|
+
require 'active_model'
|
12
|
+
rescue LoadError
|
13
|
+
puts "0 failures, 0 errors"
|
14
|
+
else
|
15
|
+
require 'test/unit'
|
16
|
+
require "test/unit/ui/console/testrunner"
|
17
|
+
class AMLintTest < Test::Unit::TestCase
|
18
|
+
def setup
|
19
|
+
@c = Class.new(Sequel::Model) do
|
20
|
+
def delete; end
|
21
|
+
end
|
22
|
+
@c.plugin :active_model
|
23
|
+
@m = @model = @c.new
|
24
|
+
@o = @c.load({})
|
25
|
+
end
|
26
|
+
include ActiveModel::Lint::Tests
|
27
|
+
|
28
|
+
def test_to_model
|
29
|
+
assert_equal @m.to_model.object_id.should, @m.object_id
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_new_record
|
33
|
+
assert_equal true, @m.new_record?
|
34
|
+
assert_equal false, @o.new_record?
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_destroyed
|
38
|
+
assert_equal false, @m.destroyed?
|
39
|
+
assert_equal false, @o.destroyed?
|
40
|
+
@m.destroy
|
41
|
+
@o.destroy
|
42
|
+
assert_equal true, @m.destroyed?
|
43
|
+
assert_equal true, @o.destroyed?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
Test::Unit::UI::Console::TestRunner.run(AMLintTest)
|
47
|
+
end
|
24
48
|
end
|
25
49
|
end
|
26
50
|
s.should =~ /0 failures, 0 errors/
|
27
51
|
end
|
28
|
-
|
29
|
-
specify "to_model should return self" do
|
30
|
-
@m.to_model.object_id.should == @m.object_id
|
31
|
-
end
|
32
|
-
|
33
|
-
specify "new_record? should be aliased to new" do
|
34
|
-
@m.new_record?.should == true
|
35
|
-
@o.new_record?.should == false
|
36
|
-
end
|
37
|
-
|
38
|
-
specify "new_record? should be aliased to new" do
|
39
|
-
@m.destroyed?.should == false
|
40
|
-
@o.destroyed?.should == false
|
41
|
-
@m.destroy
|
42
|
-
@o.destroy
|
43
|
-
@m.destroyed?.should == true
|
44
|
-
@o.destroyed?.should == true
|
45
|
-
end
|
46
52
|
end
|
47
|
-
end
|
53
|
+
end
|
@@ -158,13 +158,17 @@ context "Sequel::Migrator" do
|
|
158
158
|
end
|
159
159
|
@db = dbc.new
|
160
160
|
|
161
|
-
|
162
|
-
|
163
|
-
File.open(
|
164
|
-
File.open(
|
165
|
-
|
166
|
-
File.open(
|
167
|
-
|
161
|
+
@dirname = "migrate_#{$$}"
|
162
|
+
Dir.mkdir(@dirname)
|
163
|
+
File.open("#{@dirname}/001_create_sessions.rb", 'w') {|f| f << MIGRATION_001}
|
164
|
+
File.open("#{@dirname}/002_create_nodes.rb", 'w') {|f| f << MIGRATION_002}
|
165
|
+
File.open("#{@dirname}/003_create_users.rb", 'w') {|f| f << MIGRATION_003}
|
166
|
+
File.open("#{@dirname}/005_5_create_attributes.rb", 'w') {|f| f << MIGRATION_005}
|
167
|
+
|
168
|
+
@alt_dirname = "migrate_alt_#{$$}"
|
169
|
+
Dir.mkdir(@alt_dirname)
|
170
|
+
File.open("#{@alt_dirname}/001_create_alt_basic.rb", 'w') {|f| f << ALT_MIGRATION_001}
|
171
|
+
File.open("#{@alt_dirname}/003_create_alt_advanced.rb", 'w') {|f| f << ALT_MIGRATION_003}
|
168
172
|
end
|
169
173
|
|
170
174
|
after do
|
@@ -175,47 +179,48 @@ context "Sequel::Migrator" do
|
|
175
179
|
Object.send(:remove_const, "CreateAltBasic") if Object.const_defined?("CreateAltBasic")
|
176
180
|
Object.send(:remove_const, "CreateAltAdvanced") if Object.const_defined?("CreateAltAdvanced")
|
177
181
|
|
178
|
-
File.delete(
|
179
|
-
File.delete(
|
180
|
-
File.delete(
|
181
|
-
File.delete(
|
182
|
-
|
183
|
-
File.delete("
|
184
|
-
|
182
|
+
File.delete("#{@dirname}/001_create_sessions.rb")
|
183
|
+
File.delete("#{@dirname}/002_create_nodes.rb")
|
184
|
+
File.delete("#{@dirname}/003_create_users.rb")
|
185
|
+
File.delete("#{@dirname}/005_5_create_attributes.rb")
|
186
|
+
Dir.rmdir(@dirname)
|
187
|
+
File.delete("#{@alt_dirname}/001_create_alt_basic.rb")
|
188
|
+
File.delete("#{@alt_dirname}/003_create_alt_advanced.rb")
|
189
|
+
Dir.rmdir(@alt_dirname)
|
185
190
|
end
|
186
191
|
|
187
192
|
specify "#migration_files should return the list of files for a specified version range" do
|
188
|
-
Sequel::Migrator.migration_files(
|
189
|
-
Sequel::Migrator.migration_files(
|
190
|
-
Sequel::Migrator.migration_files(
|
191
|
-
Sequel::Migrator.migration_files(
|
192
|
-
Sequel::Migrator.migration_files(
|
193
|
-
Sequel::Migrator.migration_files(
|
193
|
+
Sequel::Migrator.migration_files(@dirname, 1..1).map{|f| File.basename(f)}.should == ['001_create_sessions.rb']
|
194
|
+
Sequel::Migrator.migration_files(@dirname, 1..3).map{|f| File.basename(f)}.should == ['001_create_sessions.rb', '002_create_nodes.rb', '003_create_users.rb']
|
195
|
+
Sequel::Migrator.migration_files(@dirname, 3..6).map{|f| File.basename(f)}.should == ['003_create_users.rb', '005_5_create_attributes.rb']
|
196
|
+
Sequel::Migrator.migration_files(@dirname, 7..8).map{|f| File.basename(f)}.should == []
|
197
|
+
Sequel::Migrator.migration_files(@alt_dirname, 1..1).map{|f| File.basename(f)}.should == ['001_create_alt_basic.rb']
|
198
|
+
Sequel::Migrator.migration_files(@alt_dirname, 1..3).map{|f| File.basename(f)}.should == ['001_create_alt_basic.rb','003_create_alt_advanced.rb']
|
194
199
|
end
|
195
200
|
|
196
201
|
specify "#latest_migration_version should return the latest version available" do
|
197
|
-
Sequel::Migrator.latest_migration_version(
|
198
|
-
Sequel::Migrator.latest_migration_version(
|
202
|
+
Sequel::Migrator.latest_migration_version(@dirname).should == 5
|
203
|
+
Sequel::Migrator.latest_migration_version(@alt_dirname).should == 3
|
199
204
|
end
|
200
205
|
|
201
206
|
specify "#migration_classes should load the migration classes for the specified range for the up direction" do
|
202
|
-
Sequel::Migrator.migration_classes(
|
203
|
-
Sequel::Migrator.migration_classes(
|
207
|
+
Sequel::Migrator.migration_classes(@dirname, 3, 0, :up).should == [CreateSessions, CreateNodes, CreateUsers]
|
208
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 3, 0, :up).should == [CreateAltBasic, CreateAltAdvanced]
|
204
209
|
end
|
205
210
|
|
206
211
|
specify "#migration_classes should load the migration classes for the specified range for the down direction" do
|
207
|
-
Sequel::Migrator.migration_classes(
|
208
|
-
Sequel::Migrator.migration_classes(
|
212
|
+
Sequel::Migrator.migration_classes(@dirname, 0, 5, :down).should == [CreateAttributes, CreateUsers, CreateNodes, CreateSessions]
|
213
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 0, 3, :down).should == [CreateAltAdvanced, CreateAltBasic]
|
209
214
|
end
|
210
215
|
|
211
216
|
specify "#migration_classes should start from current + 1 for the up direction" do
|
212
|
-
Sequel::Migrator.migration_classes(
|
213
|
-
Sequel::Migrator.migration_classes(
|
217
|
+
Sequel::Migrator.migration_classes(@dirname, 3, 1, :up).should == [CreateNodes, CreateUsers]
|
218
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 3, 2, :up).should == [CreateAltAdvanced]
|
214
219
|
end
|
215
220
|
|
216
221
|
specify "#migration_classes should end on current + 1 for the down direction" do
|
217
|
-
Sequel::Migrator.migration_classes(
|
218
|
-
Sequel::Migrator.migration_classes(
|
222
|
+
Sequel::Migrator.migration_classes(@dirname, 2, 5, :down).should == [CreateAttributes, CreateUsers]
|
223
|
+
Sequel::Migrator.migration_classes(@alt_dirname, 2, 4, :down).should == [CreateAltAdvanced]
|
219
224
|
end
|
220
225
|
|
221
226
|
specify "#schema_info_dataset should automatically create the schema_info table" do
|
@@ -266,12 +271,12 @@ context "Sequel::Migrator" do
|
|
266
271
|
end
|
267
272
|
|
268
273
|
specify "should apply migrations correctly in the up direction" do
|
269
|
-
Sequel::Migrator.apply(@db,
|
274
|
+
Sequel::Migrator.apply(@db, @dirname, 3, 2)
|
270
275
|
@db.creates.should == [3333]
|
271
276
|
|
272
277
|
Sequel::Migrator.get_current_migration_version(@db).should == 3
|
273
278
|
|
274
|
-
Sequel::Migrator.apply(@db,
|
279
|
+
Sequel::Migrator.apply(@db, @dirname, 5)
|
275
280
|
@db.creates.should == [3333, 5555]
|
276
281
|
|
277
282
|
Sequel::Migrator.get_current_migration_version(@db).should == 5
|
@@ -286,7 +291,7 @@ context "Sequel::Migrator" do
|
|
286
291
|
end
|
287
292
|
|
288
293
|
specify "should apply migrations correctly in the down direction" do
|
289
|
-
Sequel::Migrator.apply(@db,
|
294
|
+
Sequel::Migrator.apply(@db, @dirname, 1, 5)
|
290
295
|
@db.drops.should == [5555, 3333, 2222]
|
291
296
|
|
292
297
|
Sequel::Migrator.get_current_migration_version(@db).should == 1
|
@@ -297,7 +302,7 @@ context "Sequel::Migrator" do
|
|
297
302
|
end
|
298
303
|
|
299
304
|
specify "should apply migrations up to the latest version if no target is given" do
|
300
|
-
Sequel::Migrator.apply(@db,
|
305
|
+
Sequel::Migrator.apply(@db, @dirname)
|
301
306
|
@db.creates.should == [1111, 2222, 3333, 5555]
|
302
307
|
|
303
308
|
Sequel::Migrator.get_current_migration_version(@db).should == 5
|
@@ -309,7 +314,7 @@ context "Sequel::Migrator" do
|
|
309
314
|
end
|
310
315
|
|
311
316
|
specify "should apply migrations down to 0 version correctly" do
|
312
|
-
Sequel::Migrator.apply(@db,
|
317
|
+
Sequel::Migrator.apply(@db, @dirname, 0, 5)
|
313
318
|
@db.drops.should == [5555, 3333, 2222, 1111]
|
314
319
|
|
315
320
|
Sequel::Migrator.get_current_migration_version(@db).should == 0
|
@@ -320,8 +325,8 @@ context "Sequel::Migrator" do
|
|
320
325
|
end
|
321
326
|
|
322
327
|
specify "should return the target version" do
|
323
|
-
Sequel::Migrator.apply(@db,
|
324
|
-
Sequel::Migrator.apply(@db,
|
325
|
-
Sequel::Migrator.apply(@db,
|
328
|
+
Sequel::Migrator.apply(@db, @dirname, 3, 2).should == 3
|
329
|
+
Sequel::Migrator.apply(@db, @dirname, 0).should == 0
|
330
|
+
Sequel::Migrator.apply(@db, @dirname).should == 5
|
326
331
|
end
|
327
332
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "optimistic_locking plugin" do
|
4
|
+
before do
|
5
|
+
@c = Class.new(Sequel::Model(:people)) do
|
6
|
+
end
|
7
|
+
h = {1=>{:id=>1, :name=>'John', :lock_version=>2}}
|
8
|
+
lv = @lv = "lock_version"
|
9
|
+
@c.dataset.quote_identifiers = false
|
10
|
+
@c.dataset.meta_def(:h){h}
|
11
|
+
@c.dataset.meta_def(:lv){lv}
|
12
|
+
@c.dataset.meta_def(:update) do |opts|
|
13
|
+
case update_sql(opts)
|
14
|
+
when /UPDATE people SET (name|#{lv}) = ('Jim'|'Bob'|\d+), (?:name|#{lv}) = ('Jim'|'Bob'|\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
|
15
|
+
name, nlv = $1 == 'name' ? [$2, $3] : [$3, $2]
|
16
|
+
m = h[$4.to_i]
|
17
|
+
if m && m[:lock_version] == $5.to_i
|
18
|
+
m.merge!(:name=>name.gsub("'", ''), :lock_version=>nlv.to_i)
|
19
|
+
1
|
20
|
+
else
|
21
|
+
0
|
22
|
+
end
|
23
|
+
else
|
24
|
+
puts update_sql(opts)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@c.dataset.instance_eval do
|
28
|
+
def fetch_rows(sql)
|
29
|
+
m = h[1].dup
|
30
|
+
v = m.delete(:lock_version)
|
31
|
+
m[lv.to_sym] = v
|
32
|
+
yield(m)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@c.dataset.meta_def(:delete) do
|
36
|
+
case delete_sql
|
37
|
+
when /DELETE FROM people WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
|
38
|
+
m = h[$1.to_i]
|
39
|
+
if m && m[lv.to_sym] == $2.to_i
|
40
|
+
h.delete[$1.to_i]
|
41
|
+
1
|
42
|
+
else
|
43
|
+
0
|
44
|
+
end
|
45
|
+
else
|
46
|
+
puts delete_sql
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@c.columns :id, :name, :lock_version
|
50
|
+
@c.plugin :optimistic_locking
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "should raise an error when updating a stale record" do
|
54
|
+
p1 = @c[1]
|
55
|
+
p2 = @c[1]
|
56
|
+
p1.update(:name=>'Jim')
|
57
|
+
proc{p2.update(:name=>'Bob')}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
|
58
|
+
end
|
59
|
+
|
60
|
+
specify "should raise an error when destroying a stale record" do
|
61
|
+
p1 = @c[1]
|
62
|
+
p2 = @c[1]
|
63
|
+
p1.update(:name=>'Jim')
|
64
|
+
proc{p2.destroy}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
|
65
|
+
end
|
66
|
+
|
67
|
+
specify "should not raise an error when updating the same record twice" do
|
68
|
+
p1 = @c[1]
|
69
|
+
p1.update(:name=>'Jim')
|
70
|
+
proc{p1.update(:name=>'Bob')}.should_not raise_error
|
71
|
+
end
|
72
|
+
|
73
|
+
specify "should allow changing the lock column via model.lock_column=" do
|
74
|
+
@lv.replace('lv')
|
75
|
+
@c.columns :id, :name, :lv
|
76
|
+
@c.lock_column = :lv
|
77
|
+
p1 = @c[1]
|
78
|
+
p2 = @c[1]
|
79
|
+
p1.update(:name=>'Jim')
|
80
|
+
proc{p2.update(:name=>'Bob')}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "should allow changing the lock column via plugin option" do
|
84
|
+
@lv.replace('lv')
|
85
|
+
@c.columns :id, :name, :lv
|
86
|
+
@c.plugin :optimistic_locking, :lock_column=>:lv
|
87
|
+
p1 = @c[1]
|
88
|
+
p2 = @c[1]
|
89
|
+
p1.update(:name=>'Jim')
|
90
|
+
proc{p2.destroy}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
|
91
|
+
end
|
92
|
+
|
93
|
+
specify "should work when subclassing" do
|
94
|
+
c = Class.new(@c)
|
95
|
+
p1 = c[1]
|
96
|
+
p2 = c[1]
|
97
|
+
p1.update(:name=>'Jim')
|
98
|
+
proc{p2.update(:name=>'Bob')}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
|
99
|
+
end
|
100
|
+
end
|