sequel 4.40.0 → 4.41.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +30 -0
  3. data/Rakefile +6 -3
  4. data/doc/association_basics.rdoc +3 -3
  5. data/doc/opening_databases.rdoc +3 -3
  6. data/doc/release_notes/4.41.0.txt +77 -0
  7. data/doc/schema_modification.rdoc +11 -11
  8. data/lib/sequel/adapters/ado.rb +137 -9
  9. data/lib/sequel/adapters/ado/mssql.rb +1 -1
  10. data/lib/sequel/adapters/jdbc.rb +1 -1
  11. data/lib/sequel/adapters/mysql2.rb +0 -1
  12. data/lib/sequel/adapters/shared/db2.rb +30 -10
  13. data/lib/sequel/adapters/shared/mssql.rb +11 -6
  14. data/lib/sequel/database/query.rb +3 -6
  15. data/lib/sequel/database/schema_generator.rb +7 -1
  16. data/lib/sequel/database/schema_methods.rb +0 -6
  17. data/lib/sequel/dataset/actions.rb +4 -4
  18. data/lib/sequel/dataset/graph.rb +3 -2
  19. data/lib/sequel/dataset/misc.rb +23 -0
  20. data/lib/sequel/dataset/mutation.rb +15 -14
  21. data/lib/sequel/dataset/query.rb +25 -5
  22. data/lib/sequel/extensions/constraint_validations.rb +1 -3
  23. data/lib/sequel/extensions/sql_comments.rb +6 -1
  24. data/lib/sequel/model/associations.rb +7 -1
  25. data/lib/sequel/model/base.rb +1 -1
  26. data/lib/sequel/plugins/class_table_inheritance.rb +6 -6
  27. data/lib/sequel/plugins/hook_class_methods.rb +2 -2
  28. data/lib/sequel/plugins/json_serializer.rb +29 -7
  29. data/lib/sequel/version.rb +1 -1
  30. data/spec/adapters/firebird_spec.rb +2 -8
  31. data/spec/adapters/mssql_spec.rb +12 -12
  32. data/spec/adapters/mysql_spec.rb +11 -11
  33. data/spec/adapters/postgres_spec.rb +8 -9
  34. data/spec/adapters/spec_helper.rb +1 -0
  35. data/spec/adapters/sqlite_spec.rb +1 -3
  36. data/spec/core/database_spec.rb +3 -2
  37. data/spec/core/dataset_spec.rb +66 -22
  38. data/spec/core/expression_filters_spec.rb +4 -0
  39. data/spec/core/mock_adapter_spec.rb +1 -1
  40. data/spec/core/schema_generator_spec.rb +1 -1
  41. data/spec/core/schema_spec.rb +10 -1
  42. data/spec/core/spec_helper.rb +1 -0
  43. data/spec/extensions/class_table_inheritance_spec.rb +3 -0
  44. data/spec/extensions/connection_expiration_spec.rb +1 -1
  45. data/spec/extensions/graph_each_spec.rb +6 -0
  46. data/spec/extensions/hook_class_methods_spec.rb +46 -0
  47. data/spec/extensions/json_serializer_spec.rb +8 -3
  48. data/spec/extensions/set_overrides_spec.rb +4 -0
  49. data/spec/extensions/single_table_inheritance_spec.rb +2 -2
  50. data/spec/extensions/spec_helper.rb +1 -0
  51. data/spec/extensions/string_agg_spec.rb +4 -0
  52. data/spec/extensions/uuid_spec.rb +1 -2
  53. data/spec/integration/associations_test.rb +14 -0
  54. data/spec/integration/dataset_test.rb +17 -22
  55. data/spec/integration/schema_test.rb +3 -3
  56. data/spec/integration/spec_helper.rb +1 -0
  57. data/spec/integration/type_test.rb +1 -7
  58. data/spec/model/associations_spec.rb +26 -1
  59. data/spec/model/model_spec.rb +7 -1
  60. data/spec/model/spec_helper.rb +2 -0
  61. data/spec/sequel_warning.rb +4 -0
  62. metadata +6 -3
@@ -13,7 +13,7 @@ module Sequel
13
13
  #
14
14
  # album = Album[1]
15
15
  # album.to_json
