sequel 3.46.0 → 3.47.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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +96 -0
  3. data/Rakefile +7 -1
  4. data/bin/sequel +6 -4
  5. data/doc/active_record.rdoc +1 -1
  6. data/doc/advanced_associations.rdoc +14 -35
  7. data/doc/association_basics.rdoc +66 -4
  8. data/doc/migration.rdoc +4 -0
  9. data/doc/opening_databases.rdoc +6 -0
  10. data/doc/postgresql.rdoc +302 -0
  11. data/doc/release_notes/3.47.0.txt +270 -0
  12. data/doc/security.rdoc +6 -0
  13. data/lib/sequel/adapters/ibmdb.rb +9 -9
  14. data/lib/sequel/adapters/jdbc.rb +22 -7
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
  16. data/lib/sequel/adapters/mock.rb +2 -0
  17. data/lib/sequel/adapters/postgres.rb +44 -13
  18. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  20. data/lib/sequel/adapters/shared/postgres.rb +94 -55
  21. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  22. data/lib/sequel/adapters/sqlite.rb +2 -2
  23. data/lib/sequel/adapters/utils/pg_types.rb +1 -14
  24. data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
  25. data/lib/sequel/connection_pool/threaded.rb +1 -1
  26. data/lib/sequel/core.rb +1 -1
  27. data/lib/sequel/database/connecting.rb +2 -2
  28. data/lib/sequel/database/features.rb +5 -0
  29. data/lib/sequel/database/misc.rb +47 -5
  30. data/lib/sequel/database/query.rb +2 -2
  31. data/lib/sequel/dataset/actions.rb +4 -2
  32. data/lib/sequel/dataset/misc.rb +1 -1
  33. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  34. data/lib/sequel/dataset/query.rb +8 -6
  35. data/lib/sequel/dataset/sql.rb +8 -6
  36. data/lib/sequel/extensions/constraint_validations.rb +5 -2
  37. data/lib/sequel/extensions/migration.rb +10 -8
  38. data/lib/sequel/extensions/pagination.rb +3 -0
  39. data/lib/sequel/extensions/pg_array.rb +85 -25
  40. data/lib/sequel/extensions/pg_hstore.rb +8 -1
  41. data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
  42. data/lib/sequel/extensions/pg_inet.rb +16 -13
  43. data/lib/sequel/extensions/pg_interval.rb +6 -2
  44. data/lib/sequel/extensions/pg_json.rb +18 -11
  45. data/lib/sequel/extensions/pg_range.rb +17 -2
  46. data/lib/sequel/extensions/pg_range_ops.rb +7 -5
  47. data/lib/sequel/extensions/pg_row.rb +29 -12
  48. data/lib/sequel/extensions/pretty_table.rb +3 -0
  49. data/lib/sequel/extensions/query.rb +3 -0
  50. data/lib/sequel/extensions/schema_caching.rb +2 -0
  51. data/lib/sequel/extensions/schema_dumper.rb +3 -1
  52. data/lib/sequel/extensions/select_remove.rb +3 -0
  53. data/lib/sequel/model.rb +8 -2
  54. data/lib/sequel/model/associations.rb +39 -27
  55. data/lib/sequel/model/base.rb +99 -38
  56. data/lib/sequel/model/plugins.rb +25 -0
  57. data/lib/sequel/plugins/association_autoreloading.rb +27 -22
  58. data/lib/sequel/plugins/association_dependencies.rb +1 -7
  59. data/lib/sequel/plugins/auto_validations.rb +110 -0
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -6
  61. data/lib/sequel/plugins/caching.rb +6 -13
  62. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  63. data/lib/sequel/plugins/composition.rb +14 -7
  64. data/lib/sequel/plugins/constraint_validations.rb +2 -13
  65. data/lib/sequel/plugins/defaults_setter.rb +1 -6
  66. data/lib/sequel/plugins/dirty.rb +8 -0
  67. data/lib/sequel/plugins/error_splitter.rb +54 -0
  68. data/lib/sequel/plugins/force_encoding.rb +1 -5
  69. data/lib/sequel/plugins/hook_class_methods.rb +1 -6
  70. data/lib/sequel/plugins/input_transformer.rb +79 -0
  71. data/lib/sequel/plugins/instance_filters.rb +7 -1
  72. data/lib/sequel/plugins/instance_hooks.rb +7 -1
  73. data/lib/sequel/plugins/json_serializer.rb +5 -10
  74. data/lib/sequel/plugins/lazy_attributes.rb +20 -7
  75. data/lib/sequel/plugins/list.rb +1 -6
  76. data/lib/sequel/plugins/many_through_many.rb +1 -2
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
  78. data/lib/sequel/plugins/optimistic_locking.rb +1 -5
  79. data/lib/sequel/plugins/pg_row.rb +4 -2
  80. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
  81. data/lib/sequel/plugins/prepared_statements.rb +1 -5
  82. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
  83. data/lib/sequel/plugins/rcte_tree.rb +2 -2
  84. data/lib/sequel/plugins/serialization.rb +11 -13
  85. data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
  86. data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
  87. data/lib/sequel/plugins/static_cache.rb +67 -19
  88. data/lib/sequel/plugins/string_stripper.rb +7 -27
  89. data/lib/sequel/plugins/subclasses.rb +3 -5
  90. data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
  91. data/lib/sequel/plugins/timestamps.rb +2 -7
  92. data/lib/sequel/plugins/touch.rb +5 -8
  93. data/lib/sequel/plugins/tree.rb +1 -6
  94. data/lib/sequel/plugins/typecast_on_load.rb +1 -5
  95. data/lib/sequel/plugins/update_primary_key.rb +26 -14
  96. data/lib/sequel/plugins/validation_class_methods.rb +31 -16
  97. data/lib/sequel/plugins/validation_helpers.rb +50 -26
  98. data/lib/sequel/plugins/xml_serializer.rb +3 -6
  99. data/lib/sequel/sql.rb +1 -1
  100. data/lib/sequel/version.rb +1 -1
  101. data/spec/adapters/postgres_spec.rb +131 -15
  102. data/spec/adapters/sqlite_spec.rb +1 -1
  103. data/spec/core/connection_pool_spec.rb +16 -17
  104. data/spec/core/database_spec.rb +111 -40
  105. data/spec/core/dataset_spec.rb +65 -74
  106. data/spec/core/expression_filters_spec.rb +6 -5
  107. data/spec/core/object_graph_spec.rb +0 -1
  108. data/spec/core/schema_spec.rb +23 -23
  109. data/spec/core/spec_helper.rb +5 -1
  110. data/spec/extensions/association_dependencies_spec.rb +1 -1
  111. data/spec/extensions/association_proxies_spec.rb +1 -1
  112. data/spec/extensions/auto_validations_spec.rb +90 -0
  113. data/spec/extensions/caching_spec.rb +6 -0
  114. data/spec/extensions/class_table_inheritance_spec.rb +8 -1
  115. data/spec/extensions/composition_spec.rb +12 -5
  116. data/spec/extensions/constraint_validations_spec.rb +4 -4
  117. data/spec/extensions/core_refinements_spec.rb +29 -79
  118. data/spec/extensions/dirty_spec.rb +14 -0
  119. data/spec/extensions/error_splitter_spec.rb +18 -0
  120. data/spec/extensions/identity_map_spec.rb +0 -1
  121. data/spec/extensions/input_transformer_spec.rb +54 -0
  122. data/spec/extensions/instance_filters_spec.rb +6 -0
  123. data/spec/extensions/instance_hooks_spec.rb +12 -1
  124. data/spec/extensions/json_serializer_spec.rb +0 -1
  125. data/spec/extensions/lazy_attributes_spec.rb +64 -55
  126. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  127. data/spec/extensions/many_through_many_spec.rb +3 -4
  128. data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
  129. data/spec/extensions/migration_spec.rb +16 -0
  130. data/spec/extensions/null_dataset_spec.rb +1 -1
  131. data/spec/extensions/pg_array_spec.rb +48 -1
  132. data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
  133. data/spec/extensions/pg_hstore_spec.rb +5 -0
  134. data/spec/extensions/pg_inet_spec.rb +5 -0
  135. data/spec/extensions/pg_interval_spec.rb +7 -3
  136. data/spec/extensions/pg_json_spec.rb +6 -1
  137. data/spec/extensions/pg_range_ops_spec.rb +4 -1
  138. data/spec/extensions/pg_range_spec.rb +5 -0
  139. data/spec/extensions/pg_row_plugin_spec.rb +13 -0
  140. data/spec/extensions/pg_row_spec.rb +28 -19
  141. data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
  142. data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
  143. data/spec/extensions/query_literals_spec.rb +1 -1
  144. data/spec/extensions/rcte_tree_spec.rb +2 -2
  145. data/spec/extensions/schema_spec.rb +2 -2
  146. data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
  147. data/spec/extensions/serialization_spec.rb +15 -1
  148. data/spec/extensions/sharding_spec.rb +1 -1
  149. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  150. data/spec/extensions/static_cache_spec.rb +59 -9
  151. data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
  152. data/spec/extensions/update_primary_key_spec.rb +17 -1
  153. data/spec/extensions/validation_class_methods_spec.rb +25 -0
  154. data/spec/extensions/validation_helpers_spec.rb +59 -3
  155. data/spec/integration/associations_test.rb +5 -5
  156. data/spec/integration/eager_loader_test.rb +32 -63
  157. data/spec/integration/model_test.rb +2 -2
  158. data/spec/integration/plugin_test.rb +88 -56
  159. data/spec/integration/prepared_statement_test.rb +1 -1
  160. data/spec/integration/schema_test.rb +1 -1
  161. data/spec/integration/timezone_test.rb +0 -1
  162. data/spec/integration/transaction_test.rb +0 -1
  163. data/spec/model/association_reflection_spec.rb +1 -1
  164. data/spec/model/associations_spec.rb +106 -84
  165. data/spec/model/base_spec.rb +4 -4
  166. data/spec/model/eager_loading_spec.rb +8 -8
  167. data/spec/model/model_spec.rb +27 -9
  168. data/spec/model/plugins_spec.rb +71 -0
  169. data/spec/model/record_spec.rb +99 -13
  170. metadata +12 -2
