sequel 4.31.0 → 4.32.0

Sign up to get free protection for your applications and to get access to all the features.
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