sequel 4.43.0 → 4.44.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/doc/active_record.rdoc +2 -2
  4. data/doc/code_order.rdoc +15 -0
  5. data/doc/dataset_filtering.rdoc +1 -1
  6. data/doc/model_dataset_method_design.rdoc +132 -0
  7. data/doc/opening_databases.rdoc +2 -2
  8. data/doc/release_notes/4.44.0.txt +125 -0
  9. data/lib/sequel/adapters/jdbc.rb +5 -1
  10. data/lib/sequel/adapters/jdbc/as400.rb +1 -1
  11. data/lib/sequel/adapters/postgres.rb +23 -14
  12. data/lib/sequel/core.rb +1 -1
  13. data/lib/sequel/database/schema_generator.rb +27 -0
  14. data/lib/sequel/dataset/actions.rb +1 -1
  15. data/lib/sequel/dataset/query.rb +5 -1
  16. data/lib/sequel/extensions/eval_inspect.rb +4 -4
  17. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  18. data/lib/sequel/extensions/to_dot.rb +1 -1
  19. data/lib/sequel/model.rb +3 -5
  20. data/lib/sequel/model/associations.rb +107 -4
  21. data/lib/sequel/model/base.rb +98 -12
  22. data/lib/sequel/model/dataset_module.rb +1 -1
  23. data/lib/sequel/plugins/active_model.rb +11 -3
  24. data/lib/sequel/plugins/association_dependencies.rb +7 -0
  25. data/lib/sequel/plugins/auto_validations.rb +10 -0
  26. data/lib/sequel/plugins/blacklist_security.rb +13 -4
  27. data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
  28. data/lib/sequel/plugins/column_conflicts.rb +8 -0
  29. data/lib/sequel/plugins/composition.rb +12 -2
  30. data/lib/sequel/plugins/constraint_validations.rb +12 -0
  31. data/lib/sequel/plugins/csv_serializer.rb +9 -0
  32. data/lib/sequel/plugins/defaults_setter.rb +6 -0
  33. data/lib/sequel/plugins/force_encoding.rb +4 -3
  34. data/lib/sequel/plugins/hook_class_methods.rb +6 -0
  35. data/lib/sequel/plugins/input_transformer.rb +9 -0
  36. data/lib/sequel/plugins/insert_returning_select.rb +8 -0
  37. data/lib/sequel/plugins/instance_hooks.rb +4 -3
  38. data/lib/sequel/plugins/json_serializer.rb +9 -0
  39. data/lib/sequel/plugins/lazy_attributes.rb +7 -0
  40. data/lib/sequel/plugins/many_through_many.rb +13 -2
  41. data/lib/sequel/plugins/nested_attributes.rb +7 -0
  42. data/lib/sequel/plugins/pg_array_associations.rb +18 -2
  43. data/lib/sequel/plugins/pg_row.rb +3 -3
  44. data/lib/sequel/plugins/pg_typecast_on_load.rb +7 -0
  45. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  46. data/lib/sequel/plugins/prepared_statements_safe.rb +7 -0
  47. data/lib/sequel/plugins/serialization.rb +9 -0
  48. data/lib/sequel/plugins/single_table_inheritance.rb +13 -1
  49. data/lib/sequel/plugins/subclasses.rb +15 -1
  50. data/lib/sequel/plugins/touch.rb +7 -0
  51. data/lib/sequel/plugins/tree.rb +7 -0
  52. data/lib/sequel/plugins/typecast_on_load.rb +7 -0
  53. data/lib/sequel/plugins/update_refresh.rb +24 -13
  54. data/lib/sequel/plugins/validation_class_methods.rb +13 -0
  55. data/lib/sequel/sql.rb +28 -0
  56. data/lib/sequel/version.rb +1 -1
  57. data/spec/adapters/postgres_spec.rb +18 -15
  58. data/spec/core/dataset_spec.rb +5 -0
  59. data/spec/core/expression_filters_spec.rb +33 -0
  60. data/spec/extensions/active_model_spec.rb +15 -1
  61. data/spec/extensions/association_dependencies_spec.rb +8 -0
  62. data/spec/extensions/auto_validations_spec.rb +8 -0
  63. data/spec/extensions/blacklist_security_spec.rb +6 -0
  64. data/spec/extensions/class_table_inheritance_spec.rb +9 -0
  65. data/spec/extensions/column_conflicts_spec.rb +6 -0
  66. data/spec/extensions/composition_spec.rb +8 -0
  67. data/spec/extensions/constraint_validations_plugin_spec.rb +12 -0
  68. data/spec/extensions/csv_serializer_spec.rb +7 -0
  69. data/spec/extensions/defaults_setter_spec.rb +7 -0
  70. data/spec/extensions/force_encoding_spec.rb +14 -0
  71. data/spec/extensions/hook_class_methods_spec.rb +10 -0
  72. data/spec/extensions/implicit_subquery_spec.rb +60 -0
  73. data/spec/extensions/input_transformer_spec.rb +10 -0
  74. data/spec/extensions/insert_returning_select_spec.rb +6 -0
  75. data/spec/extensions/json_serializer_spec.rb +7 -0
  76. data/spec/extensions/lazy_attributes_spec.rb +6 -0
  77. data/spec/extensions/many_through_many_spec.rb +44 -0
  78. data/spec/extensions/nested_attributes_spec.rb +5 -0
  79. data/spec/extensions/pg_array_associations_spec.rb +46 -0
  80. data/spec/extensions/pg_typecast_on_load_spec.rb +5 -0
  81. data/spec/extensions/prepared_statements_safe_spec.rb +5 -0
  82. data/spec/extensions/serialization_spec.rb +7 -0
  83. data/spec/extensions/single_table_inheritance_spec.rb +19 -2
  84. data/spec/extensions/subclasses_spec.rb +13 -0
  85. data/spec/extensions/to_dot_spec.rb +1 -1
  86. data/spec/extensions/touch_spec.rb +6 -0
  87. data/spec/extensions/tree_spec.rb +6 -0
  88. data/spec/extensions/typecast_on_load_spec.rb +6 -0
  89. data/spec/extensions/update_refresh_spec.rb +7 -1
  90. data/spec/extensions/validation_class_methods_spec.rb +13 -0
  91. data/spec/model/association_reflection_spec.rb +177 -0
  92. data/spec/model/associations_spec.rb +16 -0
  93. data/spec/model/dataset_methods_spec.rb +59 -0
  94. data/spec/model/model_spec.rb +59 -0
  95. metadata +8 -2