@@ -66,7 +66,7 @@ describe "constraint_validations extension" do
66
66
  @db.sqls.should == ["DELETE FROM cv WHERE (table = 'foo')"]
67
67
  end
68
68
 
69
- it "should raise an error without deleting if attempting to drop validations with table, column, or constraint" do
69
+ it "should raise an error without deleting if attempting to drop validations without table, column, or constraint" do
70
70
  proc{@db.drop_constraint_validations_for({})}.should raise_error(Sequel::Error)
71
71
  @db.sqls.should == []
72
72
  end
@@ -97,7 +97,7 @@ describe "constraint_validations extension" do
97
97
  @db.create_table(:foo){String :name; validate{presence :name, :allow_nil=>true}}
98
98
  sqls = @db.sqls
99
99
  parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t'}
100
- sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK (trim(name) != ''))"]
100
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NULL) OR (trim(name) != '')))"]
101
101
  end
102
102
 
103
103
  it "should handle :name option when adding validations" do
@@ -193,7 +193,7 @@ describe "constraint_validations extension" do
193
193
  it "should support :format constraint validation" do
194
194
  @db = Sequel.mock(:host=>'postgres')
195
195
  @db.extension(:constraint_validations)
196
- @db.create_table(:foo){String :name; validate{format /^foo.*/, :name}}
196
+ @db.create_table(:foo){String :name; validate{format(/^foo.*/, :name)}}
197
197
  sqls = @db.sqls
