sequel 3.28.0 → 3.29.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 +119 -3
- data/Rakefile +5 -3
- data/bin/sequel +1 -5
- data/doc/model_hooks.rdoc +9 -1
- data/doc/opening_databases.rdoc +49 -40
- data/doc/prepared_statements.rdoc +27 -6
- data/doc/release_notes/3.28.0.txt +2 -2
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +18 -9
- data/doc/transactions.rdoc +41 -1
- data/lib/sequel/adapters/ado.rb +28 -17
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/amalgalite.rb +11 -7
- data/lib/sequel/adapters/db2.rb +122 -70
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do.rb +5 -36
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +3 -6
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/informix.rb +2 -4
- data/lib/sequel/adapters/jdbc.rb +47 -11
- data/lib/sequel/adapters/jdbc/as400.rb +5 -24
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +10 -12
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +64 -51
- data/lib/sequel/adapters/mysql2.rb +15 -9
- data/lib/sequel/adapters/odbc.rb +13 -6
- data/lib/sequel/adapters/odbc/db2.rb +0 -4
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +333 -51
- data/lib/sequel/adapters/postgres.rb +80 -27
- data/lib/sequel/adapters/shared/access.rb +0 -6
- data/lib/sequel/adapters/shared/db2.rb +13 -15
- data/lib/sequel/adapters/shared/firebird.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +23 -18
- data/lib/sequel/adapters/shared/mysql.rb +6 -6
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -30
- data/lib/sequel/adapters/shared/postgres.rb +35 -18
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +116 -37
- data/lib/sequel/adapters/sqlite.rb +16 -8
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/tinytds.rb +13 -10
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
- data/lib/sequel/core.rb +40 -0
- data/lib/sequel/database/connecting.rb +1 -2
- data/lib/sequel/database/dataset.rb +3 -3
- data/lib/sequel/database/dataset_defaults.rb +58 -0
- data/lib/sequel/database/misc.rb +62 -2
- data/lib/sequel/database/query.rb +113 -49
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/actions.rb +37 -19
- data/lib/sequel/dataset/features.rb +24 -0
- data/lib/sequel/dataset/graph.rb +7 -6
- data/lib/sequel/dataset/misc.rb +11 -3
- data/lib/sequel/dataset/mutation.rb +2 -3
- data/lib/sequel/dataset/prepared_statements.rb +6 -4
- data/lib/sequel/dataset/query.rb +46 -15
- data/lib/sequel/dataset/sql.rb +28 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +115 -33
- data/lib/sequel/model/base.rb +91 -31
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +6 -10
- data/lib/sequel/plugins/prepared_statements.rb +12 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/sharding.rb +0 -5
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/timezones.rb +42 -42
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +29 -29
- data/spec/adapters/mysql_spec.rb +86 -104
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +98 -33
- data/spec/adapters/spec_helper.rb +0 -5
- data/spec/adapters/sqlite_spec.rb +24 -21
- data/spec/core/connection_pool_spec.rb +9 -15
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +491 -227
- data/spec/core/dataset_spec.rb +638 -1051
- data/spec/core/expression_filters_spec.rb +0 -1
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +51 -114
- data/spec/core/spec_helper.rb +3 -90
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +3 -0
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/spec_helper.rb +4 -0
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/integration/associations_test.rb +123 -12
- data/spec/integration/dataset_test.rb +140 -47
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +179 -128
- data/spec/integration/prepared_statement_test.rb +92 -91
- data/spec/integration/schema_test.rb +42 -23
- data/spec/integration/spec_helper.rb +25 -31
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +161 -34
- data/spec/integration/type_test.rb +3 -3
- data/spec/model/association_reflection_spec.rb +83 -7
- data/spec/model/associations_spec.rb +393 -676
- data/spec/model/base_spec.rb +186 -116
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +343 -867
- data/spec/model/hooks_spec.rb +160 -79
- data/spec/model/model_spec.rb +118 -165
- data/spec/model/plugins_spec.rb +7 -13
- data/spec/model/record_spec.rb +138 -207
- data/spec/model/spec_helper.rb +10 -73
- metadata +14 -8
data/spec/core/spec_helper.rb
CHANGED
|
@@ -10,93 +10,6 @@ if ENV['SEQUEL_COLUMNS_INTROSPECTION']
|
|
|
10
10
|
Sequel::Dataset.introspect_all_columns
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def update(*args)
|
|
19
|
-
@db.execute update_sql(*args)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def fetch_rows(sql)
|
|
23
|
-
@db.execute(sql)
|
|
24
|
-
yield({:id => 1, :x => 1})
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def quoted_identifier(c)
|
|
28
|
-
"\"#{c}\""
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
class MockDatabase < Sequel::Database
|
|
33
|
-
set_adapter_scheme :mock
|
|
34
|
-
@@quote_identifiers = false
|
|
35
|
-
self.identifier_input_method = nil
|
|
36
|
-
self.identifier_output_method = nil
|
|
37
|
-
attr_reader :sqls
|
|
38
|
-
|
|
39
|
-
def execute(sql, opts={})
|
|
40
|
-
@sqls ||= []
|
|
41
|
-
@sqls << sql
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def reset
|
|
45
|
-
@sqls = []
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def transaction(opts={}); yield; end
|
|
49
|
-
|
|
50
|
-
def dataset; MockDataset.new(self); end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
class SchemaDummyDatabase < Sequel::Database
|
|
54
|
-
attr_reader :sqls
|
|
55
|
-
self.identifier_input_method = nil
|
|
56
|
-
self.identifier_output_method = nil
|
|
57
|
-
|
|
58
|
-
def execute(sql, opts={})
|
|
59
|
-
@sqls ||= []
|
|
60
|
-
@sqls << sql
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
class DummyDataset < Sequel::Dataset
|
|
65
|
-
def first
|
|
66
|
-
raise if @opts[:from] == [:a]
|
|
67
|
-
true
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
class DummyDatabase < Sequel::Database
|
|
72
|
-
attr_reader :sqls
|
|
73
|
-
|
|
74
|
-
def execute(sql, opts={})
|
|
75
|
-
@sqls ||= []
|
|
76
|
-
@sqls << sql
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def transaction; yield; end
|
|
80
|
-
|
|
81
|
-
def dataset
|
|
82
|
-
DummyDataset.new(self)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
class Dummy2Database < Sequel::Database
|
|
87
|
-
attr_reader :sql
|
|
88
|
-
def execute(sql); @sql = sql; end
|
|
89
|
-
def transaction; yield; end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
class DummyDataset < Sequel::Dataset
|
|
93
|
-
VALUES = [
|
|
94
|
-
{:a => 1, :b => 2},
|
|
95
|
-
{:a => 3, :b => 4},
|
|
96
|
-
{:a => 5, :b => 6}
|
|
97
|
-
]
|
|
98
|
-
def fetch_rows(sql, &block)
|
|
99
|
-
VALUES.each(&block)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
13
|
+
Sequel.quote_identifiers = false
|
|
14
|
+
Sequel.identifier_input_method = nil
|
|
15
|
+
Sequel.identifier_output_method = nil
|
|
@@ -8,7 +8,7 @@ describe "class_table_inheritance plugin" do
|
|
|
8
8
|
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
|
|
9
9
|
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
|
|
10
10
|
:staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
|
|
11
|
-
}[table]
|
|
11
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
|
12
12
|
end
|
|
13
13
|
def db.dataset(*args)
|
|
14
14
|
ds = super(*args)
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Plugins::DatasetAssociations" do
|
|
4
|
+
before do
|
|
5
|
+
@db = Sequel::Database.new
|
|
6
|
+
@Base = Class.new(Sequel::Model)
|
|
7
|
+
@Base.plugin :dataset_associations
|
|
8
|
+
|
|
9
|
+
@Artist = Class.new(@Base)
|
|
10
|
+
@Album = Class.new(@Base)
|
|
11
|
+
@Tag = Class.new(@Base)
|
|
12
|
+
|
|
13
|
+
@Artist.meta_def(:name){'Artist'}
|
|
14
|
+
@Album.meta_def(:name){'Album'}
|
|
15
|
+
@Tag.meta_def(:name){'Tag'}
|
|
16
|
+
|
|
17
|
+
@Artist.dataset = @db[:artists]
|
|
18
|
+
@Album.dataset = @db[:albums]
|
|
19
|
+
@Tag.dataset = @db[:tags]
|
|
20
|
+
|
|
21
|
+
@Artist.columns :id, :name
|
|
22
|
+
@Album.columns :id, :name, :artist_id
|
|
23
|
+
@Tag.columns :id, :name
|
|
24
|
+
|
|
25
|
+
@Artist.plugin :many_through_many
|
|
26
|
+
@Artist.one_to_many :albums, :class=>@Album
|
|
27
|
+
@Artist.one_to_one :first_album, :class=>@Album
|
|
28
|
+
@Album.many_to_one :artist, :class=>@Artist
|
|
29
|
+
@Album.many_to_many :tags, :class=>@Tag
|
|
30
|
+
@Tag.many_to_many :albums, :class=>@Album
|
|
31
|
+
@Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]], :class=>@Tag
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should work for many_to_one associations" do
|
|
35
|
+
ds = @Album.artists
|
|
36
|
+
ds.should be_a_kind_of(Sequel::Dataset)
|
|
37
|
+
ds.model.should == @Artist
|
|
38
|
+
ds.sql.should == "SELECT * FROM artists WHERE (artists.id IN (SELECT albums.artist_id FROM albums))"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should work for one_to_many associations" do
|
|
42
|
+
ds = @Artist.albums
|
|
43
|
+
ds.should be_a_kind_of(Sequel::Dataset)
|
|
44
|
+
ds.model.should == @Album
|
|
45
|
+
ds.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should work for one_to_one associations" do
|
|
49
|
+
ds = @Artist.first_albums
|
|
50
|
+
ds.should be_a_kind_of(Sequel::Dataset)
|
|
51
|
+
ds.model.should == @Album
|
|
52
|
+
ds.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "should work for many_to_many associations" do
|
|
56
|
+
ds = @Album.tags
|
|
57
|
+
ds.should be_a_kind_of(Sequel::Dataset)
|
|
58
|
+
ds.model.should == @Tag
|
|
59
|
+
ds.sql.should == "SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM albums INNER JOIN albums_tags ON (albums_tags.album_id = albums.id)))"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should work for many_through_many associations" do
|
|
63
|
+
ds = @Artist.tags
|
|
64
|
+
ds.should be_a_kind_of(Sequel::Dataset)
|
|
65
|
+
ds.model.should == @Tag
|
|
66
|
+
ds.sql.should == "SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM artists INNER JOIN albums ON (albums.artist_id = artists.id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id)))"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should have an associated method that takes an association symbol" do
|
|
70
|
+
ds = @Album.associated(:artist)
|
|
71
|
+
ds.should be_a_kind_of(Sequel::Dataset)
|
|
72
|
+
ds.model.should == @Artist
|
|
73
|
+
ds.sql.should == "SELECT * FROM artists WHERE (artists.id IN (SELECT albums.artist_id FROM albums))"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should raise an Error if an invalid association is given to associated" do
|
|
77
|
+
proc{@Album.associated(:foo)}.should raise_error(Sequel::Error)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should raise an Error if an unrecognized association type is used" do
|
|
81
|
+
@Album.association_reflection(:artist)[:type] = :foo
|
|
82
|
+
proc{@Album.artists}.should raise_error(Sequel::Error)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should work correctly when chaining" do
|
|
86
|
+
ds = @Artist.albums.tags
|
|
87
|
+
ds.should be_a_kind_of(Sequel::Dataset)
|
|
88
|
+
ds.model.should == @Tag
|
|
89
|
+
ds.sql.should == "SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM albums INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE (albums.artist_id IN (SELECT artists.id FROM artists))))"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should deal correctly with filters before the association method" do
|
|
93
|
+
@Artist.filter(:id=>1).albums.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE (id = 1)))"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should deal correctly with filters after the association method" do
|
|
97
|
+
@Artist.albums.filter(:id=>1).sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id = 1))"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should deal correctly with block on the association" do
|
|
101
|
+
@Artist.one_to_many :albums, :clone=>:albums do |ds| ds.filter(:id=>1..100) end
|
|
102
|
+
@Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id >= 1) AND (id <= 100))"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "should deal correctly with :conditions option on the association" do
|
|
106
|
+
@Artist.one_to_many :albums, :clone=>:albums, :conditions=>{:id=>1..100}
|
|
107
|
+
@Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id >= 1) AND (id <= 100))"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should deal correctly with :distinct option on the association" do
|
|
111
|
+
@Artist.one_to_many :albums, :clone=>:albums, :distinct=>true
|
|
112
|
+
@Artist.albums.sql.should == "SELECT DISTINCT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "should deal correctly with :eager option on the association" do
|
|
116
|
+
@Artist.one_to_many :albums, :clone=>:albums, :eager=>:tags
|
|
117
|
+
@Artist.albums.opts[:eager].should == {:tags=>nil}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "should deal correctly with :eager_block option on the association, ignoring the association block" do
|
|
121
|
+
@Artist.one_to_many :albums, :clone=>:albums, :eager_block=>proc{|ds| ds.filter(:id=>1..100)} do |ds| ds.filter(:id=>2..200) end
|
|
122
|
+
@Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id >= 1) AND (id <= 100))"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should deal correctly with :extend option on the association" do
|
|
126
|
+
@Artist.one_to_many :albums, :clone=>:albums, :extend=>Module.new{def foo(x) filter(:id=>x) end}
|
|
127
|
+
@Artist.albums.foo(1).sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id = 1))"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "should deal correctly with :order option on the association" do
|
|
131
|
+
@Artist.one_to_many :albums, :clone=>:albums, :order=>:name
|
|
132
|
+
@Artist.albums.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists)) ORDER BY name"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "should deal correctly with :select option on the association" do
|
|
136
|
+
@Artist.one_to_many :albums, :clone=>:albums, :select=>[:id, :name]
|
|
137
|
+
@Artist.albums.sql.should == "SELECT id, name FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe "Sequel::Plugins::DatasetAssociations with composite keys" do
|
|
142
|
+
before do
|
|
143
|
+
@db = Sequel::Database.new
|
|
144
|
+
@Base = Class.new(Sequel::Model)
|
|
145
|
+
@Base.plugin :dataset_associations
|
|
146
|
+
|
|
147
|
+
@Artist = Class.new(@Base)
|
|
148
|
+
@Album = Class.new(@Base)
|
|
149
|
+
@Tag = Class.new(@Base)
|
|
150
|
+
|
|
151
|
+
@Artist.meta_def(:name){'Artist'}
|
|
152
|
+
@Album.meta_def(:name){'Album'}
|
|
153
|
+
@Tag.meta_def(:name){'Tag'}
|
|
154
|
+
|
|
155
|
+
@Artist.dataset = @db[:artists]
|
|
156
|
+
@Album.dataset = @db[:albums]
|
|
157
|
+
@Tag.dataset = @db[:tags]
|
|
158
|
+
|
|
159
|
+
@Artist.set_primary_key([:id1, :id2])
|
|
160
|
+
@Album.set_primary_key([:id1, :id2])
|
|
161
|
+
@Tag.set_primary_key([:id1, :id2])
|
|
162
|
+
|
|
163
|
+
@Artist.columns :id1, :id2, :name
|
|
164
|
+
@Album.columns :id1, :id2, :name, :artist_id1, :artist_id2
|
|
165
|
+
@Tag.columns :id1, :id2, :name
|
|
166
|
+
|
|
167
|
+
@Artist.plugin :many_through_many
|
|
168
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>[:artist_id1, :artist_id2]
|
|
169
|
+
@Artist.one_to_one :first_album, :class=>@Album, :key=>[:artist_id1, :artist_id2]
|
|
170
|
+
@Album.many_to_one :artist, :class=>@Artist, :key=>[:artist_id1, :artist_id2]
|
|
171
|
+
@Album.many_to_many :tags, :class=>@Tag, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2]
|
|
172
|
+
@Tag.many_to_many :albums, :class=>@Album, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2]
|
|
173
|
+
@Artist.many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]], :class=>@Tag
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should work for many_to_one associations" do
|
|
177
|
+
@Album.artists.sql.should == "SELECT * FROM artists WHERE ((artists.id1, artists.id2) IN (SELECT albums.artist_id1, albums.artist_id2 FROM albums))"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "should work for one_to_many associations" do
|
|
181
|
+
@Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists))"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "should work for one_to_one associations" do
|
|
185
|
+
@Artist.first_albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists))"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "should work for many_to_many associations" do
|
|
189
|
+
@Album.tags.sql.should == "SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM albums INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2))))"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "should work for many_through_many associations" do
|
|
193
|
+
@Artist.tags.sql.should == "SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM artists INNER JOIN albums ON ((albums.artist_id1 = artists.id1) AND (albums.artist_id2 = artists.id2)) INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2))))"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "should work correctly when chaining" do
|
|
197
|
+
@Artist.albums.tags.sql.should == "SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM albums INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2)) WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists))))"
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -177,3 +177,74 @@ describe "InstanceHooks plugin" do
|
|
|
177
177
|
@r.should == [2, 1, 4, 3]
|
|
178
178
|
end
|
|
179
179
|
end
|
|
180
|
+
|
|
181
|
+
describe "InstanceHooks plugin with transactions" do
|
|
182
|
+
before do
|
|
183
|
+
@logger = Object.new
|
|
184
|
+
def @logger.method_missing(meth, sql)
|
|
185
|
+
(@sqls ||= []) << sql
|
|
186
|
+
end
|
|
187
|
+
def @logger.sqls
|
|
188
|
+
@sqls
|
|
189
|
+
end
|
|
190
|
+
@db = Class.new(Sequel::Database) do
|
|
191
|
+
def connect(*)
|
|
192
|
+
Object.new
|
|
193
|
+
end
|
|
194
|
+
def log_connection_execute(conn, sql)
|
|
195
|
+
execute(sql)
|
|
196
|
+
end
|
|
197
|
+
def execute(sql, opts={})
|
|
198
|
+
@loggers.each{|l| l.info(sql)}
|
|
199
|
+
end
|
|
200
|
+
end.new(:loggers=>[@logger])
|
|
201
|
+
pr = proc{|x| r(x)}
|
|
202
|
+
@c = Class.new(Sequel::Model(@db[:items])) do
|
|
203
|
+
attr_accessor :rb
|
|
204
|
+
def _delete
|
|
205
|
+
end
|
|
206
|
+
def after_save
|
|
207
|
+
db.execute('as')
|
|
208
|
+
raise Sequel::Rollback if rb
|
|
209
|
+
end
|
|
210
|
+
def after_destroy
|
|
211
|
+
db.execute('ad')
|
|
212
|
+
raise Sequel::Rollback if rb
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
@c.use_transactions = true
|
|
216
|
+
@c.plugin :instance_hooks
|
|
217
|
+
@o = @c.load({:id=>1})
|
|
218
|
+
@or = @c.load({:id=>1})
|
|
219
|
+
@or.rb = true
|
|
220
|
+
@r = []
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it "should support after_commit_hook" do
|
|
224
|
+
@o.after_commit_hook{@db.execute('ac1')}
|
|
225
|
+
@o.after_commit_hook{@db.execute('ac2')}
|
|
226
|
+
@o.save.should_not be_nil
|
|
227
|
+
@logger.sqls.should == ['BEGIN', 'as', 'COMMIT', 'ac1', 'ac2']
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it "should support after_rollback_hook" do
|
|
231
|
+
@or.after_rollback_hook{@db.execute('ar1')}
|
|
232
|
+
@or.after_rollback_hook{@db.execute('ar2')}
|
|
233
|
+
@or.save.should be_nil
|
|
234
|
+
@logger.sqls.should == ['BEGIN', 'as', 'ROLLBACK', 'ar1', 'ar2']
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it "should support after_commit_hook" do
|
|
238
|
+
@o.after_destroy_commit_hook{@db.execute('adc1')}
|
|
239
|
+
@o.after_destroy_commit_hook{@db.execute('adc2')}
|
|
240
|
+
@o.destroy.should_not be_nil
|
|
241
|
+
@logger.sqls.should == ['BEGIN', 'ad', 'COMMIT', 'adc1', 'adc2']
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "should support after_rollback_hook" do
|
|
245
|
+
@or.after_destroy_rollback_hook{@db.execute('adr1')}
|
|
246
|
+
@or.after_destroy_rollback_hook{@db.execute('adr2')}
|
|
247
|
+
@or.destroy.should be_nil
|
|
248
|
+
@logger.sqls.should == ['BEGIN', 'ad', 'ROLLBACK', 'adr1', 'adr2']
|
|
249
|
+
end
|
|
250
|
+
end
|
|
@@ -66,8 +66,28 @@ describe "Sequel named_timezones extension" do
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
it "should work with the thread_local_timezones extension" do
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
q, q1, q2 = Queue.new, Queue.new, Queue.new
|
|
70
|
+
tz1, tz2 = nil, nil
|
|
71
|
+
t1 = Thread.new do
|
|
72
|
+
Sequel.thread_application_timezone = 'America/New_York'
|
|
73
|
+
q2.push nil
|
|
74
|
+
q.pop
|
|
75
|
+
tz1 = Sequel.application_timezone
|
|
76
|
+
end
|
|
77
|
+
t2 = Thread.new do
|
|
78
|
+
Sequel.thread_application_timezone = 'America/Los_Angeles'
|
|
79
|
+
q2.push nil
|
|
80
|
+
q1.pop
|
|
81
|
+
tz2 = Sequel.application_timezone
|
|
82
|
+
end
|
|
83
|
+
q2.pop
|
|
84
|
+
q2.pop
|
|
85
|
+
q.push nil
|
|
86
|
+
q1.push nil
|
|
87
|
+
t1.join
|
|
88
|
+
t2.join
|
|
89
|
+
tz1.should == @tz_out
|
|
90
|
+
tz2.should == @tz_in
|
|
71
91
|
end
|
|
72
92
|
end
|
|
73
93
|
end
|
|
@@ -57,7 +57,7 @@ describe Sequel::Model, "create_table and schema" do
|
|
|
57
57
|
|
|
58
58
|
it "should reload the schema from the database" do
|
|
59
59
|
schem = {:name=>{:type=>:string}, :price=>{:type=>:float}}
|
|
60
|
-
@model.db.should_receive(:schema).with(
|
|
60
|
+
@model.db.should_receive(:schema).with(@model.dataset, :reload=>true).and_return(schem.to_a.sort_by{|x| x[0].to_s})
|
|
61
61
|
@model.create_table
|
|
62
62
|
@model.db_schema.should == schem
|
|
63
63
|
@model.instance_variable_get(:@columns).should == [:name, :price]
|
|
@@ -22,11 +22,11 @@ describe "Serialization plugin" do
|
|
|
22
22
|
it "should allow setting additional serializable attributes via plugin :serialization call" do
|
|
23
23
|
@c.plugin :serialization, :yaml, :abc
|
|
24
24
|
@c.create(:abc => 1, :def=> 2)
|
|
25
|
-
MODEL_DB.sqls.last.should =~ /INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n', 2|2, '--- 1\n')\)/
|
|
25
|
+
MODEL_DB.sqls.last.should =~ /INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n(\.\.\.\n)?', 2|2, '--- 1\n(\.\.\.\n)?')\)/
|
|
26
26
|
|
|
27
27
|
@c.plugin :serialization, :marshal, :def
|
|
28
28
|
@c.create(:abc => 1, :def=> 1)
|
|
29
|
-
MODEL_DB.sqls.last.should =~ /INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n', 'BAhpBg==\n'|'BAhpBg==\n', '--- 1\n')\)/
|
|
29
|
+
MODEL_DB.sqls.last.should =~ /INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n(\.\.\.\n)?', 'BAhpBg==\n'|'BAhpBg==\n', '--- 1\n(\.\.\.\n)?')\)/
|
|
30
30
|
|
|
31
31
|
@c.plugin :serialization, :json, :ghi
|
|
32
32
|
@c.create(:ghi => [123])
|
|
@@ -38,10 +38,7 @@ describe "Serialization plugin" do
|
|
|
38
38
|
@c.create(:abc => 1)
|
|
39
39
|
@c.create(:abc => "hello")
|
|
40
40
|
|
|
41
|
-
MODEL_DB.sqls.should == [ \
|
|
42
|
-
"INSERT INTO items (abc) VALUES ('--- 1\n')", \
|
|
43
|
-
"INSERT INTO items (abc) VALUES ('--- hello\n')", \
|
|
44
|
-
]
|
|
41
|
+
MODEL_DB.sqls.map{|s| s.sub("...\n", '')}.should == ["INSERT INTO items (abc) VALUES ('--- 1\n')", "INSERT INTO items (abc) VALUES ('--- hello\n')"]
|
|
45
42
|
end
|
|
46
43
|
|
|
47
44
|
it "serialization_format should be the serialization format used" do
|
|
@@ -97,7 +94,7 @@ describe "Serialization plugin" do
|
|
|
97
94
|
|
|
98
95
|
o.update(:abc => 23)
|
|
99
96
|
@c.create(:abc => [1, 2, 3])
|
|
100
|
-
MODEL_DB.sqls.should == ["UPDATE items SET abc = '
|
|
97
|
+
MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
|
|
101
98
|
"INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')"]
|
|
102
99
|
end
|
|
103
100
|
|
|
@@ -164,7 +161,7 @@ describe "Serialization plugin" do
|
|
|
164
161
|
|
|
165
162
|
o.update(:abc => 23)
|
|
166
163
|
Class.new(@c).create(:abc => [1, 2, 3])
|
|
167
|
-
MODEL_DB.sqls.should == ["UPDATE items SET abc = '
|
|
164
|
+
MODEL_DB.sqls.should == ["UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
|
|
168
165
|
"INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')"]
|
|
169
166
|
end
|
|
170
167
|
|