CATEGORY: Ruby On Rails

If you are using Amazon Web Services or Product Advertising API, Amazon probably had been bugging you to sign your requests by August 15.

If you have not, by now all your requests should have failed. Some of you who are using amazon-ecs have contacted me last week, if there’s a patch to solve this issue; yep there is, the latest version, amazon-ecs-0.5.6 is able to sign your requests. Thanks to Dan Milne for the initial patch; I have also applied a few small patches to ensure that it works smoothly.

sudo gem update amazon-ecs

To sign the request you would need AWS secret key, which you can generate from Amazon associate account page.

# Include your secret key
Amazon::Ecs.options = {:aWS_access_key_id => [your developer token], :aWS_secret_key => [your secret access key]}

# Or you can also set it through 'configure' method
Amazon::Ecs.configure do |options|
    options[:aWS_access_key_id] = [your access key]
    options[:aWS_secret_key] = [you secret key]
end

# Search like usual
res = Amazon::Ecs.item_search('ruby', {:response_group => 'Medium', :sort => 'salesrank'})

Some weeks ago, I heard about Redis from Chu Yeow’s tweet. Redis is a key-value database, similar with memcached, but with the persistence feature. And one of the first things that popped up into my mind was, this is going to be handy to extend Singapore National Library Board (NLB) catalog search on BookJetty.

So I stopped my adventure with iPhone for a while to work on this hack. After all, BookJetty was built to be a proxy to plug-in other catalogs easily. With the plug, all vertical features found in BookJetty such as importing books to your shelves, tagging, book reviews, ratings, the social networking features, Google Books preview, author pages, cross libraries search, and other vertical features will be immediately available for the catalog. Well, with just a plug or an adapter.

BookJetty Services

I was lucky that Redis 0.900 (1.0 RC 1) was just released, though not a production release, I took my chance. It was very Ruby to work with Redis; download the codes, read the documentation, bam bam and you got it working, one of the reasons why I chose Redis over Tokyo Cabinet (What a name!). More about the differences between Redis and Tokyo Cabinet here.

There is an issue with NLB search result, it does not return ISBN and some other information BookJetty needs to perform the matching and to display the information nicely. Thus, it needs a second call, and it is expensive. A fast database is required to cache this kind of information, and Redis is the answer.

Previously on BookJetty, you can only search Amazon catalog, and BookJetty will find matches in NLB catalog based on the book ISBNs. But due to data discrepancies, some books found in Amazon are not found in NLB.

As a library user myself, I had always wanted to search the other way round. And today, I’m glad to announce to you, and I hope you are as excited as me. We can now do the other way round; search NLB catalog, and BookJetty is smart enough to find matches in Amazon and probably from other sources in the future. I dreamt about this, and today that dream is fulfilled. Click image or here for a sample search.

NLB Search

There is one limitation though, the search API does not sort by the newest books as in NLB online catalog. Hopefully there will be an upgrade to the API soon, and we would be close in our quest to find the holy grail for our local library search. Meanwhile, let’s cherish what we have.

May the books be with you.

permalink_fu has been great to create a Search Engine Optimized (SEO) friendly URL, but I was a bit irritated when the-permalink-is-getting-too-long-with-too-many-unecessary-keywords.

From some of the SEO tips I read, it is advisable not too put too many keywords in the URL. Unecessary stop words such as ‘the, a, in, on, and the list goes on’ can be removed, and the search engine robots will not be irritated too.

I wrote permalink_fu_hack plugin some time ago to solve this problem, thought some of you may find it useful in your application. To think about all the possible stop words, I took the shortcut, and got the inspiration from SEO Slugs Wordpress plugin by Andrei Mikrukov.

It is a monkey patch for permalink_fu, you need to first install the permalink_fu plugin. It also has an additonal :unique option to turn off the feature to automatically create unique permalink, which on some cases where the permalink already includes a unique primary key id, I find that the extra check is not necessary. With the :unique option, you can set it to false for such occasion.

Installation

  script/plugin install git://github.com/technoweenie/permalink_fu.git
  script/plugin install git://github.com/jugend/permalink_fu_hack.git

