Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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