16
- # # => '{"json_class"=>"Album","id"=>1,"name"=>"RF","artist_id"=>2}'
16
+ # # => '{"id"=>1,"name"=>"RF","artist_id"=>2}'
17
17
  #
18
18
  # In addition, you can provide options to control the JSON output:
19
19
  #
@@ -22,16 +22,23 @@ module Sequel
22
22
  # # => '{"json_class"="Album","name"=>"RF"}'
23
23
  #
24
24
  # album.to_json(:include=>:artist)
25
- # # => '{"json_class":"Album","id":1,"name":"RF","artist_id":2,
26
- # # "artist":{"json_class":"Artist","id":2,"name":"YJM"}}'
25
+ # # => '{"id":1,"name":"RF","artist_id":2,
26
+ # # "artist":{"id":2,"name":"YJM"}}'
27
27
  #
28
28
  # You can use a hash value with <tt>:include</tt> to pass options
29
29
  # to associations:
30
30
  #
31
31
  # album.to_json(:include=>{:artist=>{:only=>:name}})
32
- # # => '{"json_class":"Album","id":1,"name":"RF","artist_id":2,
33
- # # "artist":{"json_class":"Artist","name":"YJM"}}'
32
+ # # => '{"id":1,"name":"RF","artist_id":2,
33
+ # # "artist":{"name":"YJM"}}'
34
34
  #
35
+ # You can specify a name for a given association by passing <tt>:name</tt>
36
+ # to the <tt>:include</tt> hash
37
+ #
38
+ # album.to_json(:include=>{Sequel.as(:artist, :singer)=>{:only=>:name}})
39
+ # # => '{"id":1,"name":"RF","artist_id":2,
40
+ # # "singer":{"name":"YJM"}}'
41
+ #
35
42
  # You can specify the <tt>:root</tt> option to nest the JSON under the
36
43
  # name of the model:
37
44
  #
@@ -299,6 +306,13 @@ module Sequel
299
306
  if inc = opts[:include]
300
307
  if inc.is_a?(Hash)
301
308
  inc.each do |k, v|
309
+ if k.is_a?(Sequel::SQL::AliasedExpression)
310
+ key_name = k.aliaz.to_s
311
+ k = k.expression
312
+ else
313
+ key_name = k.to_s
314
+ end
315
+
302
316
  v = v.empty? ? [] : [v]
303
317
 
304
318
  objs = send(k)
@@ -309,14 +323,22 @@ module Sequel
309
323
  objs.is_a?(Array)
310
324
  end
311
325
 
312
- h[k.to_s] = if is_array
326
+ h[key_name] = if is_array
313
327
  objs.map{|obj| Literal.new(Sequel.object_to_json(obj, *v))}
314
328
  else
315
329
  Literal.new(Sequel.object_to_json(objs, *v))
316
330
  end
317
331
  end
318
332
  else
319
- Array(inc).each{|c| h[c.to_s] = send(c)}
333
+ Array(inc).each do |c|
334
+ if c.is_a?(Sequel::SQL::AliasedExpression)
335
+ key_name = c.aliaz.to_s
336
+ c = c.expression
337
+ else
338
+ key_name = c.to_s
339
+ end
340
+ h[key_name] = send(c)
341
+ end
320
342
  end
321
343
  end
322
344
 
@@ -5,7 +5,7 @@ module Sequel
5
5
  MAJOR = 4
6
6
  # The minor version of Sequel. Bumped for every non-patch level
7
7
  # release, generally around once a month.
8
- MINOR = 40
8
+ MINOR = 41
9
9
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
10
10
  # releases that fix regressions from previous versions.
11
11
  TINY = 0
@@ -58,9 +58,8 @@ end
58
58
 
59
59
  describe "A Firebird dataset" do
60
60
  before do
61
- @d = DB[:test]
62
- @d.delete # remove all records
63
- @d.quote_identifiers = true
61
+ @d = DB[:test].with_quote_identifiers(true)
62
+ @d.delete
64
63
  end
65
64
 
66
65
  it "should return the correct record count" do
@@ -90,8 +89,6 @@ describe "A Firebird dataset" do
90
89
  @d << {:name => 'def', :val => 789}
