sequel 4.31.0 → 4.32.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +24 -0
  3. data/Rakefile +17 -15
  4. data/doc/association_basics.rdoc +7 -3
  5. data/doc/opening_databases.rdoc +7 -0
  6. data/doc/release_notes/4.32.0.txt +132 -0
  7. data/doc/schema_modification.rdoc +1 -1
  8. data/doc/security.rdoc +70 -26
  9. data/doc/testing.rdoc +1 -0
  10. data/lib/sequel/adapters/jdbc.rb +2 -1
  11. data/lib/sequel/adapters/postgres.rb +3 -4
  12. data/lib/sequel/adapters/shared/mysql.rb +14 -1
  13. data/lib/sequel/adapters/shared/sqlite.rb +2 -2
  14. data/lib/sequel/extensions/_pretty_table.rb +2 -0
  15. data/lib/sequel/extensions/arbitrary_servers.rb +2 -0
  16. data/lib/sequel/extensions/columns_introspection.rb +2 -0
  17. data/lib/sequel/extensions/connection_validator.rb +2 -0
  18. data/lib/sequel/extensions/constraint_validations.rb +2 -0
  19. data/lib/sequel/extensions/core_extensions.rb +1 -5
  20. data/lib/sequel/extensions/current_datetime_timestamp.rb +2 -0
  21. data/lib/sequel/extensions/dataset_source_alias.rb +2 -0
  22. data/lib/sequel/extensions/date_arithmetic.rb +2 -0
  23. data/lib/sequel/extensions/empty_array_consider_nulls.rb +3 -1
  24. data/lib/sequel/extensions/error_sql.rb +2 -0
  25. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  26. data/lib/sequel/extensions/filter_having.rb +2 -0
  27. data/lib/sequel/extensions/from_block.rb +2 -0
  28. data/lib/sequel/extensions/graph_each.rb +2 -0
  29. data/lib/sequel/extensions/hash_aliases.rb +2 -0
  30. data/lib/sequel/extensions/inflector.rb +2 -0
  31. data/lib/sequel/extensions/looser_typecasting.rb +3 -1
  32. data/lib/sequel/extensions/meta_def.rb +2 -0
  33. data/lib/sequel/extensions/migration.rb +4 -0
  34. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +2 -0
  35. data/lib/sequel/extensions/named_timezones.rb +2 -0
  36. data/lib/sequel/extensions/no_auto_literal_strings.rb +84 -0
  37. data/lib/sequel/extensions/null_dataset.rb +2 -0
  38. data/lib/sequel/extensions/pagination.rb +2 -0
  39. data/lib/sequel/extensions/pg_array.rb +2 -4
  40. data/lib/sequel/extensions/pg_array_ops.rb +2 -0
  41. data/lib/sequel/extensions/pg_enum.rb +2 -0
  42. data/lib/sequel/extensions/pg_hstore.rb +2 -4
  43. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  44. data/lib/sequel/extensions/pg_inet.rb +2 -4
  45. data/lib/sequel/extensions/pg_inet_ops.rb +2 -0
  46. data/lib/sequel/extensions/pg_interval.rb +2 -4
  47. data/lib/sequel/extensions/pg_json.rb +4 -4
  48. data/lib/sequel/extensions/pg_json_ops.rb +3 -0
  49. data/lib/sequel/extensions/pg_loose_count.rb +2 -0
  50. data/lib/sequel/extensions/pg_range.rb +2 -4
  51. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  52. data/lib/sequel/extensions/pg_row.rb +2 -4
  53. data/lib/sequel/extensions/pg_row_ops.rb +2 -0
  54. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -0
  55. data/lib/sequel/extensions/pretty_table.rb +2 -0
  56. data/lib/sequel/extensions/query.rb +3 -0
  57. data/lib/sequel/extensions/query_literals.rb +7 -5
  58. data/lib/sequel/extensions/round_timestamps.rb +4 -3
  59. data/lib/sequel/extensions/schema_caching.rb +2 -0
  60. data/lib/sequel/extensions/schema_dumper.rb +2 -0
  61. data/lib/sequel/extensions/select_remove.rb +2 -0
  62. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +2 -0
  63. data/lib/sequel/extensions/server_block.rb +3 -0
  64. data/lib/sequel/extensions/set_overrides.rb +2 -0
  65. data/lib/sequel/extensions/split_array_nil.rb +2 -0
  66. data/lib/sequel/extensions/thread_local_timezones.rb +2 -0
  67. data/lib/sequel/extensions/to_dot.rb +2 -0
  68. data/lib/sequel/model/associations.rb +95 -55
  69. data/lib/sequel/plugins/association_pks.rb +58 -33
  70. data/lib/sequel/plugins/eager_each.rb +22 -0
  71. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -2
  72. data/lib/sequel/plugins/tactical_eager_loading.rb +44 -3
  73. data/lib/sequel/version.rb +2 -2
  74. data/spec/adapters/mysql_spec.rb +34 -6
  75. data/spec/adapters/oracle_spec.rb +1 -1
  76. data/spec/bin_spec.rb +2 -2
  77. data/spec/core/dataset_spec.rb +7 -0
  78. data/spec/extensions/association_pks_spec.rb +38 -0
  79. data/spec/extensions/class_table_inheritance_spec.rb +24 -0
  80. data/spec/extensions/eager_each_spec.rb +25 -1
  81. data/spec/extensions/no_auto_literal_strings_spec.rb +65 -0
  82. data/spec/extensions/pg_range_spec.rb +1 -0
  83. data/spec/extensions/spec_helper.rb +5 -5
  84. data/spec/extensions/tactical_eager_loading_spec.rb +71 -17
  85. data/spec/integration/associations_test.rb +77 -62
  86. data/spec/integration/dataset_test.rb +3 -3
  87. data/spec/integration/plugin_test.rb +22 -0
  88. data/spec/integration/prepared_statement_test.rb +8 -8
  89. data/spec/integration/spec_helper.rb +4 -0
  90. data/spec/model/association_reflection_spec.rb +30 -0
  91. data/spec/model/associations_spec.rb +177 -16
  92. metadata +6 -2
