Installing MySQL gem on OSX 10.6

Hopefully this will be a time saver for anyone else who goes through the following pain.

I have a brand spanking new OSX 10.6 installation.
I installed mysql 5.5.9-OSX10.6-X86 (because I thought everything was still i386 based…watch this space!)
I installed RVM and installed ruby 1.92 under RVM control.
I then do a ‘bundle install’ and it gripes about not having mysql2 gem installed. I do a manual install using the following command.

sudo gem install mysql2

and after a bit of crunching, we get to the following error:

Building native extensions.  This could take a while...
ERROR:  Error installing mysql2:
    ERROR: Failed to build gem native extension.

1.9.1/ruby/ruby.h:108: error: size of array ‘ruby_check_sizeof_long’ is negative
/Users/peter/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1/ruby/ruby.h:112: error: size of array ‘ruby_check_sizeof_voidp’ is negative
In file included from /Users/peter/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1/ruby/intern.h:29,
                 from /Users/peter/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1/ruby/ruby.h:1327,
                 from /Users/peter/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1/ruby.h:32,
                 from ./mysql2_ext.h:4,
                 from client.c:1:
/Users/peter/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1/ruby/st.h:69: error: size of array ‘st_check_for_sizeof_st_index_t’ is negative

The Solution

Uninstall your MySQL using the commands below

sudo rm /usr/local/mysql
sudo rm -rf /usr/local/mysql*
sudo rm -rf /Library/StartupItems/MySQLCOM
sudo rm -rf /Library/PreferencePanes/My*
edit /etc/hostconfig and remove the line MYSQLCOM=-YES-
sudo rm -rf /Library/Receipts/mysql*
sudo rm -rf /Library/Receipts/MySQL*

Now go and download the 64 bit version of the MySQL package – get the dmg rather than the gzip file.

The rationale behind this is that your new beaut 10.6 is actually referencing 64 bit modules by default. How to prove this I do not know – I’m confused and still searching for the answer. If I do a uname – a I get…

Darwin MacBook-Air.local 10.6.0 Darwin Kernel Version 10.6.0: Wed Nov 10 18:13:17 PST 2010; root:xnu-1504.9.26~3/RELEASE_I386 i386

Now, if I’m not mistaken that’s an i386 at the end – go figure. If anyone has any further insights please let me know.

The next step is to download the gem for your ruby/rails use

sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- \
  --with-mysql-dir=/usr/local/mysql --with-mysql-lib=/usr/local/mysql/lib \
  --with-mysql-include=/usr/local/mysql/include

The next error encountered is when I try to start the rails server using ‘rails s’