Example

  class Article < ActiveRecord::Base
    has_permalink :title, :filter_stop_words => true,
        :unique => false
  end

  article = Article.new
  article.title = 'The Permalink Is Getting Too Long With Too Many Unecessary Keywords'
  article.save
  puts article.permalink     # => 'permalink-getting-long-unecessary-keywords'

Additional has_permalink Options

  :unique               
     # Create unique permalink (default: true).
  :filter_stop_words 
     # Flag to filter to stop words (default: false)
  :stop_words         
     # Array of stop words to filter (default: auto assigned to stop words similar to SEO Slugs Wordpress Plugin).
  :max_words         
     # Maximum number of words to show on permalink (default: 6)

Testing flash.now in Rails

TUE, 22 JAN 2008

I know this is an old problem, but I was still not satisfied with the current way of testing flash.now messages. If you love writing test cases, I’m sure that you will bump into the problem of testing flash.now message in your controller.

We usually use flash.now to display a message for an immediately rendered page, meaning not redirected:

flash.now[:notice] = 'You gotta be kidding me!'

The :notice key-value pair will be cleared once the action has been performed, and when you perform your test, flash.now[:notice] will return null.

# flahs.now[:notice] returns nil
assert_equal 'You gotta be kidding me!', flash.now[:notice]

# or in rspec
flash.now[:notice].should == 'You gotta be kidding me!'

So, what do we do now? The good news is Rails Wiki suggested two ways, which I’ll probably add the third way shortly after this:

First is to use assert_select:

assert_select "div.message", "You gotta be kidding me!"

The problem with assert_select, it needs to render your view, but often we just want to test the message without rendering the view, rendering view may throw errors especially if you are testing with mock objects.

The second way was suggested by Justin Gus, through his flashblack plugin, pretty nifty I thought, because it meets the objectives in which he stated:

  • The solution should only have effect in tests. It should not implicitly weave its way into production behavior.
  • I should be conscious when the behavior is in affect.
  • It should require very little effort on my part to enable the feature
  • It should be simple

Sample codes with flashback plugin:

flashback
get :index
assert_equal 'You gotta be kidding me!', flash.flashed[:notice]

But there’s one problem I was facing, I don’t like the call the extra flashback method.

So here is my attempt through a simple monkey patching of FlashNow and FlashHash class. The patch will store flash.now messages into flash[:now], which can be easily referred in your test codes, through flash.now_cache.

  1. Append this code snippet to your /test/test_helper.rb file or /spec/spec_helper.rb if you are using RSpec.
    module ActionController
      module Flash 
        class FlashNow
          def initialize(flash)
            @flash = flash
            @flash[:now_cache] = {}
          end
          
          def []=(k, v)
            @flash[k] = v
            @flash.discard(k)
            @flash[:now_cache][k] = v
            v
          end
        end
        
        class FlashHash
          def now_cache
            self[:now_cache] || {}
          end
        end
      end 
    end
    
  2. And now you can test flash.now mesages through:
    # Unit Test
    assert_equal 'You gotta be kidding me!', flash.now_cache[:notice]
    
    # RSpec
    flash.now_cache[:notice].should == 'You gotta be kidding me!'
    

Would welcome suggestion to improve this further. Thanks, and hope it will be helpful to you.

I was playing with SOAP4R again lately, as I needed to connect to a web service, and the experience had been smooth, from the code generation using wsdl2ruby up to the part when I tried to connect to the service.

My SOAP request could not be translated by the server, because SOAP4R will automatically append n1, n2, n#…namespace to your elements or attributes, and the service that was expecting elements without n# namepace.

 <n1:ServiceContext xmlns:n1="http://domain.com/name">
      <n1:UserName>x</n1:UserName>
      <n1:UserAccessKey>x</n1:UserAccessKey>
      <n1:Password>x</n1:Password>
 </n1:ServiceContext>

I thought, to fix this should be easy, by changing some settings when generating the request document, but the problem was, where should I set.

Not being able to find any clue in the rdoc and SOAP4R how-tos, forced me to code-trace like a British spy, but thanks to ruby-debug for making it easier.