198
198
  parse_insert(sqls.slice!(1)).should == {:validation_type=>"format", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
199
199
  sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~ '^foo.*')))"]
@@ -202,7 +202,7 @@ describe "constraint_validations extension" do
202
202
  it "should support :format constraint validation with case insensitive format" do
203
203
  @db = Sequel.mock(:host=>'postgres')
204
204
  @db.extension(:constraint_validations)
205
- @db.create_table(:foo){String :name; validate{format /^foo.*/i, :name}}
205
+ @db.create_table(:foo){String :name; validate{format(/^foo.*/i, :name)}}
206
206
  sqls = @db.sqls
207
207
  parse_insert(sqls.slice!(1)).should == {:validation_type=>"iformat", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
208
208
  sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~* '^foo.*')))"]
@@ -16,45 +16,6 @@ describe "Core refinements" do
16
16
  end
17
17
  end
18
18
 
19
- if RUBY_VERSION < '1.9.0'
20
- it "should not allow inequality operations on true, false, or nil" do
21
- @d.lit(:x > 1).should == "(x > 1)"
22
- @d.lit(:x < true).should == "(x < 't')"
23
- @d.lit(:x >= false).should == "(x >= 'f')"
24
- @d.lit(:x <= nil).should == "(x <= NULL)"
25
- end
26
-
27
- it "should not allow inequality operations on boolean complex expressions" do
28
- @d.lit(:x > (:y > 5)).should == "(x > (y > 5))"
29
- @d.lit(:x < (:y < 5)).should == "(x < (y < 5))"
30
- @d.lit(:x >= (:y >= 5)).should == "(x >= (y >= 5))"
31
- @d.lit(:x <= (:y <= 5)).should == "(x <= (y <= 5))"
32
- @d.lit(:x > {:y => nil}).should == "(x > (y IS NULL))"
33
- @d.lit(:x < ~{:y => nil}).should == "(x < (y IS NOT NULL))"
34
- @d.lit(:x >= {:y => 5}).should == "(x >= (y = 5))"
35
- @d.lit(:x <= ~{:y => 5}).should == "(x <= (y != 5))"
36
- @d.lit(:x >= {:y => [1,2,3]}).should == "(x >= (y IN (1, 2, 3)))"
37
- @d.lit(:x <= ~{:y => [1,2,3]}).should == "(x <= (y NOT IN (1, 2, 3)))"
38
- end
39
-
40
- it "should support >, <, >=, and <= via Symbol#>,<,>=,<=" do
41
- @d.l(:x > 100).should == '(x > 100)'
42
- @d.l(:x < 100.01).should == '(x < 100.01)'
43
- @d.l(:x >= 100000000000000000000000000000000000).should == '(x >= 100000000000000000000000000000000000)'
44
- @d.l(:x <= 100).should == '(x <= 100)'
45
- end
46
-
47
- it "should support negation of >, <, >=, and <= via Symbol#~" do
48
- @d.l(~(:x > 100)).should == '(x <= 100)'
49
- @d.l(~(:x < 100.01)).should == '(x >= 100.01)'
50
- @d.l(~(:x >= 100000000000000000000000000000000000)).should == '(x < 100000000000000000000000000000000000)'
51
- @d.l(~(:x <= 100)).should == '(x > 100)'
52
- end
53
-
54
- it "should support double negation via ~" do
55
- @d.l(~~(:x > 100)).should == '(x > 100)'
56
- end
57
- end
58
19
  it "should support NOT via Symbol#~" do
