Archive for the ‘Ruby on Rails’ Category

Live fulltext search in Ruby on Rails

Some time ago I promised to create a small tutorial about live fulltext search. A fulltext search, that gives you results as you type.

Ingredients:

  • Ruby on rails
  • ferret gem (gem install ferret)
  • acts_as_ferret gem (gem install acts_as_ferret)
  • auto_complete plugin (from the application root: ruby script/plugin install auto_complete)

What we will do

  1. Create an empty application – simple book database
  2. Add fulltext search capabilities
  3. Create the live search
    1. Create search pane partial – the one that will display the search box
    2. Create the search results partial – that will render the hints (search results)
    3. Modify controller to respond to the search pane

Create a book database application

We will create a small application for book management. It will store, list, update books and it will also provide the live search.
So, lets create the skeleton of the aplication:

# Create the rails application
rails books
# create database books
echo "create database books"  | mysql -u root -p
cd books

Configure database login and password in app/config/database.yml.

development:
  adapter: mysql
  database: books
  username: root
  password: password
  host: localhost
  port: 3306

Create skeleton of the application. From root of the application run:

ruby script/generate scaffold Book title:string abstract:text

Create the books table

rake db:migrate

Start up the development server

ruby script/server

Now, browse to http://127.0.0.1:3000/books and type in some data.

Add fulltext search capabilities

Change the app/models/book.rb to support fulltext search

require "acts_as_ferret"
 
class Book < ActiveRecord::Base
    acts_as_ferret
end

You can check in the console, that the fulltext is enabled. Just start the console via
ruby script/console and put there

Book.find_by_contents("book").

It should return a result set, similar to this:

=> #<ActsAsFerret::SearchResults:0x2540f54 @results=[#<Book id: 2, title: "Book secondo", abstract: "Book about book", created_at: "2008-07-07 23:16:38", updated_at: "2008-07-07 23:16:38">, #<Book id: 1, title: "First book", abstract: "This is a first book", created_at: "2008-07-07 23:16:23", updated_at: "2008-07-07 23:16:23">], @total_hits=2>

Create the live search

Finally, create the live search.

Create search pane partial

The search_pane will be used to display search box.

Create a partial _search_pane.html.erb in app/views/books and put there simple tag. The tag create Ajax Autocompleter that calls auto_complete_for_search_query method of the default controller (in our case it will be books)

<%= text_field_with_auto_complete :search, :query %>

Add javascript include and partial rendering to the books template app/views/layouts/books.html.erb.

Do not forget! The javascript include must be in the head of the template.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Books: <%= controller.action_name %></title>
  <!-- HERE -->
  <%= stylesheet_link_tag 'scaffold' %>
	<%= javascript_include_tag :defaults %> 
</head>
<body>
 
<!-- AND HERE -->
<%= render :partial=>"books/search_pane" %>
 
<p style="color: green"><%= flash[:notice] %></p>
 
<%= yield  %>
 
</body>
</html>

Create the search results partial

The search_results will format the results of the full text search and will “offer” the resulting records. Create a partial app/views/books/_search_results.html.erb and add there the formatting code:

<ul>
	<% for book in @books %>
	<li><%= link_to h(book.title), :controller=>"books", :action=>"show", :id=>book %></li>
  	<% end %>
</ul>

Modify controller

Add the following line at the beginning of the books_controller.

protect_from_forgery :only => [:create, :update, :destroy]

Create a method in books_controller that will search for the books

 def auto_complete_for_search_query
   @books = Book.find_by_contents(params["search"]["query"]+"*", {:limit => 5})
   render :partial => "search_results"
 end

We do not want to generate the whole page layout, so it is necessary to specify it in the books controller:

layout 'books', :except => [:auto_complete_for_search_query]

And now, navigate to http://127.0.0.1:3000/books and start searching. As soon as you start typing into the search box, it shows results. Click on one of the proposed links to see what happens. Source code is here.

Block level helpers in Ruby on Rails

Web developer often comes to a situation where he needs to “decorate” a block of a page. There is a simple solution and simple and elegant solution.
Helpers concept in Ruby on Rails is very powerful and will be used to do the trick.