And here is the trick, to use default namespace or to remove the default n1, n2, n# namepace, when calling SOAP::RPC::Driver#add_[rpc|document]_operation method, set the elementformdefault or attributeformdefault option to true, for element and attribute namespace handling respectively.

If you are using wsdl2ruby to generate your SOAP::RPC::Driver, edit [ServiceName|default]Driver.rb and append the options to each of the methods generated:

Methods = [
  [ "http://domain/ws/ServiceName:Operation", "operationMethod",
    [ ["in", "operationMethodRequest", ["::SOAP::SOAPElement", "http://domain.com/ws/ServiceName", "OperationMethodRequest"]],
      ["out", "OperationMethodResult", ["::SOAP::SOAPElement", "http://domain.com/ws/ServiceName", "OperationMethodResponse"]] ],
         { :request_style =>  :document, :request_use =>  :literal,
           :response_style => :document, :response_use => :literal, :faults => {},  
           :elementformdefault => true, :attributeformdefault => true  }
    ],
    
    # Other methods
    ....
  ]

If you are like me, most likely you will have quite a number of methods to apply, what you can do is to put a short plug in init_methods to enable that options for all methods, and that, will be nice and DRY.

[ServiceName|'default']Driver.rb
-------------------------------

def init_methods
  Methods.each do |definitions| 
    # set options to use default namespace instead of using n[#]
    opt = definitions.last
    opt.merge!({
      :elementformdefault => true,
      :attributeformdefault => true
    })
    
    # rest of the codes generated by wsdl2ruby
    if opt[:request_style] == :document
      add_document_operation(*definitions)
    else
      add_rpc_operation(*definitions)

      ....              
    end
  end
end

My last Ruby on Rails project, I implemented a full text search using acts_as_ferret (AAF). For the production environment, I used AAF DrbServer for centralised access to the index files, or you probably will scratch your head, bumping into an error message similar to this, due to concurrent manipulation of Ferret index files:

File Not Found Error occured:
tried to open "/[app_name]/[model]\_8e_0.del" but it doesn't exist: <No such file or directory>

It is easy to start/stop AAF DrbServer on Unix environment using a simple shell script as shown in the help page. Creating similar script for windows should not be too difficult, but running it in a command console, is usually a no-no for the system administrator. So the workaround is you have to create a Win32 service for AAF DrbServer.

I scoured the web hoping that someone had done the dirty job, but not my lucky day, the closest I found was an article by Sausheong on how to create a Win32 service on Rails. Picking up from there, I created some scripts that allows you to install/remove Ferret DrbServer as Win32 service through simple commands.

Let’s get started:

  1. Make sure you have installed acts_as_ferret.
  2. Install win32-service gem.
    gem install win32-service
  3. Download ferret_win32_service.zip and extract ferret_service and ferret_daemon to [app_name]/script directory.

    ferret_service script is to install/remove/start/stop your win32 service, while ferret_daemon script is to be callled by Win32 service to start/stop the DrbServer.

  4. To install AAF DrbServer as Win32 service:

    Usage: ruby script/ferret_service install [options]
        -n, --name=NAME          Service name
        -d, --display=NAME       Service display name
        -l, --log FILE           Service log file
        -e, --environment ENV    Rails environment
        -t, --trace              Display stack trace when exception thrown
        -h, --help               Show this help message
    

    Example:

    ruby script/ferret_service install -n [service_name] -e production 
  5. To remove an installed service:
    ruby script/ferret_service remove -n [service_name]
  6. To start/stop a service:
    ruby script/ferret_service start|stop -n [service_name]
    
    // or you can also run:
    net start|stop [service_name]
    

By default, ferret_daemon will create [app]/log/ferret_service_[environment].log file, unless it is overwritten when installing the service using the -l or --log option. The log file will show the trace of the service status, if it is has been started, stopped or currently listening.

ferret_daemon deamon is to be called by win-32 service, but if you need to run in on command console, use can use the -c or --console option:

ruby script/ferret_daemon -c -e [environment]