@@ -2057,6 +2057,11 @@ describe "Dataset#from_self" do
2057
2057
  @ds.from_self.sql.must_equal 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS t1'
2058
2058
  end
2059
2059
 
2060
+ it "should keep any existing columns" do
2061
+ @ds.columns(:id, :a)
2062
+ @ds.from_self.columns.must_equal [:id, :a]
2063
+ end
2064
+
2060
2065
  it "should modify only the new dataset" do
2061
2066
  @ds.from_self.select(:bogus).sql.must_equal 'SELECT bogus FROM (SELECT name FROM test LIMIT 1) AS t1'
2062
2067
  end
@@ -248,6 +248,10 @@ describe "Blockless Ruby Filters" do
248
248
  @d.l(~((((Sequel.lit('x') - :y)/(Sequel.expr(:x) + :y))*:z) <= 100)).must_equal '((((x - y) / (x + y)) * z) > 100)'
249
249
  end
250
250
 
251
+ it "should have LiteralString#inspect show it is a literal string" do
252
+ Sequel.lit('x').inspect.must_equal "#<Sequel::LiteralString \"x\">"
253
+ end
254
+
251
255
  it "should support hashes by ANDing the conditions" do
252
256
  @d.l(:x => 100, :y => 'a')[1...-1].split(' AND ').sort.must_equal ['(x = 100)', '(y = \'a\')']
253
257
  @d.l(:x => true, :y => false)[1...-1].split(' AND ').sort.must_equal ['(x IS TRUE)', '(y IS FALSE)']
@@ -270,6 +274,10 @@ describe "Blockless Ruby Filters" do
270
274
  @d.l([:x, :y]=>Sequel.value_list([[1,2], [3,4]])).must_equal '(((x = 1) AND (y = 2)) OR ((x = 3) AND (y = 4)))'
