stew(@)rtmatheson.com

Thoughts on Rails, Ruby, and Javascript

Moving to Octopress

Looks like I will be blogging in octopress from now on. Looks really nice and I can host my whole blog on github. I can also blog with vim which is very cool too. Still getting the hang of things so the blog might have some weirdness over the next few weeks but everything is still here. The biggest pain is converting all my posts to markdown from html which will have to be done over time.

I really like the idea of running right of HTML. It’s a great low tech solution to dynamic webpages. It also means no database errors… and no code issues. Static content for the win!

Anyone for Code Golf?

It’s been ages since I have posted anything. So busy with work. I just wrote this little snippet then and liked it. Was wondering if I could make it any shorter.

1
2
3
def shorten_tweet(tweet)
  tweet.length > 140 ? "#{tweet[0..136]}..." : tweet
end

Ruby Acronyms

I love extending the ruby core! Makes code so neat and works so well with rails initializers. This method allows you to fetch the Acronym of an array of strings. It would be kinda cool to write something that works the other way taking a single string. Make using Faker… but I can’t think of any use. Then again IBM could not understand why people needed computers in their home so who knows. Anyway… it’s code time.

1
2
3
4
5
6
7
8
9
class Array
  def acronym
    result = ""
    self.each do |item|
      result << item[0]
    end
    result
  end
end

Rounding Time to the Closest Hour in Ruby

Ahhh… its one of the moments in life that make you so happy your a ruby developer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Time
  def round(seconds = 60)
    Time.at((self.to_f / seconds).round * seconds)
  end

  def floor(seconds = 60)
    Time.at((self.to_f / seconds).floor * seconds)
  end

  def round_to_closest_hour
    if self.min > 30
      self.round(1.hour)
    else
      self.floor(1.hour)
    end
  end
end

Source: http://stackoverflow.com/questions/449271/how-to-round-a-time-down-to-the-nearest-15-minutes-in-ruby

Converting Dates Form Javascript to Rails

Time based web applications written in rails and javascript will sooner or later have to think about the following object life cycle.

  1. Javascript Object
  2. Request Params
  3. Rails object
  4. MySQL row

While rails has the last two covered and jQuery has mostly the second covered how do we get our data in to a form where this flow will be smooth. Javascript date objects don’t directly fit so I extended the date prototype to allow for this. This porototype mirrors the rails date time form helper and creates the correct data with the keys rails would expect to have.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  Date.prototype.toRails = function(name) {
    var dateParams;
    dateParams = {};
    dateParams[name + "(1i)"] = this.getFullYear();
    dateParams[name + "(2i)"] = this.getMonth() + 1;
    dateParams[name + "(3i)"] = this.getDate();
    dateParams[name + "(4i)"] = this.getHours();
    dateParams[name + "(5i)"] = this.getMinutes();
    return dateParams;
  };

  //Use

  d = new Date();
  date = d.toRails('start');

  //now we can put this object on the server
  $.ajax
      ...
      data : date
      ...

As far as rails is concerned we might as well be using the datetime helper.

Readline Requirements With Cent OS 5 and RVM

It seems that “rvm requirements” omits to mention the install of ncurses-devel when setting up for ruby installations with readline on Centos 5. Although you can install the readline package and and ruby its self with out getting an error there is a issue with the shared object. You will see this issue the first time you run irb. Ruby will complain that it can’t load readline. This took me the best part of two hours to figure out however it ended up being a missing package. Make sure you…
sudo yum -y install ncurses-devel
before you install ruby. Then you can install ruby compiling it with readline. HTH.

Want to Learn Javascript?

I have set-up a tutorial site for JavaScript with plans to add other languages. Its a getting started guide for any new programmers looking to start out with JavaScript development. If I get some positive feedback I will add more tutorials. To check them out head to Stewtorials

Installing Ruby 1.9.2 on Mac Os X 10.7 (Lion)

The other day I updated to Lion from Leopard. I know… I was keeping it real with an old OS but it got to the point where new apps where not working with leopard. While everything with the operating system update was fine there was a lot of time spent getting my ruby on rails install back up and running. One thing I did not understand is that I had made the leap to a 64bit system. This means a lot of recompiling as I hate package managers for osx.

Reinstalling MySQL

First things first you will need to recompile MySQL from source. I am using 5.1. It seems to be the version of mysql that everyone is using. Some people might stick with sqlite however as I would never use it in production I would also never use it in development so I stick with MySQL. These commands are taken from Dan Benjamin’s hivelogic blog. Great work Dan! The link to the article is below in the sources section so if you run in to trouble check the article out.

