CATEGORY: Ruby On Rails

So you heard of Ruby On Rails (RoR), watched a 15 minutes screencast. And you kind of liked it. You tried coding ‘Hello world’, and you said, “Hey, this is easy”. You thought you should push yourself and try out a more complicated stuff, stuff real developers do.

You wanted to create a few standard panels on your home page, because almost every site you visited lately have panels on their homepage. You wanted each panel to have its own header and footer, probably something like this:

  <div class="panel">
    <div class="header">Panel Name</div>
    <div class="body">Some content here....</div>
    <div class="footer">Panel Footer</div>
  </div>

And you did it real fast, you were done with 6 panels, 2 on each column for a 3-column layout in 2 minutes. But… hm… you did not like your codes, because you see a lot of repeated <div> elements.

You remembered Ruby programming language advocates Don’t repeat yourself. You are a smart guy, you found a solution in no time, “Why not create a helper ‘panel’ method?”“. Then you can do something like this in your RHTML template file.

  <% panel (:header => 'Panel header', :footer => 'Panel footer') do %>
       Some content here...
  <% end %>

That surely looks more beautiful than the codes above. You started to love RoR even more, but… when you tried to create the helper method, you bumped into some problems.

   class ControllerHelper
       def panel(options={}, &proc)
           # How to add panel header <div>s?
           
           # Generate content
           yield 

           # How to add panel footer <div>s?          
       end
   end

You were a Java web developer, your first instinct is to find the response outputstream. You found the closest match, 'response.out' but it did not return the template output, instead it passed you a standard output.

You read about render :text or render :inline, you tried them too, but they throwed an error saying that you should not call render statement more than once.

You asked uncle Google for ‘rails response outputstream’ and you tried other questions too, but you could not find an answer.

You were getting frustrated, but you thanked God that people still use Internet Relay Chat (IRC) these days, and you found #rubyonrails channel flooded with people. You asked your question, but no answer, you asked again, and again, no answer. Frustrated, tired, and dehydrated, you started to hallucinate that you probably was a ghost, you saw your own message, but others did not.

It got nasty this time, you started screaming words you did not want your mum to hear, ‘WTF’, @#%@$%^*!!!.

And your did not love RoR anymore.

By luck, fate, chance or whatever it was, you probably found this page, coz there was another guy like you, faced a similar issue, he found a solution, and he shared his solution. He said you probably want to do it this way:

class ControllerHelper
     def panel(options={}, &proc)
          # add panel header to current template IO stream
           concat("<div class='panel'><div class='header'>#{options[:header]}</div><div class='body'>", proc.binding)
           
           # Generate content
           yield 

          # add panel footer to current template IO stream
           concat("</div><div class='footer'>#{options[:footer]}</div></div>", proc.binding)
       end
end

And it worked like magic, you started to read more about ActionView::Helpers::TextHelper#concat, more about Binding, and you started to understand that RoR behaves differently in this aspect, coz the RHTML template file is compiled by ERB library, and concat method helps you to retrieve the RHTML output sting buffer through the ‘Binding’ object and the variable _erbout.

Shortly after, you understood more about Ruby and RoR, and your love for Ruby and RoR started to grow again. You called it a day, a happy man because you found a solution to your problem.

You promised yourself that you would want to share solutions to problems you are facing in the future, just like what that guy did. And you hoped for a better world, a world where less of us have to say words our mum would not like to hear.

When I planned to migrate BookJetty from Java to Ruby On Rails (RoR), I looked around for a ruby library to access Amazon E-commerce Service (ECS).

The first library I found was Ruby/Amazon, a very easy to use API, but supports only version 3.1 API. While I need the latest version, ECS 4.0.

There are no other ECS ruby library out there, so I turned to SOAP4R, afterall I used SOAP on Java platform.

A happy man I was, but not for long. Coding with SOAP generated classes was not so pretty, when an element is not available, calling the attribute will throw method not found exception, so you always need to check if an attribute is available or not. What matters most is Ruby SOAP performance is quite slow, especially when retrieving large data set.

