sequel 3.37.0 → 3.38.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/CHANGELOG +56 -0
  2. data/README.rdoc +82 -58
  3. data/Rakefile +6 -5
  4. data/bin/sequel +1 -1
  5. data/doc/active_record.rdoc +67 -52
  6. data/doc/advanced_associations.rdoc +33 -48
  7. data/doc/association_basics.rdoc +41 -51
  8. data/doc/cheat_sheet.rdoc +21 -21
  9. data/doc/core_extensions.rdoc +374 -0
  10. data/doc/dataset_basics.rdoc +5 -5
  11. data/doc/dataset_filtering.rdoc +47 -43
  12. data/doc/mass_assignment.rdoc +1 -1
  13. data/doc/migration.rdoc +4 -5
  14. data/doc/model_hooks.rdoc +3 -3
  15. data/doc/object_model.rdoc +31 -25
  16. data/doc/opening_databases.rdoc +19 -5
  17. data/doc/prepared_statements.rdoc +2 -2
  18. data/doc/querying.rdoc +109 -52
  19. data/doc/reflection.rdoc +6 -6
  20. data/doc/release_notes/3.38.0.txt +234 -0
  21. data/doc/schema_modification.rdoc +22 -13
  22. data/doc/sharding.rdoc +8 -9
  23. data/doc/sql.rdoc +154 -112
  24. data/doc/testing.rdoc +47 -7
  25. data/doc/thread_safety.rdoc +1 -1
  26. data/doc/transactions.rdoc +1 -1
  27. data/doc/validations.rdoc +1 -1
  28. data/doc/virtual_rows.rdoc +29 -43
  29. data/lib/sequel/adapters/do/postgres.rb +1 -4
  30. data/lib/sequel/adapters/jdbc.rb +14 -3
  31. data/lib/sequel/adapters/jdbc/db2.rb +9 -0
  32. data/lib/sequel/adapters/jdbc/derby.rb +41 -4
  33. data/lib/sequel/adapters/jdbc/jtds.rb +11 -0
  34. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -6
  35. data/lib/sequel/adapters/mock.rb +10 -4
  36. data/lib/sequel/adapters/postgres.rb +1 -28
  37. data/lib/sequel/adapters/shared/mssql.rb +23 -13
  38. data/lib/sequel/adapters/shared/postgres.rb +46 -0
  39. data/lib/sequel/adapters/swift.rb +21 -13
  40. data/lib/sequel/adapters/swift/mysql.rb +1 -0
  41. data/lib/sequel/adapters/swift/postgres.rb +4 -5
  42. data/lib/sequel/adapters/swift/sqlite.rb +2 -1
  43. data/lib/sequel/adapters/tinytds.rb +14 -2
  44. data/lib/sequel/adapters/utils/pg_types.rb +5 -0
  45. data/lib/sequel/core.rb +29 -17
  46. data/lib/sequel/database/query.rb +1 -1
  47. data/lib/sequel/database/schema_generator.rb +3 -0
  48. data/lib/sequel/dataset/actions.rb +5 -6
  49. data/lib/sequel/dataset/query.rb +7 -7
  50. data/lib/sequel/dataset/sql.rb +5 -18
  51. data/lib/sequel/extensions/core_extensions.rb +8 -12
  52. data/lib/sequel/extensions/pg_array.rb +59 -33
  53. data/lib/sequel/extensions/pg_array_ops.rb +32 -4
  54. data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
  55. data/lib/sequel/extensions/pg_hstore.rb +32 -17
  56. data/lib/sequel/extensions/pg_hstore_ops.rb +32 -3
  57. data/lib/sequel/extensions/pg_inet.rb +1 -2
  58. data/lib/sequel/extensions/pg_interval.rb +0 -1
  59. data/lib/sequel/extensions/pg_json.rb +41 -23
  60. data/lib/sequel/extensions/pg_range.rb +36 -11
  61. data/lib/sequel/extensions/pg_range_ops.rb +32 -4
  62. data/lib/sequel/extensions/pg_row.rb +572 -0
  63. data/lib/sequel/extensions/pg_row_ops.rb +164 -0
  64. data/lib/sequel/extensions/query.rb +3 -3
  65. data/lib/sequel/extensions/schema_dumper.rb +7 -8
  66. data/lib/sequel/extensions/select_remove.rb +1 -1
  67. data/lib/sequel/model/base.rb +1 -0
  68. data/lib/sequel/no_core_ext.rb +1 -1
  69. data/lib/sequel/plugins/pg_row.rb +121 -0
  70. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  71. data/lib/sequel/plugins/validation_helpers.rb +31 -0
  72. data/lib/sequel/sql.rb +64 -44
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/mssql_spec.rb +37 -12
  75. data/spec/adapters/mysql_spec.rb +39 -75
  76. data/spec/adapters/oracle_spec.rb +11 -11
  77. data/spec/adapters/postgres_spec.rb +414 -237
  78. data/spec/adapters/spec_helper.rb +1 -1
  79. data/spec/adapters/sqlite_spec.rb +14 -14
  80. data/spec/core/database_spec.rb +6 -6
  81. data/spec/core/dataset_spec.rb +169 -205
  82. data/spec/core/expression_filters_spec.rb +182 -295
  83. data/spec/core/object_graph_spec.rb +6 -6
  84. data/spec/core/schema_spec.rb +14 -14
  85. data/spec/core/spec_helper.rb +1 -0
  86. data/spec/{extensions/core_extensions_spec.rb → core_extensions_spec.rb} +208 -14
  87. data/spec/extensions/columns_introspection_spec.rb +5 -5
  88. data/spec/extensions/hook_class_methods_spec.rb +28 -36
  89. data/spec/extensions/many_through_many_spec.rb +4 -4
  90. data/spec/extensions/pg_array_ops_spec.rb +15 -7
  91. data/spec/extensions/pg_array_spec.rb +81 -48
  92. data/spec/extensions/pg_auto_parameterize_spec.rb +2 -2
  93. data/spec/extensions/pg_hstore_ops_spec.rb +13 -9
  94. data/spec/extensions/pg_hstore_spec.rb +66 -65
  95. data/spec/extensions/pg_inet_spec.rb +2 -4
  96. data/spec/extensions/pg_interval_spec.rb +2 -3
  97. data/spec/extensions/pg_json_spec.rb +20 -18
  98. data/spec/extensions/pg_range_ops_spec.rb +11 -4
  99. data/spec/extensions/pg_range_spec.rb +30 -7
  100. data/spec/extensions/pg_row_ops_spec.rb +48 -0
  101. data/spec/extensions/pg_row_plugin_spec.rb +45 -0
  102. data/spec/extensions/pg_row_spec.rb +323 -0
  103. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  104. data/spec/extensions/query_literals_spec.rb +11 -11
  105. data/spec/extensions/query_spec.rb +3 -3
  106. data/spec/extensions/schema_dumper_spec.rb +20 -4
  107. data/spec/extensions/schema_spec.rb +18 -41
  108. data/spec/extensions/select_remove_spec.rb +4 -4
  109. data/spec/extensions/spec_helper.rb +4 -8
  110. data/spec/extensions/to_dot_spec.rb +5 -5
  111. data/spec/extensions/validation_class_methods_spec.rb +28 -16
  112. data/spec/integration/associations_test.rb +20 -20
  113. data/spec/integration/dataset_test.rb +98 -98
  114. data/spec/integration/eager_loader_test.rb +13 -27
  115. data/spec/integration/plugin_test.rb +5 -5
  116. data/spec/integration/prepared_statement_test.rb +22 -13
  117. data/spec/integration/schema_test.rb +28 -18
  118. data/spec/integration/spec_helper.rb +1 -1
  119. data/spec/integration/timezone_test.rb +2 -2
  120. data/spec/integration/type_test.rb +15 -6
  121. data/spec/model/association_reflection_spec.rb +1 -1
  122. data/spec/model/associations_spec.rb +4 -4
  123. data/spec/model/base_spec.rb +5 -5
  124. data/spec/model/eager_loading_spec.rb +15 -15
  125. data/spec/model/model_spec.rb +32 -32
  126. data/spec/model/record_spec.rb +16 -0
  127. data/spec/model/spec_helper.rb +2 -6
  128. data/spec/model/validations_spec.rb +1 -1
  129. metadata +16 -4
