Hoang Pham

iOS, Rails developer

Mar 21

Setting up a Spree Commerce site on a Digital Ocean Droplet (VPS) on Ubuntu 12.04 with Rails, Nginx, Unicorn, MySQL, Redis and Capistrano

This tutorial is based largely on the tutorial by Andrew Gertig.

However, I use a slightly different stack: - I use bitbucket as the repository cause I can create free unlimited repositories and use capistrano to pull source code to my server using ssh as in github. - I use MySQL as my previous project already used MySQL. - I show how to restore a MySQL database dump by remotely copying the file over SSH - I use a Gem to add manage Rails environment variables.

SETUPING DROPLET

a Droplet is a Virtual Private Server that Digital Ocean calls it that way. Setup a new Droplet with default size:

512MB / 1 CPU 
20GB SSD 
$5 / Month

Select the Ubuntu 12.04 x32 image and then click on Create. After creation, we will have an email with root access password to the server machine.

We can ssh to root in terminal with the server’s ip address. Replace with your own IP address.

$ ssh root@188.226.186.211

Add ssh fingerprint and enter the password.

Update the password for root if you prefer as the generated one is hard to remember.

$ passwd

Create new user for deployment later in the post, we should not use root as the user for deployment.

$ adduser deployer

Set new users privileges

$ visudo

Find user privileges section and add your new user privileges under root. Type Ctrl+X then y to save

# User privilege specification
root  ALL=(ALL:ALL) ALL
deployer ALL=(ALL:ALL) ALL

CONFIGURE SSH

$ nano /etc/ssh/sshd_config

Find and change port to one that isn’t default (22 is default: choose between 1025 ..65536)

# Port 22 
Port 1026
Protocol 2
PermitRootLogin yes # You can change this to "no" if you don't want the root user to be able to ssh in anymore

Add to bottom of sshd_config file after changing port (Ctrl+X then Y to Save)

UseDNS no
AllowUsers deployer # Only add this line if you have changed PermitRootLogin to no, or you can do AllowUsers deployer root

Reload ssh

$ reload ssh

Don’t close root! Open a new shell and ssh to your VPS (droplet) with deployer (remember the port you set above or you’ll be locked out)

$ ssh -p 1026 deployer@188.226.186.211

Update the packages as deployer on your VPS. This installs curl, some python stuff, git and node.js (node.js is for the Rails asset pipeline)

$ sudo apt-get update
$ sudo apt-get -y install curl python-software-properties git-core nodejs

INSTALL REDIS (optional)

Prepare to install Redis

$ sudo apt-get -y install build-essential tcl8.5

Change to your home directory and download Redis with wget (I use it for Sidekiq)

$ cd ~
$ wget http://download.redis.io/releases/redis-2.8.7.tar.gz

Untar Redis, switch into its directory then run make and make install. Then run the Redis install script.

$ tar xzf redis-2.8.7.tar.gz
$ cd redis-2.8.7
$ make
$ make test (don't need to, but run it to see some test results of redis project)
$ sudo make install
$ cd utils
$ sudo ./install_server.sh

To automatically start Redis when the server boots

$ sudo update-rc.d redis_6379 defaults

RVM

Install latest stable version of rvm

$ curl -L get.rvm.io | bash -s stable

Load rvm

$ source ~/.rvm/scripts/rvm

Install rvm dependencies

$ rvm requirements

Install ruby 2.1.1

$ rvm install 2.1.1

Use 2.1.1 as rvm default

$ rvm use 2.1.1 --default

Install latest version of rubygems if rvm install didn’t

$ rvm rubygems current

RAILS

Install the rails gem with no documentation, we don’t need to reference the documentation on a production server.

$ gem install rails -v 4.0.3 --no-ri --no-rdoc

Install bundler

$ gem install bundler

POSTGRESQL (in case you use MYSQL, see below)

Install postgres

$ sudo apt-get install postgresql postgresql-server-dev-9.1
$ gem install pg -- --with-pg-config=/usr/bin/pg_config

Create a new postgres user. It’s not a bad idea to use the same username here. Remember these so you can add them to your database.yml file later.

$ sudo -u postgres psql
> create user deployer with password 'secret';
> alter role deployer superuser createrole createdb replication;
> create database my_store_production owner deployer;
> \q

MYSQL

Install MYSQL:

$ sudo apt-get install mysql-server

Set password for MySQL @root during installation: secret

Activate MySQL:

$ sudo mysql_install_db

Finish up by running the MySQL set up script:

$ sudo /usr/bin/mysql_secure_installation

Answer Yes to all the questions

Check if MySQL is running:

$ service mysql status 

$ service mysqld status
$ service mysqld stop
$ service mysqld start 

or we can do

$ sudo /etc/init.d/mysql start

NGINX

Setup nginx

$ sudo apt-get install nginx

These 3 commands test if nginx is installed

$ nginx -h
$ cat /etc/init.d/nginx
$ /etc/init.d/nginx -h

Start Nginx

$ sudo service nginx start
# cd /etc/nginx

LOCAL MACHINE

Enough for setting up on the server, we now switch our attention to the local machine with our source code for Rails app.

Let’s clone the Spree project so that we have the source code to look at:

$ git clone https://github.com/spree/spree

then, create a new store application, more instruction is on the spree github readme page: https://github.com/spree/spree

$ gem install rails -v 4.0.3
$ gem install spree
$ rails _4.0.3_ new my_store
$ spree install my_store

Preparing for Deployment locally with Unicorn, Nginx and Capistrano

Add Unicorn to the gemfile

gem "unicorn"

To use Unicorn, we have to manually create 3 files:

config/nginx.conf
config/unicorn.rb
config/unicorn_init.sh

Configure our nginx server in config/nginx.conf

upstream unicorn {
  server unix:/tmp/unicorn.my_store.sock fail_timeout=0;
}

server {
  listen 80 default deferred;
  # server_name example.com;
  root /home/deployer/apps/my_store/current/public;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @unicorn;
  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
}

Setup the Unicorn server to serve our Rails app: config/unicorn.rb

root = "/home/deployer/apps/my_store/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"

listen "/tmp/unicorn.my_store.sock"
worker_processes 2
timeout 30

# Force the bundler gemfile environment variable to
# reference the capistrano "current" symlink
before_exec do |_|
  ENV["BUNDLE_GEMFILE"] = File.join(root, 'Gemfile')
end

Create the bash script to start, stop and restart our unicorn server for a specific application: config/unicorn_init.sh

#!/bin/sh
### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Manage unicorn server
# Description:       Start, stop, restart unicorn server for a specific application.
### END INIT INFO
set -e

# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/home/deployer/apps/my_store/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
AS_USER=deployer
set -u

OLD_PIN="$PID.oldbin"

sig () {
  test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
  test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
}

run () {
  if [ "$(id -un)" = "$AS_USER" ]; then
    eval $1
  else
    su -c "$1" - $AS_USER
  fi
}

case "$1" in
start)
  sig 0 && echo >&2 "Already running" && exit 0
  run "$CMD"
  ;;
stop)
  sig QUIT && exit 0
  echo >&2 "Not running"
  ;;
force-stop)
  sig TERM && exit 0
  echo >&2 "Not running"
  ;;
restart|reload)
  sig HUP && echo reloaded OK && exit 0
  echo >&2 "Couldn't reload, starting '$CMD' instead"
  run "$CMD"
  ;;
upgrade)
  if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
  then
    n=$TIMEOUT
    while test -s $OLD_PIN && test $n -ge 0
    do
      printf '.' && sleep 1 && n=$(( $n - 1 ))
    done
    echo

    if test $n -lt 0 && test -s $OLD_PIN
    then
      echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
      exit 1
    fi
    exit 0
  fi
  echo >&2 "Couldn't upgrade, starting '$CMD' instead"
  run "$CMD"
  ;;
reopen-logs)
  sig USR1
  ;;
*)
  echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
  exit 1
  ;;
esac

Change permissions to executable on unicorn_init.sh

$ chmod +x config/unicorn_init.sh

Capistrano

Capistrano is a remote server automation tool. It supports the scripting and execution of arbitrary tasks, and includes a set of sane-default deployment workflows.

Capistrano can be used to: Reliably deploy web application to any number of machines simultaneously, in sequence or as a rolling set To automate audits of any number of machines (checking login logs, enumerating uptimes, and/or applying security patches) To script arbitrary workflows over SSH To automate common tasks in software teams. To drive infrastructure provisioning tools such as chef-solo, Ansible or similar.

Add capistrano and rvm capistrano to gemfile

gem 'capistrano'
gem 'rvm-capistrano'

Create Capfile & config/deploy.rb files by running

$ capify .

For deploy.rb you should change the server ip address, :user (I’m using deployer), :repository path (pnhoang), and note that :port is not 22 since we changed it above to 1026

Add these lines to: config/deploy.rb

require "bundler/capistrano"
require "rvm/capistrano"
require 'sidekiq/capistrano'

server "188.226.186.211", :web, :app, :db, primary: true

set :application, "my_store"
set :user, "deployer"
set :port, 1026
set :deploy_to, "/home/#{user}/apps/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false

set :scm, "git"
set :repository, "git@github.com:pnhoang/my_store.git"
set :branch, "master"

default_run_options[:pty] = true
ssh_options[:forward_agent] = true

after "deploy", "deploy:cleanup" # keep only the last 5 releases

namespace :deploy do
  %w[start stop restart].each do |command|
    desc "#{command} unicorn server"
    task command, roles: :app, except: {no_release: true} do
      run "/etc/init.d/unicorn_#{application} #{command}"
    end
  end

  task :setup_config, roles: :app do
    sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
    sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}"
    run "mkdir -p #{shared_path}/config"
    put File.read("config/database.example.yml"), "#{shared_path}/config/database.yml"
    puts "Now edit #{shared_path}/config/database.yml and add your username and password"
  end
  after "deploy:setup", "deploy:setup_config"

  task :symlink_config, roles: :app do
    run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
  end
  after "deploy:finalize_update", "deploy:symlink_config"

  desc "Make sure local git is in sync with remote."
  task :check_revision, roles: :web do
    unless `git rev-parse HEAD` == `git rev-parse origin/master`
      puts "WARNING: HEAD is not the same as origin/master"
      puts "Run `git push` to sync changes."
      exit
    end
  end
  before "deploy", "deploy:check_revision"