@@ -1090,9 +1090,9 @@ describe "Sequel::Dataset main SQL methods" do
1090
1090
 
1091
1091
  it "#filter and #exclude should work with placeholder strings" do
1092
1092
  @ds.insert(20, 30)
1093
- @ds.filter("a > ?", 15).all.must_equal [{:a=>20, :b=>30}]
1094
- @ds.exclude("b < ?", 15).all.must_equal [{:a=>20, :b=>30}]
1095
- @ds.filter("b < ?", 15).invert.all.must_equal [{:a=>20, :b=>30}]
1093
+ @ds.filter(Sequel.lit("a > ?", 15)).all.must_equal [{:a=>20, :b=>30}]
1094
+ @ds.exclude(Sequel.lit("b < ?", 15)).all.must_equal [{:a=>20, :b=>30}]
1095
+ @ds.filter(Sequel.lit("b < ?", 15)).invert.all.must_equal [{:a=>20, :b=>30}]
1096
1096
  end
1097
1097
 
1098
1098
  it "#and and #or should work correctly" do
@@ -1302,6 +1302,11 @@ describe "AssociationPks plugin" do
1302
1302
  album.tag_pks.must_equal [@t1, @t2]
1303
1303
  album.save
1304
1304
  album_class.with_pk!(album.pk).tag_pks.sort.must_equal [@t1, @t2]
1305
+
1306
+ album.tag_pks = []
1307
+ album.tag_pks.must_equal []
1308
+ album.save
1309
+ album_class.with_pk!(album.pk).tag_pks.sort.must_equal []
1305
1310
  end
1306
1311
 
1307
1312
  it "should handle :delay=>:all association option for existing instances" do
