sequel 3.21.0 → 3.24.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 (104) hide show
  1. data/CHANGELOG +112 -0
  2. data/README.rdoc +15 -1
  3. data/doc/association_basics.rdoc +159 -40
  4. data/doc/model_hooks.rdoc +64 -27
  5. data/doc/prepared_statements.rdoc +8 -4
  6. data/doc/reflection.rdoc +8 -2
  7. data/doc/release_notes/3.22.0.txt +39 -0
  8. data/doc/release_notes/3.23.0.txt +172 -0
  9. data/doc/release_notes/3.24.0.txt +420 -0
  10. data/doc/virtual_rows.rdoc +2 -2
  11. data/lib/sequel/adapters/ado.rb +2 -1
  12. data/lib/sequel/adapters/db2.rb +8 -1
  13. data/lib/sequel/adapters/firebird.rb +25 -9
  14. data/lib/sequel/adapters/informix.rb +4 -19
  15. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  16. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  17. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  18. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  19. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  20. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  21. data/lib/sequel/adapters/jdbc.rb +39 -20
  22. data/lib/sequel/adapters/odbc.rb +2 -0
  23. data/lib/sequel/adapters/oracle.rb +12 -0
  24. data/lib/sequel/adapters/postgres.rb +30 -1
  25. data/lib/sequel/adapters/shared/access.rb +10 -0
  26. data/lib/sequel/adapters/shared/informix.rb +45 -0
  27. data/lib/sequel/adapters/shared/mssql.rb +106 -11
  28. data/lib/sequel/adapters/shared/mysql.rb +25 -7
  29. data/lib/sequel/adapters/shared/postgres.rb +39 -6
  30. data/lib/sequel/adapters/shared/sqlite.rb +57 -5
  31. data/lib/sequel/adapters/sqlite.rb +8 -3
  32. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  33. data/lib/sequel/adapters/tinytds.rb +4 -3
  34. data/lib/sequel/ast_transformer.rb +190 -0
  35. data/lib/sequel/core.rb +1 -1
  36. data/lib/sequel/database/connecting.rb +1 -1
  37. data/lib/sequel/database/misc.rb +6 -0
  38. data/lib/sequel/database/query.rb +33 -3
  39. data/lib/sequel/database/schema_methods.rb +13 -4
  40. data/lib/sequel/dataset/features.rb +6 -0
  41. data/lib/sequel/dataset/prepared_statements.rb +17 -2
  42. data/lib/sequel/dataset/query.rb +17 -0
  43. data/lib/sequel/dataset/sql.rb +2 -53
  44. data/lib/sequel/exceptions.rb +4 -0
  45. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  46. data/lib/sequel/extensions/migration.rb +4 -3
  47. data/lib/sequel/extensions/to_dot.rb +95 -83
  48. data/lib/sequel/model/associations.rb +234 -32
  49. data/lib/sequel/model/base.rb +187 -60
  50. data/lib/sequel/model/exceptions.rb +3 -1
  51. data/lib/sequel/model.rb +5 -0
  52. data/lib/sequel/plugins/association_pks.rb +6 -4
  53. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  54. data/lib/sequel/plugins/identity_map.rb +2 -2
  55. data/lib/sequel/plugins/many_through_many.rb +33 -3
  56. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  57. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  58. data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
  59. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  60. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  61. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  62. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  63. data/lib/sequel/sql.rb +8 -0
  64. data/lib/sequel/version.rb +1 -1
  65. data/spec/adapters/mssql_spec.rb +36 -0
  66. data/spec/adapters/mysql_spec.rb +6 -0
  67. data/spec/adapters/postgres_spec.rb +43 -18
  68. data/spec/adapters/spec_helper.rb +5 -0
  69. data/spec/core/connection_pool_spec.rb +56 -77
  70. data/spec/core/database_spec.rb +33 -0
  71. data/spec/core/dataset_spec.rb +127 -16
  72. data/spec/core/expression_filters_spec.rb +13 -0
  73. data/spec/core/schema_spec.rb +13 -1
  74. data/spec/core/spec_helper.rb +5 -0
  75. data/spec/extensions/association_pks_spec.rb +7 -0
  76. data/spec/extensions/columns_introspection_spec.rb +91 -0
  77. data/spec/extensions/defaults_setter_spec.rb +64 -0
  78. data/spec/extensions/many_through_many_spec.rb +77 -0
  79. data/spec/extensions/migration_spec.rb +17 -17
  80. data/spec/extensions/nested_attributes_spec.rb +1 -0
  81. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  82. data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
  83. data/spec/extensions/prepared_statements_spec.rb +72 -0
  84. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  85. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  86. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  87. data/spec/extensions/spec_helper.rb +3 -1
  88. data/spec/extensions/to_dot_spec.rb +3 -5
  89. data/spec/extensions/xml_serializer_spec.rb +12 -0
  90. data/spec/integration/associations_test.rb +212 -0
  91. data/spec/integration/dataset_test.rb +8 -1
  92. data/spec/integration/plugin_test.rb +134 -0
  93. data/spec/integration/prepared_statement_test.rb +72 -1
  94. data/spec/integration/schema_test.rb +66 -8
  95. data/spec/integration/spec_helper.rb +5 -0
  96. data/spec/integration/transaction_test.rb +40 -0
  97. data/spec/integration/type_test.rb +7 -0
  98. data/spec/model/associations_spec.rb +463 -5
  99. data/spec/model/base_spec.rb +59 -0
  100. data/spec/model/eager_loading_spec.rb +269 -1
  101. data/spec/model/hooks_spec.rb +161 -0
  102. data/spec/model/record_spec.rb +30 -0
  103. data/spec/model/spec_helper.rb +5 -0
  104. metadata +29 -4