end

Capistrano create another file named: Capfile, we add these lines into the Capfile:

load 'deploy'
load 'deploy/assets'
load 'config/deploy'

SHAKE HANDS WITH BITBUCKET

In the original article, the author guides you to use GitHub. I like GitHub too, but it costs to have a private repository, I switch to BitBucket and life still goes fine. One lesson learned: don’t stick with your own traditional thinking, try something new. Though in this case, BitBucket is not a new thing, it’s just because I did not notice its existence.

First let’s check in our local machine

$ cd ~/.ssh
$ ls -al
# Lists the files in your .ssh directory

Check the directory listing to see if you have files named either id_rsa.pub or id_dsa.pub. If not, then we use a tool called the ssh-keygen to generate the private and public key.

$ ssh-keygen -t rsa -C "your_email@example.com"
$ ssh-add id_rsa

Copy the contents of the id_rsa.pub file to clipboard (on Mac OSX)

$ pbcopy < ~/.ssh/id_rsa.pub

Add SSH key to BitBucket: https://bitbucket.org/account/user/pnhoang/ssh-keys/ (use your own account). Paste the content of the clipboard into the Key field, and click on Add Key.

Verify if we are able to connect to bitbucket using ssh from the local machine:

$ ssh -T hg@bitbucket.org
conq: logged in as username.

this is for GitHub, I do not use GitHub here, but I leave it here for reference.

$ ssh -T git@github.com
# Attempts to ssh to github

Add ssh key to digitalocean VPS:

# if we get an error "No such file or directory, when run this command", then run $ ssh -p 1026 deployer@188.226.186.211 and $ mkdir -p ~/.ssh/
$ cat ~/.ssh/id_rsa | ssh -p 1026 deployer@188.226.186.211 'cat >> ~/.ssh/id_rsa'
$ chmod 600 ~/.ssh/id_rsa

Verify that we are able to connect to bitbucket from the remote server (VPS) on Digital Ocean:

$ ssh -p 1026 deployer@188.226.186.211
$ ssh -T hg@bitbucket.org

If error: "Could not open a connection to your authentication agent"

$ eval `ssh-agent -s`
$ ssh-add -l
$ ssh-add 

RAILS TOKEN

A better way to manage the Rails secret token

# Be sure to restart your server when you modify this file.

# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.

MyApp::Application.config.secret_token = if Rails.env.development? or Rails.env.test?
  ('x' * 30) # meets minimum requirement of 30 chars long
else
  ENV['SECRET_TOKEN']
end

Add to Gemfile:

gem 'dotenv-rails'

it loads environment variables from the .env file. Simply create this file in the RAILS_ROOT on the production web server.

copy the secret_token from config/initializers/secret_token.rb into .env file and also the redis_url:

# .env should NOT be checked in to source control
SECRET_TOKEN=068605091eab388035b25cb9990085cf047bbeb1bf1ec3a643e7f13fa638cc4f9b0d923c1bd41117032f559a7b0ca095bc0b6c3e76fe854b4f3f47c115718983
REDIS_URL=redis://188.226.186.211:6379

Read more on Phusion Corporate Blog: Securing the Rails session secret token

Ruby on Rails security guide

Create repo and push to BitBucket

Go to our my_store folder and initialize a git directory, but before, create a new .gitignore file in that folder and copy the content from github/gitignore

# Add config/database.yml to .gitignore
# https://github.com/github/gitignore/blob/master/Rails.gitignore

Add ‘sidekiq’ gem into our Gemfile:

gem 'sidekiq'

Install and update

$ bundle install

$ bundle update

Initialize git directory and push code to bitbucket:

$ cp config/database.yml config/database.example.yml
$ git init
$ git add .
$ git commit -m "Inital Commit"
$ git remote add origin git@bitbucket.org:pnhoang/my_store.git
$ git push origin master

DEPLOYMENT

$ cap deploy:setup

if error: connection failed for: 188.226.186.211 (Net::SSH::AuthenticationFailed: Authentication failed for user deployer@188.226.186.211), check this link

$ gem uninstall net-ssh -v 2.8.0

Install net-ssh -v 2.8.1 will fix the error, add this into Gemfile:

gem ‘net-ssh’, ‘~> 2.8.1’, :git => “https://github.com/net-ssh/net-ssh”

$ bundle install

Edit database.yml on server to add username and password to the production database, see here

$ nano apps/my_store/shared/config/database.yml

Use one of the following configurations depends on postgresql or mysql you are using:

production:
  adapter: postgresql
  encoding: unicode
  database: my_store_production
  pool: 5
  username: deployer
  password: secret

production:
  adapter: mysql2
  encoding: utf8
  database: my_store_production
  pool: 5
  username: deployer
  password: secret
  # socket: /tmp/mysql.sock

Restoring the database

I have the dump file of the database, I need to first copy it into one location on the server before running import command.

An article on Copy Files Over SSH

Secure copy is a really useful command, and it’s really easy to use. The basic format of the command is as follows:

$ scp [options] original_file destination_file

The biggest kicker is how to format the remote part. When you address a remote file, you need to do it in the following manner:

user@server:path/to/file

The server can be a URL or an IP address. This is followed by a colon, then the path to the file or folder in question. Let’s look at an example.

$ scp -P 1026 dump2.sql.zip deployer@188.226.186.211:~/apps/my_store/dumps/dump2.sql.zip

This command features the [-P] flag (note that it’s a capital P). This allows me to specify a port number instead of the default 22.

Install unzip utility tool and unzip the file:

$ sudo apt-get install unzip

$ unzip dump2.sql.zip

$ mysql -u root -p
password: secret

> create database my_store_production;
> exit

Restoring the database from the file dump:

$ mysql my_store_production -u root -p < dump2.sql
password: secret

mysql> CREATE USER 'deployer'@'localhost' IDENTIFIED BY 'secret';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'deployer'@'localhost'
    ->     WITH GRANT OPTION;

Installing mysql2 gem on VPS

$ sudo apt-get install libmysql-ruby libmysqlclient-dev

$ gem install mysql2

COLD DEPLOY

Come back to our machine, we can now cold deploy to ask our server to fetch the source code from bitbucket and install the necessary gems:

$ cap deploy:cold

Then remove the nginx default folder (if it doesn’t exist don’t worry about it)

$ sudo rm /etc/nginx/sites-enabled/default

Restart Nginx

$ sudo service nginx restart
$ sudo update-rc.d -f unicorn_my_store defaults

Hoooraay!

Navigate to: http://188.226.186.211. Hey it works! We have a fresh Spree site installed on a Virtual Private Server on Digital Ocean.

Now whenever you make changes do the following.

# Make changes
$ git add .
$ git commit -m "Changes"
$ git push origin master

Deploy

$ cap deploy

Stop and Start the unicorn server

$ sudo service unicorn_my_store stop
$ sudo service unicorn_my_store start

Restart Nginx

$ sudo service nginx restart

And it just works! That’s really awesome!


Jan 21

Tutorial - Deploying Spree Commerce into Heroku


Deploying Spree to Heroku Tutorial

Recently I had a chance to work with Spree Commerce, an open source e-commerce framework based on Ruby on Rails platform. Follow the tutorials and the deploying instruction to Heroku on Spreecommerce site, I thought that they are all enough, but during the way, I had so many troubles that I would like to write them down here so anyone with similar issues can see.

In this document, I will document the step-by-step guide to launch a fresh SpreeCommerce site into Heroku, so that Spree Commerce will be available immediately accessible via an http link.

Overview

SpreeCommerce has a nice tutorial on getting started with Spree, you can follow the tutorial until you get the result on your local browswer with http://localhost:3000/. Summarizing the steps:

$ gem install rails   # install rails
$ gem install bundler # the rails bundler
# install ImageMagick, which is the image library necessary for handling images
$ brew install imagemagick
# the command line utility for Spree commands 
$ gem install spree_cmd   

# create a new fresh Rails application
$ rails new mystore 
# from now on, we will work on this directory
$ cd mystore 
# install Spree using the default configuration into our Rails app
$ spree install --auto-accept 
# start the server, if everything's ok, then you should be able to 
# visit: http://localhost:3000/
$ rails server 

# we init a git directory, because Heroku relies on Git to be able to push to their server
$ git init 
# I use Sublime Text to edit the .gitignore file, you can use any text editor to add the .gitignore file.
# Locate the rails.gitignore from https://github.com/github/gitignore
$ subl .gitignore  
# add everything, except the ignored ones. 
$ git add . 
$ git commit -m "add initial Rails application"

Deploying to Heroku

In Gemfile, specify the ruby version:

ruby '2.1.0'

Add the [Heroku 12 Factor gem] to our Gemfile:

gem 'rails_12factor', group: :production

and because Sqlite can not be used as a database on Heroku, we need to switch to Postgresql or MySql, in my case, I chose Postgresql. In Gemfile, replace the ‘sqlite3’ gem with ‘pg’ gem.

# gem 'sqlite3'
gem 'pg'

Now we can run

$ bundle install

so that the gems are installed for our Rails project. When we switch to use Postgresql, we also have to change the database configuration, in the database.yml, we should update the test, development and production database configuration as follows:

development:
  adapter: postgresql
  encoding: unicode
  database: spreecommercetest
  pool: 5
  password:

test:
  adapter: postgresql
  encoding: unicode
  database: spreecommercedev
  pool: 5
  password:

production:
  adapter: postgresql
  encoding: unicode
  database: spreecommerceprod
  pool: 5
  password:  