91
90
  @d.filter(:name => 'abc').update(:val => 530)
92
91
 
93
- # the third record should stay the same
94
- # floating-point precision bullshit
95
92
  @d[:name => 'def'][:val].must_equal 789
96
93
  @d.filter(:val => 530).count.must_equal 2
97
94
  end
@@ -112,7 +109,6 @@ describe "A Firebird dataset" do
112
109
  end
113
110
 
114
111
  it "should quote columns and tables using double quotes if quoting identifiers" do
115
- @d.quote_identifiers = true
116
112
  @d.select(:name).sql.must_equal \
117
113
  'SELECT "NAME" FROM "TEST"'
118
114
 
@@ -157,7 +153,6 @@ describe "A Firebird dataset" do
157
153
  end
158
154
 
159
155
  it "should quote fields correctly when reversing the order if quoting identifiers" do
160
- @d.quote_identifiers = true
161
156
  @d.reverse_order(:name).sql.must_equal \
162
157
  'SELECT * FROM "TEST" ORDER BY "NAME" DESC'
163
158
 
@@ -220,7 +215,6 @@ describe "A Firebird dataset" do
220
215
 
221
216
  it "should quote and upcase reserved keywords" do
222
217
  @d = DB[:testing]
223
- @d.quote_identifiers = true
224
218
  @d.select(:select).sql.must_equal \
225
219
  'SELECT "SELECT" FROM "TESTING"'
226
220
  end
@@ -247,7 +247,7 @@ describe "Offset support" do
247
247
  @db.create_table!(:i){Integer :id; Integer :parent_id}
248
248
  @ds = @db[:i].order(:id)
249
249
  @hs = []
250
- @ds.row_proc = proc{|r| @hs << r.dup; r[:id] *= 2; r[:parent_id] *= 3; r}
250
+ @ds = @ds.with_row_proc(proc{|r| @hs << r.dup; r[:id] *= 2; r[:parent_id] *= 3; r})
251
251
  @ds.import [:id, :parent_id], [[1,nil],[2,nil],[3,1],[4,1],[5,3],[6,5]]
252
252
  end
253
253
  after do
@@ -356,7 +356,9 @@ describe "MSSSQL::Dataset#insert" do
356
356
  end
357
357
 
358
358
  it "should have insert_select return nil if the server version is not 2005+" do
359
- def @ds.server_version() 8000760 end
359
+ @ds = @ds.with_extend(Module.new do
360
+ def server_version() 8000760 end
361
+ end)
360
362
  @ds.insert_select(:value=>10).must_equal nil
361
363
  end
362
364
 
@@ -424,20 +426,16 @@ describe "A MSSQL database" do
424
426
  end
425
427
 
426
428
  describe "MSSQL::Database#rename_table" do
427
- after do
428
- DB.drop_table?(:foo)
429
- end
430
-
431
429
  it "should work on non-schema bound tables which need escaping" do
432
430
  DB.quote_identifiers = true
433
431
  DB.create_table! :'foo bar' do
434
432
  text :name
435
433
  end
436
- DB.drop_table? :foo
437
434
  DB.rename_table 'foo bar', 'foo'
435
+ DB.drop_table :foo
438
436
  end
439
437
 
440
- it "should work on schema bound tables" do
438
+ it "should work on schema bound tables within the same schema" do
441
439
  DB.execute(<<-SQL)
442
440
  IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'MY')
443
441
  EXECUTE sp_executesql N'create schema MY'
@@ -447,6 +445,7 @@ describe "MSSQL::Database#rename_table" do
447
445
  end
448
446
  DB.rename_table Sequel[:MY][:foo], Sequel[:MY][:bar]
449
447
  DB.rename_table Sequel[:MY][:bar], :foo
448
+ DB.drop_table Sequel[:MY][:foo]
450
449
  end
451
450
  end
452
451
 
@@ -488,10 +487,11 @@ describe "MSSQL::Database#mssql_unicode_strings = false" do
488
487
  DB.create_table!(:items){String :name}
489
488
  ds = DB[:items]
490
489
  ds.mssql_unicode_strings.must_equal false
