sequel 2.2.0 → 2.3.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 +1551 -4
- data/README +306 -19
- data/Rakefile +84 -56
- data/bin/sequel +106 -0
- data/doc/cheat_sheet.rdoc +225 -0
- data/doc/dataset_filtering.rdoc +182 -0
- data/lib/sequel_core.rb +136 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +54 -0
- data/lib/sequel_core/adapters/ado.rb +80 -0
- data/lib/sequel_core/adapters/db2.rb +148 -0
- data/lib/sequel_core/adapters/dbi.rb +117 -0
- data/lib/sequel_core/adapters/informix.rb +78 -0
- data/lib/sequel_core/adapters/jdbc.rb +186 -0
- data/lib/sequel_core/adapters/jdbc/mysql.rb +55 -0
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +66 -0
- data/lib/sequel_core/adapters/jdbc/sqlite.rb +47 -0
- data/lib/sequel_core/adapters/mysql.rb +231 -0
- data/lib/sequel_core/adapters/odbc.rb +155 -0
- data/lib/sequel_core/adapters/odbc_mssql.rb +106 -0
- data/lib/sequel_core/adapters/openbase.rb +64 -0
- data/lib/sequel_core/adapters/oracle.rb +170 -0
- data/lib/sequel_core/adapters/postgres.rb +199 -0
- data/lib/sequel_core/adapters/shared/mysql.rb +275 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +351 -0
- data/lib/sequel_core/adapters/shared/sqlite.rb +146 -0
- data/lib/sequel_core/adapters/sqlite.rb +138 -0
- data/lib/sequel_core/connection_pool.rb +194 -0
- data/lib/sequel_core/core_ext.rb +203 -0
- data/lib/sequel_core/core_sql.rb +184 -0
- data/lib/sequel_core/database.rb +471 -0
- data/lib/sequel_core/database/schema.rb +156 -0
- data/lib/sequel_core/dataset.rb +457 -0
- data/lib/sequel_core/dataset/callback.rb +13 -0
- data/lib/sequel_core/dataset/convenience.rb +245 -0
- data/lib/sequel_core/dataset/pagination.rb +96 -0
- data/lib/sequel_core/dataset/query.rb +41 -0
- data/lib/sequel_core/dataset/schema.rb +15 -0
- data/lib/sequel_core/dataset/sql.rb +889 -0
- data/lib/sequel_core/deprecated.rb +26 -0
- data/lib/sequel_core/exceptions.rb +42 -0
- data/lib/sequel_core/migration.rb +187 -0
- data/lib/sequel_core/object_graph.rb +216 -0
- data/lib/sequel_core/pretty_table.rb +71 -0
- data/lib/sequel_core/schema.rb +2 -0
- data/lib/sequel_core/schema/generator.rb +239 -0
- data/lib/sequel_core/schema/sql.rb +325 -0
- data/lib/sequel_core/sql.rb +812 -0
- data/lib/sequel_model.rb +5 -1
- data/lib/sequel_model/association_reflection.rb +3 -8
- data/lib/sequel_model/base.rb +15 -10
- data/lib/sequel_model/inflector.rb +3 -5
- data/lib/sequel_model/plugins.rb +1 -1
- data/lib/sequel_model/record.rb +11 -3
- data/lib/sequel_model/schema.rb +4 -4
- data/lib/sequel_model/validations.rb +6 -1
- data/spec/adapters/ado_spec.rb +17 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +764 -0
- data/spec/adapters/oracle_spec.rb +222 -0
- data/spec/adapters/postgres_spec.rb +441 -0
- data/spec/adapters/spec_helper.rb +7 -0
- data/spec/adapters/sqlite_spec.rb +400 -0
- data/spec/integration/dataset_test.rb +51 -0
- data/spec/integration/eager_loader_test.rb +702 -0
- data/spec/integration/schema_test.rb +102 -0
- data/spec/integration/spec_helper.rb +44 -0
- data/spec/integration/type_test.rb +43 -0
- data/spec/rcov.opts +2 -0
- data/spec/sequel_core/connection_pool_spec.rb +363 -0
- data/spec/sequel_core/core_ext_spec.rb +156 -0
- data/spec/sequel_core/core_sql_spec.rb +427 -0
- data/spec/sequel_core/database_spec.rb +964 -0
- data/spec/sequel_core/dataset_spec.rb +2977 -0
- data/spec/sequel_core/expression_filters_spec.rb +346 -0
- data/spec/sequel_core/migration_spec.rb +261 -0
- data/spec/sequel_core/object_graph_spec.rb +234 -0
- data/spec/sequel_core/pretty_table_spec.rb +58 -0
- data/spec/sequel_core/schema_generator_spec.rb +122 -0
- data/spec/sequel_core/schema_spec.rb +497 -0
- data/spec/sequel_core/spec_helper.rb +51 -0
- data/spec/{association_reflection_spec.rb → sequel_model/association_reflection_spec.rb} +6 -6
- data/spec/{associations_spec.rb → sequel_model/associations_spec.rb} +47 -18
- data/spec/{base_spec.rb → sequel_model/base_spec.rb} +2 -1
- data/spec/{caching_spec.rb → sequel_model/caching_spec.rb} +0 -0
- data/spec/{dataset_methods_spec.rb → sequel_model/dataset_methods_spec.rb} +13 -1
- data/spec/{eager_loading_spec.rb → sequel_model/eager_loading_spec.rb} +75 -14
- data/spec/{hooks_spec.rb → sequel_model/hooks_spec.rb} +4 -4
- data/spec/sequel_model/inflector_spec.rb +119 -0
- data/spec/{model_spec.rb → sequel_model/model_spec.rb} +30 -11
- data/spec/{plugins_spec.rb → sequel_model/plugins_spec.rb} +0 -0
- data/spec/{record_spec.rb → sequel_model/record_spec.rb} +47 -6
- data/spec/{schema_spec.rb → sequel_model/schema_spec.rb} +18 -4
- data/spec/{spec_helper.rb → sequel_model/spec_helper.rb} +3 -2
- data/spec/{validations_spec.rb → sequel_model/validations_spec.rb} +37 -17
- data/spec/spec_config.rb +9 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +110 -37
- data/spec/inflector_spec.rb +0 -34
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
2
|
+
|
|
3
|
+
describe "Database schema parser" do
|
|
4
|
+
before do
|
|
5
|
+
INTEGRATION_DB.create_table!(:items){integer :number}
|
|
6
|
+
clear_sqls
|
|
7
|
+
end
|
|
8
|
+
after do
|
|
9
|
+
INTEGRATION_DB.drop_table(:items) if INTEGRATION_DB.table_exists?(:items)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
specify "should be a hash with table_names as symbols" do
|
|
13
|
+
schema = INTEGRATION_DB.schema(nil, :reload=>true)
|
|
14
|
+
schema.should be_a_kind_of(Hash)
|
|
15
|
+
schema.should include(:items)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
specify "should not issue an sql query if the schema has been loaded unless :reload is true" do
|
|
19
|
+
INTEGRATION_DB.schema(nil, :reload=>true)
|
|
20
|
+
clear_sqls
|
|
21
|
+
INTEGRATION_DB.schema
|
|
22
|
+
sqls_should_be
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
specify "should give the same result for a single table regardless of whether schema was called for a single table" do
|
|
26
|
+
INTEGRATION_DB.schema(:items, :reload=>true).should == INTEGRATION_DB.schema(nil, :reload=>true)[:items]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
specify "should return the schema correctly" do
|
|
30
|
+
INTEGRATION_DB.create_table!(:items){integer :number}
|
|
31
|
+
schema = INTEGRATION_DB.schema(:items, :reload=>true)
|
|
32
|
+
schema.should be_a_kind_of(Array)
|
|
33
|
+
schema.length.should == 1
|
|
34
|
+
col = schema.first
|
|
35
|
+
col.should be_a_kind_of(Array)
|
|
36
|
+
col.length.should == 2
|
|
37
|
+
col.first.should == :number
|
|
38
|
+
col_info = col.last
|
|
39
|
+
col_info.should be_a_kind_of(Hash)
|
|
40
|
+
col_info[:type].should == :integer
|
|
41
|
+
clear_sqls
|
|
42
|
+
INTEGRATION_DB.schema(:items)
|
|
43
|
+
sqls_should_be
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "Database schema modifiers" do
|
|
48
|
+
before do
|
|
49
|
+
INTEGRATION_DB.create_table!(:items){integer :number}
|
|
50
|
+
@ds = INTEGRATION_DB[:items]
|
|
51
|
+
@ds.insert([10])
|
|
52
|
+
clear_sqls
|
|
53
|
+
end
|
|
54
|
+
after do
|
|
55
|
+
INTEGRATION_DB.drop_table(:items) if INTEGRATION_DB.table_exists?(:items)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
specify "should create tables correctly" do
|
|
59
|
+
INTEGRATION_DB.table_exists?(:items).should == true
|
|
60
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number]
|
|
61
|
+
@ds.columns!.should == [:number]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
specify "should add columns to tables correctly" do
|
|
65
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number]
|
|
66
|
+
@ds.columns!.should == [:number]
|
|
67
|
+
INTEGRATION_DB.alter_table(:items){add_column :name, :text}
|
|
68
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :name]
|
|
69
|
+
@ds.columns!.should == [:number, :name]
|
|
70
|
+
unless INTEGRATION_DB.url =~ /sqlite/
|
|
71
|
+
INTEGRATION_DB.alter_table(:items){add_primary_key :id}
|
|
72
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :name, :id]
|
|
73
|
+
@ds.columns!.should == [:number, :name, :id]
|
|
74
|
+
unless INTEGRATION_DB.url =~ /mysql/
|
|
75
|
+
INTEGRATION_DB.alter_table(:items){add_foreign_key :item_id, :items}
|
|
76
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :name, :id, :item_id]
|
|
77
|
+
@ds.columns!.should == [:number, :name, :id, :item_id]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
specify "should remove columns from tables correctly" do
|
|
83
|
+
INTEGRATION_DB.create_table!(:items) do
|
|
84
|
+
primary_key :id
|
|
85
|
+
text :name
|
|
86
|
+
integer :number
|
|
87
|
+
foreign_key :item_id, :items
|
|
88
|
+
end
|
|
89
|
+
@ds.insert(:number=>10)
|
|
90
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:id, :name, :number, :item_id]
|
|
91
|
+
@ds.columns!.should == [:id, :name, :number, :item_id]
|
|
92
|
+
INTEGRATION_DB.drop_column(:items, :number)
|
|
93
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:id, :name, :item_id]
|
|
94
|
+
@ds.columns!.should == [:id, :name, :item_id]
|
|
95
|
+
INTEGRATION_DB.drop_column(:items, :name)
|
|
96
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:id, :item_id]
|
|
97
|
+
@ds.columns!.should == [:id, :item_id]
|
|
98
|
+
INTEGRATION_DB.drop_column(:items, :item_id)
|
|
99
|
+
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:id]
|
|
100
|
+
@ds.columns!.should == [:id]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
unless Object.const_defined?('Sequel')
|
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
|
|
4
|
+
require 'sequel'
|
|
5
|
+
end
|
|
6
|
+
require File.join(File.dirname(__FILE__), '../spec_config.rb')
|
|
7
|
+
|
|
8
|
+
$sqls = []
|
|
9
|
+
def clear_sqls
|
|
10
|
+
$sqls.clear
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if defined?(INTEGRATION_DB) || defined?(INTEGRATION_URL) || ENV['SEQUEL_INTEGRATION_URL']
|
|
14
|
+
unless defined?(INTEGRATION_DB)
|
|
15
|
+
url = defined?(INTEGRATION_URL) ? INTEGRATION_URL : ENV['SEQUEL_INTEGRATION_URL']
|
|
16
|
+
INTEGRATION_DB = Sequel.connect(url)
|
|
17
|
+
end
|
|
18
|
+
class Spec::Example::ExampleGroup
|
|
19
|
+
def sqls_should_be(*args)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
sql_logger = Object.new
|
|
24
|
+
def sql_logger.info(str)
|
|
25
|
+
$sqls << str
|
|
26
|
+
end
|
|
27
|
+
INTEGRATION_DB = Sequel.sqlite('', :loggers=>[sql_logger], :quote_identifiers=>false)
|
|
28
|
+
class Spec::Example::ExampleGroup
|
|
29
|
+
def sqls_should_be(*sqls)
|
|
30
|
+
sqls.zip($sqls).each do |should_be, is|
|
|
31
|
+
case should_be
|
|
32
|
+
when String
|
|
33
|
+
is.should == should_be
|
|
34
|
+
when Regexp
|
|
35
|
+
is.should =~ should_be
|
|
36
|
+
else
|
|
37
|
+
raise ArgumentError, "need String or RegExp"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
$sqls.length.should == sqls.length
|
|
41
|
+
clear_sqls
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
2
|
+
|
|
3
|
+
describe "Supported types" do
|
|
4
|
+
def create_items_table_with_column(name, type)
|
|
5
|
+
INTEGRATION_DB.create_table!(:items){column name, type}
|
|
6
|
+
INTEGRATION_DB[:items]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
specify "should support NULL correctly" do
|
|
10
|
+
ds = create_items_table_with_column(:number, :integer)
|
|
11
|
+
ds.insert(:number => nil)
|
|
12
|
+
ds.all.should == [{:number=>nil}]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
specify "should support integer type" do
|
|
16
|
+
ds = create_items_table_with_column(:number, :integer)
|
|
17
|
+
ds.insert(:number => 2)
|
|
18
|
+
ds.all.should == [{:number=>2}]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
specify "should support varchar type" do
|
|
22
|
+
ds = create_items_table_with_column(:name, 'varchar(255)'.lit)
|
|
23
|
+
ds.insert(:name => 'Test User')
|
|
24
|
+
ds.all.should == [{:name=>'Test User'}]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
specify "should support date type" do
|
|
28
|
+
ds = create_items_table_with_column(:dat, :date)
|
|
29
|
+
d = Date.today
|
|
30
|
+
ds.insert(:dat => d)
|
|
31
|
+
x = ds.first[:dat]
|
|
32
|
+
x = x.iso8601.to_date if Time === x
|
|
33
|
+
x.to_s.should == d.to_s
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
specify "should support time type" do
|
|
37
|
+
ds = create_items_table_with_column(:tim, :time)
|
|
38
|
+
t = Time.now
|
|
39
|
+
ds.insert(:tim => t)
|
|
40
|
+
x = ds.first[:tim]
|
|
41
|
+
[t.strftime('%H:%M:%S'), t.iso8601].should include(x.respond_to?(:strftime) ? x.strftime('%H:%M:%S') : x.to_s)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
|
2
|
+
CONNECTION_POOL_DEFAULTS = {:pool_timeout=>5, :pool_sleep_time=>0.001,
|
|
3
|
+
:pool_reuse_connections=>:allow, :pool_convert_exceptions=>true, :max_connections=>4}
|
|
4
|
+
|
|
5
|
+
context "An empty ConnectionPool" do
|
|
6
|
+
setup do
|
|
7
|
+
@cpool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
specify "should have no available connections" do
|
|
11
|
+
@cpool.available_connections.should == []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
specify "should have no allocated connections" do
|
|
15
|
+
@cpool.allocated.should == {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
specify "should have a created_count of zero" do
|
|
19
|
+
@cpool.created_count.should == 0
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "A connection pool handling connections" do
|
|
24
|
+
setup do
|
|
25
|
+
@max_size = 2
|
|
26
|
+
@cpool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>@max_size)) {:got_connection}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
specify "#hold should increment #created_count" do
|
|
30
|
+
@cpool.hold do
|
|
31
|
+
@cpool.created_count.should == 1
|
|
32
|
+
@cpool.hold {@cpool.hold {@cpool.created_count.should == 1}}
|
|
33
|
+
Thread.new{@cpool.hold {@cpool.created_count.should == 2}}.join
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
specify "#hold should add the connection to the #allocated array" do
|
|
38
|
+
@cpool.hold do
|
|
39
|
+
@cpool.allocated.size.should == 1
|
|
40
|
+
|
|
41
|
+
@cpool.allocated.should == {Thread.current=>:got_connection}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
specify "#hold should yield a new connection" do
|
|
46
|
+
@cpool.hold {|conn| conn.should == :got_connection}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
specify "a connection should be de-allocated after it has been used in #hold" do
|
|
50
|
+
@cpool.hold {}
|
|
51
|
+
@cpool.allocated.size.should == 0
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
specify "#hold should return the value of its block" do
|
|
55
|
+
@cpool.hold {:block_return}.should == :block_return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
specify "#make_new should not make more than max_size connections" do
|
|
59
|
+
@cpool.send(:make_new).should == :got_connection
|
|
60
|
+
@cpool.send(:make_new).should == :got_connection
|
|
61
|
+
@cpool.send(:make_new).should == nil
|
|
62
|
+
@cpool.created_count.should == 2
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class DummyConnection
|
|
67
|
+
@@value = 0
|
|
68
|
+
def initialize
|
|
69
|
+
@@value += 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def value
|
|
73
|
+
@@value
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context "ConnectionPool#hold" do
|
|
78
|
+
setup do
|
|
79
|
+
@pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS) {DummyConnection.new}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
specify "should pass the result of the connection maker proc to the supplied block" do
|
|
83
|
+
res = nil
|
|
84
|
+
@pool.hold {|c| res = c}
|
|
85
|
+
res.should be_a_kind_of(DummyConnection)
|
|
86
|
+
res.value.should == 1
|
|
87
|
+
@pool.hold {|c| res = c}
|
|
88
|
+
res.should be_a_kind_of(DummyConnection)
|
|
89
|
+
res.value.should == 1 # the connection maker is invoked only once
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
specify "should be re-entrant by the same thread" do
|
|
93
|
+
cc = nil
|
|
94
|
+
@pool.hold {|c| @pool.hold {|c| @pool.hold {|c| cc = c}}}
|
|
95
|
+
cc.should be_a_kind_of(DummyConnection)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
specify "should catch exceptions and reraise them" do
|
|
99
|
+
proc {@pool.hold {|c| c.foobar}}.should raise_error(NoMethodError)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
specify "should handle Exception errors (normally not caught by rescue)" do
|
|
103
|
+
err = nil
|
|
104
|
+
begin
|
|
105
|
+
@pool.hold {raise Exception}
|
|
106
|
+
rescue => e
|
|
107
|
+
err = e
|
|
108
|
+
end
|
|
109
|
+
err.should be_a_kind_of(RuntimeError)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context "ConnectionPool#connection_proc" do
|
|
114
|
+
setup do
|
|
115
|
+
@pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
specify "should be nil if no block is supplied to the pool" do
|
|
119
|
+
@pool.connection_proc.should be_nil
|
|
120
|
+
proc {@pool.hold {}}.should raise_error
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
specify "should be mutable" do
|
|
124
|
+
@pool.connection_proc = proc {'herro'}
|
|
125
|
+
res = nil
|
|
126
|
+
proc {@pool.hold {|c| res = c}}.should_not raise_error
|
|
127
|
+
res.should == 'herro'
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "A connection pool with a max size of 1" do
|
|
132
|
+
setup do
|
|
133
|
+
@invoked_count = 0
|
|
134
|
+
@pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>1)) {@invoked_count += 1; 'herro'}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
specify "should let only one thread access the connection at any time" do
|
|
138
|
+
cc,c1, c2 = nil
|
|
139
|
+
|
|
140
|
+
t1 = Thread.new {@pool.hold {|c| cc = c; c1 = c.dup; while c == 'herro';sleep 0.1;end}}
|
|
141
|
+
sleep 0.2
|
|
142
|
+
cc.should == 'herro'
|
|
143
|
+
c1.should == 'herro'
|
|
144
|
+
|
|
145
|
+
t2 = Thread.new {@pool.hold {|c| c2 = c.dup; while c == 'hello';sleep 0.1;end}}
|
|
146
|
+
sleep 0.2
|
|
147
|
+
|
|
148
|
+
# connection held by t1
|
|
149
|
+
t1.should be_alive
|
|
150
|
+
t2.should be_alive
|
|
151
|
+
|
|
152
|
+
cc.should == 'herro'
|
|
153
|
+
c1.should == 'herro'
|
|
154
|
+
c2.should be_nil
|
|
155
|
+
|
|
156
|
+
@pool.available_connections.should be_empty
|
|
157
|
+
@pool.allocated.should == {t1=>cc}
|
|
158
|
+
|
|
159
|
+
cc.gsub!('rr', 'll')
|
|
160
|
+
sleep 0.5
|
|
161
|
+
|
|
162
|
+
# connection held by t2
|
|
163
|
+
t1.should_not be_alive
|
|
164
|
+
t2.should be_alive
|
|
165
|
+
|
|
166
|
+
c2.should == 'hello'
|
|
167
|
+
|
|
168
|
+
@pool.available_connections.should be_empty
|
|
169
|
+
@pool.allocated.should == {t2=>cc}
|
|
170
|
+
|
|
171
|
+
cc.gsub!('ll', 'rr')
|
|
172
|
+
sleep 0.5
|
|
173
|
+
|
|
174
|
+
#connection released
|
|
175
|
+
t2.should_not be_alive
|
|
176
|
+
|
|
177
|
+
cc.should == 'herro'
|
|
178
|
+
|
|
179
|
+
@invoked_count.should == 1
|
|
180
|
+
@pool.size.should == 1
|
|
181
|
+
@pool.available_connections.should == [cc]
|
|
182
|
+
@pool.allocated.should be_empty
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
specify "should let the same thread reenter #hold" do
|
|
186
|
+
c1, c2, c3 = nil
|
|
187
|
+
@pool.hold do |c|
|
|
188
|
+
c1 = c
|
|
189
|
+
@pool.hold do |c|
|
|
190
|
+
c2 = c
|
|
191
|
+
@pool.hold do |c|
|
|
192
|
+
c3 = c
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
c1.should == 'herro'
|
|
197
|
+
c2.should == 'herro'
|
|
198
|
+
c3.should == 'herro'
|
|
199
|
+
|
|
200
|
+
@invoked_count.should == 1
|
|
201
|
+
@pool.size.should == 1
|
|
202
|
+
@pool.available_connections.size.should == 1
|
|
203
|
+
@pool.allocated.should be_empty
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
context "A connection pool with a max size of 5" do
|
|
208
|
+
setup do
|
|
209
|
+
@invoked_count = 0
|
|
210
|
+
@pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {@invoked_count += 1}
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
specify "should let five threads simultaneously access separate connections" do
|
|
214
|
+
cc = {}
|
|
215
|
+
threads = []
|
|
216
|
+
stop = nil
|
|
217
|
+
|
|
218
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.1;end}}; sleep 0.1}
|
|
219
|
+
sleep 0.2
|
|
220
|
+
threads.each {|t| t.should be_alive}
|
|
221
|
+
cc.size.should == 5
|
|
222
|
+
@invoked_count.should == 5
|
|
223
|
+
@pool.size.should == 5
|
|
224
|
+
@pool.available_connections.should be_empty
|
|
225
|
+
i = 0
|
|
226
|
+
h = {}
|
|
227
|
+
threads.each{|t| h[t] = (i+=1)}
|
|
228
|
+
@pool.allocated.should == h
|
|
229
|
+
|
|
230
|
+
threads[0].raise "your'e dead"
|
|
231
|
+
sleep 0.1
|
|
232
|
+
threads[3].raise "your'e dead too"
|
|
233
|
+
|
|
234
|
+
sleep 0.1
|
|
235
|
+
|
|
236
|
+
@pool.available_connections.should == [1, 4]
|
|
237
|
+
@pool.allocated.should == {threads[1]=>2, threads[2]=>3, threads[4]=>5}
|
|
238
|
+
|
|
239
|
+
stop = true
|
|
240
|
+
sleep 0.2
|
|
241
|
+
|
|
242
|
+
@pool.available_connections.size.should == 5
|
|
243
|
+
@pool.allocated.should be_empty
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
specify "should block threads until a connection becomes available" do
|
|
247
|
+
cc = {}
|
|
248
|
+
threads = []
|
|
249
|
+
stop = nil
|
|
250
|
+
|
|
251
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.1;end}}; sleep 0.1}
|
|
252
|
+
sleep 0.2
|
|
253
|
+
threads.each {|t| t.should be_alive}
|
|
254
|
+
@pool.available_connections.should be_empty
|
|
255
|
+
|
|
256
|
+
3.times {|i| threads << Thread.new {@pool.hold {|c| cc[i + 5] = c}}}
|
|
257
|
+
|
|
258
|
+
sleep 0.2
|
|
259
|
+
threads[5].should be_alive
|
|
260
|
+
threads[6].should be_alive
|
|
261
|
+
threads[7].should be_alive
|
|
262
|
+
cc.size.should == 5
|
|
263
|
+
cc[5].should be_nil
|
|
264
|
+
cc[6].should be_nil
|
|
265
|
+
cc[7].should be_nil
|
|
266
|
+
|
|
267
|
+
stop = true
|
|
268
|
+
sleep 0.3
|
|
269
|
+
|
|
270
|
+
threads.each {|t| t.should_not be_alive}
|
|
271
|
+
|
|
272
|
+
@pool.size.should == 5
|
|
273
|
+
@invoked_count.should == 5
|
|
274
|
+
@pool.available_connections.size.should == 5
|
|
275
|
+
@pool.allocated.should be_empty
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
context "ConnectionPool#disconnect" do
|
|
280
|
+
setup do
|
|
281
|
+
@count = 0
|
|
282
|
+
@pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {{:id => @count += 1}}
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
specify "should invoke the given block for each available connection" do
|
|
286
|
+
threads = []
|
|
287
|
+
stop = nil
|
|
288
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
|
|
289
|
+
while @pool.size < 5
|
|
290
|
+
sleep 0.2
|
|
291
|
+
end
|
|
292
|
+
stop = true
|
|
293
|
+
sleep 1
|
|
294
|
+
threads.each {|t| t.join}
|
|
295
|
+
|
|
296
|
+
@pool.size.should == 5
|
|
297
|
+
@pool.available_connections.size.should == 5
|
|
298
|
+
@pool.available_connections.each {|c| c[:id].should_not be_nil}
|
|
299
|
+
conns = []
|
|
300
|
+
@pool.disconnect {|c| conns << c}
|
|
301
|
+
conns.size.should == 5
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
specify "should remove all available connections" do
|
|
305
|
+
threads = []
|
|
306
|
+
stop = nil
|
|
307
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
|
|
308
|
+
while @pool.size < 5
|
|
309
|
+
sleep 0.2
|
|
310
|
+
end
|
|
311
|
+
stop = true
|
|
312
|
+
sleep 1
|
|
313
|
+
threads.each {|t| t.join}
|
|
314
|
+
|
|
315
|
+
@pool.size.should == 5
|
|
316
|
+
@pool.disconnect
|
|
317
|
+
@pool.size.should == 0
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
specify "should not touch connections in use" do
|
|
321
|
+
threads = []
|
|
322
|
+
stop = nil
|
|
323
|
+
5.times {|i| threads << Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
|
|
324
|
+
while @pool.size < 5
|
|
325
|
+
sleep 0.2
|
|
326
|
+
end
|
|
327
|
+
stop = true
|
|
328
|
+
sleep 1
|
|
329
|
+
threads.each {|t| t.join}
|
|
330
|
+
|
|
331
|
+
@pool.size.should == 5
|
|
332
|
+
|
|
333
|
+
@pool.hold do |conn|
|
|
334
|
+
@pool.available_connections.size.should == 4
|
|
335
|
+
@pool.available_connections.each {|c| c.should_not be(conn)}
|
|
336
|
+
conns = []
|
|
337
|
+
@pool.disconnect {|c| conns << c}
|
|
338
|
+
conns.size.should == 4
|
|
339
|
+
end
|
|
340
|
+
@pool.size.should == 1
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
context "SingleThreadedPool" do
|
|
345
|
+
setup do
|
|
346
|
+
@pool = Sequel::SingleThreadedPool.new(CONNECTION_POOL_DEFAULTS){1234}
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
specify "should provide a #hold method" do
|
|
350
|
+
conn = nil
|
|
351
|
+
@pool.hold {|c| conn = c}
|
|
352
|
+
conn.should == 1234
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
specify "should provide a #disconnect method" do
|
|
356
|
+
@pool.hold {|c|}
|
|
357
|
+
@pool.conn.should == 1234
|
|
358
|
+
conn = nil
|
|
359
|
+
@pool.disconnect {|c| conn = c}
|
|
360
|
+
conn.should == 1234
|
|
361
|
+
@pool.conn.should be_nil
|
|
362
|
+
end
|
|
363
|
+
end
|