You have to setup local postgresql and start the server for your Rails app to work normally again because we have already switched to use postgres, if you start with $ rails server and have an error, it’s probably because you have not started postgres server. You can continue to use sqlite for development and test, however, to avoid database specific errors, we should all switch to use the same database.

In the ‘config/application.rb’, add this line inside the config block:

config.assets.initialize_on_precompile = false

and because we will use Amazon S3 to store the images, add the following lines to config/initializers/spree.rb, so that Spree will upload images to S3, you have to get your own bucket, key, secret values from your Amazon S3 account:

Spree.config do |config|
  config.use_s3 = true
  config.s3_bucket = ''
  config.s3_access_key = ""
  config.s3_secret = ""

  # if you create your Amazon S3 bucket on Western Europe server, you need these two additional options:
  # config.attachment_url = ":s3_eu_url"
  # config.s3_host_alias = "s3-eu-west-1.amazonaws.com"
end

and while you are on this file, append the following line to the end of the file

Paperclip.interpolates(:s3_eu_url) do |attachment, style|
"#{attachment.s3_protocol}://#{Spree::Config[:s3_host_alias]}/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/},"")}"
end

Now we are ready to commit to our master branch on git:

$ git add .
$ git commit -m "update config for heroku"

and you think that we can now push to heroku using the $ git push heroku master, but we are not ready yet. First, let’s make sure that you are logged in to heroku

$ heroku login

and for uploading to Cedar stack, we need to run the assets precompile rake, be sure to start postgres server

$ RAILS_ENV=production bundle exec rake assets:precompile
$ git add public/assets
$ git commit -m "add vendor compiled assets"

Remove the line config/initializers/secret_token.rb, in your .gitignore file if when copying the Rails.gitignore file, you have it. Edit the file config/initializers/secret_token.rb, replacing the line with MyApp::Application.config.secret_key_base, and update it to:

Deploystore::Application.config.secret_token = ENV['SECRET_TOKEN']

In , run the following command to generate the token secret.

$ rake secret

Notice the value generated, and create a new file called .env at the top level project directory (where you have .gitignore and Gemfile). Copy the value to SECRET_TOKEN and save file:

SECRET_TOKEN=306d...

Now we can add this file into git and we are now ready to push to heroku

$ bundle install
$ git add .
$ git commit -m "Add token secret to environment"
$ heroku create # if here, heroku asks you to input the credit card because you have finished their 5 apps limits, login to their website and go to account information and input the credit card
$ git push heroku master

Now we can run the rake db:migrate command to set up the schema, normally you would do

$ heroku run rake db:migrate

but because, my ISP provider Fastweb in Italy blocks all connections on port 5000 which heroku uses, here I need to run it on detached mode:

$ heroku run:detached rake db:migrate

Finally, we can see our empty e-commerce site, this command will open our Spree Commerce app on the browswer:

$ heroku open

Summarize and references

Now that we see the sample Spree Commerce application running on a herokuapp URL, I put here some errors and pointers that you might got when you missed one of the steps above:

$ RAILS_ENV=production bundle exec rake assets:precompile
$ git add public/assets
$ git commit -m "vendor compiled assets"
$ heroku run:detached rake db:migrate

Nov 23

My 2 days at Slush conference in Helsinki, Finland

It happened to me to be in Turku, Finland during the time of the conference, I arrived in Turku a week before to visit my sister and to sign up some documents with the bank as I opened my company called Cosmo Studios Oy here with her to focus on iOS development. I heard of the conference through Facebook by the guys at BoostTurku, “Slush is the largest tech, design & startup conference covering Northern Europe and Russia”, as they advertised on their website, I found it to be a perfect chance to extend my stay in Finland, so I decided to delay the return ticket to Milan to two days after the conference. After attending the conference, I can say that I did not regret about my decision.

————Follow me on Twitter: @pnhoang —————-

I registered through their website in a lightweight track as a startup, with hope that they will accept my application and let me have the chance to demo my application FMFinder during the event, but that was not the case. There were about 500 startups all over Europe registered to their website, and there’re only 100 slots for pitching. I got accepted as a startup but without the chance to pitch, I was a little bit disappointed at first, but I decided to go to the conference anyway. I bought the train ticket through vr.fi website and took the early train from Turku to Helsinki, as I want to save, I chose a Hostel close to the station that costs the least, it was ok for me by the way, the Hostel Erottajanpuisto has affordable price, and I had chance to talk to a bunch of people the night I stay there. Arrived in Helsinki station, I walked to the hostel which took me about 20 minutes, not so bad, exercising in the morning, dropped my luggage inside, and headed to the bus station close to the hostel. The bus 20 took me directly to the Cable Factory, where the conference was taken place, it was the place where early employees of Nokia started to develop their first phones. Arrived at 9.30 in the morning, it rained lightly outside, my first impression in any tech conference was the queue to register to get my badges, the organizers seemed to be very professional, they divided the badges to several difference booths by attendances’ last name, one could just jump in the queue with the last name and get the badge really fast. The info desk is right inside, where they provided me with all information about the locations, breakfast, restaurants, the stages. Breakfasts are included in the ticket conference, so the first thing I did was filling my stomach for the busy morning program ahead. In time to get back to the main stage where the Finnish prime minister Jyrki Katainen had his initial speech to officially open the conference, I heard how eager the leader of a high tech country was with his speech, applauded by the whole audience, then the keynote by Kristian Segerstråle was also very impressive, MCs announced that there were about 3000 people inside the venue, such a big number of people in a tech conference gathered by a non profit organization. After the lightning talks by David Gardner from EA and Jason Chein from Amazon, the team at Jolla Mobile officially launch the mobile operating system called Sailfish OS, the one developed by the team behind MeeGo of Nokia. I was not really looking for this but the whole audience seemed to be very supported, easily understood as most of them are Finnish. Finns can’t stand to see their biggest phone maker on the planet loosing their market shares to Apple, Google and Samsung. It’s promising, the team had their great demo. Hope they will become good player on the market. 

I did not have much time in the main stage, as I felt I need to talk to investors to introduce as fast as I can my application FMFinder. So I walked out, a lot of companies presenting their products in the stands, investors also have their own stands, but in the pitching stage, something caught my eyes, several small startups have their two-three minutes pitching to the judges and the audience, I sat down and listen to some of them, it happened to be the one I listened to on the very first day won the final price the day after, FishBrain. There are so many different things that happened in different places, I felt quite lost in the first day, one reason be that I did not spend time reading the agenda very carefully the day before, other be that there were so many startups participated that I gave up looking on each of the website of the companies on slush web page. Lunch time was the best time to talk, I had great time talking with several different people that sat down in the same table with me, only at lunch time, people are willing to talk most as I realized that during the event, you don’t have such a chance. Investors, developers, CEOs, founders, students, media reporters were all there eating the same dish as me, I was not lucky enough to get my application idea to the right person during lunch break, but I had great time talking to a couple of investors in their stands after that. There are several startups looking for funding, 500 of them and investors were limited, it was like 50 guys dating with a girl, you had to be stand out from the crowd for them to be considered. But I did not give up on presenting my application to a couple of them, VisionPlus, Tekes, Polte had their stands with someone to talk to, so I didn’t miss the chance. Other venture capitalists and angel investors I met during lunch break or inside the venue did not seem to be interested. I was so innocent to think that investors are the one that having money and startups are the one that need them, so let’s pitch to anyone I can see in their badge written “Investor”. Thing does not happen that way, I talked to some venture capitalists and angel investors, their interests are in very specific field of the industry, or sometimes only on a very high level of standards. I wish that on Slush website, there’s a section describing what the venture capitalists, angel investors are specializing on, the contact information, or at least their Twitter accounts to follow. Playing match game with dozens of investors seemed to be pretty hard. I asked the event assistant in the info desk if there’s a way to talk to investors, the only information that she can give me is the color of the badge of the investors, and that I could see them in the venue walking somehow. Hmm, that’s interesting. 

The first day event seemed to last for the whole night, after the event conference, the main stage was closed for the production of the TV channel MTV3, a Finnish broadcasting company, they had direct program that interviewed Peter Vesterbacka, the founder of Rovio Mobile and also the event organizers. The after party was provided by Supercell, a Finnish company that have two of the titles among the top grossing iOS games on the AppStore, Hay Day and Clash of Clans. Beers, wines, champagnes, fruit juices are always ready to be served, outside, they are grilling hot dogs on one side and on the other side, people are relaxing by entering to the sauna bus, that is typical Finnish culture, almost every house has their own sauna. The event organizer wants to bring that to the participants of Slush as well, I didn’t know about that sooner otherwise I also brought my own swim suit to enjoy! The party was the perfect time for networking, but unfortunately I had to come back to the hostel with the late night bus.

Second day of the conference, I brought my luggage and leave it at the cloakroom, the assistants were so nice, they always smiled when I brought them my luggage and got it back after the day. I spent the breakfast talking with a bunch of people, I presented my application FMFinder to them and got several suggestions such as how I could improve the UI for the app to look better, which color schemes I should use. Arrived in the main stage where there were several different speakers talking about design, it was so good to listen to them, I learned quite some lessons from leading designers in the industry. Although I had the feeling that one had a unprepared presentation, not sure if he drank so much the day before. I was tired after the lunch break so I decided to get into the stands to talk to other developers to see how they are doing, there are bunches of awesomenesses that developers, CEOs and founders all over Europe and Russia brought to the conference. Ovelin has been around for quite some years, but it’s the first time I know them, their product called Guitar Bots  is definitely the thing that I will try to learn to play guitar when I come back to Milan, using the guitar itself as a controller, one can play the guitar and on the screen, the virtual band will rock your afternoon, if you are a good guitar player then you’ll be a super star with this application. I listened to the presentation of Berggram in the Event stage, it seemed a huge amount of work had been spent on designing the Music Theory Automation. Deveo had the git hosting service themselves and it promised to give small team free hosting forever, so why not try their product? Rovio Mobile had their stand for two days, who does not know that they are the makers of several Angry Birds games in the AppStore, and the Holywood movie Angry Birds Rio. Holvi helps provide the banking solution to business owners the easiest way possible, their sale team was very helpful in giving me useful information to help my sister build her own payment website to help her guesses pay when they stay in her apartments in Turku. Info.gram sounds promising, as they revealed during the event in the Event Stage, several big media companies in the world use their service to generate infographics graph to put into the articles. Several others that I did not have enough time to write them down here. Check out the list of all startups on Slush webpage to find out by yourself!

