Tech Tidbits - Ruby, Ruby On Rails, Merb, .Net, Javascript, jQuery, Ajax, CSS...and other random bits and pieces.

Wednesday, September 19, 2007

Ruby on Rails - Legacy MySQL Database

I'm rewriting a company admin tool written in mod_perl (Catalyst) and replacing it with a new one using Ruby on Rails. At the moment, I have to keep the legacy database/schema. Most of the old tables don't use auto-incrementing keys, which means I won't get the Rails Active::Record magic and I need to work around the keys.

Here's an example table:

mysql> describe clients;
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| client_abbrv | varchar(5) | NO | PRI | | |
| client_name | varchar(50) | YES | | NULL | |
+--------------+-------------+------+-----+---------+-------+

To get this to work with Rails, I had to make a few small changes.

I made one small change in the view, which was setting the name of the primary key field. I changed "client_abbrv" to "id" in the form:

app/views/client/new.rhtml

<%= error_messages_for 'client' %>

<fieldset>
<legend>Add a Client
<% form_tag :action => "create" do %>
<p>
<label for="client_client_name">Client Name:
<%= text_field 'client', 'client_name' %>
</p>
<p>
<label for="client_id">Client Abbrv:
<%= text_field 'client', 'id' %>
</p>
<p>
<%= submit_tag 'Add' %>
</p>
<% end %>
</fieldset>


For the model, I had set the primary key with set_primary_key.

app/models/clients.rb

class Client < ActiveRecord::Base
set_primary_key "client_abbrv"
end


Luckily, the table was already pluralized. If it had been "client," then I would have had to add this line:


def self.table_name() "client" end


For the controller, I had to change the create method:

app/controllers/client_controller.rb

def create
@client = Client.new
@client.id = params[:client][:id]
@client.client_name = params[:client][:client_name]
if @client.save
flash[:notice] = "Client was successfully created."
redirect_to :action => 'list'
else
render :action => 'new'
end
end


That replaced the old create method where I had the normal:

@client = Client.new(params[:client])


When using set_primary_key, you insert data using "id" and typically select data using the actual field name, "client_abbrv" in this case.

Here's what the controller is doing for the create method:


dsparling-imbps-computer:~/my/rubydev/uclick-admin dsparlingimbp$ script/console
Loading development environment.
>> client = Client.new
=> #<Client:0x32b5e14 @new_record=true, @attributes={"client_name"=>nil}>
>> client.id = "abc"
=> "abc"
>> client.client_name = "ABC Company"
=> "ABC Company"
>> client.valid?
=> true
>> client.save
=> true

Saturday, September 1, 2007

Upgrading to PHP5 on Mac OS X 10.4

I've been running PHP5 for ages on my Linux box, but my new (for me) MacBook Pro had PHP4...um, time for an upgrade. Well, easy enough...

I downloaded the PHP5 tarball from http://www.entropy.ch/software/macosx/php/ and did the install.

Before the install, I commented out all the php4 lines in my apache config file(/etc/httpd/httpd.conf).

The entropy package installed under /usr/local/php5 and it added a new config file (/private/etc/httpd/users/+entropy-php.conf), which is included by the main apache httpd.conf file...restart apache, that's it!

Tuesday, August 28, 2007

RMagick on Mac OSX

When installing RMagick, I found that X11 hadn't been installed. It's on the developer CD, which I didn't have handy, so here's what I did.

1) read the notes here:

http://developer.apple.com/opensource/tools/runningx11.html

2) Downloaded and installed Xcode and X11SDK from Apple Developers Center.

3) I still needed X11.app, so I downloaded and installed that from:

http://xanana.ucsc.edu/xtal/x11.html

4) Downloaded and installed rm_install from:


http://rubyforge.org/projects/rmagick/


This will install ImageMagick and RMagick with all dependencies.

I originally had downloaded ImageMagick binary and got this error when trying to install RMagick gem:

"Can't install RMagick. Can't find libMagick or one of the dependent libraries. Check the config.log file for more detailed information."

Info on the error here:


http://rubyforge.org/forum/message.php?msg_id=4066

Sunday, August 5, 2007

Ruby on Rails - script/console

A nice and easy way to check you validation rules...