Imagine, you want to create a rounded box helper similar to Nifty Corners (http://www.html.it/articoli/nifty/index.html), but you do not want to use javascript. So, a handy helper needs to be created.
The principle of the Nifty Corners trick is adding few b tags before content, few b tags after it and few lines to CSS.

<div id="container">
<b class="rtop">
  <b class="r1"></b> <b class="r2"></b> <b class="r3"></b> <b class="r4"></b>
</b>
<!--content goes here -->
<b class="rbottom">
  <b class="r4"></b> <b class="r3"></b> <b class="r2"></b> <b class="r1"></b>
</b>
</div>

and modification of CSS

.rtop, .rbottom{display:block}
.rtop *, .rbottom *{display: block; height: 1px; overflow: hidden}
.r1{margin: 0 5px;}
.r2{margin: 0 3px;}
.r3{margin: 0 2px;}
.r4{margin: 0 1px; height: 2px;}
.r1, .r2, .r3, .r4 {background-color:red;}
.cont{background-color:red;}

Easy solution

The easy solution uses two helpers. One to put before the content and one to put it after. So, the view could look like:

...
<%= round_box_start %>
content
<%= round_box_end %>
...

Well, it does not look good. So, what about the other solution?

Easy and elegant solution

We will create a block level helper. The view will look much better.

...
<% round_box do %>
content
<% end %>
...

Now, let’s create the helper:

def round_box(&block)
  b = '<div id="container"><b class="rtop"><b class="r1"></b><b class="r2"></b><b class="r3"></b><b class="r4"></b></b><div class="cont">'
  e = '</div><b class="rbottom"><b class="r4"></b> <b class="r3"></b> <b class="r2"></b> <b class="r1"></b></b></div>'
 
  # Get the data from the block 
  data = capture(&block)
 
  res = b + data + e
 
  # Use concat method to pass text back to the view 
  concat(res, block.binding)
end

Good, but works only for red boxes. How to pass another color? Simply:

...
<% round_box(color) do %>
content
<% end %>
...

and in helper

def round_box(color, &block)
  b = '<div id="container"><b class="rtop"><b class="r1"></b><b class="r2"></b><b class="r3"></b><b class="r4"></b></b><div class="cont">'
  e = '</div><b class="rbottom"><b class="r4"></b> <b class="r3"></b> <b class="r2"></b> <b class="r1"></b></b></div>'
 
  # Get the data from the block 
  data = capture(&block)
 
  res = b + data + e
 
  # Use concat method to pass text back to the view 
  concat(res, block.binding)
end

Final implementation of the colored box is left as a homework :o)

Rails and Grails comparison

I have spent few years developing in Ruby on Rails. For the last half of year I have been learning Grails, too. The things described here are a summarization of the differences that I had to come over.

The comparison will be written in a simple “table based” structure. I just got used to it when preparing comparisons or product studies for tenders.

Ruby on Rails

A lightweight web framework written in Ruby scripting programming language. It contains its own application server. See the Rails home website.

Grails

A web framework written in Groovy. The source code is compiled to byte code and can be run on Java application servers. Grails home website.

Maturity

Before I start comparison, it is fair to say, that Rails has been around from 2004 while Grails final version dates from 2008. So, some of the differences are caused by this time shift.

Both of the frameworks are more evolutionary than revolutionary. They just implement the right patterns and they do it right.

Ruby on Rails Grails
First release (version 1.0) July 2004 February 2008
Life cycle Mature framework with solid base of developers Young framework with a growing base of developers and a huge base of potential developers (from Java)

Documentation

Ruby on Rails Grails
Framework Excellent. Uses the RDoc that contains not only list of methods, classes and files, but also source code of a method, with syntax highlighting. Very good. Contains list of classes, methods, files… but I am really missing the source code. If the code is not documented, the documentation is useless
Application Excellent. Only the application files are documented using the RDoc (including syntax highlighting) On one hand it is exhaustive, because it generates documentation for all classes in the project (including plugins). On the other hand it does not contain the source code. So, once the code is not documented, the documentation is useless. So, on average I would say it is good.

Development

Both frameworks are based on flexible languages that allow meta programming – changing classes on the fly.

Ruby on Rails Grails
Developer audience From beginners Some experience is required
Language constructs Readable, sometimes like natural language Readable, sometimes too many brackets (but as I said, I spent some time in Ruby :o)
Mapping objects to database Excellent, all declarative Excellent, all declarative
Libraries Wide variety of libraries and plugins. Almost everything I needed was available (except for Kerberos support…) Huge amount of java libraries could be used together with Grails. This is one of Grails killer features.
Scaffold The default scaffold looks terrible, custom plugins needs to be installed. Looks nice out of the box, implements handy nice features like table sorting.
Tools rake (~make), rjs (ruby java script – library that allows to write java script functionality in ruby), migrations (tool that uses ruby syntax to change database schema – very useful) ant (~make)
Log file Very descriptive, it provides exactly the information needed: controller/action/parameters, time spent on DB, VIEW, CONTROLLER, SQL statements (including timing) Verbose… very. Exception generated 1000 lines of code in log, missing the information about SQL statements and the things that are in the Rails log. This was a disappointment.
Console Simple terminal window working in command/result mode Window based – command answer is displayed in different frame at the end of it (unfortunately it is not scrolling properly, so it is quite annoying). I take it as a temporary problem.  
Folder structure Simple, follows the MVC Following MVC, slightly more complex than RoR
Thread support Poor Native