In the main stage, the winner of the lightweight track finally was announced, FishBrain. Slush 2012 ended. I came back to Turku to stay for two days before taking the flight to go back to Milan, Italy. Hope I will be here in the conference next year with my own stand. Who knows?

About me:

Hoang Pham graduated from Ho Chi Minh City University of Technology, he spent two years working for a Danish company in Sai Gon, he was so bored that he decided to get a master degree in Politecnico di Milano, Italy. He has spent the last 3 years dedicating on iPhone/iPad development working as a consultant and an employee. 


Sep 28

Gemfile install issue with Mountain Lion

I have been traveling for almost 4 months and so I have not updated the blog so far. This morning I ran a new Rails app and get this issue:

$ cd work

$ rails new test_app

…

An error occured while installing json (1.7.5), and Bundler cannot continue.

Make sure that `gem install json -v '1.7.5'` succeeds before bundling.

I found the solution on StackOverflow, which relates to the “Command line tools” not being installed by default on Mountain Lion for Xcode 4.3. The original post suggests downloading the “Command line tools” through Preferences panel of Xcode or through Apple’s download website. After installation, run the command:

$ sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer

And make the symbolic link on gcc-4.2 in order for make command to work

$ sudo ln -s /usr/bin/gcc /usr/bin/gcc-4.2

That’s it. It should now solve the bundler install error.


Jun 1

Build a Rails backend API for an iPhone client

I have been an iOS developer for over three years, but I am a newcomer to Rails. I think the best way to learn Rails is to do a sample Todo list app with RESTFUL API and basic authentication, and because I want to review my iOS skills, I will write an iOS client to query the server’s API to show on the iPhone and iPad. Along the way, I will document the process of making these two projects and the source code for all two of them will be available on my Github account

The purpose of this entry is to learn Rails, so I will use a lot of techniques/frameworks/open source projects along the way:

  • I will use Twitter’s bootstrap framework to make the front end on the web look nicer
  • I will use Markdown for editing ToDo item, as sometimes I want to be able to show links in my ToDo item.
  • Many techniques from Railscasts’ Ryan Bates. I strongly suggest you subscribe to his site for $9/month because he always bring to the community the best knowledge.
  • Many techniques learned from Rails tutorial book by Michael Hartl.

Enough for the introduction, let’s get started.

Warning: Part 1 is pretty long and does not contain any iOS code yet!!!

PART 1: THE SERVER RAILS APP

Configuration

I use Rails 3.2.3 and Ruby 1.9.2 for the server and iOS 5 SDK with Xcode 4.2 for the client. 

Todo project with Rails

Let’s create the project using the rails new command

$ cd work
$ rails new todo-app
$ cd todo-app
$ git init 
$ git add . 
$ git commit -m "Initial Rails project"

At this time, we already have a running Rails app, if we want to see what’s already there, we can try:

$ rails server

And then we can open the Chrome browser, type http://localhost:3000/ and the magic default Rails homepage will appear

We don’t need just that, we’ll soon delete that homepage, for now, let’s open the project in the editor so that we can make some modifications

$ mate .

My favorite editor of choice is TextMate, and here I use the mate command line to open the current directory, which is now the whole Rails application.

Let’s edit the gemfile,  to add Twitter bootstrap gems to our project, we’ll use the twitter-bootstrap-rails gem because it provides us with some useful commands to speedup our process.

group :assets do
     ...
     gem 'twitter-bootstrap-rails'
end

Then I’ll run the bundle command to install the new gem:

$ bundle install

After that we’ll run the rails generate command to install the bootstrap

$ rails generate bootstrap:install 

You’ll notice that there are some javascript, css and less files installed in your assets folder in the Rails app. That means that we are ready to have Bootstrap supported in our application. Now let’s create our first model view controller (MVC) ToDo item using the rails generate scaffold command, our todo will have only two fields: item for storing the todo, completed_at for storing the completion date, if it is nil then it’s not yet done, and then we migrate the database using the rake command. (You’ll notice that there’s something wrong here, the completed_at should be datetime, we’ll see how to fix our schema later)

$ rails generate scaffold todo item:text completed_at:date --skip-stylesheets
$ rake db:migrate

We use the —skip-stylesheets command to avoid using the stylesheets generated by the scaffold, as we want to use the bootstrap stylesheets.

The gem twitter-bootstrap-rails comes with some handy functions to enable our application to have the nice layout, we’ll use the rails generate bootstrap:layout command to have our application a fixed layout

$ rails generate bootstrap:layout application fixed

At this time, if we run the rails server command, then we’ll see a nice layout applied to our todos page, it automatically adds a nav bar and a side bar to our project and apply the correct CSS classes to the page, http://localhost:3000/todos 

 We are really happy now, and we go ahead to apply the bootstrap’s themed to our todos resource, the gem we used has another command to generate the themes as follows:

$ rails generate bootstrap:themed todos

When run this command, it will ask for the resolution of conflicts, but it’s ok to answer Y (YES) to all the questions. You can run the generate command with -f option to force all to override the conflicts with the ones from bootstrap. Now we have a pretty complete and beautiful Todo app, we can navigate to create new todo, update, delete them.

Adding new todo item:

And the new listing Todos page looks like this:

Now let’s add some validation to our code, we don’t want a Todo item to not have any text, but before doing that, we run the test to see if the auto generated ones pass

$ rake test
Finished in 0.004226 seconds.

0 tests, 0 assertions, 0 failures, 0 errors, 0 skips

Finished in 0.440987 seconds.

7 tests, 10 assertions, 0 failures, 0 errors, 0 skips

Let’s open the model file in app/models/todo.rb and add the validation for todo’s item

class Todo < ActiveRecord::Base
  attr_accessible :completed_at, :item
  
  validates :item, presence: true
end

Now if we open the rails console and try to create a new todo item without specifying the item text, then it will fail:

$ rails console --sandbox
Loading development environment in sandbox (Rails 3.2.3)
Any modifications you make will be rolled back on exit
1.9.2p290 :001 > todo = Todo.new(item: "")
 => # 
1.9.2p290 :002 > todo.save
   (0.3ms)  SAVEPOINT active_record_1
   (0.1ms)  ROLLBACK TO SAVEPOINT active_record_1
 => false 
1.9.2p290 :003 > todo.valid?
 => false 
1.9.2p290 :004 > todo.errors.full_messages
 => ["Item can't be blank"] 
1.9.2p290 :005 > quit
   (0.2ms)  rollback transaction

Let’s go ahead and add the alert when we successfully create/update a todo item. Open the app/views/layouts/application.html.erb and add the following code:

    <div class="container">
      <div class="content">
        <% flash.each do |name, msg| %>
          <div class="alert alert-<%= name == :notice ? "success" : "error" %>">
            <a class="close" data-dismiss="alert" href="#">x</a>
            <%= msg %>
          </div>
        <% end %>
        ...

Now when we create/update an item, there will be an alert with the corresponding message and a close button to dismiss it.

Next, we also want to add the simple_form gem into our application so that the validation of the fields in the form work. This gem will help us generate the correct stylesheets class for our form’s fields and additionally install Bootstrap CSS classes for us.

First we need to add gem ‘simple_form’ to the end of our gemfile:

...
gem 'simple_form'

Then we run the simple_form:install command to generate the configuration and scaffold template:

$ rails generate simple_form:install --bootstrap
       exist  config
      create  config/initializers/simple_form.rb
      create  config/locales/simple_form.en.yml
      create  lib/templates/erb/scaffold/_form.html.erb
===============================================================================

  Be sure to have a copy of the Bootstrap stylesheet available on your
  application, you can get it on http://twitter.github.com/bootstrap.

  Inside your views, use the 'simple_form_for' with one of the Bootstrap form
  classes, '.form-horizontal', '.form-inline', '.form-search' or
  '.form-vertical', as the following:

    = simple_form_for(@user, :html => {:class => 'form-horizontal' }) do |form|

===============================================================================

Next, we need to change form_for function to use simple_form_for and just let simple_form handle the correct types of form fields for us, like so:

# app/views/todos/_form.html.erb

<%= simple_form_for @todo, :html => { :class => 'form-horizontal' } do |f| %>

  <%= f.input :item %>
  <%= f.input :completed_at %>
   
  ...
<% end %>

# app/views/todos/_form_new.html.erb
<%= simple_form_for @todo, html: { class: 'form-horizontal'} do |f| %>
  
  <%= f.input :item %>
  
  ...
<% end %>

Now that when an item is created with blank, then the error will be displayed nicely like this:

Use Markdown to highlight Todo items