data/CHANGELOG CHANGED
@@ -1,3 +1,115 @@
1
+ === 3.24.0 (2011-06-01)
2
+
3
+ * Add prepared_statements_association plugin, for using prepared statements by default for regular association loading (jeremyevans)
4
+
5
+ * Add prepared_statements_safe plugin, for making prepared statement use with models more safe (jeremyevans)
6
+
7
+ * Add prepared_statements_with_pk plugin, for using prepared statements for dataset lookups by primary key (jeremyevans)
8
+
9
+ * Fix bug in emulated prepared statement support not supporting nil or false as bound values (jeremyevans)
10
+
11
+ * Add Dataset#unbind for unbinding values from a dataset, for use with creating prepared statements (jeremyevans)
12
+
13
+ * Add prepared_statements plugin for using prepared statements for updates, inserts, deletes, and lookups by primary key (jeremyevans)
14
+
15
+ * Make Dataset#[] for model datasets consider a single integer argument as a lookup by primary key (jeremyevans)
16
+
17
+ * Add Dataset#with_pk for model datasets, for finding first record with matching primary key value (jeremyevans)
18
+
19
+ * Add defaults_setter plugin for setting default values when initializing model instances (jeremyevans)
20
+
21
+ * Add around hooks (e.g. around_save) to Sequel::Model (jeremyevans)
22
+
23
+ * Add Model#initialize_set private method to ease extension writing (jeremyevans)
24
+
25
+ * Only typecast bit fields to booleans on MSSQL, the MySQL bit type is a bitfield, not a boolean (jeremyevans)
26
+
27
+ * Set SQL_AUTO_IS_NULL=0 by default when connecting to MySQL via the swift and jdbc adapters (jeremyevans)
28
+
29
+ * Fix bug in multiple column IN/NOT IN emulation when a model dataset is used (jeremyevans)
30
+
31
+ * Add support for filtering and excluding by association datasets (jeremyevans)
32
+
33
+ * Fix literalization of boolean values in filters on SQLite and MSSQL (jeremyevans)
34
+
35
+ * Add support for filtering and excluding by multiple associations (jeremyevans)
36
+
37
+ * Add support for inverting some SQL::Constant instances such as TRUE, FALSE, NULL, and NOTNULL (jeremyevans)
38
+
39
+ * Add support for excluding by associations to model datasets (jeremyevans)
40
+
41
+ * The Sequel::Postgres.use_iso_date_format setting now only affects future Database objects (jeremyevans)
42
+
43
+ * Add Sequel::Postgres::PG_NAMED_TYPES hash for extensions to register type conversions for non-standard types (jeremyevans, pvh)
44
+
45
+ * Make create_table? use IF NOT EXISTS instead of using SELECT to determine existence, if supported (jeremyevans)
46
+
47
+ * Fix bug in association_pks plugin when associated table has a different primary key column name (jfirebaugh)
48
+
49
+ * Fix limiting rows when connecting to DB2 (semmons99)
50
+
51
+ * Exclude columns from tables in the INFORMATION_SCHEMA when parsing table schema on JDBC (jeremyevans)
52
+
53
+ * Fix limiting rows when connecting to Microsoft Access (jeremyevans)
54
+
55
+ * Add Database#views for getting an array of symbols of view names for the database (jeremyevans, christian.michon)
56
+
57
+ * Make Datbase#tables no longer include view names on MySQL (jeremyevans)
58
+
59
+ * Convert Java CLOB objects to ruby strings when using the JDBC JTDS subadapter (christian.michon)
60
+
61
+ * If Thread#kill is called on a thread with an open transaction, roll the transaction back on ruby 1.8 and rubinius (jeremyevans)
62
+
63
+ * Split informix adapter into shared/specific parts, add JDBC informix subadapter (jeremyevans)
64
+
65
+ === 3.23.0 (2011-05-02)
66
+
67
+ * Migrate issue tracker from Google Code to GitHub Issues (jeremyevans)
68
+
69
+ * Add support for filtering by associations to model datasets (jeremyevans)
70
+
71
+ * Don't call insert_select when saving a model that doesn't select all columns of the table (jeremyevans)
72
+
73
+ * Fix bug when using :select=>[] option for a many_to_many association (jeremyevans)
74
+
75
+ * Add a columns_introspection extension that attempts to skip database queries by introspecting selected columns (jeremyevans)
76
+
77
+ * When combining old integer migrations and new timestamp migrations, make sure old integer migrations are all applied first (jeremyevans)
78
+
79
+ * Support dynamic callbacks to customize regular association loading at query time (jeremyevans)
80
+
81
+ * Support cascading of eager loading with dynamic callbacks for both eager and eager_graph (jeremyevans)
82
+
83
+ * Make the xml_serializer plugin handle namespaced models by using __ instead of / as a separator (jeremyevans)
84
+
85
+ * Allow the :eager_grapher association proc to accept a single hash instead of 3 arguments (jfirebaugh)
86
+
87
+ * Support dynamic callbacks to customize eager loading at query time (jfirebaugh, jeremyevans)
88
+
89
+ * Fix bug in the identity_map plugin for many_to_one associations when the association reflection hadn't been filled in yet (funny-falcon)
90
+
91
+ * Add serialization_modification_detection plugin for detecting changes in serialized columns (jeremyevans) (#333)
92
+
93
+ === 3.22.0 (2011-04-01)
94
+
95
+ * Add disconnect detection to tinytds adapter, though correct behavior may require an update to tiny_tds (cult_hero)
96
+
97
+ * Add Dataset/Database#mssql_unicode_strings accessor when connecting to MSSQL to control string literalization (semmons99, jeremyevans)
98
+
99
+ * Fix ODBC::Time instance handling in the odbc adapter (jeremyevans)
100
+
101
+ * Use Sequel.application_timezone when connecting in the oracle adapter to set the connection's session's timezone (jmthomas)
102
+
103
+ * In the ADO adapter, assume access to SQL Server if a :conn_string option is given that doesn't indicate Access/Jet (damir.si) (#332)
104
+
105
+ * Use the correct class when loading instances for descendents of model classes that use single table inheritance (jeremyevans)
106
+
107
+ * Support for COLLATE in column definitions (jfirebaugh)
108
+
109
+ * Don't use a schema when creating a temporary table (jeremyevans)
110
+
111
+ * Make migrator work correctly when a default_schema is set (jeremyevans) (#331)
112
+
1
113
  === 3.21.0 (2011-03-01)
2
114
 
3
115
  * Make symbol splitting (:table__column___alias) work correctly for identifiers that are not in the \w character class (authorNari)
data/README.rdoc CHANGED
@@ -20,7 +20,7 @@ toolkit for Ruby.
20
20
  * {Website}[http://sequel.rubyforge.org]
21
21
  * {Blog}[http://sequel.heroku.com]
22
22
  * {Source code}[http://github.com/jeremyevans/sequel]
23
- * {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
23
+ * {Bug tracking}[http://github.com/jeremyevans/sequel/issues]
24
24
  * {Google group}[http://groups.google.com/group/sequel-talk]
25
25
  * {RDoc}[http://sequel.rubyforge.org/rdoc]
26
26
 
@@ -685,6 +685,20 @@ Associations can be eagerly loaded via +eager+ and the <tt>:eager</tt> associati
685
685
 
686
686
  In addition to using +eager+, you can also use +eager_graph+, which will use a single query to get the object and all associated objects. This may be necessary if you want to filter or order the result set based on columns in associated tables. It works with cascading as well, the syntax is exactly the same. Note that using eager_graph to eagerly load multiple *_to_many associations will cause the result set to be a cartesian product, so you should be very careful with your filters when using it in that case.
687
687
 
688
+ You can dynamically customize the eagerly loaded dataset by using using a proc. This proc is passed the dataset used for eager loading, and should return a modified copy of that dataset:
689
+
690
+ # Eagerly load only replies containing 'foo'
691
+ Post.eager(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all
692
+
693
+ This also works when using +eager_graph+, in which case the proc is called with dataset to graph into the current dataset:
694
+
695
+ Post.eager_graph(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all
696
+
697
+ You can dynamically customize eager loads for both +eager+ and +eager_graph+ while also cascading, by making the value a single entry hash with the proc as a key, and the cascaded associations as the value:
698
+
699
+ # Eagerly load only replies containing 'foo', and the person and tags for those replies
700
+ Post.eager(:replies=>{proc{|ds| ds.filter(text.like('%foo%'))}=>[:person, :tags]}).all
701
+
688
702
  === Extending the underlying dataset
689
703
 
690
704
  The obvious way to add table-wide logic is to define class methods to the model class definition. That way you can define subsets of the underlying dataset, change the ordering, or perform actions on multiple records:
@@ -293,32 +293,6 @@ Examples:
293
293
  @artist.remove_album(@album)
294
294
  @artist.remove_all_albums
295
295
 
296
- == Dataset Method
297
-
298
- In addition to the above methods, associations also add a instance method
299
- ending in +_dataset+ that returns a dataset representing the objects in the associated table:
300
-
301
- @album.artist_id
302
- # 10
303
- @album.artist_dataset
304
- # SELECT * FROM artists WHERE (id = 10)
305
-
306
- @artist.id
307
- # 20
308
- @artist.albums_dataset
309
- # SELECT * FROM albums WHERE (artist_id = 20)
310
-
311
- The association dataset is just like any other Sequel dataset, in that
312
- it can be further filtered, ordered, etc.:
313
-
314
- @artist.albums_dataset.
315
- filter(:name.like('A%')).
316
- order(:copies_sold).
317
- limit(10)
318
- # SELECT * FROM albums
319
- # WHERE ((artist_id = 20) AND (name LIKE 'A%'))
320
- # ORDER BY copies_sold LIMIT 10
321
-
322
296
  == Caching
323
297
 
324
298
  Associations are cached after being retrieved:
@@ -352,13 +326,150 @@ instance method:
352
326
  @album.artists # [<Artist ...>, ...]
353
327
  @album.associations[:artists] # [<Artist ...>, ...]
354
328
 
355
- Note that while the association method caches associated objects, if you
356
- retrieve access through the association dataset directly, the results
357
- will not be cached:
329
+ == Dataset Method
330
+
331
+ In addition to the above methods, associations also add a instance method
332
+ ending in +_dataset+ that returns a dataset representing the objects in the associated table:
333
+
334
+ @album.artist_id
335
+ # 10
336
+ @album.artist_dataset
337
+ # SELECT * FROM artists WHERE (id = 10)
338
+
339
+ @artist.id
340
+ # 20
341
+ @artist.albums_dataset
342
+ # SELECT * FROM albums WHERE (artist_id = 20)
343
+
344
+ The association dataset is just like any other Sequel dataset, in that
345
+ it can be further filtered, ordered, etc.:
346
+
347
+ @artist.albums_dataset.
348
+ filter(:name.like('A%')).
349
+ order(:copies_sold).
350
+ limit(10)
351
+ # SELECT * FROM albums
352
+ # WHERE ((artist_id = 20) AND (name LIKE 'A%'))
353
+ # ORDER BY copies_sold LIMIT 10
354
+
355
+ Records retrieved using the +_dataset+ method are not cached in the
356
+ associations cache.
358
357
 
359
358
  @album.artists_dataset.all # [<Artist ...>, ...]
360
359
  @album.associations[:artists] # nil
361
360
 
361
+ == Dynamic Association Modification
362
+
363
+ Similar to the +_dataset+ method, you can provide a block to the association
364
+ method to customize the dataset that will be used to retrieve the records. So
365
+ you can apply a filter in either of these two ways:
366
+
367
+ @artist.albums_dataset.filter(:name.like('A%'))
368
+ @artist.albums{|ds| ds.filter(:name.like('A%'))}
369
+
370
+ While they both apply the same filter, using the +_dataset+ method does not
371
+ apply any of the association callbacks or handle association reciprocals (see
372
+ below for details about callbacks and reciprocals). Using a block instead handles
373
+ all those things, and also caches its results in the associations cache (ignoring
374
+ any previously cached value).
375
+
376
+ Note that if you are using ruby 1.8.6, you can't pass a block to the association
377
+ method, you have to pass a proc as an argument:
378
+
379
+ @artist.albums(proc{|ds| ds.filter(:name.like('A%'))})
380
+
381
+ == Filtering By Associations
382
+
383
+ In addition to using the association method to get associated objects, you
384
+ can also use associated objects in filters. For example, while to get
385
+ all albums for a given artist, you would usually do:
386
+
387
+ @artist.albums
388
+ # or @artist.albums_dataset for a dataset
389
+
390
+ You can also do the following:
391
+
392
+ Album.filter(:artist=>@artist).all
393
+ # or leave off the .all for a dataset
394
+
395
+ For filtering by a single association, this isn't very useful. However, unlike
396
+ using the association method, using a filter allows you to filter by multiple
397
+ associations:
398
+
399
+ Album.filter(:artist=>@artist, :publisher=>@publisher)
400
+
401
+ This will return all albums by that artist and published by that publisher.
402
+ This isn't possible using just the association method approach, though you
403
+ can combine the approaches:
404
+
405
+ @artist.albums_dataset.filter(:publisher=>@publisher)
406
+
407
+ This doesn't just work for +many_to_one+ associations, it also works for
408
+ +one_to_one+, +one_to_many+, and +many_to_many+ associations:
409
+
410
+ Album.one_to_one :album_info
411
+ # The album related to that AlbumInfo instance
412
+ Album.filter(:album_info=>AlbumInfo[2])
413
+
414
+ Album.one_to_many :tracks
415
+ # The album related to that Track instance
416
+ Album.filter(:tracks=>Track[3])
417
+
418
+ Album.many_to_many :tags
419
+ # All albums related to that Tag instance
420
+ Album.filter(:tags=>Tag[4])
421
+
422
+ Note that for +one_to_many+ and +many_to_many+ associations, you still
423
+ use the plural form even though only a single model object is given.
424
+
425
+ You can also exclude by associations:
426
+
427
+ Album.exclude(:artist=>@artist).all
428
+
429
+ This will return all albums not by that artist.
430
+
431
+ You can also provide an array with multiple model objects:
432
+
433
+ Album.filter(:artist=>[@artist1, @artist2]).all
434
+
435
+ Similar to using an array of integers or strings, this will return
436
+ all albums whose artist is one of those two artists. You can also
437
+ use +exclude+ if you want all albums not by either of those artists:
438
+
439
+ Album.exclude(:artist=>[@artist1, @artist2]).all
440
+
441
+ If you are using a +one_to_many+ or +many_to_many+ association, you
442
+ may want to return records where the records matches all of multiple
443
+ records, instead of matching any of them. For example:
444
+
445
+ Album.filter(:tags=>[@tag1, @tag2])
446
+
447
+ This matches albums that are associated with either @tag1 or @tag2 or
448
+ both. If you only want ones that you are associated with both, you can
449
+ use separate filter calls:
450
+
451
+ Album.filter(:tags=>@tag1).filter(:tags=>@tag2)
452
+
453
+ Or the the array form of condition specifiers:
454
+
455
+ Album.filter([[:tags, @tag1], [:tags, @tag2]])
456
+
457
+ These will return albums associated with both @tag1 and @tag2.
458
+
459
+ You can also provide a dataset value when filtering by associations:
460
+
461
+ Album.filter(:artist=>Artist.filter(:name.like('A%'))).all
462
+
463
+ This will return all albums whose artist starts with 'A'. Like
464
+ the other forms, this can be inverted:
465
+
466
+ Album.exclude(:artist=>Artist.filter(:name.like('A%'))).all
467
+
468
+ This will return all albums whose artist does not start with 'A'.
469
+
470
+ Note that filtering by associations only works correctly for simple
471
+ associations (ones without conditions).
472
+
362
473
  == Name Collisions
363
474
 
364
475
  Because associations create instance methods, it's possible to override
@@ -1221,22 +1332,30 @@ a JOIN USING or NATURAL JOIN for the graph:
1221
1332
  ==== :eager_grapher
1222
1333
 
1223
1334
  Sets up a custom grapher to use when eager loading the objects via eager_graph.
1224
- This is the eager_graph analogue to the :eager_loader option.
1335
+ This is the eager_graph analogue to the :eager_loader option. This isn't generally
1336
+ needed, as one of the other eager_graph related association options is usually sufficient.
1337
+
1338
+ If specified, should be a proc that accepts one or three three arguments.
1339
+ If the proc takes one argument, it will be given a hash with the following keys:
1225
1340
 
1226
- If specified, should be a proc that accepts three three arguments: a dataset,
1227
- an alias to use for the table to graph for this association, and the alias that
1228
- was used for the current table (since you can cascade associations). Should
1229
- return a modified copy of the dataset with the association graphed into it.
1341
+ :self :: The dataset that is doing the eager loading
1342
+ :table_alias :: An alias to use for the table to graph for this association.
1343
+ :implicit_qualifier :: The alias that was used for the current table (since you can cascade associations).
1344
+ :callback :: A callback proc used to dynamically modify the dataset to graph into the
1345
+ current dataset, before such graphing is done. This is nil if no callback
1346
+ proc is used.
1347
+
1348
+ If the proc takes three arguments, it gets passed the :self, :association_alias,
1349
+ and :table_alias values. The 3 argument procs are allowed for backwards
1350
+ compatibility, and it is recommended to use the 1 argument proc format
1351
+ for new code.
1230
1352
 
1231
1353
  Artist.one_to_many :self_title_albums, :class=>:Album,
1232
- :eager_grapher=>(proc do |ds, ta, iq|
1233
- ds.graph(Album, {:artist_id=>:id, :name=>:name},
1234
- :table_alias=>ta, :implicit_qualifier=>iq)
1354
+ :eager_grapher=>(proc do |eo|
1355
+ eo[:self].graph(Album, {:artist_id=>:id, :name=>:name},
1356
+ :table_alias=>eo[:table_alias], :implicit_qualifier=>eo[:implicit_qualifier])
1235
1357
  end)
1236
1358
 
1237
- This isn't generally needed, as one of the other eager_graph related
1238
- association options is usually sufficient.
1239
-
1240
1359
  ==== :order_eager_graph
1241
1360
 
1242
1361
  Whether to add the order to the dataset's order when graphing via eager_graph.
data/doc/model_hooks.rdoc CHANGED
@@ -4,7 +4,7 @@ This guide is based on http://guides.rubyonrails.org/activerecord_validations_ca
4
4
 
5
5
  == Overview
6
6
 
7
- Model hooks, also known as model callbacks, are used to specify actions that occur at a given point in a model instance's lifecycle, such as before or after the model object is saved, created, updated, destroyed, or validated.
7
+ Model hooks, also known as model callbacks, are used to specify actions that occur at a given point in a model instance's lifecycle, such as before or after the model object is saved, created, updated, destroyed, or validated. There are also around hooks for all types, which wrap the before hooks, the behavior, and the after hooks.
8
8
 
9
9
  == Basic Usage
10
10
 
@@ -23,41 +23,51 @@ The one important thing to note here is the call to +super+ inside the hook. Wh
23
23
 
24
24
  Sequel calls hooks in the following order when saving/creating a new object (one that does not already exist in the database):
25
25
 
26
- * +before_validation+
27
- * +after_validation+
28
- * +before_save+
29
- * +before_create+
30
- * INSERT QUERY
31
- * +after_create+
32
- * +after_save+
26
+ * +around_validation+
27
+ * +before_validation+
28
+ * +validate+ method called
29
+ * +after_validation+
30
+ * +around_save+
31
+ * +before_save+
32
+ * +around_create+
33
+ * +before_create+
34
+ * INSERT QUERY
35
+ * +after_create+
36
+ * +after_save+
33
37
 
34
38
  Sequel calls hooks in the following order when saving an existing object:
35
39
 
36
- * +before_validation+
37
- * +after_validation+
38
- * +before_save+
39
- * +before_update+
40
- * UPDATE QUERY
41
- * +after_update+
42
- * +after_save+
40
+ * +around_validation+
41
+ * +before_validation+
42
+ * +validate+ method called
43
+ * +after_validation+
44
+ * +around_save+
45
+ * +before_save+
46
+ * +around_update+
47
+ * +before_update+
48
+ * INSERT QUERY
49
+ * +after_update+
50
+ * +after_save+
43
51
 
44
- Note that all of the hook calls are the same, except that +before_create+ and +after_create+ are used for a new object, and +before_update+ and +after_update+ are used for an existing object. Note that +before_save+ is called in both cases, before either +before_create+ or +before_update+, and that +after_save+ is also called in both cases, after either +after_create+ or +after_update+.
52
+ Note that all of the hook calls are the same, except that +around_create+, +before_create+ and +after_create+ are used for a new object, and +around_update+, +before_update+ and +after_update+ are used for an existing object. Note that +around_save+, +before_save+, and +after_save+ are called in both cases.
45
53
 
46
54
  Also note that the validation hooks are not called if the <tt>:validate => false</tt> option is passed to save. However, the validation hooks are called if you call <tt>Model#valid?</tt> manually:
47
55
 
48
- * +before_validation+
49
- * VALIDATION HAPPENS
50
- * +after_validation+
56
+ * +around_validation+
57
+ * +before_validation+
58
+ * +validate+ method called
59
+ * +after_validation+
51
60
 
52
61
  Sequel calls hooks in the following order when destroying an existing object:
53
62
 
54
- * +before_destroy+
55
- * DELETE QUERY
56
- * +after_destroy+
63
+ * +around_destroy+
64
+ * +before_destroy+
65
+ * DELETE QUERY
66
+ * +after_destroy+
57
67
 
58
68
  Note that these hooks are only called when using <tt>Model#destroy</tt>, they are not called if you use <tt>Model#delete</tt>.
59
69
 
60
- <tt>Sequel::Model</tt> does support one additional hook, +after_intialize+, which is called after the model object has been initalized. It can be used to set default attribute values for new objects, since by default new <tt>Sequel::Model</tt> objects have no attributes, and the attributes are not filled in until the model object is saved. You should be careful when you are using +after_initialize+, since it is called for every created record. So if you run a query that returns 1000 model objects, it will be called 1000 times.
70
+ <tt>Sequel::Model</tt> does support one additional hook, +after_intialize+, which is called after the model object has been initalized. It can be used to set default attribute values for new objects, since by default new <tt>Sequel::Model</tt> objects have no attributes, and the attributes are not filled in until the model object is saved. You should be careful when you are using +after_initialize+, since it is called for every created record. So if you run a query that returns 1000 model objects, it will be called 1000 times. If you only want to change the behavior for new records, you can override the +initialize_set+ private method, which is called with the hash passed to +initialize+.
61
71
 
62
72
  == Running Hooks
63
73
 
@@ -98,7 +108,7 @@ If you want to insert a row into the model's table without running the creation
98
108
 
99
109
  == Halting Hook Processing
100
110
 
101
- Sequel uses a convention that if any <tt>before_*</tt> hook method returns false (but not nil), that the action will be canceled. You can use this to implement validation-like behavior, that will run even if validations are skipped. For example:
111
+ Sequel uses a convention that if any <tt>before_*</tt> hook method returns false (but not nil), that the action will be canceled and a <tt>Sequel::HookFailed</tt> raised (or +nil+ to be returned by +save+ if +raise_on_save_failure+ is +false+). You can use this to implement validation-like behavior, that will run even if validations are skipped. For example:
102
112
 
103
113
  class Album < Sequel::Model
104
114
  def before_save
@@ -107,9 +117,9 @@ Sequel uses a convention that if any <tt>before_*</tt> hook method returns false
107
117
  end
108
118
  end
109
119
 
110
- While returning false is not really recommended, you should be aware of this behavior so that you do not inadvertently return false.
120
+ While returning false is not really recommended, you should be aware of this behavior so that you do not inadvertently return false. For around hooks, neglecting to call +super+ halts hook processing in the same way as returning +false+ in a before hook. You can't halt hook processing in after hooks, since by then the main processing has already taken place.
111
121
 
112
- By default, Sequel runs hooks other than validation hooks inside a transaction, so if you abort the hook by returning false in a before hook or by raising an exception in the hook, Sequel will rollback the transaction. However, note that the implicit use of transactions when saving and destroying model objects is conditional (it depends on the model instance's +use_transactions+ setting).
122
+ By default, Sequel runs hooks other than validation hooks inside a transaction, so if you abort the hook by returning false in a before hook or by raising an exception in any hook, Sequel will rollback the transaction. However, note that the implicit use of transactions when saving and destroying model objects is conditional (it depends on the model instance's +use_transactions+ setting and the <tt>:transaction</tt> option passed to save).
113
123
 
114
124
  == Conditional Hooks
115
125
 
@@ -183,6 +193,7 @@ This allows the following general principles to be true:
183
193
 
184
194
  * before hooks are run in reverse order of inclusion
185
195
  * after hooks are run in order of inclusion
196
+ * returning false in any before hook will pass the false value down the hook method chain, halting the hook processing.
186
197
 
187
198
  So if you define the same before hook in both a model and a plugin that the model uses, the hooks will be called in this order:
188
199
 
@@ -193,6 +204,32 @@ So if you define the same before hook in both a model and a plugin that the mode
193
204
 
194
205
  Again, Sequel does not enforce that, and you are free to call +super+ in an order other than the recommended one (just make sure that you call it).
195
206
 
207
+ == Around Hooks
208
+
209
+ Around hooks should only be used if you cannot accomplish the same results with before and after hooks. For example, if you want to catch database errors caused by the +INSERT+ or +UPDATE+ query when saving a model object and raise them as validation errors, you cannot use a before or after hook. You have use an +around_save+ hook:
210
+
211
+ class Album < Sequel::Model
212
+ def around_save
213
+ super
214
+ rescue Sequel::DatabaseError => e
215
+ # parse database error, set error on self, and reraise a Sequel::ValidationFailed
216
+ end
217
+ end
218
+
219
+ Likewise, let's say that upon retrieval, you associate an object with a file descriptor, and you want to ensure that the file descriptor is closed after the object is saved to the database. Let's assume you are always saving the object and you are not using validations. You could not use an +after_save+ hook safely, since if the database raises an error, the +after_save+ method will not be called. In this case, an +around_save+ hook is also the correct choice:
220
+
221
+ class Album < Sequel::Model
222
+ def around_save
223
+ super
224
+ ensure
225
+ @file_descriptor.close
226
+ end
227
+ end
228
+
196
229
  == +hook_class_methods+
197
230
 
198
- While it's recommended to write your hooks as instance methods, Sequel ships with a +hook_class_methods+ plugin that allows you to define hooks via class methods. It exists mostly for legacy compatibility, but is still supported.
231
+ While it's recommended to write your hooks as instance methods, Sequel ships with a +hook_class_methods+ plugin that allows you to define hooks via class methods. It exists mostly for legacy compatibility, but is still supported. However, it does not implement around hooks.
232
+
233
+ == +instance_hooks+
234
+
235
+ Sequel also ships with an +instance_hooks+ plugin that allows you to define before and after hooks on a per instance basis. It's very useful as it allows you to delay action on an instance until before or after saving. This can be important if you want to modify a group of related objects together (which is how the +nested_attributes+ plugin uses +instance_hooks+).
@@ -5,7 +5,7 @@ database you are using, the Sequel prepared statement/bound variable API remains
5
5
  the same. There is native support for prepared statements/bound variables on
6
6
  the following databases:
7
7
 
8
- * PostgreSQL (using the pg driver, requires type specifiers)
8
+ * PostgreSQL (using the pg driver, may require type specifiers)
9
9
  * MySQL (prepared statements only, as the ruby mysql driver doesn't support
10
10
  bound variables)
11
11
  * SQLite
@@ -13,6 +13,10 @@ the following databases:
13
13
 
14
14
  Support on other databases is emulated via string interpolation.
15
15
 
16
+ You can use the prepared_statements model plugin to automatically use prepared
17
+ statements for some common model actions such as saving or deleting a model
18
+ instance, or looking up a model based on a primary key.
19
+
16
20
  == Placeholders
17
21
 
18
22
  Generally, when using prepared statements (and certainly when using bound
@@ -74,9 +78,9 @@ and update queries, the hash to insert/update is passed to +prepare+:
74
78
 
75
79
  If you are using the ruby-postgres or postgres-pr driver, PostgreSQL uses the
76
80
  default emulated support. If you are using ruby-pg, there is native support,
77
- but it requires type specifiers most of the time. This is easy if you have
81
+ but it may require type specifiers. This is easy if you have
78
82
  direct control over the SQL string, but since Sequel abstracts that, the types
79
- have to be specified another way. This is done by adding a __* suffix to the
83
+ can be specified another way. This is done by adding a __* suffix to the
80
84
  placeholder symbol (e.g. :$name__text, which will be compiled to "$1::text"
81
85
  in the SQL). Prepared statements are always server side.
82
86
 
@@ -87,7 +91,7 @@ statements are cached per connection.
87
91
 
88
92
  === MySQL
89
93
 
90
- The MySQL ruby driver does not support bound variables, so the the bound
94
+ The MySQL ruby driver does not support bound variables, so the bound
91
95
  variable methods fall back to string interpolation. It uses server side
92
96
  prepared statements.
93
97
 
data/doc/reflection.rdoc CHANGED
@@ -16,13 +16,19 @@ In some cases, the adapter scheme will be the same as the database to which you
16
16
 
17
17
  == Tables in the Database
18
18
 
19
- On many database types/adapters, Database#tables exists and gives an array of table name symbols:
19
+ Database#tables gives an array of table name symbols:
20
+
21
+ DB.tables # [:table1, :table2, :table3, ...]
22
+
23
+ == Views in the Database
24
+
25
+ Database#views and gives an array of view name symbols:
20
26
 
21
27
  DB.tables # [:table1, :table2, :table3, ...]
22
28
 
23
29
  == Indexes on a table
24
30
 
25
- On a few database types/adapters, Database#indexes takes a table name gives a hash of index information. Keys are index names, values are subhashes with the keys :columns and :unique :
31
+ Database#indexes takes a table name gives a hash of index information. Keys are index names, values are subhashes with the keys :columns and :unique :
26
32
 
27
33
  DB.indexes(:table1) # {:index1=>{:columns=>[:column1], :unique=>false}, :index2=>{:columns=>[:column2, :column3], :unique=>true}}
28
34
 
@@ -0,0 +1,39 @@
1
+ = New Features
2
+
3
+ * Support COLLATE in column definitions. At least MySQL and Microsoft
4
+ SQL Server support them, and PostgreSQL 9.1 should as well.
5
+
6
+ * When connecting to Microsoft SQL Server, you can use the
7
+ mssql_unicode_strings accessor to turn of the default usage
8
+ of unicode strings (N'') and use regular strings (''). This
9
+ can improve performance, but changes the behavior. It's
10
+ set to true by default for backwards compatibility. You can
11
+ change it at both the dataset and database level:
12
+
13
+ DB.mssql_unicode_strings = false # default for datasets
14
+ dataset.mssql_unicode_strings = false # just this dataset
15
+
16
+ * In the oracle adapter, if Sequel.application_timezone is :utc, set
17
+ the timezone for the connection to use the 00:00 timezone.
18
+
19
+ = Other Improvements
20
+
21
+ * In the single_table_inheritance plugin, correctly handle a
22
+ multi-level class hierarchy so that loading instances from a
23
+ middle level of the hierarchy can return instances of subclasses.
24
+
25
+ * Don't use a schema when creating a temporary table, even if
26
+ default_schema is set.
27
+
28
+ * Fix the migrator when a default_schema is used.
29
+
30
+ * In the ado adapter, assume a connection to SQL Server if the
31
+ :conn_string is given and doesn't indicate Access/Jet.
32
+
33
+ * Fix fetching rows in the tinytds adapter when the
34
+ identifier_output_method is nil.
35
+
36
+ * The tinytds adapter now checks for disconnect errors, but it might
37
+ not be reliable until the next release of tiny_tds.
38
+
39
+ * The odbc adapter now handles ODBC::Time instances correctly.