data/CHANGELOG CHANGED
@@ -1,3 +1,59 @@
1
+ === 3.38.0 (2012-08-01)
2
+
3
+ * Sequel now recognizes the double(x, y) and double(x, y) unsigned MySQL types (Slike9, jeremyevans) (#528)
4
+
5
+ * The swift subadapters now require swift-db-* instead of swift itself (deepfryed, jeremyevans) (#526)
6
+
7
+ * Add :textsize option to tinytds adapter to override the default TEXTSIZE (jeremyevans, wardrop) (#525)
8
+
9
+ * Support an output identifier method in the swift adapter (jeremyevans)
10
+
11
+ * Add Model#to_hash as an alias to Model#values (jeremyevans)
12
+
13
+ * When loading multiple pg_* extensions via Database#extension, only reset the conversion procs once (jeremyevans)
14
+
15
+ * Don't allow model typecasting from string to postgres array, hstore, or composite types (jeremyevans)
16
+
17
+ * Add pg_typecast_on_load plugin for converting advanced PostgreSQL types on load the {jdbc,do,swift}/postgres adapters (jeremyevans)
18
+
19
+ * Make all adapters that connect to PostgreSQL store type conversion procs (jeremyevans)
20
+
21
+ * Add type oid to column schema on PostgreSQL (jeremyevans)
22
+
23
+ * Add pg_row plugin, for using Sequel::Model classes to represent PostgreSQL row-valued/composite types (jeremyevans)
24
+
25
+ * Add pg_row_ops extension for DSL support for PostgreSQL row-valued/composite types (jeremyevans)
26
+
27
+ * Add pg_row extension for dealing with PostgreSQL row-valued/composite types (jeremyevans)
28
+
29
+ * Allow custom registered array types in the pg_array extension to be Database instance specific (jeremyevans)
30
+
31
+ * Remove Sequel::SQL::IdentifierMethods (jeremyevans)
32
+
33
+ * Don't have the schema_dumper extension produce code that relies on the core_extensions (jeremyevans)
34
+
35
+ * Fix dropping of columns with constraints on Microsoft SQL Server (mluu, jeremyevans) (#515, #518)
36
+
37
+ * Don't have pg_* extensions add methods to core classes unless the core_extensions extension is loaded (jeremyevans)
38
+
39
+ * Use real boolean literals on derby 10.7+ (jeremyevans, matthauck) (#514)
40
+
41
+ * Work around JRuby 1.6 ruby 1.9 mode bug in Time#nsec for Time prepared statement arguments on jdbc (jeremyevans)
42
+
43
+ * Handle blob prepared statement arguments on jdbc/db2 and jdbc/oracle (jeremyevans)
44
+
45
+ * Handle blob values in the swift adapter (jeremyevans)
46
+
47
+ * Handle better nil prepared statement arguments on jdbc (jeremyevans) (#513)
48
+
49
+ * Make SQL::Blob objects handle as, cast, and lit methods even if the core extensions are not loaded (jeremyevans)
50
+
51
+ * Make #* with no arguments produce a ColumnAll for Identifier and QualifiedIdentifier (jeremyevans)
52
+
53
+ * Sequel.expr(:symbol) now returns Identifier, QualifiedIdentifier, or AliasedExpression instead of Wrapper (jeremyevans)
54
+
55
+ * Treat clob columns as string instead of blob on Derby (jeremyevans) (#509)
56
+
1
57
  === 3.37.0 (2012-07-02)
2
58
 
3
59
  * Allow specifying eager_graph alias base on a per-call basis using an AliasedExpression (jeremyevans)
data/README.rdoc CHANGED
@@ -171,7 +171,7 @@ You can also iterate through records one at a time using +each+:
171
171
 
172
172
  Or perform more advanced stuff:
173
173
 
174
- names_and_dates = posts.map{|r| [r[:name], r[:date]]}
174
+ names_and_dates = posts.map([:name, :date])
175
175
  old_posts, recent_posts = posts.partition{|r| r[:date] < Date.today - 7}
176
176
 
177
177
  You can also retrieve the first record in a dataset:
@@ -191,29 +191,29 @@ If the dataset is ordered, you can also ask for the last record:
191
191
 
192
192
  === Filtering Records
193
193
 
194
- An easy way to filter records is to provide a hash of values to match to +filter+:
194
+ An easy way to filter records is to provide a hash of values to match to +where+:
195
195
 
196
- my_posts = posts.filter(:category => 'ruby', :author => 'david')
196
+ my_posts = posts.where(:category => 'ruby', :author => 'david')
197
197
  # WHERE category = 'ruby' AND author = 'david'
198
198
 
199
199
  You can also specify ranges:
200
200
 
201
- my_posts = posts.filter(:stamp => (Date.today - 14)..(Date.today - 7))
201
+ my_posts = posts.where(:stamp => (Date.today - 14)..(Date.today - 7))
202
202
  # WHERE stamp >= '2010-06-30' AND stamp <= '2010-07-07'
203
203
 
204
204
  Or arrays of values:
205
205
 
206
- my_posts = posts.filter(:category => ['ruby', 'postgres', 'linux'])
206
+ my_posts = posts.where(:category => ['ruby', 'postgres', 'linux'])
207
207
  # WHERE category IN ('ruby', 'postgres', 'linux')
208
208
 
209
209
  Sequel also accepts expressions:
210
210
 
211
- my_posts = posts.filter{stamp > Date.today << 1}
211
+ my_posts = posts.where{stamp > Date.today << 1}
212
212
  # WHERE stamp > '2010-06-14'
213
213
 
214
214
  Some adapters will also let you specify Regexps:
215
215
 
216
- my_posts = posts.filter(:category => /ruby/i)
216
+ my_posts = posts.where(:category => /ruby/i)
217
217
  # WHERE category ~* 'ruby'
218
218
 
219
219
  You can also use an inverse filter via +exclude+:
@@ -223,22 +223,21 @@ You can also use an inverse filter via +exclude+:
223
223
 
224
224
  You can also specify a custom WHERE clause using a string:
225
225
 
226
- posts.filter('stamp IS NOT NULL')
226
+ posts.where('stamp IS NOT NULL')
227
227
  # WHERE stamp IS NOT NULL
228
228
 
229
229
  You can use parameters in your string, as well:
230
230
 
231
231
  author_name = 'JKR'
232
- posts.filter('(stamp < ?) AND (author != ?)', Date.today - 3, author_name)
232
+ posts.where('(stamp < ?) AND (author != ?)', Date.today - 3, author_name)
233
233
  # WHERE (stamp < '2010-07-11') AND (author != 'JKR')
234
- posts.filter{(stamp < Date.today - 3) & ~{:author => author_name}} # same as above
235
234
 
236
235
  Datasets can also be used as subqueries:
237
236
 
238
- DB[:items].filter('price > ?', DB[:items].select{avg(price) + 100})
237
+ DB[:items].where('price > ?', DB[:items].select{avg(price) + 100})
239
238
  # WHERE price > (SELECT avg(price) + 100 FROM items)
240
239
 
241
- After filtering you can retrieve the matching records by using any of the retrieval methods:
240
+ After filtering, you can retrieve the matching records by using any of the retrieval methods:
242
241
 
243
242
  my_posts.each{|row| p row}
244
243
 
@@ -248,7 +247,7 @@ See the doc/dataset_filtering.rdoc file for more details.
248
247
 
249
248
  Counting records is easy using +count+:
250
249
 
251
- posts.filter(:category.like('%ruby%')).count
250
+ posts.where(:category.like('%ruby%')).count
252
251
  # SELECT COUNT(*) FROM posts WHERE category LIKE '%ruby%'
253
252
 
254
253
  And you can also query maximum/minimum values via +max+ and +min+:
@@ -275,7 +274,7 @@ Ordering datasets is simple using +order+:
275
274
  posts.order(:stamp, :name)
276
275
  # ORDER BY stamp, name
277
276
 
278
- Chaining +order+ doesn't work the same as +filter+:
277
+ Chaining +order+ doesn't work the same as +where+:
279
278
 
280
279
  posts.order(:stamp).order(:name)
281
280
  # ORDER BY name
@@ -285,10 +284,27 @@ The +order_append+ method chains this way, though:
285
284
  posts.order(:stamp).order_append(:name)
286
285
  # ORDER BY stamp, name
287
286
 
287
+ The +order_prepend+ method can be used as well:
288
+
289
+ posts.order(:stamp).order_prepend(:name)
290
+ # ORDER BY name, stamp
291
+
288
292
  You can also specify descending order:
289
293
 
290
- posts.order(:stamp.desc)
294
+ posts.reverse_order(:stamp)
291
295
  # ORDER BY stamp DESC
296
+ posts.order(Sequel.desc(:stamp))
297
+ # ORDER BY stamp DESC
298
+
299
+ === Core Extensions
300
+
301
+ Note the use of <tt>Sequel.desc(:stamp)</tt> in the above example. Much of Sequel's DSL uses this style, calling methods on the Sequel module that return SQL expression objects. Sequel also ships with a {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]) that integrates Sequel's DSL better into the ruby language, allowing you to write:
302
+
303
+ :stamp.desc
304
+
305
+ instead of:
306
+
307
+ Sequel.desc(:stamp)
292
308
 
293
309
  === Selecting Columns
294
310
 
@@ -299,7 +315,7 @@ Selecting specific columns to be returned is also simple using +select+:
299
315
  posts.select(:stamp, :name)
300
316
  # SELECT stamp, name FROM posts
301
317
 
302
- Chaining +select+ works like +order+, not +filter+:
318
+ Chaining +select+ works like +order+, not +where+:
303
319
 
304
320
  posts.select(:stamp).select(:name)
305
321
  # SELECT name FROM posts
@@ -313,16 +329,16 @@ As you might expect, there is an +order_append+ equivalent for +select+ called +
313
329
 
314
330
  Deleting records from the table is done with +delete+:
315
331
 
316
- posts.filter('stamp < ?', Date.today - 3).delete
332
+ posts.where('stamp < ?', Date.today - 3).delete
317
333
  # DELETE FROM posts WHERE stamp < '2010-07-11'
318
334
 
319
335
  Be very careful when deleting, as +delete+ affects all rows in the dataset.
320
- +filter+ first, +delete+ second, unless you want to empty the table:
336
+ Call +where+ first and +delete+ second:
321
337
 
322
338
  # DO THIS:
323
- posts.filter('stamp < ?', Date.today - 7).delete
339
+ posts.where('stamp < ?', Date.today - 7).delete
324
340
  # NOT THIS:
325
- posts.delete.filter('stamp < ?', Date.today - 7)
341
+ posts.delete.where('stamp < ?', Date.today - 7)
326
342
 
327
343
  === Inserting Records
328
344
 
@@ -335,21 +351,21 @@ Inserting records into the table is done with +insert+:
335
351
 
336
352
  Updating records in the table is done with +update+:
337
353
 
338
- posts.filter('stamp < ?', Date.today - 7).update(:state => 'archived')
354
+ posts.where('stamp < ?', Date.today - 7).update(:state => 'archived')
339
355
  # UPDATE posts SET state = 'archived' WHERE stamp < '2010-07-07'
340
356
 
341
357
  You can reference table columns when choosing what values to set:
342
358
 
343
- posts.filter{|o| o.stamp < Date.today - 7}.update(:backup_number => :backup_number + 1)
359
+ posts.where{|o| o.stamp < Date.today - 7}.update(:backup_number => :backup_number + 1)
344
360
  # UPDATE posts SET backup_number = backup_number + 1 WHERE stamp < '2010-07-07'
345
361
 
346
- As with +delete+, +update+ affects all rows in the dataset, so +filter+ first,
347
- +update+ second, unless you want to update all rows:
362
+ As with +delete+, +update+ affects all rows in the dataset, so +where+ first,
363
+ +update+ second:
348
364
 
349
365
  # DO THIS:
350
- posts.filter('stamp < ?', Date.today - 7).update(:state => 'archived')
366
+ posts.where('stamp < ?', Date.today - 7).update(:state => 'archived')
351
367
  # NOT THIS:
352
- posts.update(:state => 'archived').filter('stamp < ?', Date.today - 7)
368
+ posts.update(:state => 'archived').where('stamp < ?', Date.today - 7)
353
369
 
354
370
  === Transactions
355
371
 
@@ -357,7 +373,7 @@ You can wrap some code in a database transaction using the <tt>Database#transact
357
373
 
358
374
  DB.transaction do
359
375
  posts.insert(:category => 'ruby', :author => 'david')
360
- posts.filter('stamp < ?', Date.today - 7).update(:state => 'archived')
376
+ posts.where('stamp < ?', Date.today - 7).update(:state => 'archived')
361
377
  end
362
378
 
363
379
  If the block does not raise an exception, the transaction will be committed.
@@ -378,11 +394,15 @@ and not raise an exception outside the block, you can raise the
378
394
  Sequel makes it easy to join tables:
379
395
 
380
396
  order_items = DB[:items].join(:order_items, :item_id => :id).
381
- filter(:order_items__order_id => 1234)
397
+ where(:order_items__order_id => 1234)
382
398
  # SELECT * FROM items INNER JOIN order_items
383
399
  # ON order_items.item_id = items.id
384
400
  # WHERE order_items.order_id = 1234
385
401
 
402
+ The important thing to note here is that item_id is automatically qualified with
403
+ the table being joined, and id is automatically qualified with the last table
404
+ joined.
405
+
386
406
  You can then do anything you like with the dataset:
387
407
 
388
408
  order_total = order_items.sum(:price)
@@ -406,14 +426,14 @@ Using +graph+, you can split the result hashes into subhashes, one per join:
406
426
 
407
427
  Sequel expects column names to be specified using symbols. In addition, returned hashes always use symbols as their keys. This allows you to freely mix literal values and column references in many cases. For example, the two following lines produce equivalent SQL:
408
428
 
409
- items.filter(:x => 1)
429
+ items.where(:x => 1)
410
430
  # SELECT * FROM items WHERE (x = 1)
411
- items.filter(1 => :x)
431
+ items.where(1 => :x)
412
432
  # SELECT * FROM items WHERE (1 = x)"
413
433
 
414
434
  Ruby strings are generally treated as SQL strings:
415
435
 
416
- items.filter(:x => 'x')
436
+ items.where(:x => 'x')
417
437
  # SELECT * FROM items WHERE (x = 'x')
418
438
 
419
439
  === Qualifying column names
@@ -423,9 +443,9 @@ Column references can be qualified by using the double underscore special notati
423
443
  items.literal(:items__price)
424
444
  # items.price
425
445
 
426
- Another way to qualify columns is to use the +qualify+ method:
446
+ Another way to qualify columns is to use the <tt>Sequel.qualify</tt> method:
427
447
 
428
- items.literal(:price.qualify(:items))
448
+ items.literal(Sequel.qualify(:items, :price))
429
449
  # items.price
430
450
 
431
451
  === Column aliases
@@ -437,9 +457,9 @@ You can also alias columns by using the triple undersecore special notation <tt>
437
457
  items.literal(:items__price___p)
438
458
  # items.price AS p
439
459
 
440
- Another way to alias columns is to use the +as+ method:
460
+ Another way to alias columns is to use the <tt>Sequel.as</tt> method:
441
461
 
442
- items.literal(:price.as(:p))
462
+ items.literal(Sequel.as(:price, :p))
443
463
  # price AS p
444
464
 
445
465
  == Sequel Models
@@ -465,7 +485,7 @@ You can explicitly set the table name or even the dataset used:
465
485
 
466
486
  If you call +set_dataset+ with a symbol, it assumes you are referring to the table with the same name. You can also call it with a dataset, which will set the defaults for all retrievals for that model:
467
487
 
468
- Post.set_dataset DB[:my_posts].filter(:category => 'ruby')
488
+ Post.set_dataset DB[:my_posts].where(:category => 'ruby')
469
489
  Post.set_dataset DB[:my_posts].select(:id, :name).order(:date)
470
490
 
471
491
  === Model instances
@@ -500,12 +520,12 @@ A single model instance can also be fetched by specifying a condition:
500
520
 
501
521
  A model class lets you iterate over subsets of records by proxying many methods to the underlying dataset. This means that you can use most of the +Dataset+ API to create customized queries that return model instances, e.g.:
502
522
 
503
- Post.filter(:category => 'ruby').each{|post| p post}
523
+ Post.where(:category => 'ruby').each{|post| p post}
504
524
 
505
525
  You can also manipulate the records in the dataset:
506
526
 
507
- Post.filter{num_comments < 7}.delete
508
- Post.filter(:title.like(/ruby/)).update(:category => 'ruby')
527
+ Post.where{num_comments < 7}.delete
528
+ Post.where(Sequel.like(:title, /ruby/)).update(:category => 'ruby')
509
529
 
510
530
  === Accessing record values
511
531
 
@@ -523,10 +543,12 @@ If the record's attributes names are not valid columns in the model's dataset (m
523
543
  post[:id] #=> 123
524
544
  post[:title] #=> 'hello world'
525
545
 
526
- You can also modify record values using attribute setters or the +set+ method:
546
+ You can also modify record values using attribute setters, the <tt>[]=</tt> method, or the +set+ method:
527
547
 
528
548
  post.title = 'hey there'
529
549
  # or
550
+ post[:title] = 'hey there'
551
+ # or
530
552
  post.set(:title=>'hey there')
531
553
 
532
554
  That will just change the value for the object, it will not update the row in the database. To update the database row, call the +save+ method:
@@ -575,7 +597,7 @@ You can execute custom code when creating, updating, or deleting records by defi
575
597
 
576
598
  Note the use of +super+ if you define your own hook methods. Almost all <tt>Sequel::Model</tt> class and instance methods (not just hook methods) can be overridden safely, but you have to make sure to call +super+ when doing so, otherwise you risk breaking things.
577
599
 
578
- For the example above, you should probably use a database trigger if you can. Hooks can be used for data integrity, but they will only enforce that integrity when you are modifying the database through model instances. If you plan on allowing any other access to the database, it's best to use database triggers and constraints for data integrity.
600
+ For the example above, you should probably use a database trigger if you can. Hooks can be used for data integrity, but they will only enforce that integrity when you are modifying the database through model instances, and even then they are often subject to race conditions. It's best to use database triggers and constraints to enforce data integrity.
579
601
 
580
602
  === Deleting records
581
603
 
@@ -586,8 +608,8 @@ You can delete individual records by calling +delete+ or +destroy+. The only dif
586
608
 
587
609
  Records can also be deleted en-masse by calling <tt>Model.delete</tt> and <tt>Model.destroy</tt>. As stated above, you can specify filters for the deleted records:
588
610
 
589
- Post.filter(:category => 32).delete # => bypasses hooks
590
- Post.filter(:category => 32).destroy # => runs hooks
611
+ Post.where(:category => 32).delete # => bypasses hooks
612
+ Post.where(:category => 32).destroy # => runs hooks
591
613
 
592
614
  Please note that if <tt>Model.destroy</tt> is called, each record is deleted
593
615
  separately, but <tt>Model.delete</tt> deletes all matching records with a single
@@ -632,7 +654,7 @@ All associations add a dataset method that can be used to further filter or reor
632
654
  post.comments_dataset.destroy
633
655
 
634
656
  # Return all tags related to this post with no subscribers, ordered by the tag's name
635
- post.tags_dataset.filter(:subscribers=>0).order(:name).all
657
+ post.tags_dataset.where(:subscribers=>0).order(:name).all
636
658
 
637
659
  === Eager Loading
638
660
 
@@ -663,7 +685,7 @@ Associations can be eagerly loaded via +eager+ and the <tt>:eager</tt> associati
663
685
  Post.eager(:person).all
664
686
 
665
687
  # eager is a dataset method, so it works with filters/orders/limits/etc.
666
- Post.filter{topic > 'M'}.order(:date).limit(5).eager(:person).all
688
+ Post.where{topic > 'M'}.order(:date).limit(5).eager(:person).all
667
689
 
668
690
  person = Person.first
669
691
  # Eager loading via :eager (will eagerly load the tags for this person's posts)
@@ -690,16 +712,16 @@ In addition to using +eager+, you can also use +eager_graph+, which will use a s
690
712
  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:
691
713
 
692
714
  # Eagerly load only replies containing 'foo'
693
- Post.eager(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all
715
+ Post.eager(:replies=>proc{|ds| ds.where(Sequel.like(text, '%foo%'))}).all
694
716
 
695
717
  This also works when using +eager_graph+, in which case the proc is called with dataset to graph into the current dataset:
696
718
 
697
- Post.eager_graph(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all
719
+ Post.eager_graph(:replies=>proc{|ds| ds.where(Sequel.like(text, '%foo%'))}).all
698
720
 
699
721
  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:
700
722
 
701
723
  # Eagerly load only replies containing 'foo', and the person and tags for those replies
702
- Post.eager(:replies=>{proc{|ds| ds.filter(text.like('%foo%'))}=>[:person, :tags]}).all
724
+ Post.eager(:replies=>{proc{|ds| ds.where(Sequel.like(text, '%foo%'))}=>[:person, :tags]}).all
703
725
 
704
726
  === Extending the underlying dataset
705
727
 
@@ -707,7 +729,7 @@ The obvious way to add table-wide logic is to define class methods to the model
707
729
 
708
730
  class Post < Sequel::Model
709
731
  def self.posts_with_few_comments
710
- filter{num_comments < 30}
732
+ where{num_comments < 30}
711
733
  end
712
734
 
713
735
  def self.clean_posts_with_few_comments
@@ -715,27 +737,29 @@ The obvious way to add table-wide logic is to define class methods to the model
715
737
  end
716
738
  end
717
739
 
718
- You can also implement table-wide logic by defining methods on the dataset using +def_dataset_method+:
740
+ You can also implement table-wide logic by defining methods on the dataset using +dataset_module+:
719
741
 
720
742
  class Post < Sequel::Model
721
- def_dataset_method(:posts_with_few_comments) do
722
- filter{num_comments < 30}
723
- end
724
-
725
- def_dataset_method(:clean_posts_with_few_comments) do
726
- posts_with_few_comments.delete
743
+ dataset_module do
744
+ def posts_with_few_comments
745
+ where{num_comments < 30}
746
+ end
747
+
748
+ def clean_posts_with_few_comments
749
+ posts_with_few_comments.delete
750
+ end
727
751
  end
728
752
  end
729
753
 
730
754
  This is the recommended way of implementing table-wide operations, and allows you to have access to your model API from filtered datasets as well:
731
755
 
732
- Post.filter(:category => 'ruby').clean_posts_with_few_comments
756
+ Post.where(:category => 'ruby').clean_posts_with_few_comments
733
757
 
734
758
  Sequel models also provide a +subset+ class method that creates a dataset method with a simple filter:
735
759
 
736
760
  class Post < Sequel::Model
737
761
  subset(:posts_with_few_comments){num_comments < 30}
738
- subset :invisible, ~:visible
762
+ subset :invisible, Sequel.~(:visible)
739
763
  end
740
764
 
741
765
  === Model Validations
data/Rakefile CHANGED
@@ -29,7 +29,7 @@ end
29
29
 
30
30
  desc "Upload sequel gem to gemcutter"
31
31
  task :release=>[:package] do
32
- sh %{gem push ./#{NAME}-#{VERS.call}.gem}
32
+ sh %{gem push ./#{NAME}-#{VERS.call}.gem}
33
33
  end
34
34
 
35
35
  ### RDoc
@@ -132,14 +132,15 @@ begin
132
132
  spec.call("spec_core", Dir["spec/core/*_spec.rb"], "Run core specs")
133
133
  spec.call("spec_model", Dir["spec/model/*_spec.rb"], "Run model specs")
134
134
  spec.call("_spec_model_no_assoc", Dir["spec/model/*_spec.rb"].delete_if{|f| f =~ /association|eager_loading/}, '')
135
+ spec_with_cov.call("spec_core_ext", ["spec/core_extensions_spec.rb"], "Run core extensions specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|adapters|connection_pool|database|dataset|model)"')}
135
136
  spec_with_cov.call("spec_plugin", Dir["spec/extensions/*_spec.rb"], "Run extension/plugin specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|adapters|connection_pool|database|dataset|model)"')}
136
137
  spec_with_cov.call("spec_integration", Dir["spec/integration/*_test.rb"], "Run integration tests")
137
-
138
+
138
139
  %w'postgres sqlite mysql informix oracle firebird mssql db2'.each do |adapter|
139
140
  spec_with_cov.call("spec_#{adapter}", ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"], "Run #{adapter} specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|connection_pool|database|dataset|model|extensions|plugins)"')}
140
141
  end
141
-
142
- task :spec_travis=>[:spec, :spec_plugin, :spec_sqlite] do
142
+
143
+ task :spec_travis=>[:spec, :spec_plugin, :spec_core_ext, :spec_sqlite] do
143
144
  if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
144
145
  ENV['SEQUEL_PG_SPEC_DB'] = "jdbc:postgresql://localhost/sequel_test?user=postgres"
145
146
  ENV['SEQUEL_MY_SPEC_DB'] = "jdbc:mysql://localhost/sequel_test?user=root"
@@ -173,7 +174,7 @@ end
173
174
  desc "Report code statistics (KLOCs, etc) from the application"
174
175
  task :stats do
175
176
  STATS_DIRECTORIES = [%w(Code lib/), %w(Spec spec)].map{|name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir)}
176
- require "extra/stats"
177
+ require "./extra/stats"
177
178
  verbose = true
178
179
  CodeStatistics.new(*STATS_DIRECTORIES).to_s
179
180
  end