If you need to create any DrbServer as windows service, you can always take a quick look at ferret_service and ferret_daemon scripts for reference. So fear not for Win32 service.

UPDATE (Nov 27, 2007)

The scripst are now part of acts_as_ferret 0.4.3. Thanks Jens.

UPDATE (Jan 31, 2008):

You may face a problem that the service can’t be started properly after a server reboot, with “..did not respond in a timely fashion…” message in the event log. I faced the same issue as well, banging my head against the wall a couple of times. And this was what I did.

By default, the Win32 service will timeout if it doesn’t complete the start process within 30 seconds; starting rails acts_as_ferret service on windows can be quite slow, especially after server reboot, when there are so many services to be started at the same time.

So I increase the timeout to 120 seconds, just to be sure, as previous attempt to set it at 60 seconds still failed occasionally. Follow to link below on how to do that:
http://support.microsoft.com/kb/839803

The other step that I did as another preventive measure, is to configure the service to restart on failure for the first, second and subsequent failures, and configured the restart service period to be 7 minutes between each failure, to give some ample time for the server to off load the heavy CPU usage after reboot. You can do that by double click on the service name from control panel, under the Recovery tab.

And lastly, good luck. :)

We just had our monthly Ruby Brigade meeting last night, and I shared for about how to create an easily customisable widget with Ruby On Rails (RoR).

BookJetty Widget

A simple framework that I used when creating BookJetty widget. Web widget has been like bread and butter for web 2.0 sites, it’s an amazing tool that will help to spread the word for your site, and with a good implementation will nurture happy and active users.

Creating a simple widget is easy, but how can you create one that can easily extended and customised to cater for more customisation and different types widgets in the future.

The easy way to create customisation parameters is by using URL paramters, but that can be confusing for your users, and make it hard for your users to customise it on the fly without having to use your widget customisation form.

Compare this widget URL:

<script language="javascript" type="text/javascript" src="http://www.domain.com/widget.php?username=nicky&show=random
&header=1&num=5&covers=small&text=all&tag=alltags&css=1&style=1&charset=&version=1">
</script>

And Google Adsense widget URL:

<script type="text/javascript"><!--
google_ad_client = "pub-32432432432432";
google_ad_width = 468;
google_ad_height = 60;
google_ad_type = "text";
google_color_border = "ffffff";
google_color_bg = "ffffff";
google_color_link = "1465b7";
google_color_url = "1465b7";
google_color_text = "000000";
//--></script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>

Google Adsense widget approach is just like the Amazon widget approach, it is using Javascript variables, and they are so much easier to customise, as compared to URL parameters.

These slides will help you to look at how I approached the widget creation following the best practices of the big guys, Google and Amazon widgets, and hope they will shed some lights. And happy widgeting.

A few months ago, when I migrated BookJetty from Java to Ruby On Rails (RoR) platform, one obstacle I faced was I could not find any object pooling library that checks for idle objects on a fixed interval, and evicts invalid and expired objects in the pool.

While in Java I used Apache Commons Pool, in Ruby, I was scratching my head back then.

But, the beauty of Ruby-and-Rails world is the citizen love to hack, when they can’t find a way, well, they always hack a way out. So being a good citizen wannabe, I wrote the Ruby version of first-in-first-out (FIFO) object pool library inspired by Apache Commons Pool.

It currently supports the following configuration parameters:

  • min_idle
  • max_idle
  • max_idle_time
  • max_active
  • timeout
  • validation_timeout
  • idle_check_no_per_run
  • idle_check_interval

This morning I’ve just got the time to wrap it up as a gem. To get started:

$ gem install common-pool

And some code snippets:

require 'common_pool'    
  
# Extend data source object
class RandomNumberDataSource < CommonPool::PoolDataSource
  # Overwrite to return object to be stored in the pool.
  def create_object
    rand(1000)
  end
    
  # Overwrite to check if idle object in the pool is still valid.
  def valid?(object)
    true
  end
end
  
# Create a new object pool
object_pool = ObjectPool.new(RandomNumberDataSource.new)
  
# Borrow object from the pool
object = object_pool.borrow_object

