sequel 3.4.0 → 3.5.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 +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
|