491
- ds.mssql_unicode_strings = true
492
- ds.mssql_unicode_strings.must_equal true
493
- ds.insert(:name=>'foo')
494
- ds.select_map(:name).must_equal ['foo']
490
+ ds1 = ds.with_mssql_unicode_strings(true)
491
+ ds.mssql_unicode_strings.must_equal false
492
+ ds1.mssql_unicode_strings.must_equal true
493
+ ds1.insert(:name=>'foo')
494
+ ds1.select_map(:name).must_equal ['foo']
495
495
  end
496
496
  end
497
497
 
@@ -150,9 +150,10 @@ if [:mysql, :mysql2].include?(DB.adapter_scheme)
150
150
 
151
151
  it "should allow disabling the conversion on a per-dataset basis" do
152
152
  @db.convert_tinyint_to_bool = true
153
- ds = @ds.clone
154
- def ds.cast_tinyint_integer?(f) true end #mysql
155
- def ds.convert_tinyint_to_bool?() false end #mysql2
153
+ ds = @ds.with_extend(Module.new do
154
+ def cast_tinyint_integer?(f) true end #mysql
155
+ def convert_tinyint_to_bool?() false end #mysql2
156
+ end)
156
157
  ds.delete
157
158
  ds << {:b=>true, :i=>10}
158
159
  ds.all.must_equal [{:b=>1, :i=>10}]
@@ -172,7 +173,7 @@ describe "A MySQL dataset" do
172
173
  end
173
174
 
174
175
  it "should quote columns and tables using back-ticks if quoting identifiers" do
175
- @d.quote_identifiers = true
176
+ @d = @d.with_quote_identifiers(true)
176
177
  @d.select(:name).sql.must_equal 'SELECT `name` FROM `items`'
177
178
  @d.select(Sequel.lit('COUNT(*)')).sql.must_equal 'SELECT COUNT(*) FROM `items`'
178
179
  @d.select(Sequel.function(:max, :value)).sql.must_equal 'SELECT max(`value`) FROM `items`'
@@ -190,7 +191,7 @@ describe "A MySQL dataset" do
190
191
  end
191
192
 
192
193
  it "should quote fields correctly when reversing the order" do
193
- @d.quote_identifiers = true
194
+ @d = @d.with_quote_identifiers(true)
194
195
  @d.reverse_order(:name).sql.must_equal 'SELECT * FROM `items` ORDER BY `name` DESC'
195
196
  @d.reverse_order(Sequel.desc(:name)).sql.must_equal 'SELECT * FROM `items` ORDER BY `name` ASC'
196
197
  @d.reverse_order(:name, Sequel.desc(:test)).sql.must_equal 'SELECT * FROM `items` ORDER BY `name` DESC, `test` ASC'
@@ -242,7 +243,7 @@ describe "MySQL datasets" do
242
243
  end
243
244
 
244
245
  it "should correctly quote column references" do
245
- @d.quote_identifiers = true
246
+ @d = @d.with_quote_identifiers(true)
246
247
  market = 'ICE'
247
248
  ack_stamp = Time.now - 15 * 60 # 15 minutes ago
248
249
  @d.select(:market, Sequel.function(:minute, Sequel.function(:from_unixtime, :ack)).as(:minute)).
@@ -321,8 +322,7 @@ describe "Joined MySQL dataset" do
321
322
  end
322
323
 
323
324
  it "should quote fields correctly" do
324
- @ds.quote_identifiers = true
325
- @ds.join(:attributes, :node_id => :id).sql.must_equal "SELECT * FROM `nodes` INNER JOIN `attributes` ON (`attributes`.`node_id` = `nodes`.`id`)"
325
+ @ds.with_quote_identifiers(true).join(:attributes, :node_id => :id).sql.must_equal "SELECT * FROM `nodes` INNER JOIN `attributes` ON (`attributes`.`node_id` = `nodes`.`id`)"
326
326
  end
327
327
 
328
328
  it "should put a having clause before an order by clause" do
@@ -1168,7 +1168,7 @@ if DB.adapter_scheme == :mysql or DB.adapter_scheme == :jdbc or DB.adapter_schem
1168
1168
  @d.call_sproc(:select, :test_sproc, 3).must_equal []
1169
1169
  @d.insert(:value=>1)