271
275
  @d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).must_equal '(((x = 1) AND (y = 2) AND (z = 5)) OR ((x = 3) AND (y = 4) AND (z = 6)))'
272
276
  end
277
+
278
+ it "should have SQL::ValueList#inspect show it is a value list" do
279
+ Sequel.value_list([[1,2], [3,4]]).inspect.must_equal "#<Sequel::SQL::ValueList [[1, 2], [3, 4]]>"
280
+ end
273
281
 
274
282
  it "should support StringExpression#+ for concatenation of SQL strings" do
275
283
  @d.lit(Sequel.expr(:x).sql_string + :y).must_equal '(x || y)'
@@ -952,6 +960,21 @@ describe "Sequel core extension replacements" do
952
960
  Sequel.blob(o).must_be_same_as(o)
953
961
  end
954
962
 
963
+ it "Sequel.blob#inspect output should indicate it is a blob and the size" do
964
+ o = Sequel.blob('a')
965
+ o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=1 content=\"a\">"
966
+ o = Sequel.blob(('a'..'z').to_a.join)
967
+ o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=26 start=\"abcdefghij\" end=\"qrstuvwxyz\">"
968
+ o = Sequel.blob(255.chr)
969
+ o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=1 content=\"#{RUBY_VERSION >= '1.9' ? "\\xFF" : "\\377"}\">"
970
+ o = Sequel.blob((230..255).map(&:chr).join)
971
+ if RUBY_VERSION >= '1.9'
972
+ o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=26 start=\"\\xE6\\xE7\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\" end=\"\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF\">"
973
+ else
974
+ o.inspect.must_equal "#<Sequel::SQL::Blob:0x#{'%x' % o.object_id} bytes=26 start=\"\\346\\347\\350\\351\\352\\353\\354\\355\\356\\357\" end=\"\\366\\367\\370\\371\\372\\373\\374\\375\\376\\377\">"
975
+ end
976
+ end
977
+
955
978
  it "Sequel.deep_qualify should do a deep qualification into nested structors" do
956
979
  l(Sequel.deep_qualify(:t, Sequel.+(:c, 1)), "(t.c + 1)")
957
980
  end
@@ -1138,6 +1161,11 @@ describe "Sequel::SQLTime" do
1138
1161
  Sequel::SQLTime.create(1, 2, 3).strftime('%Y-%m-%d').must_equal Date.new(2000).strftime('%Y-%m-%d')
1139
1162
  end
1140
1163
 
1164
+ it "#inspect should show class and time by default" do
1165
+ Sequel::SQLTime.create(1, 2, 3).inspect.must_equal "#<Sequel::SQLTime 01:02:03>"
1166
+ Sequel::SQLTime.create(13, 24, 35).inspect.must_equal "#<Sequel::SQLTime 13:24:35>"
1167
+ end
1168
+
1141
1169
  it "#to_s should include hour, minute, and second by default" do
1142
1170
  Sequel::SQLTime.create(1, 2, 3).to_s.must_equal "01:02:03"
1143
1171
  Sequel::SQLTime.create(1, 2, 3, 500000).to_s.must_equal "01:02:03"
@@ -1225,6 +1253,11 @@ describe "Sequel.recursive_map" do
1225
1253
  Sequel.recursive_map([nil], proc{|s| s.to_i}).must_equal [nil]
1226
1254
  Sequel.recursive_map([[nil]], proc{|s| s.to_i}).must_equal [[nil]]
1227
1255
  end
1256
+
1257
+ it "should call callable for falsey value" do
1258
+ Sequel.recursive_map([false], proc{|s| s.to_s}).must_equal ['false']
1259
+ Sequel.recursive_map([[false]], proc{|s| s.to_s}).must_equal [['false']]
1260
+ end
1228
1261
  end
1229
1262
 
1230
1263
  describe "Sequel.delay" do
@@ -19,6 +19,7 @@ describe "ActiveModel plugin" do
19
19
  end
20
20
  @c = AMLintTest
21
21
  @c.plugin :active_model
22
+ @c.freeze if @freeze_class
22
23
  @m = @model = @c.new