59
20
  @d.l(~:x).should == 'NOT x'
60
21
  @d.l(~:x__y).should == 'NOT x.y'
@@ -221,7 +182,7 @@ end
221
182
 
222
183
  describe "String#lit" do
223
184
  before do
224
- @ds = ds = Sequel::Database.new[:t]
185
+ @ds = Sequel::Database.new[:t]
225
186
  end
226
187
 
227
188
  specify "should return an LiteralString object" do
@@ -274,13 +235,13 @@ describe "#desc" do
274
235
  end
275
236
 
276
237
  specify "should format a DESC clause for a column ref" do
277
- :test.desc.to_s(@ds).should == 'test DESC'
238
+ @ds.literal(:test.desc).should == 'test DESC'
278
239
 
279
- :items__price.desc.to_s(@ds).should == 'items.price DESC'
240
+ @ds.literal(:items__price.desc).should == 'items.price DESC'
280
241
  end
281
242
 
282
243
  specify "should format a DESC clause for a function" do
283
- :avg.sql_function(:test).desc.to_s(@ds).should == 'avg(test) DESC'
244
+ @ds.literal(:avg.sql_function(:test).desc).should == 'avg(test) DESC'
284
245
  end
285
246
  end
286
247
 
@@ -290,13 +251,13 @@ describe "#asc" do
290
251
  end
291
252
 
292
253
  specify "should format a ASC clause for a column ref" do
293
- :test.asc.to_s(@ds).should == 'test ASC'
254
+ @ds.literal(:test.asc).should == 'test ASC'
294
255
 
295
- :items__price.asc.to_s(@ds).should == 'items.price ASC'
256
+ @ds.literal(:items__price.asc).should == 'items.price ASC'
296
257
  end
297
258
 
298
259
  specify "should format a ASC clause for a function" do
299
- :avg.sql_function(:test).asc.to_s(@ds).should == 'avg(test) ASC'
260
+ @ds.literal(:avg.sql_function(:test).asc).should == 'avg(test) ASC'
300
261
  end
301
262
  end
302
263
 
@@ -306,17 +267,17 @@ describe "#as" do
306
267
  end
307
268
 
308
269
  specify "should format a AS clause for a column ref" do
309
- :test.as(:t).to_s(@ds).should == 'test AS t'
270
+ @ds.literal(:test.as(:t)).should == 'test AS t'
310
271
 
311
- :items__price.as(:p).to_s(@ds).should == 'items.price AS p'
272
+ @ds.literal(:items__price.as(:p)).should == 'items.price AS p'
312
273
  end
313
274
 
314
275
  specify "should format a AS clause for a function" do
315
- :avg.sql_function(:test).as(:avg).to_s(@ds).should == 'avg(test) AS avg'
276
+ @ds.literal(:avg.sql_function(:test).as(:avg)).should == 'avg(test) AS avg'
316
277
  end
317
278
 
318
279
  specify "should format a AS clause for a literal value" do
319
- 'abc'.as(:abc).to_s(@ds).should == "'abc' AS abc"
280
+ @ds.literal('abc'.as(:abc)).should == "'abc' AS abc"
320
281
  end
321
282
  end
322
283
 
@@ -364,34 +325,23 @@ describe "Blob" do
364
325
  end
365
326
  end
366
327
 
367
- if RUBY_VERSION < '1.9.0'
368
- describe "Symbol#[]" do
369
- specify "should format an SQL Function" do
370
- ds = Sequel::Dataset.new(nil)
371
- ds.literal(:xyz[]).should == 'xyz()'
372
- ds.literal(:xyz[1]).should == 'xyz(1)'
373
- ds.literal(:xyz[1, 2, :abc[3]]).should == 'xyz(1, 2, abc(3))'
374
- end
375
- end
376
- end
377
-
378
328
  describe "Symbol#*" do
379
329
  before do
380
330
  @ds = Sequel::Dataset.new(nil)
381
331
  end
382
332
 
383
333
  specify "should format a qualified wildcard if no argument" do