1
2
3
4
5
6
7
8
9
10
11
12
cd /usr/local/src
curl -O http://mysql.he.net/Downloads/MySQL-5.1/mysql-5.1.33.tar.gz
tar xzvf mysql-5.1.33.tar.gz
cd mysql-5.1.33
CC=gcc CFLAGS="-O3 -fno-omit-frame-pointer" CXX=gcc
CXXFLAGS="-O3 -fno-omit-frame-pointer -felide-constructors
-fno-exceptions -fno-rtti"
./configure --prefix=/usr/local/mysql
--with-extra-charsets=complex --enable-thread-safe-client
--enable-local-infile --enable-shared --with-plugins=innobase
make
sudo make install

Removing the old Ruby

After these steps you should have the database recompiled for the new architecture of Lion. Great lets move on. Its worth noting that I am using RVM. The first thing I needed to do after the update is remove my existing version of ruby. This is the 32bit version and will not work.

1
rvm remove 1.9.2

Reinstalling dependencies

With that done we need to reinstall ruby 1.9.2. We can use RVM to do this however there are a few things we need to update before this. Lets start with libxml. Download the latest version and compile it.

1
2
3
4
5
tar xzvf libxml2-2.7.3.tar.gz
cd libxml2-2.7.3
./configure --with-python=/System/Library/Frameworks/Python.framework/Versions/2.3/
make
sudo make install

After this we need to install libxlst.

1
2
3
4
5
6
cd /usr/local/src
curl -O ftp://xmlsoft.org/libxslt/libxslt-1.1.20.tar.gz
cd libxslt-1.1.20
./configure
make
sudo make install

All done now lets move on to readline.

Installing Readline

At the time of this writing I was unable to install readline via rvm packages. There is a patch that fixes this however I am not sure its in head yet. The catch here is that in the current state when you install readline it will say that the install has been successful. This is not true. There is a topic on the RVM mailing list about this. For the moment I was able to get around this by compiling and installing readline myself. I have no doubt that this will be fixed soon but for the moment you will need to take this step. This last step is done assuming that you have readline 5.2 downloaded and untared. If you need help then I can add the download link.

1
2
3
4
5
6
7
8
9
10
11
export MACOSX_DEPLOYMENT_TARGET=10.7
export CFLAGS="-arch x86_64 -g -Os -pipe -no-cpp-precomp"
export CCFLAGS="-arch x86_64 -g -Os -pipe"
export CXXFLAGS="-arch x86_64 -g -Os -pipe"
export LDFLAGS="-arch x86_64 -bind_at_load"
./configure --prefix=/usr/local
cd shlib
sed -e 's/-dynamic/-dynamiclib/' Makefile > Makefile.good
mv Makefile.good Makefile
cd ..
make && sudo make install

Next lets install zlib and iconv and openssh via RVM.

1
2
3
rvm pkg install zlib
rvm pkg install iconv
rvm pkg install openssl

Installing Ruby

Now we can install ruby 1.9.2. This will install the 64 bit version for us.

1
rvm install 1.9.2-head -C --enable-shared,--with-readline-dir=/usr/local --with-iconv-dir=$rvm_path/usr  --with-zlib-dir=$rvm_path/usr --with-openssl-dir=$HOME/.rvm/usr,--build=x86_64-apple-darwin10

Finishing Up

Once you have recompiled ruby you will need to create a new gem set. For me I have a gem set for each one of my projects so it was a matter of creating the gem set again and then running a bundle install.

Sources

http://www.markhneedham.com/blog/2010/07/08/installing-ruby-1-9-2-with-rvm-on-snow-leopard/

http://stackoverflow.com/questions/6317980/bundle-rake-error

http://exceptionz.wordpress.com/2010/02/03/how-to-fix-the-iconv-require-error-in-ruby-1-9/

http://groups.google.com/group/rubyversionmanager/browse_thread/thread/4130f40a654242a6?hl=en

http://techdebug.com/blog/2009/01/03/compiling-readline-on-an-osx-105-intel-x86_64

https://rvm.beginrescueend.com/packages/zlib/

http://nokogiri.org/tutorials/installing_nokogiri.html