@@ -1313,6 +1318,11 @@ describe "AssociationPks plugin" do
1313
1318
  album.tag_pks.must_equal [@t1, @t2]
1314
1319
  album.save_changes
1315
1320
  album_class.with_pk!(album.pk).tag_pks.sort.must_equal [@t1, @t2]
1321
+
1322
+ album.tag_pks = []
1323
+ album.tag_pks.must_equal []
1324
+ album.save_changes
1325
+ album_class.with_pk!(album.pk).tag_pks.sort.must_equal []
1316
1326
  end
1317
1327
 
1318
1328
  it "should set associated pks correctly for a one_to_many association" do
@@ -1330,6 +1340,9 @@ describe "AssociationPks plugin" do
1330
1340
  Artist[@ar1].album_pks = [@al1, @al2]
1331
1341
  Artist[@ar2].album_pks.must_equal [@al3]
1332
1342
  Album.order(:id).select_map(:artist_id).must_equal [@ar1, @ar1, @ar2]
1343
+
1344
+ Artist[@ar1].album_pks = []
1345
+ Album.order(:id).select_map(:artist_id).must_equal [nil, nil, @ar2]
1333
1346
  end
1334
1347
 
1335
1348
  it "should set associated pks correctly for a many_to_many association" do
@@ -1369,6 +1382,9 @@ describe "AssociationPks plugin" do
1369
1382
  Album[@al1].vocalist_pks = [@v1, @v2]
1370
1383
  Album[@al2].vocalist_pks.must_equal [@v3]
1371
1384
  Vocalist.order(:first, :last).select_map(:album_id).must_equal [@al1, @al1, @al2]
1385
+
1386
+ Album[@al1].vocalist_pks = []
1387
+ Vocalist.order(:first, :last).select_map(:album_id).must_equal [nil, nil, @al2]
1372
1388
  end
1373
1389
 
1374
1390
  it "should set associated right-side cpks correctly for a many_to_many association" do
@@ -1410,6 +1426,9 @@ describe "AssociationPks plugin" do
1410
1426
  Vocalist[@v1].instrument_pks = [@i1, @i2]
1411
1427
  Vocalist[@v2].instrument_pks.must_equal [@i3]
1412
1428
  Instrument.order(:id).select_map([:first, :last]).must_equal [@v1, @v1, @v2]
1429
+
1430
+ Vocalist[@v1].instrument_pks = []
1431
+ Instrument.order(:id).select_map([:first, :last]).must_equal [[nil, nil], [nil, nil], @v2]
1413
1432
  end
1414
1433
 
1415
1434
  it "should set associated pks correctly with left-side cpks for a many_to_many association" do
@@ -1451,6 +1470,9 @@ describe "AssociationPks plugin" do
1451
1470
  Vocalist[@v1].hit_pks = [@h1, @h2]
1452
1471
  Vocalist[@v2].hit_pks.must_equal [@h3]
1453
1472
  Hit.order(:year, :week).select_map([:first, :last]).must_equal [@v1, @v1, @v2]
1473
+
1474
+ Vocalist[@v1].hit_pks = []
1475
+ Hit.order(:year, :week).select_map([:first, :last]).must_equal [[nil, nil], [nil, nil], @v2]
1454
1476
  end
1455
1477
 
1456
1478
  it "should set associated right-side cpks correctly with left-side cpks for a many_to_many association" do
@@ -55,14 +55,14 @@ describe "Prepared Statements and Bound Arguments" do
55
55
  end
56
56
 
57
57
  it "should support placeholder literal strings with call" do
58
- @ds.filter("numb = ?", :$n).call(:select, :n=>10).must_equal [{:id=>1, :numb=>10}]
58
+ @ds.filter(Sequel.lit("numb = ?", :$n)).call(:select, :n=>10).must_equal [{:id=>1, :numb=>10}]
59
59
  end
60
60
 
61
61
  it "should support named placeholder literal strings and handle multiple named placeholders correctly with call" do
