Jim Neath

Manchester based Ruby on Rails & Facebook App Developer

Showing blog posts tagged as "Ruby on Rails"

Ruby SSL Certificate Verify Failed

If you’re using Ruby to open connections to an external server over https, eg. the Facebook Graph API, you may run into the following error:

OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed

This error is due to Ruby not being able to find the certification authority certificates (CA Certs) used to verify the authenticity of secured web servers. The solution is to download the this ca-bundle.crt into your application’s lib/ directory:

Then add the following code to config/initializers/fix_ssl.rb:

require 'open-uri'
require 'net/https'

module Net
  class HTTP
    alias_method :original_use_ssl=, :use_ssl=
    
    def use_ssl=(flag)
      self.ca_file = Rails.root.join('lib/ca-bundle.crt')
      self.verify_mode = OpenSSL::SSL::VERIFY_PEER
      self.original_use_ssl = flag
    end
  end
end

This should force ruby to use the CA bundle from your application’s lib/ directory.

Party on, Wayne!

Tagged with

The standard way of deploying rails applications is to serve a static maintenance page while the code is being updated, to prevent errors being thrown by the user and to prevent writes happening to your database. This is all fine and dandy in the real world but in Facebook land we run into issues.

Facebook will always send a POST on the initial iframe request for security reasons, and this can be to any end-point in our application, while in reality, this should be treated as a GET.

So, the first time a page of your app is requested by user, Facebook sends a POST request. If you’re currently in maintenance mode you’re going to get a big 405 Method not allowed error.

To fix this we need to tell nginx to serve your maintenance page to POST requests, as well as GET requests. We can do this by updating your servers config files. Say your server config initially looks like this:

server {
  # all your other server stuff
  # ...

  # show maintenance page if exists 
  if (-f $document_root/system/maintenance.html) {
    rewrite ^(.*)$ /system/maintenance.html break;
    break;
  }
}

Add the following location rule to allow nginx to serve the page for POST requests:

server {
  # all your other server stuff
  # ...

  # show maintenance page if exists 
  if (-f $document_root/system/maintenance.html) {
    rewrite ^(.*)$ /system/maintenance.html break;
    break;
  }

  # serve maintenance page to POST requests
  location = /system/maintenance.html {
    post_to_static on;
  }
}

Reload your nginx config and everything should be rocking as expected.

Rails 3, nginx and send_file

Trying to use send_file from your Rails 3 app and getting a file with a size of zero bytes? It’s more than likely that you’re using nginx and haven’t setup your app to use send_file properly.

Open up your config/environments/production.rb file and find the following lines:

# Specifies the header that your server uses for sending files
config.action_dispatch.x_sendfile_header = "X-Sendfile"

# For nginx:
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

Then comment out the first config.action_dispatch.x_sendfile_header line and uncomment out the second. So it should look like this:

# Specifies the header that your server uses for sending files
# config.action_dispatch.x_sendfile_header = "X-Sendfile"

# For nginx:
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

Then just commit, push, deploy and voila!

Tagged with

Using Redis with Ruby on Rails

Redis Book

Buy The Redis Book!

Written by Redis creator, Salvatore Sanfilippo, and key contributor, Pieter Noordhuis, the Redis Book will show you how to work with different data structures, how to handle memory, replication, and the cache itself, and how to implement messaging, amongst other things! Buy the book

Redis is an extremely fast, atomic key-value store. It allows the storage of strings, sets, sorted sets, lists and hashes. Redis keeps all the data in RAM, much like Memcached but unlike Memcached, Redis periodically writes to disk, giving it persistence.

Redis is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets.

You can run atomic operations on these types, like appending to a string; incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; or getting the member with highest ranking in a sorted set.

In order to achieve its outstanding performance, Redis works with an in-memory dataset. Depending on your use case, you can persist it either by dumping the dataset to disk every once in a while, or by appending each command to a log.

The above quote was taken from the official Introduction to Redis page.

Table of Contents

Redis Data Types

Below is a general overview of the data types available to you in Redis:

Data TypeDescription
StringA string of characters. The most basic data type. Can also be used as a counter, using incr, incrby, decr and decrby
SetSets are unsorted collections of strings with no duplicate members.
Sorted SetSorted sets (zset) are like sets in that each member has to be unqiue. Unlike sets, sorted sets have a score associated with each member
ListLists are sorted collections of strings, essentially like an Array in ruby. You can push and pop on either end, making them useful for queues.
HashHashes are what you’d expect: keys and string values.

As well as those basic types, there is also the ability to do PubSub with Redis. A full list of Redis commands is available on the official site.

Installing Redis

Installation is simple:

curl -O http://redis.googlecode.com/files/redis-2.2.2.tar.gz
tar xzf redis-2.2.2.tar.gz
cd redis-2.2.2
make
cp src/redis-server src/redis-cli /usr/bin

Starting Redis Server

If you’re using redis-server locally, you can get away with just running the following, which will use the default Redis config file:

redis-server

If you’re running it on a server you’ll probably want to use your own config file:

redis-server /path/to/redis.conf

The default redis.conf looks like this.

Redis and Rails

Now that we have Redis installed and running, we need to get it playing nice with Rails. Enter redis-rb, created by Ezra Zygmuntowicz:

https://github.com/ezmobius/redis-rb

First step, add redis-rb to your Gemfile:

gem 'redis', '2.1.1'

Then install the gem via Bundler:

bundle install

Lastly, create an initializer in config/initializers/redis.rb and add the following:

$redis = Redis.new(:host => 'localhost', :port => 6379)

This will create a new instance of the Redis client, connected to localhost:6379 (the default), and store it in the global variable $redis.

Let’s check that everything is working by firing up rails console:

> $redis
=> #<Redis client v2.1.1 connected to redis://localhost:6379/0 (Redis v2.2.2)> 
> $redis.set('chunky', 'bacon')
=> "OK" 
> $redis.get('chunky')
=> "bacon"

Example Uses in Rails

User Friendships

The first example I’ll show you is modelling friendships using ActiveRecord and Redis. We’ll be using sets to store the friendship data. Here is our User model:

class User < ActiveRecord::Base
  # follow a user
  def follow!(user)
    $redis.multi do
      $redis.sadd(self.redis_key(:following), user.id)
      $redis.sadd(user.redis_key(:followers), self.id)
    end
  end
  
  # unfollow a user
  def unfollow!(user)
    $redis.multi do
      $redis.srem(self.redis_key(:following), user.id)
      $redis.srem(user.redis_key(:followers), self.id)
    end
  end
  
  # users that self follows
  def followers
    user_ids = $redis.smembers(self.redis_key(:followers))
    User.where(:id => user_ids)
  end

  # users that follow self
  def following
    user_ids = $redis.smembers(self.redis_key(:following))
    User.where(:id => user_ids)
  end

  # users who follow and are being followed by self
  def friends
    user_ids = $redis.sinter(self.redis_key(:following), self.redis_key(:followers))
    User.where(:id => user_ids)
  end

  # does the user follow self
  def followed_by?(user)
    $redis.sismember(self.redis_key(:followers), user.id)
  end
  
  # does self follow user
  def following?(user)
    $redis.sismember(self.redis_key(:following), user.id)
  end

  # number of followers
  def followers_count
    $redis.scard(self.redis_key(:followers))
  end

  # number of users being followed
  def following_count
    $redis.scard(self.redis_key(:following))
  end
  
  # helper method to generate redis keys
  def redis_key(str)
    "user:#{self.id}:#{str}"
  end
end

Some sample useage:

