sequel 3.46.0 → 3.47.0

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