23
24
  @o = @c.load({})
24
25
  end
@@ -26,7 +27,6 @@ describe "ActiveModel plugin" do
26
27
  Object.send(:remove_const, :AMLintTest)
27
28
  Object.send(:remove_const, :Blog)
28
29
  end
29
- include ActiveModel::Lint::Tests
30
30
 
31
31
  it ".to_model should return self, not a proxy object" do
32
32
  @m.object_id.must_equal @m.to_model.object_id
@@ -40,6 +40,7 @@ describe "ActiveModel plugin" do
40
40
  @o.to_key.must_be_nil
41
41
 
42
42
  @c.set_primary_key [:id2, :id]
43
+ @c.freeze
43
44
  @o.to_key.must_be_nil
44
45
  @o.id = 1
45
46
  @o.id2 = 2
@@ -55,6 +56,7 @@ describe "ActiveModel plugin" do
55
56
  @o.id = 1
56
57
  @o.to_param.must_equal '1'
57
58
  @c.set_primary_key [:id2, :id]
59
+ @c.freeze
58
60
  @o.id2 = 2
59
61
  @o.to_param.must_equal '2-1'
60
62
  @o.meta_def(:to_param_joiner){'|'}
@@ -81,5 +83,17 @@ describe "ActiveModel plugin" do
81
83
  @m.to_partial_path.must_equal 'am_lint_tests/am_lint_test'
82
84
  Blog::Post.new.to_partial_path.must_equal 'blog/posts/post'
83
85
  end
86
+
87
+ describe "with unfrozen model class" do
88
+ include ActiveModel::Lint::Tests
89
+ end
90
+
91
+ describe "with frozen model class" do
92
+ before do
93
+ @freeze_class = true
94
+ end
95
+
96
+ include ActiveModel::Lint::Tests
97
+ end
84
98
  end
85
99
  end
@@ -72,6 +72,14 @@ describe "AssociationDependencies plugin" do
72
72
  DB.sqls.must_equal ['DELETE FROM aoa WHERE (l = 2)', 'DELETE FROM artists WHERE id = 2']
73
73
  end
74
74
 
75
+ it "should not allow modifications if class is frozen" do
76
+ @Artist.add_association_dependencies :other_artists=>:nullify
77
+ @Artist.freeze
78
+ proc{@Artist.add_association_dependencies :albums=>:nullify}.must_raise RuntimeError, TypeError
79
+ @Artist.association_dependencies.frozen?.must_equal true
80
+ @Artist.association_dependencies[:before_nullify].frozen?.must_equal true
81
+ end
82
+
75
83
  it "should raise an error if attempting to nullify a many_to_one association" do
76
84
  proc{@Album.add_association_dependencies :artist=>:nullify}.must_raise(Sequel::Error)
77
85
  end
@@ -189,4 +189,12 @@ describe "Sequel::Plugins::AutoValidations" do
189
189
  @m.valid?.must_equal false
190
190
  @m.errors.must_equal([:name, :num]=>["u_message"])
191
191
  end
192
+
193
+ it "should not allow modifying auto validation information for frozen model classes" do
194
+ @c.freeze
195
+ @c.auto_validate_not_null_columns.frozen?.must_equal true
196
+ @c.auto_validate_explicit_not_null_columns.frozen?.must_equal true
197
+ @c.auto_validate_max_length_columns.frozen?.must_equal true
198
+ @c.auto_validate_unique_columns.frozen?.must_equal true
199
+ end
192
200
  end
@@ -85,4 +85,10 @@ describe Sequel::Model, ".restricted_columns " do
85
85
  i.values.must_equal(:y => 7)
86
86
  DB.sqls.must_equal ["INSERT INTO blahblah (y) VALUES (7)", "SELECT * FROM blahblah WHERE id = 10"]
87
87
  end
88
+
89
+ it "should freeze restricted_columns when freezing class" do
90
+ @c.set_restricted_columns :z
91
+ @c.freeze
92
+ @c.restricted_columns.frozen?.must_equal true
93
+ end
88
94
  end
@@ -51,6 +51,15 @@ describe "class_table_inheritance plugin" do
51
51
  Object.send(:remove_const, :Employee)