384
- :xyz.*.to_s(@ds).should == 'xyz.*'
385
- :abc.*.to_s(@ds).should == 'abc.*'
334
+ @ds.literal(:xyz.*).should == 'xyz.*'
335
+ @ds.literal(:abc.*).should == 'abc.*'
386
336
  end
387
337
 
388
338
  specify "should format a filter expression if an argument" do
389
- :xyz.*(3).to_s(@ds).should == '(xyz * 3)'
390
- :abc.*(5).to_s(@ds).should == '(abc * 5)'
339
+ @ds.literal(:xyz.*(3)).should == '(xyz * 3)'
340
+ @ds.literal(:abc.*(5)).should == '(abc * 5)'
391
341
  end
392
342
 
393
343
  specify "should support qualified symbols if no argument" do
394
- :xyz__abc.*.to_s(@ds).should == 'xyz.abc.*'
344
+ @ds.literal(:xyz__abc.*).should == 'xyz.abc.*'
395
345
  end
396
346
  end
397
347
 
@@ -445,12 +395,12 @@ describe "Symbol" do
445
395
  end
446
396
 
447
397
  specify "should support sql_function method" do
448
- :COUNT.sql_function('1').to_s(@ds).should == "COUNT('1')"
398
+ @ds.literal(:COUNT.sql_function('1')).should == "COUNT('1')"
449
399
  @ds.select(:COUNT.sql_function('1')).sql.should == "SELECT COUNT('1')"
450
400
  end
451
401
 
452
402
  specify "should support cast method" do
453
- :abc.cast(:integer).to_s(@ds).should == "CAST(abc AS integer)"
403
+ @ds.literal(:abc.cast(:integer)).should == "CAST(abc AS integer)"
454
404
  end
455
405
 
456
406
  specify "should support sql array accesses via sql_subscript" do
@@ -463,19 +413,19 @@ describe "Symbol" do
463
413
  specify "should support cast_numeric and cast_string" do
464
414
  x = :abc.cast_numeric
465
415
  x.should be_a_kind_of(Sequel::SQL::NumericExpression)
466
- x.to_s(@ds).should == "CAST(abc AS integer)"
416
+ @ds.literal(x).should == "CAST(abc AS integer)"
467
417
 
468
418
  x = :abc.cast_numeric(:real)
469
419
  x.should be_a_kind_of(Sequel::SQL::NumericExpression)
470
- x.to_s(@ds).should == "CAST(abc AS real)"
420
+ @ds.literal(x).should == "CAST(abc AS real)"
471
421
 
472
422
  x = :abc.cast_string
473
423
  x.should be_a_kind_of(Sequel::SQL::StringExpression)
474
- x.to_s(@ds).should == "CAST(abc AS varchar(255))"
424
+ @ds.literal(x).should == "CAST(abc AS varchar(255))"
475
425
 
476
426
  x = :abc.cast_string(:varchar)
477
427
  x.should be_a_kind_of(Sequel::SQL::StringExpression)
478
- x.to_s(@ds).should == "CAST(abc AS varchar(255))"
428
+ @ds.literal(x).should == "CAST(abc AS varchar(255))"
479
429
  end
480
430
 
481
431
  specify "should allow database independent types when casting" do
@@ -485,16 +435,16 @@ describe "Symbol" do
485
435
  return :bar if type == String
486
436
  type
487
437
  end
488
- :abc.cast(String).to_s(@ds).should == "CAST(abc AS bar)"
489
- :abc.cast(String).to_s(@ds).should == "CAST(abc AS bar)"
490
- :abc.cast_string.to_s(@ds).should == "CAST(abc AS bar)"
491
- :abc.cast_string(Integer).to_s(@ds).should == "CAST(abc AS foo)"
492
- :abc.cast_numeric.to_s(@ds).should == "CAST(abc AS foo)"
493
- :abc.cast_numeric(String).to_s(@ds).should == "CAST(abc AS bar)"
438
+ @ds.literal(:abc.cast(String)).should == "CAST(abc AS bar)"
439
+ @ds.literal(:abc.cast(String)).should == "CAST(abc AS bar)"
440
+ @ds.literal(:abc.cast_string).should == "CAST(abc AS bar)"
441
+ @ds.literal(:abc.cast_string(Integer)).should == "CAST(abc AS foo)"
442
+ @ds.literal(:abc.cast_numeric).should == "CAST(abc AS foo)"
443
+ @ds.literal(:abc.cast_numeric(String)).should == "CAST(abc AS bar)"
494
444
  end
495
445
 
496
446
  specify "should support SQL EXTRACT function via #extract " do
497
- :abc.extract(:year).to_s(@ds).should == "extract(year FROM abc)"
447
+ @ds.literal(:abc.extract(:year)).should == "extract(year FROM abc)"
498
448
  end
499
449
  end
500
450
 