1170
1170
  @d.call_sproc(:select, :test_sproc, 4).must_equal [{:id=>nil, :value=>1, :b=>4}]
1171
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
1171
+ @d = @d.with_row_proc(proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r})
1172
1172
  @d.call_sproc(:select, :test_sproc, 3).must_equal [{:id=>nil, :value=>2, :b=>6}]
1173
1173
  end
1174
1174
 
@@ -1179,7 +1179,7 @@ if DB.adapter_scheme == :mysql or DB.adapter_scheme == :jdbc or DB.adapter_schem
1179
1179
  @d.call_sproc(:select, :test_sproc, 3, 4).must_equal []
1180
1180
  @d.insert(:value=>1)
1181
1181
  @d.call_sproc(:select, :test_sproc, 4, 5).must_equal [{:id=>nil, :value=>1, :b=>4, :d=>5}]
1182
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
1182
+ @d = @d.with_row_proc(proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r})
1183
1183
  @d.call_sproc(:select, :test_sproc, 3, 4).must_equal [{:id=>nil, :value=>2, :b=>6, :d => 8}]
1184
1184
  end
1185
1185
  end
@@ -1259,7 +1259,7 @@ if DB.adapter_scheme == :mysql
1259
1259
  end
1260
1260
 
1261
1261
  it "should have regular row_procs work when splitting multiple result sets" do
1262
- @ds.row_proc = proc{|x| x[x.keys.first] *= 2; x}
1262
+ @ds = @ds.with_row_proc(proc{|x| x[x.keys.first] *= 2; x})
1263
1263
  @ds.split_multiple_result_sets.all.must_equal [[{:a=>20}, {:a=>30}], [{:b=>40}, {:b=>50}]]
1264
1264
  end
1265
1265
 
@@ -964,13 +964,10 @@ describe "A PostgreSQL database" do
964
964
  @db[:posts].order(:a).map(:a).must_equal [1, 2, 10, 20, 21]
965
965
  end
966
966
 
967
- it "should support specifying Integer/Bignum/Fixnum types in primary keys and have them be auto incrementing" do
967
+ it "should support specifying Integer/Bignum types in primary keys and have them be auto incrementing" do
968
968
  @db.create_table(:posts){primary_key :a, :type=>Integer}
969
969
  @db[:posts].insert.must_equal 1
970
970
  @db[:posts].insert.must_equal 2
971
- @db.create_table!(:posts){primary_key :a, :type=>Fixnum}
972
- @db[:posts].insert.must_equal 1
973
- @db[:posts].insert.must_equal 2
974
971
  @db.create_table!(:posts){primary_key :a, :type=>:Bignum}
975
972
  @db[:posts].insert.must_equal 1
976
973
  @db[:posts].insert.must_equal 2
@@ -1730,11 +1727,13 @@ if DB.adapter_scheme == :postgres
1730
1727
  end
1731
1728
 
1732
1729
  it "should handle returning inside block" do
1733
- def @ds.check_return
1734
- use_cursor.each{|r| return}
1735
- end
1736
- @ds.check_return
1737
- @ds.all.must_equal @ds.use_cursor.all
1730
+ ds = @ds.with_extend(Module.new do
1731
+ def check_return
1732
+ use_cursor.each{|r| return}
1733
+ end
1734
+ end)
1735
+ ds.check_return
1736
+ ds.all.must_equal ds.use_cursor.all
1738
1737
  end
1739
1738
  end
1740
1739
 
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'logger'
3
+ require "#{File.dirname(File.dirname(__FILE__))}/sequel_warning.rb"
3
4
 
4
5
  if ENV['COVERAGE']
5
6
  require File.join(File.dirname(File.expand_path(__FILE__)), "../sequel_coverage")
@@ -457,9 +457,7 @@ describe "SQLite dataset" do
457
457
  end
458
458
 
459
459
  it "should have #explain work when identifier_output_method is modified" do
460
- ds = DB[:test]
461
- ds.identifier_output_method = :upcase
462
- ds.explain.must_be_kind_of(String)
460
+ DB[:test].with_identifier_output_method(:upcase).explain.must_be_kind_of(String)
463
461
  end
464
462
  end
465
463
 