At this stage, we are quite happy now with our product, but here we want to go further, let’s enable our wonderful users to use Markdown to create/edit their Todo items, how great is that? When creating their Todo item, for example, “Read the Ruby on Rails tutorial”, they can easily create “Read the [Ruby on Rails tutorials](http://ruby.railstutorial.org)”, and the Todo item will be nicely converted to “Read the Ruby on Rails tutorials

Notice: in this section, I use the materials that come from the Pro version of railscasts.org 207 Syntax highlighting revised, please consider support Ryan Bates by subscribing to his site for only 9$/month.

We are going to need two new gems, pygments.rb and redcarpet. Let’s go ahead and add them into our gemfile, remember to run the $ bundle install command after that:

# Gemfile
..
gem "pygments.rb", "~> 0.2.12"
gem 'redcarpet'


In order to use the Pygments.rb syntax highlighting, we need to add Pygments.css to our project, luckily, the function Pygments.css add this file to our project for us, so we are going to create a new file pygments.css.erb in our assets/stylesheets folder

# /app/assets/stylesheets/pygments.css.erb
<%= Pygments.css %>

Now if we restart our server with $ rails server, we may encounter a crash of the whole rails application, it’s because Pygments is the syntax highlighting on the Server side written in Python, Ruby gem pygments.rb is the Ruby port of this Python program, it executes Python code using Python binary, if your system has the 32-bit only Python, you may encounter the error:

Could not open library '/Library/Frameworks/Python.framework/Versions/2.7/lib/libPython2.7.dylib': dlopen(/Library/Frameworks/Python.framework/Versions/2.7/lib/libPython2.7.dylib, 9): no suitable image found. Did find:
/Library/Frameworks/Python.framework/Versions/2.7/lib/libPython2.7.dylib: no matching architecture in universal wrapper
(in /Users/pnhoang/Developer/rails_casts/railscasts-episodes/episode-207/revised/highlighter-after/app/assets/stylesheets/pygments.css.erb)

Download and install the 64bit binary Python will solve this problem.

Next step is to add a new class HTMLwithPygments and a function markdown into our ApplicationHelper

#/app/helpers/application_helper.rb
module ApplicationHelper
  class HTMLwithPygments < Redcarpet::Render::HTML
    def block_code(code, language)
      sha = Digest::SHA1.hexdigest(code)
      Rails.cache.fetch ["code", language, sha].join('-') do
        Pygments.highlight(code, lexer: language)
      end
    end
  end

  def markdown(text)
    renderer = HTMLwithPygments.new(hard_wrap: true, filter_html: true)
    options = {
      autolink: true,
      no_intra_emphasis: true,
      fenced_code_blocks: true,
      lax_html_blocks: true,
      strikethrough: true,
      superscript: true
    }
    Redcarpet::Markdown.new(renderer, options).render(text).html_safe
  end
end

The functions defined in /app/helpers/ folder are automatically available in the controllers and the views, so we can call markdown from our show.html.erb and index.html.erb as below:

# /app/views/todos/index.html.erb
  ..
        <td><%= markdown todo.item %></td>  
  ..

# /app/views/todos/show.html.erb
  ..
  <dd><%= markdown @todo.item %></dd>
  ..

Now we can edit and update our Todo items with Markdown, the results are shown in this figure:

Add Pagination into Todo items

One more twist we would like to add is the pagination into Todo items list page, suppose, you have 20 items in your Todo list, you would like to show only 5 items per page. That’s easy to do with Rails. We’ll use two new gems ‘will_paginate’ and the ‘bootstrap-will_paginate’ gem to configure ‘will_paginate’ to use Bootstrap stylesheets, and as usual, after adding the new gem, we need to run the $ bundle install command and restart the server.

# file: Gemfile
...
gem 'will_paginate'
gem 'bootstrap-will_paginate'

In order to use this gem, we just need to add a Ruby function call to will_paginate at the bottom of the index page.

# file: /app/views/todos/index.html.erb
...
<%= will_paginate %>

<%= link_to t('.new', :default => t("helpers.links.new")),
            new_todo_path,
            :class => 'btn btn-primary' %>

and changing our controller to call the ‘paginate’ function of the Todo model.

# file: /app/controllers/todos_controller.rb
  def index
    @todos = Todo.paginate(page: params[:page], per_page: 5)
    ...

And now, we have our awesome pagination for Todo items:

Fixing the database schema

I accidentally created the table todos with the column completed_at as date, it was wrong, either it should be a datetime column or it should be renamed completed and the data type is boolean. So let’s fix that before we go ahead on adding the Todo Lists to the database schema. The rule of thumb when handling errors in the schema is to create another migration to fix it.

Let’s first generate the migration using the generate command:

# 
$ rails generate migration ChangeTodoTable

When creating a migration to change a table’s structure, Rails does not support the change method so we need to define both the up and down functions. In the up function, we specify how we want to change the database forward, conversely, in the down function, we specify how to return back to the previous state. This implies that by doing an up and down, the database should come back to the current state. In our case, when we add a completed column with value true/false, we will populate the corresponding true/false value into column completed based on the completed_at date column, this requires us to do a loop through all rows in Todo table, after that, we will remove column completed_at. Hence the up function is as follows:

# db/migrate/20120521121851_change_todo_table.rb
class ChangeTodoTable < ActiveRecord::Migration
  def up
    change_table :todos do |t|
      t.boolean :completed
      Todo.all.each do |todo|
        if todo.completed_at != nil
          todo.update_attributes! completed: true
        else
          todo.update_attributes! completed: false
        end        
      end
      t.remove :completed_at
    end
  end

  def down
    ...
  end
end

With the up function in place, we need to set the attrs_accessible to completed in our model, otherwise Rails will complaint about mass assigned values error

# app/models/todo.rb
class Todo < ActiveRecord::Base
  attr_accessible :completed_at, :item, :completed
  
  ...
end

Now we can run the $ rake db:migrate command to see the changes reflected in the database. We can open the Rails console to see the changes in action.

# BEFORE MIGRATION

$ rails c
Loading development environment (Rails 3.2.3)
1.9.2p290 :001 > Todo.first
  Todo Load (0.1ms)  SELECT "todos".* FROM "todos" LIMIT 1
 => #<Todo id: 5, item: "Read the [Ruby on Rails Tutorials](http://ruby.rail...", created_at: "2012-05-16 16:14:05", updated_at: "2012-05-21 13:00:39", completed_at: "2012-05-21"> 

$ rake db:migrate

# AFTER MIGRATION
$ rails c
Loading development environment (Rails 3.2.3)
1.9.2p290 :001 > Todo.first
  Todo Load (0.1ms)  SELECT "todos".* FROM "todos" LIMIT 1
 => #<Todo id: 5, item: "Read the [Ruby on Rails Tutorials](http://ruby.rail...", created_at: "2012-05-16 16:14:05", updated_at: "2012-05-21 13:20:39", completed: true> 


With the down function, we just need to do the reverse process, first we add the completed_at as date data type, then we loop through all Todo rows and update the completed_at with the latest date of updated_at. (Notice: this is not really correct down migration, though for us now, it’s not too important the exact completed_at date)

# db/migrate/20120521121851_change_todo_table.rb
class ChangeTodoTable < ActiveRecord::Migration
  def up
    ...
  end

  def down
    change_table :todos do |t|
      t.date :completed_at
      Todo.all.each do |todo|
        if todo.completed == true
          todo.update_attributes! completed_at: todo.updated_at.to_date
        else
          todo.update_attributes! completed_at: nil
        end
      end
      t.remove :completed
    end    
  end
end

If you want to try to rollback, just try $ rake db:rollback and now the database schema is the same as before. Though we don’t want to run it now. Just for completeness, we write the down function.

After running the $ rake db:migrate command, we can remove the :completed_at from the attr_accessible of Todo model

# app/models/todo.rb
class Todo < ActiveRecord::Base
  attr_accessible :item, :completed
  
  ...
end

And also be able to remove the _form_new.html.erb from folder app/views/todos/, as now we don’t need a separate form for creating new Todo. In the app/views/todos/new.html.erb, we can revert back to use render :partial ‘form’

# 
...
<%= render :partial => 'form' %>

Everywhere we have the completed_at, we should now change to completed, luckily because we use simple_form_for for our attributes, we don’t need to re-create the form’s field for completed. simple_form_for is intelligent enough to use the checkbox in place for completed field

# app/views/todos/show.html.erb
  <dt><strong><%= model_class.human_attribute_name(:completed) %>:</strong></dt>
  <dd><%= @todo.completed %></dd>

# app/views/todos/index.html.erb
...
      <th><%= model_class.human_attribute_name(:item) %></th>
      <th><%= model_class.human_attribute_name(:completed) %></th>
      <th><%= model_class.human_attribute_name(:created_at) %></th>
...

        <td><%= markdown todo.item %></td>
        <td><%= check_box_tag :completed, todo.completed.to_s,  todo.completed, disabled: true %></td>
        <td><%=l todo.created_at %></td>

Add Todo lists to database schema

We are almost ready with the Todo items so far, but as our Todo items grow, we need to organize the Todo items into lists. The next iteration we will make is to generate the scaffold for Todo lists

$ rails generate scaffold List name:string --skip-stylesheets

$ rails generate bootstrap:themed lists -f

With the generate scaffold command, we don’t want to generate the scaffold stylesheets, so we pass the option —skip-stylesheets to the generator. Right after creating the scaffold, we want to apply the Bootstrap theme into the List views, so we run the generate bootstrap:themed command with -f option to force the changes in case there is conflict.

We need to have a reference from Todo item table to List table, so we create a database migration and then modify the change function

$ rails generate migration AddReferencesToTodo

# db/migrate/add_reference_to_todo.rb
class AddReferencesToTodo < ActiveRecord::Migration
  def change
    change_table :todos do |t|
      t.references :list
    end
  end
end

In the model classes, we need to define some relationships: a List has many Todos, and conversely, Todo belongs to a List. We also declare the relationship between Lists and Todos with dependent: :destroy, because  we want to delete all of a list’s Todo items whenever we delete a list. This declaration is necessary in the parent item, not the child item.

# app/models/list.rb
class List < ActiveRecord::Base
  attr_accessible :name
  
  has_many :todo, dependent: :destroy
end

# app/models/todo.rb
class Todo < ActiveRecord::Base
  ...
  belongs_to :list
end

And we would like to have the default value for completed as false instead of nil. First we need to add a callback to the after_initialize event, this callback function will then initialize the values of the instance of the class. Here’s the full Todo model up to this point

class Todo < ActiveRecord::Base
  attr_accessible :item, :completed
  after_initialize :default_values
  
  validates :item, presence: true
  belongs_to :list
  
  private
    def default_values
      self.completed ||= false
    end
end

Update the routes to nested resources

With our model ready, we can now update the routes to have nested resources. The concept is simple, one list has many todo items, so Todo items should be included in lists. In Rails routing, we do as follows

# /config/routes.rb
  resources :lists do
    resources :todos
  end

We can see all the routes that Rails created for us with $ rake routes command. These includes 7 restful resource routes for parent element: list and 7 restful resource routes for child element: todo within a list. The child element is accessed by using the /lists/:list_id/ path, all Todo items path starts with this prefix.

$ rake routes
        list_todos GET    /lists/:list_id/todos(.:format)          todos#index
                   POST   /lists/:list_id/todos(.:format)          todos#create
     new_list_todo GET    /lists/:list_id/todos/new(.:format)      todos#new
    edit_list_todo GET    /lists/:list_id/todos/:id/edit(.:format) todos#edit
         list_todo GET    /lists/:list_id/todos/:id(.:format)      todos#show
                   PUT    /lists/:list_id/todos/:id(.:format)      todos#update
                   DELETE /lists/:list_id/todos/:id(.:format)      todos#destroy
             lists GET    /lists(.:format)                         lists#index
                   POST   /lists(.:format)                         lists#create
          new_list GET    /lists/new(.:format)                     lists#new
         edit_list GET    /lists/:id/edit(.:format)                lists#edit
              list GET    /lists/:id(.:format)                     lists#show
                   PUT    /lists/:id(.:format)                     lists#update
                   DELETE /lists/:id(.:format)                     lists#destroy

The effect of adding nested resources into Todo items is that now we are not able to access Todo items anymore. There’s no way to create/edit/show/list todo items at the moment. However, adding this nested resource allows us to have a lot of new paths to access the todo items from the list. For example, we can see from above, to get the list of todo items of a specific list, we have the list_todos GET, this means that we can use the list_todos_path(@list) or list_todos_url(@list) to access the Todo list index page of a list.

We’ll make a small change to the show method of lists_controller to have a way to go back and forth. Let’s use Twitter boostrap’s breadcrumb to do this.

# /views/lists/show.html.erb
<%- model_class = @list.class -%>
<ul class="breadcrumb">
  <li>
    <%= link_to 'My Lists', lists_path() %>
    <span class="divider">/</span>
  </li>
  <li>
    <%= link_to 'Edit This List', list_todos_path(@list) %>
    <span class="divider">/</span>
  </li>
  <li class="active">
    <a href="#">Current List</a>
  </li>
</ul>

<h1><%=t '.title', :default => model_class.model_name.human %></h1>
...

While we are at show view, we add a method to add new Todo items and to view the existing Todo items if any. In case there is todo item, we want to render the whole collection, Rails will enumerate each item in the collection and apply the render partial on each item. As a result, we need to create a new partial _todo.html.erb in app/views/todos folder

<% if @list.todos.any? %>
  <h2>Todo items</h2>
  <%= render @list.todos %>
<% end %>

<h2>Add todo item</h2>
<%= render "todos/form"%>

When displaying the Todo items, we also want to use Markdown to highlight its syntax. Besides, for completed todo item, we will use Rails’s check_box_tag function to display a checkbox aside. Currently, we will disable this checkbox, as we have not had yet the Ajax function to enable checking/unchecking the completed status of a Todo item.

# app/views/todos/_todo.html.erb
<%- model_class = todo.class -%>

<dl class="dl-horizontal">
  <dt><strong><%= model_class.human_attribute_name(:item) %>:</strong></dt>
  <dd><%= markdown todo.item %></dd>
  <dt><strong><%= model_class.human_attribute_name(:completed) %>:</strong></dt>
  <dd><%= check_box_tag :completed, todo.completed.to_s, todo.completed, disabled: true %></dd>
</dl>

The command render todos/forms use a partial in app/views/lists called _form.html.erb. In this form, we will use simple_form_for to simplify the form generation. It comes with default error management on each form fields compatible with bootstrap

# app/views/lists/_form.html.erb
<%= simple_form_for @list, :html => { :class => 'form-horizontal' } do |f| %>
  <%= f.input :name %>
  <div class="form-actions">
    <%= f.button :submit, :class => 'btn-primary' %>
    <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
                lists_path, :class => 'btn' %>
  </div>
<% end %>

Now we should be able to add new Todo items to the List and view all of a list’s items. We can use Markdown syntax to add our Todo item, an example is shown below:

And here’s the result in the listings page:

Advertisement: I’ll be participating in this year’s WWDC event. I would be very happy to meet all of you. Follow me on Twitter: @pnhoang

Fixing the Todo items controller

We are now able to view the list and add items into it. However, we would like to fix the existed Todo item controller that we already did. The first one is the index, instead of directly getting the Todo from the params[:todo_id], we now have to first get the list from params[:list_id] and then use this list to find the todo items. 

# app/controllers/todos_controller.rb
  def index
    @list = List.find(params[:list_id])
    @todos = @list.todos.paginate(page: params[:page], per_page: 5)
    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @list }
    end
  end

