Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 671 lines (420 sloc) 21.706 kb
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
1 == Sequel: The Database Toolkit for Ruby
2
3 Sequel is a lightweight database access toolkit for Ruby.
4
5 * Sequel provides thread safety, connection pooling and a concise DSL
6 for constructing database queries and table schemas.
7 * Sequel also includes a lightweight but comprehensive ORM layer for
8 mapping records to Ruby objects and handling associated records.
645073f @jeremyevans Add DataObjects adapter, with PostgreSQL, MySQL, and SQLite subadapters
authored
9 * Sequel supports advanced database features such as prepared
10 statements, bound variables, stored procedures, master/slave
11 configurations, and database sharding.
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
12 * Sequel makes it easy to deal with multiple records without having
13 to break your teeth on SQL.
645073f @jeremyevans Add DataObjects adapter, with PostgreSQL, MySQL, and SQLite subadapters
authored
14 * Sequel currently has adapters for ADO, DataObjects, DB2, DBI,
ae31911 @jeremyevans Add Firebird adapter to README
authored
15 Firebird, Informix, JDBC, MySQL, ODBC, OpenBase, Oracle, PostgreSQL
16 and SQLite3.
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
17
18 == Resources
19
189fe30 @jeremyevans Use boof's website design for sequel.rubyforge.org
authored
20 * {Website}[http://sequel.rubyforge.org]
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
21 * {Source code}[http://github.com/jeremyevans/sequel]
22 * {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
23 * {Google group}[http://groups.google.com/group/sequel-talk]
189fe30 @jeremyevans Use boof's website design for sequel.rubyforge.org
authored
24 * {RDoc}[http://sequel.rubyforge.org/rdoc]
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
25
26 To check out the source code:
27
28 git clone git://github.com/jeremyevans/sequel.git
29
30 === Contact
31
32 If you have any comments or suggestions please post to the Google group.
33
34 == Installation
35
36 sudo gem install sequel
37
38 == A Short Example
39
40 require 'rubygems'
41 require 'sequel'
42
43 DB = Sequel.sqlite # memory database
44
45 DB.create_table :items do # Create a new table
7c0583f @jackdempsey add in demonstration of primary_key method
jackdempsey authored
46 primary_key :id
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
47 column :name, :text
48 column :price, :float
49 end
50
51 items = DB[:items] # Create a dataset
52
53 # Populate the table
54 items << {:name => 'abc', :price => rand * 100}
55 items << {:name => 'def', :price => rand * 100}
56 items << {:name => 'ghi', :price => rand * 100}
57
58 # Print out the number of records
59 puts "Item count: #{items.count}"
60
61 # Print out the records in descending order by price
62 items.reverse_order(:price).print
63
64 # Print out the average price
65 puts "The average price is: #{items.avg(:price)}"
66
67 == The Sequel Console
68
69 Sequel includes an IRB console for quick'n'dirty access to databases. You can use it like this:
70
71 sequel sqlite://test.db # test.db in current directory
72
73 You get an IRB session with the database object stored in DB.
74
75 == An Introduction
76
77 Sequel is designed to take the hassle away from connecting to databases and manipulating them. Sequel deals with all the boring stuff like maintaining connections, formatting SQL correctly and fetching records so you can concentrate on your application.
78
ce1c70f @jeremyevans Documentation cleanup
authored
79 Sequel uses the concept of datasets to retrieve data. A Dataset object encapsulates an SQL query and supports chainability, letting you fetch data using a convenient Ruby DSL that is both concise and flexible.
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
80
81 For example, the following one-liner returns the average GDP for the five biggest countries in the middle east region:
82
83 DB[:countries].filter(:region => 'Middle East').reverse_order(:area).limit(5).avg(:GDP)
84
85 Which is equivalent to:
86
87 SELECT avg(GDP) FROM countries WHERE region = 'Middle East' ORDER BY area DESC LIMIT 5
88
89 Since datasets retrieve records only when needed, they can be stored and later reused. Records are fetched as hashes (they can also be fetched as custom model objects), and are accessed using an Enumerable interface:
90
91 middle_east = DB[:countries].filter(:region => 'Middle East')
ce1c70f @jeremyevans Documentation cleanup
authored
92 middle_east.order(:name).each{|r| puts r[:name]}
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
93
94 Sequel also offers convenience methods for extracting data from Datasets, such as an extended map method:
95
96 middle_east.map(:name) #=> ['Egypt', 'Greece', 'Israel', ...]
97
98 Or getting results as a transposed hash, with one column as key and another as value:
99
100 middle_east.to_hash(:name, :area) #=> {'Israel' => 20000, 'Greece' => 120000, ...}
101
102 == Getting Started
103
104 === Connecting to a database
105
106 To connect to a database you simply provide Sequel with a URL:
107
108 require 'sequel'
109 DB = Sequel.connect('sqlite://blog.db')
110
111 The connection URL can also include such stuff as the user name and password:
112
113 DB = Sequel.connect('postgres://cico:12345@localhost:5432/mydb')
114
115 You can also specify optional parameters, such as the connection pool size, or loggers for logging SQL queries:
116
117 DB = Sequel.connect("postgres://postgres:postgres@localhost/my_db",
118 :max_connections => 10, :loggers => [Logger.new('log/db.log']))
119
120 You can specify a block to connect, which will disconnect from the database after it completes:
121
122 Sequel.connect('postgres://cico:12345@localhost:5432/mydb'){|db| db[:posts].delete}
123
124 === Arbitrary SQL queries
125
ce1c70f @jeremyevans Documentation cleanup
authored
126 DB.execute_ddl("create table t (a text, b text)")
127 DB.execute_insert("insert into t values ('a', 'b')")
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
128
129 Or more succinctly:
130
131 DB << "create table t (a text, b text)"
132 DB << "insert into t values ('a', 'b')"
133
134 You can also create datasets based on raw SQL:
135
136 dataset = DB['select * from items']
137 dataset.count # will return the number of records in the result set
138 dataset.map(:id) # will return an array containing all values of the id column in the result set
139
140 You can also fetch records with raw SQL through the dataset:
141
142 DB['select * from items'].each do |row|
143 p row
144 end
145
ce1c70f @jeremyevans Documentation cleanup
authored
146 You can use placeholders in your SQL string as well:
147
148 DB['select * from items where name = ?', name].each do |row|
149 p row
150 end
151
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
152 === Getting Dataset Instances
153
154 Dataset is the primary means through which records are retrieved and manipulated. You can create an blank dataset by using the dataset method:
155
156 dataset = DB.dataset
157
158 Or by using the from methods:
159
160 posts = DB.from(:posts)
161
162 The recommended way is the equivalent shorthand:
163
164 posts = DB[:posts]
165
166 Datasets will only fetch records when you explicitly ask for them. Datasets can be manipulated to filter through records, change record order, join tables, etc..
167
168 === Retrieving Records
169
170 You can retrieve records by using the all method:
171
172 posts.all
173
174 The all method returns an array of hashes, where each hash corresponds to a record.
175
176 You can also iterate through records one at a time:
177
178 posts.each{|row| p row}
179
180 Or perform more advanced stuff:
181
182 posts.map(:id)
183 posts.inject({}){|h, r| h[r[:id]] = r[:name]}
184
185 You can also retrieve the first record in a dataset:
186
187 posts.first
188
189 Or retrieve a single record with a specific value:
190
191 posts[:id => 1]
192
193 If the dataset is ordered, you can also ask for the last record:
194
195 posts.order(:stamp).last
196
197 === Filtering Records
198
199 The simplest way to filter records is to provide a hash of values to match:
200
201 my_posts = posts.filter(:category => 'ruby', :author => 'david')
202
203 You can also specify ranges:
204
205 my_posts = posts.filter(:stamp => (Date.today - 14)..(Date.today - 7))
206
ce1c70f @jeremyevans Documentation cleanup
authored
207 Or arrays of values:
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
208
209 my_posts = posts.filter(:category => ['ruby', 'postgres', 'linux'])
210
211 Sequel also accepts expressions:
212
ce1c70f @jeremyevans Documentation cleanup
authored
213 my_posts = posts.filter{|o| o.stamp > Date.today << 1}
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
214
215 Some adapters (like postgresql) will also let you specify Regexps:
216
217 my_posts = posts.filter(:category => /ruby/i)
218
219 You can also use an inverse filter:
220
221 my_posts = posts.exclude(:category => /ruby/i)
222 my_posts = posts.filter(:category => /ruby/i).invert # same as above
223
224 You can also specify a custom WHERE clause using a string:
225
226 posts.filter('stamp IS NOT NULL')
227
228 You can use parameters in your string, as well (ActiveRecord style):
229
230 posts.filter('(stamp < ?) AND (author != ?)', Date.today - 3, author_name)
ce1c70f @jeremyevans Documentation cleanup
authored
231 posts.filter{|o| (o.stamp < Date.today + 3) & ~{:author => author_name}} # same as above
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
232
233 Datasets can also be used as subqueries:
234
235 DB[:items].filter('price > ?', DB[:items].select('AVG(price) + 100'))
236
237 After filtering you can retrieve the matching records by using any of the retrieval methods:
238
239 my_posts.each{|row| p row}
240
241 See the doc/dataset_filtering.rdoc file for more details.
242
243 === Summarizing Records
244
245 Counting records is easy:
246 posts.filter(:category => /ruby/i).count
247
248 And you can also query maximum/minimum values:
249 max_value = DB[:history].max(:value)
250
251 Or calculate a sum:
252 total = DB[:items].sum(:price)
253
254 === Ordering Records
255
256 Ordering datasets is simple:
257
258 posts.order(:stamp) # ORDER BY stamp
259 posts.order(:stamp, :name) # ORDER BY stamp, name
ce1c70f @jeremyevans Documentation cleanup
authored
260
261 Chaining order doesn't work the same as filter:
262
263 posts.order(:stamp).order(:name) # ORDER BY name
264
265 The order_more method chains this way, though:
266
267 posts.order(:stamp).order_more(:name) # ORDER BY stamp, name
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
268
269 You can also specify descending order
270
271 posts.order(:stamp.desc) # ORDER BY stamp DESC
272
ce1c70f @jeremyevans Documentation cleanup
authored
273 === Selecting Columns
274
275 Selecting specific columns to be returned is also simple:
276
277 posts.select(:stamp) # SELECT stamp FROM posts
278 posts.select(:stamp, :name) # SELECT stamp, name FROM posts
279
280 Chaining select works like order, not filter:
281
282 posts.select(:stamp).select(:name) # SELECT name FROM posts
283
284 As you might expect, there is an order_more equivalent for select:
285
286 posts.select(:stamp).select_more(:name) # SELECT stamp, name FROM posts
287
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
288 === Deleting Records
289
290 Deleting records from the table is done with delete:
291
292 posts.filter('stamp < ?', Date.today - 3).delete
293
294 === Inserting Records
295
296 Inserting records into the table is done with insert:
297
298 posts.insert(:category => 'ruby', :author => 'david')
299 posts << {:category => 'ruby', :author => 'david'} # same as above
300
301 === Updating Records
302
303 Updating records in the table is done with update:
304
305 posts.filter('stamp < ?', Date.today - 7).update(:state => 'archived')
306
307 === Joining Tables
308
309 Joining is very useful in a variety of scenarios, for example many-to-many relationships. With Sequel it's really easy:
310
311 order_items = DB[:items].join(:order_items, :item_id => :id).
312 filter(:order_items__order_id => 1234)
313
314 This is equivalent to the SQL:
315
43f43ba @jeremyevans Fix incorrect SQL in the README
authored
316 SELECT * FROM items INNER JOIN order_items
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
317 ON order_items.item_id = items.id
318 WHERE order_items.order_id = 1234
319
320 You can then do anything you like with the dataset:
321
322 order_total = order_items.sum(:price)
323
324 Which is equivalent to the SQL:
325
43f43ba @jeremyevans Fix incorrect SQL in the README
authored
326 SELECT sum(price) FROM items INNER JOIN order_items
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
327 ON order_items.item_id = items.id
328 WHERE order_items.order_id = 1234
329
330 === Graphing Datasets
331
ce1c70f @jeremyevans Documentation cleanup
authored
332 When retrieving records from joined datasets, you get the results in a single hash, which is subject to clobbering if you have columns with the same name in multiple tables:
778a032 @jeremyevans Combine sequel and sequel_core into one gem
authored
333
334 DB[:items].join(:order_items, :item_id => :id).first
335 => {:id=>(could be items.id or order_items.id), :item_id=>order_items.order_id}
336
337 Using graph, you can split the result hashes into subhashes, one per join:
338
339 DB[:items].graph(:order_items, :item_id => :id).first
340 => {:items=>{:id=>items.id}, :order_items=>{:id=>order_items.id, :item_id=>order_items.item_id}}
341
ce1c70f @jeremyevans Documentation cleanup
authored
342 == An aside: column references in Sequel
343
344 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. For example, the two following lines produce equivalent SQL:
345
346 items.filter(:x => 1) #=> "SELECT * FROM items WHERE (x = 1)"
347 items.filter(1 => :x) #=> "SELECT * FROM items WHERE (1 = x)"
348
349 === Qualifying column names
350
351 Column references can be qualified by using the double underscore special notation :table__column:
352
353 items.literal(:items__price) #=> "items.price"
354
355 === Column aliases
356
357 You can also alias columns by using the triple undersecore special notation :column___alias or :table__column___alias:
358
359 items.literal(:price___p) #=> "price AS p"
360 items.literal(:items__price___p) #=> "items.price AS p"
361
362 Another way to alias columns is to use the #AS method:
363
364 items.literal(:price.as(:p)) #=> "price AS p"
365
a0181eb @jeremyevans Various doc fixes
authored
366 == Sequel Models
3ece811 @ciconia In preparation for 1.0.
ciconia authored
367
a0181eb @jeremyevans Various doc fixes
authored
368 Models in Sequel are based on the Active Record pattern described by Martin Fowler (http://www.martinfowler.com/eaaCatalog/activeRecord.html). A model class corresponds to a table or a dataset, and an instance of that class wraps a single record in the model's underlying dataset.
3ece811 @ciconia In preparation for 1.0.
ciconia authored
369
a0181eb @jeremyevans Various doc fixes
authored
370 Model classes are defined as regular Ruby classes:
371
372 DB = Sequel.connect('sqlite:/blog.db')
373 class Post < Sequel::Model
374 end
375
82d2593 @jeremyevans Some additions to sequel/README from the Google Code Wiki
authored
376 Just like in DataMapper or ActiveRecord, Sequel model classes assume that the table name is a plural of the class name:
a0181eb @jeremyevans Various doc fixes
authored
377
82d2593 @jeremyevans Some additions to sequel/README from the Google Code Wiki
authored
378 Post.table_name #=> :posts
379
380 You can, however, explicitly set the table name or even the dataset used:
381
382 class Post < Sequel::Model(:my_posts)
a0181eb @jeremyevans Various doc fixes
authored
383 end
82d2593 @jeremyevans Some additions to sequel/README from the Google Code Wiki
authored
384 # or:
385 Post.set_dataset :my_posts
386 # or:
387 Post.set_dataset DB[:my_posts].where(:category => 'ruby')
388
a0181eb @jeremyevans Various doc fixes
authored
389 === Model instances
3ece811 @ciconia In preparation for 1.0.
ciconia authored
390
a0181eb @jeremyevans Various doc fixes
authored
391 Model instance are identified by a primary key. By default, Sequel assumes the primary key column to be :id. The Model#[] method can be used to fetch records by their primary key:
3ece811 @ciconia In preparation for 1.0.
ciconia authored
392
a0181eb @jeremyevans Various doc fixes
authored
393 post = Post[123]
3ece811 @ciconia In preparation for 1.0.
ciconia authored
394
a0181eb @jeremyevans Various doc fixes
authored
395 The Model#pk method is used to retrieve the record's primary key value:
3ece811 @ciconia In preparation for 1.0.
ciconia authored
396
a0181eb @jeremyevans Various doc fixes
authored
397 post.pk #=> 123
3ece811 @ciconia In preparation for 1.0.
ciconia authored
398
a0181eb @jeremyevans Various doc fixes
authored
399 Sequel models allow you to use any column as a primary key, and even composite keys made from multiple columns:
3ece811 @ciconia In preparation for 1.0.
ciconia authored
400
a0181eb @jeremyevans Various doc fixes
authored
401 class Post < Sequel::Model
402 set_primary_key [:category, :title]
403 end
3ece811 @ciconia In preparation for 1.0.
ciconia authored
404
a0181eb @jeremyevans Various doc fixes
authored
405 post = Post['ruby', 'hello world']
406 post.pk #=> ['ruby', 'hello world']
3ece811 @ciconia In preparation for 1.0.
ciconia authored
407
a0181eb @jeremyevans Various doc fixes
authored
408 You can also define a model class that does not have a primary key, but then you lose the ability to update records.
3ece811 @ciconia In preparation for 1.0.
ciconia authored
409
a0181eb @jeremyevans Various doc fixes
authored
410 A model instance can also be fetched by specifying a condition:
3ece811 @ciconia In preparation for 1.0.
ciconia authored
411
a0181eb @jeremyevans Various doc fixes
authored
412 post = Post[:title => 'hello world']
ce1c70f @jeremyevans Documentation cleanup
authored
413 post = Post.find{|o| o.num_comments < 10}
3ece811 @ciconia In preparation for 1.0.
ciconia authored
414
a0181eb @jeremyevans Various doc fixes
authored
415 === Iterating over records
3ece811 @ciconia In preparation for 1.0.
ciconia authored
416
ce1c70f @jeremyevans Documentation cleanup
authored
417 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.:
3ece811 @ciconia In preparation for 1.0.
ciconia authored
418
a0181eb @jeremyevans Various doc fixes
authored
419 Post.filter(:category => 'ruby').each{|post| p post}
3ece811 @ciconia In preparation for 1.0.
ciconia authored
420
a0181eb @jeremyevans Various doc fixes
authored
421 You can also manipulate the records in the dataset:
3ece811 @ciconia In preparation for 1.0.
ciconia authored
422
ce1c70f @jeremyevans Documentation cleanup
authored
423 Post.filter{|o| o.num_comments < 7}.delete
17c0143 @jeremyevans Small documentation updates to the READMEs
authored
424 Post.filter(:title.like(/ruby/)).update(:category => 'ruby')
3ece811 @ciconia In preparation for 1.0.
ciconia authored
425
a0181eb @jeremyevans Various doc fixes
authored
426 === Accessing record values
3ece811 @ciconia In preparation for 1.0.
ciconia authored
427
a0181eb @jeremyevans Various doc fixes
authored
428 A model instances stores its values as a hash:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
429
a0181eb @jeremyevans Various doc fixes
authored
430 post.values #=> {:id => 123, :category => 'ruby', :title => 'hello world'}
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
431
a0181eb @jeremyevans Various doc fixes
authored
432 You can read the record values as object attributes (assuming the attribute names are valid columns in the model's dataset):
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
433
a0181eb @jeremyevans Various doc fixes
authored
434 post.id #=> 123
435 post.title #=> 'hello world'
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
436
a0181eb @jeremyevans Various doc fixes
authored
437 You can also change record values:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
438
a0181eb @jeremyevans Various doc fixes
authored
439 post.title = 'hey there'
440 post.save
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
441
ce1c70f @jeremyevans Documentation cleanup
authored
442 Another way to change values by using the #update method:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
443
ce1c70f @jeremyevans Documentation cleanup
authored
444 post.update(:title => 'hey there')
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
445
a0181eb @jeremyevans Various doc fixes
authored
446 === Creating new records
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
447
a0181eb @jeremyevans Various doc fixes
authored
448 New records can be created by calling Model.create:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
449
a0181eb @jeremyevans Various doc fixes
authored
450 post = Post.create(:title => 'hello world')
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
451
a0181eb @jeremyevans Various doc fixes
authored
452 Another way is to construct a new instance and save it:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
453
a0181eb @jeremyevans Various doc fixes
authored
454 post = Post.new
455 post.title = 'hello world'
456 post.save
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
457
a0181eb @jeremyevans Various doc fixes
authored
458 You can also supply a block to Model.new and Model.create:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
459
ce1c70f @jeremyevans Documentation cleanup
authored
460 post = Post.create{|p| p.title = 'hello world'}
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
461
a0181eb @jeremyevans Various doc fixes
authored
462 post = Post.new do |p|
463 p.title = 'hello world'
464 p.save
465 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
466
a0181eb @jeremyevans Various doc fixes
authored
467 === Hooks
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
468
17c0143 @jeremyevans Small documentation updates to the READMEs
authored
469 You can execute custom code when creating, updating, or deleting records by using hooks. The before_create and after_create hooks wrap record creation. The before_update and after_update wrap record updating. The before_save and after_save wrap record creation and updating. The before_destroy and after_destroy wrap destruction. The before_validation and after_validation hooks wrap validation.
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
470
a0181eb @jeremyevans Various doc fixes
authored
471 Hooks are defined by supplying a block:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
472
a0181eb @jeremyevans Various doc fixes
authored
473 class Post < Sequel::Model
474 after_create do
1d2404f @jeremyevans Fix confusing code in after_create example in sequel/README
authored
475 author.increase_post_count
a0181eb @jeremyevans Various doc fixes
authored
476 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
477
a0181eb @jeremyevans Various doc fixes
authored
478 after_destroy do
1d2404f @jeremyevans Fix confusing code in after_create example in sequel/README
authored
479 author.decrease_post_count
a0181eb @jeremyevans Various doc fixes
authored
480 end
481 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
482
a0181eb @jeremyevans Various doc fixes
authored
483 === Deleting records
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
484
a0181eb @jeremyevans Various doc fixes
authored
485 You can delete individual records by calling #delete or #destroy. The only difference between the two methods is that #destroy invokes before_destroy and after_destroy hooks, while #delete does not:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
486
a0181eb @jeremyevans Various doc fixes
authored
487 post.delete #=> bypasses hooks
488 post.destroy #=> runs hooks
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
489
a0181eb @jeremyevans Various doc fixes
authored
490 Records can also be deleted en-masse by invoking Model.delete and Model.destroy. As stated above, you can specify filters for the deleted records:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
491
a0181eb @jeremyevans Various doc fixes
authored
492 Post.filter(:category => 32).delete #=> bypasses hooks
493 Post.filter(:category => 32).destroy #=> runs hooks
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
494
a0181eb @jeremyevans Various doc fixes
authored
495 Please note that if Model.destroy is called, each record is deleted
496 separately, but Model.delete deletes all relevant records with a single
497 SQL statement.
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
498
a0181eb @jeremyevans Various doc fixes
authored
499 === Associations
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
500
a0181eb @jeremyevans Various doc fixes
authored
501 Associations are used in order to specify relationships between model classes that reflect relations between tables in the database using foreign keys.
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
502
a0181eb @jeremyevans Various doc fixes
authored
503 class Post < Sequel::Model
504 many_to_one :author
505 one_to_many :comments
506 many_to_many :tags
507 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
508
17c0143 @jeremyevans Small documentation updates to the READMEs
authored
509 many_to_one creates a getter and setter for each model object:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
510
a0181eb @jeremyevans Various doc fixes
authored
511 class Post < Sequel::Model
512 many_to_one :author
513 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
514
a0181eb @jeremyevans Various doc fixes
authored
515 post = Post.create(:name => 'hi!')
516 post.author = Author[:name => 'Sharon']
517 post.author
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
518
17c0143 @jeremyevans Small documentation updates to the READMEs
authored
519 one_to_many and many_to_many create a getter method, a method for adding an object to the association, a method for removing an object from the association, and a method for removing all associated objected from the association:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
520
a0181eb @jeremyevans Various doc fixes
authored
521 class Post < Sequel::Model
522 one_to_many :comments
523 many_to_many :tags
524 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
525
a0181eb @jeremyevans Various doc fixes
authored
526 post = Post.create(:name => 'hi!')
527 post.comments
528 comment = Comment.create(:text=>'hi')
529 post.add_comment(comment)
530 post.remove_comment(comment)
17c0143 @jeremyevans Small documentation updates to the READMEs
authored
531 post.remove_all_comments
a0181eb @jeremyevans Various doc fixes
authored
532 tag = Tag.create(:tag=>'interesting')
533 post.add_tag(tag)
534 post.remove_tag(tag)
17c0143 @jeremyevans Small documentation updates to the READMEs
authored
535 post.remove_all_tags
a0181eb @jeremyevans Various doc fixes
authored
536
537 === Eager Loading
538
539 Associations can be eagerly loaded via .eager and the :eager association option. Eager loading is used when loading a group of objects. It loads all associated objects for all of the current objects in one query, instead of using a separate query to get the associated objects for each current object. Eager loading requires that you retrieve all model objects at once via .all (instead of individually by .each). Eager loading can be cascaded, loading association's associated objects.
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
540
a0181eb @jeremyevans Various doc fixes
authored
541 class Person < Sequel::Model
542 one_to_many :posts, :eager=>[:tags]
543 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
544
a0181eb @jeremyevans Various doc fixes
authored
545 class Post < Sequel::Model
546 many_to_one :person
547 one_to_many :replies
548 many_to_many :tags
549 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
550
a0181eb @jeremyevans Various doc fixes
authored
551 class Tag < Sequel::Model
552 many_to_many :posts
553 many_to_many :replies
554 end
555
556 class Reply < Sequel::Model
557 many_to_one :person
558 many_to_one :post
559 many_to_many :tags
560 end
561
562 # Eager loading via .eager
563 Post.eager(:person).all
2b572b9 @jeremyevans In the RDoc, give an example of .eager being used with filters, and d…
authored
564
565 # eager is a dataset method, so it works with filters/orders/limits/etc.
ce1c70f @jeremyevans Documentation cleanup
authored
566 Post.filter{|o| o.topic > 'M'}.order(:date).limit(5).eager(:person).all
a0181eb @jeremyevans Various doc fixes
authored
567
568 person = Person.first
569 # Eager loading via :eager (will eagerly load the tags for this person's posts)
570 person.posts
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
571
a0181eb @jeremyevans Various doc fixes
authored
572 # These are equivalent
573 Post.eager(:person, :tags).all
574 Post.eager(:person).eager(:tags).all
575
576 # Cascading via .eager
577 Tag.eager(:posts=>:replies).all
578
579 # Will also grab all associated posts' tags (because of :eager)
580 Reply.eager(:person=>:posts).all
581
582 # No depth limit (other than memory/stack), and will also grab posts' tags
583 # Loads all people, their posts, their posts' tags, replies to those posts,
584 # the person for each reply, the tag for each reply, and all posts and
585 # replies that have that tag. Uses a total of 8 queries.
586 Person.eager(:posts=>{:replies=>[:person, {:tags=>{:posts, :replies}}]}).all
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
587
a0181eb @jeremyevans Various doc fixes
authored
588 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 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.
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
589
a0181eb @jeremyevans Various doc fixes
authored
590 === Caching model instances with memcached
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
591
a0181eb @jeremyevans Various doc fixes
authored
592 Sequel models can be cached using memcached based on their primary keys. The use of memcached can significantly reduce database load by keeping model instances in memory. The set_cache method is used to specify caching:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
593
a0181eb @jeremyevans Various doc fixes
authored
594 require 'memcache'
595 CACHE = MemCache.new 'localhost:11211', :namespace => 'blog'
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
596
a0181eb @jeremyevans Various doc fixes
authored
597 class Author < Sequel::Model
598 set_cache CACHE, :ttl => 3600
599 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
600
a0181eb @jeremyevans Various doc fixes
authored
601 Author[333] # database hit
602 Author[333] # cache hit
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
603
a0181eb @jeremyevans Various doc fixes
authored
604 === Extending the underlying dataset
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
605
a0181eb @jeremyevans Various doc fixes
authored
606 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:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
607
a0181eb @jeremyevans Various doc fixes
authored
608 class Post < Sequel::Model
609 def self.posts_with_few_comments
ce1c70f @jeremyevans Documentation cleanup
authored
610 filter{|o| o.num_comments < 30}
a0181eb @jeremyevans Various doc fixes
authored
611 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
612
a0181eb @jeremyevans Various doc fixes
authored
613 def self.clean_posts_with_few_comments
614 posts_with_few_comments.delete
615 end
616 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
617
a0181eb @jeremyevans Various doc fixes
authored
618 You can also implement table-wide logic by defining methods on the dataset:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
619
a0181eb @jeremyevans Various doc fixes
authored
620 class Post < Sequel::Model
621 def_dataset_method(:posts_with_few_comments) do
ce1c70f @jeremyevans Documentation cleanup
authored
622 filter{|o| o.num_comments < 30}
a0181eb @jeremyevans Various doc fixes
authored
623 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
624
a0181eb @jeremyevans Various doc fixes
authored
625 def_dataset_method(:clean_posts_with_few_comments) do
626 posts_with_few_comments.delete
627 end
628 end
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
629
a0181eb @jeremyevans Various doc fixes
authored
630 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:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
631
ce1c70f @jeremyevans Documentation cleanup
authored
632 Post.filter(:category => 'ruby').clean_posts_with_few_comments
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
633
a0181eb @jeremyevans Various doc fixes
authored
634 Sequel models also provide a short hand notation for filters:
de172d9 @ciconia Merged new associations branch into trunk.
ciconia authored
635
a0181eb @jeremyevans Various doc fixes
authored
636 class Post < Sequel::Model
ce1c70f @jeremyevans Documentation cleanup
authored
637 subset(:posts_with_few_comments){|o| o.num_comments < 30}
638 subset :invisible, ~:visible
a0181eb @jeremyevans Various doc fixes
authored
639 end
640
641 === Basic Model Validations
98169b7 @jeremyevans Various small doc fixes
authored
642
a0181eb @jeremyevans Various doc fixes
authored
643 To assign default validations to a sequel model:
98169b7 @jeremyevans Various small doc fixes
authored
644
a0181eb @jeremyevans Various doc fixes
authored
645 class MyModel < Sequel::Model
646 validates do
647 acceptance_of...
648 confirmation_of...
ce1c70f @jeremyevans Documentation cleanup
authored
649 format_of...
650 format_of...
651 presence_of...
a0181eb @jeremyevans Various doc fixes
authored
652 length_of...
ce1c70f @jeremyevans Documentation cleanup
authored
653 not_string ...
a0181eb @jeremyevans Various doc fixes
authored
654 numericality_of...
655 each...
656 end
657 end
98169b7 @jeremyevans Various small doc fixes
authored
658
a0181eb @jeremyevans Various doc fixes
authored
659 You may also perform the usual 'longhand' way to assign default model validates directly within the model class itself:
98169b7 @jeremyevans Various small doc fixes
authored
660
a0181eb @jeremyevans Various doc fixes
authored
661 class MyModel < Sequel::Model
662 validates_format_of...
663 validates_presence_of...
664 validates_acceptance_of...
665 validates_confirmation_of...
666 validates_length_of...
667 validates_numericality_of...
668 validates_format_of...
669 validates_each...
670 end
Something went wrong with that request. Please try again.