@@ -1899,8 +1899,9 @@ describe "Database#raise_error" do
1899
1899
  def @db.database_error_regexps
1900
1900
  {/foo/ => Sequel::DatabaseDisconnectError, /bar/ => Sequel::ConstraintViolation}
1901
1901
  end
1902
- proc{@db.send(:raise_error, Interrupt.new('foo'))}.must_raise(Sequel::DatabaseDisconnectError)
1903
- proc{@db.send(:raise_error, Interrupt.new('bar'))}.must_raise(Sequel::ConstraintViolation)
1902
+ e = Class.new(StandardError)
1903
+ proc{@db.send(:raise_error, e.new('foo'))}.must_raise(Sequel::DatabaseDisconnectError)
1904
+ proc{@db.send(:raise_error, e.new('bar'))}.must_raise(Sequel::ConstraintViolation)
1904
1905
  end
1905
1906
  end
1906
1907
 
@@ -89,6 +89,33 @@ describe "Dataset" do
89
89
  @dataset.send(:output_identifier, "at_b_C").must_equal :C_b_ta
90
90
  end
91
91
 
92
+ it "should have with_quote_identifiers method which returns cloned dataset with changed literalization of identifiers" do
93
+ @dataset.with_quote_identifiers(true).literal(:a).must_equal '"a"'
94
+ @dataset.with_quote_identifiers(false).literal(:a).must_equal 'a'
95
+ ds = @dataset.freeze.with_quote_identifiers(false)
96
+ ds.literal(:a).must_equal 'a'
97
+ ds.frozen?.must_equal true
98
+ end
99
+
100
+ it "should have with_identifier_input_method method which returns cloned dataset with changed literalization of identifiers" do
101
+ @dataset.with_identifier_input_method(:upcase).literal(:a).must_equal 'A'
102
+ @dataset.with_identifier_input_method(:downcase).literal(:A).must_equal 'a'
103
+ @dataset.with_identifier_input_method(:reverse).literal(:at_b).must_equal 'b_ta'
104
+ ds = @dataset.freeze.with_identifier_input_method(:reverse)
105
+ ds.frozen?.must_equal true
106
+ ds.literal(:at_b).must_equal 'b_ta'
107
+ end
108
+
109
+ it "should have with_identifier_output_method method which returns cloned dataset with changed identifiers returned from the database" do
110
+ @dataset.send(:output_identifier, "at_b_C").must_equal :at_b_C
111
+ @dataset.with_identifier_output_method(:upcase).send(:output_identifier, "at_b_C").must_equal :AT_B_C
112
+ @dataset.with_identifier_output_method(:downcase).send(:output_identifier, "at_b_C").must_equal :at_b_c
113
+ @dataset.with_identifier_output_method(:reverse).send(:output_identifier, "at_b_C").must_equal :C_b_ta
114
+ ds = @dataset.freeze.with_identifier_output_method(:reverse)
115
+ ds.send(:output_identifier, "at_b_C").must_equal :C_b_ta
116
+ ds.frozen?.must_equal true
117
+ end
118
+
92
119
  it "should have output_identifier handle empty identifiers" do
93
120
  @dataset.send(:output_identifier, "").must_equal :untitled
94
121
  @dataset.identifier_output_method = :upcase
@@ -167,8 +194,7 @@ describe "Dataset#==" do
167
194
 
168
195
  it "should be different for datasets with different SQL" do
169
196
  ds = @db[:t]
170
- ds.quote_identifiers = true
171
- ds.wont_equal @db[:t]
197
+ ds.with_quote_identifiers(true).wont_equal ds
172
198
  end
173
199
  end
174
200
 
@@ -194,8 +220,7 @@ describe "Dataset#hash" do
194
220
 
195
221
  it "should be different for datasets with different SQL" do
196
222
  ds = @db[:t]
197
- ds.quote_identifiers = true
198
- ds.hash.wont_equal @db[:t].hash
223
+ ds.with_quote_identifiers(true).hash.wont_equal ds.hash
199
224
  end
200
225
  end
201
226
 
@@ -937,7 +962,7 @@ describe "Dataset#literal" do
937
962
  end
938
963
 
939
964
  it "should support spaces inside column names" do
