Pierre-Louis Gottfrois
Pierre-Louis Gottfrois

Reputation: 17631

Polymorphic behavior with link_to

I'm facing kind of a tricky challenge here, maybe one of you could help me on this. Nothing too hard, I just don't know the good way to go. Here comes:

I have 2 models:

Activity and Comment

I'm using MongoID here with a famous gem to handle votes (likes). It only take care of embedding a vote model inside an Activity or a Comment.

This means that I don't have a Like model.

How ever I would like to be able to "like" an "activity" and/or a "comment". My guess is to go with a single Controller named LikesController with 2 methods for both liking or unliking an item.

I would then have this kind of routes:

resources :activities,  :only => [] do
  resources :comments,  :only => [:create, :destroy]
  resources :likes,     :only => [:create, :destroy]
end

resources :comments, :only => [] do
  resources :likes,  :only => [:create, :destroy]
end

Here are the given routes from bundle exec rake routes:

activity_comments POST   /activities/:activity_id/comments(.:format)     comments#create
activity_comment  DELETE /activities/:activity_id/comments/:id(.:format) comments#destroy
activity_likes    POST   /activities/:activity_id/likes(.:format)        likes#create
activity_like     DELETE /activities/:activity_id/likes/:id(.:format)    likes#destroy
comment_likes     POST   /comments/:comment_id/likes(.:format)           likes#create
comment_like      DELETE /comments/:comment_id/likes/:id(.:format)       likes#destroy

Now I would like to have a "Like" / "Unlike" link in some view page pointing to the LikesController on the corresponding actions (create => like ; destroy => unlike).

The only thing is that I don't know how to create the link_to with the good path. Here is what I have so far:

link_to 'Like', eval("#{item.class.name.downcase}_likes_path(item)"), :method => 'post'

link_to 'Unlike, eval("#{item.class.name.downcase}_likes_path(item)"), :method => 'destroy'

Can anyone give me a tip on how to do that in a cleaner way ?

Here are my models:

class Activity
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongo::Voteable

  has_many    :comments,  :as => :item
  belongs_to  :subject,   :polymorphic => true
  embeds_one  :user

  validates :user, :presence => true

  voteable self, :up => +1, :down => -1
end

class Comment
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongo::Voteable

  belongs_to :item, :polymorphic => true
  embeds_one :user

  field :text, :type => String, :default => nil

  validates :text, :presence => true
  validates :user, :presence => true

  attr_accessible :text, :user

  voteable self, :up => +1, :down => -1
end

You have to keep in mind that a lot of futur objects could be "likeable" also, that's why I choose to go with a LikesController beside having duplicate method inside each item's controller to handle "likes" and "unlikes". Maybe I'm wrong ?

Thank you.

Upvotes: 0

Views: 496

Answers (1)

ksol
ksol

Reputation: 12235

I would go with a view helper here.

def likehelper(likable)
  if likable.is_a? Activity
    # return activitycommentpath..
  ...
end

and then in your view link_to 'Like', likehelper(item), and the same for Unlike. If other objects become "likable", all you would have to do is add a new "rule" to your helper.

Upvotes: 2

Related Questions