sequel 2.12.0 → 3.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.
- data/CHANGELOG +62 -0
- data/README.rdoc +3 -3
- data/Rakefile +7 -0
- data/doc/advanced_associations.rdoc +44 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/lib/sequel/adapters/amalgalite.rb +208 -0
- data/lib/sequel/adapters/db2.rb +3 -0
- data/lib/sequel/adapters/dbi.rb +9 -0
- data/lib/sequel/adapters/do.rb +0 -4
- data/lib/sequel/adapters/firebird.rb +16 -18
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +24 -20
- data/lib/sequel/adapters/jdbc/h2.rb +15 -4
- data/lib/sequel/adapters/mysql.rb +4 -8
- data/lib/sequel/adapters/odbc.rb +0 -4
- data/lib/sequel/adapters/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/mssql.rb +16 -5
- data/lib/sequel/adapters/shared/mysql.rb +87 -86
- data/lib/sequel/adapters/shared/oracle.rb +92 -3
- data/lib/sequel/adapters/shared/postgres.rb +85 -29
- data/lib/sequel/adapters/shared/progress.rb +8 -3
- data/lib/sequel/adapters/shared/sqlite.rb +53 -23
- data/lib/sequel/adapters/sqlite.rb +4 -7
- data/lib/sequel/adapters/utils/unsupported.rb +3 -3
- data/lib/sequel/connection_pool.rb +18 -25
- data/lib/sequel/core.rb +2 -21
- data/lib/sequel/database.rb +60 -44
- data/lib/sequel/database/schema_generator.rb +26 -31
- data/lib/sequel/database/schema_methods.rb +8 -3
- data/lib/sequel/database/schema_sql.rb +114 -28
- data/lib/sequel/dataset.rb +14 -41
- data/lib/sequel/dataset/convenience.rb +31 -54
- data/lib/sequel/dataset/graph.rb +7 -13
- data/lib/sequel/dataset/sql.rb +43 -54
- data/lib/sequel/extensions/inflector.rb +0 -5
- data/lib/sequel/extensions/schema_dumper.rb +238 -0
- data/lib/sequel/metaprogramming.rb +0 -20
- data/lib/sequel/model.rb +1 -2
- data/lib/sequel/model/base.rb +18 -16
- data/lib/sequel/model/inflections.rb +6 -9
- data/lib/sequel/plugins/caching.rb +0 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +2 -0
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/firebird_spec.rb +35 -8
- data/spec/adapters/mysql_spec.rb +173 -266
- data/spec/adapters/oracle_spec.rb +13 -0
- data/spec/adapters/postgres_spec.rb +127 -227
- data/spec/adapters/sqlite_spec.rb +13 -171
- data/spec/core/connection_pool_spec.rb +15 -4
- data/spec/core/core_sql_spec.rb +14 -170
- data/spec/core/database_spec.rb +50 -132
- data/spec/core/dataset_spec.rb +47 -930
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/core/schema_generator_spec.rb +37 -45
- data/spec/core/schema_spec.rb +26 -16
- data/spec/core/spec_helper.rb +0 -25
- data/spec/extensions/inflector_spec.rb +0 -3
- data/spec/extensions/schema_dumper_spec.rb +292 -0
- data/spec/extensions/serialization_spec.rb +9 -0
- data/spec/extensions/single_table_inheritance_spec.rb +6 -1
- data/spec/extensions/spec_helper.rb +1 -3
- data/spec/extensions/validation_helpers_spec.rb +4 -4
- data/spec/integration/database_test.rb +18 -0
- data/spec/integration/dataset_test.rb +112 -1
- data/spec/integration/eager_loader_test.rb +70 -9
- data/spec/integration/prepared_statement_test.rb +2 -2
- data/spec/integration/schema_test.rb +76 -27
- data/spec/integration/spec_helper.rb +0 -14
- data/spec/integration/transaction_test.rb +27 -0
- data/spec/model/associations_spec.rb +0 -36
- data/spec/model/base_spec.rb +18 -123
- data/spec/model/hooks_spec.rb +2 -235
- data/spec/model/inflector_spec.rb +15 -115
- data/spec/model/model_spec.rb +0 -120
- data/spec/model/plugins_spec.rb +0 -70
- data/spec/model/record_spec.rb +35 -93
- data/spec/model/spec_helper.rb +0 -27
- data/spec/model/validations_spec.rb +0 -931
- metadata +9 -14
- data/lib/sequel/deprecated.rb +0 -593
- data/lib/sequel/deprecated_migration.rb +0 -91
- data/lib/sequel/model/deprecated.rb +0 -204
- data/lib/sequel/model/deprecated_hooks.rb +0 -103
- data/lib/sequel/model/deprecated_inflector.rb +0 -335
- data/lib/sequel/model/deprecated_validations.rb +0 -388
- data/spec/core/core_ext_spec.rb +0 -156
- data/spec/core/migration_spec.rb +0 -263
- data/spec/core/pretty_table_spec.rb +0 -58
- data/spec/model/caching_spec.rb +0 -217
- data/spec/model/schema_spec.rb +0 -92
@@ -1,5 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
2
|
|
3
|
+
require 'yaml'
|
4
|
+
|
3
5
|
describe "Serialization plugin" do
|
4
6
|
before do
|
5
7
|
@c = Class.new(Sequel::Model(:items)) do
|
@@ -49,6 +51,8 @@ describe "Serialization plugin" do
|
|
49
51
|
o = @c.first
|
50
52
|
o.id.should == 1
|
51
53
|
o.abc.should == 1
|
54
|
+
o.abc.should == 1
|
55
|
+
o.def.should == "hello"
|
52
56
|
o.def.should == "hello"
|
53
57
|
|
54
58
|
o.update(:abc => 23)
|
@@ -69,6 +73,8 @@ describe "Serialization plugin" do
|
|
69
73
|
o = @c.first
|
70
74
|
o.id.should == 1
|
71
75
|
o.abc.should == 1
|
76
|
+
o.abc.should == 1
|
77
|
+
o.def.should == "hello"
|
72
78
|
o.def.should == "hello"
|
73
79
|
|
74
80
|
o.update(:abc => 23)
|
@@ -89,6 +95,8 @@ describe "Serialization plugin" do
|
|
89
95
|
o = Class.new(@c).first
|
90
96
|
o.id.should == 1
|
91
97
|
o.abc.should == 1
|
98
|
+
o.abc.should == 1
|
99
|
+
o.def.should == "hello"
|
92
100
|
o.def.should == "hello"
|
93
101
|
|
94
102
|
o.update(:abc => 23)
|
@@ -103,6 +111,7 @@ describe "Serialization plugin" do
|
|
103
111
|
o = @c.load(:id => 1, :abc => "--- 1\n", :def => "--- hello\n")
|
104
112
|
o.abc = 23
|
105
113
|
o.deserialized_values.length.should == 1
|
114
|
+
o.abc.should == 23
|
106
115
|
o.refresh
|
107
116
|
o.deserialized_values.length.should == 0
|
108
117
|
end
|
@@ -4,7 +4,7 @@ describe Sequel::Model, "#sti_key" do
|
|
4
4
|
before do
|
5
5
|
class ::StiTest < Sequel::Model
|
6
6
|
def kind=(x); self[:kind] = x; end
|
7
|
-
def
|
7
|
+
def _refresh(x); end
|
8
8
|
plugin :single_table_inheritance, :kind
|
9
9
|
end
|
10
10
|
class ::StiTestSub1 < StiTest
|
@@ -14,6 +14,11 @@ describe Sequel::Model, "#sti_key" do
|
|
14
14
|
@ds = StiTest.dataset
|
15
15
|
MODEL_DB.reset
|
16
16
|
end
|
17
|
+
|
18
|
+
specify "should have simple_table = nil" do
|
19
|
+
StiTest.simple_table.should == nil
|
20
|
+
StiTestSub1.simple_table.should == nil
|
21
|
+
end
|
17
22
|
|
18
23
|
it "should return rows with the correct class based on the polymorphic_key value" do
|
19
24
|
def @ds.fetch_rows(sql)
|
@@ -10,7 +10,7 @@ end
|
|
10
10
|
|
11
11
|
Sequel.virtual_row_instance_eval = true
|
12
12
|
|
13
|
-
extensions = %w'string_date_time inflector pagination query pretty_table blank migration'
|
13
|
+
extensions = %w'string_date_time inflector pagination query pretty_table blank migration schema_dumper'
|
14
14
|
plugins = {:hook_class_methods=>[], :schema=>[], :validation_class_methods=>[]}
|
15
15
|
|
16
16
|
extensions.each{|e| require "sequel/extensions/#{e}"}
|
@@ -70,12 +70,10 @@ end
|
|
70
70
|
|
71
71
|
class << Sequel::Model
|
72
72
|
alias orig_columns columns
|
73
|
-
alias orig_str_columns str_columns
|
74
73
|
def columns(*cols)
|
75
74
|
return if cols.empty?
|
76
75
|
define_method(:columns){cols}
|
77
76
|
@dataset.instance_variable_set(:@columns, cols) if @dataset
|
78
|
-
define_method(:str_columns){cols.map{|x|x.to_s.freeze}}
|
79
77
|
def_column_accessor(*cols)
|
80
78
|
@columns = cols
|
81
79
|
@db_schema = {}
|
@@ -241,10 +241,10 @@ describe "Sequel::Plugins::ValidationHelpers" do
|
|
241
241
|
|
242
242
|
@user = @c.load(:id=>1, :username => "0records", :password => "anothertest")
|
243
243
|
@user.should be_valid
|
244
|
-
MODEL_DB.sqls.last.should == "SELECT COUNT(*) FROM items WHERE ((username = '0records') AND (id != 1)) LIMIT 1"
|
244
|
+
MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (id != 1)) LIMIT 1"
|
245
245
|
@user = @c.new(:username => "0records", :password => "anothertest")
|
246
246
|
@user.should be_valid
|
247
|
-
MODEL_DB.sqls.last.should == "SELECT COUNT(*) FROM items WHERE (username = '0records') LIMIT 1"
|
247
|
+
MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE (username = '0records') LIMIT 1"
|
248
248
|
end
|
249
249
|
|
250
250
|
it "should support validates_unique with multiple attributes" do
|
@@ -283,9 +283,9 @@ describe "Sequel::Plugins::ValidationHelpers" do
|
|
283
283
|
|
284
284
|
@user = @c.load(:id=>1, :username => "0records", :password => "anothertest")
|
285
285
|
@user.should be_valid
|
286
|
-
MODEL_DB.sqls.last.should == "SELECT COUNT(*) FROM items WHERE (((username = '0records') AND (password = 'anothertest')) AND (id != 1)) LIMIT 1"
|
286
|
+
MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE (((username = '0records') AND (password = 'anothertest')) AND (id != 1)) LIMIT 1"
|
287
287
|
@user = @c.new(:username => "0records", :password => "anothertest")
|
288
288
|
@user.should be_valid
|
289
|
-
MODEL_DB.sqls.last.should == "SELECT COUNT(*) FROM items WHERE ((username = '0records') AND (password = 'anothertest')) LIMIT 1"
|
289
|
+
MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = 'anothertest')) LIMIT 1"
|
290
290
|
end
|
291
291
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe Sequel::Database do
|
4
|
+
specify "should provide disconnect functionality" do
|
5
|
+
INTEGRATION_DB.test_connection
|
6
|
+
INTEGRATION_DB.pool.size.should == 1
|
7
|
+
INTEGRATION_DB.disconnect
|
8
|
+
INTEGRATION_DB.pool.size.should == 0
|
9
|
+
end
|
10
|
+
|
11
|
+
specify "should raise Sequel::DatabaseError on invalid SQL" do
|
12
|
+
proc{INTEGRATION_DB << "SELECT"}.should raise_error(Sequel::DatabaseError)
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "should not have the connection pool swallow non-StandardError based exceptions" do
|
16
|
+
proc{INTEGRATION_DB.pool.hold{raise Interrupt, "test"}}.should raise_error(Interrupt)
|
17
|
+
end
|
18
|
+
end
|
@@ -4,7 +4,7 @@ describe "Simple Dataset operations" do
|
|
4
4
|
before do
|
5
5
|
INTEGRATION_DB.create_table!(:items) do
|
6
6
|
primary_key :id
|
7
|
-
|
7
|
+
Integer :number
|
8
8
|
end
|
9
9
|
@ds = INTEGRATION_DB[:items]
|
10
10
|
@ds.insert(:number=>10)
|
@@ -14,6 +14,15 @@ describe "Simple Dataset operations" do
|
|
14
14
|
INTEGRATION_DB.drop_table(:items)
|
15
15
|
end
|
16
16
|
|
17
|
+
specify "should support sequential primary keys" do
|
18
|
+
@ds << {:number=>20}
|
19
|
+
@ds << {:number=>30}
|
20
|
+
@ds.order(:number).all.should == [
|
21
|
+
{:id => 1, :number=>10},
|
22
|
+
{:id => 2, :number=>20},
|
23
|
+
{:id => 3, :number=>30} ]
|
24
|
+
end
|
25
|
+
|
17
26
|
specify "should insert with a primary key specified" do
|
18
27
|
@ds.insert(:id=>100, :number=>20)
|
19
28
|
sqls_should_be(/INSERT INTO items \((number, id|id, number)\) VALUES \((100, 20|20, 100)\)/)
|
@@ -55,6 +64,108 @@ describe "Simple Dataset operations" do
|
|
55
64
|
end
|
56
65
|
end
|
57
66
|
|
67
|
+
describe Sequel::Dataset do
|
68
|
+
before do
|
69
|
+
INTEGRATION_DB.create_table!(:test) do
|
70
|
+
String :name
|
71
|
+
Integer :value
|
72
|
+
end
|
73
|
+
@d = INTEGRATION_DB[:test]
|
74
|
+
clear_sqls
|
75
|
+
end
|
76
|
+
after do
|
77
|
+
INTEGRATION_DB.drop_table(:test)
|
78
|
+
end
|
79
|
+
|
80
|
+
specify "should return the correct record count" do
|
81
|
+
@d.count.should == 0
|
82
|
+
@d << {:name => 'abc', :value => 123}
|
83
|
+
@d << {:name => 'abc', :value => 456}
|
84
|
+
@d << {:name => 'def', :value => 789}
|
85
|
+
@d.count.should == 3
|
86
|
+
end
|
87
|
+
|
88
|
+
specify "should return the correct records" do
|
89
|
+
@d.to_a.should == []
|
90
|
+
@d << {:name => 'abc', :value => 123}
|
91
|
+
@d << {:name => 'abc', :value => 456}
|
92
|
+
@d << {:name => 'def', :value => 789}
|
93
|
+
|
94
|
+
@d.order(:value).to_a.should == [
|
95
|
+
{:name => 'abc', :value => 123},
|
96
|
+
{:name => 'abc', :value => 456},
|
97
|
+
{:name => 'def', :value => 789}
|
98
|
+
]
|
99
|
+
end
|
100
|
+
|
101
|
+
specify "should update records correctly" do
|
102
|
+
@d << {:name => 'abc', :value => 123}
|
103
|
+
@d << {:name => 'abc', :value => 456}
|
104
|
+
@d << {:name => 'def', :value => 789}
|
105
|
+
@d.filter(:name => 'abc').update(:value => 530)
|
106
|
+
@d[:name => 'def'][:value].should == 789
|
107
|
+
@d.filter(:value => 530).count.should == 2
|
108
|
+
end
|
109
|
+
|
110
|
+
specify "should delete records correctly" do
|
111
|
+
@d << {:name => 'abc', :value => 123}
|
112
|
+
@d << {:name => 'abc', :value => 456}
|
113
|
+
@d << {:name => 'def', :value => 789}
|
114
|
+
@d.filter(:name => 'abc').delete
|
115
|
+
@d.count.should == 1
|
116
|
+
@d.first[:name].should == 'def'
|
117
|
+
end
|
118
|
+
|
119
|
+
specify "should be able to literalize booleans" do
|
120
|
+
proc {@d.literal(true)}.should_not raise_error
|
121
|
+
proc {@d.literal(false)}.should_not raise_error
|
122
|
+
end
|
123
|
+
|
124
|
+
specify "should correctly escape strings" do
|
125
|
+
INTEGRATION_DB.get("\\dingo".as(:a)) == "\\dingo"
|
126
|
+
end
|
127
|
+
|
128
|
+
specify "should correctly escape strings with quotes" do
|
129
|
+
INTEGRATION_DB.get("\\'dingo".as(:a)) == "\\'dingo"
|
130
|
+
end
|
131
|
+
|
132
|
+
specify "should properly escape binary data" do
|
133
|
+
INTEGRATION_DB.get("\1\2\3".to_sequel_blob.as(:a)) == "\1\2\3"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "An SQLite dataset" do
|
138
|
+
before do
|
139
|
+
INTEGRATION_DB.create_table! :items do
|
140
|
+
primary_key :id
|
141
|
+
Integer :value
|
142
|
+
end
|
143
|
+
@d = INTEGRATION_DB[:items]
|
144
|
+
@d << {:value => 123}
|
145
|
+
@d << {:value => 456}
|
146
|
+
@d << {:value => 789}
|
147
|
+
end
|
148
|
+
after do
|
149
|
+
INTEGRATION_DB.drop_table(:items)
|
150
|
+
end
|
151
|
+
|
152
|
+
specify "should correctly return avg" do
|
153
|
+
@d.avg(:value).to_i.should == 456
|
154
|
+
end
|
155
|
+
|
156
|
+
specify "should correctly return sum" do
|
157
|
+
@d.sum(:value).to_i.should == 1368
|
158
|
+
end
|
159
|
+
|
160
|
+
specify "should correctly return max" do
|
161
|
+
@d.max(:value).to_i.should == 789
|
162
|
+
end
|
163
|
+
|
164
|
+
specify "should correctly return min" do
|
165
|
+
@d.min(:value).to_i.should == 123
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
58
169
|
describe "Simple Dataset operations" do
|
59
170
|
before do
|
60
171
|
INTEGRATION_DB.create_table!(:items) do
|
@@ -154,7 +154,7 @@ describe "Association Extensions" do
|
|
154
154
|
INTEGRATION_DB.create_table!(:authorships) do
|
155
155
|
primary_key :id
|
156
156
|
foreign_key :author_id, :authors
|
157
|
-
|
157
|
+
String :name
|
158
158
|
end
|
159
159
|
class ::Authorship < Sequel::Model
|
160
160
|
many_to_one :author
|
@@ -170,26 +170,26 @@ describe "Association Extensions" do
|
|
170
170
|
|
171
171
|
it "should allow methods to be called on the dataset method" do
|
172
172
|
Authorship.count.should == 0
|
173
|
-
sqls_should_be('SELECT COUNT(*) FROM authorships LIMIT 1')
|
173
|
+
sqls_should_be('SELECT COUNT(*) AS \'count\' FROM authorships LIMIT 1')
|
174
174
|
authorship = @author.authorships_dataset.find_or_create_by_name('Bob')
|
175
175
|
sqls_should_be("SELECT * FROM authorships WHERE ((authorships.author_id = 1) AND (name = 'Bob')) LIMIT 1",
|
176
176
|
/INSERT INTO authorships \((author_id, name|name, author_id)\) VALUES \((1, 'Bob'|'Bob', 1)\)/,
|
177
177
|
"SELECT * FROM authorships WHERE (id = 1) LIMIT 1")
|
178
178
|
Authorship.count.should == 1
|
179
179
|
Authorship.first.should == authorship
|
180
|
-
sqls_should_be('SELECT COUNT(*) FROM authorships LIMIT 1', "SELECT * FROM authorships LIMIT 1")
|
180
|
+
sqls_should_be('SELECT COUNT(*) AS \'count\' FROM authorships LIMIT 1', "SELECT * FROM authorships LIMIT 1")
|
181
181
|
authorship.name.should == 'Bob'
|
182
182
|
authorship.author_id.should == @author.id
|
183
183
|
@author.authorships_dataset.find_or_create_by_name('Bob').should == authorship
|
184
184
|
sqls_should_be("SELECT * FROM authorships WHERE ((authorships.author_id = 1) AND (name = 'Bob')) LIMIT 1")
|
185
185
|
Authorship.count.should == 1
|
186
|
-
sqls_should_be('SELECT COUNT(*) FROM authorships LIMIT 1')
|
186
|
+
sqls_should_be('SELECT COUNT(*) AS \'count\' FROM authorships LIMIT 1')
|
187
187
|
authorship2 = @author.authorships_dataset.find_or_create(:name=>'Jim')
|
188
188
|
sqls_should_be("SELECT * FROM authorships WHERE ((authorships.author_id = 1) AND (name = 'Jim')) LIMIT 1",
|
189
189
|
/INSERT INTO authorships \((author_id, name|name, author_id)\) VALUES \((1, 'Jim'|'Jim', 1)\)/,
|
190
190
|
"SELECT * FROM authorships WHERE (id = 2) LIMIT 1")
|
191
191
|
Authorship.count.should == 2
|
192
|
-
sqls_should_be('SELECT COUNT(*) FROM authorships LIMIT 1')
|
192
|
+
sqls_should_be('SELECT COUNT(*) AS \'count\' FROM authorships LIMIT 1')
|
193
193
|
Authorship.order(:name).map(:name).should == ['Bob', 'Jim']
|
194
194
|
sqls_should_be('SELECT * FROM authorships ORDER BY name')
|
195
195
|
authorship2.name.should == 'Jim'
|
@@ -364,8 +364,8 @@ describe "Polymorphic Associations" do
|
|
364
364
|
INTEGRATION_DB.instance_variable_set(:@schemas, nil)
|
365
365
|
INTEGRATION_DB.create_table!(:assets) do
|
366
366
|
primary_key :id
|
367
|
-
|
368
|
-
|
367
|
+
Integer :attachable_id
|
368
|
+
String :attachable_type
|
369
369
|
end
|
370
370
|
class ::Asset < Sequel::Model
|
371
371
|
m = method(:constantize)
|
@@ -536,7 +536,7 @@ describe "many_to_one/one_to_many not referencing primary key" do
|
|
536
536
|
INTEGRATION_DB.instance_variable_set(:@schemas, nil)
|
537
537
|
INTEGRATION_DB.create_table!(:clients) do
|
538
538
|
primary_key :id
|
539
|
-
|
539
|
+
String :name
|
540
540
|
end
|
541
541
|
class ::Client < Sequel::Model
|
542
542
|
one_to_many :invoices, :reciprocal=>:client, \
|
@@ -570,7 +570,7 @@ describe "many_to_one/one_to_many not referencing primary key" do
|
|
570
570
|
|
571
571
|
INTEGRATION_DB.create_table!(:invoices) do
|
572
572
|
primary_key :id
|
573
|
-
|
573
|
+
String :client_name
|
574
574
|
end
|
575
575
|
class ::Invoice < Sequel::Model
|
576
576
|
many_to_one :client, :key=>:client_name, \
|
@@ -681,3 +681,64 @@ describe "many_to_one/one_to_many not referencing primary key" do
|
|
681
681
|
@invoice2.client_name.should == nil
|
682
682
|
end
|
683
683
|
end
|
684
|
+
|
685
|
+
describe "statistics associations" do
|
686
|
+
before do
|
687
|
+
INTEGRATION_DB.create_table!(:projects) do
|
688
|
+
primary_key :id
|
689
|
+
String :name
|
690
|
+
end
|
691
|
+
class ::Project < Sequel::Model
|
692
|
+
many_to_one :ticket_hours, :read_only=>true, :key=>:id,
|
693
|
+
:dataset=>proc{Ticket.filter(:project_id=>id).select{sum(hours).as(hours)}},
|
694
|
+
:eager_loader=>(proc do |kh, projects, a|
|
695
|
+
projects.each{|p| p.associations[:ticket_hours] = nil}
|
696
|
+
Ticket.filter(:project_id=>kh[:id].keys).
|
697
|
+
group(:project_id).
|
698
|
+
select{[project_id.as(project_id), sum(hours).as(hours)]}.
|
699
|
+
all do |t|
|
700
|
+
p = kh[:id][t.values.delete(:project_id)].first
|
701
|
+
p.associations[:ticket_hours] = t
|
702
|
+
end
|
703
|
+
end)
|
704
|
+
def ticket_hours
|
705
|
+
if s = super
|
706
|
+
s[:hours]
|
707
|
+
end
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
INTEGRATION_DB.create_table!(:tickets) do
|
712
|
+
primary_key :id
|
713
|
+
foreign_key :project_id, :projects
|
714
|
+
Integer :hours
|
715
|
+
end
|
716
|
+
class ::Ticket < Sequel::Model
|
717
|
+
many_to_one :project
|
718
|
+
end
|
719
|
+
|
720
|
+
@project1 = Project.create(:name=>'X')
|
721
|
+
@project2 = Project.create(:name=>'Y')
|
722
|
+
@ticket1 = Ticket.create(:project=>@project1, :hours=>1)
|
723
|
+
@ticket2 = Ticket.create(:project=>@project1, :hours=>10)
|
724
|
+
@ticket3 = Ticket.create(:project=>@project2, :hours=>2)
|
725
|
+
@ticket4 = Ticket.create(:project=>@project2, :hours=>20)
|
726
|
+
clear_sqls
|
727
|
+
end
|
728
|
+
after do
|
729
|
+
INTEGRATION_DB.drop_table :tickets, :projects
|
730
|
+
Object.send(:remove_const, :Project)
|
731
|
+
Object.send(:remove_const, :Ticket)
|
732
|
+
end
|
733
|
+
|
734
|
+
it "should give the correct sum of ticket hours for each project" do
|
735
|
+
@project1.ticket_hours.to_i.should == 11
|
736
|
+
@project2.ticket_hours.to_i.should == 22
|
737
|
+
end
|
738
|
+
|
739
|
+
it "should give the correct sum of ticket hours for each project when eager loading" do
|
740
|
+
p1, p2 = Project.order(:name).eager(:ticket_hours).all
|
741
|
+
p1.ticket_hours.to_i.should == 11
|
742
|
+
p2.ticket_hours.to_i.should == 22
|
743
|
+
end
|
744
|
+
end
|
@@ -76,7 +76,7 @@ describe "Prepared Statements and Bound Arguments" do
|
|
76
76
|
INTEGRATION_DB.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
|
77
77
|
@ds.filter(:number=>@ds.ba(:$n)).prepare(:first, :select_n)
|
78
78
|
INTEGRATION_DB.call(:select_n, :n=>10).should == {:id=>1, :number=>10}
|
79
|
-
if INTEGRATION_DB.
|
79
|
+
if INTEGRATION_DB.class.adapter_scheme == :jdbc and INTEGRATION_DB.database_type == :sqlite
|
80
80
|
# Work around for open prepared statements on a table not allowing the
|
81
81
|
# dropping of a table when using SQLite over JDBC
|
82
82
|
INTEGRATION_DB.synchronize{|c| c.prepared_statements[:select_n][1].close}
|
@@ -121,7 +121,7 @@ describe "Prepared Statements and Bound Arguments" do
|
|
121
121
|
INTEGRATION_DB.call(:select_n, :n=>10).should == [@c.load(:id=>1, :number=>10)]
|
122
122
|
@c.filter(:number=>@ds.ba(:$n)).prepare(:first, :select_n)
|
123
123
|
INTEGRATION_DB.call(:select_n, :n=>10).should == @c.load(:id=>1, :number=>10)
|
124
|
-
if INTEGRATION_DB.
|
124
|
+
if INTEGRATION_DB.class.adapter_scheme == :jdbc and INTEGRATION_DB.database_type == :sqlite
|
125
125
|
# Work around for open prepared statements on a table not allowing the
|
126
126
|
# dropping of a table when using SQLite over JDBC
|
127
127
|
INTEGRATION_DB.synchronize{|c| c.prepared_statements[:select_n][1].close}
|
@@ -19,20 +19,13 @@ describe "Database schema parser" do
|
|
19
19
|
INTEGRATION_DB.identifier_output_method = :reverse
|
20
20
|
INTEGRATION_DB.identifier_input_method = :reverse
|
21
21
|
INTEGRATION_DB.default_schema = nil if INTEGRATION_DB.default_schema
|
22
|
-
INTEGRATION_DB.create_table!(:items){
|
22
|
+
INTEGRATION_DB.create_table!(:items){Integer :number}
|
23
23
|
INTEGRATION_DB.schema(:items, :reload=>true).should be_a_kind_of(Array)
|
24
24
|
INTEGRATION_DB.schema(:items, :reload=>true).first.first.should == :number
|
25
25
|
end
|
26
26
|
|
27
|
-
deprec_specify "should be a hash with table_names as symbols" do
|
28
|
-
INTEGRATION_DB.create_table!(:items){integer :number}
|
29
|
-
schema = INTEGRATION_DB.schema(nil, :reload=>true)
|
30
|
-
schema.should be_a_kind_of(Hash)
|
31
|
-
schema[:items].should_not == nil
|
32
|
-
end
|
33
|
-
|
34
27
|
specify "should not issue an sql query if the schema has been loaded unless :reload is true" do
|
35
|
-
INTEGRATION_DB.create_table!(:items){
|
28
|
+
INTEGRATION_DB.create_table!(:items){Integer :number}
|
36
29
|
INTEGRATION_DB.schema(:items, :reload=>true)
|
37
30
|
clear_sqls
|
38
31
|
INTEGRATION_DB.schema(:items)
|
@@ -42,17 +35,12 @@ describe "Database schema parser" do
|
|
42
35
|
sqls_should_be "PRAGMA table_info('items')"
|
43
36
|
end
|
44
37
|
|
45
|
-
deprec_specify "should give the same result for a single table regardless of whether schema was called for a single table" do
|
46
|
-
INTEGRATION_DB.create_table!(:items){integer :number}
|
47
|
-
INTEGRATION_DB.schema(:items, :reload=>true).should == INTEGRATION_DB.schema(nil, :reload=>true)[:items]
|
48
|
-
end
|
49
|
-
|
50
38
|
specify "should raise an error when the table doesn't exist" do
|
51
39
|
proc{INTEGRATION_DB.schema(:no_table)}.should raise_error(Sequel::Error)
|
52
40
|
end
|
53
41
|
|
54
42
|
specify "should return the schema correctly" do
|
55
|
-
INTEGRATION_DB.create_table!(:items){
|
43
|
+
INTEGRATION_DB.create_table!(:items){Integer :number}
|
56
44
|
schema = INTEGRATION_DB.schema(:items, :reload=>true)
|
57
45
|
schema.should be_a_kind_of(Array)
|
58
46
|
schema.length.should == 1
|
@@ -69,35 +57,96 @@ describe "Database schema parser" do
|
|
69
57
|
end
|
70
58
|
|
71
59
|
specify "should parse primary keys from the schema properly" do
|
72
|
-
INTEGRATION_DB.create_table!(:items){
|
60
|
+
INTEGRATION_DB.create_table!(:items){Integer :number}
|
73
61
|
INTEGRATION_DB.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == []
|
74
62
|
INTEGRATION_DB.create_table!(:items){primary_key :number}
|
75
63
|
INTEGRATION_DB.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == [:number]
|
76
|
-
INTEGRATION_DB.create_table!(:items){
|
64
|
+
INTEGRATION_DB.create_table!(:items){Integer :number1; Integer :number2; primary_key [:number1, :number2]}
|
77
65
|
INTEGRATION_DB.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == [:number1, :number2]
|
78
66
|
end
|
79
67
|
|
80
68
|
specify "should parse NULL/NOT NULL from the schema properly" do
|
81
|
-
INTEGRATION_DB.create_table!(:items){
|
69
|
+
INTEGRATION_DB.create_table!(:items){Integer :number, :null=>true}
|
82
70
|
INTEGRATION_DB.schema(:items).first.last[:allow_null].should == true
|
83
|
-
INTEGRATION_DB.create_table!(:items){
|
71
|
+
INTEGRATION_DB.create_table!(:items){Integer :number, :null=>false}
|
84
72
|
INTEGRATION_DB.schema(:items).first.last[:allow_null].should == false
|
85
73
|
end
|
86
74
|
|
87
75
|
specify "should parse defaults from the schema properly" do
|
88
|
-
INTEGRATION_DB.create_table!(:items){
|
76
|
+
INTEGRATION_DB.create_table!(:items){Integer :number}
|
89
77
|
INTEGRATION_DB.schema(:items).first.last[:default].should == nil
|
90
|
-
INTEGRATION_DB.create_table!(:items){
|
78
|
+
INTEGRATION_DB.create_table!(:items){Integer :number, :default=>0}
|
91
79
|
INTEGRATION_DB.schema(:items).first.last[:default].to_s.should == "0"
|
92
|
-
INTEGRATION_DB.create_table!(:items){
|
93
|
-
INTEGRATION_DB.schema(:items).first.last[:default].
|
80
|
+
INTEGRATION_DB.create_table!(:items){String :a, :default=>"blah"}
|
81
|
+
INTEGRATION_DB.schema(:items).first.last[:default].should include('blah')
|
82
|
+
end
|
83
|
+
|
84
|
+
specify "should parse types from the schema properly" do
|
85
|
+
INTEGRATION_DB.create_table!(:items){Integer :number}
|
86
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :integer
|
87
|
+
INTEGRATION_DB.create_table!(:items){Fixnum :number}
|
88
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :integer
|
89
|
+
INTEGRATION_DB.create_table!(:items){Bignum :number}
|
90
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :integer
|
91
|
+
INTEGRATION_DB.create_table!(:items){Float :number}
|
92
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :float
|
93
|
+
INTEGRATION_DB.create_table!(:items){BigDecimal :number}
|
94
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :decimal
|
95
|
+
INTEGRATION_DB.create_table!(:items){Numeric :number}
|
96
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :decimal
|
97
|
+
INTEGRATION_DB.create_table!(:items){String :number}
|
98
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :string
|
99
|
+
INTEGRATION_DB.create_table!(:items){Date :number}
|
100
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :date
|
101
|
+
INTEGRATION_DB.create_table!(:items){Time :number}
|
102
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :datetime
|
103
|
+
INTEGRATION_DB.create_table!(:items){DateTime :number}
|
104
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :datetime
|
105
|
+
INTEGRATION_DB.create_table!(:items){File :number}
|
106
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :blob
|
107
|
+
INTEGRATION_DB.create_table!(:items){TrueClass :number}
|
108
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :boolean
|
109
|
+
INTEGRATION_DB.create_table!(:items){FalseClass :number}
|
110
|
+
INTEGRATION_DB.schema(:items).first.last[:type].should == :boolean
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
if INTEGRATION_DB.respond_to?(:indexes)
|
116
|
+
describe "Database index parsing" do
|
117
|
+
after do
|
118
|
+
INTEGRATION_DB.drop_table(:items)
|
119
|
+
end
|
120
|
+
|
121
|
+
specify "should parse indexes into a hash" do
|
122
|
+
INTEGRATION_DB.create_table!(:items){Integer :n; Integer :a}
|
123
|
+
INTEGRATION_DB.indexes(:items).should == {}
|
124
|
+
INTEGRATION_DB.add_index(:items, :n)
|
125
|
+
INTEGRATION_DB.indexes(:items).should == {:items_n_index=>{:columns=>[:n], :unique=>false}}
|
126
|
+
INTEGRATION_DB.drop_index(:items, :n)
|
127
|
+
INTEGRATION_DB.indexes(:items).should == {}
|
128
|
+
INTEGRATION_DB.add_index(:items, :n, :unique=>true, :name=>:blah_blah_index)
|
129
|
+
INTEGRATION_DB.indexes(:items).should == {:blah_blah_index=>{:columns=>[:n], :unique=>true}}
|
130
|
+
INTEGRATION_DB.add_index(:items, [:n, :a])
|
131
|
+
INTEGRATION_DB.indexes(:items).should == {:blah_blah_index=>{:columns=>[:n], :unique=>true}, :items_n_a_index=>{:columns=>[:n, :a], :unique=>false}}
|
132
|
+
INTEGRATION_DB.drop_index(:items, :n, :name=>:blah_blah_index)
|
133
|
+
INTEGRATION_DB.indexes(:items).should == {:items_n_a_index=>{:columns=>[:n, :a], :unique=>false}}
|
134
|
+
INTEGRATION_DB.drop_index(:items, [:n, :a])
|
135
|
+
INTEGRATION_DB.indexes(:items).should == {}
|
136
|
+
end
|
137
|
+
|
138
|
+
specify "should not include a primary key index" do
|
139
|
+
INTEGRATION_DB.create_table!(:items){primary_key :n}
|
140
|
+
INTEGRATION_DB.indexes(:items).should == {}
|
141
|
+
INTEGRATION_DB.create_table!(:items){Integer :n; Integer :a; primary_key [:n, :a]}
|
142
|
+
INTEGRATION_DB.indexes(:items).should == {}
|
94
143
|
end
|
95
144
|
end
|
96
145
|
end
|
97
146
|
|
98
147
|
describe "Database schema modifiers" do
|
99
148
|
before do
|
100
|
-
INTEGRATION_DB.create_table!(:items){
|
149
|
+
INTEGRATION_DB.create_table!(:items){Integer :number}
|
101
150
|
@ds = INTEGRATION_DB[:items]
|
102
151
|
@ds.insert([10])
|
103
152
|
clear_sqls
|
@@ -118,7 +167,7 @@ describe "Database schema modifiers" do
|
|
118
167
|
INTEGRATION_DB.alter_table(:items){add_column :name, :text}
|
119
168
|
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :name]
|
120
169
|
@ds.columns!.should == [:number, :name]
|
121
|
-
unless INTEGRATION_DB.
|
170
|
+
unless INTEGRATION_DB.database_type == :sqlite
|
122
171
|
INTEGRATION_DB.alter_table(:items){add_primary_key :id}
|
123
172
|
INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :name, :id]
|
124
173
|
@ds.columns!.should == [:number, :name, :id]
|
@@ -131,8 +180,8 @@ describe "Database schema modifiers" do
|
|
131
180
|
specify "should remove columns from tables correctly" do
|
132
181
|
INTEGRATION_DB.create_table!(:items) do
|
133
182
|
primary_key :id
|
134
|
-
|
135
|
-
|
183
|
+
String :name
|
184
|
+
Integer :number
|
136
185
|
foreign_key :item_id, :items
|
137
186
|
end
|
138
187
|
@ds.insert(:number=>10)
|