views:

81

answers:

3

Lets say I have a wine and beer model/controller along with a wine_review and beer_review model with a has many relationship.

Each table has near identical data types, for example, wine_review and beer_review each has a rating and comment column. Wine and beer each, have names.

How can I dry up my code so they can both share the same views.

Right now I have two views doing the same thing, once for beer and once for wine.

<% @wine_reviews.each do |review| -%>
  <ul>
    <li><%= link_to review.wine.name, review.wine %></li>
    <li><%= review.rating %></li>
    <li><%=h review.comment %></li>
  </ul>
<% end -%>

<% @beer_reviews.each do |review| -%>
  <ul>
  ....
  ....

What I would really like to do is something like

<li><%= link_to review.beverage.name, review.beverage %></li>

Where beverage would be replaced with wine or beer depending on what I'm rendering at that time but I can't figure out how to do that.

I can't pass beverage as a local to a partial as review.beverge will break.

Thanks.

+1  A: 

Assuming a review doesn't have reviews for wine and beer, you could try adding this to your review model. This might not be the exact code, but the point is that in your model you find out what type of review it is and your views don't care.

def beverage
  @beverage ||= self.beer || self.wine
end
Andy Gaskell
+6  A: 

You have several different options here:

Duck typing:

In Ruby, as long as an object responds to a method call that method call with work. So if both beer and wine have an attribute :name, then calling @result.name will work with either model.

Polymorphic Associations:

Instead of having review.beer or review.wine. Make Wine and Beer reviewable via a polymorphic association. This will also work for comments, e.g., both become commentable. Take a look at acts_as_commentable for how this works. Railscasts has an excellent screencast on how this works.

Single Table Inheritance:

You could create an object called Beverage from which Wine and Beer inherit. This allows for a single place to put the similar attributes and still allows for small differences.

From your description, you are looking at a combination of all three possibly.

Chris Johnston
I think handling this with Polymorphic Associations would be the preferred choice here.
Ben
Checking out the railscast now. Looks like it's exactly what I'm looking for. Thanks guys.
KJF
+1  A: 

Polymorphic associations is the answer. You can design your table that contains review with:

create_table :beverage_reviews, :force => true do |t|
  t.string :review_text
  t.integer :rating
  t.string  :beverage_type
  t.string  :beverage_id
end

And the BeverageReview model can be:

class BeverageReview < ActiveRecord::Base
  belongs_to :beverage, :polymorphic => true
end

And the Beer and Wine models can be:

class Beer < ActiveRecord::Base
  has_one :beverage_review, :as => :beverage
end

This way if you are going to display all the reviews, you can possibly write your code as:

<% @all_reviews.each do |review| -%>
  <%= review.review_text -%>
  <%= review.beverage.name -%>
<% end -%>
Hemant Kumar