62
- @ds.filter("numb = :n", :n=>:$n).call(:select, :n=>10).must_equal [{:id=>1, :numb=>10}]
62
+ @ds.filter(Sequel.lit("numb = :n", :n=>:$n)).call(:select, :n=>10).must_equal [{:id=>1, :numb=>10}]
63
63
  @ds.insert(:numb=>20)
64
64
  @ds.insert(:numb=>30)
65
- @ds.filter("numb > :n1 AND numb < :n2 AND numb = :n3", :n3=>:$n3, :n2=>:$n2, :n1=>:$n1).call(:select, :n3=>20, :n2=>30, :n1=>10).must_equal [{:id=>2, :numb=>20}]
65
+ @ds.filter(Sequel.lit("numb > :n1 AND numb < :n2 AND numb = :n3", :n3=>:$n3, :n2=>:$n2, :n1=>:$n1)).call(:select, :n3=>20, :n2=>30, :n1=>10).must_equal [{:id=>2, :numb=>20}]
66
66
  end
67
67
 
68
68
  it "should support datasets with static sql and placeholders with call" do
@@ -78,7 +78,7 @@ describe "Prepared Statements and Bound Arguments" do
78
78
  end
79
79
 
80
80
  it "should support subselects with literal strings with call" do
81
- @ds.filter(:id=>:$i, :numb=>@ds.select(:numb).filter("numb = ?", :$n)).call(:select, :n=>10, :i=>1).must_equal [{:id=>1, :numb=>10}]
81
+ @ds.filter(:id=>:$i, :numb=>@ds.select(:numb).filter(Sequel.lit("numb = ?", :$n))).call(:select, :n=>10, :i=>1).must_equal [{:id=>1, :numb=>10}]
82
82
  end
83
83
 
84
84
  it "should support subselects with static sql and placeholders with call" do
@@ -186,14 +186,14 @@ describe "Prepared Statements and Bound Arguments" do
186
186
  end
187
187
 
188
188
  it "should support placeholder literal strings with prepare" do
189
- @ds.filter("numb = ?", :$n).prepare(:select, :seq_select).call(:n=>10).must_equal [{:id=>1, :numb=>10}]
189
+ @ds.filter(Sequel.lit("numb = ?", :$n)).prepare(:select, :seq_select).call(:n=>10).must_equal [{:id=>1, :numb=>10}]
190
190
  end
191
191
 
192
192
  it "should support named placeholder literal strings and handle multiple named placeholders correctly with prepare" do
193
- @ds.filter("numb = :n", :n=>:$n).prepare(:select, :seq_select).call(:n=>10).must_equal [{:id=>1, :numb=>10}]
193
+ @ds.filter(Sequel.lit("numb = :n", :n=>:$n)).prepare(:select, :seq_select).call(:n=>10).must_equal [{:id=>1, :numb=>10}]
194
194
  @ds.insert(:numb=>20)
195
195
  @ds.insert(:numb=>30)
196
- @ds.filter("numb > :n1 AND numb < :n2 AND numb = :n3", :n3=>:$n3, :n2=>:$n2, :n1=>:$n1).call(:select, :n3=>20, :n2=>30, :n1=>10).must_equal [{:id=>2, :numb=>20}]
196
+ @ds.filter(Sequel.lit("numb > :n1 AND numb < :n2 AND numb = :n3", :n3=>:$n3, :n2=>:$n2, :n1=>:$n1)).call(:select, :n3=>20, :n2=>30, :n1=>10).must_equal [{:id=>2, :numb=>20}]
197
197
  end
198
198
 
199
199
  it "should support datasets with static sql and placeholders with prepare" do
@@ -209,7 +209,7 @@ describe "Prepared Statements and Bound Arguments" do
209
209
  end
210
210
 
211
211
  it "should support subselects with literal strings with prepare" do
