sequel 3.4.0 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +84 -0
- data/Rakefile +1 -1
- data/doc/cheat_sheet.rdoc +5 -2
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/lib/sequel/adapters/ado.rb +3 -1
- data/lib/sequel/adapters/ado/mssql.rb +2 -2
- data/lib/sequel/adapters/do.rb +2 -11
- data/lib/sequel/adapters/do/mysql.rb +7 -0
- data/lib/sequel/adapters/do/postgres.rb +2 -2
- data/lib/sequel/adapters/firebird.rb +3 -3
- data/lib/sequel/adapters/informix.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +3 -3
- data/lib/sequel/adapters/jdbc/mssql.rb +7 -0
- data/lib/sequel/adapters/mysql.rb +60 -21
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +3 -3
- data/lib/sequel/adapters/oracle.rb +1 -5
- data/lib/sequel/adapters/postgres.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +142 -33
- data/lib/sequel/adapters/shared/mysql.rb +54 -31
- data/lib/sequel/adapters/shared/oracle.rb +17 -6
- data/lib/sequel/adapters/shared/postgres.rb +7 -7
- data/lib/sequel/adapters/shared/progress.rb +3 -3
- data/lib/sequel/adapters/shared/sqlite.rb +3 -17
- data/lib/sequel/connection_pool.rb +4 -6
- data/lib/sequel/core.rb +29 -113
- data/lib/sequel/database.rb +14 -12
- data/lib/sequel/dataset.rb +8 -21
- data/lib/sequel/dataset/convenience.rb +1 -1
- data/lib/sequel/dataset/graph.rb +9 -2
- data/lib/sequel/dataset/sql.rb +170 -104
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/schema_dumper.rb +7 -1
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +4 -4
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/model/associations.rb +105 -45
- data/lib/sequel/model/base.rb +37 -28
- data/lib/sequel/plugins/active_model.rb +35 -0
- data/lib/sequel/plugins/association_dependencies.rb +96 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/force_encoding.rb +61 -0
- data/lib/sequel/plugins/many_through_many.rb +32 -11
- data/lib/sequel/plugins/nested_attributes.rb +7 -2
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +61 -0
- data/lib/sequel/sql.rb +31 -30
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +262 -0
- data/spec/adapters/mysql_spec.rb +46 -8
- data/spec/adapters/postgres_spec.rb +6 -3
- data/spec/adapters/spec_helper.rb +21 -0
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +1 -1
- data/spec/core/database_spec.rb +27 -1
- data/spec/core/dataset_spec.rb +63 -1
- data/spec/core/object_graph_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -0
- data/spec/extensions/active_model_spec.rb +47 -0
- data/spec/extensions/association_dependencies_spec.rb +108 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/force_encoding_spec.rb +75 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +60 -2
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +29 -1
- data/spec/extensions/schema_dumper_spec.rb +10 -0
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +60 -0
- data/spec/integration/database_test.rb +8 -0
- data/spec/integration/dataset_test.rb +9 -9
- data/spec/integration/plugin_test.rb +139 -0
- data/spec/integration/schema_test.rb +7 -7
- data/spec/integration/spec_helper.rb +32 -1
- data/spec/integration/timezone_test.rb +3 -3
- data/spec/integration/transaction_test.rb +1 -1
- data/spec/integration/type_test.rb +6 -6
- data/spec/model/association_reflection_spec.rb +18 -0
- data/spec/model/associations_spec.rb +169 -9
- data/spec/model/base_spec.rb +2 -0
- data/spec/model/eager_loading_spec.rb +82 -2
- data/spec/model/model_spec.rb +8 -1
- data/spec/model/record_spec.rb +52 -9
- metadata +33 -23
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe Sequel::Model, "Subclasses plugin" do
|
4
|
+
before do
|
5
|
+
@c = Class.new(Sequel::Model)
|
6
|
+
@c.plugin :subclasses
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "#subclasses should record direct subclasses of the given model" do
|
10
|
+
@c.subclasses.should == []
|
11
|
+
|
12
|
+
sc1 = Class.new(@c)
|
13
|
+
@c.subclasses.should == [sc1]
|
14
|
+
sc1.subclasses.should == []
|
15
|
+
|
16
|
+
sc2 = Class.new(@c)
|
17
|
+
@c.subclasses.should == [sc1, sc2]
|
18
|
+
sc1.subclasses.should == []
|
19
|
+
sc2.subclasses.should == []
|
20
|
+
|
21
|
+
ssc1 = Class.new(sc1)
|
22
|
+
@c.subclasses.should == [sc1, sc2]
|
23
|
+
sc1.subclasses.should == [ssc1]
|
24
|
+
sc2.subclasses.should == []
|
25
|
+
end
|
26
|
+
|
27
|
+
specify "#descendents should record all descendent subclasses of the given model" do
|
28
|
+
@c.descendents.should == []
|
29
|
+
|
30
|
+
sc1 = Class.new(@c)
|
31
|
+
@c.descendents.should == [sc1]
|
32
|
+
sc1.descendents.should == []
|
33
|
+
|
34
|
+
sc2 = Class.new(@c)
|
35
|
+
@c.descendents.should == [sc1, sc2]
|
36
|
+
sc1.descendents.should == []
|
37
|
+
sc2.descendents.should == []
|
38
|
+
|
39
|
+
ssc1 = Class.new(sc1)
|
40
|
+
@c.descendents.should == [sc1, ssc1, sc2]
|
41
|
+
sc1.descendents.should == [ssc1]
|
42
|
+
sc2.descendents.should == []
|
43
|
+
ssc1.descendents.should == []
|
44
|
+
|
45
|
+
sssc1 = Class.new(ssc1)
|
46
|
+
@c.descendents.should == [sc1, ssc1, sssc1, sc2]
|
47
|
+
sc1.descendents.should == [ssc1, sssc1]
|
48
|
+
sc2.descendents.should == []
|
49
|
+
ssc1.descendents.should == [sssc1]
|
50
|
+
sssc1.descendents.should == []
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "Sequel thread_local_timezones extension" do
|
4
|
+
after do
|
5
|
+
Sequel.default_timezone = nil
|
6
|
+
Sequel.thread_application_timezone = nil
|
7
|
+
Sequel.thread_database_timezone = nil
|
8
|
+
Sequel.thread_typecast_timezone = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should allow specifying thread local timezones via thread_*_timezone=" do
|
12
|
+
proc{Sequel.thread_application_timezone = :local}.should_not raise_error
|
13
|
+
proc{Sequel.thread_database_timezone = :utc}.should_not raise_error
|
14
|
+
proc{Sequel.thread_typecast_timezone = nil}.should_not raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should use thread local timezone if available" do
|
18
|
+
Sequel.thread_application_timezone = :local
|
19
|
+
Sequel.application_timezone.should == :local
|
20
|
+
Sequel.thread_database_timezone = :utc
|
21
|
+
Sequel.database_timezone.should == :utc
|
22
|
+
Sequel.thread_typecast_timezone = nil
|
23
|
+
Sequel.typecast_timezone.should == nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should fallback to default timezone if no thread_local timezone" do
|
27
|
+
Sequel.default_timezone = :utc
|
28
|
+
Sequel.application_timezone.should == :utc
|
29
|
+
Sequel.database_timezone.should == :utc
|
30
|
+
Sequel.typecast_timezone.should == :utc
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should use a nil thread_local_timezone if set instead of falling back to the default timezone if thread_local_timezone is set to :nil" do
|
34
|
+
Sequel.typecast_timezone = :utc
|
35
|
+
Sequel.thread_typecast_timezone = nil
|
36
|
+
Sequel.typecast_timezone.should == :utc
|
37
|
+
Sequel.thread_typecast_timezone = :nil
|
38
|
+
Sequel.typecast_timezone.should == nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be thread safe" do
|
42
|
+
[Thread.new{Sequel.thread_application_timezone = :utc; sleep 0.03; Sequel.application_timezone.should == :utc},
|
43
|
+
Thread.new{sleep 0.01; Sequel.thread_application_timezone = :local; sleep 0.01; Sequel.application_timezone.should == :local}].each{|x| x.join}
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "Touch plugin" do
|
4
|
+
before do
|
5
|
+
@c = Class.new(Sequel::Model) do
|
6
|
+
def _refresh(*); end
|
7
|
+
end
|
8
|
+
p = proc{def touch_instance_value; touch_association_value; end}
|
9
|
+
@Artist = Class.new(@c, &p).set_dataset(:artists)
|
10
|
+
@Album = Class.new(@c, &p).set_dataset(:albums)
|
11
|
+
|
12
|
+
@Artist.columns :id, :updated_at, :modified_on
|
13
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
|
14
|
+
|
15
|
+
@Album.columns :id, :updated_at, :modified_on, :artist_id, :original_album_id
|
16
|
+
@Album.one_to_many :followup_albums, :class=>@Album, :key=>:original_album_id
|
17
|
+
@Album.many_to_one :artist, :class=>@Artist
|
18
|
+
|
19
|
+
@a = @Artist.load(:id=>1)
|
20
|
+
MODEL_DB.reset
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "should default to using Time.now when setting the column values for model instances" do
|
24
|
+
c = Class.new(Sequel::Model).set_dataset(:a)
|
25
|
+
c.plugin :touch
|
26
|
+
c.columns :id, :updated_at
|
27
|
+
c.load(:id=>1).touch
|
28
|
+
MODEL_DB.sqls.first.should =~ /UPDATE a SET updated_at = '[-0-9 :.]+' WHERE \(id = 1\)/
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should allow #touch instance method for updating the updated_at column" do
|
32
|
+
@Artist.plugin :touch
|
33
|
+
@a.touch
|
34
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (id = 1)"]
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "should have #touch take an argument for the column to touch" do
|
38
|
+
@Artist.plugin :touch
|
39
|
+
@a.touch(:modified_on)
|
40
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET modified_on = CURRENT_TIMESTAMP WHERE (id = 1)"]
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "should be able to specify the default column to touch in the plugin call using the :column option" do
|
44
|
+
@Artist.plugin :touch, :column=>:modified_on
|
45
|
+
@a.touch
|
46
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET modified_on = CURRENT_TIMESTAMP WHERE (id = 1)"]
|
47
|
+
end
|
48
|
+
|
49
|
+
specify "should be able to specify the default column to touch using the touch_column model accessor" do
|
50
|
+
@Artist.plugin :touch
|
51
|
+
@Artist.touch_column = :modified_on
|
52
|
+
@a.touch
|
53
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET modified_on = CURRENT_TIMESTAMP WHERE (id = 1)"]
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "should be able to specify the associations to touch in the plugin call using the :associations option" do
|
57
|
+
@Artist.plugin :touch, :associations=>:albums
|
58
|
+
@a.touch
|
59
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (id = 1)",
|
60
|
+
"UPDATE albums SET updated_at = CURRENT_TIMESTAMP WHERE (albums.artist_id = 1)"]
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "should be able to give an array to the :associations option specifying multiple associations" do
|
64
|
+
@Album.plugin :touch, :associations=>[:artist, :followup_albums]
|
65
|
+
@Album.load(:id=>4, :artist_id=>1).touch
|
66
|
+
MODEL_DB.sqls.shift.should == "UPDATE albums SET updated_at = CURRENT_TIMESTAMP WHERE (id = 4)"
|
67
|
+
MODEL_DB.sqls.sort.should == ["UPDATE albums SET updated_at = CURRENT_TIMESTAMP WHERE (albums.original_album_id = 4)",
|
68
|
+
"UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (artists.id = 1)"]
|
69
|
+
end
|
70
|
+
|
71
|
+
specify "should be able to give a hash to the :associations option specifying the column to use for each association" do
|
72
|
+
@Artist.plugin :touch, :associations=>{:albums=>:modified_on}
|
73
|
+
@a.touch
|
74
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (id = 1)",
|
75
|
+
"UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.artist_id = 1)"]
|
76
|
+
end
|
77
|
+
|
78
|
+
specify "should default to using the touch_column as the default touch column for associations" do
|
79
|
+
@Artist.plugin :touch, :column=>:modified_on, :associations=>:albums
|
80
|
+
@a.touch
|
81
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET modified_on = CURRENT_TIMESTAMP WHERE (id = 1)",
|
82
|
+
"UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.artist_id = 1)"]
|
83
|
+
end
|
84
|
+
|
85
|
+
specify "should allow the mixed use of symbols and hashes inside an array for the :associations option" do
|
86
|
+
@Album.plugin :touch, :associations=>[:artist, {:followup_albums=>:modified_on}]
|
87
|
+
@Album.load(:id=>4, :artist_id=>1).touch
|
88
|
+
MODEL_DB.sqls.shift.should == "UPDATE albums SET updated_at = CURRENT_TIMESTAMP WHERE (id = 4)"
|
89
|
+
MODEL_DB.sqls.sort.should == ["UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.original_album_id = 4)",
|
90
|
+
"UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (artists.id = 1)"]
|
91
|
+
end
|
92
|
+
|
93
|
+
specify "should be able to specify the associations to touch via a touch_associations_method" do
|
94
|
+
@Album.plugin :touch
|
95
|
+
@Album.touch_associations(:artist, {:followup_albums=>:modified_on})
|
96
|
+
@Album.load(:id=>4, :artist_id=>1).touch
|
97
|
+
MODEL_DB.sqls.shift.should == "UPDATE albums SET updated_at = CURRENT_TIMESTAMP WHERE (id = 4)"
|
98
|
+
MODEL_DB.sqls.sort.should == ["UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.original_album_id = 4)",
|
99
|
+
"UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (artists.id = 1)"]
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "should touch associated objects when destroying an object" do
|
103
|
+
@Album.plugin :touch
|
104
|
+
@Album.touch_associations(:artist, {:followup_albums=>:modified_on})
|
105
|
+
@Album.load(:id=>4, :artist_id=>1).destroy
|
106
|
+
MODEL_DB.sqls.shift.should == "DELETE FROM albums WHERE (id = 4)"
|
107
|
+
MODEL_DB.sqls.sort.should == ["UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.original_album_id = 4)",
|
108
|
+
"UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (artists.id = 1)"]
|
109
|
+
end
|
110
|
+
|
111
|
+
specify "should not update a column that doesn't exist" do
|
112
|
+
@Album.plugin :touch, :column=>:x
|
113
|
+
a = @Album.load(:id=>1)
|
114
|
+
a.touch
|
115
|
+
MODEL_DB.sqls.should == []
|
116
|
+
a.artist_id = 1
|
117
|
+
a.touch
|
118
|
+
MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = 1 WHERE (id = 1)']
|
119
|
+
end
|
120
|
+
|
121
|
+
specify "should raise an error if given a column argument in touch that doesn't exist" do
|
122
|
+
@Artist.plugin :touch
|
123
|
+
proc{@a.touch(:x)}.should raise_error(Sequel::Error)
|
124
|
+
end
|
125
|
+
|
126
|
+
specify "should raise an Error when a nonexistent association is given" do
|
127
|
+
@Artist.plugin :touch
|
128
|
+
proc{@Artist.plugin :touch, :associations=>:blah}.should raise_error(Sequel::Error)
|
129
|
+
end
|
130
|
+
|
131
|
+
specify "should work correctly in subclasses" do
|
132
|
+
@Artist.plugin :touch
|
133
|
+
c1 = Class.new(@Artist)
|
134
|
+
c1.load(:id=>4).touch
|
135
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (id = 4)"]
|
136
|
+
MODEL_DB.reset
|
137
|
+
|
138
|
+
c1.touch_column = :modified_on
|
139
|
+
c1.touch_associations :albums
|
140
|
+
c1.load(:id=>1).touch
|
141
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET modified_on = CURRENT_TIMESTAMP WHERE (id = 1)",
|
142
|
+
"UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.artist_id = 1)"]
|
143
|
+
MODEL_DB.reset
|
144
|
+
|
145
|
+
@a.touch
|
146
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (id = 1)"]
|
147
|
+
MODEL_DB.reset
|
148
|
+
|
149
|
+
@Artist.plugin :touch, :column=>:modified_on, :associations=>:albums
|
150
|
+
c2 = Class.new(@Artist)
|
151
|
+
c2.load(:id=>4).touch
|
152
|
+
MODEL_DB.sqls.should == ["UPDATE artists SET modified_on = CURRENT_TIMESTAMP WHERE (id = 4)",
|
153
|
+
"UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.artist_id = 4)"]
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe Sequel::Model, "TypecastOnLoad plugin" do
|
4
|
+
before do
|
5
|
+
@db = Sequel::Database.new({})
|
6
|
+
def @db.schema(*args)
|
7
|
+
[[:id, {}], [:y, {:type=>:boolean, :db_type=>'tinyint(1)'}], [:b, {:type=>:integer, :db_type=>'integer'}]]
|
8
|
+
end
|
9
|
+
@c = Class.new(Sequel::Model(@db[:items])) do
|
10
|
+
attr_accessor :bset
|
11
|
+
def b=(x)
|
12
|
+
self.bset = true
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
@c.instance_eval do
|
17
|
+
@columns = [:id, :b, :y]
|
18
|
+
def columns; @columns; end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "should call setter method with value when loading the object, for all given columns" do
|
23
|
+
@c.plugin :typecast_on_load, :b
|
24
|
+
o = @c.load(:id=>1, :b=>"1", :y=>"0")
|
25
|
+
o.values.should == {:id=>1, :b=>1, :y=>"0"}
|
26
|
+
o.bset.should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "should allowing setting columns separately via add_typecast_on_load_columns" do
|
30
|
+
@c.plugin :typecast_on_load
|
31
|
+
@c.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>"1", :y=>"0"}
|
32
|
+
@c.add_typecast_on_load_columns :b
|
33
|
+
@c.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>1, :y=>"0"}
|
34
|
+
@c.add_typecast_on_load_columns :y
|
35
|
+
@c.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>1, :y=>false}
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "should work with subclasses" do
|
39
|
+
@c.plugin :typecast_on_load
|
40
|
+
@c.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>"1", :y=>"0"}
|
41
|
+
|
42
|
+
c1 = Class.new(@c)
|
43
|
+
@c.add_typecast_on_load_columns :b
|
44
|
+
@c.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>1, :y=>"0"}
|
45
|
+
c1.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>"1", :y=>"0"}
|
46
|
+
|
47
|
+
c2 = Class.new(@c)
|
48
|
+
@c.add_typecast_on_load_columns :y
|
49
|
+
@c.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>1, :y=>false}
|
50
|
+
c2.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>1, :y=>"0"}
|
51
|
+
|
52
|
+
c1.add_typecast_on_load_columns :y
|
53
|
+
c1.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>"1", :y=>false}
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "should not mark the object as modified" do
|
57
|
+
@c.plugin :typecast_on_load, :b
|
58
|
+
@c.load(:id=>1, :b=>"1", :y=>"0").modified?.should == false
|
59
|
+
end
|
60
|
+
end
|
@@ -12,6 +12,14 @@ describe Sequel::Database do
|
|
12
12
|
proc{INTEGRATION_DB << "SELECT"}.should raise_error(Sequel::DatabaseError)
|
13
13
|
end
|
14
14
|
|
15
|
+
specify "should store underlying wrapped exception in Sequel::DatabaseError" do
|
16
|
+
begin
|
17
|
+
INTEGRATION_DB << "SELECT"
|
18
|
+
rescue Sequel::DatabaseError=>e
|
19
|
+
e.wrapped_exception.should be_a_kind_of(Exception)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
specify "should not have the connection pool swallow non-StandardError based exceptions" do
|
16
24
|
proc{INTEGRATION_DB.pool.hold{raise Interrupt, "test"}}.should raise_error(Interrupt)
|
17
25
|
end
|
@@ -23,7 +23,7 @@ describe "Simple Dataset operations" do
|
|
23
23
|
{:id => 3, :number=>30} ]
|
24
24
|
end
|
25
25
|
|
26
|
-
|
26
|
+
cspecify "should insert with a primary key specified", :mssql do
|
27
27
|
@ds.insert(:id=>100, :number=>20)
|
28
28
|
sqls_should_be(/INSERT INTO items \((number, id|id, number)\) VALUES \((100, 20|20, 100)\)/)
|
29
29
|
@ds.count.should == 2
|
@@ -74,7 +74,7 @@ describe "Simple Dataset operations" do
|
|
74
74
|
@ds.order(:id).limit(2, 1).all.should == [{:id=>2, :number=>20}]
|
75
75
|
end
|
76
76
|
|
77
|
-
|
77
|
+
cspecify "should fetch correctly with a limit and offset without an order", :mssql do
|
78
78
|
@ds.limit(2, 1).all.should == []
|
79
79
|
end
|
80
80
|
|
@@ -244,7 +244,7 @@ describe "Simple Dataset operations in transactions" do
|
|
244
244
|
INTEGRATION_DB.drop_table(:items_insert_in_transaction)
|
245
245
|
end
|
246
246
|
|
247
|
-
|
247
|
+
cspecify "should insert correctly with a primary key specified inside a transaction", :mssql do
|
248
248
|
INTEGRATION_DB.transaction do
|
249
249
|
@ds.insert(:id=>100, :number=>20)
|
250
250
|
sqls_should_be('BEGIN', /INSERT INTO items_insert_in_transaction \((number, id|id, number)\) VALUES \((100, 20|20, 100)\)/)
|
@@ -284,7 +284,7 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
|
|
284
284
|
end
|
285
285
|
end
|
286
286
|
|
287
|
-
|
287
|
+
cspecify "should give the correct results for UNION, EXCEPT, and INTERSECT when used with ordering and limits", :mssql do
|
288
288
|
@ds1.insert(:number=>8)
|
289
289
|
@ds2.insert(:number=>9)
|
290
290
|
@ds1.insert(:number=>38)
|
@@ -400,14 +400,14 @@ if INTEGRATION_DB.dataset.supports_window_functions?
|
|
400
400
|
[{:rank=>1, :id=>1}, {:rank=>2, :id=>2}, {:rank=>3, :id=>3}, {:rank=>4, :id=>4}, {:rank=>5, :id=>5}, {:rank=>6, :id=>6}]
|
401
401
|
end
|
402
402
|
|
403
|
-
|
403
|
+
cspecify "should give correct results for aggregate window functions with orders", :mssql do
|
404
404
|
@ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id){}.as(:sum)}.all.should ==
|
405
405
|
[{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
|
406
406
|
@ds.select(:id){sum(:over, :args=>amount, :order=>id){}.as(:sum)}.all.should ==
|
407
407
|
[{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
|
408
408
|
end
|
409
409
|
|
410
|
-
|
410
|
+
cspecify "should give correct results for aggregate window functions with frames", :mssql do
|
411
411
|
@ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id, :frame=>:all){}.as(:sum)}.all.should ==
|
412
412
|
[{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
|
413
413
|
@ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :frame=>:all){}.as(:sum)}.all.should ==
|
@@ -449,19 +449,19 @@ describe Sequel::SQL::Constants do
|
|
449
449
|
@db.drop_table(:constants)
|
450
450
|
end
|
451
451
|
|
452
|
-
|
452
|
+
cspecify "should have working CURRENT_DATE", [:odbc, :mssql], [:jdbc, :sqlite] do
|
453
453
|
@db.create_table!(:constants){Date :d}
|
454
454
|
@ds.insert(:d=>Sequel::CURRENT_DATE)
|
455
455
|
Date.today.should == @c2[@ds.get(:d)]
|
456
456
|
end
|
457
457
|
|
458
|
-
|
458
|
+
cspecify "should have working CURRENT_TIME", [:do, :mysql], [:jdbc, :sqlite] do
|
459
459
|
@db.create_table!(:constants){Time :t, :only_time=>true}
|
460
460
|
@ds.insert(:t=>Sequel::CURRENT_TIME)
|
461
461
|
(Time.now - @c[@ds.get(:t)]).should be_close(0, 1)
|
462
462
|
end
|
463
463
|
|
464
|
-
|
464
|
+
cspecify "should have working CURRENT_TIMESTAMP", [:jdbc, :sqlite] do
|
465
465
|
@db.create_table!(:constants){DateTime :ts}
|
466
466
|
@ds.insert(:ts=>Sequel::CURRENT_TIMESTAMP)
|
467
467
|
(Time.now - @c[@ds.get(:ts)]).should be_close(0, 1)
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
# H2 and MSSQL don't support USING joins
|
4
|
+
unless [:h2, :mssql].include?(INTEGRATION_DB.database_type)
|
5
|
+
describe "Class Table Inheritance Plugin" do
|
6
|
+
before do
|
7
|
+
@db = INTEGRATION_DB
|
8
|
+
@db.instance_variable_set(:@schemas, {})
|
9
|
+
@db.create_table!(:employees) do
|
10
|
+
primary_key :id
|
11
|
+
String :name
|
12
|
+
String :kind
|
13
|
+
end
|
14
|
+
@db.create_table!(:managers) do
|
15
|
+
foreign_key :id, :employees, :primary_key=>true
|
16
|
+
Integer :num_staff
|
17
|
+
end
|
18
|
+
@db.create_table!(:executives) do
|
19
|
+
foreign_key :id, :managers, :primary_key=>true
|
20
|
+
Integer :num_managers
|
21
|
+
end
|
22
|
+
@db.create_table!(:staff) do
|
23
|
+
foreign_key :id, :employees, :primary_key=>true
|
24
|
+
foreign_key :manager_id, :managers
|
25
|
+
end
|
26
|
+
class ::Employee < Sequel::Model(@db)
|
27
|
+
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}
|
28
|
+
end
|
29
|
+
class ::Manager < Employee
|
30
|
+
one_to_many :staff_members, :class=>:Staff
|
31
|
+
end
|
32
|
+
class ::Executive < Manager
|
33
|
+
end
|
34
|
+
class ::Staff < Employee
|
35
|
+
many_to_one :manager
|
36
|
+
end
|
37
|
+
|
38
|
+
@i1 =@db[:employees].insert(:name=>'E', :kind=>'Employee')
|
39
|
+
@i2 = @db[:employees].insert(:name=>'S', :kind=>'Staff')
|
40
|
+
@i3 = @db[:employees].insert(:name=>'M', :kind=>'Manager')
|
41
|
+
@i4 = @db[:employees].insert(:name=>'Ex', :kind=>'Executive')
|
42
|
+
@db[:managers].insert(:id=>@i3, :num_staff=>7)
|
43
|
+
@db[:managers].insert(:id=>@i4, :num_staff=>5)
|
44
|
+
@db[:executives].insert(:id=>@i4, :num_managers=>6)
|
45
|
+
@db[:staff].insert(:id=>@i2, :manager_id=>@i4)
|
46
|
+
|
47
|
+
clear_sqls
|
48
|
+
end
|
49
|
+
after do
|
50
|
+
@db.drop_table :executives, :managers, :staff, :employees
|
51
|
+
[:Executive, :Manager, :Staff, :Employee].each{|s| Object.send(:remove_const, s)}
|
52
|
+
end
|
53
|
+
|
54
|
+
specify "should return rows as subclass instances" do
|
55
|
+
Employee.order(:id).all.should == [
|
56
|
+
Employee.load(:id=>@i1, :name=>'E', :kind=>'Employee'),
|
57
|
+
Staff.load(:id=>@i2, :name=>'S', :kind=>'Staff'),
|
58
|
+
Manager.load(:id=>@i3, :name=>'M', :kind=>'Manager'),
|
59
|
+
Executive.load(:id=>@i4, :name=>'Ex', :kind=>'Executive')
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "should lazily load columns in subclass tables" do
|
64
|
+
a = Employee.order(:id).all
|
65
|
+
a[1][:manager_id].should == nil
|
66
|
+
a[1].manager_id.should == @i4
|
67
|
+
end
|
68
|
+
|
69
|
+
specify "should include schema for columns for tables for ancestor classes" do
|
70
|
+
Employee.db_schema.keys.sort_by{|x| x.to_s}.should == [:id, :kind, :name]
|
71
|
+
Staff.db_schema.keys.sort_by{|x| x.to_s}.should == [:id, :kind, :manager_id, :name]
|
72
|
+
Manager.db_schema.keys.sort_by{|x| x.to_s}.should == [:id, :kind, :name, :num_staff]
|
73
|
+
Executive.db_schema.keys.sort_by{|x| x.to_s}.should == [:id, :kind, :name, :num_managers, :num_staff]
|
74
|
+
end
|
75
|
+
|
76
|
+
specify "should include columns for tables for ancestor classes" do
|
77
|
+
Employee.columns.should == [:id, :name, :kind]
|
78
|
+
Staff.columns.should == [:id, :name, :kind, :manager_id]
|
79
|
+
Manager.columns.should == [:id, :name, :kind, :num_staff]
|
80
|
+
Executive.columns.should == [:id, :name, :kind, :num_staff, :num_managers]
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "should delete rows from all tables" do
|
84
|
+
e = Executive.first
|
85
|
+
i = e.id
|
86
|
+
e.staff_members_dataset.destroy
|
87
|
+
e.destroy
|
88
|
+
@db[:executives][:id=>i].should == nil
|
89
|
+
@db[:managers][:id=>i].should == nil
|
90
|
+
@db[:employees][:id=>i].should == nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# See http://www.sqlite.org/src/tktview/3338b3fa19ac4abee6c475126a2e6d9d61f26ab1
|
94
|
+
cspecify "should insert rows into all tables", :sqlite do
|
95
|
+
e = Executive.create(:name=>'Ex2', :num_managers=>8, :num_staff=>9)
|
96
|
+
i = e.id
|
97
|
+
@db[:employees][:id=>i].should == {:id=>i, :name=>'Ex2', :kind=>'Executive'}
|
98
|
+
@db[:managers][:id=>i].should == {:id=>i, :num_staff=>9}
|
99
|
+
@db[:executives][:id=>i].should == {:id=>i, :num_managers=>8}
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "should update rows in all tables" do
|
103
|
+
Executive.first.update(:name=>'Ex2', :num_managers=>8, :num_staff=>9)
|
104
|
+
@db[:employees][:id=>@i4].should == {:id=>@i4, :name=>'Ex2', :kind=>'Executive'}
|
105
|
+
@db[:managers][:id=>@i4].should == {:id=>@i4, :num_staff=>9}
|
106
|
+
@db[:executives][:id=>@i4].should == {:id=>@i4, :num_managers=>8}
|
107
|
+
end
|
108
|
+
|
109
|
+
cspecify "should handle many_to_one relationships", :sqlite do
|
110
|
+
m = Staff.first.manager
|
111
|
+
m.should == Manager[@i4]
|
112
|
+
m.should be_a_kind_of(Executive)
|
113
|
+
end
|
114
|
+
|
115
|
+
cspecify "should handle eagerly loading many_to_one relationships", :sqlite do
|
116
|
+
Staff.limit(1).eager(:manager).all.map{|x| x.manager}.should == [Manager[@i4]]
|
117
|
+
end
|
118
|
+
|
119
|
+
cspecify "should handle eagerly graphing many_to_one relationships", :sqlite do
|
120
|
+
ss = Staff.eager_graph(:manager).all
|
121
|
+
ss.should == [Staff[@i2]]
|
122
|
+
ss.map{|x| x.manager}.should == [Manager[@i4]]
|
123
|
+
end
|
124
|
+
|
125
|
+
specify "should handle one_to_many relationships" do
|
126
|
+
Executive.first.staff_members.should == [Staff[@i2]]
|
127
|
+
end
|
128
|
+
|
129
|
+
specify "should handle eagerly loading one_to_many relationships" do
|
130
|
+
Executive.limit(1).eager(:staff_members).first.staff_members.should == [Staff[@i2]]
|
131
|
+
end
|
132
|
+
|
133
|
+
cspecify "should handle eagerly graphing one_to_many relationships", :sqlite do
|
134
|
+
es = Executive.limit(1).eager_graph(:staff_members).all
|
135
|
+
es.should == [Executive[@i4]]
|
136
|
+
es.map{|x| x.staff_members}.should == [[Staff[@i2]]]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|