Skip to content

Commit

Permalink
Update the docs with information on dynamic customization of regular …
Browse files Browse the repository at this point in the history
…and eager association loading
  • Loading branch information
jeremyevans committed Apr 14, 2011
1 parent 3c631b6 commit 4139dd9
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 29 deletions.
14 changes: 14 additions & 0 deletions README.rdoc
Expand Up @@ -685,6 +685,20 @@ Associations can be eagerly loaded via +eager+ and the <tt>:eager</tt> associati


In addition to using +eager+, you can also use +eager_graph+, which will use a single query to get the object and all associated objects. This may be necessary if you want to filter or order the result set based on columns in associated tables. It works with cascading as well, the syntax is exactly the same. Note that using eager_graph to eagerly load multiple *_to_many associations will cause the result set to be a cartesian product, so you should be very careful with your filters when using it in that case. In addition to using +eager+, you can also use +eager_graph+, which will use a single query to get the object and all associated objects. This may be necessary if you want to filter or order the result set based on columns in associated tables. It works with cascading as well, the syntax is exactly the same. Note that using eager_graph to eagerly load multiple *_to_many associations will cause the result set to be a cartesian product, so you should be very careful with your filters when using it in that case.


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:

# Eagerly load only replies containing 'foo'
Post.eager(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all

This also works when using +eager_graph+, in which case the proc is called with dataset to graph into the current dataset:

Post.eager_graph(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all

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:

# Eagerly load only replies containing 'foo', and the person and tags for those replies
Post.eager(:replies=>{proc{|ds| ds.filter(text.like('%foo%'))}=>[:person, :tags]}).all

=== Extending the underlying dataset === Extending the underlying dataset


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: 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:
Expand Down
80 changes: 51 additions & 29 deletions doc/association_basics.rdoc
Expand Up @@ -293,32 +293,6 @@ Examples:
@artist.remove_album(@album) @artist.remove_album(@album)
@artist.remove_all_albums @artist.remove_all_albums


== Dataset Method

In addition to the above methods, associations also add a instance method
ending in +_dataset+ that returns a dataset representing the objects in the associated table:

@album.artist_id
# 10
@album.artist_dataset
# SELECT * FROM artists WHERE (id = 10)

@artist.id
# 20
@artist.albums_dataset
# SELECT * FROM albums WHERE (artist_id = 20)

The association dataset is just like any other Sequel dataset, in that
it can be further filtered, ordered, etc.:

@artist.albums_dataset.
filter(:name.like('A%')).
order(:copies_sold).
limit(10)
# SELECT * FROM albums
# WHERE ((artist_id = 20) AND (name LIKE 'A%'))
# ORDER BY copies_sold LIMIT 10

== Caching == Caching


Associations are cached after being retrieved: Associations are cached after being retrieved:
Expand Down Expand Up @@ -352,13 +326,58 @@ instance method:
@album.artists # [<Artist ...>, ...] @album.artists # [<Artist ...>, ...]
@album.associations[:artists] # [<Artist ...>, ...] @album.associations[:artists] # [<Artist ...>, ...]


Note that while the association method caches associated objects, if you == Dataset Method
retrieve access through the association dataset directly, the results
will not be cached: In addition to the above methods, associations also add a instance method
ending in +_dataset+ that returns a dataset representing the objects in the associated table:

@album.artist_id
# 10
@album.artist_dataset
# SELECT * FROM artists WHERE (id = 10)

@artist.id
# 20
@artist.albums_dataset
# SELECT * FROM albums WHERE (artist_id = 20)

The association dataset is just like any other Sequel dataset, in that
it can be further filtered, ordered, etc.:

@artist.albums_dataset.
filter(:name.like('A%')).
order(:copies_sold).
limit(10)
# SELECT * FROM albums
# WHERE ((artist_id = 20) AND (name LIKE 'A%'))
# ORDER BY copies_sold LIMIT 10

Records retrieved using the +_dataset+ method are not cached in the
associations cache.


@album.artists_dataset.all # [<Artist ...>, ...] @album.artists_dataset.all # [<Artist ...>, ...]
@album.associations[:artists] # nil @album.associations[:artists] # nil


== Dynamic Association Modification

Similar to the +_dataset+ method, you can provide a block to the association
method to customize the dataset that will be used to retrieve the records. So
you can apply a filter in either of these two ways:

@artist.albums_dataset.filter(:name.like('A%'))
@artist.albums{|ds| ds.filter(:name.like('A%'))}

While they both apply the same filter, using the +_dataset+ method does not
apply any of the association callbacks or handle association reciprocals (see
below for details about callbacks and reciprocals). Using a block instead handles
all those things, and also caches its results in the associations cache (ignoring
any previously cached value).

Note that if you are using ruby 1.8.6, you can't pass a block to the association
method, you have to pass a proc as an argument:

@artist.albums(proc{|ds| ds.filter(:name.like('A%'))})

== Name Collisions == Name Collisions


Because associations create instance methods, it's possible to override Because associations create instance methods, it's possible to override
Expand Down Expand Up @@ -1230,6 +1249,9 @@ If the proc takes one argument, it will be given a hash with the following keys:
:self :: The dataset that is doing the eager loading :self :: The dataset that is doing the eager loading
:table_alias :: An alias to use for the table to graph for this association. :table_alias :: An alias to use for the table to graph for this association.
:implicit_qualifier :: The alias that was used for the current table (since you can cascade associations). :implicit_qualifier :: The alias that was used for the current table (since you can cascade associations).
:callback :: A callback proc used to dynamically modify the dataset to graph into the
current dataset, before such graphing is done. This is nil if no callback
proc is used.


If the proc takes three arguments, it gets passed the :self, :association_alias, If the proc takes three arguments, it gets passed the :self, :association_alias,
and :table_alias values. The 3 argument procs are allowed for backwards and :table_alias values. The 3 argument procs are allowed for backwards
Expand Down

0 comments on commit 4139dd9

Please sign in to comment.