Tao Programmer

"What I follow is Tao - beyond all techniques! When I first began to program I would see before me the whole problem in one mass. After three years I no longer saw this mass. Instead, I used subroutines. But now I see nothing. My whole being exists in a formless void. My senses are idle. My spirit, free to work without plan, follows its own instinct. In short, my program writes itself. True, sometimes there are difficult problems. I see them coming, I slow down, I watch silently. Then I change a single line of code and the difficulties vanish like puffs of idle smoke. I then compile the program. I sit still and let the joy of the work fill my being. I close my eyes for a moment and then log off."

Tao of Programming - 4.4

Generating PDF from HTML on RoR

Well, historically generating PDF on Ruby on Rails is not an easy task.
We used to use PDF::Writer or Prawn and learn it's syntax to generate PDF.
Now we can generate pdf for every html.erb template that we have, with the help from wkhtmltopdf and wicked_pdf plugin.

I will show you how to generate PDF for every html.erb template on a particular RoR application.

First, you have to have wkhtmltopdf binary.

http://code.google.com/p/wkhtmltopdf/downloads/list

Linux users, you'll want to download the static binary, unless you want to recompile Qt :)
Put that binary somewhere on your machine.

Now on a RoR application, install the plugin:
ruby script/plugin install git://github.com/mileszs/wicked_pdf.git
ruby script/generate wicked_pdf

Edit config/initializers/wicked_pdf.rb file so it is pointing to the wkhtmltopdf binary on your machine. This is the file content on my machine:

WICKED_PDF = {
  #:wkhtmltopdf => '/usr/local/bin/wkhtmltopdf',
  #:layout => "pdf.html",
  :exe_path => '/usr/bin/wkhtmltopdf'
}

Ok, now all the preparations has been done, let me show you how to use it on a controller.

def new
  @movie = Movie.new
  respond_to do |format|
    format.html # new.html.erb
    format.pdf do
      render :pdf => "file_name",
             :template => "movies/new.html.erb"
    end
    format.xml  { render :xml => @movie }
  end
end

Here is the sample file generated:
file_name.pdf

Rails 3

Now that rails 3 has entered beta phase, probably this is a good time to start learning about rails 3.

The official announcement:
Rails 3.0: Beta release

Installing rails 3
sudo gem install tzinfo builder memcache-client rack rack-test rack-mount erubis mail text-format thor bundler i18n
sudo gem install rails --pre

This is the canonical changes to rails 3:

Rails 3.0: Release Notes

This is a great introduction to rails 3:
The Path to Rails 3: Introduction
The Path to Rails 3: Approaching the upgrade
UPGRADING AN APPLICATION TO RAILS 3 — PART 1
Notes from the field upgrading to Rails 3

Rails 3 reading material:
Rails 3 Reading Material
Getting Up To Speed With Rails 3

Rails 3 compatible gem and plugin:
Rails Wiki


Apparently mongrel doesn't work with rails 3 yet, and also unicorn, the promising new kid on rails webserver battle doesn't work too:
Subject: [Rails] Edge Rails 3: Mongrel stuck in infinite loop, Unicorn missing uninitialized constant Rack::Runtime - msg#00437

So, you might want to use passenger 2.2.9 as rails webserver since it supports rails 3 already:
Phusion Passenger 2.2.9 released

Changes to rails 3 router & controller:
Revamped Routes in Rails 3
The Rails 3 Router: Rack it Up
What's New in Edge Rails: Default RESTful Rendering
What's New in Edge Rails: Set Flash in redirect_to
Generic Actions in Rails 3
Render Options in Rails 3

HTML escaping is now the default behavior:
SafeBuffers and Rails 3.0

New activerecord finder, you should really read this since a lot of finder will be deprecated:
Active Record Query Interface 3.0

New activemodel, basically this makes a model without a database table:
ActiveModel: Make Any Ruby Object Feel Like ActiveRecord

New actionmailer API:
New ActionMailer API in Rails 3.0

Edit:

Rails 3.0 Beta: 36 Links and Resources To Get You Going


Have another good link about rails 3? Feel free to comment.

ActiveRecord Multiple Parameter Finder

Building ActiveRecord finder is simple.

User.first
User.last
User.all(:conditions => ["age > ?", params[:age]])

We can even combine it with name scoping

class User < ActiveRecord::Base
  named_scope :older_than, lambda {|age|{:conditions => ["age >= ?", age]}}
end

After we make named scope, we can chain it to regular finder.

User.older_than(params[:age]).last

The problem is when we need a query based on unknown number of parameters. For example we may have built a form to search certain user with several fields, but we cannot force user to fill all the fields.

Usually the query built will be like this:

User.all(:conditions => [
  "age = :age AND address LIKE :address AND username LIKE :username",
  {:age => params[:age], :address => "%#{params[:address]}%",
   :username => "%#{params[:username]}%"}]

But what will happen if one of the parameters is blank?

The solution is build dynamic condition.

conditions = ""
value = {}
unless params[:age].blank?
  conditions += "age = :age"
  value[:age] = params[:age]
end
unless params[:address].blank?
  conditions += " AND " unless conditions.blank?
  conditions += "address LIKE :address"
  value[:address] = "%#{params[:address]}%"
end
unless params[:username].blank?
  conditions += " AND " unless conditions.blank?
  conditions += "username LIKE :username"
  value[:username] = "%#{params[:username]}%"
end
User.all(:conditions => [conditions, value])

We can even refactor that method.

conditions = []
value = {}
unless params[:age].blank?
  conditions << "age = :age"
  value[:age] = params[:age]
end
unless params[:address].blank?
  conditions << "address LIKE :address"
  value[:address] = "%#{params[:address]}%"
end
unless params[:username].blank?
  conditions << "username LIKE :username"
  value[:username] = "%#{params[:username]}%"
end
conditions = conditions.join(" AND ")
User.all(:conditions => [conditions, value])

Tips for Continuing Rails Development from 3rd Party

Sometimes you are continuing the works of 3rd party that is not so good.
Here is how I manage to continue developing the old project.

0. Check the application, play around a little bit until you know the work flow of the project.
1. Check errors when migrating. Sometimes old migrations hadn't been done correctly. You might want to update the migrations, tidy them a little, and make sure there is no fatal bug that might lurk somewhere on the migrations.
2. Check the environment configuration, plugins, gems, stylesheets, javascripts, and other dependencies that might be missing, and update the applications when necessary.
3. Refactor the model. You can refactor the models, remove unused methods (be sure to grep them first), remove unused comments, and sort or group the methods. You can change the models so they use nested attributes, and so on.
4. Look at the controllers. Start from home, and follow the work flow. Usually that means start from registering an user. Look at the methods on the controllers, refactor the controllers when necessary, such as inefficient queries. Shorten them whenever possible and try to get used to the methods. Try to use dynamic finder, named scope, rails helper methods to make sure your application works efficiently. Also remember the general guideline, skinny controller, fat model.
5. When you investigate a method on a controllers, look at the views. This is the crucial part. Make sure you adjust the view according to the proper rails work flow, such as finding objects on controllers, not on the view. On this stage you might want to use plugins that help tidying your application such as bullet (detecting n+1 queries), rails_best_practices, request-log-analyzer.

I hope this post can help you when you are continuing 3rd party works.

Making Windows Installer for Ruby on Rails Application

You'll be needing these applications:
Inno Setup
Inno Setup homepage
ISTool
ISTool homepage
SetEnv
SetEvn CodeProject
Ruby binaries
Ruby FTP
ImageMagick binaries (if you are using imagemagick)
ImageMagick Binary Release, choose the zip archive for windows
MySQL binaries
MySQL Downloads, choose the zip archive for windows

Steps to create installer:
0. Extract ruby to some folder, i.e. c:\ruby\bin
1. Install required gem. Probably you'll notice messages telling missing required dll like iconv.dll, readline.dll, zlib.dll. Search the dll from the net and copy them to the ruby\bin folder. Make sure everything works by creating and running Ruby on Rails application.
2. Extract ImageMagick to some folder
3. Extract MySQL to some folder
4. Install Inno Setup and ISTool.
5. Open Inno Setup / ISTool and make an .iss file
6. OpenAdd the first entry to iss file:
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{4C04F356-5D30-42DA-81F2-78AD49AD47A5}
AppName=Sample Application
AppVerName=1.0.0
AppPublisher=Sample Application
AppPublisherURL=http://www.example.com
AppSupportURL=http://www.example.com
AppUpdatesURL=http://www.example.com
DefaultDirName=c:\installation\
DisableDirPage=yes
DefaultGroupName=Sample Application
DisableProgramGroupPage=yes
LicenseFile=license.txt
OutputBaseFilename=setup
Compression=lzma
SolidCompression=yes
[Languages]
Name: english; MessagesFile: compiler:Default.isl
[Icons]
Name: {group}\{cm:UninstallProgram,Sample}; Filename: {uninstallexe}
Name: {group}\Setup; Filename: {app}\setup.bat
Name: {group}\Start; Filename: {app}\start.bat

I don't recommend using program files for DefaultDirName, since ruby and mysql probably will throw errors if installed on folder named with a space.

Notice that I also make 2 file called setup.bat and start.bat
setup.bat
@echo off
cd C:\installation\rails_apps\some_app_name
set RAILS_ENV=production
rake db:migrate
start.bat
@echo off
cd C:\installation\rails_apps\some_app_name
set RAILS_ENV=production
ruby script/server
7. Adding Directories and Files
You can add directories like this:
[Dirs]
Name: {app}\ruby
[Sources]
Source: ruby\bin\ruby.exe; DestDir: {app}\ruby\bin

{{app}} stands for installation folder

For files, the first path is your current file path. You can use full path like c:\ or relative path.

Add the extracted ruby directory, mysql, and imagemagick, and application source to the installation file. You might want to sanitize the files first by removing unused files such as gem documentation files.
You can add all the file by hand, but adding 1000+ directories and gazillion files isn't worth it. You can drag and drop directories from windows explorer to ISTool. So downloading ISTool actually necessary.

Don't forget to include setenv.exe to the installation files.

8. Add this on iss file:
[Run]
Filename: {app}\SetEnv.exe; Parameters: "-a PATH %""{app}\ruby\bin"""; Flags: runminimized; StatusMsg: Adding ruby PATH
Filename: {app}\SetEnv.exe; Parameters: "-a PATH %""{app}\mysql\bin"""; Flags: runminimized; StatusMsg: Adding mysql PATH
Filename: {app}\SetEnv.exe; Parameters: "-a PATH %""{app}\ImageMagick"""; Flags: runminimized; StatusMsg: Adding imagemagick PATH
Filename: {app}\SetEnv.exe; Parameters: "-a MAGICK_HOME ""{app}\ImageMagick"""; Flags: runminimized; StatusMsg: Adding magickhome PATH
Filename: {app}\mysql\bin\mysqld.exe; Parameters: --install; Flags: runminimized; StatusMsg: mysql service installation
Filename: {sys}\net.exe; Parameters: start mysql; Flags: runminimized; StatusMsg: Starting mysql service
Filename: {app}\mysql\bin\mysqladmin.exe; Parameters: -u root password masterkey; StatusMsg: changing mysql root password; Flags: runhidden
[UninstallRun]
Filename: {sys}\net.exe; Parameters: stop mysql
Filename: {app}\mysql\bin\mysqld.exe; Parameters: --remove
Filename: {app}\SetEnv.exe; Parameters: "-d PATH %""{app}\ruby\bin"""; Flags: runminimized
Filename: {app}\SetEnv.exe; Parameters: "-d PATH %""{app}\mysql\bin"""; Flags: runminimized
Filename: {app}\SetEnv.exe; Parameters: "-d PATH %""{app}\ImageMagick"""; Flags: runminimized
Filename: {app}\SetEnv.exe; Parameters: "-d MAGICK_HOME ""{app}\ImageMagick"""; Flags: runminimized

{{sys}} stands for system32 folder.
run means programs that runned on installation.
uninstallrun means prorams that runned on uninstallation
Actually you can set environment variables such as PATH using registry, but using setenv really makes things easier.

Now you can compile the installation script on Inno Setup or ISTool.
After you get the setup.exe, you can try installing on clean machine.

Upgrading Cucumber on Existing Rails Application

If you are upgrading cucumber gem on existing rails applications, sometimes you meet error on doing cucumber test.

You can fix the errors by doing this step-by-step:
If you are seeing conflicting files, don't overwrite, but diff them first.

From cucumber 0.3.x to < 0.4.3
ruby script/generate cucumber


From cucumber 0.4.3 to > 0.4.3
ruby script/generate cucumber

From cucumber < 0.5.0 to > 0.5.0
sudo gem install cucumber-rails
rm features/support/version_check.rb
cp features/step_definitions/webrat_steps.rb features/step_definitions/backup_webrat_steps.rb
rm features/step_definitions/webrat_steps.rb
ruby script/generate cucumber
Merge your changes on features/step_definitions/webrat_steps.rb to features/step_definitions/web_steps.rb
Then you can safely remove webrat_steps definition.
rm features/step_definitions/backup_webrat_steps.rb
If you are seeing warning messages indicating deprecated feature, comment this line on features/support/env.rb
require 'cucumber/webrat/element_locator' # Deprecated in favor of #tableish - remove this line if you don't use #element_at or #table_at
You have to install database_cleaner gem too.
sudo gem install database_cleaner

Cucumber test output in html format is really nice.


You can produce test result in html format:
cucumber features -f html > somefilename.html

Ruby 1.8.7-p248 has been released

Ruby 1.8.7-p249 for linux has been released.
The changelog shows mainly just bug fixes.
You can get the source code from http://ftp.ruby-lang.org/pub/ruby/
For windows binaries, you can get it from http://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.7-p248-i386-mswin32.zip

For ruby enterprise edition, I have checked github, but apparently no much progress on that yet.
http://github.com/FooBarWidget/rubyenterpriseedition187-248

Autofocus form with prototype

How to make a web page autofocus using prototype

1. include js on header:

hasClassApplied = function(element, classname
  {
    if (element != undefined && (Element.classNames(element) != undefined || Element.classNames(element) != null))
    {return Element.classNames(element).include(classname);}
    return false;
  }

 focusFirstPageInput = function(excludeFormClassname)
  {
    for (i = 0; i < document.forms.length; i++)
    {
      if (!hasClassApplied(document.forms[i], excludeFormClassname))
      {
        focusFirstFormInput(document.forms[i]);  
        return;
      }
    }
  }

 focusFirstFormInput = function(form)
  {
    for (i = 0; i < form.length; i++)
    {
      if (form[i].type != "hidden" && form[i].type != "submit" && form[i].type != "button")
      {
        if (!form[i].disabled)
        {
          Field.activate(form[i]);
          return;
        }
      }
    }  

2. on the body tag, include on onload:

<body onload="focusFirstPageInput('')></body>