Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

Rails notification routing

In my app I'm using the gem mailboxer to handle my notifications and I've gotten everything up and running but I'm having trouble figuring out how to link_to the object that caused the notification. For example, I have a comment model that belongs to multiple models and I want to be able to display a link to the commentable model that the comment belongs to. I can't just call <%= link_to "View", notification.notified_object %> as that will try to link to the actual comment, I want the link to the status/project/event that it belongs to and I can't just call commentable. Any ideas on how to accomplish this? Thanks in advance.

Controller

class CommentsController < ApplicationController

before_filter :authenticate_member!
before_filter :load_commentable
before_filter :find_member

def index
    redirect_to root_path
end

def new
    @comment = @commentable.comments.new
end

def create
    @comment = @commentable.comments.new(params[:comment])
    @comment.member = current_member
    if @comment.save
      redirect_to :back
    else
      redirect_to :back
    end
end

def destroy
    @comment = Comment.find(params[:id])
    respond_to do |format|
      if @comment.member == current_member || @commentable.member == current_member
        @comment.destroy
        format.html { redirect_to :back }
      else
        format.html { redirect_to :back, alert: 'You can\'t delete this comment.' }
      end
    end 
end

private 

def load_commentable
    klass = [Status, Medium, Project, Event, Listing].detect { |c| params["#{c.name.underscore}_id"] }
    @commentable = klass.find(params["#{klass.name.underscore}_id"])
end

def find_member
    @member = Member.find_by_user_name(params[:user_name])
end 

end

Model

class Comment < ActiveRecord::Base
    belongs_to :member
    belongs_to :commentable, polymorphic: true
    attr_accessible :content

    validates :content, presence: true,
            length: { minimum: 2, maximum: 280 }

    after_create :create_notification, on: :create

    def create_notification
        subject = "#{member.user_name}"
        body = "wrote you a <b>Comment</b> <p><i>#{content}</i></p>"
        commentable.member.notify(subject, body, self)
    end
end

View

<% @notifications.each do |notification|%>
    <% @notification = notification %>

    <div>           
        <%= link_to(notification.subject, "#{root_url}#{notification.subject}") %>&nbsp;<%= Rinku.auto_link(truncate(notification.body, :length => 400)).html_safe %>
        <span class="not_meta"><%= time_ago_in_words(notification.created_at) %></span>
    </div>

    <div class="view">
        <% if notification.notified_object_type == 'Comment' %>
            <%= link_to("#") do %>
                <i class="icon-eye-open icon-green"></i> View
        <% end %>
    </div>
<% end %>

Migrations

class CreateComments < ActiveRecord::Migration
    def change
    create_table :comments do |t|
      t.text :content
      t.belongs_to :commentable, polymorphic: true
      t.references :member

      t.timestamps
    end
    add_index :comments, [:commentable_id, :commentable_type]
    add_index :comments, :member_id
    end
end

#Notifications and Messages
create_table :mailboxer_notifications do |t|
  t.column :type, :string
  t.column :body, :text
  t.column :subject, :string, :default => ""
  t.references :sender, :polymorphic => true
  t.column :conversation_id, :integer
  t.column :draft, :boolean, :default => false
  t.string :notification_code, :default => nil
  t.references :notified_object, :polymorphic => true
  t.column :attachment, :string
  t.column :updated_at, :datetime, :null => false
  t.column :created_at, :datetime, :null => false
  t.boolean :global, default: false
  t.datetime :expires
end

My comment resource route is nested under each model it belongs to.

1 Answer

You should be able to call comment.commentable since you have the belongs_to association set up on that. How i did this was define a notification model:

class Notification < ActiveRecord::Base

  belongs_to :user
  belongs_to :sender, :class_name => "User", :foreign_key => :sender_id
  belongs_to :notifiable, polymorphic: true

Then I can call notification.notifiable and this will return the model (Status, comment, picture, etc) on the other side of the polymorphic association. You can then interact with that model as if it was called originally.

Not sure how I missed this but you're correct I could have just called notification.notified_object.commentable. Thanks!