In the view page, we need to replace the @todos variable with @list.todos, and the corresponding paths, for example, todo_path(@todo) will be list_todo_path(@list, todo) and edit_todo_path(@todo) will be edit_list_todo_path(@list, todo), new_todo_path will be new_list_todo_path. For reference, here is the full index page

<%- model_class = Todo.new.class -%>
<ul class="breadcrumb">
  <li>
    <%= link_to 'My Lists', lists_path() %>
    <span class="divider">/</span>
  </li>
  <li>
    <%= link_to 'View This List', list_path(@list) %>
    <span class="divider">/</span>
  </li>
  <li class="active">
    <a href="#">Current List</a>
  </li>
</ul>

<h1><%= @list.name %></h1>
<table class="table table-striped">
  <thead>
    <tr>
      <th><%= model_class.human_attribute_name(:id) %></th>
      <th><%= model_class.human_attribute_name(:item) %></th>
      <th><%= model_class.human_attribute_name(:completed) %></th>
      <th><%= model_class.human_attribute_name(:created_at) %></th>
      <th><%=t '.actions', :default => t("helpers.actions") %></th>
    </tr>
  </thead>
  <tbody>
    <% @todos.each do |todo| %>
      <tr>
        <td><%= link_to todo.id, list_todo_path(@list, todo) %></td>
        <td><%= markdown todo.item %></td>
        <td><%= check_box_tag :completed, todo.completed.to_s,  todo.completed, disabled: true %></td>
        <td><%=l todo.created_at %></td>
        <td>
          <%= link_to t('.edit', :default => t("helpers.links.edit")),
                      edit_list_todo_path(@list, todo), :class => 'btn btn-mini' %>
          <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
                      list_todo_path(@list, todo),
                      :method => :delete,
                      :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')),
                      :class => 'btn btn-mini btn-danger' %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>


<%= will_paginate %>

<%= link_to t('.new', :default => t("helpers.links.new")),
            new_list_todo_path,
            :class => 'btn btn-primary' %>

We also edit the show action by first locating to the params[:list_id] for list variable, then from this list, locating the todo item with params[:id].

  def show
    @list = List.find(params[:list_id])
    @todo = @list.todos.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @list }
    end
  end

In the view page, we also need to change the paths

<div class="form-actions">
  <%= link_to t('.back', :default => t("helpers.links.back")),
              list_todos_path(@list), :class => 'btn'  %>
  <%= link_to t('.edit', :default => t("helpers.links.edit")),
              edit_list_todo_path(@list, @todo), :class => 'btn' %>
  <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
              list_todo_path(@list, @todo),
              :method => 'delete',
              :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')),
              :class => 'btn btn-danger' %>
</div>

We can repeat the process for other actions in the controller: new, edit, create, update, destroy. Here is the remaining of the todos controller

# app/controllers/todos_controller.rb
  ...
  def new
    @list = List.find(params[:list_id])
    @todo = @list.todos.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @todo }
    end
  end

  def edit
    @list = List.find(params[:list_id])
    @todo = @list.todos.find(params[:id])
  end


  def create
    @list = List.find(params[:list_id])
    @todo = @list.todos.build(params[:todo])
    
    respond_to do |format|
      if @todo.save
        format.html { redirect_to list_todos_path(@list), notice: 'Todo item was successfully created.' }
        format.json { render json: @list, status: :created, location: @list }
      else
        format.html { render action: "new" }
        format.json { render json: @todo.errors, status: :unprocessable_entity }
      end
    end
  end


  def update
    @list = List.find(params[:list_id])
    @todo = @list.todos.find(params[:id])

    respond_to do |format|
      if @todo.update_attributes(params[:todo])
        format.html { redirect_to list_todo_url(@list, @todo), notice: 'Todo was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @todo.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /todos/1
  # DELETE /todos/1.json
  def destroy
    @todo = Todo.find(params[:id])
    @todo.destroy

    respond_to do |format|
      format.html { redirect_to todos_url }
      format.json { head :no_content }
    end
  end

Last thing we need to do is to change the form for creating, updating Todo item. We should have an array of two objects for @list and @todo, we would normally write simple_form_for [@list, @todo]. However, in the case of @todo is empty such as when creating a new Todo from the list view page, we need to populate the form with Todo item fields. We do that by calling @list.todos.build method.

# app/views/todos/_form.html.erb

<%= simple_form_for [@list, if @todo.nil? then @list.todos.build else @todo end], :html => { :class => 'form-horizontal' } do |f| %>

  <%= f.input :item, input_html: {class: "span6", rows: 3}  %>
  <%= f.input :completed%>

  <div class="form-actions">
    <%= f.submit 'Save', :class => 'btn btn-primary' %>
    <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
                lists_path, :class => 'btn' %>
  </div>
<% end %>

In the above, we also change the style class of item input, we use bootstrap’s grid system to expand our input text field to span6, and 3 rows.

The index.html.erb and show.html.erb for todo items do not change because we just render the partial form.

Adding user authentication

We are pretty happy now, we have our model with Todo lists and items, with item that supports Markdown syntax. However, we have not yet the user model. We would like to allow user to sign up, sign in and sign out. Upon sign up, user will be sign in immediately. On sign in, we want a way to restrict access to lists and todo items to only signed in users. Other users could not see the other user’s lists and items.

First we need to create user model, for simplicity, here we just create user model with only email and password. The password column will be named password_digest, we use features of Rails 3.1 for handling secure password in the database. When defining has_secure_password, two columns  password and password_confirmation will be automatically created for us. 

# Terminal
$ rails generate resource user email:string password_digest:string
$ rake db:migrate

Then we run the migration to create the schema in the database.

In the user model, we need to define has_secure_password so Rails will handle the creation of the secure hash password_digest column and the related password and password_confirmation. We also want to specify the attr_accessible methods for :email, :password and :password_confirmation

class User < ActiveRecord::Base
  attr_accessible :email, :password, :password_confirmation
  
  has_secure_password
  validate :password, presence: true
  validate :email, presence: true
end

We also need to uncomment the bcrypt gem in the gem file for the has_secure_password to work

# /app/gemfile
...
gem 'bcrypt-ruby', '~> 3.0.0'

Within the users controller, we will create two actions: new and create, in order to create a new form for user sign up and handle form submission. The new action will be use to generate the form with simple_form_for and the create action to handle the form submission

class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])
    if @user.save
      redirect_to root_url, notice: "Signed up!"
    else
      render "new"
    end
  end