52
52
  end
53
53
 
54
+ it "should freeze CTI information when freezing model class" do
55
+ Employee.freeze
56
+ Employee.cti_models.frozen?.must_equal true
57
+ Employee.cti_tables.frozen?.must_equal true
58
+ Employee.cti_instance_dataset.frozen?.must_equal true
59
+ Employee.cti_table_columns.frozen?.must_equal true
60
+ Employee.cti_table_map.frozen?.must_equal true
61
+ end
62
+
54
63
  it "should not attempt to use prepared statements" do
55
64
  Manager.plugin :prepared_statements
56
65
  Manager[1]
@@ -55,4 +55,10 @@ describe "column_conflicts plugin" do
55
55
  o.get_column_value(:object_id).must_equal 3
56
56
  o.object_id.wont_equal 3
57
57
  end
58
+
59
+ it "should freeze column conflict information when freezing model class" do
60
+ @c.freeze
61
+ @c.get_column_conflicts.frozen?.must_equal true
62
+ @c.set_column_conflicts.frozen?.must_equal true
63
+ end
58
64
  end
@@ -241,4 +241,12 @@ describe "Composition plugin" do
241
241
  sql.must_include("month = 2")
242
242
  sql.must_include("day = 3")
243
243
  end
244
+
245
+ it "should freeze composition metadata when freezing model class" do
246
+ @c.composition :date, :mapping=>[:year, :month, :day]
247
+ @c.freeze
248
+ @c.compositions.frozen?.must_equal true
249
+ @c.compositions[:date].frozen?.must_equal true
250
+ @c.composition_module.frozen?.must_equal true
251
+ end
244
252
  end
@@ -285,4 +285,16 @@ describe "Sequel::Plugins::ConstraintValidations" do
285
285
  c.constraint_validations.must_equal [[:validates_presence, :name]]
286
286
  c.constraint_validation_reflections.must_equal(:name=>[[:presence, {}]])
287
287
  end
288
+
289
+ it "should freeze constraint validations data when freezing model class" do
290
+ @c = model_class
291
+ @c.freeze
292
+ @c.constraint_validations.frozen?.must_equal true
293
+ @c.constraint_validations.all?(&:frozen?).must_equal true
294
+ @c.constraint_validation_reflections.frozen?.must_equal true
295
+ @c.constraint_validation_reflections.values.all?(&:frozen?).must_equal true
296
+ @c.constraint_validation_reflections.values.all?{|r| r.all?(&:frozen?)}.must_equal true
297
+ @c.instance_variable_get(:@constraint_validation_options).frozen?.must_equal true
298
+ @c.instance_variable_get(:@constraint_validation_options).values.all?(&:frozen?).must_equal true
299
+ end
288
300
  end
@@ -177,5 +177,12 @@ describe "Sequel::Plugins::CsvSerializer" do
177
177
  @album.to_csv(:only=>:name).must_equal "RF\n"
178
178
  @album.to_csv(:except=>[:id, :artist_id]).must_equal "RF\n"
179
179
  end
180
+
181
+ it "should freeze csv serializier opts when model class is frozen" do
182
+ @Album.csv_serializer_opts[:only] = [:id]
183
+ @Album.freeze
184
+ @Album.csv_serializer_opts.frozen?.must_equal true
185
+ @Album.csv_serializer_opts[:only].frozen?.must_equal true
186
+ end
180
187
  end
181
188
  end
@@ -99,4 +99,11 @@ describe "Sequel::Plugins::DefaultsSetter" do
99
99
  c.plugin :defaults_setter
100
100
  c.default_values.must_equal(:a=>2)
101
101
  end
102
+
103
+ it "should freeze default values when freezing model class" do
104
+ c = Class.new(Sequel::Model(@db[:bar]))
105
+ c.plugin :defaults_setter
106
+ c.freeze
107
+ c.default_values.frozen?.must_equal true
108
+ end
102
109
  end
@@ -24,6 +24,20 @@ describe "force_encoding plugin" do
24
24
  o.x.encoding.must_equal @e1
25
25
  end
26
26
 
