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.
Files changed (148) hide show
  1. data/CHANGELOG +119 -3
  2. data/Rakefile +5 -3
  3. data/bin/sequel +1 -5
  4. data/doc/model_hooks.rdoc +9 -1
  5. data/doc/opening_databases.rdoc +49 -40
  6. data/doc/prepared_statements.rdoc +27 -6
  7. data/doc/release_notes/3.28.0.txt +2 -2
  8. data/doc/release_notes/3.29.0.txt +459 -0
  9. data/doc/sharding.rdoc +7 -1
  10. data/doc/testing.rdoc +18 -9
  11. data/doc/transactions.rdoc +41 -1
  12. data/lib/sequel/adapters/ado.rb +28 -17
  13. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  14. data/lib/sequel/adapters/amalgalite.rb +11 -7
  15. data/lib/sequel/adapters/db2.rb +122 -70
  16. data/lib/sequel/adapters/dbi.rb +15 -15
  17. data/lib/sequel/adapters/do.rb +5 -36
  18. data/lib/sequel/adapters/do/mysql.rb +0 -5
  19. data/lib/sequel/adapters/do/postgres.rb +0 -5
  20. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  21. data/lib/sequel/adapters/firebird.rb +3 -6
  22. data/lib/sequel/adapters/ibmdb.rb +24 -16
  23. data/lib/sequel/adapters/informix.rb +2 -4
  24. data/lib/sequel/adapters/jdbc.rb +47 -11
  25. data/lib/sequel/adapters/jdbc/as400.rb +5 -24
  26. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  28. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/h2.rb +10 -12
  30. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  31. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  32. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
  34. data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
  36. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  38. data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
  39. data/lib/sequel/adapters/mock.rb +315 -0
  40. data/lib/sequel/adapters/mysql.rb +64 -51
  41. data/lib/sequel/adapters/mysql2.rb +15 -9
  42. data/lib/sequel/adapters/odbc.rb +13 -6
  43. data/lib/sequel/adapters/odbc/db2.rb +0 -4
  44. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  45. data/lib/sequel/adapters/openbase.rb +2 -4
  46. data/lib/sequel/adapters/oracle.rb +333 -51
  47. data/lib/sequel/adapters/postgres.rb +80 -27
  48. data/lib/sequel/adapters/shared/access.rb +0 -6
  49. data/lib/sequel/adapters/shared/db2.rb +13 -15
  50. data/lib/sequel/adapters/shared/firebird.rb +6 -6
  51. data/lib/sequel/adapters/shared/mssql.rb +23 -18
  52. data/lib/sequel/adapters/shared/mysql.rb +6 -6
  53. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  54. data/lib/sequel/adapters/shared/oracle.rb +185 -30
  55. data/lib/sequel/adapters/shared/postgres.rb +35 -18
  56. data/lib/sequel/adapters/shared/progress.rb +0 -6
  57. data/lib/sequel/adapters/shared/sqlite.rb +116 -37
  58. data/lib/sequel/adapters/sqlite.rb +16 -8
  59. data/lib/sequel/adapters/swift.rb +5 -5
  60. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  61. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  62. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  63. data/lib/sequel/adapters/tinytds.rb +13 -10
  64. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
  65. data/lib/sequel/core.rb +40 -0
  66. data/lib/sequel/database/connecting.rb +1 -2
  67. data/lib/sequel/database/dataset.rb +3 -3
  68. data/lib/sequel/database/dataset_defaults.rb +58 -0
  69. data/lib/sequel/database/misc.rb +62 -2
  70. data/lib/sequel/database/query.rb +113 -49
  71. data/lib/sequel/database/schema_methods.rb +7 -2
  72. data/lib/sequel/dataset/actions.rb +37 -19
  73. data/lib/sequel/dataset/features.rb +24 -0
  74. data/lib/sequel/dataset/graph.rb +7 -6
  75. data/lib/sequel/dataset/misc.rb +11 -3
  76. data/lib/sequel/dataset/mutation.rb +2 -3
  77. data/lib/sequel/dataset/prepared_statements.rb +6 -4
  78. data/lib/sequel/dataset/query.rb +46 -15
  79. data/lib/sequel/dataset/sql.rb +28 -4
  80. data/lib/sequel/extensions/named_timezones.rb +5 -0
  81. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  82. data/lib/sequel/model.rb +2 -1
  83. data/lib/sequel/model/associations.rb +115 -33
  84. data/lib/sequel/model/base.rb +91 -31
  85. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  86. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  87. data/lib/sequel/plugins/force_encoding.rb +6 -6
  88. data/lib/sequel/plugins/identity_map.rb +1 -1
  89. data/lib/sequel/plugins/many_through_many.rb +6 -10
  90. data/lib/sequel/plugins/prepared_statements.rb +12 -1
  91. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
  92. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  93. data/lib/sequel/plugins/serialization.rb +6 -1
  94. data/lib/sequel/plugins/sharding.rb +0 -5
  95. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  96. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  97. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  98. data/lib/sequel/timezones.rb +42 -42
  99. data/lib/sequel/version.rb +1 -1
  100. data/spec/adapters/mssql_spec.rb +29 -29
  101. data/spec/adapters/mysql_spec.rb +86 -104
  102. data/spec/adapters/oracle_spec.rb +48 -76
  103. data/spec/adapters/postgres_spec.rb +98 -33
  104. data/spec/adapters/spec_helper.rb +0 -5
  105. data/spec/adapters/sqlite_spec.rb +24 -21
  106. data/spec/core/connection_pool_spec.rb +9 -15
  107. data/spec/core/core_sql_spec.rb +20 -31
  108. data/spec/core/database_spec.rb +491 -227
  109. data/spec/core/dataset_spec.rb +638 -1051
  110. data/spec/core/expression_filters_spec.rb +0 -1
  111. data/spec/core/mock_adapter_spec.rb +378 -0
  112. data/spec/core/object_graph_spec.rb +48 -114
  113. data/spec/core/schema_generator_spec.rb +3 -3
  114. data/spec/core/schema_spec.rb +51 -114
  115. data/spec/core/spec_helper.rb +3 -90
  116. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  117. data/spec/extensions/dataset_associations_spec.rb +199 -0
  118. data/spec/extensions/instance_hooks_spec.rb +71 -0
  119. data/spec/extensions/named_timezones_spec.rb +22 -2
  120. data/spec/extensions/nested_attributes_spec.rb +3 -0
  121. data/spec/extensions/schema_spec.rb +1 -1
  122. data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
  123. data/spec/extensions/serialization_spec.rb +5 -8
  124. data/spec/extensions/spec_helper.rb +4 -0
  125. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  126. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  127. data/spec/integration/associations_test.rb +123 -12
  128. data/spec/integration/dataset_test.rb +140 -47
  129. data/spec/integration/eager_loader_test.rb +19 -21
  130. data/spec/integration/model_test.rb +80 -1
  131. data/spec/integration/plugin_test.rb +179 -128
  132. data/spec/integration/prepared_statement_test.rb +92 -91
  133. data/spec/integration/schema_test.rb +42 -23
  134. data/spec/integration/spec_helper.rb +25 -31
  135. data/spec/integration/timezone_test.rb +38 -12
  136. data/spec/integration/transaction_test.rb +161 -34
  137. data/spec/integration/type_test.rb +3 -3
  138. data/spec/model/association_reflection_spec.rb +83 -7
  139. data/spec/model/associations_spec.rb +393 -676
  140. data/spec/model/base_spec.rb +186 -116
  141. data/spec/model/dataset_methods_spec.rb +7 -27
  142. data/spec/model/eager_loading_spec.rb +343 -867
  143. data/spec/model/hooks_spec.rb +160 -79
  144. data/spec/model/model_spec.rb +118 -165
  145. data/spec/model/plugins_spec.rb +7 -13
  146. data/spec/model/record_spec.rb +138 -207
  147. data/spec/model/spec_helper.rb +10 -73
  148. metadata +14 -8
