Wednesday, 8 August 2007

Association Callbacks

Similiar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get trigged when you add an object to or removing an object from a association collection. Example:
class Project
has_and_belongs_to_many :developers, :after_add => :evaluate_velocity

def evaluate_velocity(developer)
...
end
end

It’s possible to stack callbacks by passing them as an array.

Possible callbacks are: before_add, after_add, before_remove and after_remove.

Should any of the before_add callbacks throw an exception, the object does not get added to the collection. Same with the before_remove callbacks, if an exception is thrown the object doesn’t get removed.

Monday, 9 July 2007

ssh segmentation fault osx

Argh!! Every time I install an update on my MacBook it breaks ssh. Up until now I'd been using Pacifist to extract & reinstall the Kerberos Framework from the 10.4.8 update, as per these instructions.

I'm now having to do this on a regular basis which is very annoying and I'm getting concerned that replacing Kerberos with an older version is introducing security holes. so I've decided to install Fink and openssh instead of using Apple's ssh.

Thursday, 5 July 2007

Deploy Rails on Windows: Part 3

This is the last part of a series of posts on deploying Rails on a windows server using Oracle, Mongrel & Pen. My thinking: get it working with a simple app first, to minimise headaches later. The other parts can be found here:
  1. Installing Rails
  2. My-first-app with Oracle
  3. Pen of pesky Mongrels
This part heavily references a talk given by Brian Hogan at RailsConf

Part 3: Pen of pesky Mongrels
Before you go any further, make sure your app works in production mode!

Install Mongrel

The easiest way to get started with Mongrel is to install it via RubyGems:

gem install mongrel

Select the highest stable version number marked (mswin32).

Run Mongrel in the background with:

cd myrailsapp
mongrel_rails start -d

and stop it with:

mongrel_rails stop

There’s quite a few options you can set for the start command. Use the mongrel_rails start -h to see them all.


Mongrel as a Service


Install the gem (pick the most recent one):

gem install win32-service
gem install mongrel_service

Install your app as a service:

mongrel_rails service::install –N comics_4001 -c c:\my\path\to\myapp –p 4001 –e production

Your app will then appear under ‘services’ in Computer Management (accessed via Control Panel or by right clicking on My Computer)

You can then set the service to start automagically. Right click on ‘comics_4001’, choose ‘properties’ and change the startup type to ‘automatic’.

This is really all you need for a small application – perhaps an internal company app or something.

Cons:
  • You don’t get page caching
  • Doesn’t scale
  • Rails is single threaded so user requests get queued up.
While rails is serving an action to one user, all the other users need to wait. This is bad if some of your actions take a while, which is why we need Pen...


Load Balancing Mongrels with Pen

Pen requires cygwin1.dll, so make sure it’s in the same directory as pen.exe. Also, make you get the one with the ‘a’ suffix – the other version doesn’t work for some reason.

To run pen as a service you need the Windows Server Resource Kit Tools. Install it somewhere. To keep things simple I'll use c:\reskit

Start a couple of mongrels:

mongrel_rails service::install –N comics_4001 -c c:\my\path\to\myapp –p 4001 –e production
mongrel_rails service::install –N comics_4002 -c c:\my\path\to\myapp –p 4002 –e production

You could write a batch file to start and stop these multiple servers.

The following command will balance your mongrels just fine:

pen 4000 localhost:4001 localhost:4002

But to do things properly, keep reading.


Pen as a Service

Install pen as a service using srvany.exe from the reskit:

c:\reskit\instsrv Pen c:\reskit\srvany.exe
>> The service was successfuly added!

Set some parameters with regedit. Go to 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Pen' and add a new key called 'Parameters'. Add the following three String values to the parameters key:
  • Application = pen.exe
  • AppParameters = -f 4000 localhost:4001 localhost:4002
  • AppDirectory = c:\pen
Start the pen service from ‘Services’ or with the command:

net start pen

Remove with:

sc delete pen

Pen will then chug along happily and is suitable for most apps with a few users. You still don’t get page caching, for that you need Apache, but this solution is free, simple and performs moderately well.

Deploy Rails on Windows: Part 2

This post is part of a guide to deploying a small Rails application on a windows server using Oracle, Mongrel & Pen. My thinking: get it working with a simple app first, to minimise headaches later. The other posts can be found here:
  1. Installing Rails
  2. My-first-app with Oracle
  3. Pen of pesky Mongrels