end

To show the form for sign up, as we said above, we will use simple_form_for to generate the form that is compatible with Twitter bootstrap and has the default error handling, which we already use throughout the article. Here we use three fields: email, password and password_confirmation

# app/views/users/new.html.erb
<h1>Sign Up</h1>

<%= simple_form_for(@user, html: { class: "form-horizontal"}) do |f| %>
  <%= f.input :email %>
  <%= f.input :password %>
  <%= f.input :password_confirmation %>
  <div class="form-actions">
  <%= f.button :submit, class: "btn-primary"%>
  <%= submit_tag 'Cancel', type: 'reset', class: 'btn btn-danger'%>
  </div>
<% end %>

Now we need a link to go to this sign up form. The form can now be accessible at /users/new path, however, we want to customize it a little bit, we want the link to be /signup. Let’s modify it in the routes.rb file

# config/routes.rb

  match '/signup', to: 'users#new'
  ...

This declaration makes available the signup_path and signup_url to use in our views. Restarting the server and navigating to the http://localhost:3000/signup now and it works!

Creating a new user by inserting an email and password with password confirmation inserted correctly, we will be able to create a new user in the database. 

Now we need a way for users to sign in, we will use Rails session to store the current user_id of signed in user. We generate the sessions controller with three actions: new, create and destroy. The new action will be used for login form, the create action for handling login submission and destroy for signing out.

# Terminal
$ rails generate controller sessions new create destroy

Again we change the routes to get prettier paths instead of sessions/new path and sessions/destroy path

# config/routes.rb
  match '/signin', to: 'sessions#new'
  match '/signout', to: 'sessions#destroy', via: :delete
  resources :sessions, only: :create
  ...

We now create the Login form with two fields: email and password. This time we use the form_tag to sessions_path because we don’t have any model to use with simple_form_for and form_for. The form_tag gets a bit complicated because we have to put the code for Twitter bootstrap ourselves.

# app/views/sessions/new.html.erb
<h1>Log in</h1>

<%= form_tag sessions_path, class: 'form-horizontal' do %>
  <div class="control-group email">
    <%= label_tag :email, nil, class: 'email control-label' %>
    <div class="controls">
      <%= text_field_tag :email, params[:email], class: 'string email' %>
    </div>
  </div>
  <div class="control-group password">
    <%= label_tag :password, nil, class: 'password control-label' %>
    <div class="controls">
      <%= password_field_tag :password %>
    </div>
  </div>
  <div class="form-actions">
    <%= submit_tag "Log in", class: 'btn btn-primary' %>
    <%= link_to 'Cancel', root_path, class: 'btn'%>
  </div>
<% end %>

Now to handle the login form submission, we need to implement the create action for sessions controller. We will first find the email to find the user in the database and use the password from the form to authenticate this user only in case this user exists in the database. Once authorized, we want to save the :user_id in the session with user’s id. This session will be used to check if a user is signed in to the system or not.

# app/controllers/sessions_controller.rb
  def create
    user = User.find_by_email(params[:email])
    if user && user.authenticate(params[:password])
      user.api_keys.create!
      session[:user_id] = user.id
      redirect_to lists_url, notice: "Logged in!"
    else
      flash.now.alert = "Invalid email or password"
      render "new"
    end
  end

Navigating now to the http://localhost:3000/signin, we can see the form to enter email and password. If the email and password are entered incorrectly, the user will receive a ‘invalid email or password’ message, otherwise, they would be noticed with ‘Logged in!’. Our signup, signin forms now work!

While we are at the sessions controller, let’s also implement a way for user to sign out, just set the :user_id hash in session to nil, then redirecting to root_url and we’re done.

# app/controllers/sessions_controller.rb
  def destroy
    session[:user_id] = nil
    redirect_to root_url, notice: "Logged out!"
  end

When user is signed in, we want them to navigate between pages in the application and if we don’t have any way to know if the user is signed in or not, then the user will have to signed in again. Let’s go ahead and define the current_user method in application_controller so all our controllers know who the current user is. We store this information in a local variable @current_user so the query into the database happens only once.

# app/controllers/application_controller.rb
  protect_from_forgery
private 
  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
  helper_method :current_user
  ...

The helper_method :current_user makes this method available also in the views.

Updating the application layout

We have almost finished the signing mechanism, now we would like to display the links to users in the navigation menu. When user is not signed in, we display two links: sign in and sign up. When user is signed in, we show the link to sign out. This will be done using the dropdown menu provided by bootstrap. The code becomes larger in our application layout file, so let’s move it to a partial. We call it _header.html.erb, and in the application.html.erb we will render this partial

# app/views/layouts/application.html.erb
  <body>

    <%= render 'layouts/header' %>

    <div class="container">
    ...

Now we need to create the _header.html.erb in the app/views/layouts directory and make the change to the navigation menu. We change the name of the project to Markdown Todo, and use the root_path to link to the homepage, we check for the current_user, if the current user is signed in, then we display the dropdown with the menu to Account page, profile page, and settings page, also we display the sign out link. If user is not signed in, we just shown the signin_path and signup_path

# app/views/layouts/_header.html.erb
<div class="navbar navbar-fixed-top">
  <div class="navbar-inner">
    <div class="container">
      <a class="btn btn-navbar" data-target=".nav-collapse" data-toggle="collapse">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </a>
      <%= link_to "Markdown Todo", root_path, class: "brand" %>
      <div class="container nav-collapse">
        <ul class="nav">
          <li><%= link_to("Home", root_path) %></li>
          <li><%= link_to("About", "#") %></li>
          <li><%= link_to("Blog", "http://pnhoang.tumblr.com") %></li>
          
          <% if current_user %>
          <li id="fat-menu" class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown"> Account <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
              <li><%= link_to "Profile", current_user %></li>
              <li><%= link_to "Settings", edit_user_path(current_user)%></li>
              <li class="divider"></li>
              <li>
                <%= link_to "Sign out", signout_path, method: :delete %>
              </li>
            </ul>
          </li>
          <% else %>
            <li> <%= link_to "Sign in", signin_path %></li>
            <li> <%= link_to "Sign up", signup_path %> </li>
          <% end %>
        </ul>
      </div><!--/.nav-collapse -->
    </div>
  </div>
</div>

We also want to move the head into a separate partial call _head.html.erb so the application layout code is simpler.

# app/views/layouts/application.html.erb
  <head>
    <%= render 'layouts/head' %>
  </head>

The _head.html.erb partial does not contain any new code. We show it here for reference:

# app/views/layouts/_head.html.erb

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= content_for?(:title) ? yield(:title) : "Markdown Todo" %></title>
<%= csrf_meta_tags %>

<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script>
<![endif]-->

<%= stylesheet_link_tag "application", :media => "all" %>

<link href="images/favicon.ico" rel="shortcut icon">
<link href="images/apple-touch-icon.png" rel="apple-touch-icon">
<link href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon" sizes="72x72">
<link href="images/apple-touch-icon-114x114.png" rel="apple-touch-icon" sizes="114x114">

Now we would like to restrict access to lists to only signed in users (later on we will make changes to the schema to restrict access to only the correct user that signed in). Within our lists_controller and todos_controller, we can add the before_filter :authorize

# app/controllers/lists_controller.rb
class ListsController < ApplicationController
  before_filter :authorize
  ...

# app/controllers/todos_controller.rb
class TodosController < ApplicationController
  before_filter :authorize
  ...

The authorize method in the application controller is just as simple as checking the current_user variable, if it is nil then we redirect to the sign in page.

# app/helpers/application_controller.rb
  def authorize
    redirect_to signin_url, alert: "Not authorized" if current_user.nil?
  end

Now if we tried to navigate to lists index page without signing in, we will be redirected to the home page.

Adding static pages into the project

Up to now our root page is still the lists page where everyone if logged in can see all the todo lists and todo items. That is not quite right, we’ll fix it later. Now we want to make a nice welcome page to our project and make it the default root page.

Let’s start by creating a new controller

# Terminal
$ rails generate controller static_pages home about

The home page will have a hero-unit in a div with a big welcome and some sections. You can make it as fancy as you want.

# app/views/static_pages/home.html.erb
<div class="hero-unit">
  <h1>Welcome to Markdown Todo</h1>
  <p> Your Todo Items are awesome with Markdown! 
  </p>
  <p>
    Use all the available syntax at <a alt="Markdown" href="http://daringfireball.net/projects/markdown"> Markdown project</a> to create your Todo items
  </p>
  <p>
    <%= link_to "Sign up now !", signup_path, class: 'btn btn-primary btn-large' %>