With a table called users with fields user_name, password, and email, we can add simple validation to the model User.


validates_uniqueness_of :user_name, :email
validates_length_of :user_name, :within => 4..20
validates_length_of :password, :within => 4..20
validates_length_of :email, :maximum => 50
validates_format_of :user_name,
:with => /^[A-Z0-9_]*$/i,
:message => "must contain only letters, " +
"numbers and underscores"
validates_format_of :email,
:with => /@/,
:message => "must be a valid email address"



Then using script/console, we can check our validation:

First, try to add a bad record:


$ script/console
Loading development environment.
>> user = User.new(:user_name => "pooh bear",
?> :password => "pb",
?> :email => "poohbear_at_100akerwood.com")
=> #"pooh bear", "password"=>"pb", "email"=>"poohbear_at_100akerwood.com"}>
>> user.save
=> false


Then you can inspect the errors individually with errors.on method:

>> user.errors.on(:user_name)
=> "must contain only letters, numbers and underscores"
>> user.errors.on(:password)
=> "is too short (minimum is 4 characters)"
>> user.errors.on(:email)
=> "must be a valid email address"


or all at once with errors.full_messages method:

>> user.errors.full_messages
=> ["Screen name must contain only letters, numbers and underscores", "Password is too short (minimum is 4 characters)", "Email must be a valid email address"]
>>

Monday, July 16, 2007

PHP PEAR: MDB2

The PEAR DB class has been deprecated and replaced with MDB2.

MDB2 doesn't come with the MySQL driver out to the box, so you need to install that separately:


$ pear install MDB2
$ pear install MDB2_Driver_mysql


Here's a simple select, using the default fetchmode of MDB2_FETCHMODE_ORDERED.


<?
require_once "MDB2.php";

$dsn = 'mysql://my_username:@my_password@127.0.0.1/my_db';

$options = array(
'debug' => 2,
'result_buffering' => false,
);

$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}

// Proceed with a query...
$res =& $mdb2->query('SELECT * FROM my_table');

// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}

// Iterate through result set (assume default fetchmod is MDB2_FETCHMODE_ORDERED)
while (($row = $res->fetchRow())) {
echo $row[0] . " " . $row[1] . "<br/>\n";
}

?>


The three possible fetchmode values are:
1) MDB2_FETCHMODE_ORDERED (query results returned as array - column numbers are keys)
2) MDB2_FETCHMODE_ASSOC (query results returned as hash/associative array - column names are keys)
3) MDB2_FETCHMODE_OBJECT (query results returned as object - column names as properties)


To change the default fetchmode and use MDB2_FETCHMODE_ASSOC instead, use the setFetchMode method:


$mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);


and then iterate through the results:


while ($row = $res->fetchRow()) {
echo $row['column_name_a'] . ' ' . $row['column_name_b'] . "<br/>\n";
}


Or you can change it per call:


while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
echo $row['client_abbrv'] . ' ' . $row['client_name'] . "<br/>\n";
}


Here's an example of the first method:


<?
require_once "MDB2.php";

$dsn = 'mysql://my_username:@my_password@127.0.0.1/my_db';

$options = array(
'debug' => 2,
'result_buffering' => false,
);

$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}

// Set the fetchmode
$mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);

// Proceed with a query...
$res =& $mdb2->query('SELECT * FROM my_table');

// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}

// Iterate through result set
while ($row = $res->fetchRow()) {
echo $row['column_name_a'] . ' ' . $row['column_name_b'] . "<br/>\n";
}

?>


You can insert data with a prepared statement:


<?
require_once "MDB2.php";

$dsn = 'mysql://my_username:@my_password@127.0.0.1/my_db';

$options = array(
'debug' => 2,
'result_buffering' => false,
);

$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}

// Insert
$statement = $mdb2->prepare('INSERT INTO clients VALUES (?, ?)');
$data = array($var1, $var2);
$statement->execute($data);
$statement->free();
?>


These are just the basics...read all the goodness here:

http://pear.php.net/manual/en/package.database.mdb2.php

About Me

My photo
Developer (Ruby on Rails, iOS), musician/composer, Buddhist, HSP, Vegan, Aspie.

Labels