27
+ it "should not force encoding of blobs to given encoding on load" do
28
+ s = Sequel.blob('blah'.dup.force_encoding('BINARY'))
29
+ o = @c.load(:id=>1, :x=>s)
30
+ o.x.must_equal 'blah'
31
+ o.x.encoding.must_equal Encoding.find('BINARY')
32
+ end
33
+
34
+ it "should not force encoding of blobs to given encoding when setting column values" do
35
+ s = Sequel.blob('blah'.dup.force_encoding('BINARY'))
36
+ o = @c.new(:x=>s)
37
+ o.x.must_equal 'blah'
38
+ o.x.encoding.must_equal Encoding.find('BINARY')
39
+ end
40
+
27
41
  it "should work correctly when given a frozen string" do
28
42
  s = 'blah'.dup
29
43
  s.force_encoding('US-ASCII')
@@ -12,6 +12,16 @@ describe Sequel::Model, "hook_class_methods plugin" do
12
12
  DB.reset
13
13
  end
14
14
 
15
+ it "should freeze hooks when freezing model class" do
16
+ c = model_class.call Sequel::Model do
17
+ before_save{adds << 'hi'}
18
+ end
19
+ c.freeze
20
+ hooks = c.instance_variable_get(:@hooks)
21
+ hooks.frozen?.must_equal true
22
+ hooks.values.all?(&:frozen?).must_equal true
23
+ end
24
+
15
25
  it "should be definable using a block" do
16
26
  adds = []
