sequel 3.13.0 → 3.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +36 -0
- data/doc/release_notes/3.14.0.txt +118 -0
- data/lib/sequel/adapters/oracle.rb +7 -2
- data/lib/sequel/adapters/shared/mssql.rb +9 -3
- data/lib/sequel/connection_pool/sharded_threaded.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +3 -3
- data/lib/sequel/database/connecting.rb +47 -11
- data/lib/sequel/database/dataset.rb +17 -6
- data/lib/sequel/database/dataset_defaults.rb +15 -3
- data/lib/sequel/database/logging.rb +4 -3
- data/lib/sequel/database/misc.rb +33 -21
- data/lib/sequel/database/query.rb +61 -22
- data/lib/sequel/database/schema_generator.rb +108 -45
- data/lib/sequel/database/schema_methods.rb +8 -5
- data/lib/sequel/dataset/actions.rb +194 -45
- data/lib/sequel/dataset/features.rb +1 -1
- data/lib/sequel/dataset/graph.rb +51 -43
- data/lib/sequel/dataset/misc.rb +29 -5
- data/lib/sequel/dataset/mutation.rb +0 -1
- data/lib/sequel/dataset/prepared_statements.rb +14 -2
- data/lib/sequel/dataset/query.rb +268 -125
- data/lib/sequel/dataset/sql.rb +33 -44
- data/lib/sequel/extensions/migration.rb +3 -2
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/model/associations.rb +89 -87
- data/lib/sequel/model/base.rb +386 -109
- data/lib/sequel/model/errors.rb +15 -1
- data/lib/sequel/model/exceptions.rb +3 -3
- data/lib/sequel/model/inflections.rb +2 -2
- data/lib/sequel/model/plugins.rb +9 -5
- data/lib/sequel/plugins/rcte_tree.rb +43 -15
- data/lib/sequel/plugins/schema.rb +6 -5
- data/lib/sequel/plugins/serialization.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tree.rb +33 -1
- data/lib/sequel/timezones.rb +16 -10
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +36 -2
- data/spec/adapters/mysql_spec.rb +4 -4
- data/spec/adapters/postgres_spec.rb +1 -1
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/core/database_spec.rb +8 -1
- data/spec/core/dataset_spec.rb +36 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +40 -8
- data/spec/extensions/schema_spec.rb +5 -0
- data/spec/extensions/serialization_spec.rb +4 -4
- data/spec/extensions/single_table_inheritance_spec.rb +7 -0
- data/spec/extensions/tree_spec.rb +36 -0
- data/spec/integration/dataset_test.rb +19 -0
- data/spec/integration/prepared_statement_test.rb +2 -2
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/spec_helper.rb +4 -4
- data/spec/integration/timezone_test.rb +27 -21
- data/spec/model/associations_spec.rb +5 -5
- data/spec/model/dataset_methods_spec.rb +13 -0
- data/spec/model/hooks_spec.rb +31 -0
- data/spec/model/record_spec.rb +24 -7
- data/spec/model/validations_spec.rb +9 -4
- metadata +6 -4
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -89,7 +89,7 @@ context "A MySQL database" do
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
if MYSQL_DB.
|
92
|
+
if MYSQL_DB.adapter_scheme == :mysql
|
93
93
|
context "Sequel::MySQL.convert_tinyint_to_bool" do
|
94
94
|
before do
|
95
95
|
@db = MYSQL_DB
|
@@ -536,7 +536,7 @@ context "A MySQL database" do
|
|
536
536
|
end
|
537
537
|
|
538
538
|
# Socket tests should only be run if the MySQL server is on localhost
|
539
|
-
if %w'localhost 127.0.0.1 ::1'.include?(MYSQL_URI.host) and MYSQL_DB.
|
539
|
+
if %w'localhost 127.0.0.1 ::1'.include?(MYSQL_URI.host) and MYSQL_DB.adapter_scheme == :mysql
|
540
540
|
context "A MySQL database" do
|
541
541
|
specify "should accept a socket option" do
|
542
542
|
db = Sequel.mysql(MYSQL_DB.opts[:database], :host => 'localhost', :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
|
@@ -895,7 +895,7 @@ context "MySQL::Dataset#complex_expression_sql" do
|
|
895
895
|
end
|
896
896
|
end
|
897
897
|
|
898
|
-
unless MYSQL_DB.
|
898
|
+
unless MYSQL_DB.adapter_scheme == :do
|
899
899
|
context "MySQL Stored Procedures" do
|
900
900
|
before do
|
901
901
|
MYSQL_DB.create_table(:items){Integer :id; Integer :value}
|
@@ -940,7 +940,7 @@ unless MYSQL_DB.class.adapter_scheme == :do
|
|
940
940
|
end
|
941
941
|
end
|
942
942
|
|
943
|
-
if MYSQL_DB.
|
943
|
+
if MYSQL_DB.adapter_scheme == :mysql
|
944
944
|
context "MySQL bad date/time conversions" do
|
945
945
|
after do
|
946
946
|
Sequel::MySQL.convert_invalid_date_time = false
|
@@ -962,7 +962,7 @@ context "Postgres::Database functions, languages, and triggers" do
|
|
962
962
|
end
|
963
963
|
end
|
964
964
|
|
965
|
-
if POSTGRES_DB.
|
965
|
+
if POSTGRES_DB.adapter_scheme == :postgres
|
966
966
|
context "Postgres::Dataset #use_cursor" do
|
967
967
|
before(:all) do
|
968
968
|
@db = POSTGRES_DB
|
@@ -34,12 +34,12 @@ class Spec::Example::ExampleGroup
|
|
34
34
|
pending = false
|
35
35
|
checked.each do |c|
|
36
36
|
case c
|
37
|
-
when INTEGRATION_DB.
|
37
|
+
when INTEGRATION_DB.adapter_scheme
|
38
38
|
pending = c
|
39
39
|
when Proc
|
40
40
|
pending = c if c.first.call(INTEGRATION_DB)
|
41
41
|
when Array
|
42
|
-
pending = c if c.first == INTEGRATION_DB.
|
42
|
+
pending = c if c.first == INTEGRATION_DB.adapter_scheme && c.last == INTEGRATION_DB.call(INTEGRATION_DB)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
if pending
|
data/spec/core/database_spec.rb
CHANGED
@@ -186,6 +186,11 @@ context "A new Database" do
|
|
186
186
|
db.should be_a_kind_of(Sequel::Database)
|
187
187
|
db.opts[:uri].should == 'do:test://host/db_name'
|
188
188
|
end
|
189
|
+
|
190
|
+
specify "should populate :adapter option when using connection string" do
|
191
|
+
Sequel::Database.should_receive(:adapter_class).once.with(:do).and_return(Sequel::Database)
|
192
|
+
Sequel.connect('do:test://host/db_name').opts[:adapter] == :do
|
193
|
+
end
|
189
194
|
end
|
190
195
|
|
191
196
|
context "Database#disconnect" do
|
@@ -311,7 +316,7 @@ context "Database#uri" do
|
|
311
316
|
end
|
312
317
|
end
|
313
318
|
|
314
|
-
context "Database.adapter_scheme" do
|
319
|
+
context "Database.adapter_scheme and #adapter_scheme" do
|
315
320
|
specify "should return the database schema" do
|
316
321
|
Sequel::Database.adapter_scheme.should be_nil
|
317
322
|
|
@@ -320,6 +325,7 @@ context "Database.adapter_scheme" do
|
|
320
325
|
end
|
321
326
|
|
322
327
|
@c.adapter_scheme.should == :mau
|
328
|
+
@c.new({}).adapter_scheme.should == :mau
|
323
329
|
end
|
324
330
|
end
|
325
331
|
|
@@ -1493,6 +1499,7 @@ context "Database#each_server" do
|
|
1493
1499
|
@db.each_server do |db|
|
1494
1500
|
db.should be_a_kind_of(Sequel::Database)
|
1495
1501
|
db.should_not == @db
|
1502
|
+
db.opts[:adapter].should == :mock
|
1496
1503
|
db.opts[:database].should == 2
|
1497
1504
|
hosts << db.opts[:host]
|
1498
1505
|
end
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -3089,6 +3089,41 @@ context "Dataset#grep" do
|
|
3089
3089
|
"SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
|
3090
3090
|
end
|
3091
3091
|
|
3092
|
+
specify "should support the :all_patterns option" do
|
3093
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true).sql.should ==
|
3094
|
+
"SELECT * FROM posts WHERE (((title LIKE 'abc') OR (body LIKE 'abc')) AND ((title LIKE 'def') OR (body LIKE 'def')))"
|
3095
|
+
end
|
3096
|
+
|
3097
|
+
specify "should support the :all_columns option" do
|
3098
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true).sql.should ==
|
3099
|
+
"SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) AND ((body LIKE 'abc') OR (body LIKE 'def')))"
|
3100
|
+
end
|
3101
|
+
|
3102
|
+
specify "should support the :case_insensitive option" do
|
3103
|
+
@ds.grep([:title, :body], ['abc', 'def'], :case_insensitive=>true).sql.should ==
|
3104
|
+
"SELECT * FROM posts WHERE ((title ILIKE 'abc') OR (title ILIKE 'def') OR (body ILIKE 'abc') OR (body ILIKE 'def'))"
|
3105
|
+
end
|
3106
|
+
|
3107
|
+
specify "should support the :all_patterns and :all_columns options together" do
|
3108
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true).sql.should ==
|
3109
|
+
"SELECT * FROM posts WHERE ((title LIKE 'abc') AND (body LIKE 'abc') AND (title LIKE 'def') AND (body LIKE 'def'))"
|
3110
|
+
end
|
3111
|
+
|
3112
|
+
specify "should support the :all_patterns and :case_insensitive options together" do
|
3113
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :case_insensitive=>true).sql.should ==
|
3114
|
+
"SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (body ILIKE 'abc')) AND ((title ILIKE 'def') OR (body ILIKE 'def')))"
|
3115
|
+
end
|
3116
|
+
|
3117
|
+
specify "should support the :all_columns and :case_insensitive options together" do
|
3118
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true, :case_insensitive=>true).sql.should ==
|
3119
|
+
"SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (title ILIKE 'def')) AND ((body ILIKE 'abc') OR (body ILIKE 'def')))"
|
3120
|
+
end
|
3121
|
+
|
3122
|
+
specify "should support the :all_patterns, :all_columns, and :case_insensitive options together" do
|
3123
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should ==
|
3124
|
+
"SELECT * FROM posts WHERE ((title ILIKE 'abc') AND (body ILIKE 'abc') AND (title ILIKE 'def') AND (body ILIKE 'def'))"
|
3125
|
+
end
|
3126
|
+
|
3092
3127
|
specify "should support regexps though the database may not support it" do
|
3093
3128
|
@ds.grep(:title, /ruby/).sql.should ==
|
3094
3129
|
"SELECT * FROM posts WHERE ((title ~ 'ruby'))"
|
@@ -3596,7 +3631,7 @@ describe "Sequel timezone support" do
|
|
3596
3631
|
@dataset.literal(t).should == "#{s}#{@offset}'"
|
3597
3632
|
|
3598
3633
|
t = DateTime.now.new_offset(0)
|
3599
|
-
s = t.new_offset(
|
3634
|
+
s = t.new_offset(DateTime.now.offset).strftime("'%Y-%m-%d %H:%M:%S")
|
3600
3635
|
@dataset.literal(t).should == "#{s}#{@offset}'"
|
3601
3636
|
end
|
3602
3637
|
|
@@ -33,7 +33,7 @@ context "A paginated dataset" do
|
|
33
33
|
@d.paginate(4, 50).next_page.should be_nil
|
34
34
|
end
|
35
35
|
|
36
|
-
specify "should return the previous page number or nil if we're on the
|
36
|
+
specify "should return the previous page number or nil if we're on the first" do
|
37
37
|
@paginated.prev_page.should be_nil
|
38
38
|
@d.paginate(4, 50).prev_page.should == 3
|
39
39
|
end
|
@@ -33,16 +33,24 @@ describe Sequel::Model, "rcte_tree" do
|
|
33
33
|
@c.plugin :rcte_tree
|
34
34
|
@o.parent_dataset.sql.should == 'SELECT * FROM nodes WHERE (nodes.id = 1) LIMIT 1'
|
35
35
|
@o.children_dataset.sql.should == 'SELECT * FROM nodes WHERE (nodes.parent_id = 2)'
|
36
|
-
@o.ancestors_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE (id = 1) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.parent_id = nodes.id)) SELECT * FROM t'
|
37
|
-
@o.descendants_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE (parent_id = 2) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.id = nodes.parent_id)) SELECT * FROM t'
|
36
|
+
@o.ancestors_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE (id = 1) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.parent_id = nodes.id)) SELECT * FROM t AS nodes'
|
37
|
+
@o.descendants_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE (parent_id = 2) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.id = nodes.parent_id)) SELECT * FROM t AS nodes'
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should use the correct SQL for lazy associations when giving options" do
|
41
41
|
@c.plugin :rcte_tree, :primary_key=>:i, :key=>:pi, :cte_name=>:cte, :order=>:name, :ancestors=>{:name=>:as}, :children=>{:name=>:cs}, :descendants=>{:name=>:ds}, :parent=>{:name=>:p}
|
42
42
|
@o.p_dataset.sql.should == 'SELECT * FROM nodes WHERE (nodes.i = 4) ORDER BY name LIMIT 1'
|
43
43
|
@o.cs_dataset.sql.should == 'SELECT * FROM nodes WHERE (nodes.pi = 3) ORDER BY name'
|
44
|
-
@o.as_dataset.sql.should == 'WITH cte AS (SELECT * FROM nodes WHERE (i = 4) UNION ALL SELECT nodes.* FROM nodes INNER JOIN cte ON (cte.pi = nodes.i)) SELECT * FROM cte ORDER BY name'
|
45
|
-
@o.ds_dataset.sql.should == 'WITH cte AS (SELECT * FROM nodes WHERE (pi = 3) UNION ALL SELECT nodes.* FROM nodes INNER JOIN cte ON (cte.i = nodes.pi)) SELECT * FROM cte ORDER BY name'
|
44
|
+
@o.as_dataset.sql.should == 'WITH cte AS (SELECT * FROM nodes WHERE (i = 4) UNION ALL SELECT nodes.* FROM nodes INNER JOIN cte ON (cte.pi = nodes.i)) SELECT * FROM cte AS nodes ORDER BY name'
|
45
|
+
@o.ds_dataset.sql.should == 'WITH cte AS (SELECT * FROM nodes WHERE (pi = 3) UNION ALL SELECT nodes.* FROM nodes INNER JOIN cte ON (cte.i = nodes.pi)) SELECT * FROM cte AS nodes ORDER BY name'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should use the correct SQL for lazy associations with :conditions option" do
|
49
|
+
@c.plugin :rcte_tree, :conditions => {:i => 1}
|
50
|
+
@o.parent_dataset.sql.should == 'SELECT * FROM nodes WHERE ((nodes.id = 1) AND (i = 1)) LIMIT 1'
|
51
|
+
@o.children_dataset.sql.should == 'SELECT * FROM nodes WHERE ((nodes.parent_id = 2) AND (i = 1))'
|
52
|
+
@o.ancestors_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE ((id = 1) AND (i = 1)) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.parent_id = nodes.id) WHERE (i = 1)) SELECT * FROM t AS nodes WHERE (i = 1)'
|
53
|
+
@o.descendants_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE ((parent_id = 2) AND (i = 1)) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.id = nodes.parent_id) WHERE (i = 1)) SELECT * FROM t AS nodes WHERE (i = 1)'
|
46
54
|
end
|
47
55
|
|
48
56
|
it "should add all parent associations when lazily loading ancestors" do
|
@@ -93,7 +101,7 @@ describe Sequel::Model, "rcte_tree" do
|
|
93
101
|
{:id=>8, :name=>'?', :parent_id=>nil, :x_root_x=>2}, {:id=>8, :name=>'?', :parent_id=>nil, :x_root_x=>1}]]
|
94
102
|
os = @ds.eager(:ancestors).all
|
95
103
|
MODEL_DB.sqls.first.should == "SELECT * FROM nodes"
|
96
|
-
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT id AS x_root_x, nodes\.\* FROM nodes WHERE \(id IN \([12], [12]\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.parent_id = nodes\.id\)\) SELECT \* FROM t/
|
104
|
+
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT id AS x_root_x, nodes\.\* FROM nodes WHERE \(id IN \([12], [12]\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.parent_id = nodes\.id\)\) SELECT \* FROM t AS nodes/
|
97
105
|
os.should == [@c.load(:id=>2, :parent_id=>1, :name=>'AA'), @c.load(:id=>6, :parent_id=>2, :name=>'C'), @c.load(:id=>7, :parent_id=>1, :name=>'D'), @c.load(:id=>9, :parent_id=>nil, :name=>'E')]
|
98
106
|
os.map{|o| o.ancestors}.should == [[@c.load(:id=>1, :name=>'00', :parent_id=>8), @c.load(:id=>8, :name=>'?', :parent_id=>nil)],
|
99
107
|
[@c.load(:id=>2, :name=>'AA', :parent_id=>1), @c.load(:id=>1, :name=>'00', :parent_id=>8), @c.load(:id=>8, :name=>'?', :parent_id=>nil)],
|
@@ -126,7 +134,19 @@ describe Sequel::Model, "rcte_tree" do
|
|
126
134
|
os.map{|o| o.p.p.p.p if o.p and o.p.p and o.p.p.p}.should == [nil, nil, nil, nil]
|
127
135
|
MODEL_DB.new_sqls.should == []
|
128
136
|
end
|
129
|
-
|
137
|
+
|
138
|
+
it "should eagerly load ancestors respecting association option :conditions" do
|
139
|
+
@c.plugin :rcte_tree, :conditions => {:i => 1}
|
140
|
+
@ds.row_sets = [[{:id=>2, :parent_id=>1, :name=>'AA'}, {:id=>6, :parent_id=>2, :name=>'C'}, {:id=>7, :parent_id=>1, :name=>'D'}, {:id=>9, :parent_id=>nil, :name=>'E'}],
|
141
|
+
[{:id=>2, :name=>'AA', :parent_id=>1, :x_root_x=>2},
|
142
|
+
{:id=>1, :name=>'00', :parent_id=>8, :x_root_x=>1}, {:id=>1, :name=>'00', :parent_id=>8, :x_root_x=>2},
|
143
|
+
{:id=>8, :name=>'?', :parent_id=>nil, :x_root_x=>2}, {:id=>8, :name=>'?', :parent_id=>nil, :x_root_x=>1}]]
|
144
|
+
os = @ds.eager(:ancestors).all
|
145
|
+
MODEL_DB.sqls.first.should == "SELECT * FROM nodes"
|
146
|
+
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT id AS x_root_x, nodes\.\* FROM nodes WHERE \(\(id IN \([12], [12]\)\) AND \(i = 1\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.parent_id = nodes\.id\) WHERE \(i = 1\)\) SELECT \* FROM t AS nodes WHERE \(i = 1\)/
|
147
|
+
MODEL_DB.new_sqls.should == []
|
148
|
+
end
|
149
|
+
|
130
150
|
it "should eagerly load descendants" do
|
131
151
|
@c.plugin :rcte_tree
|
132
152
|
@ds.row_sets = [[{:id=>2, :parent_id=>1, :name=>'AA'}, {:id=>6, :parent_id=>2, :name=>'C'}, {:id=>7, :parent_id=>1, :name=>'D'}],
|
@@ -135,7 +155,7 @@ describe Sequel::Model, "rcte_tree" do
|
|
135
155
|
{:id=>4, :name=>'?', :parent_id=>7, :x_root_x=>7}, {:id=>5, :name=>'?', :parent_id=>4, :x_root_x=>7}]]
|
136
156
|
os = @ds.eager(:descendants).all
|
137
157
|
MODEL_DB.sqls.first.should == "SELECT * FROM nodes"
|
138
|
-
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT parent_id AS x_root_x, nodes\.\* FROM nodes WHERE \(parent_id IN \([267], [267], [267]\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.id = nodes\.parent_id\)\) SELECT \* FROM t/
|
158
|
+
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT parent_id AS x_root_x, nodes\.\* FROM nodes WHERE \(parent_id IN \([267], [267], [267]\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.id = nodes\.parent_id\)\) SELECT \* FROM t AS nodes/
|
139
159
|
os.should == [@c.load(:id=>2, :parent_id=>1, :name=>'AA'), @c.load(:id=>6, :parent_id=>2, :name=>'C'), @c.load(:id=>7, :parent_id=>1, :name=>'D')]
|
140
160
|
os.map{|o| o.descendants}.should == [[@c.load(:id=>6, :parent_id=>2, :name=>'C'), @c.load(:id=>9, :parent_id=>2, :name=>'E'), @c.load(:id=>3, :name=>'00', :parent_id=>6)],
|
141
161
|
[@c.load(:id=>3, :name=>'00', :parent_id=>6)],
|
@@ -173,7 +193,7 @@ describe Sequel::Model, "rcte_tree" do
|
|
173
193
|
{:id=>4, :name=>'?', :parent_id=>7, :x_root_x=>7, :x_level_x=>0}, {:id=>5, :name=>'?', :parent_id=>4, :x_root_x=>7, :x_level_x=>1}]]
|
174
194
|
os = @ds.eager(:descendants=>2).all
|
175
195
|
MODEL_DB.sqls.first.should == "SELECT * FROM nodes"
|
176
|
-
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT parent_id AS x_root_x, nodes\.\*, 0 AS x_level_x FROM nodes WHERE \(parent_id IN \([267], [267], [267]\)\) UNION ALL SELECT t\.x_root_x, nodes\.\*, \(t\.x_level_x \+ 1\) AS x_level_x FROM nodes INNER JOIN t ON \(t\.id = nodes\.parent_id\) WHERE \(t\.x_level_x < 1\)\) SELECT \* FROM t/
|
196
|
+
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT parent_id AS x_root_x, nodes\.\*, 0 AS x_level_x FROM nodes WHERE \(parent_id IN \([267], [267], [267]\)\) UNION ALL SELECT t\.x_root_x, nodes\.\*, \(t\.x_level_x \+ 1\) AS x_level_x FROM nodes INNER JOIN t ON \(t\.id = nodes\.parent_id\) WHERE \(t\.x_level_x < 1\)\) SELECT \* FROM t AS nodes/
|
177
197
|
os.should == [@c.load(:id=>2, :parent_id=>1, :name=>'AA'), @c.load(:id=>6, :parent_id=>2, :name=>'C'), @c.load(:id=>7, :parent_id=>1, :name=>'D')]
|
178
198
|
os.map{|o| o.descendants}.should == [[@c.load(:id=>6, :parent_id=>2, :name=>'C'), @c.load(:id=>9, :parent_id=>2, :name=>'E'), @c.load(:id=>3, :name=>'00', :parent_id=>6)],
|
179
199
|
[@c.load(:id=>3, :name=>'00', :parent_id=>6)],
|
@@ -202,4 +222,16 @@ describe Sequel::Model, "rcte_tree" do
|
|
202
222
|
os.map{|o1| o1.associations[:cs].map{|o2| o2.associations[:cs].map{|o3| o3.associations[:cs]}}}.should == [[[[]], []], [[]], [[nil]]]
|
203
223
|
MODEL_DB.new_sqls.should == []
|
204
224
|
end
|
225
|
+
|
226
|
+
it "should eagerly load descendants respecting association option :conditions" do
|
227
|
+
@c.plugin :rcte_tree, :conditions => {:i => 1}
|
228
|
+
@ds.row_sets = [[{:id=>2, :parent_id=>1, :name=>'AA'}, {:id=>6, :parent_id=>2, :name=>'C'}, {:id=>7, :parent_id=>1, :name=>'D'}],
|
229
|
+
[{:id=>6, :parent_id=>2, :name=>'C', :x_root_x=>2}, {:id=>9, :parent_id=>2, :name=>'E', :x_root_x=>2},
|
230
|
+
{:id=>3, :name=>'00', :parent_id=>6, :x_root_x=>6}, {:id=>3, :name=>'00', :parent_id=>6, :x_root_x=>2},
|
231
|
+
{:id=>4, :name=>'?', :parent_id=>7, :x_root_x=>7}, {:id=>5, :name=>'?', :parent_id=>4, :x_root_x=>7}]]
|
232
|
+
os = @ds.eager(:descendants).all
|
233
|
+
MODEL_DB.sqls.first.should == "SELECT * FROM nodes"
|
234
|
+
MODEL_DB.new_sqls.last.should =~ /WITH t AS \(SELECT parent_id AS x_root_x, nodes\.\* FROM nodes WHERE \(\(parent_id IN \([267], [267], [267]\)\) AND \(i = 1\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.id = nodes\.parent_id\) WHERE \(i = 1\)\) SELECT \* FROM t AS nodes WHERE \(i = 1\)/
|
235
|
+
MODEL_DB.new_sqls.should == []
|
236
|
+
end
|
205
237
|
end
|
@@ -53,6 +53,11 @@ describe Sequel::Model, "create_table and schema" do
|
|
53
53
|
MODEL_DB.sqls.should == ['CREATE TABLE items (name text, price float NOT NULL)']
|
54
54
|
end
|
55
55
|
|
56
|
+
it "should allow setting schema and creating the table in one call" do
|
57
|
+
@model.create_table { text :name }
|
58
|
+
MODEL_DB.sqls.should == ['CREATE TABLE items (name text)']
|
59
|
+
end
|
60
|
+
|
56
61
|
it "should reload the schema from the database" do
|
57
62
|
schem = {:name=>{:type=>:string}, :price=>{:type=>:float}}
|
58
63
|
@model.db.should_receive(:schema).with(:items, :reload=>true).and_return(schem.to_a.sort_by{|x| x[0].to_s})
|
@@ -71,7 +71,7 @@ describe "Serialization plugin" do
|
|
71
71
|
@c.create(:ghi => [1])
|
72
72
|
@c.create(:ghi => ["hello"])
|
73
73
|
|
74
|
-
x =
|
74
|
+
x = ["hello"].to_json
|
75
75
|
MODEL_DB.sqls.should == [ \
|
76
76
|
"INSERT INTO items (ghi) VALUES ('[1]')", \
|
77
77
|
"INSERT INTO items (ghi) VALUES ('#{x}')", \
|
@@ -129,7 +129,7 @@ describe "Serialization plugin" do
|
|
129
129
|
|
130
130
|
ds = @c.dataset
|
131
131
|
def ds.fetch_rows(sql, &block)
|
132
|
-
block.call(:id => 1, :abc =>
|
132
|
+
block.call(:id => 1, :abc => [1].to_json, :def => ["hello"].to_json)
|
133
133
|
end
|
134
134
|
|
135
135
|
o = @c.first
|
@@ -142,8 +142,8 @@ describe "Serialization plugin" do
|
|
142
142
|
o.update(:abc => [23])
|
143
143
|
@c.create(:abc => [1,2,3])
|
144
144
|
|
145
|
-
MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{
|
146
|
-
"INSERT INTO items (abc) VALUES ('#{
|
145
|
+
MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{[23].to_json}' WHERE (id = 1)",
|
146
|
+
"INSERT INTO items (abc) VALUES ('#{[1,2,3].to_json}')"]
|
147
147
|
end
|
148
148
|
|
149
149
|
it "should copy serialization formats and columns to subclasses" do
|
@@ -88,6 +88,13 @@ describe Sequel::Model, "#sti_key" do
|
|
88
88
|
MODEL_DB.sqls.should == ["INSERT INTO sti_tests (kind) VALUES ('StiTestSub1')"]
|
89
89
|
end
|
90
90
|
|
91
|
+
it "should have the before_create hook handle columns with the same name as existing method names" do
|
92
|
+
StiTest.plugin :single_table_inheritance, :type
|
93
|
+
StiTest.columns :id, :type
|
94
|
+
StiTest.create
|
95
|
+
MODEL_DB.sqls.should == ["INSERT INTO sti_tests (type) VALUES ('StiTest')"]
|
96
|
+
end
|
97
|
+
|
91
98
|
it "should add a filter to model datasets inside subclasses hook to only retreive objects with the matching key" do
|
92
99
|
StiTest.dataset.sql.should == "SELECT * FROM sti_tests"
|
93
100
|
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub1'))"
|
@@ -103,6 +103,15 @@ describe Sequel::Model, "tree plugin" do
|
|
103
103
|
"SELECT * FROM nodes WHERE (nodes.id = 5) LIMIT 1"]
|
104
104
|
end
|
105
105
|
|
106
|
+
it "should have root? return true for a root node and false for a child node" do
|
107
|
+
@c.load(:parent_id => nil).root?.should be_true
|
108
|
+
@c.load(:parent_id => 1).root?.should be_false
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should have root? return false for an new node" do
|
112
|
+
@c.new.root?.should be_false
|
113
|
+
end
|
114
|
+
|
106
115
|
it "should have self_and_siblings return the children of the current node's parent" do
|
107
116
|
y(@c, [{:id=>1, :parent_id=>3, :name=>'r'}], [{:id=>7, :parent_id=>1, :name=>'r2'}, @o.values.dup])
|
108
117
|
@o.self_and_siblings.should == [@c.load(:id=>7, :parent_id=>1, :name=>'r2'), @o]
|
@@ -116,4 +125,31 @@ describe Sequel::Model, "tree plugin" do
|
|
116
125
|
@db.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 1) LIMIT 1",
|
117
126
|
"SELECT * FROM nodes WHERE (nodes.parent_id = 1)"]
|
118
127
|
end
|
128
|
+
|
129
|
+
describe ":single_root option" do
|
130
|
+
before do
|
131
|
+
@c = klass(:single_root => true)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should have root class method return the root" do
|
135
|
+
y(@c, [{:id=>1, :parent_id=>nil, :name=>'r'}])
|
136
|
+
@c.root.should == @c.load(:id=>1, :parent_id=>nil, :name=>'r')
|
137
|
+
end
|
138
|
+
|
139
|
+
it "prevents creating a second root" do
|
140
|
+
y(@c, [{:id=>1, :parent_id=>nil, :name=>'r'}])
|
141
|
+
lambda { @c.create }.should raise_error(Sequel::Plugins::Tree::TreeMultipleRootError)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "errors when promoting an existing record to a second root" do
|
145
|
+
y(@c, [{:id=>1, :parent_id=>nil, :name=>'r'}])
|
146
|
+
n = @c.load(:id => 2, :parent_id => 1)
|
147
|
+
lambda { n.update(:parent_id => nil) }.should raise_error(Sequel::Plugins::Tree::TreeMultipleRootError)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "allows updating existing root" do
|
151
|
+
y(@c, [{:id=>1, :parent_id=>nil, :name=>'r'}])
|
152
|
+
lambda { @c.root.update(:name => 'fdsa') }.should_not raise_error
|
153
|
+
end
|
154
|
+
end
|
119
155
|
end
|
@@ -869,6 +869,25 @@ describe "Dataset string methods" do
|
|
869
869
|
@ds.grep([:a, :b], %w'boo far').all.should == []
|
870
870
|
end
|
871
871
|
|
872
|
+
it "#grep should work with :all_patterns and :all_columns options" do
|
873
|
+
@ds.insert('foo bar', '')
|
874
|
+
@ds.insert('foo d', 'bar')
|
875
|
+
@ds.insert('foo e', '')
|
876
|
+
@ds.insert('', 'bar')
|
877
|
+
@ds.insert('foo f', 'baz')
|
878
|
+
@ds.insert('foo baz', 'bar baz')
|
879
|
+
@ds.insert('foo boo', 'boo foo')
|
880
|
+
|
881
|
+
@ds.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true).all.should == [{:a=>'foo bar', :b=>''}, {:a=>'foo baz', :b=>'bar baz'}, {:a=>'foo d', :b=>'bar'}]
|
882
|
+
@ds.grep([:a, :b], %w'%foo% %bar% %blob%', :all_patterns=>true).all.should == []
|
883
|
+
|
884
|
+
@ds.grep([:a, :b], %w'%bar% %foo%', :all_columns=>true).all.should == [{:a=>"foo baz", :b=>"bar baz"}, {:a=>"foo boo", :b=>"boo foo"}, {:a=>"foo d", :b=>"bar"}]
|
885
|
+
@ds.grep([:a, :b], %w'%baz%', :all_columns=>true).all.should == [{:a=>'foo baz', :b=>'bar baz'}]
|
886
|
+
|
887
|
+
@ds.grep([:a, :b], %w'%baz% %foo%', :all_columns=>true, :all_patterns=>true).all.should == []
|
888
|
+
@ds.grep([:a, :b], %w'%boo% %foo%', :all_columns=>true, :all_patterns=>true).all.should == [{:a=>'foo boo', :b=>'boo foo'}]
|
889
|
+
end
|
890
|
+
|
872
891
|
it "#like should return matching rows" do
|
873
892
|
@ds.insert('foo', 'bar')
|
874
893
|
@ds.filter(:a.like('foo')).all.should == [{:a=>'foo', :b=>'bar'}]
|
@@ -118,7 +118,7 @@ describe "Prepared Statements and Bound Arguments" do
|
|
118
118
|
INTEGRATION_DB.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
|
119
119
|
@ds.filter(:number=>@ds.ba(:$n)).prepare(:first, :select_n)
|
120
120
|
INTEGRATION_DB.call(:select_n, :n=>10).should == {:id=>1, :number=>10}
|
121
|
-
if INTEGRATION_DB.
|
121
|
+
if INTEGRATION_DB.adapter_scheme == :jdbc and INTEGRATION_DB.database_type == :sqlite
|
122
122
|
# Work around for open prepared statements on a table not allowing the
|
123
123
|
# dropping of a table when using SQLite over JDBC
|
124
124
|
INTEGRATION_DB.synchronize{|c| c.prepared_statements[:select_n][1].close}
|
@@ -204,7 +204,7 @@ describe "Prepared Statements and Bound Arguments" do
|
|
204
204
|
INTEGRATION_DB.call(:select_n, :n=>10).should == [@c.load(:id=>1, :number=>10)]
|
205
205
|
@c.filter(:number=>@ds.ba(:$n)).prepare(:first, :select_n)
|
206
206
|
INTEGRATION_DB.call(:select_n, :n=>10).should == @c.load(:id=>1, :number=>10)
|
207
|
-
if INTEGRATION_DB.
|
207
|
+
if INTEGRATION_DB.adapter_scheme == :jdbc and INTEGRATION_DB.database_type == :sqlite
|
208
208
|
# Work around for open prepared statements on a table not allowing the
|
209
209
|
# dropping of a table when using SQLite over JDBC
|
210
210
|
INTEGRATION_DB.synchronize{|c| c.prepared_statements[:select_n][1].close}
|
@@ -53,7 +53,7 @@ describe "Database schema parser" do
|
|
53
53
|
INTEGRATION_DB.schema(:items)
|
54
54
|
end
|
55
55
|
|
56
|
-
cspecify "should parse primary keys from the schema properly", [proc{|db| db.
|
56
|
+
cspecify "should parse primary keys from the schema properly", [proc{|db| db.adapter_scheme != :jdbc}, :mssql] do
|
57
57
|
INTEGRATION_DB.create_table!(:items){Integer :number}
|
58
58
|
INTEGRATION_DB.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == []
|
59
59
|
INTEGRATION_DB.create_table!(:items){primary_key :number}
|
@@ -39,17 +39,17 @@ class Spec::Example::ExampleGroup
|
|
39
39
|
when Array
|
40
40
|
case c.length
|
41
41
|
when 1
|
42
|
-
pending = c if c.first == INTEGRATION_DB.
|
42
|
+
pending = c if c.first == INTEGRATION_DB.adapter_scheme
|
43
43
|
when 2
|
44
44
|
if c.first.is_a?(Proc)
|
45
45
|
pending = c if c.first.call(INTEGRATION_DB) && c.last == INTEGRATION_DB.database_type
|
46
46
|
elsif c.last.is_a?(Proc)
|
47
|
-
pending = c if c.first == INTEGRATION_DB.
|
47
|
+
pending = c if c.first == INTEGRATION_DB.adapter_scheme && c.last.call(INTEGRATION_DB)
|
48
48
|
else
|
49
|
-
pending = c if c.first == INTEGRATION_DB.
|
49
|
+
pending = c if c.first == INTEGRATION_DB.adapter_scheme && c.last == INTEGRATION_DB.database_type
|
50
50
|
end
|
51
51
|
when 3
|
52
|
-
pending = c if c[0] == INTEGRATION_DB.
|
52
|
+
pending = c if c[0] == INTEGRATION_DB.adapter_scheme && c[1] == INTEGRATION_DB.database_type && c[2].call(INTEGRATION_DB)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
break if pending
|
@@ -2,25 +2,31 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
|
|
2
2
|
|
3
3
|
describe "Sequel timezone support" do
|
4
4
|
def test_timezone
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
# Tests should cover both DST and non-DST times.
|
6
|
+
[Time.now, Time.local(2010,1,1,12), Time.local(2010,6,1,12)].each do |t|
|
7
|
+
@db[:t].insert(t)
|
8
|
+
t2 = @db[:t].single_value
|
9
|
+
t2 = Sequel.database_to_application_timestamp(t2.to_s) unless t2.is_a?(Time)
|
10
|
+
(t2 - t).should be_close(0, 2)
|
11
|
+
t2.utc_offset.should == 0 if Sequel.application_timezone == :utc
|
12
|
+
t2.utc_offset.should == t.getlocal.utc_offset if Sequel.application_timezone == :local
|
13
|
+
@db[:t].delete
|
14
|
+
end
|
15
|
+
|
15
16
|
Sequel.datetime_class = DateTime
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
local_dst_offset = Rational(Time.local(2010, 6).utc_offset, 60*60*24)
|
18
|
+
local_std_offset = Rational(Time.local(2010, 1).utc_offset, 60*60*24)
|
19
|
+
[DateTime.now, DateTime.civil(2010,1,1,12,0,0,local_std_offset), DateTime.civil(2010,6,1,12,0,0,local_dst_offset)].each do |dt|
|
20
|
+
@db[:t].insert(dt)
|
21
|
+
dt2 = @db[:t].single_value
|
22
|
+
dt2 = Sequel.database_to_application_timestamp(dt2.to_s) unless dt2.is_a?(DateTime)
|
23
|
+
(dt2 - dt).should be_close(0, 0.00002)
|
24
|
+
dt2.offset.should == 0 if Sequel.application_timezone == :utc
|
25
|
+
dt2.offset.should == dt.offset if Sequel.application_timezone == :local
|
26
|
+
@db[:t].delete
|
27
|
+
end
|
22
28
|
end
|
23
|
-
|
29
|
+
|
24
30
|
before do
|
25
31
|
@db = INTEGRATION_DB
|
26
32
|
@db.create_table!(:t){DateTime :t}
|
@@ -36,19 +42,19 @@ describe "Sequel timezone support" do
|
|
36
42
|
Sequel.application_timezone = :local
|
37
43
|
test_timezone
|
38
44
|
end
|
39
|
-
|
45
|
+
|
40
46
|
cspecify "should support using local time for database storage and UTC for the application", [:do, proc{|db| db.database_type != :sqlite}] do
|
41
47
|
Sequel.database_timezone = :local
|
42
48
|
Sequel.application_timezone = :utc
|
43
49
|
test_timezone
|
44
50
|
end
|
45
|
-
|
51
|
+
|
46
52
|
cspecify "should support using UTC for both database storage and for application", [:do, proc{|db| db.database_type != :sqlite}] do
|
47
53
|
Sequel.default_timezone = :utc
|
48
54
|
test_timezone
|
49
55
|
end
|
50
|
-
|
51
|
-
|
56
|
+
|
57
|
+
cspecify "should support using local time for both database storage and for application", [:do, proc{|db| db.database_type != :sqlite}] do
|
52
58
|
Sequel.default_timezone = :local
|
53
59
|
test_timezone
|
54
60
|
end
|