</div>

The important thing is to change the routes config to the new controller’s action accordingly and remove the public/index.html

# config/routes.rb
  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  root :to => 'static_pages#home'

Our new nice home page:

Enabling JSON API

We have almost finished our application, let us now enable the JSON api so client applications can access our server app. JSON is by default configured with Rails app, when we create a scaffold, JSON is already enabled. Our problem is that we have use the authorize function to restrict access on the web using session. But with an API, we could not use the session, we need a way for the client application to specify authorization token in the header. An example is shown below

# Terminal
$ curl http://localhost:3000/lists -H 'Authorization: Token token="34b9d9eb4943786a791aecb366d91613"'

We go ahead and create a new model to keep the access_token for a user id and run the migration

# Terminal
$ rails generate model api_key access_token:string user_id:integer
$ rake db:migrate

We add the has_many and belongs_to relationship into user and api_key model respectively

# app/models/user.rb
  has_many :api_keys

# app/models/api_key.rb
  belongs_to :user

For the ApiKey class, we add a hook to before_create to generate the access token on object creation. The access_token is generated using the Ruby’s SecureRandome.hex function

# app/models/api_key.rb
class ApiKey < ActiveRecord::Base
  before_create :generate_access_token
  belongs_to :user
  
private
  def generate_access_token
    begin
      self.access_token = SecureRandom.hex
    end while self.class.exists?(access_token: access_token)
  end
end

So now when user registers to the application, we will call the user.api_keys.create! function to create the new api_key for that user

# app/controllers/sessions_controller.rb
  def create
    user = User.find_by_email(params[:email])
    if user && user.authenticate(params[:password])
      user.api_keys.create!
      session[:user_id] = user.id
      redirect_to lists_url, notice: "Logged in!"
    else
      flash.now.alert = "Invalid email or password"
      render "new"
    end
  end

Now we can rewrite the authorize function for inclusion of JSON format to use the Rails’s authenticate_or_request_with_http_token function and check on the existence of user’s api_keys with the specific access_token provided. The authenticate function does not rely on current_user variable, but instead checking the user_id param and the corresponding token to grant access.

# app/controllers/application_controller.rb
  def authorize
    case request.format
    when Mime::JSON, Mime::XML, Mime::ATOM      
      authenticate_or_request_with_http_token do |token, options|
        user = User.find(params[:user_id])
        if user
          user.api_keys.exists?(access_token: token)
        end
      end
    else  
      redirect_to signin_url, alert: "Not authorized" if current_user.nil?
    end
  end

We make a page for user to view his id and access_token. This page should be visible to only the user himself not any one else. To handle this, we define a new function in application controller called correct_user in which we check the params of user’s id, find the user based on this id and compare with the current signed in user.

# app/controllers/application_controller.rb
  def correct_user
    @user ||= User.find(params[:id])
    redirect_to(root_path) unless current_user?(@user)
  end

This function is then called by the before_filter, and applied to only the show action

# app/controllers/user_controller.rb
class UsersController < ApplicationController
  # we want to make a profile page for user to see his api_key
  before_filter :correct_user, only: :show
  
  ...

  def show
    @user = User.find(params[:id])
  end

Our show page is simply a page to display user’s information along with the access_token

# app/views/users/show.html.erb
<section class="row">
  <h1>Welcome: <%= @user.email %></h1>
  <p class="intro">Your current access_token: <%= @user.api_keys.first.access_token %></p>
  <p> Your user_id: <%= @user.id %> </p>
</section>

At this point, we are ready to expose the JSON API to the world and to build the iPhone client on the API. We’ll do that in the second part of this tutorial. Stay tuned! If you like the tutorial, follow me on Twitter @pnhoang to get the latest updates about the project.


May 25

Use CURL to manage RESTful Rails resource

I really like how powerful Rails is in terms of handling routings on the server. With only one line of code, you’re set up with 7 routes for handling with such resource. This line:

# config/routes.rb
resource :products

will define 7 routes available in your application:

$ rake routes
    products GET    /products(.:format)          products#index
             POST   /products(.:format)          products#create
 new_product GET    /products/new(.:format)      products#new
edit_product GET    /products/:id/edit(.:format) products#edit
     product GET    /products/:id(.:format)      products#show
             PUT    /products/:id(.:format)      products#update
             DELETE /products/:id(.:format)      products#destroy

To test these routes, the fastest way is to use a command line tool, CURL. In this tutorial, I will first create a new rails application, then I will use CURL to manage resources without using a browser.

Let’s first create a Rails application

$ rails new store
$ cd store

Then we use Rails scaffold generate function to be able quickly generate the complete code for our model, let’s say we have a Product model with two attributes: name and description

$ rails generate scaffold Product name:string description:text

Then we run the rake migration command to create the database tables

$ rake db:migrate

Now we should be able to see our Rails app in the server with rails server command

$ rails server

Navigating to http://localhost:3000/products we should be able to create new product, view the product listings, edit product or destroy it

Now we don’t want to use browser to do all that, we just use CURL to interact with our server’s database

  1. The first action we want to do is to add some products, we’ll have to use the new_product url to get the data for the new product. Let’s add a .json into the request url so we have the data in json.
     new_product GET    /products/new(.:format)      products#new
    
    $ curl http://localhost:3000/products/new.json
    {"created_at":null,"description":null,"id":null,"name":null,"updated_at":null}
    
  2. Now we might use this data to create a new product in the database, using the POST /products url scheme
                 POST   /products(.:format)          products#create
    
    $ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"created_at":null,"description":"Pragmatic programmers","id":null,"name":"Agile development with Rails","updated_at":null}'  http://localhost:3000/products
    
  3. Now we can verify if the product is created in the database by accessing the index route
        products GET    /products(.:format)          products#index
    
    $ curl http://localhost:3000/products.json
    
    [{"created_at":"2012-05-25T15:56:39Z","description":"Pragmatic programmers","id":1,"name":"Agile development with Rails","updated_at":"2012-05-25T15:56:39Z"}]
    
    And it’s there in the database, we already succeeded to create a new product. Notice that the result is a [] array because we are querying a list of all products.
  4. We can also view a specific product by querying its product GET route
         product GET    /products/:id(.:format)      products#show
    
    $ curl http://localhost:3000/products/1.json
    {"created_at":"2012-05-25T15:56:39Z","description":"Pragmatic programmers","id":1,"name":"Agile development with Rails","updated_at":"2012-05-25T15:56:39Z"}
    
    Here we have only one record, so it’s a hash {} with all product’s attributes.
  5. The next route we might want to test is the edit product route, we would like to change the name to “Agile development with Rails second edition”, the edit product path will be used to populate existing product into the form
    edit_product GET    /products/:id/edit(.:format) products#edit
    
    $ curl http://localhost:3000/products/1/edit.json
    
    Template is missing
    
    Missing template products/edit, application/edit with {:locale=>[:en], :formats=>[:json], :handlers=>[:erb, :builder, :coffee]}. Searched in: * "/Users/pnhoang/work/store/app/views"
    
    Here we have a 501 internal server error, as the default scaffold template does not generate data for edit product path. Let’s change it in the products_controller.rb file
    # app/controllers/products_controller.rb
    
      # GET /products/1/edit
      def edit
        @product = Product.find(params[:id])
            
        respond_to do |format|
          format.html # new.html.erb
          format.json { render json: @product }
        end
      end
    
    Now we can re-try
    $ curl http://localhost:3000/products/1/edit.json
    
    {"created_at":"2012-05-25T15:56:39Z","description":"Pragmatic programmers","id":1,"name":"Agile development with Rails","updated_at":"2012-05-25T15:56:39Z"}
    
    Great! We can see the existing product.
  6. In order to actually edit the product, we have to submit the PUT request to the product url. In the data submitted, I have omitted the created_at and updated_at fields, as these fields are automatically handled by Rails. We don’t have to submit those fields.
                 PUT    /products/:id(.:format)      products#update
    
    $ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X PUT -d '{"description":"Pragmatic programmers","id":1,"name":"Agile development with Rails second edition"}'  http://localhost:3000/products/1.json
    
  7. The last route that you might want to test is the Delete route. This is easy, we just need to send a DELETE request to the products/id url
                 DELETE /products/:id(.:format)      products#destroy
    
    $ curl -X DELETE http://localhost:3000/products/1
    
    
    

That’s it for now. I hope you enjoy it. Stay tuned as I will soon post a long article on building a Rails application as the backend for an iPhone app. Follow me on Twitter: @pnhoang


May 11

Install TextMate bundles for SCSS, Less, CoffeeScript

I have started working with Rails for about a month or so, and Rails comes together with a lot of new cool stuffs, such as SCSS (Sassy CSS) or Less or CoffeeScript. But as I open a Rails project in TextMate, these files are not properly formatted, only the boring black and white colors. I have just found out that we can customize TextMate to use different bundles for different languages. These bundles will make the code looks better and sometimes they support even autocomplete. 

You just need to copy the *.tmbundle file into your TextMate support folder. In my case, I have my TextMate bundles located at:

$ cd /Applications/TextMate.app/Contents/SharedSupport/Bundles/

Now suppose I want TextMate to know the format of Less, SCSS or CoffeeScript files, I just do a cloning from the proper repository on GitHub.

  • Less file: e.s. custom.css.less:
$ git clone https://github.com/appden/less.tmbundle.git 
  • CoffeeScript file: e.s. custom.js.coffeescript:
$ git clone git://github.com/jashkenas/coffee-script-tmbundle CoffeeScriptBundle.tmbundle
  • SCSS file: e.s. custom.css.scss:
$ git clone git://github.com/kuroir/SCSS.tmbundle.git "SCSS.tmbundle"
There are many more bundles that you can install depends on your need. Just try to search for them in Github with “TextMate bundle”

May 10