Production

Ruby on Rails Grails
Resource usage Medium resource usage Higher resource usage

Potential

Ruby on Rails Grails
Internet High potential. The framework allows fast development of an application with a very good performance. High potential. It alows fast development and it can utilize all of the Java frameworks.
Enterprises Just for prototyping or small applications. The support of enterprise technologies is not at the focus of the community. Could be used for prototyping and also for real applications

Last, but not least, the popularity of both frameworks:

There are many more differences that were not mentioned here. If you find a major one not mentioned here, please leave me a comment. I will appreciate it.

Ad-hoc fulltext search in RoR ActiveRecord

I came to a situation where I needed to search my Active record, but I did not know which field contains the information. The solution with Ferret was just three steps away…

Let’s say, you want to search Stories for ‘Giant’ keyword. You have to create a Ferret index in memory (ferret gem needs to be installed), index all active records and gather all IDs matching the keyword.

1
2
3
4
5
6
7
index=Ferret::I.new
 
Story.find(:all).each { |s| index << {:id=>s.id, :content=>s.inspect} }
 
index.search_each('Giant', :limit=>100) do |id, score| 
  puts "Active record ID: #{index[id][:id]} with score #{score}"
end

… now you have the full power of the Ferret engine in your hands.

Utilizing Caches.rb with Ferret

We needed to cache a Ruby class method calling the Ferret indexing engine.
Yurii Rashkovskii developed a great library called Caches.rb.

When I googled it out, it seemed very simple to use and promised to do EXACTLY what I need (even the default timeout was JUST IT). I especially liked the very Rails-like tutorial Don’t tell, show me!. However, it required quite some effort to make it work, mainly because of the rather sparse documentation. Still, in the end the usage is very elegant, the solution is simple and it does what it promises. Thank you, Yurii!

To help our esteemed readers get faster over that less agreeable middle phase, here are a few tips:

  • Downloading it: I tried gems but the gem list server seemed to be overloaded, and when it worked at last, I just got an older version (0.2.0). When checking out (or exporting) version 0.4.0 from SVN trunk directly, the trick was in finding out the latest working SVN URL:
    ruby script/plugin install http://svn.verbdev.com/rb/caches.rb/trunk

  • With all the typical Rails mixin stuff petrified in my mind, it took me a while to notice that caching should be declared AFTER the definition of the method to be cached, and not at the beginning of the class definition.
  • The example in the documentation shows it, but it’s easily overseen.

  • We do use Rails, so I included class_cache_storage Caches::Storage::Global as suggested here
  • For some reason (I suspect my shallow knowledge of Ruby ;-), I did not manage to successfully require ‘caches.rb’ from the plugin installation dir _#{RAILS_ROOT}/vendor/plugins/caches.rb/lib_, so I copied it to _#{RAILS_ROOT}/lib_, which helped.
  • For similar reasons, I had to use a workaround to extend the class definition with caching, instead of the recommended way of extending conf/environment.rb by ActiveRecord::Base.extend Caches::ClassMethods

So our class looks as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
require 'ferret'
include Ferret
require 'caches.rb'
 
class FIndex
  extend Caches
  ...
  def self.search(user=nil, results_per_page=10)
    ...
  end
  class_cache_storage Caches::Storage::Global
  class_caches :search
end

N.B.: The class to be cached works with Ferret and not the DB, so it did not inherit from ActiveRecord.

  • For a pure RoR developer, the terminology may be a little confusing (which indicates that Yurii can deal with more programming languages than just Ruby):
    • class methods mentioned in the library description are obviously just a shorthand for “the methods of a class”
  • in Yurii’s documentation, Ruby class methods are called static methods (as known in C/C++ or Java), and their caching is supported by class_caches (feature not available in caches.rb 0.2.0 from gems or RubyForge, but included in the more recent versions from SVN, like 0.4.0 we are using)