/Users/peter/.rvm/gems/ruby-1.9.2-p180/gems/mysql2-0.2.6/lib/mysql2.rb:7:in `require': dlopen(/Users/peter/.rvm/gems/ruby-1.9.2-p180/gems/mysql2-0.2.6/lib/mysql2/mysql2.bundle, 9): Library not loaded: libmysqlclient.16.dylib (LoadError)

The answer to this problem is to run the following command…

sudo install_name_tool -change libmysqlclient.16.dylib /usr/local/mysql/lib/libmysqlclient.16.dylib ~/.rvm/gems/ruby-1.9.2-p180/gems/mysql2-0.2.6/lib/mysql2/mysql2.bundle/

Bear in mind, I’m running version 1.9.2-p180 fo ruby – you will need to change the command for whatever version you’re running.

Share

Setup Git Local and Remote Repositories

If you are a remote worker or, like me get some work done on the train on the way to/from your office, you’ll appreciate the need for set up of a local and remote source code repository, This allows you to develop using your laptop/netbook, check in your changes locally and when you arrive at the mothership later, you can synchronise your local repository with the remote one ensuring all your changes are available for other developers. This article shows how to set up git for both a mothership type repository (let’s call it the remote repository) and a local version on your own laptop.

Configuring Git project on the mothership.

Prerequisites:

A Git server (the mothership) has been set up on a box called bluelight. This box is available to the network as git.petermac.com.

A git user has been created on the server called ‘git’. This user has access to the folder where the git repositories are stored.

The Steps:

1.On your dev machine create your code project using whatever tools you need.

2.Initialise this working project under the git version control system

$ cd ~/projects/[myprojectname]
$ git init
peter@peter-desktop:~/Projects/rentmanager$ git init
Initialized empty Git repository in /home/peter/Projects/rentmanager/.git/

3.Add whatever work you’ve done to the repository

$ git add app/
$ git add docs/

4.Check the files you want added have been added

$ git status
# On branch master
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached ..." to unstage)
#
#    new file: app/rentmanager/,
#

5.Open a SSH session to bluelight – (the central git repository server)

$ sudo mkdir /usr/local/share/gitrepos/[myprojectname].git

6.Obviously substitute your real projectname and don’t forget to leave the .git extenstion

7.Initialise the repository under the new folder

$cd /usr/local/share/gitrepos/[myprojectname].git

[peter@bluelight myprojectname.git]$ sudo git init
Initialized empty Git repository in /usr/local/share/gitrepos/myprojectname.git/.git/

8.Change ownership of the repository to the system git user

$cd ..; sudo chown git.git -R /usr/local/share/gitrepos/myprojectname.git

Configure git on your laptop to use the remote repository

1. Return back to the local machine and add reference to the new ‘remote’ repository from the base directory of the project.

$ git remote add remote ssh://git at git.petermac.com/usr/local/share/gitrepos/myprojectname].git

2. Here the ‘git remote add’ part says add a reference to a remote repository. The second ‘remote’ is the friendly name I want to use when referring to the repository on the git server

3. Now commit the local files to the local repository – Note: Step 3 was only an add, not a commit. When you commit you’ll be prompted (or you can enter it as a -m option) to enter a message to be used as a comment.

peter@peter-desktop:~/Projects/myprojectname$ git commit
Created initial commit 633fd3c: initial checkin of project core and data migration files

4. It’s time to test the new remote repository by ‘pushing your local repository info up to it. This is done using git push

peter@peter-desktop:~/Projects/myprojectname$ git push –dry-run –all --repo=remote
fatal: 'origin': unable to chdir or not a git archive
fatal: The remote end hung up unexpectedly

Didn’t quite go to plan – so let’s see what’s wrong

peter@peter-desktop:~/Projects/myprojectname$ git remote show remote
The authenticity of host 'git.petermac.com (192.168.0.15)' can't be established.
RSA key fingerprint is 5a:ce:6e:a4:78:d5:01:50:36:2b:bb:12:67:e1:be:53.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '
git.petermac.com' (RSA) to the list of known hosts.
git at git.petermac.com'
s password:
* remote remote
URL: ssh://git at git.petermac.com/usr/local/share/gitrepos/myprojectname.git

let’s try again

$ git push –dry-run –all –repo=remote
git at git.petermac.com's password:
To ssh://git at git.petermac.com/usr/local/share/gitrepos/myprojectname.git
[new branch]      master -> master

looks like it will work so remove the dry-run parameter

$ git push –all –repo=remote

That’s all folks!

Share

Rails 3, jQuery and multiselect dependencies part 2.

This article is a follow up to another article on this website which focuses on using jQuery to manage the processing of multiple inter-dependent select lists on a single web page. I had a number of requests to explain the data model in a bit more detail so here goes.

I have worked on a number of internationalised (in the US, that’s spelt internationalized) applications where the selection of a ‘home country’ by a user means the display of their address fields should change to reflect their selection of a country. I’ve used this example as the basis of the multi-select article.

MySQL Data Structure

The relevant tables are in a MySQL database and are as follows:

  • locales
  • countries
  • countrynames
  • regionnames
  • regions
  • cities
  • citynames
  • users

The structure of each of the tables is as follows. Note, this is by no means a strict design criteria for your tables, it’s just a design decision that I made because it made sense to me and my warped view of the world.

The locales table contains a listing of a possible 29 locales that can be used to tailor how your application displays and interprets data

mysql> desc locales;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | int(11)     | YES  | UNI | NULL    |       |
| locale   | char(5)     | NO   | PRI | NULL    |       |
| name     | varchar(60) | NO   |     | NULL    |       |
| fullname | varchar(60) | NO   |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

The countries table contains core country information including their names in English, their international code and their latitude and longitude.

mysql> desc countries;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | YES  | UNI | NULL    |       |
| code      | char(2)     | NO   | PRI | NULL    |       |
| code3     | char(3)     | YES  |     | NULL    |       |
| numcode   | smallint(6) | YES  |     | NULL    |       |
| url       | varchar(50) | NO   | MUL | NULL    |       |
| name      | varchar(50) | NO   | UNI | NULL    |       |
| latitude  | double      | NO   |     | NULL    |       |
| longitude | double      | NO   |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+

The countrynames table contains the names of countries as it is interpreted in each locale. For example the country Andorra is the same in most locales ‘Andorra’ but in Russian it is ‘Андорра’.

mysql> desc countrynames;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| id         | int(11)     | YES  | UNI | NULL    |       |
| country_id | int(11)     | NO   |     | NULL    |       |
| locale_id  | int(11)     | YES  |     | NULL    |       |
| name       | varchar(60) | NO   |     | NULL    |       |
| fullname   | varchar(60) | NO   |     | NULL    |       |
+------------+-------------+------+-----+---------+-------+

Now we’re into the messy bit. If you live in the United States, well it’s like it says on the box…it’s united ‘States’; however if you live in the United Kingdom it’s all about counties. If like me you live in Australia, we have both States and Territories (it’s a long story..look up the difference on google). Regardless of the type of country divisions, the name I’ve used is ‘Region’.

The regions table defines the region names within countries.

mysql> desc regions;
+------------+-----------------+------+-----+---------+----------------+
| Field      | Type            | Null | Key | Default | Extra          |
+------------+-----------------+------+-----+---------+----------------+
| id         | int(4) unsigned | NO   | PRI | NULL    | auto_increment |
| country_id | int(11)         | YES  |     | NULL    |                |
| code       | char(3)         | NO   |     | NULL    |                |
| url        | varchar(50)     | NO   |     | NULL    |                |
| name       | varchar(50)     | NO   |     | NULL    |                |
| latitude   | double          | NO   |     | NULL    |                |
| longitude  | double          | NO   |     | NULL    |                |
+------------+-----------------+------+-----+---------+----------------+

The localised version of the regions table is regionnames.

mysql> desc regionnames;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | NO   | PRI | 0       |       |
| region_id | int(11)     | NO   |     | NULL    |       |
| locale_id | int(11)     | NO   |     | NULL    |       |
| name      | varchar(60) | NO   |     | NULL    |       |
| fullname  | varchar(60) | NO   |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+

Next is the cities within the regions and their localised versions. These use the tables ‘cities’ and ‘citynames’ respectively. You can see the foreign key link to the country and region tables below.

The cities table

mysql> desc cities;
+------------+-----------------+------+-----+---------+----------------+
| Field      | Type            | Null | Key | Default | Extra          |
+------------+-----------------+------+-----+---------+----------------+
| id         | int(8) unsigned | NO   | PRI | NULL    | auto_increment |
| country_id | int(11)         | NO   |     | NULL    |                |
| region_id  | int(11)         | YES  |     | NULL    |                |
| url        | varchar(50)     | NO   |     | NULL    |                |
| name       | varchar(50)     | NO   |     | NULL    |                |
| latitude   | double          | NO   |     | NULL    |                |
| longitude  | double          | NO   |     | NULL    |                |
+------------+-----------------+------+-----+---------+----------------+

The citynames table holds the localised version of city names.

mysql> desc citynames;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | YES  | UNI | NULL    |       |
| city_id   | int(11)     | NO   |     | NULL    |       |
| locale_id | int(11)     | YES  |     | NULL    |       |
| name      | varchar(60) | NO   |     | NULL    |       |
| fullname  | varchar(60) | NO   |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+

There are other tables in the system including a users’ table with a locale_id which the user sets by selecting from a drop-down list on their profile-page. I haven’t included that table her because there’s also a bundle of application specific stuff in there too.

The Rails Schema

Now, like all good rails developers, you’re using schema migrations. If you’re not, go stand in the corner for twenty minutes and think about spending the same twenty minutes every week wasted looking at the conjoin between two walls. Rails database migrations are the way to go. They take a little bit of getting used to but well worth it to keep all your team on the same page, or if you’re doing your bit as a solo programmer it means you can screw it up and rewind to the last working version with ease.

As an example, I’ll show you how to create a new schema file for the cities entity. I’ll leave the remaining entities for your own interpretation.

In Rails 2, you’d use the following line

$ ruby script/generate migration create_cities

In Rails 3, it’s more DRY

$ rails g migration create_cities

This will create a new file in you db/migrate folder ending in ‘create cities.rb’. Edit this accordingly and add the fields to the migration that reflect the structure of the table we want to create. My version looks like the following:

class CreateCities < ActiveRecord::Migration
  def self.up
    create_table :cities do |t|
      t.integer :country_id
      t.integer :region_id
      t.string :url, :limit => 50,  :default => "",    :null => true
      t.string :name, :limit => 50,  :default => "",    :null => false
      t.float :latitude
      t.float :longitude
    end
  end
  def self.down
    drop_table :cities
  end
end

The Models

In the root of your application, create the necessary models. Under Rails 3, that can be done by entering

$ rails g model city

A new file is generated under the app/models folder called city.rb.

The full listing of my models folder is as follows:

locale.rb  
city.rb
country.rb
region.rb
cityname.rb
countryname.rb
regionname.rb  
profile.rb
user.rb
role.rb
admin.rb

For the purposes of this discussion we’re interested in everything but the last 4 files. These are used to store user permissions, roles, and profile data.

The content of the relevant model files is used to implement the relationships between the different entities and some field level validation rules. There is nothing whiz-bang about this, just the use of the Rails convention over configuration principle at work.

Taking it from the top, my files look like the following:

$ app/models/locale.rb
class Locale < ActiveRecord::Base
  validates_presence_of :name
end
$ app/models/city.rb
class City < ActiveRecord::Base
  validates_presence_of :name

  belongs_to :country
  has_many :citynames
end
$ app/models/country.rb
class Country < ActiveRecord::Base
  validates_presence_of :name
  validates_presence_of :code

  has_many :regions
  has_many :citys
end
$ app/models/region.rb
class Region < ActiveRecord::Base
  validates_presence_of :name

  belongs_to :country
  has_many :regionnames
end
$ app/models/cityname.rb
class Cityname < ActiveRecord::Base
  belongs_to:city
  belongs_to:locale
end
$ app/models/countryname.rb
class Countryname < ActiveRecord::Base
  belongs_to :country
  belongs_to :locale
end
$ app/models/regionname.rb
class Regionname < ActiveRecord::Base
  belongs_to :region
  belongs_to :locale
end

So, there you have it, fairly simple really, a couple of tables, a couple of model files and the material from this article and the previous article should have you up and running. As always, any queries, comments or improvements, please let me know.

Share

Scaling images in batch mode

I have lots of folders with images uploaded from digital cameras. Average image size is 3MB which is way too large for uploading to web based image sites. What I need to do is to rescale the images but doing them one at a time is a pain so I wrote a little script to do them one folder at a time.

The script makes use of a program called ‘convert’. This is part of the ImageMagick suite of tools.

Install ImageMagick

sudo apt-get install imagemagick

Once installed try to verify the location of the convert program

which convert
convert is /usr/bin/convert

The way the script works is to create a new folder called ‘resized’ under the folder you want to process. It then places rescaled versions of the images it finds in the source folder.

The scaling is performed by passing a width x height parameter to the convert program. The question is how do you know what value to provide without screwing up the ratio of your image. What I do is open a sample image in Gimp. Go to the ‘Image|Scale’ menu option. I pick a value for the width, hit tab and it provides you with the correct value for the height. This is fine if all your images are of the same size. You’ll have to put in a bit of work if they are different sizes.

Here’s the script..it’s really simple as you can see, but I had an itch and I had to scratch it.

#!/bin/bash

convert_command=/usr/bin/convert
#get the folder name to be procesed
if [ $# -ne 2 ]
then
    echo "Error in $0 - Invalid number of arguments."
    echo "Syntax: $0 <folder name> <new size>"
    echo "Example $0 ./images 1024x680"
    exit 1
fi
folder=$1
newsize=$2
resized="resized"
resized_dir="$1/$resized"

echo "Resized images will be placed into the folder $resized"
if [ ! -d $resized_dir ]
then
    mkdir $resized_dir
fi
cd $folder
for file in $( ls . )
do
   if [ -f $file ]
   then
       echo "Processing $file..."
       convert -resize $newsize $file "$resized/$file"
   fi

done

Save the script as image_resize.sh. Do a chmod a+x so you can execute it. Then create a sample folder with a few images copied just for testing. Run it as follows:

./image_resize.sh path/to/images 1024x680

That’s all folks!

Share

Public Key

—–BEGIN PGP PUBLIC KEY BLOCK—–
Version: GnuPG v1.4.9 (MingW32)

mQGiBEj1SQoRBACbRUMtyYFvFa81V7cnzbYJzSfk5MDuUdV0Wo+ckxKKRfW4Qf30
+ORIJeqzirJp9tOd8/uoPVnxNOK4LBKw47W+lHHLUYNMy2qByy7zVBXfg9EXuIrU
edebiwM92yODj/VoAxTjI1p5Mmr5fhFSSEo8yh7GTqg8g0j4jWPR+eZxywCgi78d
CZ6WmIS+tpNvEqrV/DwQUd0D/R9tyFMuwVKKOcyPxpekJ5RmKk8DDJ7cx6z0s6wM
kpx5qpL2QKhVp/3s7t9M40FT3ZmT2ERjJwZzncM8JMxDOK/vS8CRGlTd/JJIzWNj
8SOo55HCq1gJP8U01P1wU1AK9/7n+RcENOGvHEPvKfWweY8HuE8OwHkPGxkqT4og
1TNgA/97226C/NTf7MmOmV5y3yo5k5VUzGRjPpyvZIxIxnQJR4viquNEJfnW9BQT
kRw4J7qQePvcbYrvDWxHlJJzlIzQxbBUqVry4K79ByudQA8QaQAT+XJIMjjppgHz
rKWB6YVTqsSlfjASM9RqjfDCMZpefpjlq2hvJ2WzyLnYGyEJ+rRuUGV0ZXIgTWFj
IEdpb2xsYWZoZWFyZ2EgKEtleSBmb3IgbWFpbCBmcm9tIHBldGVyQHBldGVybWFj
LmNvbSAtIFBldGVyIE1hYyBBbmQgQXNzb2NpYXRlcykgPHBldGVyQHBldGVybWFj
LmNvbT6IZgQTEQIAJgUCSPVJCgIbIwUJCWYBgAYLCQgHAwIEFQIIAwQWAgMBAh4B
AheAAAoJEJlpD3yHCMV4/qYAn0WbPZejFQGgFi3g5IT2Q0D8u++xAJsGmFMdEwPm
zFi4mAEAbhfO+tuUr7kCDQRI9UkKEAgAhf491b6MaNOCp1qo9hjvX1NLQ1sORkI9
suP7PceRZfXuqc92GMXw8lYds9cPddtOBPpBZCaezxvRZHfERwiSWEVjEdwXyC5z
y8ys8L4jxaPS8YS3SnEjkg+1DCLRb5fry+jiSaFeC2YklFFopSxWiSJQow9+jIPF
hfvE4ZswP3EBx54SYC92sZgqfq/DcDcqlOp7gGAxHhxP+rdt4ow4aEzhdzyzNVpb
C26BwRWH6T1JtsBSiy0PoaxV2eVl3Fb93aHPVldtdk5p6Pqm03KAbowurFfLshK9
jyPuOyaM8cUSN+RXSgi846mXT0EhY6J4Ofb0rClm8maXwtSHpk8wBwADBQf/b4i6
oDa5K69S2tyERpxAPhSBPdUnYT0oxlZvj75Wp1SfBkmUy8O+1jONIE1+OhLslxke
WdFMmDrH8AiRU0q19bGEsKDqUWt58UV7GL26+QZbQWjhz7fWzI6ljftgshmXQozI
3725Y2AVQR3U4BaFNiFrJQECPYLB/yypf8OCsZjspERb+d4J7CXfk5mVkFFAOj7N
5UQv0u+dMVJAP3OCVdEibjXc/c4qPPxLUioAqN8MbixM1OzZDOH55K/oJdRoGyIG
mfWU1lToNDCSi7BSqXjPGC+DMDjFc4QfnBFJfV0301GvLshuw+b/r8qJNfxkYkk4
zK/sgbPlXPL032aM4ohPBBgRAgAPBQJI9UkKAhsMBQkJZgGAAAoJEJlpD3yHCMV4
JQMAnA21n/10YG9hXHrUEel1HTFcU+opAJ9KlShcMzJoLI5VVIqfJTa8y/GE+Q==
=L39p
—–END PGP PUBLIC KEY BLOCK—–

Click here to download this as key as a file

Share

Purrfect Java Solutions

Meet “Java”, a new addition to the PeterMac team.

java at 9 weeks

Although only 9 weeks old, she shows great focus in object oriented design (she chases any object that moves). She’s got an excellent grasp of string manipulation technniques and we have to agree that her display of experience in the J2SE (Sleeping and Eating) area is beyond expectations for one so young.

Share