17
27
  c = model_class.call Sequel::Model do
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Dataset::ImplicitSubquery" do
4
+ it "should implicitly use a subquery for most dataset query methods" do
5
+ db = Sequel.mock
6
+ db.extend_datasets{def supports_cte?; true end}
7
+ ds = db["SELECT * FROM table"].extension(:implicit_subquery)
8
+ ds.columns(:id, :a)
9
+ ods = db[:c]
10
+ ods.columns(:id, :b)
11
+
12
+ ds.and(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE c"
13
+ ds.cross_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 CROSS JOIN c"
14
+ ds.distinct.sql.must_equal "SELECT DISTINCT * FROM (SELECT * FROM table) AS t1"
15
+ ds.except(ods).sql.must_equal "SELECT * FROM (SELECT * FROM (SELECT * FROM table) AS t1 EXCEPT SELECT * FROM c) AS t1"
16
+ ds.exclude(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE NOT c"
17
+ ds.exclude_having(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 HAVING NOT c"
18
+ ds.exclude_where(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE NOT c"
19
+ ds.filter(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE c"
20
+ ds.for_update.sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FOR UPDATE"
21
+ ds.full_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FULL JOIN c"
22
+ ds.full_outer_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FULL OUTER JOIN c"
23
+ ds.graph(ods).sql.must_equal "SELECT t1.id, t1.a, c.id AS c_id, c.b FROM (SELECT * FROM table) AS t1 LEFT OUTER JOIN c"
24
+ ds.grep(:c, 'a').sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE ((c LIKE 'a' ESCAPE '\\'))"
25
+ ds.group(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 GROUP BY c"
26
+ ds.group_append(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 GROUP BY c"
27
+ ds.group_and_count(:c).sql.must_equal "SELECT c, count(*) AS count FROM (SELECT * FROM table) AS t1 GROUP BY c"
28
+ ds.group_by(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 GROUP BY c"
29
+ ds.having(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 HAVING c"
30
+ ds.inner_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 INNER JOIN c USING (d)"
31
+ ds.intersect(ods).sql.must_equal "SELECT * FROM (SELECT * FROM (SELECT * FROM table) AS t1 INTERSECT SELECT * FROM c) AS t1"
32
+ ds.invert.sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE 'f'"
33
+ ds.join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 INNER JOIN c USING (d)"
34
+ ds.join_table(:inner, :c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 INNER JOIN c USING (d)"
35
+ ds.left_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 LEFT JOIN c USING (d)"
36
+ ds.left_outer_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 LEFT OUTER JOIN c USING (d)"
37
+ ds.limit(1).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 LIMIT 1"
38
+ ds.lock_style('FOR UPDATE').sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 FOR UPDATE"
39
+ ds.natural_full_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL FULL JOIN c"
40
+ ds.natural_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL JOIN c"
41
+ ds.natural_left_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL LEFT JOIN c"
42
+ ds.natural_right_join(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 NATURAL RIGHT JOIN c"
43
+ ds.offset(1).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 OFFSET 1"
44
+ ds.order(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
45
+ ds.order_append(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
46
+ ds.order_by(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
47
+ ds.order_more(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
48
+ ds.order_prepend(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 ORDER BY c"
49
+ ds.right_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 RIGHT JOIN c USING (d)"
50
+ ds.right_outer_join(:c, [:d]).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 RIGHT OUTER JOIN c USING (d)"
51
+ ds.select(:c).sql.must_equal "SELECT c FROM (SELECT * FROM table) AS t1"
52
+ ds.select_append(:c).sql.must_equal "SELECT *, c FROM (SELECT * FROM table) AS t1"
53
+ ds.select_group(:c).sql.must_equal "SELECT c FROM (SELECT * FROM table) AS t1 GROUP BY c"
54
+ ds.select_more(:c).sql.must_equal "SELECT *, c FROM (SELECT * FROM table) AS t1"
55
+ ds.union(ods).sql.must_equal "SELECT * FROM (SELECT * FROM (SELECT * FROM table) AS t1 UNION SELECT * FROM c) AS t1"
56
+ ds.where(:c).sql.must_equal "SELECT * FROM (SELECT * FROM table) AS t1 WHERE c"
57
+ ds.with(:d, ods).sql.must_equal "WITH d AS (SELECT * FROM c) SELECT * FROM (SELECT * FROM table) AS t1"
58
+ ds.with_recursive(:d, ods, ods).sql.must_equal "WITH d AS (SELECT * FROM c UNION ALL SELECT * FROM c) SELECT * FROM (SELECT * FROM table) AS t1"
59
+ end
60
+ end
@@ -51,4 +51,14 @@ describe "Sequel::Plugins::InputTransformer" do
51
51
  @o.name = ' name '.dup
52
52
  @o.name.must_equal 'raboof eman '
53
53
  end
54
+
55
+ it "should freeze input transformers when freezing model class" do
56
+ @c.skip_input_transformer :reverser, :name
57
+ @c.freeze
58
+ @c.input_transformers.frozen?.must_equal true
59
+ @c.input_transformer_order.frozen?.must_equal true
60
+ skip = @c.instance_variable_get(:@skip_input_transformer_columns)
61
+ skip.frozen?.must_equal true
62
+ skip.values.all?(&:frozen?).must_equal true
63
+ end
54
64
  end
@@ -63,4 +63,10 @@ describe "Sequel::Plugins::InsertReturningSelect" do
63
63
  b.create(:x=>2).must_equal b.load(:id=>1, :x=>2)
64
64
  @db.sqls.must_equal ['INSERT INTO albums (x) VALUES (2) RETURNING id, x']
65
65
  end
66
+
67
+ it "should freeze instance_insert_dataset when freezing model class" do
68
+ @Album.plugin :insert_returning_select
69
+ @Album.freeze
70
+ @Album.instance_insert_dataset.frozen?.must_equal true
71
+ end
66
72
  end
@@ -303,4 +303,11 @@ describe "Sequel::Plugins::JsonSerializer" do
303
303
  it "should raise an error if using an unsupported :associations option" do
304
304
  proc{Artist.from_json(@artist.to_json, :associations=>'')}.must_raise(Sequel::Error)
305
305
  end
306
+
307
+ it "should freeze json serializier opts when model class is frozen" do
308
+ Album.json_serializer_opts[:only] = [:id]
309
+ Album.freeze
310
+ Album.json_serializer_opts.frozen?.must_equal true
311
+ Album.json_serializer_opts[:only].frozen?.must_equal true
312
+ end
306
313
  end
@@ -167,4 +167,10 @@ describe "Sequel::Plugins::LazyAttributes" do
167
167
  m.name.must_equal 3
168
168
  @db.sqls.must_equal ["SELECT la.id FROM la LIMIT 1", "SELECT la.name FROM la WHERE (id = 1) LIMIT 1"]
169
169
  end
170
+
171
+ it "should freeze lazy_attributes_module when freezing model class" do
172
+ @c.plugin :lazy_attributes, :blah
173
+ @c.freeze
174
+ @c.lazy_attributes_module.frozen?.must_equal true
175
+ end
170
176
  end
@@ -2115,3 +2115,47 @@ describe "one_through_many eager loading methods" do
2115
2115
  @c1.order(:artists__blah2, :artists__blah3).eager_graph(:tag).sql.must_equal 'SELECT artists.id, tag.id AS tag_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags AS tag ON (tag.id = albums_tags.tag_id) ORDER BY artists.blah2, artists.blah3, tag.blah__id, tag.blah__id DESC, blah.id DESC, blah.id, tag.album_id, tag.album_id DESC, 1, RANDOM(), b.a'
2116
2116
  end
2117
2117
  end
2118
+
2119
+ describe "Sequel::Model.finalize_associations" do
2120
+ before do
2121
+ class ::Item < Sequel::Model
2122
+ plugin :many_through_many
2123
+ many_through_many :items, [[:foos, :item1_id, :foo1_id], [:bars, :foo2_id, :item2_id]]
2124
+ one_through_many :item, [[:foos, :item1_id, :foo1_id], [:bars, :foo2_id, :item2_id]]
2125
+ finalize_associations
2126
+ end
2127
+ end
2128
+ after do
2129
+ Object.send(:remove_const, :Item)
2130
+ end
2131
+
2132
+ it "should finalize one_through_many associations" do
2133
+ r = Item.association_reflection(:item)
2134
+ r[:class].must_equal Item
2135
+ r[:_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id) LIMIT 1"
2136
+ r[:associated_eager_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id)"
2137
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT foos.item1_id FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id) WHERE (foos.item1_id IS NOT NULL)"
2138
+ r[:placeholder_loader].wont_be_nil
2139
+ r[:predicate_key].must_equal Sequel.qualify(:foos, :item1_id)
2140
+ r[:associated_key_table].must_equal :foos
2141
+ r[:edges].must_equal [{:table=>:foos, :left=>:id, :right=>:item1_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}, {:table=>:bars, :left=>:foo1_id, :right=>:foo2_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}]
2142
+ r[:final_edge].must_equal(:table=>:items, :left=>:item2_id, :right=>:id, :conditions=>nil, :join_type=>nil, :block=>nil)
2143
+ r[:final_reverse_edge].must_equal(:table=>:foos, :left=>:foo1_id, :right=>:foo2_id, :alias=>:foos)
2144
+ r[:reverse_edges].must_equal [{:table=>:bars, :left=>:item2_id, :right=>:id, :alias=>:bars}]
2145
+ end
2146
+
2147
+ it "should finalize many_through_many associations" do
2148
+ r = Item.association_reflection(:items)
2149
+ r[:class].must_equal Item
2150
+ r[:_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id)"
2151
+ r[:associated_eager_dataset].sql.must_equal "SELECT items.* FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id)"
2152
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT foos.item1_id FROM items INNER JOIN bars ON (bars.item2_id = items.id) INNER JOIN foos ON (foos.foo1_id = bars.foo2_id) WHERE (foos.item1_id IS NOT NULL)"
2153
+ r[:placeholder_loader].wont_be_nil
2154
+ r[:predicate_key].must_equal Sequel.qualify(:foos, :item1_id)
2155
+ r[:associated_key_table].must_equal :foos
2156
+ r[:edges].must_equal [{:table=>:foos, :left=>:id, :right=>:item1_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}, {:table=>:bars, :left=>:foo1_id, :right=>:foo2_id, :conditions=>[], :join_type=>:left_outer, :block=>nil}]
2157
+ r[:final_edge].must_equal(:table=>:items, :left=>:item2_id, :right=>:id, :conditions=>nil, :join_type=>nil, :block=>nil)
2158
+ r[:final_reverse_edge].must_equal(:table=>:foos, :left=>:foo1_id, :right=>:foo2_id, :alias=>:foos)
2159
+ r[:reverse_edges].must_equal [{:table=>:bars, :left=>:item2_id, :right=>:id, :alias=>:bars}]
2160
+ end
2161
+ end