Jul 18 2006

Acts As Taggable Conditions Hack

I’ve been happily using the Ruby on Rails acts_as_taggable plugin for some time now. But recently I had a situation that where I had to hack the plugin. The taggable plugin provides a find_tagged_with method that is used to find all records for a given model with a certain tag. Here is the typical usage of this method:

@post = Post.find_tagged_with ['tag1', 'tag2']

What I needed to do is add some conditions to this method. I wanted to find all post tagged ‘tag1’ or ‘tag2’ that were created by a given user. To do this I needed to hack the acts_as_taggable find_tagged_with method as follows:

def find_tagged_with(list, options = {})
  query = "SELECT #{table_name}.* FROM #{table_name}, tags, taggings " +
    "WHERE #{table_name}.#{primary_key} = taggings.taggable_id " +
    "AND taggings.taggable_type = ? " +
    "AND taggings.tag_id = tags.id AND tags.name IN (?) "

  arr = [query, acts_as_taggable_options[:taggable_type], list]

  if options[:conditions] != nil
    conditions = options[:conditions]
    arr.first << " AND #{conditions.first}"
    arr = arr + conditions[1..conditions.size-1]
  end

  found = find_by_sql(arr)
  found.uniq!
  found
end

With this code in place, I was able to do what I wanted like this:

@posts = Post.find_tagged_with params[:id],
  :conditions => ['posts.user_id = ?', session[:user].id]

Technorati Tags: , , , , ,


Jul 17 2006

Creating Custom Rails Routes

There comes a time in a Ruby on Rails project that will require a custom routes definition. Routes, in rails, are somewhat similar to what you can do in Apache’s .htaccess files, that is you can map URLs to different locations. For example, if you use WordPress you will need to edit your .htaccess file so that you can create permalinks. In Rails, you can create routes so that a given URL is managed by the right controller. Here is a routes example. Lets say that I want to write a ‘are you hot or not’ type of application and I need to find two records at the same time from the database. In this case the records are pictures of hot chicks. To do this I would first start by editing the rails config/routes.rb file and add the following routing:

[source:ruby]
# Sometimes I need an extra id
map.connect ‘:controller/:action/:id/:xid’
[/source]

This route will resolve any url that looks like this:

http://mydomain.com/myrailsapp/mycontroller/myaction/1/2

Where 1 and 2 are the record ids. In the controller you have access to these ids via the params variable.

[source:ruby]
id = params[:id]
xid = params[:xid]
[/source]

Another example of when to use routes could be when you want the url to remain the same but need the flexibility to refactor or rename the controller at a later point. In this case you can use a routing that is similar to the following:

[source:java]
map.connect ‘user/:id’, :controller => ‘admin/user’, :action => ‘profile’
[/source]

Technorati Tags: , , , ,


Jul 15 2006

Acts As Taggable Tag Cloud

If you are using the Ruby on Rails acts_as_taggable plugin you may want to display the top tags in a tag cloud. To generate a tag cloud you will to write some code beyond just writing the controller action. You will need to augment the taggable plugin itself and create a function in the application helper. Lets start by adding a new method in the Tag model provided by the acts_as_taggable plugin. Open the vendor/plugins/acts_as_taggable/lib/tag.rb file and add this method:

def self.tags(options = {})
  query = "select tags.id, name, count(*) as count"
  query << " from taggings, tags"
  query << " where tags.id = tag_id"
  query << " group by tag_id"
  query << " order by #{options[:order]}" if options[:order] != nil
  query << " limit #{options[:limit]}" if options[:limit] != nil
  tags = Tag.find_by_sql(query)
end

This method will return the id, name, and usage count for each tag. This method also provides a way to limit and order the tags. Once we have added the new method in the Tag model we will need to add a method to the application helper which will help in selecting the right style class for each tag. Add the following function to app/helpers/application_helper.rb:

def tag_cloud(tags, classes)
  max, min = 0, 0
  tags.each { |t|
    max = t.count.to_i if t.count.to_i > max
    min = t.count.to_i if t.count.to_i < min
  }

  divisor = ((max - min) / classes.size) + 1

  tags.each { |t|
    yield t.name, classes[(t.count.to_i - min) / divisor]
  }
end

The implementation for the tag_cloud method is based on the work done by Tom Fakes. With the tags method in the Tag model and the tag_cloud method in the application helper we can now focus on the controller and view. In the controller you can use the following bit of code to get the the first one hundred tags order by name:

@tags = Tag.tags(:limit => 100, :order => "name desc")

In the view we will use the tag_cloud method so that it will select the right css class based on the tag usage count. Here is the view code:

<% tag_cloud @tags, %w(nube1 nube2 nube3 nube4 nube5) do |name, css_class| %>
  <%= link_to name, {:action => :tag, :id => name},
    :class => css_class %>
<% end %>

Please note that nube1 through nube5 are css class names which will need to be defined in your stylesheet to generate the cloud effect. Just completeness sake here are my css classes:

.nube1 {font-size: 1.0em;}
.nube2 {font-size: 1.2em;}
.nube3 {font-size: 1.4em;}
.nube4 {font-size: 1.6em;}
.nube5 {font-size: 1.8em;}
.nube6 {font-size: 2.0em;}

Technorati Tags: , , , ,


Jul 15 2006

Generate Pretty GWT JavaScript

If you are just starting out with the Google Web Toolkit you may have already noticed that GWT generates obfuscated javascript code in those cache.html files. If you ever want to debug the GWT auto-generated JavaScript code, or in my case learn from it, you can have GWT generate legible JavaScript code. To have GWT generate pretty JavaScript code edit the MyApplication-compile script. Right after where you read com.google.gwt.dev.GWTCompiler add the following option:

-style PRETTY

Instead of PRETTY you can also use DETAILED or the default OBF.

Technorati Tags: , , , , ,


Jul 9 2006

Comments on Acts As Commentable

I have received a bit positive feeback rearding the acts_as_commentable rails plugin. One suggestion asked for me to provide more sample code, especially in regards to the view and controller. To fulfill that request lets imagine that we are developing an application where different type of models act as commentable. We can start by creating a _comment_form.rhtml partial view that can be used by other views. The comment form should have the following fields:

<% start_form_tag :action => 'add_comment' %>
  <%= text_field 'comment', 'title' %>
  <%= text_field 'comment', 'comment' %>

  <%= submit_tag 'Add Comment' %>

  <%= hidden_field 'commentable', 'commentable', :value => @model.type %>
  <%= hidden_field 'commentable', 'commentable_id', :value => @model.id %>
<% end_form_tag %>

The @model variable holds an intance to the record you want to add a comment to. In this example, the add_comment action will be generic enough that it will be able to add comments to any ActiveRecord in the application. Because the add_comment action can add comments to say either Tasks or Posts you need to pass the model type to the action via a hidden field.

In the controller you can define the add_comment action like this:

def add_comment
  commentable_type = params[:commentable][:commentable]
  commentable_id = params[:commentable][:commentable_id]
  # Get the object that you want to comment
  commentable = Comment.find_commentable(commentable_type, commentable_id)

  # Create a comment with the user submitted content
  comment = Comment.new(params[:comment])
  # Assign this comment to the logged in user
  comment.user_id = session[:user].id

  # Add the comment
  commentable.comments << comment

  redirect_to :action => commentable_type.downcase,
    :id => commentable_id
end

In the sample code for the action there is an assumtion that only logged in user can add comments and that there is a action named after the commentable active record type.

Technorati Tags: , , , , ,


Jul 5 2006

Top 11 Rails Plugins

I was explaining to a Java developer at work how you get soo much for free when working Ruby on Rails. I tried to explain the simplicity of ActiveRecord and the lack of getters and setters. I mentioned the number of rails plugins that provided tagging, versioning, and commenting functionality. Out of that conversation I thought to write a list of the top Ruby on Rails plugins that I have found useful.

Acts As Taggable – Use this plugin to add tagging, folksonomy, support to your ActiveRecord models. You can search active records via tags.

Acts As Rateable – This plugin allows ActiveRecord models to be rated between 1 upto some application specified number. This plugin will calcuate the average rating for a record.

Acts As Commentable – With this rails plugin you can add comments to your ActiveRecord models.

Acts As Voteable – With this plugin users can cast a vote for or against an ActiveRecord model. You can filter by votes, get a total vote count, etc.

Acts As Blog – This plugin will help convert Textile, Markup, SmartyPants markup text to HTML. This plugin is useful in blog or wiki-like systems.

Acts As Versioned – With this plugin you can manage different version of a ActiveRecord models. Use this plugin in version controlled applcitions like a wiki.

Acts As Bookmarkable – This plugin allows user to bookmark any ActiveRecord model. In addition to bookmarking records you can combine this plugin with Acts As Taggable so that bookmarks can also be tagged.

Riff Rails – This plugin functions as a diff tool for active records. You might find it useful to combine this plugin with Acts As Versioned.

Rails PDF – This ActiveView plugin allows rails developers to create actions that produce PDF documents. This is a handy plugin for applications that want to generate reports.

Calendar Helper – Another ActiveView plugin that will produce stylish calendars.

Graphs Rails – Use this ActiveRecord plugin to generate CSS powered bar graphs.