Adding AJAX Bookmarks to Your Rails Application (Part 2 of 2)

In part 1 of this series, we discussed the base models, controller, database migrations necessary to get this project off the ground. Now we are going to continue with this functionality

Let’s take a look at what needs to go into the models to support this. If you have a model that uses a slug generated via to_param, then your code will look like the top model, If you are using the normal numeric id convention, then it will look like the bottom model. The reason for the specifically named methods get_title and get_description will become apparent when you start displaying bookmarks. The thought process is that you can use a consistent set of calls for displaying the bookmark information and put the code to grab that information in the model where it belongs rather than loading up the helper methods. What should also be noted is that the title and description fields are not always consistent across models. Therefore the method naming conventions returns the proper column with consistent method names.

Slug based model:

  def self.get_title(id)
    find_by_id(id).name
  end

  def self.get_description(id)
    find_by_id(id).description
  end
 
  def self.find_id_by_site_url(site_url)
    url = site_url.split(%r{/})
    find_by_slug(url[2]).id
  end

Numeric ID based model:

  def self.get_title(id)
    find_by_id(id).title
  end

  def self.get_description(id)
    find_by_id(id).description
  end
 
  def self.find_id_by_site_url(site_url)
    site_url.split(%r{/})[2]
  end

Now that we have the code in our models, controllers, and the database is setup, let’s put it into our views so we can actually take advantage of our bookmark system. Let’s dive right in to adding the code. Inside our show view, we just add the lines below. We check to see if the user is logged in. If they are, check their bookmark status of this page and show the appropriate image.

< % if current_user %>
    <a href="/bookmarks/toggle?url=<%=h request.request_uri %>" id="bookmark_toggle" class="ajax_get">
    < % if bookmarked?(current_user.id, request.request_uri) %>
        < %= image_tag("/images/star_bookmark_enabled.png", :id => "bookmark_star", :size => "16x16", :alt => "Bookmark", :class => "bookmark", :border => 0) %>
    < % else %>
        < %= image_tag("/images/star_bookmark_disabled.png", :id => "bookmark_star", :size => "16x16", :alt => "Bookmark", :class => "bookmark", :border => 0) %>
    < % end %>
    </a>
< % end %>

The are a few helper methods that are required here to support this functionality. The bookmarks_helper.rb looks as follows. Notice the use of the send method in the link_to_bookmark method. That enables us to abstract the link_to to be for any model_path that you have in the Bookmarktypes.

module BookmarksHelper

  # Make sure that self.find_id_by_site_url is in every model that can be bookmarked
  def bookmarked?(user_id,request_uri)
    uri_array = request_uri.split(%r{/})
    bookmark_type_id = Bookmarktype.find_by_model(uri_array[1].singularize).id
    modeltype = Bookmarktype.find_by_model(uri_array[1].singularize).model.capitalize.constantize
    model_id = modeltype.find_id_by_site_url(request_uri)
    return Bookmark.is_bookmarked?(user_id,bookmark_type_id,model_id)
  end
 
  def print_bookmark_type(model_type_id)
    Bookmarktype.find(model_type_id).model.capitalize
  end
 
  # Make sure that self.get_title is in every model that can be bookmarked
  def link_to_bookmark(anchor_text,bookmark)
    controller = Bookmarktype.find(bookmark.model_type_id).model.pluralize
    title = Bookmarktype.find(bookmark.model_type_id).model.capitalize.constantize.get_title(bookmark.model_id)
    @item = Bookmarktype.find(bookmark.model_type_id).model.capitalize.constantize.find_by_id(bookmark.model_id)
    link_to "#{anchor_text}. #{title}", send("#{controller.singularize}_path", @item)
  end
 
  # Make sure that self.get_description is in every model that can be bookmarked
  def print_bookmark_desc(bookmark)
    Bookmarktype.find(bookmark.model_type_id).model.capitalize.constantize.get_description(bookmark.model_id)
  end

end

You’ll recognize some of this code from the model and the controller. I know this isn’t necessarily DRY, but I am by no means a Rails expert. But I do know this code works.

The last item to tie this whole thing together is allowing the user to view their bookmarks. We already have the code for the show page in our controller. Keeping that in mind, let’s take a look at using that and our helper methods to give a decent presentation of our bookmarks.

<div class="middle_back">
    <p>To bookmark a page or remove a bookmark, just click the star.</p>
    < %= image_tag("/images/star_bookmark_enabled.png", :size => "16x16", :alt => "Bookmark", :border => 0) %> Bookmarked<br />
    < %= image_tag("/images/star_bookmark_disabled.png", :size => "16x16", :alt => "Bookmark", :border => 0) %> Not Bookmarked<br />
    <br />
    < % if !@bookmarks.blank? %>
        < % count = 0 %>
        <p>Below are the pages that you have bookmarked.</p>
        <br />
        < % @bookmarks.each do |bookmark| %>
            <div id="horizontal_line"></div>
            <div id="bookmark_list">
                < % count = count + 1 %>
                <span id="bookmarkLink">< %= link_to_bookmark(count, bookmark) %></span><br />
                <span id="bookmarkType">< %= print_bookmark_type(bookmark.model_type_id) %></span><br />
                <span id="bookmarkDescription">< %= print_bookmark_desc(bookmark) %></span><br />
                <span id="bookmarkAddedDate">Added < %= bookmark.created_at %></span>
            </div>
        < % end %>
        <div id="horizontal_line"></div>
    < % else %>
      You currently have no items bookmarked.
    < % end %>
</div>

And there you have it. Your users can now bookmark pages and view their list of bookmarks in a profile type setting. There might be other ways to do this, but this way has worked for me thus far. Good luck.

Posted in Rails. Tags: , . 3 Comments »
  • Pingback: Adding AJAX Bookmarks to Your Rails Application (Part 1 of 2) | Eric's Tech Blog

  • http://integrumtech.com/ Derek Neighbors

    Thanks for taking the time for the detailed write up. I could have used something like this on a recent project.

  • http://integrumtech.com/ Derek Neighbors

    Thanks for taking the time for the detailed write up. I could have used something like this on a recent project.