“` http://hivelogic.com/articles/installing-mysql-on-mac-os-x/

Setting Up Nginx to Work With Rails, Capistrano and SSL.

I need to set up a billing system. I want to run you though how to set up such a system on a server complete with SSL and nginx as its a rails application we are deploying. I will be using a brand new install of Ubuntu however the ideas in this post should work with any server. First off lets set up our linux box

Base Linux Setup

After you log in the first time you want to change your root password.

1
passwd

The system will prompt you to change the root password. Once you have done this then you can create a new user. You should NEVER log in with the root user.

1
adduser username

This command will prompt you to enter details about the user your creating however you only really need to enter the password. This is the user that we will use from this stage on so we need to add it to sudoers. If the user is not a sudoer then we cant install/compile anything. Nor can we make changes to system settings. Open the sudoers file with your favorite editor and add the following line

1
2
3
vim /etc/sudoers
## Add this line to the file
username ALL=(ALL)       ALL

Where username is the name of the user you have just created. Great now we have done this we have a user account and we don’t need to worry about our root user any more. Log out and then log back in with the new user. You should be able to run sudo commands.

SSH

First lets set up SSH Keys. This allows us to log in to our server with out having to enter a password. This is such a time saver and worth setting up. Any time we are doing a deployment using capistrano we are going to be glad to have our SSH key in place. First off if you don’t have it create a .ssh folder in your home directory(on your local machine - not on the server). Windows users… sorry your on your own here. You might have to read the PuTTY doc.

1
mkdir .ssh

After you have created the .ssh folder cd to it and run…

1
ssh-keygen -t rsa

This will create two files. One public and one private key. Never give out the private key. The public key can be given to anyone. In this case we are going to put the public key on our server. The public key is called id_rsa.pub.

1
scp ~/.ssh/id_rsa.pub username@serveraddress

You will have to enter your password here. Once we have the key up there log back in to your server. You will still have to enter the password at this stage. You need to move the key file to a file called authorized_keys and set the ownership and permissions correctly on it.

1
2
3
4
5
mkdir .ssh
mv id_rsa.pub .ssh/authorized_keys
chown -R username:username .ssh/
chmod 700 .ssh/
chmod 600 .ssh/authorized_keys

Done! Logout and log back in. You should NOT be asked for a password. Once we have our keys set up there is one more thing that I would recommend doing at this stage. Changing the default SSH port. I always like to use the same port so set it to something that you will remember. Write it down because if you can not remember the port number you may be locked out of your system. To change the port we need to edit the ssh config file. Do so like this…

1
sudo vim /etc/ssh/sshd_config

The port setting should be on the first line. Chagne it save the file. The settings will not take effect until you restart ssh. At this point I would recommend you set up IP tables. While I am not going to talk about this in the first revision of this post I intend to add it later down the track when I have more time.

Now restart sshd

1
/etc/init.d/ssh reload

When you login you should use the new port. Remember that ssh keys do not have any port information so you will still need to tell ssh what port we want to use as its no longer set to default.

Install Ruby

Once we have logged back in we need to install RVM. Ruby Version Manager is a great application that will help us manage ruby versions and gemsets. We can have many different gemsets and different versions of ruby. This allows us to serve applications from the same server in different ways.

First lets install some dependencies that RVM has.

1
sudo apt-get install git-core curl build-essential vim libcurl4-openssl-dev

Next we run the remote script that will install and setup rvm for us

1
2
su
user$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

This script will use git to download RVM and install it. Note how we changed to the super user. This is because we want RVM multisite install. This is best for when we are installing RVM on servers.

1
type rvm | head -1

This should output the string “rvm is a function”. If so then we are in business and rvm has been installed.

We still have not installed ruby. Ruby its self has a number of dependencies. Lets install them first. The package names may differ from distro to distro so you might want to double check what all of them are. They should work for ubuntu though.

1
sudo apt-get install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf

Currently we only have sqlite. If you want to use mysql run the following line.

1
sudo apt-get install mysql-server libmysqlclient-dev libmysql-ruby

Now lets use RVM to install some packages for ruby.

1
2
3
rvm pkg install zlib
rvm pkg install openssl
rvm pkg install readline

Now we can install ruby 1.9.2

1
rvm install 1.9.2 --with-zlib-dir=$rvm_path/usr --with-openssl-dir=$rvm_path/usr --with-readline-dir=$rvm_path/usr

Now we need to install bundler and passenger so we need to create a gem set. Gemsets are logical groups of gems. I normally go one gem set per application.

1
rvm gemset create application_name

Now install bundler. This is how rails will manage the gems it needs.

1
gem install bundler

OK now we have ruby installed lets install the passenger gem. This makes live so easy as it will configure and compile nginx for us.

1
gem install passenger

Once we have the gem installed we have access to some VERY helpful commands. passenger-install-nginx-module is the one we are going to run. This will install passenger for us and better yet it will go ahead and compile nginx for us too. All the time checking for any software that nginx might need. Very handy. I will not guide you though this process aside from running the command because the Phusion guys have created an amazing easy to use installer. To start the installer run

1
rvmsudo passenger-install-nginx-module

Once we are complete nginx will be installed with passenger and our webserver will be set up. Phusion even include the SSL module for Nginx which is great because we will need to use it for our credit card gateway. Incase you missed it nginx also gives you a configuration snippet. Incase you missed it here it is.

1
2
3
4
5
6
server {
    listen 80;
    server_name www.yourhost.com;
    root /somewhere/public;   # <--- be sure to point to 'public'!                                                       
    passenger_enabled on;
}

We will keep this handy as we will need it for our rails application when we set it up. Now we have everything installed we need to get a rails application on our server. I am going to assume you already have an application and that you are ready for deployment.

Deploying The Application

We will use capistrano to manage the deployment of the application. Lets log out of our server and go back to our application so we can install the capistrano gem locally. I will assume that your using RVM on your development machine and have not already got cap installed

1
gem install capistrano

With the gem installed lets “capify” our project. Make sure your in your current project directory and run the following command.

1
capify .

This should create a couple of files but the one we are interested in is deploy.rb. This file holds all of the details that cap needs to deploy the application to our server. It also gives us a place to write any extra tasks that might need to be performed on our application.

1
2
3
4
5
$:.unshift(File.expand_path('./lib', ENV['rvm_path']))
require "rvm/capistrano"
require "bundler/capistrano"
set :rvm_ruby_string, '1.9.2@gemset'
set :rvm_bin_path, "/usr/local/rvm/bin"

The above code tells cap what gem set we want or application to useon the server. It also tells cap that we are working with a single user install of RVM. Cap has a number of other details that need to be set for the deployment to work correctly. These include user names and server addresses. At this stage I like to add some tasks to my cap deploy file. These are…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
namespace :db do
  desc "Create database yaml in shared path"
  task :default do
    db_config = ERB.new <<-EOF
    base: &base
      adapter: mysql
      username: username
      password: password

    development:
      database: #{application}_dev
      <<: *base

    test:
      database: #{application}_test
      <<: *base

    production:
      database: #{application}_production
      <<: *base
    EOF

    run "mkdir -p #{shared_path}/config"
    put db_config.result, "#{shared_path}/config/database.yml"
  end

  desc "Make symlink for database yaml"
  task :symlink do
    run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
  end
end

namespace :rvm do
  desc "Create correct RVM file"
  task :create_rvmrc do
    run "cd #{current_path} && rvm use 1.9.2@#{application} --rvmrc --create"
  end
end

before "deploy:setup", :db
after "deploy:update_code", "db:symlink"
after "deploy:symlink", "rvm:create_rvmrc"

The above tasks manage our database configuration and create the correct RVM file. They are very handy as they completely automate the install on the server.

1
cap deploy:setup

This will login to the server and create all the folders. This is where our rails application will be set up on our server. Note how we don’t have to enter a password. SSH keys are great when dealing with cap because entering a password for each deployment gets old quickly.

1
cap deploy:check

Cap will go ahead and make sure your server has correct permissions to deploy your application. It will also check for any needed software such as source control managers. Once this is done and works then we are ready to deploy our application. Next lets deploy our application

1
cap deploy

This will install the application on the server in the deploy_to path. It will also create a number of symlinks. Its a great way of deploying an application because if something goes wrong it will roll back to the last version. Since this is the first time that we have deployed this does not apply to us. Now lets set up the database.yml file on our server. This is done with the “db” cap task.

1
cap db

Run another cap deploy after this and the database file will correctly link to the application. The next thing to do is create a database for the application and a database user. In the cap task make sure you replace username and password with the user account we are about to set up in mysql. Remember these can be any username and password you like. Just remember that the username and password in the database.yml file need to match what has been created in mysql. To create the user and pass in mysql do the following

1
2
3
4
CREATE DATABASE application_name_production;
CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON application_name_production.* TO 'username'@'localhost'
WITH GRANT OPTION;

Basic overview on SSL

SSL uses private key authentication to verify that a site is who it claims to be. This is done by matching up keys. Much the same way as installing SSH keys works. To do this we generate our own private key. This is super secret! We do this on our server and it stays on the server. It is a security risk taking it off the server as you will have to transfer it over a network(this is unless you have physical access to your server). With this private key we create a certificate signing request. This CSR is then given to our SSL provider. Based on the CSR your SSL provider will create a certificate and send it back to you. They might do this via email as there is no risk if your certificate is out in the open. This certificate will only match the private key that we have on our server meaning that there is only one server that the certificate will match. Your SSL provider should have information available on how to prepare the certificate for use with Nginx as most people will most likely be running Apache(poor fools). One you have th crt file ready to go we point Nginx at that file and the private key we generated. The server will match the two up and users will be safe sending their credit card details via https.

Configuring Nginx with SSL

Okay so we have everything installed. We have our application deployed. We have our application settings correct (we think… time will tell). The next thing to do is configure nginx to pick up on our rails application and work over SSL. Remember the code snippet we got when we installed nginx. Place that in your nginx config file with some changes. Here is the code snippet set up to work for SSL.

Open nginx.conf

1
vim /opt/nginx/conf/nginx.conf

Add our server configuration.

1
2
3
4
5
6
7
8
9
server {
        listen 443;
        server_name domain.com;
        root /where/your/app/is;
        passenger_enabled on;
        ssl on;
        ssl_certificate /path/to/server.crt;
        ssl_certificate_key /path/to/server.key;
}

Note the ssl configuration is pointing at two files. A certificate and a key. For this setup we will generate the certificate and sign it our selves. This will NOT do in production as it will show users that the certificate is not trusted however for now its fine.

1
2
3
4
5
6
cd /path/to/ssl/certs
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

OK now we have nginx all ready to go lets create our init script. The init script allows us to stop and start nginx when we need to and its not provided for us when we install nginx via passenger.

1
sudo vim /etc/init.d/nginx

Add the following to this file and save.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#! /bin/sh

### BEGIN INIT INFO
# Provides:          nginx
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO


PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/sbin/nginx
NAME=nginx
DESC=nginx

test -x $DAEMON || exit 0

# Include nginx defaults if available
if [ -f /etc/default/nginx ] ; then
        . /etc/default/nginx
fi

set -e

case "$1" in
  start)
        echo -n "Starting $DESC: "
        start-stop-daemon --start --quiet --pidfile /usr/local/nginx/logs/$NAME.pid \
                --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        ;;
  stop)
        echo -n "Stopping $DESC: "
        start-stop-daemon --stop --quiet --pidfile /usr/local/nginx/logs/$NAME.pid \
                --exec $DAEMON
        echo "$NAME."
        ;;
  restart|force-reload)
        echo -n "Restarting $DESC: "
        start-stop-daemon --stop --quiet --pidfile \
                /usr/local/nginx/logs/$NAME.pid --exec $DAEMON
        sleep 1
        start-stop-daemon --start --quiet --pidfile \
                /usr/local/nginx/logs/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        ;;
  reload)
      echo -n "Reloading $DESC configuration: "
      start-stop-daemon --stop --signal HUP --quiet --pidfile /usr/local/nginx/logs/$NAME.pid \
          --exec $DAEMON
      echo "$NAME."
      ;;
  *)
        N=/etc/init.d/$NAME
        echo "Usage: $N {start|stop|restart|force-reload}" >&2
        exit 1
        ;;
esac

exit 0

Now we have everything set up lets start nginx.

1
sudo /etc/init.d/nginx start

Once this is started point your browser to the correct domain via https and test your site out. Good luck!

Sources

http://articles.slicehost.com/2010/10/18/ubuntu-maverick-setup-part-1

http://articles.slicehost.com/2007/12/19/ubuntu-gutsy-nginx-ssl-and-vhosts

http://www.modrails.com/install.html

http://blog.bigrocksoftware.com/2011/01/07/rvm-nginx-passenger-rails-3/

https://rvm.beginrescueend.com/rvm/install/

http://excid3.com/blog/ruby-on-rails-3-and-mysql-on-ubuntu-10-10/

http://stackoverflow.com/questions/3644897/rvm-cannot-use-ruby-with-sudo

http://help.github.com/deploy-with-capistrano/

http://beginrescueend.com/integration/capistrano/

http://blog.ninjahideout.com/posts/a-guide-to-a-nginx-passenger-and-rvm-server

http://blog.ninjahideout.com/posts/the-path-to-better-rvm-and-passenger-integration

http://stackoverflow.com/questions/4349628/rails-3-app-deployment-bundler-rake-issues

http://wiki.nginx.org/HttpSslModule

Post updates

23/08/2011 Added links to sources, added intro to ssl.

New Theme

Simple… fast… better for screen reading.