This part heavily references a tutorial provided by Oracle.

Part 2: My-first-app with Oracle


This guide assumes you are either:
  • connecting to an Oracle Database installed on your local machine.
  • connecting to a remote database using Oracle Instant Client installed on your local machine.
If you do not have at least the client software installed, the Ruby-Oracle driver will complain that it can’t find files such as ‘oci.dll’.


Install Oracle Database Driver

Download the driver and install with:

ruby ruby-oci8-1.0.0-rc3-mswin32.rb

You can test your connection to the database with the following one-liner:

ruby -r oci8 -e "OCI8.new('username', 'password', '127.0.0.1:1521/orcl').exec('SELECT * FROM test_table') do |r| puts r.join('|'); end"

Of course, make sure the schema & table that you are testing exists!


Create a User

Using SQL*Plus, create a user with DBA privileges that you can use for this application.

GRANT dba TO ruby IDENTIFIED BY ruby;
ALTER USER ruby DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;
EXIT


Create a Table

CREATE TABLE comics (
id NUMBER(10) NOT NULL,
title VARCHAR2(60),
issue NUMBER(4),
publisher VARCHAR2(60),
PRIMARY KEY (id)
);
CREATE SEQUENCE comics_seq;

Alternatively use this SQL file to create the application table, COMICS.

sqlplus ruby/ruby@rails @comics.sql


Note, in this case the database SID is ‘rails’. The default SID for Oracle Enterprise Edition 10g is ‘orcl’, for Express Edition it’s ‘XE’


Create a dinky Application

Your application skeleton:

rails comics_catalog
cd comics_catalog


Edit ‘comics_catalog/config/databases.yml’ – rails needs to know your login & password info.

Within your project directory, there is a directory called config and in it is a file named database.yml. You need to edit database.yml using your favorite text editor. Initially, the file will look like this:

development:
adapter: mysql
database: rails_development
host: localhost
username: root
password:

# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test:
adapter: mysql
database: rails_test
host: localhost
username: root
password:

production:
adapter: mysql
database: rails_production
host: localhost
username: root
password:

Change the development properties as follows:

development:
adapter: oci
username: ruby
password: ruby
host: 127.0.0.1:1234/orcl

host: takes a connect string of the form <ip_address>:<port>/<database> for the machine that your Oracle instance resides on.

Use the magical scaffold command to build a web based CRUD interface to the table we created above...

ruby script/generate scaffold Comic


Now you can access your Rails Comic Catalog application on your own development machine, using your favorite Web browser. Just access the following URL: http://localhost:3000/comics/list

Phew! Done. Last step is Part 3: Pen of pesky Mongrels...

Deploy Rails on Windows: Part 1

An application I'm developing is due to go public around Christmas. I thought the best way to get to grips with deployment would be to set up a little test application, before trying to move all my code onto the server. This guide will consist of 3 parts:
  1. Installing Rails
  2. My-first-app with Oracle
  3. Pen of pesky Mongrels
I'll be using Windows Server, Oracle 10g, Mongrel & Pen. Many thanks to Brian Hogan for his informative talk at RailsConf which made the whole process very easy. See Deployment Strategies for Rails on Windows Servers for more detailed information.


Part 1: Installing Rails

Install Ruby


Download the one click windows installer. When installing, uncheck 'SciTE' – there are better editors available. Also, make sure ‘Enable RubyGems’ is checked. This is the package manager for Ruby, used to install Rails itself and myriad of other stuff.

Install to 'c:\program files\ruby' (or your installation path), then add ‘c:\program files\ruby\bin’ to your PATH for command line stuff.

Goto ‘Control Panel\System’, select the ‘Advanced’ tab and click ‘Environment Variables’. Look under user variables. If PATH isn’t already defined, click ‘New’.
  • Variable name = ‘PATH’
  • Variable value = ‘%PATH%;c:\program files\ruby\bin’
Note the ‘;’ separator in the value string. If PATH is already defined just add ‘;c:\program files\ruby\bin’ to the end of it.


Install Rails

Now that we have ruby installed, we can use RubyGems to install rails.

gem install rails --include-dependencies

Yes, it is that easy.


My-first-app


Create your application skeleton and start the server:

rails path/to/your/new/application
cd path/to/your/new/application
ruby script/server

Congratulations! You’re now on Rails. Onto Part 2: My-first-app with Oracle...

Wednesday, 23 May 2007

Improve Default Rails Database Performance

Active Record (AR) is slow. All that nice reflection comes at a price. You should use AR to prototype your app, then take a look at the logs.
1. Sort your development log.
If a database query is only run a few times, let AR handle it. If one is being run a 1000 times then we should pull it out and tweak it.
2. Pull the SQL from the log
Rails has already done the hard work for us, right?
3. Tweak the SQL
Stick the tweaked DB queries in your models and things should move along nicely...

RailsConf 2007


Ok, I've just got back from RailsConf 2007, so the next few posts will be about stuff I picked up while I was there. I found the conference a bit light on technical content so don't expect anything ground breaking, just some general advice. For now, here's where you can find most of the slides from the conference.

Thursday, 22 March 2007

Named Callbacks

Coming from a Java background I often find myself wanting to override constructors and other hooks. I should really pay more attention to the documentation.

Instead of:
class MyClass
def after_initialize
#Do Something...
end
end

Do:

class MyClass
after_initialize :do_something

def do_something
#Do Something...
end
end

Update 13/06/07:

Using named callbacks better conveys your intention, making your code more readable.
Similar article on The Rails Way

Wednesday, 21 March 2007

Active Record Constructors

Do not write your own initialiser to instantiate the object. e.g:
class MyClass < ActiveRecord::Base
def initialize(params)
@params = params
end
end

Instead do this:

my_object = MyClass.new(:params => params)
my_object.save

Or this:

MyClass.create :params => params

The above will automatically save the record to the DB.

Environment Variables in Mac OS X

I've been recently having trouble starting certain java applets on my MacBook. For some reason , once downloaded, the Jar file would not extract or run. After much tinkering I found that the applets would work if I started my browser from the command line rather than the dock.

Similarly I couldn't get certain scripts to work from textmate, but they would work if I started textmate from the command line.

The problem was that the $PATH variable I had set in my .bash_profile was not the same as that set in my ~/.MacOS/environment.plist file.

Certain applications depend on being able to read your PATH and other environment variables. However those set in .bash_profile have absolutely no effect on apps started from the Dock. Instead make sure that the PATH variable in your environment.plist file is in sync with that in your .bash_login by doing 'echo $PATH' in a terminal and copying the result into said file.

Syntax Highlighting in blogger

Eventually (in the far distint future) I want to set up my own server to host rails apps on and perhaps a blog driven by Mephisto - particularly cause you can get good syntax highlighting plugins for it.

In the meantime, I discovered that you can export your current highlighting theme from textmate as CSS, using the bundle command 'Create CSS From Current Theme' (vaguely enough ;) ). Then any time I want to post code snippets here, I use the similarly vague command 'Create HTML From Document'

All you need to do is copy and paste the CSS into your Blogger template.

Beautiful.

Selection List syntax

I dont think the explanation of 'selection lists' in Agile Web Development with Rails(1st ed) is very clear. It mentions:

select(:variable, :attribute, choices, options, html_options)

Where you can then set the default selection with:

@variable.attribute

However this variable is not available automatically, you need to create it yourself. The def in the api is more intuitively described as:

select(:my_object, :method, choices, options, html_options)

You must then create your own class with the accessor methods you need so that in the controller you can do something like:

def action
@my_object = MyClass.new
@my_object.method = params[:my_object][:method]
end

session variable not available

You CANNOT access the session hash from within a controllers initialize() method.

Rails Fixtures with Legacy Database

My setup was:

MySQL Table name: TABLENAME
Rails Model name: TableName

  1. The fixture file has to be TABLENAME.yml (not case sensitive)
  2. fixtures() takes the symbol name of the Model, in this case :tableName (note the first lowercase letter) This is the bit that drove me mad - i was writing fixtures :TableName which appeared to work, but then the test failed if I tried TableName(:foo)
  3. Now you can write tableName(:foo)

Hello and Welcome!

This humble blog serves mainly as an aide-memoire for the Ruby on Rails mistakes I've made, so that the head-against-brick-wall process is less painful next time. Who knows, perhaps as I get better, some of these posts will be useful to others too.