940
- @ds.quote_identifiers = true
965
+ @ds = @ds.with_quote_identifiers(true)
941
966
  @ds.literal(:"AB C").must_equal '"AB C"'
942
967
  @ds.literal(:"Zvas htoy__aB cD").must_equal '"Zvas htoy"."aB cD"'
943
968
  @ds.literal(:"aB cD___XX XX").must_equal '"aB cD" AS "XX XX"'
@@ -1061,7 +1086,7 @@ describe "Dataset#literal" do
1061
1086
  end
1062
1087
 
1063
1088
  it "should not modify literal strings" do
1064
- @dataset.quote_identifiers = true
1089
+ @dataset = @dataset.with_quote_identifiers(true)
1065
1090
  @dataset.literal(Sequel.lit('col1 + 2')).must_equal 'col1 + 2'
1066
1091
  @dataset.update_sql(Sequel::SQL::Identifier.new(Sequel.lit('a')) => Sequel.lit('a + 2')).must_equal 'UPDATE "test" SET a = a + 2'
1067
1092
  end
@@ -1115,12 +1140,11 @@ describe "Dataset#from" do
1115
1140
  end
1116
1141
 
1117
1142
  it "should treat string arguments as identifiers" do
1118
- @dataset.quote_identifiers = true
1119
- @dataset.from('a').select_sql.must_equal "SELECT * FROM \"a\""
1143
+ @dataset.with_quote_identifiers(true).from('a').select_sql.must_equal "SELECT * FROM \"a\""
1120
1144
  end
1121
1145
 
1122
1146
  it "should not treat literal strings or blobs as identifiers" do
1123
- @dataset.quote_identifiers = true
1147
+ @dataset = @dataset.with_quote_identifiers(true)
1124
1148
  @dataset.from(Sequel.lit('a')).select_sql.must_equal "SELECT * FROM a"
1125
1149
  @dataset.from(Sequel.blob('a')).select_sql.must_equal "SELECT * FROM 'a'"
1126
1150
  end
@@ -1671,6 +1695,31 @@ describe "Dataset#offset" do
1671
1695
  end
1672
1696
  end
1673
1697
 
1698
+ describe "Dataset#with_extend" do
1699
+ it "should returned clone dataset extended with given modules" do
1700
+ d = Sequel.mock.dataset
1701
+ m1 = Module.new{def a; 1; end}
1702
+ m2 = Module.new{def b; a+2; end}
1703
+ d.with_extend(m1, m2).b.must_equal 3
1704
+ d.respond_to?(:b).must_equal false
1705
+ ds = d.freeze.with_extend(m1, m2)
1706
+ ds.b.must_equal 3
1707
+ ds.frozen?.must_equal true
1708
+ end
1709
+ end
1710
+
1711
+ describe "Dataset#with_row_proc" do
1712
+ it "should returned clone dataset with the given row_proc" do
1713
+ d = Sequel.mock.dataset
1714
+ l = lambda{|r| r}
1715
+ d.with_row_proc(l).row_proc.must_equal l
1716
+ assert_equal nil, d.row_proc
1717
+ ds = d.freeze.with_row_proc(l)
1718
+ ds.frozen?.must_equal true
1719
+ ds.row_proc.must_equal l
1720
+ end
1721
+ end
1722
+
1674
1723
  describe "Dataset#naked" do
1675
1724
  it "should returned clone dataset without row_proc" do
1676
1725
  d = Sequel.mock.dataset
@@ -2126,8 +2175,7 @@ end
2126
2175
 
2127
2176
  describe "Dataset#join_table" do
2128
2177
  before do
2129
- @d = Sequel.mock.dataset.from(:items)
2130
- @d.quote_identifiers = true
2178
+ @d = Sequel.mock.dataset.from(:items).with_quote_identifiers(true)
2131
2179
  end
2132
2180
 
2133
2181
  it "should format the JOIN clause properly" do
@@ -2791,7 +2839,7 @@ describe "Dataset#single_value!" do
2791
2839
 
2792
2840
  it "should call each and return the first value of the first record" do
2793
2841
  @db.fetch = [{:a=>1, :b=>2}, {:a=>3, :b=>4}]