212
- @ds.filter(:id=>:$i, :numb=>@ds.select(:numb).filter("numb = ?", :$n)).prepare(:select, :seq_select).call(:n=>10, :i=>1).must_equal [{:id=>1, :numb=>10}]
212
+ @ds.filter(:id=>:$i, :numb=>@ds.select(:numb).filter(Sequel.lit("numb = ?", :$n))).prepare(:select, :seq_select).call(:n=>10, :i=>1).must_equal [{:id=>1, :numb=>10}]
213
213
  end
214
214
 
215
215
  it "should support subselects with static sql and placeholders with prepare" do
@@ -36,6 +36,10 @@ if DB.adapter_scheme == :ibmdb || (DB.adapter_scheme == :ado && DB.database_type
36
36
  end
37
37
  end
38
38
 
39
+ if ENV['SEQUEL_NO_AUTO_LITERAL_STRINGS']
40
+ DB.extension :no_auto_literal_strings
41
+ end
42
+
39
43
  if ENV['SEQUEL_ERROR_SQL']
40
44
  DB.extension :error_sql
41
45
  end
@@ -523,3 +523,33 @@ describe Sequel::Model::Associations::AssociationReflection, "with caching disab
523
523
  end
524
524
  end
525
525
 
526
+ describe Sequel::Model::Associations::AssociationReflection, "with default association options" do
527
+ before do
528
+ @db = Sequel.mock
529
+ @c = Class.new(Sequel::Model)
530
+ @c.dataset = @db[:foo]
531
+ end
532
+
533
+ it "should use default_association_options as defaults" do
534
+ @c.default_association_options = {:foo=>1, :bar=>2}
535
+ @c.many_to_one :c, :class=>@c, :foo=>3
536
+ r = @c.association_reflection(:c)
537
+ r[:foo].must_equal 3
538
+ r[:bar].must_equal 2
539
+ end
540
+
541
+ it "should inherit default_association_options" do
542
+ @c.default_association_options = {:foo=>1, :bar=>2}
543
+ c = Class.new(@c)
544
+ c.many_to_one :c, :class=>c, :foo=>3
545
+ r = c.association_reflection(:c)
546
+ r[:foo].must_equal 3
547
+ r[:bar].must_equal 2
548
+
549
+ @c.default_association_options[:bar] = 4
550
+ c.many_to_one :d, :class=>c, :foo=>3
551
+ r = c.association_reflection(:d)
552
+ r[:foo].must_equal 3
553
+ r[:bar].must_equal 2
554
+ end
555
+ end
@@ -448,6 +448,8 @@ describe Sequel::Model, "many_to_one" do
448
448
  d.associations[:parent] = 42
449
449
  d.parent(true).wont_equal 42
450
450
  DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
451
+ d.parent(:reload=>true).wont_equal 42
452
+ DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
451
453
  end
452
454
 
453
455
  it "should use a callback if given one as the argument" do
@@ -728,7 +730,7 @@ describe Sequel::Model, "one_to_one" do
728
730
  DB.reset
729
731
  end
730
732
 
731
- it "should have the getter method return a single object if the :one_to_one option is true" do
733
+ it "should have the getter method return a single object" do
732
734
  @c2.one_to_one :attribute, :class => @c1
733
735
  att = @c2.new(:id => 1234).attribute
734
736
  DB.sqls.must_equal ['SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 1']
@@ -1122,21 +1124,6 @@ describe Sequel::Model, "one_to_one" do
1122
1124
  proc{p.parent = nil}.must_raise(Sequel::HookFailed)
1123
1125
  end
1124
1126
 
1125
- it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
1126
- p = @c2.load(:id=>321)
1127
- c = @c2.load(:id=>123)
1128
- p.raise_on_save_failure = false
1129
- @c2.one_to_one :parent, :class => @c2, :before_set=>:bs
1130
- def p.bs(x) cancel_action end
1131
- def p._parent=; raise; end
1132
- proc{p.parent = c}.must_raise(Sequel::HookFailed)
1133
-
1134
- p.associations[:parent].must_equal nil
1135
- p.associations[:parent] = c
1136
- p.parent.must_equal c
1137
- proc{p.parent = nil}.must_raise(Sequel::HookFailed)
1138
- end
1139
-
1140
1127
  it "should not validate the associated object in setter if the :validate=>false option is used" do
1141
1128
  @c2.one_to_one :parent, :class => @c2, :validate=>false
1142
1129
  n = @c2.new(:id => 1234)
@@ -2814,6 +2801,9 @@ describe Sequel::Model, "one_through_one" do
2814
2801
  [@c1, @c2].each{|c| c.dataset._fetch = {}}
2815
2802
  DB.reset
2816
2803
  end
2804
+ after do
2805
+ DB.fetch = {:id => 1, :x => 1}
2806
+ end
2817
2807
 
2818
2808
  it "should use implicit key values and join table if omitted" do
2819
2809
  @c2.one_through_one :attribute, :class => @c1
@@ -3021,6 +3011,177 @@ describe Sequel::Model, "one_through_one" do
3021
3011
  @c2.one_through_one :attribute, :class => @c1, :distinct=>true
3022
3012
  @c2.load(:id=>10).attribute_dataset.sql.must_equal "SELECT DISTINCT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 10) LIMIT 1"
3023
3013
  end
3014
+
3015
+ it "should not add a setter method if the :read_only option is true" do
3016
+ @c2.one_through_one :attribute, :class => @c1, :read_only=>true
3017
+ im = @c2.instance_methods.collect{|x| x.to_s}
3018
+ im.must_include('attribute')
3019
+ im.wont_include('attribute=')
3020
+ end
3021
+
3022
+ it "should add a setter method" do
3023
+ @c2.one_through_one :attribute, :class => @c1
3024
+ attrib = @c1.new(:id=>3)
3025
+ DB.fetch = []
3026
+ o = @c2.load(:id => 1234)
3027
+ o.attribute = nil
3028
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1"]
3029
+
3030
+ o.attribute = attrib
3031
+ sqls = DB.sqls
3032
+ ["INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (3, 1234)",
3033
+ "INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 3)"].must_include(sqls.slice! 1)
3034
+ sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1"]
3035
+
3036
+ DB.fetch = {:node_id=>1234, :attribute_id=>5}
3037
+ o.attribute = nil
3038
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1",
3039
+ "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 5))"]
3040
+
3041
+ o.attribute = attrib
3042
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1",
3043
+ "UPDATE attributes_nodes SET attribute_id = 3 WHERE ((node_id = 1234) AND (attribute_id = 5))"]
3044
+
3045
+ @c2.load(:id => 1234).attribute = @c1.new(:id=>5)
3046
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1"]
3047
+ end
3048
+
3049
+ it "should use a transaction in the setter method" do
3050
+ @c2.one_through_one :attribute, :class => @c1
3051
+ @c2.use_transactions = true
3052
+ attrib = @c1.load(:id=>3)
3053
+ DB.fetch = []
3054
+ @c2.new(:id => 1234).attribute = nil
3055
+ DB.sqls.must_equal ['BEGIN',
3056
+ "SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1",
3057
+ 'COMMIT']
3058
+ end
3059
+
3060
+ it "should have setter method respect :join_table_block option" do
3061
+ @c2.one_through_one :attribute, :class => @c1, :join_table_block=>proc{|ds| ds.where(:a)}
3062
+ attrib = @c1.new(:id=>3)
3063
+ DB.fetch = []
3064
+ o = @c2.new(:id => 1234)
3065
+ o.attribute = nil
3066
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1"]
3067
+
3068
+ o.attribute = attrib
3069
+ sqls = DB.sqls
3070
+ ["INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (3, 1234)",
3071
+ "INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 3)"].must_include(sqls.slice! 1)
3072
+ sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1"]
3073
+
3074
+ DB.fetch = {:node_id=>1234, :attribute_id=>5}
3075
+ o.attribute = nil
3076
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1",
3077
+ "DELETE FROM attributes_nodes WHERE (a AND (node_id = 1234) AND (attribute_id = 5))"]
3078
+
3079
+ o.attribute = attrib
3080
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1",
3081
+ "UPDATE attributes_nodes SET attribute_id = 3 WHERE (a AND (node_id = 1234) AND (attribute_id = 5))"]
3082
+ end
3083
+
3084
+ it "should have the setter method respect the :left_primary_key and :right_primary_key option" do
3085
+ @c2.one_through_one :attribute, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
3086
+ attrib = @c1.new(:id=>3, :yyy=>7)
3087
+ DB.fetch = []
3088
+ o = @c2.new(:id => 1234, :xxx=>5)
3089
+ o.attribute = nil
3090
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1"]
3091
+
3092
+ o.attribute = attrib
3093
+ sqls = DB.sqls
3094
+ ["INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (7, 5)",
3095
+ "INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (5, 7)"].must_include(sqls.slice! 1)
3096
+ sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1"]
3097
+
3098
+ DB.fetch = {:node_id=>1234, :attribute_id=>9}
3099
+ o.attribute = nil
3100
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1",
3101
+ "DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 9))"]
3102
+
3103
+ o.attribute = attrib
3104
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1",
3105
+ "UPDATE attributes_nodes SET attribute_id = 7 WHERE ((node_id = 5) AND (attribute_id = 9))"]
3106
+ end
3107
+
3108
+ it "should have the setter method respect composite keys" do
3109
+ @c2.one_through_one :attribute, :class => @c1, :left_key=>[:node_id, :y], :left_primary_key=>[:id, :x], :right_key=>[:attribute_id, :z], :right_primary_key=>[:id, :w]
3110
+ attrib = @c1.load(:id=>3, :w=>7)
3111
+ @c1.def_column_alias :w, :w
3112
+ DB.fetch = []
3113
+ o = @c2.new(:id => 1234, :x=>5)
3114
+ o.attribute = nil
3115
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1"]
3116
+
3117
+ o.attribute = attrib
3118
+ sqls = DB.sqls
3119
+ sqls.slice!(1).must_match(/\AINSERT INTO attributes_nodes \((attribute_id|z|node_id|y), (attribute_id|z|node_id|y), (attribute_id|z|node_id|y), (attribute_id|z|node_id|y)\) VALUES \((3|7|1234|5), (3|7|1234|5), (3|7|1234|5), (3|7|1234|5)\)\z/)
3120
+ sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1"]
3121
+
3122
+ DB.fetch = {:node_id=>1234, :attribute_id=>10, :y=>6, :z=>8}
3123
+ o.attribute = nil
3124
+ DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1",
3125
+ "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5) AND (attribute_id = 10) AND (z = 8))"]
3126
+
3127
+ o.attribute = attrib
3128
+ sqls = DB.sqls
3129
+ ["UPDATE attributes_nodes SET attribute_id = 3, z = 7 WHERE ((node_id = 1234) AND (y = 5) AND (attribute_id = 10) AND (z = 8))",
3130
+ "UPDATE attributes_nodes SET z = 7, attribute_id = 3 WHERE ((node_id = 1234) AND (y = 5) AND (attribute_id = 10) AND (z = 8))"].must_include(sqls.slice!(1))
3131
+ sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1"]
3132
+ end
3133
+
3134
+ it "should raise an error if the current model object that doesn't have a valid primary key" do
3135
+ @c2.one_through_one :attribute, :class => @c1
3136
+ p = @c2.new
3137
+ c = @c2.load(:id=>123)
3138
+ proc{c.attribute = p}.must_raise(Sequel::Error)
3139
+ end
3140
+
3141
+ it "should raise an error if the associated object that doesn't have a valid primary key" do
3142
+ @c2.one_through_one :attribute, :class => @c1
3143
+ p = @c2.new
3144
+ c = @c2.load(:id=>123)
3145
+ proc{p.attribute = c}.must_raise(Sequel::Error)
3146
+ end
3147
+
3148
+ it "should make the change to the foreign_key value inside a _association= method" do
3149
+ @c2.one_through_one :attribute, :class => @c1
3150
+ @c2.private_instance_methods.collect{|x| x.to_s}.sort.must_include("_attribute=")
3151
+ attrib = @c1.new(:id=>3)
3152
+ o = @c2.new(:id => 1234)
3153
+ def o._attribute=(x)
3154
+ @x = x
3155
+ end
3156
+ o.attribute = attrib
3157
+ o.instance_variable_get(:@x).must_equal attrib
3158
+ end
3159
+
3160
+ it "should have a :setter option define the _association= method" do
3161
+ @c2.one_through_one :attribute, :class => @c1, :setter=>proc{|x| @x = x}
3162
+ attrib = @c1.new(:id=>3)
3163
+ o = @c2.new(:id => 1234)
3164
+ o.attribute = attrib
3165
+ o.instance_variable_get(:@x).must_equal attrib
3166
+ end
3167
+
3168
+ it "should support (before|after)_set callbacks" do
3169
+ h = []
3170
+ @c2.one_through_one :attribute, :class => @c1, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << :l}
3171
+ @c2.class_eval do
3172
+ self::Foo = h
3173
+ def blah(x)
3174
+ model::Foo << (x ? x.pk : :x)
3175
+ end
3176
+ end
3177
+ attrib = @c1.new(:id=>3)
3178
+ o = @c2.new(:id => 1234)
3179
+ h.must_equal []
3180
+ o.attribute = attrib
3181
+ h.must_equal [1234, -3, 3, :l]
3182
+ o.attribute = nil
3183
+ h.must_equal [1234, -3, 3, :l, 1234, :y, :x, :l]
3184
+ end
3024
3185
  end