@@ -121,6 +121,12 @@ describe "Sequel::Plugins::Dirty" do
121
121
  o.column_change(:initial).should == [v, 'a']
122
122
  end
123
123
  end
124
+
125
+ it "should work when freezing objects" do
126
+ @o.freeze
127
+ @o.initial_value(:initial).should == 'i'
128
+ proc{@o.initial = 'b'}.should raise_error
129
+ end
124
130
  end
125
131
 
126
132
  describe "with new instance" do
@@ -158,5 +164,13 @@ describe "Sequel::Plugins::Dirty" do
158
164
  @o.save
159
165
  @o.previous_changes.should == {:initial_changed=>['ic', 'ic2'], :missing_changed=>[nil, 'mc2']}
160
166
  end
167
+
168
+ it "should work when freezing objects after saving" do
169
+ @o.initial = 'a'
170
+ @o.save
171
+ @o.freeze
172
+ @o.previous_changes[:initial].should == ['i', 'a']
173
+ proc{@o.initial = 'b'}.should raise_error
174
+ end
161
175
  end
162
176
  end
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::ErrorSplitter" do
4
+ before do
5
+ @c = Class.new(Sequel::Model)
6
+ @c.plugin :error_splitter
7
+ @m = @c.new
8
+ def @m.validate
9
+ errors.add([:a, :b], 'is bad')
10
+ end
11
+ end
12
+
13
+ it "should split errors for multiple columns and assign them to each column" do
14
+ @m.valid?.should be_false
15
+ @m.errors.should == {:a=>['is bad'], :b=>['is bad']}
16
+ end
17
+ end
18
+
@@ -183,7 +183,6 @@ describe "Sequel::Plugins::IdentityMap" do
183
183
  end
184
184
 
185
185
  it "should not use the identity map as a lookup cache for a one_to_one association" do
186
- c = @c2
187
186
  @c2.one_to_one :artist, :class=>@c1, :key=>:artist_id
188
187
  @c.with_identity_map do
189
188
  MODEL_DB.sqls.length.should == 0
@@ -0,0 +1,54 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::InputTransformer" do
4
+ before do
5
+ @c = Class.new(Sequel::Model)
6
+ @c.columns :name, :b
7
+ @c.plugin(:input_transformer, :reverser){|v| v.is_a?(String) ? v.reverse : v}
8
+ @o = @c.new
9
+ end
10
+
11
+ it "should apply transformation to input" do
12
+ @o.name = ' name '
13
+ @o.name.should == ' eman '
14
+ @o.name = [1, 2, 3]
15
+ @o.name.should == [1, 2, 3]
16
+ end
17
+
18
+ it "should not apply any transformers by default" do
19
+ c = Class.new(Sequel::Model)
20
+ c.columns :name, :b
21
+ c.plugin :input_transformer
22
+ c.new(:name => ' name ').name.should == ' name '
23
+ end
24
+
25
+ it "should allow skipping of columns using .skip_input_transformer" do
26
+ @c.skip_input_transformer :reverser, :name
27
+ v = ' name '
28
+ @o.name = v
29
+ @o.name.should equal(v)
30
+ end
31
+
32
+ it "should work correctly in subclasses" do
33
+ o = Class.new(@c).new
34
+ o.name = ' name '
35
+ o.name.should == ' eman '
36
+ end
37
+
38
+ it "should raise an error if adding input filter without name" do
39
+ proc{@c.add_input_transformer(nil){}}.should raise_error(Sequel::Error)
40
+ proc{@c.plugin(:input_transformer){}}.should raise_error(Sequel::Error)
41
+ end
42
+
43
+ it "should raise an error if adding input filter without block" do
44
+ proc{@c.add_input_transformer(:foo)}.should raise_error(Sequel::Error)
45
+ proc{@c.plugin(:input_transformer, :foo)}.should raise_error(Sequel::Error)
46
+ end
47
+
48
+ it "should apply multiple input transformers in reverse order of their call" do
49
+ @c.add_input_transformer(:add_bar){|v| v << 'bar'}
50
+ @c.add_input_transformer(:add_foo){|v| v << 'foo'}
51
+ @o.name = ' name '
52
+ @o.name.should == 'raboof eman '
53
+ end
54
+ end
@@ -42,4 +42,10 @@ describe "instance_filters plugin" do
42
42
  @p.update(:name=>'Bob')
43
43
  MODEL_DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE (id = 1)"]
44
44
  end
45
+
46
+ specify "shouldn't allow instance filters on frozen objects" do
47
+ @p.instance_filter(:name=>'Joe')
48
+ @p.freeze
49
+ proc{@p.instance_filter(:name=>'Jim')}.should raise_error
50
+ end
45
51
  end
@@ -176,12 +176,23 @@ describe "InstanceHooks plugin" do
176
176
  @x.save.should_not == nil