So I needed a Ruby Amazon E-commerce REST Service API that supports ECS 4.0. Since I couldn’t find it, I wrote the codes 2 days ago, I have just open-sourced it as amazon-ecs. Happy Amazoning!!

Click here to start exploring amazon-ecs.

I know this is trivial, but if you are new to RoR, you may get confused, coz they all come in different shapes and sizes.

For mongrel (other options taken out for clarity purpose):

mongrel_rails start -e production

For rake command:

rake [task] RAILS_ENV=production

For ruby console:

ruby script/console production

You can use Module as a helper to store common methods to be shared across different classes.

By including the module in your class, you inherit its methods. But, problem pops up, when on some cases you need to call the helper method from a class mehod instead of an instance method. And you know you can’t call an instance method directly from a class method.

So this was what I did.

module Helper
    self.included(base)
        base.extend(self)
    end

    def hello
        "Hello world"
    end
end

class Service
    include Helper

    def instance_call
       hello
    end

    def self.class_call
       hello
    end
end

Service.new.instance_call # returns 'hello world'
Service.class_call        # returns 'hello world'

Exploit TechPluit has just embarked on a new Ruby On Rails project with Exploit Technologies Pte. Ltd., a commercial arm of
Agency for Science, Technology and Research (A*STAR) that helps to identify, protect and exploit Intellectual Property (IP) created by the Research Institutes (RIs) funded and managed by A*STAR.

This project marks the beginning of Pluit’s involvement in commercial project using Ruby On Rails (RoR), and Pluit’s second RoR project after BookJetty (the next release, coming up real soon). Although I am happy for this achievement, but behind it, it was a tough job to push RoR to the corporate world who likes the word ‘Enterprise’.

You can say RoR is still relatively new, as version 1.0 was released in December 2005; but in a hot open-source world — a year is relatively very long. Lots of stuff can happen within a year, just look at how much RoR has changed since v1.0.

In fact, RoR has spread like a wild-fire in US, Japan, Europe and Canada. But in Singapore, people and companies are still very careful about RoR. So questions like this are common, as I was also asked:

“Who is backing Rails and Ruby?”

“Are they going to be another abandoned open-source project in the next few years?”

Well, you can’t blame them, there’s a common saying here that says, You don’t love, because you don’t know. But does BIG always mean good? How about looking at EJB 1.0 and 2.0, JSTL, and Java Server Faces, are they good? I am not sure about you, but people do question, even though it is backed by big company. And I meant really BIG company.

With this project going, I hope in the next couple of months, there will be greater acceptance for RoR. And speaking from devleoper’s perspective, it is NOT the end result that counts, but how do you achieve the result that really counts. With RoR, all I can say is HALLELUJA.

It all started with Nick asking a question like this in Singapore Ruby Brigade. How come?

String === String
=> false
# but
String == String
=> true

Let’s start with the easy one first:

Equality (==, eql?, equal?)

#== is the common equality method that compares object values, ignoring the object id. #eql? is the alias of #==, so they are the same. Btw the “#” prefix means it is an instance method (just in case), without the prefix, it means a class method.

The stricter version of is #equal? compares the object id. Example:

"test" == "test"         
# returns true, both have the same value, i.e. "test"

"test".equal?("test")  
# returns false, because "test" (left) is a different object from "test" (right), where "test".__id__ != "test".__id__

Case Equality (===)

=== is the case equality method, in English it means it is called internally when you use ‘case’ statement, but of course, it can also be called outside the case statement.

=== method functions differently, depending on the caller class type.

If you call it on a Class, it will check if the compared object is an instance/descendant of the class. On Object, it is the same with #== method. Please refer to Ruby documentation for more information on how it performs on different class types.

String === "test"    
# returns true because "test" is an instance of String

"test" === "test"
# returns true, because both objects have the same value or "==" returns true

"test" === "different test"
# returns false, because the two objects have different values.