@@ -10,93 +10,6 @@ if ENV['SEQUEL_COLUMNS_INTROSPECTION']
10
10
  Sequel::Dataset.introspect_all_columns
11
11
  end
12
12
 
13
- class MockDataset < Sequel::Dataset
14
- def insert(*args)
15
- @db.execute insert_sql(*args)
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
- [Thread.new{Sequel.thread_application_timezone = 'America/New_York'; sleep 0.03; Sequel.application_timezone.should == @tz_out},
70
- Thread.new{sleep 0.01; Sequel.thread_application_timezone = 'America/Los_Angeles'; sleep 0.01; Sequel.application_timezone.should == @tz_in}].each{|x| x.join}
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
@@ -29,6 +29,9 @@ describe "NestedAttributes plugin" do
29
29
  end
30
30
  end
31
31
  db = Sequel::Database.new({})
32
+ def db.connect(opts)
33
+ Object.new
34
+ end
32
35
  db.meta_def(:dataset) do |*a|
33
36
  x = super(*a)
34
37
  x.extend(ds_mod)
@@ -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(:items, :reload=>true).and_return(schem.to_a.sort_by{|x| x[0].to_s})
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]
@@ -1,4 +1,5 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+ require 'yaml'
2
3
 
3
4
  describe "serialization_modification_detection plugin" do
4
5
  before do
@@ -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 = '--- 23\n' WHERE (id = 1)",
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 = '--- 23\n' WHERE (id = 1)",
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