177
177
  @r.should == [2, 1, 4, 3]
178
178
  end
179
+
180
+ it "should not allow addition of instance hooks to frozen instances" do
181
+ @x.after_destroy_hook{r 1}
182
+ @x.before_destroy_hook{r 2}
183
+ @x.before_update_hook{r 3}
184
+ @x.before_save_hook{r 4}
185
+ @x.freeze
186
+ proc{@x.after_destroy_hook{r 1}}.should raise_error(Sequel::Error)
187
+ proc{@x.before_destroy_hook{r 2}}.should raise_error(Sequel::Error)
188
+ proc{@x.before_update_hook{r 3}}.should raise_error(Sequel::Error)
189
+ proc{@x.before_save_hook{r 4}}.should raise_error(Sequel::Error)
190
+ end
179
191
  end
180
192
 
181
193
  describe "InstanceHooks plugin with transactions" do
182
194
  before do
183
195
  @db = Sequel.mock(:numrows=>1)
184
- pr = proc{|x| r(x)}
185
196
  @c = Class.new(Sequel::Model(@db[:items])) do
186
197
  attr_accessor :rb
187
198
  def after_save
@@ -182,7 +182,6 @@ describe "Sequel::Plugins::JsonSerializer" do
182
182
  end
183
183
 
184
184
  it "should have dataset to_json method work with naked datasets" do
185
- album = @album
186
185
  ds = Album.dataset.naked
187
186
  ds._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
188
187
  Sequel.parse_json(ds.to_json).should == [@album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}]
@@ -68,17 +68,13 @@ describe "Sequel::Plugins::LazyAttributes" do
68
68
  m.name.should == '1'
69
69
  end
70
70
 
71
- it "should lazily load the attribute for a single model object if there is an active identity map" do
72
- @c.with_identity_map do
73
- m = @c.first
74
- m.values.should == {:id=>1}
75
- m.name.should == '1'
76
- m.values.should == {:id=>1, :name=>'1'}
77
- @db.sqls.should == ['SELECT id FROM la LIMIT 1', 'SELECT name FROM la WHERE (id = 1) LIMIT 1']
78
- end
71
+ it "should raise error if the model has no primary key" do
72
+ m = @c.first
73
+ @c.no_primary_key
74
+ proc{m.name}.should raise_error(Sequel::Error)
79
75
  end
80
76
 
81
- it "should lazily load the attribute for a single model object if there is no active identity map" do
77
+ it "should lazily load the attribute for a single model object" do
82
78
  m = @c.first
83
79
  m.values.should == {:id=>1}
84
80
  m.name.should == '1'
@@ -86,34 +82,47 @@ describe "Sequel::Plugins::LazyAttributes" do
86
82
  @db.sqls.should == ['SELECT id FROM la LIMIT 1', 'SELECT name FROM la WHERE (id = 1) LIMIT 1']
87
83
  end
88
84
 
85
+ it "should lazily load the attribute for a frozen model object" do
86
+ m = @c.first
87
+ m.freeze
88
+ m.name.should == '1'
89
+ @db.sqls.should == ['SELECT id FROM la LIMIT 1', 'SELECT name FROM la WHERE (id = 1) LIMIT 1']
90
+ m.name.should == '1'
91
+ @db.sqls.should == ['SELECT name FROM la WHERE (id = 1) LIMIT 1']
92
+ end
93
+
89
94
  it "should not lazily load the attribute for a single model object if the value already exists" do
90
- @c.with_identity_map do
91
- m = @c.first
92
- m.values.should == {:id=>1}
93
- m[:name] = '1'
94
- m.name.should == '1'
95
- m.values.should == {:id=>1, :name=>'1'}
96
- @db.sqls.should == ['SELECT id FROM la LIMIT 1']
97
- end
95
+ m = @c.first
96
+ m.values.should == {:id=>1}
97
+ m[:name] = '1'
98
+ m.name.should == '1'
99
+ m.values.should == {:id=>1, :name=>'1'}
100
+ @db.sqls.should == ['SELECT id FROM la LIMIT 1']
98
101
  end
99
102
 
100
103
  it "should not lazily load the attribute for a single model object if it is a new record" do
101
- @c.with_identity_map do
102
- m = @c.new
103
- m.values.should == {}
104
- m.name.should == nil
105
- @db.sqls.should == []
106
- end
104
+ m = @c.new
105
+ m.values.should == {}
106
+ m.name.should == nil
107
+ @db.sqls.should == []
107
108
  end
108
109
 
109
110
  it "should eagerly load the attribute for all model objects reteived with it" do