Just a bit more spice, on Ruby on Rails (RoR) framework, the Range class overrides “===” method to returns true if the compared value is found in the in the range values.

1..10 === 1    # returns true
1..10 === 11   # returns false

case var
   when 1..10
       "do some stuff here"
   when 11..20
       "do something else here"
   else
       "out of range"
end

So back to the one-million-dollar question, why then String === String returns false?

Calling String in the runtime environment, returns a Class object that represents String class type. If you call String.__id__, it will return the object id of Class object. Thus:

String === String
# returns false, because String (right) is treated as a Class object, passed as an argument to String  "===" method.

And… I think you get it, obviously Class object is not an instance of String class. Sweet.

And why String == String returns true?

You should be able to answer it by now. =)

Overriding Methods in Ruby

THU, 5 OCT 2006

If you are new to Ruby, you may bump into some confusion when overriding methods, especially when you still need to refer to the old or parent methods. Just to share some notes I have taken on overriding methods in Ruby.

You can override a method by either extending a class or by re-declaring an exisiting class with just the new methods.

Let’s dive into codes, Ruby codes speak a thousand words.

Overriding Parent Instance Method

This is the typical way in Object-Oriented programming, by extending a class.

class Parent
    def call_method
        'parent'
    end
end

class Child < Parent
    def call_method
        super + '-child'     #return: 'parent-child'
    end 
end

Overriding Parent Class Method

Class methods are a bit more confusing, because there are different ways to declare them. Well, welcome to ruby, there are a lot of magic here.

There are three ways to declare class methods:

class ClassName
    def ClassName.method_1
        ...
    end

    def self.method_2
        ...
    end

    class << self
        def method_3
            ...
        end

       def method_4
           ...
       end
    end
end

To override a class method:

class Parent
    def self.call_method
        'parent'
    end
end

class Child < Parent
     def self.call_method
         superclass.call_method + '-child'    #return: 'parent-child'
     end

end

Overriding Method of Existing Class

To override a method in an existing class, you just have to re-declare the class with the new methods. If you need to call the old method in your new method, you first have to create an alias of for the old method. You can use either alias or alias_method. This is something that I dont’ see in Java, pretty cool.

Note that you don’t need to redeclare Parent extension. And also note the difference in declaration between alias and alias_method; alias is a reserved keyword in Ruby, while alias_method is just a function call.

class Child
  alias old_call_method call_method

  def call_method
     old_call_method + '-new'   #return 'parent-child-new'
  end
end

class Child
  alias_method :old_call_method, :call_method

  def call_method
     old_call_method + '-new'   #return 'parent-child-new'
  end
end

Or you can also do it this way:

class Child
   alias_method :old_call_method, :call_method
   alias_method :call_method, :new_call_method

   def new_call_method
     old_call_method + '-new'   #return 'parent-child-new'
   end
end

# If you are using RailsOnEdge, this is the shortcut 
class Child
   alias_method_chain :call_method, :extra
  
   def call_method_with_extra
        call_method_without_extra + '-new'    #return 'parent-child-new'
   end
end

BookJetty on Ruby On Rails

MON, 25 SEP 2006

After downloaded the latest version of Hibernate, Spring and Acegi Security, configuring the long list of XML configurations, just about to start working on the next enhancement for BookJetty; I decided to make a U-turn to re-code BookJetty from scratch again using Ruby On Rails (RoR).

I know it is crazy, and it sounds like a lot more work at first thought, but my developer instinct assured me, with RoR, I’ll be able to save a lot more time in the future. Besides, I’ll be able to learn more about RoR, so what the heck.

Strange, somehow, I get more excited.

If your tables have created_at/created_on and updated_at/updated_on columns, Ruby On Rails (RoR) framework is smart enough to auto-assign the date/time value, without you writing a single line of code.

That’s great. But when you need to keep track of created_by and updated_by, you have to manually update your controllers to set the values. This obviously contradicts with Don’t Repeat Yourself (DRY) principle of Ruby. So, as a good and obedient Ruby programmer, let’s work around that.