3025
3186
 
3026
3187
  describe "Filtering by associations" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.31.0
4
+ version: 4.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-01 00:00:00.000000000 Z
11
+ date: 2016-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -232,6 +232,7 @@ extra_rdoc_files:
232
232
  - doc/release_notes/4.29.0.txt
233
233
  - doc/release_notes/4.30.0.txt
234
234
  - doc/release_notes/4.31.0.txt
235
+ - doc/release_notes/4.32.0.txt
235
236
  files:
236
237
  - CHANGELOG
237
238
  - MIT-LICENSE
@@ -352,6 +353,7 @@ files:
352
353
  - doc/release_notes/4.3.0.txt
353
354
  - doc/release_notes/4.30.0.txt
354
355
  - doc/release_notes/4.31.0.txt
356
+ - doc/release_notes/4.32.0.txt
355
357
  - doc/release_notes/4.4.0.txt
356
358
  - doc/release_notes/4.5.0.txt
357
359
  - doc/release_notes/4.6.0.txt
@@ -488,6 +490,7 @@ files:
488
490
  - lib/sequel/extensions/migration.rb
489
491
  - lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb
490
492
  - lib/sequel/extensions/named_timezones.rb
493
+ - lib/sequel/extensions/no_auto_literal_strings.rb
491
494
  - lib/sequel/extensions/null_dataset.rb
492
495
  - lib/sequel/extensions/pagination.rb
493
496
  - lib/sequel/extensions/pg_array.rb
@@ -697,6 +700,7 @@ files:
697
700
  - spec/extensions/mssql_optimistic_locking_spec.rb
698
701
  - spec/extensions/named_timezones_spec.rb
699
702
  - spec/extensions/nested_attributes_spec.rb
703
+ - spec/extensions/no_auto_literal_strings_spec.rb
700
704
  - spec/extensions/null_dataset_spec.rb
701
705
  - spec/extensions/optimistic_locking_spec.rb
702
706
  - spec/extensions/pagination_spec.rb