# Return object to the pool
object_pool.return_object(object)
  
# Or invalidate object and remove it from the pool
object_pool.invalidate_object(object)
  
# Create object pool with idle objects eviction thread
object_pool = ObjectPool.new(RandomNumberDataSource.new) do |config|
  config.min_idle = 5
  config.max_idle = 10
  
  # max 10 idle objects checked per run
  config.idle_check_no_per_run = 10

  # check idle objects every 10 minutes
  config.idle_check_interval = 10 * 60
end

# Return a hash of pool instance status variables, i.e.
# active and idle lists size, and configuration options
object_pool.status_info

For more information, visit common-pool rdoc documentation.

RubyWe are meeting up again, this time we manage to get to a proper room with Wi-Fi connection at Central Lending Library, instead of the usual corner at Starbucks.

Thanks to Doug for organising this meetup, and to Ivan Chew, from Singapore National Library Board (NLB) for facilitating the use of this room.

It is the first time we are having presentions, and two of our top brigades are presenting, here is the agenda:

After that, you are forced to hang out a little longer, to get to know each other, have a good discussion and a good time learning from each other.

So free up your time next Thursday, 19 April, 7:00 pm, and see you at Central Lending Library; meeting room is to be confirmed, but you should see one of us at the lobby, if you don’t, we believe you will hack out a way to find the right room.

Hint: Contingency plan, note down my HP number.

I love Ajax.InPlaceEditor, cause it allows me quickly edit a field by just clicking on the text value. If you have used Flickr, I am pretty sure you know what I meant.

It is easy to create an in-place-editor if you are using Ruby On Rails (RoR).

  # Controller
  class BlogController < ApplicationController
    in_place_edit_for :post, :title
  end

  # View
  <%= in_place_editor_field :post, 'title' %>

Problem

The problem with InPlaceEditor is when you enter an empty value, you lost the text where you can click to edit.

One way is to extend Ajax.InPlaceEditor to display “click to edit…” text if the value is empty.

But on some cases, that is not what you want. For example if the text field is a numeric value, you would want your model class to peform the ActiveRecord validation and display the validation message if user enters an invalid value. And you probably would not want to re-code the validation rules in Javascript.

Solution

This is a hack that extends ActiveController::Base to include an additional class method in_place_edit_with_validation_for, which basically displays an alert box containing the model error messages is it is not valid.

And this is how you go about doing it:

  1. Create custom_in_place_editing.rb and save it in [app_root]/lib directory.
    module ActionController
      module Macros
       module CustomInPlaceEditing
        def self.included(base)
         base.extend(ClassMethods)
        end
       
        module ClassMethods
         def in_place_edit_with_validation_for(object, attribute)
          define_method("set_#{object}_#{attribute}") do
           klass = object.to_s.camelize.constantize
          @item = klass.find(params[:id])
          @item.send("#{attribute}=", params[:value])
           if @item.save
            render :update do |page| 
             page.replace_html("#{object}_#{attribute}_#{params[:id]}_in_place_editor", 
             @item.send(attribute))
            end
           else
            render :update do |page|
             page.alert(@item.errors.full_messages.join("\n"))
             klass.query_cache.clear_query_cache if klass.method_defined?:query_cache
             @item.reload
             page.replace_html("#{object}_#{attribute}_#{params[:id]}_in_place_editor", 
             @item.send(attribute))
            end
           end
          end
         end
        end
       end
      end
    end
    
    ActionController::Base.class_eval do
      include ActionController::Macros::CustomInPlaceEditing
    end
    
  2. Update the following files accordingly:
       # Controller file
       class ProductController < ApplicationController
         in_place_edit_with_validation_for :product, :price
       end
       
       # View file, remember to set the :script option to true
       # Rails 1.2.3
       <%= in_place_editor_field :product, :price, :script => true %>
    
       # Rails on edge
       <%= in_place_editor_field :product, :price, {}, :script => true %>
    
       # [app_root]/config/environment.rb 
       require 'custom_in_place_editing'
    
  3. Restart your server instance.

And that’s it, there is no other step, you are done.