For a start, RoR wiki is the best place to find information, it provides a write up on how to enhance ActiveRecord::Base to auto update created_by and updated_by. The tricky part is how do you retrieve the currently logged-in user, which is usually only exposed in controller and view layers, but not the data access layer.

If you have read the article, it shows a clever way by adding a class method (current_user) to user.rb, and updating aplication.rb to set currently logged-in user:

user.rb

cattr_accessor :current_user

application.rb

before_filter do |c|
    User.current_user = User.find(c.session[:user]) unless c.session[:user].nil?
end

This way however is prone to concurrency issue, because RoR Dispatcher is not thread-safe and class variable is a single instance variable. Thus concurrent thread executions may provides current_user value with the other concurrently-executing-logged-in user.

Another way is to use Thread.current. With it, you can store a short-live variable value for the executing thread, and it is thread-save.

Now, you only need to update the application.rb and leave user.rb intact. Copy usermonitor.rb in to the /lib directory and you are done, a RoR framework that auto-updates created_by and updated_by, isn’t RoR wonderful?

application.rb

class ApplicationController < ActionController::Base
  ....

  before_filter :set_current_user
  
  def set_current_user
    Thread.current['user'] = session[:user]
  end
end

usermonitor.rb

module ActiveRecord
  
  module UserMonitor
    def self.included(base)
      base.class_eval do
        alias_method_chain :create, :user
        alias_method_chain :update, :user

        def current_user
          Thread.current['user']
        end
      end
    end

    def create_with_user
      user = current_user
      if !user.nil?
        self[:created_by] = user.id if respond_to?(:created_by) && created_by.nil?
        self[:updated_by] = user.id if respond_to?(:updated_by)
      end
      create_without_user
    end
    
    def update_with_user
      user = current_user
      self[:updated_by] = user.id if respond_to?(:updated_by) && !user.nil?
      update_without_user
    end
    
    def created_by
      begin
        current_user.class.find(self[:created_by]) if current_user
      rescue ActiveRecord::RecordNotFound
        nil
      end
    end
   
    def updated_by
      begin
        current_user.class.find(self[:updated_by]) if current_user
      rescue ActiveRecord::RecordNotFound
        nil
      end
    end
  end
end

Please note the following changes to usermonitor.rb as compared with the version found in the wiki:

  • Use alias_method_chain for cleaner code, it overrides ActiveRecord::Base#create to call create_with_user, and link the previous ActiveRecord::Base#create method with create_without_user.
    alias_method_chaing is currently only available for for RoR on Edge
  • Overriding ActiveRecord::Base is no longer necessary due to auto-retrieval of model name through current_user.class, which assumed to return the user object instead of just the user id.

Finally, remember to add the following codes to extend ActiveRecord::Base with UserMonitor:
config/environment.rb

require 'usermonitor'
ActiveRecord::Base.class_eval do
    include ActiveRecord::UserMonitor
end

UPDATE: After delving with RoR for a while, I found out that Rails is based on CGI request model, thus there is not thread safety issue, and you don’t necessarily need to use Thread.current to store your current user, and an instance variable is as good.

When doing functional test, sooner or later you will need to do some testing with cookie, for example when you need to test the infamous ‘Remember me’ functionality.

It is easy to do that in Rails with some points to take note:

To set a cookie in a request:

@request.cookies['name'] = CGI::Cookie.new('name', 'cookie value')

To retrieve a cookie after a response:

cookies['name']
Note: Use a string for cookie name, symbol somehow returns nil in functional test.

And a sample of Remember Me functional test:

def test_login_with_remember_me
    post :login, :username => 'herry', :password => 'passwd',
        :rememberme => '1'
    assert session[:user]
    assert cookies['auth_token']
    assert_response :redirect
    assert_tag :tag => 'div', 
        :child => /[replace with your error message]/

    # reset user session and set request cookie
    session[:user] = nil
    @request.cookies['auth_token'] = cookies['auth_token']    
  
    get :protected_page
    assert_response :success
    assert session[:user]
end