2794
- @db[:test].single_value!.to_s.must_match /\A(1|2)\z/
2842
+ @db[:test].single_value!.to_s.must_match(/\A(1|2)\z/)
2795
2843
  @db.sqls.must_equal ['SELECT * FROM test']
2796
2844
  end
2797
2845
 
@@ -3302,8 +3350,7 @@ describe "Dataset#insert_sql" do
3302
3350
  end
3303
3351
 
3304
3352
  it "should quote string keys" do
3305
- @ds.quote_identifiers = true
3306
- @ds.insert_sql('c' => 'd').must_equal "INSERT INTO \"items\" (\"c\") VALUES ('d')"
3353
+ @ds.with_quote_identifiers(true).insert_sql('c' => 'd').must_equal "INSERT INTO \"items\" (\"c\") VALUES ('d')"
3307
3354
  end
3308
3355
 
3309
3356
  it "should accept array subscript references" do
@@ -3712,7 +3759,6 @@ describe "Dataset prepared statements and bound variables " do
3712
3759
  end
3713
3760
 
3714
3761
  it "should handle usage with Dataset.prepared_statements_module" do
3715
- m = Module.new
3716
3762
  @ds.extend(Sequel::Dataset.send(:prepared_statements_module, :prepare_bind, [Sequel::Dataset::ArgumentMapper, Sequel::Dataset::PreparedStatementMethods]){def foo; :bar; end})
3717
3763
  @ds.foo.must_equal :bar
3718
3764
  @ds.prepared_statement_name = 'foo'
@@ -4016,7 +4062,7 @@ describe "Sequel::Dataset #with and #with_recursive" do
4016
4062
  end
4017
4063
 
4018
4064
  it "#with and #with_recursive should quote the columns in the :args option" do
4019
- @ds.quote_identifiers = true
4065
+ @ds = @ds.with_quote_identifiers(true)
4020
4066
  @ds.with(:t, @db[:x], :args=>[:b]).sql.must_equal 'WITH "t"("b") AS (SELECT * FROM x) SELECT * FROM "t"'
4021
4067
  @ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c]).sql.must_equal 'WITH "t"("b", "c") AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM "t"'
4022
4068
  end
@@ -4631,8 +4677,7 @@ end
4631
4677
 
4632
4678
  describe "Dataset emulating bitwise operator support" do
4633
4679
  before do
4634
- @ds = Sequel::Database.new.dataset
4635
- @ds.quote_identifiers = true
4680
+ @ds = Sequel::Database.new.dataset.with_quote_identifiers(true)
4636
4681
  def @ds.complex_expression_sql_append(sql, op, args)
4637
4682
  complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:bitand, a, b)}
4638
4683
  end
@@ -4681,20 +4726,19 @@ describe "Dataset extensions" do
4681
4726
  end
4682
4727
 
4683
4728
  it "should be able to register an extension with a block and Database#extension call the block" do
4684
- @ds.quote_identifiers = false
4729
+ @ds = @ds.with_quote_identifiers(false)
4685
4730
  Sequel::Dataset.register_extension(:foo){|db| db.quote_identifiers = true}
4686
4731
  @ds.extension(:foo).quote_identifiers?.must_equal true
4687
4732
  end
4688
4733
 
4689
4734
  it "should be able to register an extension with a callable and Database#extension call the callable" do
4690
- @ds.quote_identifiers = false
4735
+ @ds = @ds.with_quote_identifiers(false)
4691
4736
  Sequel::Dataset.register_extension(:foo, proc{|db| db.quote_identifiers = true})
4692
4737
  @ds.extension(:foo).quote_identifiers?.must_equal true
4693
4738
  end
4694
4739
 
4695
4740
  it "should be able to load multiple extensions in the same call" do
4696
- @ds.quote_identifiers = false
4697
- @ds.identifier_input_method = :downcase
4741
+ @ds = @ds.with_quote_identifiers(false).with_identifier_input_method(:downcase)
4698
4742
  Sequel::Dataset.register_extension(:foo, proc{|ds| ds.quote_identifiers = true})
4699
4743
  Sequel::Dataset.register_extension(:bar, proc{|ds| ds.identifier_input_method = nil})
4700
4744
  ds = @ds.extension(:foo, :bar)