sequel 3.13.0 → 3.14.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 +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
|