= ActiveCollection
Lazy-loaded Array-like collections of records.
Compatible with will_paginate.
== Example
A quick example:
If you have a model
class Beer < ActiveRecord::Base
end
You can make ActiveCollections of Beers like so:
class BeerCollection < ActiveCollection::Base
end
Or a more complex version:
class BeerCollection < ActiveCollection::Base
scope :geolocation
scope :by_brewery
order_by "distance ASC"
def names
map(&:name)
end
protected
def geolocation
if params[:lat] && params[:lng]
{ :origin => [params[:lat], params[:lng]], :within => params[:radius] || 50 }
end
end
def by_brewery
if params[:brewery_id]
{ :conditions => { :brewery_id => params[:brewery_id] } }
end
end
end
And you would use it like so:
beers = BeerCollection.new(:lat => 38.1234, :lng => -117.6543)
# All of these are lazy loaded only when they're needed.
beers.size # => Beer.count(:origin => [38.1234, -117.6543], :within => 50)
beers.each # => Beer.all(:origin => [38.1234, -117.6543], :within => 50, :order => "distance ASC") and yields each record
==== Custom conditions
You can specify anything you want for conditions using the scope, find_scope, and count_scope class methods. Conditions on the fly is on my road map.
brewery_beers = BeerCollection.new(:brewery_id => 1)
brewery_beers.to_a # Beer.all(:conditions => {:brewery_id => 1}) => [Beer, Beer, Beer, ...]
brewery_beers.size # Does not load count, just takes the size of the loaded collection.
brewery_beers.names # => ["La Folie", "1554", ...]
==== Pagination
ActiveCollections are fully will_paginate compliant.
paginated_beers = brewery_beers.paginate
paginated_beers.size # => size of this page only (doesn't query if already loaded)
paginated_beers.total_entries # => size of the entire collection without paging. (performs a database lookup if it can't be inferred by the collection size)
paginated_beers.total_pages
paginated_beers.next_page_collection # => new BeerCollection for page 2. Again, lazily-loaded.
==== Includes
Specify eager loading for a collection.
beers.include(:brewery) # => new collection that will eager load Brewery association when it loads.
Includes can also be specified in the class along with order. Anything specified on the class will combine using active record's rules (merge includes, overwrite order).
class BeerCollection < ActiveCollection::Base
includes :brewery => :owner
order_by "name asc"
# ...
end
== Usage
I tend to use this in my index action in a controller by just passing in params.
def index
@beers = BeerCollection.new(params)
end
It will automatically take care of paging. You can also pass the collection itself to the index named route to pass along the params necessary to link to the collection.
beers_path(@beers) # => Includes the right options for paging and any specified order or search query.
== Coming Soon
==== Search Integration
This can already be done by overloading load_collection and load_count in your collection.
I have sphinx and solr searching integrated on another project but I've yet to abstract it. Probably will work something like this:
BeerCollection.new(:q => "search term")
and then you specify searchability like so:
class BeerCollection < ActiveCollection::Base
# It could possibly auto-configure by looking at what search libraries are loaded.
# Note: this doesn't exist yet.
search_on :q, :using => :thinking_sphinx
end
==== Geolocation Integration
This can already be done as shown above, but I'd like it to be knowledgeable of the available geolocation libraries like Geokit and also support local search with solr and sphinx.
== Copyright
Copyright (c) 2009 Martin Emde. See LICENSE for details.