> %w[Alfred Bob].each{|name| User.create(:name => name)}
=> ['Alfred', 'Bob']
> a, b = User.all
=> [#<User id: 1, name: "Alfred">, #<User id: 2, name: "Bob">] 
> a.follow!(b)
=> [1, 1] 
> a.following?(b)
=> true 
> b.followed_by?(a)
=> true 
> a.following
=> [#<User id: 2, name: "Bob">] 
> b.followers
=> [#<User id: 1, name: "Alfred">]
> a.friends
=> [] 
> b.follow!(a)
=> [1, 1] 
> a.friends
=> [#<User id: 2, name: "Bob">] 
> b.friends
=> [#<User id: 1, name: "Alfred">] 

It is all pretty simple. We’re using the following set commands: sadd, srem, smembers, sinter, scard, sismember and the multi command. An overview of the commands is provided below:

CommandOverview
saddAdd member to the set stored at key. If member is already a member of this set, no operation is performed. If key does not exist, a new set is created with member as its sole member.
sremRemove member from the set stored at key. If member is not a member of this set, no operation is performed.
smembersReturns all the members of the set value stored at key.
sinterReturns the members of the set resulting from the intersection of all the given sets.
scardReturns the set cardinality (number of elements) of the set stored at key.
sismemberReturns if member is a member of the set stored at key.
multiMarks the start of a transaction block. Subsequent commands will be queued for atomic execution using EXEC.

High Score Table

We can construct a simple high score table using Redis sorted sets:

class User < ActiveRecord::Base
  # log high score
  def scored(score)
    if score > self.high_score
      $redis.zadd("highscores", score, self.id)
    end
  end
  
  # table rank
  def rank
    $redis.zrevrank("highscores", self.id) + 1
  end
  
  # high score
  def high_score
    $redis.zscore("highscores", self.id).to_i
  end
  
  # load top 3 users
  def self.top_3
    $redis.zrevrange("highscores", 0, 2).map{|id| User.find(id)}
  end
end

Sample useage:

> a, b, c, d = User.limit(4)
=> [#<User id: 1, name: "Alfred">, #<User id: 2, name: "Bob">, #<User id: 3, name: "Charlie">, #<User id: 4, name: "Derek"">] 
> a.scored 100
=> true 
> b.scored 500
=> true 
> c.scored 25
=> true 
> d.scored 10000
 => true 
> d.high_score
 => 10000 
> d.rank
=> 1 
> c.rank
=> 4 
> c.scored 5000000
=> false 
> c.high_score
=> 5000000 
> c.rank
=> 1 
> User.top_3
=> [#<User id: 3, name: "Charlie">, #<User id: 4, name: "Derek">, #<User id: 2, name: "Bob">] 

We’re using the following set commands: zadd, zrevrank, zrevrange and the zscore commands. An overview of the commands is provided below:

CommandOverview
zaddAdds the member with the specified score to the sorted set stored at key. If member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering. If key does not exist, a new sorted set with the specified member as sole member is created.
zrevrankReturns the rank of member in the sorted set stored at key, with the scores ordered from high to low. The rank (or index) is 0-based, which means that the member with the highest score has rank 0.
zrevrangeReturns the specified range of elements in the sorted set stored at key. The elements are considered to be ordered from the highest to the lowest score. Descending lexicographical order is used for elements with equal score.
zscoreReturns the score of member in the sorted set at key.

More Redis Use Cases

Using Redis As Your Rails Cache Store

If you want to use Redis as your cache store, then look no further than Redis Store by Luca Guidi. Redis Store provides namespaced Rack::Session, Rack::Cache, I18n and cache Redis stores for Ruby web frameworks. It allows you to use Redis as your Rails cache store, session store and integrates well with Rack::Cache.

https://github.com/jodosha/redis-store

First, let’s add redis-store to the Gemfile:

gem 'redis-store', '1.0.0.beta4'

Then install the gem via Bundler:

bundle install

Then update your config/environments/production.rb:

config.cache_store = :redis_store

Then you’re good to go. Your cache store should now be using Redis.

Monitoring Redis

If you’re using Redis on your server and you have daemonize set to yes in your redis.conf, you need to monitor it. Below are an example Monit and God script:

Monit

check process redis
  start program = "/usr/bin/redis-server /etc/redis/redis.conf"
  stop program = "/usr/bin/redis-cli -p 6379 shutdown"
  with pidfile /var/run/redis.pid
  group redis

God

Adapted from this Gist:

%w{6379}.each do |port|
  God.watch do |w| 
    w.name = "redis" 
    w.interval = 30.seconds 
  
    w.start = "/usr/bin/redis-server /etc/redis/redis.conf" 
    w.stop = "/usr/bin/redis-cli -p 6379 shutdown" 
    w.restart = "#{w.stop} && #{w.start}" 
  
    w.start_grace = 10.seconds 
    w.restart_grace = 10.seconds 
  
    w.start_if do |start| 
      start.condition(:process_running) do |c| 
        c.interval = 5.seconds 
        c.running = false 
      end 
    end 
  end
end

Redis and Unix Sockets

Redis 2.2 introduced the ability to connect to Redis via unix sockets. To allow this functionality you need to uncomment the following line in your redis.conf:

unixsocket /tmp/redis.sock

Then you can connect to Redis in your app, change the contents of your config/initializers/redis.rb, to this:

$redis = Redis.new(:path => "/tmp/redis.sock")

Below are some rough benchmarks from my MacBook Pro:

~ redis-benchmark -q
PING (inline): 54347.82 requests per second
PING: 52083.33 requests per second
MSET (10 keys): 28571.43 requests per second
SET: 48309.18 requests per second
GET: 49504.95 requests per second
INCR: 47846.89 requests per second
LPUSH: 48309.18 requests per second
LPOP: 49751.24 requests per second
SADD: 48543.69 requests per second
SPOP: 51282.05 requests per second
LPUSH (again, in order to bench LRANGE): 48076.92 requests per second
LRANGE (first 100 elements): 34129.69 requests per second
LRANGE (first 300 elements): 17064.85 requests per second
LRANGE (first 450 elements): 13661.20 requests per second
LRANGE (first 600 elements): 11074.20 requests per second

~ redis-benchmark -q -s /tmp/redis.sock
PING (inline): 80000.00 requests per second
PING: 82644.62 requests per second
MSET (10 keys): 41841.00 requests per second
SET: 72463.77 requests per second
GET: 81300.81 requests per second
INCR: 63694.27 requests per second
LPUSH: 70422.53 requests per second
LPOP: 80000.00 requests per second
SADD: 72463.77 requests per second
SPOP: 77519.38 requests per second
LPUSH (again, in order to bench LRANGE): 80645.16 requests per second
LRANGE (first 100 elements): 42372.88 requests per second
LRANGE (first 300 elements): 22779.04 requests per second
LRANGE (first 450 elements): 16891.89 requests per second
LRANGE (first 600 elements): 13458.95 requests per second

Redis Objects

Redis objects maps redis data types to ruby objects. Created as a compliment for current ORMs instead of a replacement.

https://github.com/nateware/redis-objects

Resque

Resque (pronounced like “rescue”) is a Redis-backed library for creating background jobs, placing those jobs on multiple queues, and processing them later. It was written by GitHub and is used in production there, as well as on many other apps. Read their blog post: Introducing Resque

https://github.com/defunkt/resque

Vanity

Vanity is an Experiment Driven Development framework for Rails that uses Redis. It allows you to define A/B tests in your Ruby on Rails application and integrates with Google Analytics via Garb.

https://github.com/assaf/vanity

Ohm

Ohm is a library for storing objects in Redis, a persistent key-value database. It includes an extensible list of validations and has very good performance.

https://github.com/soveran/ohm

Rollout

Rollout lets you roll out new features to select groups of users for testing. Rollout has been covered on the changelog. Also works in tandem with Degrade

https://github.com/jamesgolick/rollout

Soulmate

Soulmate is a tool to help solve the common problem of developing a fast autocomplete feature. It uses Redis’s sorted sets to build an index of partially completed words and the corresponding top matching items.

https://github.com/seatgeek/soulmate

Redis Book

Buy The Redis Book!

Written by Redis creator, Salvatore Sanfilippo, and key contributor, Pieter Noordhuis, the Redis Book will show you how to work with different data structures, how to handle memory, replication, and the cache itself, and how to implement messaging, amongst other things! Buy the book

Tagged with

If you’ve ever wanted to know how many active or idle Passenger processes your server has and display them inside your app, you can use the following helper:

def passenger_stats
  @passenger_stats ||= begin
    `passenger-status`.split("\n")[1..4].inject({}) do |memo, line| 
      memo.merge(line[/^\w+/].to_sym => line[/\d+$/].to_i)
    end
  end
end

Calling passenger_stats will return a hash resembling the following:

{ :inactive => 1, :count => 4, :max => 15, :active => 3 }

Which you could then display in your views, thus:

<h3>Passenger Stats</h3>
<p><strong>Total:</strong> <%= passenger_stats[:total] %></p>
<p><strong>Maximum:</strong> <%= passenger_stats[:max] %></p>
<p><strong>Active:</strong> <%= passenger_stats[:active] %></p>
<p><strong>Inactive:</strong> <%= passenger_stats[:inactive] %></p>

Enjoy.

Tagged with

NGINX is an awesome server, but unfortunately contains an error in earlier versions (pre 0.8.32), that sends the incorrect headers for 201 (created) responses. It does not set the Content-Length header, which causes modern browsers to keep the connection open and wait until the timeout value is exceeded.

There are two possible fixes for this; Upgrade NGINX to version 0.8.32 or greater, or fix the issue in your server side code. I’ll show you how we can do the latter, using Rack.

Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.

We will use a piece of Rack middleware to set the correct headers for any 201 responses.

Copy the code below into lib/content_length_fix.rb:

module Nginx
  class ContentLengthFix
    def initialize(app)
      @app = app       
    end                

    def call(env)
      status, headers, response = @app.call(env)
      headers["Content-Length"] = response.length.to_s if status == 201
      [status, headers, response]
    end                
  end
end

Then add the following to your config.ru file:

require 'content_length_fix' 
use Nginx::ContentLengthFix

Rejoice and your 201 responses now have the correct headers set!

Fixing Long Key Lengths in MySQL

While using Query Reviewer, I occasionally come across a warning along the lines of the following:

2 identical queries Table videos: Long key length (768)

With the following explanation:

The key used for the index was rather long, potentially affecting indices in memory

To fix this we need to limit the length of our index. We can do this by creating a migration with :length added to the add_index method:

add_index :videos, :cached_slug, :length => 20

If you’re indexing more than one column, you can use the following:

add_index :stories, [:title, :author_name], :length => { :title => 10, :author_name => 5 }

Hopefully this post will save someone a few minutes.

Optimizing your MySQL queries and performance in your rails applicationcan be a real pain. The plugins below help to make things a little easier.

Bullet

Bullet is a rails plugin, written by Richard Huang (@flyerhzm), that helps to kill N+1 queries. It shows you where you should be using :include in your ActiveRecord calls. Bullet also informs you where you’re missing counter caches as well as warning you of any unused eager loading.

http://github.com/flyerhzm/bullet/

SlimScrooge

SlimScrooge is a plugin by Stephen Sykes (@sdsykes), that restricts the columns retrieved in your MySQL queries by learning which attributes are subsequently called on the ActiveRecord model.

Below is an example, taken from the readme:

# 1st request, sql is unchanged but columns accesses are recorded
Brochure Load SlimScrooged 1st time (27.1ms)   SELECT * FROM `brochures` WHERE (expires_at IS NULL)

# 2nd request, only fetch columns that were used the first time
Brochure Load SlimScrooged (4.5ms)   SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.id FROM 
`brochures` WHERE (expires_at IS NULL)

# 2nd request, later in code we need another column which causes a reload of all remaining columns
Brochure Reload SlimScrooged (0.6ms) `brochures`.name,`brochures`.comment,`brochures`.image_height,`brochures`.id,
`brochures`.tel,`brochures`.long_comment,`brochures`.image_name,`brochures`.image_width FROM 
`brochures` WHERE `brochures`.id IN ('5646','5476','4562','3456','4567','7355')

# 3rd request
Brochure Load SlimScrooged (4.5ms)   SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.name,
`brochures`.id FROM `brochures` WHERE (expires_at IS NULL)

http://github.com/sdsykes/slim_scrooge/

Query Reviewer

Query Reviewer by David Stevenson (@dsboulder) of Pivotal Labs, is a Rails plugin that adds a div to the top left of the screen that contains lots of useful details regarding the MySQL executed on the current page.

The plugin makes it easy to view the EXPLAIN output for each SELECT query on the page. It also provides ratings and warnings on each query executed on a page.

Query Reviewer

http://github.com/dsboulder/query_reviewer

Rails Indexes

Rails Indexes is a plugin by Elad Meidar (@eladmeidar), that helps to generate a migration containing suggested indexes for your database.

By running the following rake command:

rake db:index_migration

Rails Indexes will generate the example migration containing suggested indexes. While these indexes are a good start, you will probably find that you need some detailed database indexes.

http://github.com/eladmeidar/rails_indexes/

Ambitious Query Indexer

Ambitious Query Indexer is an index generating plugin by Sam Phillips (@samsworldofno). Like Rails Indexes, it generates a migration containing suggested indexes, but it works by analysing all of the queries it can find in your app.

http://github.com/samdanavia/ambitious_query_indexer

If you ever need to know if an image is portrait or landscape in orientation, you can do the following (assuming you have an orientation attribute on your model:

class Image < ActiveRecord::Base
  # Paperclip
  has_attached_file :file
  
  # Callbacks
  before_create :set_orientation
  
  protected
  
  def set_orientation
    original_file = self.file.to_file(:original)
    dimensions = Paperclip::Geometry.from_file(original_file)
    self.orientation = (dimensions.width > dimensions.height) ? 'landscape' : 'portrait'
  end
end

Tagged with

You know those times when Textmate beachballs for about 5 seconds when it regains focus, it’s generally caused by the fact you’ve got about 16 million folders in your public/system folder used by paperclip.

Here’s a quick tip to speed things up by ignoring those folders:

Open Textmate Preferences and goto the Advanced section. Then click on Folder References.

picture 7

And enter the following in Folder Pattern:

!.*/(\.[^/]*|**log|system**|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$

Rejoice in the fact that Textmate doesn’t make you want to cry.

Tagged with

Note: MailStyle was originally called Shemail but was changed due to a general negative response to the name from the public.

Background

Writing inline styles for HTML emails is dull and boring but unfortunately a necessary evil. Rather than do it by hand we thought it would be nicer to let our app do it for us. So we wrote MailStyle.

Enter MailStyle

MailStyle allows you to write the css for your html emails as you normally would, then writes the styles inline when you send your emails. It also makes sure that your image paths are absolute rather than relative.

Installation

First install the dependencies:

sudo gem install nokogiri css_parser

Then install MailStyle to your rails app:

script/plugin install http://github.com/purify/mail_style

Usage

Rendering CSS Inline

Simply add the css method to your deliver actions:

class Notifier < ActionMailer::Base
  def welcome_email
    css :email

    subject 'Welcome Aboard'
    recipients 'someone@example.com'
    from 'jimneath@googlemail.com'
    sent_on Time.now
  end
end

This will look for a css file called email.css in your public/stylesheets folder. The css method can take either a string or a symbol. You can also pass the css file name with or without the .css extension.

MailStyle will now write the styles from email.css into html part of the welcome email (eg. welcome_email.text.html.erb). It won’t touch the plain text part, neither will it do anything if you’re only sending a single part email (eg. welcome_email.html.erb). You shouldn’t be sending html emails without a plain text version anyway.

Say that our email.css file looks like the following:

body { background: #000 }
p { color: #f00; line-height: 1.5 }
.text { font-size: 14px }

And our welcome_email.text.html.erb looks like this:

<p class="text">Hello World</p>

Then the resulting html that will be sent out will resemble the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  </head>
  <body style="background: #000">
    <p style="color: #f00;line-height:1.5;font-size: 14px">Hello World</p>
  </body>
</html>

Fixing Image URLs

If you have default_url_options:host set in your mailer, then MailStyle will do it’s best to make the urls of images absolute.

In your mailer
ActionMailer::Base.default_url_options[:host] = "example.com"
Example CSS
p { background-image: url(../images/chunky-bacon.png) }
Example HTML View
<img src="/images/header.jpg">
<p>Hello World</p>
Example Output
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  </head>
  <body>
    <img src="http://example.com/images/header.jpg">
    <p style="background-image: url(http://example.com/images/chunky-bacon.png)">Hello World</p>
  </body>
</html>

How You Can Help

The main issue at the moment is speed, so any help speeding things up would be awesome. As always, you can fork the repo at Github.

Further Reading

Below are a couple of links you may find useful regarding HTML emails:

ThinkingSphinx and Nil Results

Have you ever gotten a result like the following when using ThinkingSphinx (by the awesome Pat Allen):

NoMethodError (undefined method `constantize' for nil:NilClass):

This error occurs when your index isn’t quite up to date and ThinkingSphinx returns results that have since been deleted from your database. To fix the problem add :retry_stale => true to your searches, like this:

Article.search('chunky bacon', :retry_stale => true)

There is more information about :retry_stale in the source code:

# If you pass :retry_stale => true to a single-model search, missing records will
# cause Thinking Sphinx to retry the query but excluding those records. Since search
# is paginated, the new search could potentially include missing records as well, so by
# default Thinking Sphinx will retry three times. Pass :retry_stale => 5 to retry five
# times, and so on. If there are still missing ids on the last retry, they are
# shown as nils.

I hope this saves someone a few minutes in the future :)

Creating PDF Documents in Ruby on Rails

Those of you that follow me on Twitter will probably know that I’ve spent the last week or so trying to find a decent way of generating PDF documents from a Rails application. I finally found a solution that suited my needs so I thought I’d share it with you lovely people.

Initial Probing

After my first round of googling I came across 2 solutions that people seem to be using:

I’ll admit I didn’t really look too much into PDF::Writer but I did spend a couple of days playing around with Prawn.

All was going well until I ran into certain situations. The two main problems I had with Prawn were that it’s too difficult to style things as I wanted to and generating a table of contents. I might be missing something but I couldn’t get my head around how to solve the latter.

I don’t want to learn a new syntax to define my PDF documents. I don’t want to learn a new way of styling things. I like my HTML and CSS.

WHERE’S MY HTML AND CSS?!

Introducing Prince

Prince XML is a command line program that takes your spiffy html and css and returns a nicely formatted PDF document for you.

Prince is a computer program that converts XML and HTML into PDF documents. Prince can read many XML formats, including XHTML and SVG. Prince formats documents according to style sheets written in CSS.

See, I told you.

So, we’ve got prince xml, but what about hooking it up with rails?

Introducing Princely

Princely is a fantastic plugin by Michael Bleigh, author of other great plugins, such as subdomain-fu and acts-as-taggable-on.

Princely is basically a wrapper around the Prince API. It allows you to define your PDF views as templates like show.pdf.erb. You can write your views as you would normal html views and Princely will return a pdf when you hit those pages.

def show
  respond_to do |format|
    format.html
    format.pdf do
      render :pdf => "filename", :stylesheets => ["application", "prince"], :layout => "pdf"
    end
  end
end

Awesome, right?

Installing Prince and Princely

To install Prince XML, visit the download page and chose the package which suits your needs. Done.

Next, to install Princely, run the following command from your Rails app.

script/plugin install git://github.com/mbleigh/princely.git

Page Numbering

To set the page numbers of you PDFs you’ll need to use the counter attribute of CSS3.

@page {
  @bottom-left {
    content: counter(page);
  }
}

This snippet of code will, funnily enough, add the current page number to the bottom left of each page. That’s all there is to it.

If you’d prefer to show the page number along with the total number of pages, you can use the following:

@page {
  @bottom-left {
    content: "Page " counter(page) " of " counter(pages);
  }
}

Table of Contents

If you’re creating a large PDF with a few sections, it’s generally a good idea to include a table of contents at the start of your document.

The first thing you need to do, is output a list of links at the start of you document that link to internal anchors:

<ul id="toc">
  <li><a href="#ruby">Chapter 1: An Introduction to Ruby</a></li>
  <li><a href="#rails">Chapter 2: Hello, Rails!</a></li>
  <li><a href="#prince">Chapter 3: Pump it up, Prince!</a></li>
</ul>

This will give you a list, that looks something along the lines of the following image:

toc

Now with a bit of CSS magic, we can add the page numbers from our PDF to the table of contents. Ready for this? Go!

ul#toc a::after
{
  content: leader('.') target-counter(attr(href), page);
}

That’s it. attr(href) finds the target of the link. target-counter finds the page that the anchor is on and the leader call just pads the left had side with periods. Win.

That nifty bit of CSS should leave you with something looking like this:

toc unstyled

A bit more CSS to tidy it up a bit:

ul#toc ul {
	margin: 0;
	list-style: none;
	padding: 0;
}

ul#toc a {
	text-decoration: none;
}

ul#toc li {
	margin: 0;
	padding: 0;
	list-style: none;
}

And then we should have something that looks a bit nicer:

toc styled

That’s all there is to it. Easy, right?

Metadata

Prince writes metadata to your PDFs via the meta tags in your html. You can set the document author, subject and keywords using the following:

<head>
  <title>PDF Generation</title>
  <meta name="author" content="Jim Neath"/>
  <meta name="subject" content="Generating PDFs with Prince"/>
  <meta name="keywords" content="PDF, prince, ruby, rails"/>
</head>

Further Reading

Escaping Params Containing Periods

Just a quick one. Say you have a route like the following:

map.resources :users

And you’re using email addresses to look up users like:

/users/jim@somewhere.com

You’re going to run into an error along the lines of:

Missing template users/show.com.erb in view path blah/app/views

Which obviously isn’t what you want. To fix this, change your route to the following:

map.resources :users, :requirements => { :id => /.*/ }

Rejoice.

Tagged with

Speeding Up Rails Development

Over the last few months I’ve realised that the speed at which I develop new projects is a lot quicker than it used to be. So I thought I’d share some of the things I’ve learned and also some quite obvious things (to me at least).

Use a Base Application

I’m obviously going to be horrifically biased due to the fact that I helped to develop Bort, but I think that base apps are the way to roll. They save you about half a days worth of development and let you get straight into developing your application rather than fucking around doing the same monotonous stuff every time.

So here’s a run down of base apps floating around:

I haven’t used any of these apart from Bort, so I can’t really give you any opinion but everything I’ve seen by Thoughtbot and James Golick have always been awesome. Just look through them and find which one suits your needs.

I would like to end this section with a nice graph taken from Rails Rumble Observations, part II :)

Write Your Own Scaffold Generator

The default Rails scaffold generator is alright for prototyping an app but let’s face it, you wouldn’t use it for everything. So why don’t you made your own that you can use for everything. At the start of the last project we worked on, we spent 2-3 days working on a scaffold generator that would help to generate parts of the admin.

We made the generator generate all the search stuff, add sortable tables, generate basic specs and a whole bunch of other awesome stuff. Now we can get an awesome admin section set up for a model by running line from terminal.

This must have saved us at least a weeks worth of time. Time that we can now spend making sure that the rest of the site is as brilliant as possible. With the extra time, you take it easy, or you could add extra features, improve the UI, whatever. Keep it RESTful, kids.

Use a Form Builder

I hate forms. No secret there. But alas, nearly every application you’ll develop need to have forms. I wrote a custom form builder for the chaps at Fudge and it saves us a hell of a lot of time.

Now instead of writing something like the following:

<% form_for @story do |f| %>
  <%= f.error_messages %>
  <fieldset>
    <legend>Story Details</legend>
    <ol>
      <li>
        <%= f.label :title %>
        <%= f.text_field :title %>
      </li>
      <li>
        <%= f.label :body, 'Content' %>
        <%= f.text_area :body %>
      </li>
    </ol>
    <div class="buttons">
      <%= f.submit 'Create' %>
    </div>
  </fieldset>
<% end %>

Using our form builder we write:

<% form_for @story do |f| %>
  <%= f.error_messages %>
  <% f.field_set "Story Details" do %>
    <%= f.text_field :title %>
    <%= f.text_area :body, :label => 'Content' %>
  <% end %>
  <%= f.submit 'Create' %>
<% end %>

Now imagine you’re got to write close to 50 forms for an application. Can you guess which ones saves you time? Which one is more enjoyable to use? You got it.

Now while I wouldn’t say our form builder is ready for the general coding public (it isn’t), there are still a few out there.

I have used Semantic Form Builder by RubyPond before and it also happens to be the one we based out form builder on.

Build a Populate Rake Task

We started using Populator/Faker a couple of months a go and this is probably one of our biggest time savers. It’s a pain in the ass adding test data into your applications.

Ryan Bates has made a great railscast on how to use Populator along with Faker to generate fake datausing a rake task so I’ll leave it to his awesome video to tell you all about it.

There are also a couple other options out there for generating fake data, the random-data gem and the Forgery plugin.

Peter Cooper has a more thorough run down of all three options over at Rails Inside.

Use Plugins/Gems

This should really go without saying, but I’ve seen a few people trying to write (poor) code for tasks that have already been solved, tested and improved on.

Gems and plugins are probably your biggest time saver. One of the things I love about the ruby community is that a lot of people give back to it.

If you have a problem, have a look on the awesome GitHub and see if there’s a plugin/gem floating around that looks like it could solve your problem. Try it out. If it works brilliant, if not see if you can fix it and improve the original code. Then if someone else has the same problem, they can use the plugin. If everyone helps out, we all have an easier job, we can do less work and enjoy life more.

Seriously, Just Buy a Fucking Mac

Just do it. Stop making excuses. I was a Windows user for about ten years but mainly because I didn’t know any better. I now work full time on a mac, both at home and at work, and there’s not a thing you could do to make me go back to Windows.

Windows simply won’t do a lot of things that you’ll want to do. Background jobs? Not a chance. Git? oh yeah, you can use msysgit but who the fuck wants to open up a separate program just to use git? Fuck off Windows. You’re slow and you suck.

Why get a mac? Rails runs faster. You can use the best text editor around, TextMate. You can install all those gems and plugins that all say: “This won’t work on Windows”.

Think getting a mac is too expensive? Get a low spec mac mini for $599. That’s what I started using and even though it’s low spec I never had a problem with it. You can use your USB keyboard, mouse and your monitor from your Windows machine. Still think it’s too much? Have a look on Amazon…

So do you, my lovely readers, have any more suggestions/tips to speed up your development?

Getting the Dimensions of a Flash (SWF) File

This is a just a quick post that will hopefully save some people a hell of a lot of time.

If you’re trying to get the width and height of a flash file, a banner for example, then you can use the ImageSpec library by Brandon Anderson.

You can checkout the code with the following:

svn checkout http://ruby-imagespec.googlecode.com/svn/trunk/ vendor/plugins/ruby-imagespec

Then call it in your code to get the dimensions of a flash movie. Say you’re using Paperclip, then you could do the following:

dimensions = ImageSpec::Dimensions.new(@banner.file.path)
dimension.height # Get the height
dimesions.width # Get the width

Rad.

Bort Update to Rails 2.2

I probably should have done this sooner but I’ve finally got around to updating Bort to Rails 2.2

Here are some other changed.

Will_paginate, AASM, Rspec and Rspec-rails updated to gems

I’ve decided to remove the above plugins and unpack the most recent version into the vendor/gems directory. The main reason is its easier to keep track of which version you’re using and what needs updating. So Bort is currently rocking the most recent versions of those gems.

Exception Notifier Email Automatically Set to the Email Set in Settings.yml

This is something I thought we’d already done but alas not. Thanks to feedback on UserVoice for that one.

README Updated

The readme has been updated to let people know that they need to change the default admin password in the bort_migration along with the RESTFUL_AUTH_SITE_KEY in each of the environment files.

If you haven’t done this yet, make sure you do it yo.

Multistage Capistrano Deployment

Thanks for Phil Jeffs for fixing this one. Bort is now setup out of the box to deploy to a staging environment along with production.

Bort Sends a 404 on ActiveRecord::RecordNotFound

This is something that I normally forget to do in applications so it’s now baked into Bort. If a user requests a page and the record it’s looking for doesn’t exist, they’ll get sent to the 404 page with the correct status. If you don’t want this to happen, just remove the code from application.rb file.

Rake db:database_dump

Matt Hall has added a rake task to allow you to dump the contents of you database. Simply run rake db:database to get a dump of all the contents of your database.

Upcoming Suggested Changes

The two main upcoming changes to Bort are:

Default User Admin Panel

A lot of people seem to want this feature. We’re currently on the fence about it. Bort has tried to keep as simple as possible while keeping the features that a lot of us use. Thoughts on this would be great.

Add i18n support

Again a lot of people seem to want this but again we’re on the fence due to the reason above.

Removal of Role_requirement

I’ve noticed a lot of people on github who fork Bort seem to remove role_requirement. Also, I’ve found myself doing the same thing in favour of other options.

What do you guys think?

Suggestions? Problems?

As always, if you have any suggestions or problems then leave a message on our UserVoice and we’ll see what we can do.

Tagged with

Bort! Now with OpenID!

Just a small update on Bort.

Bort Project Page

I decided to make a dedicated page for Bort, so I only have one place to keep up to date.

Bort Project Page

Open ID Authentication

I’ve integrated OpenID with RESTful Auth on Bort in the new version. I’ll be perfectly honest though, I’ve never actually set up OpenID before, so I’d appreciate if people could take a look at it and let me know if it sucks.

User Voice! Load Noises!

We’ve set up an account on User Voice so you guys can submit ideas and all that jazz to us, then we can act like power hungry sultans and deny your ideas.

Bort @ User Voice

At this moment in time, we have 239 watchers on Github and are the 25th most popular project. Hooray for us.

Tagged with

Bort - A Base Rails Application

An Introduction

When I set up a new rails app, I generally do the same things every time. Set up a config file, install RESTful authentication, add password recovery stuff, blah blah. I’m sick of doing the same stuff over and over again. Enter Bort.

Bort is a base application featuring all the things I set up with every application. Now I can just download the app from git and be up and running within a matter of minutes. POW!

For those of you wondering is Bort stands for anything like “Bionic Optical Radioactive Trout”, it doesn’t. It’s taken from this episode of The Simpsons:

Anyway, enough of this piffle, on to the features:

The Features

I’m going to copy and paste this jazz from the readme file because, quite frankly, I’m lazy.

RESTful Authentication

RESTful Authentication is already setup. The routes are setup, along with the mailers and observers. Forgotten password comes setup, so you donít have to mess around setting it up with every project.

The AASM plugin comes pre-installed. RESTful Authentication is also setup to use user activation.

Will Paginate

We use will_paginate in pretty much every project we use, so Bort comes with it pre-installed.

Rspec & Rspec-rails

You should be testing your code, so Bort comes with Rspec and Rspec-rails already installed so youíre ready to roll.

Exception Notifier

You donít want your applications to crash and burn so Exception Notifier is already installed to let you know when everything goes to shit.

Asset Packager

Packages up your css/javascript so youíre not sending 143 files down to the user at the same time. Reduces load times and saves you bandwidth.

Routes

The routes for RESful Auth and the forgot password stuff are already sorted for you.

Settings YAML

There is a settings.yml file that contains site-wide stuff. The site name, url and admin email are all used in the RESTful Auth mailers, so you donít need to worry about editing them.

Database YAML

The database.yml defaults to sqlite3 but also contains the settings for MySQL in comments so you can switch over easily.

Capistrano Recipe

Bort comes ready to rock capistrano. The recipe that is setup is based on using git and passenger. Edit as you see fit.

Uses the Database for Sessions

Bort is setup to use the database to store sessions by default.

Misc

  • password and password_confirmation are set up to be filtered
  • there is a default application layout file
  • a page title helper has been added
  • index.html is already deleted
  • rails.png is already deleted
  • a few changes have been made to the default views

Using Bort

The git repo is located here: http://github.com/fudgestudios/bort/tree/master

  • Download and unzip Bort
  • Edit the database.yml and the settings.yml files
  • Rake db:migrate
  • Have a brew and celebrate

Suggestions etc

If you have any suggestions about things we should add, edit, delete from Bort then let me know. Think we should gemify it? You decide.

Tagged with

Edit: The code has been updated due to the awesome information presented in the comments.

For a project we’ve been working recently, we had to create four (uurgh) models from one form. After a lot of digging around and some trial and error we came up with a method which works well for us. I thought I’d document it for others as I couldn’t find a lot about it when originally trying do it.

Background

Say we have 3 models that are all mutually exculise from each other but we need to make sure that all of the models are valid before we save them. The original pplan was to use something similiar to the following:

if @person.save && @cat.save && @dog.save
  // Woohoo!
else
  // Epic fail
end

The problem with this method is: if @person and @dog are valid and save correctly but @dog fails due to validation, then you end up with a person and cat in your database when you don’t want them. This leads to infinite amounts of problems, as rSpec has happily announced on more than one occassion.

The second thing I tried was checking that all the models are valid first then saving them:

if @person.valid? && @cat.valid? && @dog.valid?
  @person.save
  @cat.save
  @dog.save
else
  // Epic fail
end

While this looks good on the controller side, it actually sucks. If person is invalid then the errors are shown on the page as expected, but you don’t to see if any of the other models have any errors because valid? doesn’t get called on them. Aaargh!

The Solution

The View

Did you know you could pass in more than one model to error_messages_for? No, neither did I? What about fields_for? It’s a life saver. Let’s look at an example form for a Person, Cat and a Dog:

<% form_for @person do |f| %>
<%= error_messages_for :object => [@person, @cat, @dog] %>
  <fieldset>
    <legend>Person Details</legend>
    <ol>
      <li>
        <%= f.label :name %>
        <%= f.text_field :name %>
      </li>
    </ol>
  </fieldset>
  <% fields_for :cat do |cat| %>
    <fieldset>
      <legend>Cat Details</legend>
      <ol>
        <li>
          <%= cat.label :breed %>
          <%= cat.text_field :breed %>
        </li>
      </ol>
    </fieldset>
  <% end %>
  <% fields_for :dog do |dog| %>
    <fieldset>
      <legend>Dog Details</legend>
      <ol>
        <li>
          <%= dog.label :breed %>
          <%= dog.text_field :breed %>
        </li>
      </ol>
    </fieldset>
  <% end %>
  <%= f.submit 'Pow!' %>
<% end %>

The main things to look out for here are error_messages_for and fields_for. We pass in an array of the objects we want to display errors for into error_messages_for using :object. This will display all the errors for those models, in our case the person cat and dog errors. Although you need to make sure all the errors for these models are being raised. We’ll look at this later on.

The other thing to take a look at is fields_for. I’ll let the API docs explain this for you:

Creates a scope around a specific model object like form_for, but doesnët create the form tags themselves. This makes fields_for suitable for specifying additional model objects in the same form.

So that’s our views done. On to the controller:

The Controller

This is the way we’ve been coping with the problem of validating all the models. I’m sure other people will have other suggestions, but this is what we’re rocking:

def new
  @person = Person.new
  @cat = Cat.new
  @dog = Dog.new
end

def create
  @person = Person.new(params[:person])
  @cat = Cat.new(params[:cat])
  @dog = Dog.new(params[:dog])
	
  # Run valid? on each model and check for failures
  if [@person, @cat, @dog].all?(&amp;:valid?)
    Person.transaction do
      @person.save!
      @cat.save!
      @dog.save!
    end
  else
    // Epic fail
  end
end

The only line that you really need to checkout here is the line that runs valid? on each model and check results:

if [@person, @cat, @dog].all?(&:valid?)

This line runs through each model and runs the valid? method and checks that all the results are true.

As pointed out in the comments, this line could be replaced with:

if @person.valid? & @cat.valid? & @dog.valid?

The Person.transaction block makes sure that if one of the models fails to save then the other models aren’t saved as well. This stops you ending up with random saved models that shouldn’t be there.

Bosh.

Further Things to Read with Your Eyes

Using jQuery with Ruby on Rails

By default, Rails comes packed with the Prototype javascript library and the effects library, Scriptaculous. While this is all well and good sometimes you want a change. I personally prefer jQuery to prototype. I don’t have any beef with prototype, infact I used it for about a year before even getting into rails, but I just prefer the jQuery syntax and selectors.

jQuery

jQuery is a fast, concise, JavaScript Library that simplifies how you traverse HTML documents, handle events, perform animations, and add Ajax interactions to your web pages. jQuery is designed to change the way that you write JavaScript.

jQuery, like Protoype, is a javascript library that takes care of much of the grunt work you face day to day. It helps to resolve your cross browser issues, makes creating dom elements easier and various other things.

Some Prototype / jQuery Examples

I’ll post a couple of code examples so you can see the difference in the libraries. I’m not saying these examples are the best way to do things (I know people will say otherwise), they’re just here to help you understand the libraries.

Note: I haven’t tested these examples, although I believe they should all work.

Prototype

document.observe('dom:loaded', function() {
  $$('a[rel~=external]').invoke('writeAttribute', 'target', 'blank');
});

jQuery

$(function() {
  $('a[rel=external]').attr('target', 'blank');
});

Changes class of a div on click of an element

Prototype

$('the-link').observe('click', function() {
  $('the-div').addClass('hello');
});

jQuery

$('#the-link').click( function() {
  $('#the-div').addClass('hello');
});

For more information on jQuery syntax and all the bells and whistles, have a look at the jQuery Documentation.

More Prototype <=> jQuery Examples

Remy Sharp has also published a walk through comparison between Prototype and jQuery to help developers go from one language to another.

Hello, jRails!

Want to use jQuery but love your prototype helpers too much? Fear not! Enter jRails, stage left.

jRails is a drop-in jQuery replacement for Prototype/script.aculo.us on Rails. Using jRails, you can get all of the same default Rails helpers for javascript functionality using the lighter jQuery library.

That basically says it all.

Installing jRails

ruby script/plugin install http://ennerchi.googlecode.com/svn/trunk/plugins/jrails

Bosh. All sorted. The plugin will install all the required javascript files over to public/javascripts. Unless you’re planning on using prototype along side jQuery you will probably want to delete the prototype files in your javascripts folder.

Including jQuery and Friends

Now you want to open up your application.html.erb file and edit the javascript include tag:

<%= javascript_include_tag :defaults %>

This will now include all the jRails files instead of the prototype/script.aculo.us files. You might want to just include the files that you need instead of all these:

<%= javascript_include_tag 'jquery' %>
<%= javascript_include_tag 'jquery-ui' %>
<%= javascript_include_tag 'jrails' %>

Using jQuery in RJS

You don’t have to do anything. Brilliant. That’s the main point of the jRails plugin. It does all the grunt work for you. Just use your helpers as you normally would.

I will say one thing though: a lot of the time you may just find it easier to use page << at certain times:

page << "$('span#bacon').text('CHunKy');"

jQuery UI

jQuery UI provides abstractions for low-level interaction and high-level, themeable widgets, built on top of the jQuery JavaScript Library, that you can use to build highly interactive web applications.

The core of the library revolves around different mouse interactions, namely drag and dropping, sorting, selecting, and resizing, as well as a powerful set of effects.

On top of the core interactions are built a number of reusable widgets, including accordions, date pickers, dialogs, sliders and tabs.

I was going to write some blurb about jQuery UI but I feel that this quote says it all, really. Want to know more? Check out the jQuery UI site

Some jQuery Scripts

I thought I’d list a couple of helpful scripts to help you on your way to the wonderful world of jQuery. So here goes:

Validation

Validation is a great form validation script by Jˆrn Zaefferer. Unobtrusive, sexy and just plain brilliant.

Lightbox

jQuery lightBox plugin is simple, elegant, unobtrusive, no need extra markup and is used to overlay images on the current page through the power and flexibility of jQuery¥s selector.

lightBox is a plugin for jQuery. It was inspired in Lightbox JS by Lokesh Dhakar.

The better way to know what is jQuery lightBox plugin, click the Example tab above and see it in action.

Everybody loves a lightbox, surely? The jQuery Lightbox script is based on by Leandro Vieira Pinho.

If you have any other suggestions for great jQuery scripts, let me know and I’ll add them to the list.

So you’ve installed FFMPEG. Now it’s time to move onto converting the video. For this we’re going to be using a couple of plugins.

Paperclip

We will be using the paperclip plugin to upload the videos onto our server. Details of how to install paperclip can be found in my previous article: Paperclip: Attaching Files in Rails. To install you can use any of the following:

git://github.com/thoughtbot/paperclip.git # Github
https://svn.thoughtbot.com/plugins/paperclip/trunk/ # SVN
http://rubyforge.org/projects/paperclip/ # Gem version

Acts As State Machine

Acts_as_state_machine allows you to turn your model into a Finite State Machine (FSM).

A finite state machine (FSM) or finite state automaton (plural: automata) or simply a state machine, is a model of behaviour composed of a finite number of states, transitions between those states, and actions. A finite state machine is an abstract model of a machine with a primitive internal memory.

So let’s install acts_as_state_machine:

ruby script/plugin install http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk/ acts_as_state_machine

Creating the Model

First off, let’s create our video model. We’ll call it video. Inventive, eh?

ruby script/generate model video

Open up your new video model and let’s edit it to look like this:

class Video < ActiveRecord::Base
  # Paperclip
  # http://www.thoughtbot.com/projects/paperclip
  has_attached_file :source

  # Paperclip Validations
  validates_attachment_presence :source
  validates_attachment_content_type :source, :content_type => 'video/quicktime'
end

Nothing spectacular. If you’ve used Paperclip before then nothing should surprise you here. The has_attached_file :source line tells our model that it has an uploaded file called source. This is where we will be storing our video files. The rest of the file uses the built in Paperclip validations.

Adding States

Underneath your current model content, we want to add the following:

  # Acts as State Machine
  # http://elitists.textdriven.com/svn/plugins/acts_as_state_machine
  acts_as_state_machine :initial => :pending
  state :pending
  state :converting
  state :converted, :enter => :set_new_filename
  state :error
  
  event :convert do
    transitions :from => :pending, :to => :converting
  end
  
  event :converted do
    transitions :from => :converting, :to => :converted
  end
  
  event :failed do
    transitions :from => :converting, :to => :error
  end

This will setup acts_as_state_machine along with the states and transitions we want to use. It’s all pretty simple.

Run the Migrations

Open up the xxx_create_videos.rb migration file and edit it so it looks like the following:

class CreateVideos < ActiveRecord::Migration
  def self.up
    create_table :videos do |t|
      t.string :source_content_type
      t.string :source_file_name
      t.integer :source_file_size
      t.string :state
      t.timestamps
    end
  end

  def self.down
    drop_table :videos
  end
end

The columns starting with :source_ are all files for Paperclip. The :state column is used with acts_as_state_machine.

Run the migrations:

rake db:migrate

Adding the Conversion Methods

In your video.rb file add the following methods to your model. These methods take care of the converting the video file:

# This method is called from the controller and takes care of the converting
def convert
  self.convert!
  success = system(convert_command)
  if success && $?.exitstatus == 0
    self.converted!
  else
    self.failure!
  end
end

protected

# This method creates the ffmpeg command that we'll be using
def convert_command
  flv = File.join(File.dirname(source.path), "#{id}.flv")
  File.open(flv, 'w')
  
  command = <<-end_command
    ffmpeg -i #{ source.path } -ar 22050 -ab 32 -acodec mp3 
  -s 480x360 -vcodec flv -r 25 -qscale 8 -f flv -y #{ flv }
  end_command
  command.gsub!(/\s+/, " ")
end

# This update the stored filename with the new flash video file
def set_new_filename
  update_attribute(:source_file_name, "#{id}.flv")
end

The Controller

I’m not going to post the views in this article as they should be straight forward. Just make sure to add :multipart => true to your form otherwise your file’s won’t be uploaded and you’ll look stupid.

class VideosController < ApplicationController
  def index
    @videos = Video.find :all
  end
  
  def new
    @video = Video.new
  end
  
  def create
    @video = Video.new(params[:video])
    if @video.save
      @video.convert
      flash[:notice] = 'Video has been uploaded'
      redirect_to :action => 'index'
    else
      render :action => 'new'
    end
  end
  
  def show
    @video = Video.find(params[:id])
  end
end

After the video is saved in the create method, the convert method is called. This should convert the video to a flash video file, update the database entry and set the state of the model. The state will be set to converted if everything went to plan, or error if everything went to shit.

You can view the full source of video.rb here.

Displaying the Video in a View

When you want to display the video in a view, you will need two things. The first, is swfobject, an unobtrusive way to include flash into a page.

SWFObject is a small Javascript file used for embedding Adobe Flash content. The script can detect the Flash plug-in in all major web browsers (on Mac and PC) and is designed to make embedding Flash movies as easy as possible. It is also very search engine friendly, degrades gracefully, can be used in valid HTML and XHTML 1.0 documents, and is forward compatible, so it should work for years to come.

The second is a flash video player. I highly suggest JW FLV Media Player.

The JW FLV Media Player (built with Adobe’s Flash) is an easy and flexible way to add video and audio to your website. It supports playback of any format the Adobe Flash Player can handle (FLV, but also MP3, H264, SWF, JPG, PNG and GIF). It also supports RTMP and HTTP (Lighttpd) streaming, RSS, XSPF and ASX playlists, a wide range of flashvars (variables), an extensive javascript API and accessibility features.

Make sure you include the swfobject.js file on the page you wish to display your video on:

<%= javascript_include_tag 'swfobject' %>

Next, copy the mediaplayer.swf to public/flash/mediaplayer.swf

<div id="player">
  You need to have <%= link_to 'Flash Player', 'http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash' %> installed to display this video
</div>
 
<script type="text/javascript">
  var so = new SWFObject('/flash/mediaplayer.swf', 'mpl', '480', '360', '8');
  so.addParam('allowscriptaccess', 'always');
  so.addParam('allowfullscreen', 'true');
  so.addVariable('height', '360');
  so.addVariable('width', '480');
  so.addVariable('file', '<%= @video.source.url %>');
  so.write('player');
</script>

The contents of the div will be replaced with the flash player, if flash isn’t installed the user is shown a message with a link to download flash player. The <%= @video.source.url %> line will output the path to the flv video file.

There’s a setup wizard on the JW FLV Player site located here.

Final Note

You shouldn’t really be converting videos with the user request. You should be using a background worker to do it for you. I’ll be showing you how to do this in the next article in this series.

Give it a whirl. As far as I know it should work fine. I’ve tested it on my local host and it seems to work. Admittedly, there should be a lot more error checking and whatnot, but for a simple example I think it does the job.

If you have any suggestions, improvements etc that I could incorporate into this article then leave a comment and I’ll sort it out.

More In This Series

This is the first of a three part series covering how to convert videos using the marvellous FFMPEG library. Video conversion is a must have if you are planning on creating a social network site.

In this part I’ll be covering how to install FFMPEG on Ubuntu Hardy Heron (8.04).

Install FFMPEG Dependencing

First off, you need to download and install the dependency packages for FFMPEG. Without these, it’ll all go tits. So it’s a good thing to sort out.

sudo apt-get install build-essential subversion libx264-dev libdts-dev libswscale-dev liblame-dev libfaad2-dev libfaac-dev libxvidcore4-dev liba52-0.7.4 liba52-0.7.4-dev 

The lib* files help you to convert various video formats into flash videos, or which ever format you wish. Subversion is needed so you can download the FFMPEG source.

Check Out FFMPEG Source

Next, you need to checkout the current FFMPEG source from subversion. So let’s do that:

mkdir src
cd src
svn checkout svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg
cd ffmpeg

Bosh! We now have the FFMPEG source on our server. So far so good.

Compiling FFMPEG

Now we’re ready to compile to beast. Make sure you’re in the ffmpeg directory that we just downloaded. Then run the configure command:

./configure --enable-gpl --enable-libmp3lame --enable-libvorbis --enable-libtheora --enable-liba52 --enable-libdc1394 --enable-libgsm --enable-libfaad --enable-libfaac --enable-libxvid --enable-pthreads --enable-libx264 --enable-shared--enable-postproc --enable-avfilter-lavf --enable-swscale --enable-avfilter 

Then we’re ready to compile:

make
sudo make install

This will take ages. So don’t worry if it seems to be going on for a while. The last time I did it, it took about 5 minutes or so.

Hopefully, once that’s finished, you should be ready to rock.

Testing FFMPEG

Now, hold your breath and type:

ffmpeg -v

You should now get a load of blurb along the lines of:

jim@jim-ubuntu:~$ ffmpeg -v
FFmpeg version SVN-r13207, Copyright (c) 2000-2008 Fabrice Bellard, et al.
  configuration: --enable-gpl --enable-libvorbis --enable-libtheora --enable-liba52 --enable-libdc1394 --enable-libgsm --enable-libmp3lame --enable-libfaad --enable-libfaac --enable-libxvid --enable-pthreads --enable-libx264 --enable-shared --enable-swscale --enable-avfilter --enable-postproc --enable-avfilter-lavf
  libavutil version: 49.6.0
  libavcodec version: 51.57.0
  libavformat version: 52.13.0
  libavdevice version: 52.0.0
  libavfilter version: 0.0.0
  built on May 21 2008 11:55:20, gcc: 4.2.3 (Ubuntu 4.2.3-2ubuntu7)
ffmpeg: missing argument for option '-v'

If you don’t get anything similar to the above, then you’ve fucked it. Check the FFMPEG docs or post here and I’ll try and help you out.

More In This Series

I love Phusion Passenger. It takes away most of the pain from deploying. Instead of having to mess around with lengthy apache config files, you can just upload. POW!

Phusion Passenger ó a.k.a. mod_rails makes deployment of applications built on the revolutionary Ruby on Rails web framework a breeze. It follows the usual Ruby on Rails conventions, such as ìDonít-Repeat-Yourself

Installing Passenger

Another great thing about Passenger is that it’s stupidly easy to install.

gem install passenger
passenger-install-apache2-module

Then just follow the instructions that are displayed inside the terminal. It shouldn’t take more than 5-10 minutes max to get this shit on the road.

Passenger Meet Capistrano

You are using capistrano, aren’t you? Capistrano is a great tool, by Jamis Buck, for deploying your applications. It takes all the monotonous stuff and does it for you, which is nice.

If you’re not using capistrano, you can install it with the following:

gem install capistrano

Next, go to the root directory of your application and type:

capify .

This will set up your application for use with capistrano by creating a deploy.rb file and a Capfile. The deploy file is a recipe that will be used every time you deploy your application. Now lets look at making capistrano play with passenger.

The Deploy Recipe

I must confess something at this point: I’ve not actually tested this recipe yet as I’ve not had time. As far as I’m concerned it should work. If you find any problems let me know and I’ll fix them.

#############################################################
#	Application
#############################################################

set :application, "example"
set :deploy_to, "/var/www/#{application}"

#############################################################
#	Settings
#############################################################

default_run_options[:pty] = true
set :use_sudo, true

#############################################################
#	Servers
#############################################################

set :user, "jim"
set :domain, "example.com"
server domain, :app, :web
role :db, domain, :primary => true

#############################################################
#	Subversion
#############################################################

set :repository,  "http://www.example.com/svn/example"
set :svn_username, "jim"
set :svn_password, "password"
set :checkout, "export"

#############################################################
#	Passenger
#############################################################

namespace :passenger do
  desc "Restart Application"  
  task :restart do  
    run "touch #{current_path}/tmp/restart.txt"  
  end
end

after :deploy, "passenger:restart"

From what I’ve read, the last few lines are all you should need to restart your application. This will be called after all deploy calls so you shouldn’t have to worry about anything.

Long live passenger

Some Stuff to Read

Here’s a list of a few things that are worth reading regarding capistrano and/or passenger.

Capistrano

Passenger (mod_rails)

I’m not going to cover how to actually code an entire social network site in rails as all social network sites vary in their functionality (and it’ll take too long). I will cover plugins and other things you might find useful though.

Quick Start

If you don’t really want to do the coding but want to get a site up and running and soon as possible, you may want to have a look at Lovd by Less by the guys over at Less Everything. Lovd by Less contains user signups, galleries, blogs, comments and various other things that you might want, so it’s a great starting block for your site.

Social Network Plugins

Here’s a list of plugins that I’ve found to be useful while coding my own social networking site:

Restful Authentication

RESTful Authentication is pretty much the defacto standard for user authentication in rails. It allows easily set up user signups, login functionality and email notifications. The plugin doesn’t set up thing’s like forgotten password functionality but there is a great tutorial over Rails Forum.

# To Install
ruby script/plugin source http://svn.techno-weenie.net/projects/plugins
ruby script/plugin install restful_authentication

Paperclip

Paperclip is a brilliant plugin by Jon Yurek over at ThoughtBot. Paperclip is used for managing file uploads and attaching the files to models. You can read more over at my article: Paperclip: Attaching Files in Rails.

# To Install
svn export https://svn.thoughtbot.com/plugins/paperclip/tags/rel_2-0-2
piston import https://svn.thoughtbot.com/plugins/paperclip/trunk

Will_paginate

Will paginate is a great plugin for allowing paging of your records. Paging is a pain in the ass, but will_paginate makes it easy as pie.

# To Install
ruby script/plugin install svn://errtheblog.com/svn/plugins/will_paginate

Acts_as_slugable

Acts_as_slugable takes the pain out of generating URL slugs. Everyone prefers meaningful URLs, so instead of showing a users page with ‘/users/231’, you can use ‘/users/jim-neath’. Nice.

# To Install
ruby script/plugin install http://code.dunae.ca/acts_as_slugable

White_list

White_list is yet another brilliant from Techno Weenie. The white_list helper will html encode all tags and strip all attributes that aren’t specifically allowed. It also strips href/src tags with invalid protocols, like javascript: especially. It does its best to counter any tricks that hackers may use, like throwing in unicode/ascii/hex values to get past the javascript: filters.

# To Install
ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/white_list/

Acts_as_commentable

Acts_as_commentable allows you to add comments to your models. It takes care of all the polymorphic associations for you, which is nice.

# To Install
ruby script/plugin install http://juixe.com/svn/acts_as_commentable

ReCAPTCHA

Fucking captchas. Unfortunately a necessary evil. If you’re going to use captchas then you might as well help to digitalise books. The reCAPTCHA plugin utilises the reCAPTCHA service which digitalises books by making users input the text.

# To Install
ruby script/plugin install svn.ambethia.com/pub/rails/plugins/recaptcha/

Acts_as_taggable_on_steroids

Everybody loves tagging, surely? Tag pictures, videos, blog posts, whatever you want. Acts_as_taggable_on_steroids is a great plugin for allowing your users to tag their stuff. It allows tag clouds and all that web 2.0 jazz everyone seems to love.

# To Install
ruby script/plugin install http://svn.viney.net.nz/things/rails/plugins/acts_as_taggable_on_steroids

Exception Notifier

Exception Notifier is a must have. It emails you when your live application fails. No matter how much testing you do, no doubt there’s going to be a scenario where it fails and when that happens you want to know.

# To Install
ruby script/plugin install exception_notification

Throttler

This is one of my favourite plugins. Say your site get’s slashdotted of dugg and you end up with immense traffic, the main thing is to keep your site up and running. This is where throttler comes in. You can throttle certain actions on your site when your server load is above a certain level. So you could disable video uploads while your server load is above x to prevent your server from crumbling.

# To Install
ruby script/plugin install http://svn.kabisa.nl/rails/plugins/throttler

Backup_fu

You’ve been working on your social network site for months and finally the traffic is coming in and you have a decent user base. Then one day your server dies and you lose all your data. Woe is you. You should have backed up. Using Backup_fu you can automatically backup your database and files to Amazon S3.

# To Install
sudo gem install aws-s3
ruby script/plugin install http://backup-fu.googlecode.com/svn/backup_fu/

Fischy Friends

Fischy_friends is a plugin by Daniel Fischer. It’s a great starting point for a friends system. I’ve used it on a couple of my own projects and it’s worked great for me.

Other Useful Tools

SWFUpload

I love SWFUpload. It uses a small flash file to allow users to upload multiple files at once. The front end is completely open and coded in javascript so you can customise it how you like. You can see the demos here.

Download: http://swfupload.googlecode.com/files/SWFUpload%20v2.0.2.Release.zip

TinyMCE Text Editor

TinyMCE is WYSIWYG editor coded entirely in javascript. It’s useful for the less techno savvy of your users (which will no doubt be most). There’s a whole load of plugins available for the editor so it’s highly extensible.

Download: http://prdownloads.sourceforge.net/tinymce/tinymce_3_0_7.zip?download

FFMPEG/Mencoder

FFMPEG is a command line utility to convert various formats of video into other formats. The main use you’ll want to use this for is to convert videos into flv files for use with a flash video player.

JW FLV Media Player

The JW FLV Media Player (built with Adobe’s Flash) is an easy and flexible way to add video and audio to your website. It supports playback of any format the Adobe Flash Player can handle (FLV, but also MP3, H264, SWF, JPG, PNG and GIF). It also supports RTMP and HTTP (Lighttpd) streaming, RSS, XSPF and ASX playlists, a wide range of flashvars (variables), an extensive javascript API and accessibility features.

Rails Hosting

Once you’ve got your wonderful social network finished, you’re going to want somewhere to host the beast.

I highly recommend checking out Brightbox for all your hosting needs. They offer affordable servers complete with Five Runs.

Paperclip: Attaching Files in Rails

Paperclip is an awesome rails plugin by Jon Yurek at Thoughtbot. It is one of many plugins currently available that cater for file uploading and thumbnailing (see: Attachment_fu, file_column, etc). Now a quick quote from Jon:

For some reason, file attachment is annoying. I don’t know why, and I know a lot of people have attempted to solve the problem in the past, myself included. Yet it still is. Having gotten fed up with gotchas and design decisions that we didn’t agree with, I went and wrote Paperclip on the plane to RailsConf last year. We’ve been using it here in various forms since and IMHO it’s the way to handle uploads, and finally decided that it should be released.

Installing Paperclip

You can install Paperclip using a variety of different methods:

svn export https://svn.thoughtbot.com/plugins/paperclip/tags/rel_2-0-2
piston import https://svn.thoughtbot.com/plugins/paperclip/trunk

You can also grab Paperclip from the git repository.

Quick Note: If you’re a windows user, you’re going to need to go for the trunk version as this contains a fix to a problem that basically meant that Paperclip borked.

Basic Usage

class User < ActiveRecord::Base
  # Paperclip
  has_attached_file :photo,
    :styles => {
      :thumb=> "100x100#",
      :small  => "150x150>" }
end

Attached files don’t need to have a separate model (thank god). Your attachments are treated just like any other attribute. Images aren’t saved until your model is saved. There are a lot of bonus options but I’ll cover them towards the end of the article.

class AddPhotoToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :photo_file_name, :string # Original filename
    add_column :users, :photo_content_type, :string # Mime type
    add_column :users, :photo_file_size, :integer # File size in bytes
  end

  def self.down
    remove_column :users, :photo_file_name
    remove_column :users, :photo_content_type
    remove_column :users, :photo_file_size
  end
end

Don’t forget to add these columns! Otherwise you’ll end up scratching your head and wondering where the hell you went wrong. The first part of the column names is the same as whatever you’re called your attached file. In our case that’s photo. Now update your database:

rake db:migrate

Now that your database is sorted, we can start working on adding some content. In your view you can add a file field like you would normally:

<% form_for :user, :html => { :multipart => true } do |f| %>
  <%= f.file_field :photo%>
<% end %>

Don’t forget the :multipart => true part or everything will fail. Then you will cry.

Now in your controller, you don’t need to do a thing (hooray).

def create
  @user = User.create(params[:user])
end

You should now be able to upload user photos to your hearts content.

To display your user’s photos all you need to do is call:

<%= image_tag @user.photo.url %>
<%= image_tag @user.photo.url(:thumb) %>

The first call will display the original image. The second one will display the thumbnail image. Easy, yes?

Paperclip Validations

At the moment Paperclip has two different validation types, validates_attachment_presence and validates_attachment_content_type.

validates_attachment_presence

validates_attachment_presence gives your ActiveRecord style validations to check to see if your paperclip model has an attachment present.

validates_attachment_presence :avatar

validates_attachment_content_type

validates_attachment_content_type lets your check the type of file that has been uploaded by checking it’s mime type.

validates_attachment_content_type :avatar, :content_type => 'image/jpeg'

Paperclip Options

Now that you’ve seen how easy to use and awesome Paperclip is, let’s have a look at some of the additional settings you can use:

has_attached_file :photo, :url => "/:class/:attachment/:id/:style_:basename.:extension"

Using :url you set when your images can be accessed from. The above setting would mean that your files are located at URLs similiar to /user/photo/1/thumb_originalfilename.jpg

has_attached_file :photo, :default_url => "/:class/:attachment/missing_:style.png"

The :default_url option is used if there is no attached file for a model. If a user doesn’t have any uploaded avatar you could use this option to set a default avatar to show.

has_attached_file :photo, :styles => { :normal => "100x100#", :small => ["70x70>", :jpg] }

:styles is a hash of thumbnail styles. The styles use the standard ImageMagick geometry rules. Paperclip also adds the ’#’ option which will create square thumbnails that are nicely cropped.

has_attached_file :photo, :default_style => :thumb

:default_style is pretty straight forward. You can select a default style from your style list that will be called, instead of the original file, when you use @user.photo.url

has_attached_file :photo, :path => ":rails_root/public/:class/:attachment/:id/:style_:basename.:extension"

Using :path you can select where the files are saved to on your box. If you change this, make sure to change the :url setting to relate to the new path.

has_attached_file :photo, :whiny_thumbnails => true

:whiny_thumbnails will raise an error if there is a problem creating thumbnails. Set to true by default.

Some Waffle

Paperclip is a great plugin. It has a smaller memory footprint than Attachment_fu, it doesn’t require the use of Rmagick (eugh) and it has all the options that I’ve wished that Attachment_fu had. Give it a try and let me know what you think

Again, maximum kudos to Jon Yurek and all the guys over at ThoughtBot.

I am available for freelance work! Click here to email me.

Jim Neath is a Freelance Ruby on Rails & Facebook app developer from Manchester, UK, currently working for Engine Yard.