-
Notifications
You must be signed in to change notification settings - Fork 533
Using Tire with Draper
There's many reasons for wanting to augment either the Collection or the Item returned by Tire.
For example:
- Add additional view logic that you don't want to put in a helper
- Reuse view logic between ActiveRecord/Mongoid & Tire.
- Just to add additional methods to the collection or item.
You can do this using Draper which provides View-Models/Decorators.
There's two ways to go about it.
We could define just a ordinary Draper::Decorator
:
class Article < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
end
class ArticleDecorator < Draper::Decorator
# We can either delegate all methods not defined in our decorator
# or just specific methods that we want to pass on to the Tire::Results::Item instance.
delegate_all
def caption
source.title + '-' + source.description
end
end
We can then use it like this
articles = Article.search(query: 'Draper')
decorated_articles = ArticleDecorator.decorate_collection(articles)
That would give us an instance of Draper::CollectionDecorator
which is the default collection decorator in Draper.
So we could then do:
decorated_articles.first.name
=> 'The name'
However, what we cannot do with this approach is to get to any facets or other methods defined on the Tire::Results::Collection
object.
decorated_articles.facets
=> NoMethodError: undefined method `facets' for #<Draper::CollectionDecorator:0x007f836d9888f0>
That's because the default collection decorator does not automatically delegate those methods to the source Tire::Search::Collection
instance. So if we don't need to access those properties, we are fine with this method.
We also need to define a Draper::CollectionDecorator
ArticlesDecorator < Draper::CollectionDecorator
# There's no delegate_all here so we'll need to delegate each
# methods we want to pass on to the Tire::Search::Collection
delegate :facets
def some_method_that_deals_with_the_collection
# we can access the Tire::Search::Collection instance via the method `source`.
'something'
end
end
We can now use this collection decorator directly to decorate both the collection and the items. Draper will automatically look for a decorator named ArticleDecorator
, loop through our hits and wrap them with it.
articles = Article.search(query: 'Draper')
decorated_articles = ArticlesDecorator.new(articles)
# We can now access the method we delegated
decorated_articles.facets
=> { hash with the facets }
# Or the ones we defined in our ArticlesDecorator
decorated_articles.some_method_that_deals_with_the_collection
=> 'something'
# Each of our items are decorated with ArticleDecorator
decorated_articles.first.caption
=> 'Using Tire with Draper - To augment Collection and Item'
# We also still have the methods from Tire::Results::Item at arms length
# since we used delegate_all in our ArticleDecorator.
decorated_articles.first._score
=> '0.30685282'
Alternatively
If we don't need to add any methods to the collection, but we do want to access the facets (or other methods), we can define just one collection decorator to use with Tire. We can reuse this collection decorator for multiple Tire enabled models
.
class ResultsDecorator < Draper::Base
delegate :facets
end
# We can then specify which item decorator we want to use for each item.
articles = Article.search(query: 'Draper')
decorated_articles = ResultsDecorator.new(articles, with: ArticleDecorator)
We can now reuse our view models between Tire and ActiveRecord/Mongoid, as long as they adhere to the same interface (attributes).
For more information on draper, read the Draper readme