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.
- data/CHANGELOG +112 -0
- data/README.rdoc +15 -1
- data/doc/association_basics.rdoc +159 -40
- data/doc/model_hooks.rdoc +64 -27
- data/doc/prepared_statements.rdoc +8 -4
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +2 -1
- data/lib/sequel/adapters/db2.rb +8 -1
- data/lib/sequel/adapters/firebird.rb +25 -9
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc.rb +39 -20
- data/lib/sequel/adapters/odbc.rb +2 -0
- data/lib/sequel/adapters/oracle.rb +12 -0
- data/lib/sequel/adapters/postgres.rb +30 -1
- data/lib/sequel/adapters/shared/access.rb +10 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +106 -11
- data/lib/sequel/adapters/shared/mysql.rb +25 -7
- data/lib/sequel/adapters/shared/postgres.rb +39 -6
- data/lib/sequel/adapters/shared/sqlite.rb +57 -5
- data/lib/sequel/adapters/sqlite.rb +8 -3
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +4 -3
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +33 -3
- data/lib/sequel/database/schema_methods.rb +13 -4
- data/lib/sequel/dataset/features.rb +6 -0
- data/lib/sequel/dataset/prepared_statements.rb +17 -2
- data/lib/sequel/dataset/query.rb +17 -0
- data/lib/sequel/dataset/sql.rb +2 -53
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +4 -3
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +234 -32
- data/lib/sequel/model/base.rb +187 -60
- data/lib/sequel/model/exceptions.rb +3 -1
- data/lib/sequel/model.rb +5 -0
- data/lib/sequel/plugins/association_pks.rb +6 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/many_through_many.rb +33 -3
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +8 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +6 -0
- data/spec/adapters/postgres_spec.rb +43 -18
- data/spec/adapters/spec_helper.rb +5 -0
- data/spec/core/connection_pool_spec.rb +56 -77
- data/spec/core/database_spec.rb +33 -0
- data/spec/core/dataset_spec.rb +127 -16
- data/spec/core/expression_filters_spec.rb +13 -0
- data/spec/core/schema_spec.rb +13 -1
- data/spec/core/spec_helper.rb +5 -0
- data/spec/extensions/association_pks_spec.rb +7 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/many_through_many_spec.rb +77 -0
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +12 -0
- data/spec/integration/associations_test.rb +212 -0
- data/spec/integration/dataset_test.rb +8 -1
- data/spec/integration/plugin_test.rb +134 -0
- data/spec/integration/prepared_statement_test.rb +72 -1
- data/spec/integration/schema_test.rb +66 -8
- data/spec/integration/spec_helper.rb +5 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +7 -0
- data/spec/model/associations_spec.rb +463 -5
- data/spec/model/base_spec.rb +59 -0
- data/spec/model/eager_loading_spec.rb +269 -1
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/record_spec.rb +30 -0
- data/spec/model/spec_helper.rb +5 -0
- 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://
|
|
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:
|
data/doc/association_basics.rdoc
CHANGED
|
@@ -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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
1227
|
-
|
|
1228
|
-
was used for the current table (since you can cascade associations).
|
|
1229
|
-
|
|
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 |
|
|
1233
|
-
|
|
1234
|
-
:table_alias=>
|
|
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
|
-
* +
|
|
27
|
-
* +
|
|
28
|
-
* +
|
|
29
|
-
* +
|
|
30
|
-
*
|
|
31
|
-
* +
|
|
32
|
-
* +
|
|
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
|
-
* +
|
|
37
|
-
* +
|
|
38
|
-
* +
|
|
39
|
-
* +
|
|
40
|
-
*
|
|
41
|
-
* +
|
|
42
|
-
* +
|
|
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 +
|
|
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
|
-
* +
|
|
49
|
-
*
|
|
50
|
-
* +
|
|
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
|
-
* +
|
|
55
|
-
*
|
|
56
|
-
*
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|