110
- @c.with_identity_map do
111
- ms = @c.all
112
- ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
113
- ms.map{|m| m.name}.should == %w'1 2'
114
- ms.map{|m| m.values}.should == [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
115
- @db.sqls.should == ['SELECT id FROM la', 'SELECT id, name FROM la WHERE (id IN (1, 2))']
116
- end
111
+ ms = @c.all
112
+ ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
113
+ ms.map{|m| m.name}.should == %w'1 2'
114
+ ms.map{|m| m.values}.should == [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
115
+ sqls = @db.sqls
116
+ ['SELECT id, name FROM la WHERE (id IN (1, 2))',
117
+ 'SELECT id, name FROM la WHERE (id IN (2, 1))'].should include(sqls.pop)
118
+ sqls.should == ['SELECT id FROM la']
119
+ end
120
+
121
+ it "should not eagerly load the attribute if model instance is frozen, and deal with other frozen instances if not frozen" do
122
+ ms = @c.all
123
+ ms.first.freeze
124
+ ms.map{|m| m.name}.should == %w'1 2'
125
+ @db.sqls.should == ['SELECT id FROM la', 'SELECT name FROM la WHERE (id = 1) LIMIT 1', 'SELECT id, name FROM la WHERE (id IN (2))']
117
126
  end
118
127
 
119
128
  it "should add the accessors to a module included in the class, so they can be easily overridden" do
@@ -122,35 +131,35 @@ describe "Sequel::Plugins::LazyAttributes" do
122
131
  "#{super}-blah"
123
132
  end
124
133
  end
125
- @c.with_identity_map do
126
- ms = @c.all
127
- ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
128
- ms.map{|m| m.name}.should == %w'1-blah 2-blah'
129
- ms.map{|m| m.values}.should == [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
130
- @db.sqls.should == ['SELECT id FROM la', 'SELECT id, name FROM la WHERE (id IN (1, 2))']
131
- end
134
+ ms = @c.all
135
+ ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
136
+ ms.map{|m| m.name}.should == %w'1-blah 2-blah'
137
+ ms.map{|m| m.values}.should == [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
138
+ sqls = @db.sqls
139
+ ['SELECT id, name FROM la WHERE (id IN (1, 2))',
140
+ 'SELECT id, name FROM la WHERE (id IN (2, 1))'].should include(sqls.pop)
141
+ sqls.should == ['SELECT id FROM la']
132
142
  end
133
143
 
134
144
  it "should work with the serialization plugin" do
135
145
  @c.plugin :serialization, :yaml, :name
136
146
  @c.instance_dataset._fetch = @ds._fetch = [[{:id=>1}, {:id=>2}], [{:id=>1, :name=>"--- 3\n"}, {:id=>2, :name=>"--- 6\n"}], [{:id=>1}], [{:name=>"--- 3\n"}]]
137
- @c.with_identity_map do
138
- ms = @ds.all
139
- ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
140
- ms.map{|m| m.name}.should == [3,6]
141
- ms.map{|m| m.values}.should == [{:id=>1, :name=>"--- 3\n"}, {:id=>2, :name=>"--- 6\n"}]
142
- ms.map{|m| m.deserialized_values}.should == [{:name=>3}, {:name=>6}]
143
- ms.map{|m| m.name}.should == [3,6]
144
- @db.sqls.should == ['SELECT id FROM la', 'SELECT id, name FROM la WHERE (id IN (1, 2))']
145
- end
146
- @c.with_identity_map do
147
- m = @ds.first
148
- m.values.should == {:id=>1}
149
- m.name.should == 3
150
- m.values.should == {:id=>1, :name=>"--- 3\n"}
151
- m.deserialized_values.should == {:name=>3}
152
- m.name.should == 3
153
- @db.sqls.should == ["SELECT id FROM la LIMIT 1", "SELECT name FROM la WHERE (id = 1) LIMIT 1"]
154
- end
147
+ ms = @ds.all
148
+ ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
149
+ ms.map{|m| m.name}.should == [3,6]
150
+ ms.map{|m| m.values}.should == [{:id=>1, :name=>"--- 3\n"}, {:id=>2, :name=>"--- 6\n"}]
151
+ ms.map{|m| m.deserialized_values}.should == [{:name=>3}, {:name=>6}]
152
+ ms.map{|m| m.name}.should == [3,6]
153
+ sqls = @db.sqls
154
+ ['SELECT id, name FROM la WHERE (id IN (1, 2))',
155
+ 'SELECT id, name FROM la WHERE (id IN (2, 1))'].should include(sqls.pop)
156
+ sqls.should == ['SELECT id FROM la']
157
+ m = @ds.first
158
+ m.values.should == {:id=>1}
159
+ m.name.should == 3
160
+ m.values.should == {:id=>1, :name=>"--- 3\n"}
161
+ m.deserialized_values.should == {:name=>3}
162
+ m.name.should == 3
163
+ @db.sqls.should == ["SELECT id FROM la LIMIT 1", "SELECT name FROM la WHERE (id = 1) LIMIT 1"]
155
164
  end
156
165
  end