A one-line web server in Ruby

Prodded by a tweet, here's how to serve a directory in one line of Ruby:

ruby -rwebrick -e'WEBrick::HTTPServer.new(:Port => 3000, :DocumentRoot => Dir.pwd).start'

Handy for sharing files at a conference, for example.

Installing the MySQL gem on OSX 10.6 (Snow Leopard) with MacPorts MySQL5

Took a while to work this command out. Hopefully it can save someone else some time.

sudo port install mysql5-server
sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/opt/local/bin/mysql_config5

Decoupling Nagios Host and Service check events for fun and profit

Nagios does a pretty good job of watching over my services and hosts, but I want to do a little more with the events it creates – when it checks a service and something is wrong, or when something recovers. In particular I want to give my clients the ability to select at an incredibly high resolution what sort of notifications they get, for what services, how often, and at what level of technical detail. Coupled with this I want to up-sell the services that Xeriom offers – if the disk is getting full or the transfer quota is being consumed so fast that it wont last until the end of the month I want to make it easy to upgrade plans. I’d also like to be able to try out some fun things – iPhone push notifications, SMS gateways, audible alarms, whatever – without worrying that I might destroy Nagios and bring my monitoring setup to its knees.

Message queues are a great way of decoupling systems, moving risk and complexity elsewhere. Nagios shouldn’t have to worry about all of the stuff I want to build around the monitoring system, it should focus just on the core features that I like it for: monitoring my hosts and services.

Luckily, I already have ActiveMQ running for other tasks, writing a STOMP client using SMQueue is pretty trivial, and Nagios has several ways to execute external commands when events happen including the global host and service event handlers. All I need is a command to have Nagios run that’ll accept a bunch of information from Nagios and stick it on the message queue.

Here’s what I came up with:

require 'rubygems'
require 'smqueue'
require 'json'

message = {
  :hostname => ARGV[2],
  :service => ARGV[3],
  :state => ARGV[4],
  :state_type => ARGV[5],
  :state_time => ARGV[6].to_i,
  :attempt => ARGV[7].to_i,
  :max_attempts => ARGV[8].to_i,
  :time_t => Time.now.to_i
}

configuration = {
  :host => ARGV[0],
  :name => ARGV[1],
  :adapter => :StompAdapter
}

broadcast = SMQueue(configuration)
broadcast.put message.to_json, "content-type" => "application/json"

You’ll need Ruby and RubyGems installed. Once you have those, install the script like this:

sudo su -
gem sources -a http://gems.github.com/
gem install seanohalpin-smqueue json --no-ri --no-rdoc
cd /usr/bin
wget http://gist.github.com/raw/306765/2a3e9cbade88b4c6dd430e108bc8a28f95047462/notify-service-by-stomp.rb
chmod +x notify-service-by-stomp.rb
Once it's installed tell Nagios to use it by adding this to your Nagios configuration:
define command {
  command_name notify-service-by-stomp
  command_line /usr/bin/notify-service-by-stomp.rb mq.example.com /topic/foo.bar.baz.quux $HOSTADDRESS$ "$SERVICEDESC$" $SERVICESTATE$ $SERVICESTATETYPE$ $SERVICEDURATIONSEC$ $SERVICEATTEMPT$ $MAXSERVICEATTEMPTS$
}

global_service_event_handler=notify-service-by-stomp

Change mq.example.com to be the hostname of your message broker, and /topic/foo.bar.baz.quux to be the topic or queue that you’d like notifications to be sent to. Restart Nagios and you should start receiving notifications on that queue or topic.

If you don’t receive notifications form Nagios very often then a simple way to test that this is working is to attach stompcat – a cat type tool that uses STOMP as a source – to the topic or queue, then send a few test notifications to the same queue by manually running the same command that Nagios would.

Here’s a simple stompcat tool in case you don’t have one handy:

#! /usr/bin/env ruby

# Run me like this:
#
#   ./stompcat.rb mq.example.com /topic/foo.bar.baz.quux
#

require 'rubygems'
require 'smqueue'

configuration = {
  :host => ARGV[0],
  :name => ARGV[1],
  :adapter => :StompAdapter
}

source = SMQueue(configuration)
source.get do |m|
  payload = m.body
  puts ">>> #{payload}"
end

Here’s how to send notifications to the queue or topic:

/usr/bin/notify-service-by-stomp.rb mq.example.com \
  /topic/foo.bar.baz.quux service-host.example.com "SERVICE NAME" \
  WARNING HARD 86492 6 6

If it’s working you should get an entry like this showing up where you’re running the stompcat:

{
  "time_t":1266427384,
  "state":"WARNING",
  "state_type":"HARD",
  "state_time":86492,
  "attempt":6,
  "hostname":"service-host.example.com",
  "max_attempts":6,
  "service":"SERVICE NAME"
}

You should be able to change the stompcat example to perform more complex and interesting actions – looking up clients in a database, sending text messages if an account has enough credit, whatever you fancy. If you come up with something fun, please let me know!

Returning explicitly is slower

My main concern about returning explicitly is readability. It's a very subjective thing, but in general whenever I see an unnecessary return statement my internal WTF counter increments.

Less subjective though, it has been pointed out that returning explicitly is slower.

Benchmarking in Ruby is easy. Here's how:

require 'benchmark'

def explicit
  return "TEST"
end

def implicit
  "TEST"
end

n = 100_000_000
Benchmark.bmbm do |x|
  x.report("Explicit return") { n.times { explicit } }
  x.report("Implicit return") { n.times { implicit } }
end

And here are the results of this particular benchmark:

Rehearsal ---------------------------------------------------
Explicit return  50.380000   0.210000  50.590000 ( 51.000510)
Implicit return  36.200000   0.100000  36.300000 ( 36.454038)
----------------------------------------- total: 86.890000sec

                      user     system      total        real
Explicit return  47.650000   0.070000  47.720000 ( 47.744167)
Implicit return  35.900000   0.070000  35.970000 ( 35.985493)

This shows that while returning explicitly is slower, like the to_proc hack it's not slow enough to matter. You need to return a huge number of times to see any significant difference.

Does this change my mind? No. Returning explicitly is still ugly.

Update: The above benchmark was run on Ruby 1.8.6. Tom Ward has provided similar benchmarks for Ruby 1.8.7, 1.9 and jRuby 1.1.6 (using n = 10,000,000) which show that the cost of explicitly returning on these platforms in negligible. Still ugly though.

The stack trace is precious!

The stack trace is one of the most valuable pieces of information you can have when trying to debug a problem. It tells you what line of code was being run when an error was thrown and gives you an idea of the execution path that lead to that line of code being run.

Quick plea then. Please don't do this:

def foo
  do_something
rescue => e
  puts "Problem: #{e}"
  raise e
end

This will start a new stack trace at raise e. If I rescue this further up the stack there's no indication of where the problem was originally encountered - I just get pointed at your error handling code. Precious information, gone.

Do this instead:

def foo
  do_something
rescue => e
  puts "Problem: #{e}"
  raise
end

Note the lack of argument in the call to raise. This tells Ruby to re-raise the last exception. The stack trace remains intact and debugging can continue unhindered. Glory be!

The truth speaks for itself!

Not just for Ruby this time. This applies to pretty much every programming language under the sun.

Don't use unnecessary control statements to determine whether you want to return true or false.

def foo
  if some_boolean && other_boolean
    return true
  else
    return false
  end
end

You should do this instead:

def foo
  return some_boolean && other_boolean
end

It's very very rare that I ever have to return an explicit true or false. This should be a warning sign.

Of course, in Ruby you don't need to return explicitly so you should do this:

def foo
  some_boolean && other_boolean
end

You don't need to return explicitly!

Ruby returns the value of the last line executed.

Don't do this:

def foo
  value = Foo.first(:conditions => { :label => "bar" })
  return value
end

Do this instead:

def foo
  Foo.first(:conditions => { :label => "bar" })
end

Twitter OAuth authentication using Ruby

Here are the steps involved in using Twitter to authenticate using OAuth... because I wanted this post a few days ago and couldn't find it.

Install the required gems.

sudo gem install json oauth

Set up your application at http://twitter.com/apps. Make sure you choose Browser as your application type and check the box saying that you want to use Twitter for login.

Note that if you make a mistake on the new application form it will default to Client for that application type and uncheck the login box. Make sure you set these correctly!

Here comes the magic. Despite the hugely complicated examples found elsewhere only two actions are required. One sets up the authentication request (the login action) and one handles the authentication being completed (the callback action). If you've used OpenID for authentication before this setup should be pretty familiar.

Your login action will look something like this:

# consumer_key and consumer_secret are from Twitter.
# You'll get them on your application details page.
oauth = OAuth::Consumer.new(consumer_key, consumer_secret,
                             { :site => "http://twitter.com" })

# Ask for a token to make a request
url = "http://whatever.com/login/complete"
request_token = oauth.get_request_token(:oauth_callback => url)

# Take a note of the token and the secret. You'll need these later
session[:token] = request_token.token
session[:secret] = request_token.secret

# Send the user to Twitter to be authenticated
redirect_to request_token.authorize_url

Your callback action will look something like this:

# Your callback action will look something like
# this:
#
# Your callback URL will now get a request that contains an 
# oauth_verifier. Use this and the request token from earlier to 
# construct an access request.
request_token = OAuth::RequestToken.new(oauth, session[:request_token],
                                        session[:request_token_secret])
access_token = request_token.get_access_token(
                 :oauth_verifier => params[:oauth_verifier])

# consumer_key and consumer_secret are from Twitter.
# You'll get them on your application details page.
oauth = OAuth::Consumer.new(consumer_key, consumer_secret,
                             { :site => "http://twitter.com" })

# Get account details from Twitter
response = oauth.request(:get, '/account/verify_credentials.json',
                         access_token, { :scheme => :query_string })

# Then do stuff with the details
user_info = JSON.parse(response.body)
# Like find the person that logged in...
Person.find_by_twitter_id(user_info["id"])

If after implementing this you keep getting 401 Unauthorized errors, check that your application is set to browser mode in the Twitter configuration options.

You don't need to count array offsets by hand!

Working with arrays in Ruby. Don't do this:

index = 0
for item in array
  index += 1
  puts "Item #{index}: #{item.inspect}"
end

Do this instead:

array.each_with_index do |item, index|
  puts "Item #{index}: #{item.inspect}"
end

There are a bunch of handy methods like this. Read the Enumerable documentation and make your code that little bit more readable.

First Steps with Rabbit MQ in Ruby 1.8.6

Until recently I was more than happy using ActiveMQ as my message broker. I had heard of RabbitMQ several times but never took the chance to look into it. A recent talk at LRUG made me decide that I had left it too long and that if I didn't start investigating soon I'd be left behind.

Here's how I got started using RabbitMQ 1.6.0 on OS X under Ruby 1.8.6.

Installation

 mkdir /tmp/rabbit-mq && cd /tmp/rabbit-mq
 wget http://www.rabbitmq.com/releases/rabbitmq-server/v1.6.0/rabbitmq-server-generic-unix-1.6.0.tar.gz
 tar -xzvf rabbitmq-server-generic-unix-1.6.0.tar.gz
 sudo mv rabbitmq_server-1.6.0/ /opt/local/lib

Running the server

sudo /opt/local/lib/rabbitmq_server-1.6.0/sbin/rabbitmq-server

Seriously, that's it.

Passing messages

When I wrote about getting started with SMQueue I created a consumer that pushed timestamps onto the queue and a consumer that printed the values from the queue to the terminal. Recreating that using the AMQP gem is simple.

First, install the AMQP gem.

gem sources -a http://gems.github.com
gem install tmm1-amqp

Open an IRB session and paste this code to create a producer:

require 'mq'
EM.run {
  broker = MQ.new
  EM.add_periodic_timer(1) { 
    broker.queue("timestamps").publish(Time.now.to_f)
  }
}

Open another IRB session and paste this to create a consumer:

require 'mq'
EM.run {
  broker = MQ.new
  broker.queue("timestamps").subscribe { |timestamp|
    time = Time.at(timestamp.to_f)
    puts "Got #{timestamp} which is #{time}"
  }
}

Profit. RabbitMQ is extremely easy to get started with. I don't imagine that it would take too much effort to write an adaptor for SMQueue to easily change deployed projects to use it without changing their implementation. If you do this I'd love to hear about it.

A Starling Adapter for SMQueue

Starling is a persistent, lightweight work queue implemented in Ruby which talks the memcache protocol. I've recently started playing with it because I don't have the resource to look after, or the requirement for, a full blown service bus. Starling is easier to install and configure than ActiveMQ, but it's nowhere near as fully featured. Both have their place but a discussion of when and where to use them is outside the scope of this article.

I knew that I wanted to use a message bus to turn synchronous requests into asynchronous requests, pushing work off to some background process somewhere. What I didn't know was the form that the message bus would take. If you're familiar with the Gang of Four patterns book you've probably picked out the pattern that I should use here, but to be honest I'm buggered if I know what it's called. SMQueue which I'm familiar with provides a nice abstraction that makes it easy to swap out the message bus implementation while the code remains identical. Lovely, but SMQueue doesn't come with an adaptor for Starling.

"How hard," thought I, "would it be to implement a Starling adapter for SMQueue?"

I blinked and suddenly it existed. Awesome.

require 'rubygems'
require 'smqueue'
require 'starling'
require 'yaml'

module BarkingIguana
  module Messaging
    module SMQueue
      class StarlingAdapter < ::SMQueue::Adapter
        class Configuration < ::SMQueue::AdapterConfiguration
          DEFAULT_SERVER = '127.0.0.1:22122'

          has :queue
          has :server, :default => DEFAULT_SERVER
        end

        def initialize(*args)
          super
          options = args.first
          @configuration = options[:configuration]
          @configuration[:server] ||= Configuration::DEFAULT_SERVER

          @client = ::Starling.new(@configuration[:server])
        end

        def put(*args, &block)
          @client.set @configuration[:queue], args[0].to_yaml
        end

        def get(*args, &block)
          if block_given?
            loop do
              yield next_message
            end
          else
            next_message
          end
        end

        private
        def next_message
          ::SMQueue::Message(:headers => {},
            :body => YAML.load(@client.get(@configuration[:queue])))
        end
      end
    end
  end
end

Want to use it? You'll need Starling running somewhere. After that you can implement a producer in two lines of code:

producer = SMQueue(:adapter => BarkingIguana::Messaging::SMQueue::StarlingAdapter, :queue => "some.queue.name")
producer.put "Quack quack"

On the other side of the connection, here's a sample consumer:

consumer = SMQueue(:adapter => BarkingIguana::Messaging::SMQueue::StarlingAdapter, :queue => "some.queue.name")
consumer.get do |message|
  puts message.body.inspect
  # => "Quack quack"
end

One thing that's different about this adapter compared to the current SMQueue adapters is that it assumes you want to use YAML as a transport format. I'd prefer to use XML or JSON but it is at the moment just a preference, YAML is the easiest to implement, and I'm lazy.

There's also a bunch of work to do around failover - this adapter only supports one server. I still don't know enough about how Starling would handle failover so I don't want to rush into implementing that and discover I've done it wrong.

If you can help by providing patches for either other transport formats or failover please do.

Expanding shortened URLs in a Ruby String

Everyone and their dog uses some sort of URL shortening service these days. While it's handy to cram an URL into short messages like those used on Twitter it's not always considered best practice for a bunch of reasons.

Since quite a few applications these days use Twitter feeds and similar services to gather new content, it'd be great to expand those URLs and undo any damage that these services cause.

Borrowing heavily from a Ruby based Twitter client I've extracted a module which can be used to do just that. Without further ado... here it is.

require 'net/http'

module BarkingIguana
  module ExpandUrl
    def expand_urls!
      ExpandUrl.services.each do |service|
        gsub!(service[:pattern]) { |match|
          ExpandUrl.expand($2, service[:host]) || $1
        }
      end
    end

    def expand_urls
      s = dup
      s.expand_urls!
      s
    end

    def ExpandUrl.services
      [
        { :host => "tinyurl.com", :pattern => %r'(http://tinyurl\.com(/[\w/]+))' },
        { :host => "is.gd", :pattern => %r'(http://is\.gd(/[\w/]+))' },
        { :host => "bit.ly", :pattern => %r'(http://bit\.ly(/[\w/]+))' },
        { :host => "ff.im", :pattern => %r'(http://ff\.im(/[\w/]+))'},
      ]
    end

    def ExpandUrl.expand(path, host)
      result = ::Net::HTTP.new(host).head(path)
      case result
      when ::Net::HTTPRedirection
        result['Location']
      end
    end
  end
end

To use it, first include the module into String.

class String
  include BarkingIguana::ExpandUrl
end

Then simply call expand_urls or expand_urls! on the text that contains shortened URLs. The bang method changes the string in-place where the regular method returns a copy of the string and leaves the original unchanged.

s = "http://tinyurl.com/asdf"
s.expand_urls!
puts s.inspect
# => "http://support.microsoft.com/default.aspx?scid=kb;EN-US;158122"

At the moment it supports ff.im, is.gd, bit.ly and tinyurl. If you can suggest any other services I'd love to hear about them. This code - like the original implementation - is released under the MIT licence. The full code including licence and RDoc can be found at http://pastie.org/471016. Enjoy!

Memcache statistics from the command line

To help debug memcache issues it's really useful to be able to see the output of the stats command. I got bored of using telnet to talk to the memcache daemon so I whipped up this simple Ruby script to spit out the statistics without all that fuss.

#! /usr/bin/env ruby

require 'socket'

socket = TCPSocket.open('localhost', '11211')
socket.send("stats\r\n", 0)

statistics = []
loop do
  data = socket.recv(4096)
  if !data || data.length == 0
    break
  end
  statistics << data
  if statistics.join.split(/\n/)[-1] =~ /END/
    break
  end
end

puts statistics.join()

Finding and enumerating document attributes with ActiveCouch

Continuing from my investigation into counting tags with CouchDB and map-reduce, I've made ActiveCouch support the operations required to count all uses of attributes on a certain document type in your database. Through that it's also possible to get all unique values for an attribute in your database.

To use this new functionality just call enumarate_all_[attribute_name] or find_all_[attribute_name] on your models.

>> Article.enumerate_all_tags
=> {"security"=>2, "ldap"=>1, "xen"=>1, "stories"=>3, "rails"=>13, "xeriom"=>3, "mysql"=>3, ... }

>> Article.find_all_tags
=> ["agile", "ajax", "apache", "api", "caching", "coding", ... ]

>> Article.find_all_author_ids
=> ["craig@barkingiguana.com"]

If you'd like to see exactly what I did to add this have a look at commit 1cbbe71.

Conditions and ordering with ActiveCouch views

When I posted about my hacking on ActiveCouch I noted that it didn't yet support ordering. Well, since commit 87120176 it does. Ordering isn't as fine-grained as ActiveRecord (yet), but it's good enough for what I need to do: set conditions on the finder and have the results ordered by posted_at date and then id.

I say it's not as fine-grained as ActiveRecord because that can quite easily construct queries that are order by posted_at asc, id desc, created_at desc, author asc. ActiveCouch can only order view results by key though - either ascending or descending. I don't think that this is an insurmountable challenge, I just haven't needed that much control yet.

How does it work? I hear you ask. Or at least I hear voices ask. Well, not so much ask as whisper to me. Yes, chocolate. Let's eat the chocolate. All the chocolate. Mmm.

Uhh, anyway. When you want to find by a condition but you don't so much care about the order ActiveCouch creates a view which emits keys based on just those conditions. Maybe you want to find all articles with author "craig@barkingiguana.com" that have a "Live" status.

Article.find(:all, :conditions => { :author_id => "craig@barkingiguana.com", :status => "Live" })

The first time this query is run ActiveCouch will create a view called by_author_id_and_status in the articles design document. The view will emit a key based on the author_id and the status and then it will emit the full document.

{
  "_id": "_design/articles",
  "_rev": "1532981864",
  "language": "javascript",
  "views": {
    "by_author_id_and_status": {
      "map": "function(doc) { if(doc.type == 'article') { emit([doc.author_id, doc.status], doc); }  }"
    }
    // other views cut for brevity
  }
}

The query will then run against this view, asking for a key [ "craig@barkingiguana.com", "Live" ] which will exactly match the keys of several of the documents.

When a query is run with an order a slightly different approach is taken. Since these are articles they're probably time-sensitive so let's order them by the date they were posted_at.

Article.find(:all, :conditions => { :author_id => "craig@barkingiguana.com", :status => "Live" }, :order => :posted_at)

The first time this query is run it will create a view very similar to the previous query, except the key that the view emits will include the posted_at attribute and the view will be named by_author_id_and_status_and_posted_at.

{
  "_id": "_design/articles",
  "_rev": "3752119467",
  "language": "javascript",
  "views": {
    "by_author_id_and_status_and_posted_at": {
      "map": "function(doc) { if(doc.type == 'article') { emit([doc.author_id, doc.status, doc.posted_at], doc); }  }"
    }
    // other view omitted for brevity
  }
}

When the query is run it'll take advantage of CouchDB's view collation specification by asking for result keys that, based on the finder conditions, are between the lowest possible key and the highest possible key. For the example above this means that it'd ask for keys between [ "craig@barkingiguana.com", "Live" ] and [ "craig@barkingiguana.com", "Live", "\u9999" ] ("\u9999" is an extremely high value unicode character as recommended in the view collation specification).

Since CouchDB view results are ordered by the keys, the key contains the attributes we want to order by, and we're calculating a range of keys that will contain any key that starts with the conditions we're looking for we now have simple sorting and conditions for finders.

Of course, since I've just done this work you don't have to worry about the internals. You can grab the code now using git: git clone http://barkingiguana.com/~craig/code/activecouch.git. There's a getting started section in my previous post on ActiveCouch. Enjoy, and please let me know if you're using this code!

script/console for your application

Rails coders will be more than familiar with the script/console command. It fires up a session in which you can interact with your application through the models that you've built. It's invaluable for debugging problems and can be really useful when building or administering the application. Not all of our Ruby applications are Rails applications though. Wouldn't it be nice to have a script/console anyway?

Turns out it's really easy to build one. Here's how.

First, decide on the libraries and files you'd like to have loaded. This almost always includes RubyGems and an init file from your application. I usually store the init file in config/boot.rb.

An example boot.rb could look something like this:

require 'rubygems'
require 'hpricot'
require 'net/http'
require File.dirname(__FILE__) + '/../vendor/gems/activecouch/init'

$: << File.dirname(__FILE__) + '/../app/models'

ActiveCouch::Base.class_eval do
  set_database_name 'blog'
  site 'http://localhost:5984/'
end

require 'article'
require 'comment'
require 'author'

Once we have these files we can create a Ruby script that'll run IRb, require the files, and set the prompt to something nice and simple. I also like to print out a welcome banner.

#! /usr/bin/env ruby

libs = []
libs << "irb/completion"
libs << File.dirname(__FILE__) + '/../config/boot.rb'

command_line = []
command_line << "irb"
command_line << libs.inject("") { |acc, lib| acc + %( -r "#{lib}") }
command_line << "--simple-prompt"
command = command_line.join(" ")

puts "Welcome to the <APPLICATION NAME> console interface."
exec command

I'd usually whack that code in script/console, make it chmod +x and commit it to the source code repository. Bam, instant application console.

Testing CSS @imports

I previously wrote a script to check files that are @imported exist in CSS stylesheets. I've turned that into a set of examples for our RSpec-based test suite. Fire the code into something like spec/views/stylesheets/import_spec.rb.

require File.dirname(__FILE__) + '/../../spec_helper'

describe "Stylesheet" do
  stylesheet_root = File.expand_path(RAILS_ROOT + '/public')
  stylesheets = Dir[File.join(stylesheet_root, "**", "*.css")]

  stylesheets.each do |stylesheet|
    describe stylesheet do
      it "should not @import files that don't exist" do

        missing_imports = []
        imports = File.read(stylesheet).split(/\n|\r/).grep(/\@import url\((.*)\)/)
        imports.each do |import|
          desired_path = import.scan(/url\((["'\ ])?(.*)\1\)/).to_a.first.to_a.last
          desired_root = desired_path[0,1] == "/" ? stylesheet_root : File.dirname(stylesheet)
          filesystem_path = File.expand_path(File.join(desired_root, desired_path))
          if !File.exists?(filesystem_path)
            missing_imports << { :path => filesystem_path, :directive => import }
          end
        end

        if missing_imports.any?
          exception = []
          missing_imports.each do |import|
            exception << "Missing @import file (#{import[:path]}) required for #{import[:directive]}"
          end
          raise exception.join("\n")
        end
      end
    end
  end
end

Having a good old Natter

I've been thinking about an XMPP chat-bot based interface - something similar to the XMPP bot I created in May '08 - for a project that I've started playing with recently. This project is still very new so it doesn't have much code yet. A perfect time for experimenting. My recent foray into the world of ActiveCouch reminded me of a library called Doodle that I've been meaning to get to grips with. Can you see where this is going?

Doodle is a Ruby library and gem for simplifying the definition of Ruby classes by making attributes and their properties more declarative

Doodle has a number of advantages over the ActiveCouch approach, but this isn't a post about Doodle so I'll save that for another time.

I used Doodle to create something DSL-like that can describe, in Ruby, a chat-bot that talks XMPP. It doesn't do anything fancy yet - it doesn't deal with subscription requests for example - but it can login, send and receive messages, and it has the beginnings of a basic roster so it can track who it's seen and talked to and when.

Natter.bot do
  channel do
    username "username@domain.com"
    password "sekrit"
  end
  on :message_received do |message|
    puts Time.now.to_s + "> " + message.body
    reply_to message, "Thanks for your message!"
  end
end

If you'd like to play with it, the code can be retrieved using Git.

git clone http://barkingiguana.com/~craig/code/natter.git

You'll need to install xmpp4r-simple and doodle to get it to run.

sudo gem install xmpp4r-simple doodle

There's very little documentation at the moment, but there are a few simple examples in the examples/ subdirectory and there's a quick example in the README file too.

Breaking ActiveCouch in fun and inventive ways

It's been just over 5 months since I started playing with CouchDB. Until a few days ago I didn't have much time to explore it, but since Christmas I've been playing around with it almost constantly, seeing what it can do and experimenting with it in my favourite language, Ruby.

Since I hadn't used Ruby to work with CouchDB before I decided to pick up ActiveCouch. It's an excellent library, but after playing for a few days I found that it worked with CouchDB in ways that I don't think I would. That could be down to my inexperience with using CouchDB, or it could just be that how you use CouchDB depends on how you think. Either way, I pushed a copy of ActiveCouch onto my server and began hacking at it to make it play the way that I wanted it to.

One application, one database

As it stood, ActiveCouch used one database per class. People went into the people database, comments went into the comments database, articles went into the articles database. My approach to using CouchDB is to store all application data in one database and differentiate between the document types by having a doc.type attribute.

ActiveCouch now also installs a set of views that can be used to access just the documents of one type. You should see these in the Futon client after you've run your application once.

Unknown functionality dropped

I broke ActiveCouch::Base#find_from_url while I was working. I didn't know what it was for, and I didn't use it in the application I was building, so I dropped it in 9982b348c. If you use this functionality please let me know what it's for!

Syntactic sugar

One of the goals of ActiveCouch is to act like ActiveRecord, and ActiveRecord provides #all and #first. I like them. ActiveCouch now provides them too.

New attribute types

Sometimes there's data that's too simple to merit a class and an association. I've added a new attribute type, :array. Simple tags, for example, are a perfect fit for this. The default value of these attributes is an empty array.

class Article < ActiveCouch::Base
  has :title, :which_is => :text
  has :tags, :which_is => :array
end

article = Article.new :title => "Sandwiches", :tags => [ "pickle" ]
article.tags << "cheese"
article.tags # => [ "pickle", "cheese" ]

I've also added a :datetime attribute type that defaults to Time.now.

Calculated default values

It's now possible to set a default value which is a calculation to be lazily executed ie it'll be worked out when the instance comes into existence rather than when the class is declared. You can do this be setting the default value to a proc (or anything else that responds_to?(:call)).

class Egg < ActiveCouch::Base
  has :hatches_at, :type => :datetime, :with_default_value => proc { 3.weeks.from_now }
end

The instance is yielded into the proc in case you'd like to do any calculations based on that.

Conversion to native Ruby types

When you declare a type for a document attribute, ActiveCouch now tries to convert the value from the document into a native Ruby type. For example, if you declared that your class had an attribute called created_at that was a datetime you'd now get an instance of Time when you access that attribute. Previously you'd have got a String.

class Person < ActiveCouch::Base
  has :birthday, :which_is => :datetime
end

Person.find(:first).birthday.class # => Time

Changes to associations and adding belongs_to

I've changed the has_many and has_one associations so that they don't appear in the document that declares them. These associations are declaring that other classes have keys which point at the current class and they a query is needed to fetch them.

To accomodate that change there's a new association, belongs_to, which says that the declaring class contains a key which points to an owning class.

class Pet < ActiveCouch::Base
  # This document will have a person_id attribute
  belongs_to :person
end

class Person < ActiveCouch::Base
  # Instances of this class will query the database for doc.type = "pet"
  # and doc.person_id = self.id
  has_many :pets
end

At the moment you have to set the association on the class that belongs_to. Setting it on the class that has_many won't work.

# BAD
craig.pets << cat

# GOOD
cat.person = craig

Views with multiple keys

It's now possible to have a view with more than one attribute as a key. Just use ActiveCouch::View#with_key more than once and each key you specify will be added to the view.

Design document with multiple views

The version of ActiveCouch that I checked out allowed only one view per design document. I think that was a bug since there was some code in there to merge in the existing view. I've fixed that and now design documents can have more than one view.

Finders have conditions, not params

It felt a little unnatural typing :params => { ... } when writing custom finders. ActiveRecord asks for :conditions, so now ActiveCouch does too.

Person.find(:all, :condtions => { :last_name => "Smith" })

Automatic view generation for custom finders

I don't really want to worry about writing and installing the views for a finder with conditions before I run it. Now, the first time you run a finder with conditions, ActiveCouch will generate and install the appropriate view for you.

Probably lots more too!

I've still got to clean up quite a few of my changes, make sure that the test coverage is meaningful enough, and write lots of documentation. I am using my altered version of ActiveCouch for an application so that should improve over time.

Want?

You can clone my changes using Git. The repository is available at http://barkingiguana.com/~craig/code/activecouch.git.

git clone http://barkingiguana.com/~craig/code/activecouch.git

Getting started

If you don't already have CouchDB setup, do that now. If you use Ubuntu, I wrote a brief article on setting it up there. If you use OS X then install MacPorts and run sudo port install couchdb.

The first thing you'll need to do is configure ActiveCouch to connect to your CouchDB instance. Set site to the URL that your CouchDB instance is listening at, and the database name to something that makes sence for your application.

ActiveCouch::Base.class_eval do
  set_database_name 'blog'
  site 'http://localhost:5984/'
end

Now you'll need some classes to work with.

class Author < ActiveCouch::Base
  has :name, :which_is => :text
  has :email_address, :which_is => :text
  has_many :articles
end

class Article < ActiveCouch::Base
  has :title, :which_is => :text
  has :status, :which_is => :text, :with_default_value => "draft"
  has :body, :which_is => :text
  belongs_to :author
end

has declares an attribute, has_many, has_one and belongs_to work very similar to the way that you're familiar with from ActiveRecord (except they don't have the extreme customisability - the name of the association must reflect the name of the class on the other side of the association).

That's pretty much it. Use your classes however make sense for your application.

author = Author.create :name => "Craig R Webster",
  :email_address => "craig@barkingiguana.com"

a = Article.new
a.title = "Getting started with ActiveCouch"
a.body =<<-EOF
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
  tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
  quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
  consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
  cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
  proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
EOF
a.author = author
a.save

Article.find(:all)
Author.first
Article.find(:first, :conditions => { :status => "draft" })

Known Issues

Not so much a bug as a feature which hasn't yet been implemented. ActiveCouch::Base#find doesn't support ordering. It should be possible to add this but I haven't started working on it yet. If you need ordering then a patch would be lovely ;)

Having problems? Got feedback?

There are likely to be a bunch of bugs in there still. Bug reports, patches and feedback, as always, would be awesome. Either leave a comment or get in touch directly.

Using SMQueue with message queues that failover

Previously I wrote about using SMQueue to create some simple consumers and producers for message queues. I also wrote about setting up a high availability message store. In the case of a failure the message queue will turn the slave node into the master. Unfortunately the producer and consumer I created will forever try to reconnect to the now-dead ex-master node.

Using smqueue 0.1.0 (which was produced when I created the simple producer and consumer) it's trivial to add failover capabilities to the clients. Where the SMQueue instance is created simply add another key, secondary_host, to the configuration and point it to the second broker.

queue = SMQueue(
  :name => "/queue/numbers.ascending",
  :host => "mq1.domain.com",
  :secondary_host => "mq2.domain.com",
  :adapter => :StompAdapter
)

I think that the plan is to support more than two broker nodes and support for failover strategies into future versions of SMQueue.

Writing Ruby/Stomp clients with SMQueue

SMQueue makes writing Ruby clients that interact with message queues pretty much trivial. It's got adaptors for Spread, Stomp and Stdio. Which is pretty handy 'cause that message queue I setup a few weeks back talks Stomp and I'm quite into Ruby.

Installing SMQueue

The origin SMQueue repository doesn't yet have a way of producing a gem so there are two ways to install SMQueue: add a vendor/gems/smqueue directory to your project or build a gem from my SMQueue repository. Oddly enough, I've gone with the later approach.

Clone my repository and you'll see there's a gemspec file. You can use that to build a gem using the gem command. The whole process looks something like this:

git clone http://barkingiguana.com/~craig/smqueue.git
cd smqueue
gem build smqueue.gemspec
sudo gem install ./smqueue-0.1.0.gem

I'm reliably informed that when SMQueue does build into a gem it'll start at 0.2.0 so having a 0.1.0 installed wont cause any clashes.

Note that I've removed the Spread adaptor from my branch because I don't have a working spread client on my system and I can't get SMQueue to load without one. I'm sure that'll be fixed in a future release.

Basic assumptions

I've made the following assumptions for this article: that you have a working Ruby 1.8.6 install, and that you have an ActiveMQ instance running locally with the Stomp connector enabled. You'll have to change the code to match your environment if these assumptions aren't correct.

A simple producer

Now that SMQueue is installed I'll take a contrived example and implement it. Let's say I want an ascending number to be put onto a queue roughly every second. A pretty good source for these numbers might be the current time represented as seconds from the epoch. Handily I can get just such a number really easily in Ruby.

>> Time.now.to_i
=> 1230602445
>> Time.now.to_i
=> 1230602446
>> Time.now.to_i
=> 1230602447

I can get a number to be output every second by wrapping it in a loop and sleeping a second at the end of the loop.

>> loop do
?>   puts Time.now.to_i
>>   sleep 1
>> end
1230602557
1230602558
1230602559

Easy enough to get them on STDOUT, but how do I get them into a queue? Well, for that I need to start using the SMQueue library, create a client for the queue, and put a representation of the number onto the queue.

require 'rubygems'
require 'smqueue'

queue = SMQueue(
  :name => "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

loop do
  number = Time.now.to_i
  puts "Sending #{number}"
  queue.puts number.to_yaml
  sleep 1
end

Paste the below into a terminal somewhere to kick off the producer. You should see a steady stream of output - about one message a second - saying that it's sending a number.

cat > producer.rb <<EOF
require 'rubygems'
require 'smqueue'

queue = SMQueue(
  :name => "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

loop do
  number = Time.now.to_i
  puts "Sending #{number}"
  queue.puts number.to_yaml
  sleep 1
end
EOF
ruby producer.rb

A simple consumer

Now that I have a simple producer running, l'll take the messages and convert them back into a time. It's a pretty pointless task for the consumer, but it'll show just how easy it is to write one.

require 'rubygems'
require 'smqueue'
require 'yaml'

queue = SMQueue(
  :name => "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

queue.get do |message|
  number = YAML.parse(message.body).transform
  time = Time.at(number)
  puts "Got #{number} which is #{time}"
end

Let's go through the important parts in more detail.

I tell the queue that I want to capture messages.

queue.get do |message|

The producer put the messages in as YAML so I need to transform them back to their native state. I can do this by parsing the YAML then transforming it.

number = YAML.parse(message.body).transform

Now that I have the number, I convert it to a time and output both the original number and the calculated time.

time = Time.at(number)
puts "Got #{number} which is #{time}"

That's pretty much it... run the below code to start running the consumer.

cat > consumer.rb <<EOF
require 'rubygems'
require 'smqueue'
require 'yaml'

queue = SMQueue(
  :name => "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

queue.get do |message|
  number = YAML.parse(message.body).transform
  time = Time.at(number)
  puts "Got #{number} which is #{time}"
end
EOF
ruby consumer.rb

For each message that your producer creates you should now see your consumer print a message to the screen.

Symbol#to_proc is slow... is it slow enough to matter?

It's common knowledge that using the to_proc hack is slower than not. Just how much slower is it? I decided to put together a few benchmarks to find out.

Environment

These tests were run on Ruby 1.8.6-pl111 and Rails 2.1.

Benchmarking

Say there's a database of 1,000 items that for some reason you want to iterate over. Let's forget that if you're showing 1,000 items you probably have usability issues and just roll with it.

1_000.times { |n| Bar.create :name => "bar-#{n}" }
bars = Bar.find(:all)

Here's how much slower it is over a dataset of 1,000 ActiveRecord instances.

Benchmark.measure { bars.map(&:name) }.real
#=> 0.00645709037780762

Benchmark.measure { bars.map { |b| b.name } }.real
#=> 0.00141692161560059

Now that's a horrific increase: it takes more than 350% longer to run the to_proc hack than the plain block... but let's be realistic here, over 1,000 records it's taken 0.0065 seconds. Big woop. Who cares?

How about over 1,000,000 rows? We already have 1,000 rows, let's top that up.

(1_000_000 - 1_000).times { Bar.create :name => Time.now.to_f.to_s }
bars = Bar.find(:all)

That makes it 1,000,000 rows in the table. By this stage your database is probably thinking you hate it. I'm pretty confident that presenting 1,000,000 rows to the person using your application is a bit of an edge case, but hey, here's how long it takes.

Benchmark.measure { bars.map(&:name) }.real
#=> 6.25304508209229

Benchmark.measure { bars.map { |b| b.name } }.real
#=> 1.38965106010437

Almost 5 seconds extra over a million rows. Okay so 5 seconds is a pretty big hit, but how long will your application be running before you hit a million rows in one of your tables and you need to iterate over all million rows?

Don't optimise your code prematurely. By the time to_proc becomes an issue you'll already have hit many, many other problems.

Benchmark.measure { Bar.find(:all) }.real
#=> 406.738657951355

Worry about those first.

Getting started with Story Driven Development for Rails with Cucumber

I've been hearing about Story Driven Development (SDD) for a while but I haven't tried it out because I was under the impression that there was a huge amount to learn and setup before I could get going. I'm not sure if that used to be true, but I started using Cucumber yesterday and it was really easy.

Install and configure

You'll need to install a bunch of RubyGems.

sudo gem install nokogiri term-ansicolor treetop diff-lcs hpricot cucumber

Install Cucumber into your Rails app.

ruby script/generate cucumber

Install WebRAT. Unfortuantely this doesn't seem to be available as a RubyGem. If you're using Git then install this as a submodule instead. We're not so I clone the repository then svn add it.

git clone git://github.com/brynary/webrat.git vendor/plugin/webrat

Writing your first story

Stories have three components: a business value, the role of a person that uses the feature and some description of the feature.

In order to [do something with business value]
As [role]
Should [describe the feature]

An example might be the ability to order a pizza from the online ordering system of a pizza delivery company.

Feature: Order Pizza
  In order to get some hot, tasty pizza
  A hungry pizza lover
  Should be able to order pizza

Next we need to define some scenarios. Scenarios are things that can happen during the story. Most pizza places aren't open 24 hours a day so two simple scenarios are (1) the pizza shop is closed, and (2) the pizza shop is open.

  Scenario: The pizza shop is closed
    Given the pizza shop is closed
    And I am on the home page
    And I click "Feed Me!"
    Then I should see "Sorry, the shop is closed"

  Scenario: The pizza shop is open
    Given the pizza shop is open
    And I am on the home page
    And I click "Feed Me!"
    Then I should see "Your pizza will be with you soon"

The above description should go in a file called something like features/order_pizza.feature where it is lovely and version controlled and safe.

So, we now have a story that describes how a feature should behave. How does that get turned into acceptance tests? Well, you could pass these descriptions off to your testing team, or you could turn them into part of your test suite.

Automated tests: better than cake

You might notice that when installing Cucumber you got the directory features/steps. That's where you tell your test suite how to understand your stories. There are already two files here: common_webrat.rb which gives your test suite a few funky things like the ability to click links and env.rb which does pretty much the same stuff as spec/spec_helper.rb except for Cucumber. You can ignore env.rb, but common_webrat.rb will provide a few examples of how to start writing story steps.

Create a new file, order_pizza_steps.rb. This is where you define the steps involved in ordering pizza. It's pretty much just regular expressions which match each line of a scenario to some Ruby code.


Given /the pizza shop is open/ do
  PizzaShop.open = true
end

Given /the pizza shop is closed/ do
  PizzaShop.open = false
end

And /I am on the home page/ do
  visits "/"
end

That's all we need to do. The common WebRAT steps provide the necessary mapping for clicking buttons and checking for feedback.

Running your stories

This is pretty simple: run rake features. You should get some rather pretty coloured output, and if anything has gone wrong Cucumber is pretty good at suggesting ways to fix it.

Found this article useful?

If you enjoyed this article I'd appreciate recommendations at Working with Rails.

Talking to yourself is bad mmkay?

A lot of languages encourage talking to yourself. Lots of OO PHP code is sprinkled with code that looks like $this->foo_method();. In some languages it's necessary. Ruby isn't one of them.

class Foo
  def bar
    # Why are you talking to yourself?!
    @thingy = self.foo
  end

  def foo
    "QUUX!"
  end
end

The code above could be written without self at all.

class Foo
  def bar
    @thingy = foo
  end

  def foo
    "QUUX!"
  end
end

While this is a (very) trivial example, it makes a huge difference on larger code-bases. Give it a try: if you don't talk to yourself your code will look less crazy.

Only one caveat: when you're doing assignment you'll need to talk to yourself unless you're doing a local assignment.

class Foo
  attr_accessor :thingy

  def bar
    # This will assign to a local variable.
    thingy = foo
  end

  def foo
    "QUUX!"
  end
end
class Foo
  attr_accessor :thingy

  def bar
    # This will call Foo#thingy=
    self.thingy = foo
  end

  def foo
    "QUUX!"
  end
end

Verify database connections in long-running idle Rails processes

I've interfaced one of my xmpp4r bots with the Xeriom Networks control panel. I had intended to write a post about it to show how easy it is, but I've been beaten to it. I can, however, offer one piece of advice that will stop the bot dying after a few hours of idling: periodically verify your database connections.

RAILS_DEFAULT_LOGGER.debug "Launching database connection verifier"
Thread.new do
  loop do
    sleep 1800 # Half an hour
    RAILS_DEFAULT_LOGGER.debug "Verifying database connections"
    ActiveRecord::Base.verify_active_connections!
  end
end

Adding the above code to a script will stop database connections getting dropped (or, at least, will reconnect them if it happens).

To see it in action, add support@xeriom.net to your XMPP roster and have a chat. It's not hugely intelligent, but it does support a few useful commands.

Update: the support bot is no longer running. It was fun, but not hugely useful in this context.

Love me!

If you've found this article useful I'd appreciate beer and recommendations at Working With Rails.

Offline tasks the easy way

There's been quite a lot of chat recently about various job scheduling systems and process managers for offlining expensive tasks on the LRUG list. BackgrounDRb, Beanstalk, Starling, BackgroundJob and other similar solutions have been discussed. These systems can be useful, but most of the time they're just adding unnecessary complexity.

One instance where I feel these solutions are unnecessary is where you need to strip data from an external service in a way that's totally disconnected from the HTTP request-response cycle.

Say you want to pull the most recent article from this blog every 15 minutes and create a file that could then be served statically to your visitors. A naive implementation of that functionality would look something like this:

require 'net/http'
require 'hpricot'

barking_iguana = URI.parse('http://barkingiguana.com/')
loop do
  articles = Hpricot(Net::HTTP.get(barking_iguana))
  title = (articles / "div.article a[@rel=bookmark] text()").first
  link = (articles / "div.article a[@rel=bookmark]").first['href']

  # Of course, this should have a real file path in it.
  File.open("/.../.../.../barking_iguana.ssi", "w+") do |f|
    f.write("#{title}: #{link}")
    f.flush
  end

  sleep 900 # 15 minutes
end

Doesn't that look nice? No screwing around with complex tools to handle the scheduling - just run it and it'll go forever.

"Ah," I hear you say, "but what if it crashes?" Well, in the unlikely event that such a simple script does crash I'd have something like God monitoring the processes so it would be restarted. You've got something monitoring your processes anyway (right?) so it should be pretty simple to add another process to that list.

Love me!

If you've found this article useful I'd appreciate recommendations at Working With Rails.

XMPP4R-Simple makes XMPP in Ruby uhh... simple...

I thought it might be cool to have a control interface that you could talk to using IM, something like the IM client for Twitter.

Initially I was looking at XMPP4R but a little reading pointed out that there's a gem called XMPP4R-Simple. Well, simple is always good so one gem install and 45 minutes later I had a Ruby script that could log in to an XMPP server, listen to (and log) what people said, and respond with a simple message.

#!/usr/bin/env ruby

require 'rubygems'
require 'xmpp4r-simple'

logfile = File.join('..', 'log', "#{File.basename(__FILE__)}.log")
logger = Hodel3000CompliantLogger.new(logfile)

jabber = Jabber::Simple.new "username@domain.com", "password"
sleep 1
jabber.status(:away, "No one here but us mice.")
sleep 1

jabber.deliver("craig@xeriom.net", "I woke up at #{Time.now}.")

loop do  
  begin
    jabber.received_messages do |msg|
      jid = msg.from.strip.to_s
      logger.info "%s said: %s" % [ jid, msg.body ]
      jabber.add(jid) if !jabber.subscribed_to?(jid)
      jabber.deliver(jid, "Nom nom nom.")
    end

    jabber.presence_updates do |update|
      jid, status, message = *update
      logger.info "#{jid} is #{status} (#{message})"
    end

    jabber.new_subscriptions do |friend, presence|
      logger.info "#{friend.jid} #{presence.type}"
      jabber.add(friend.jid) if !jabber.subscribed_to?(friend.jid)
    end
  rescue Exception => e
    logger.error e.to_s
  end
  sleep 1
end

Our own little pet XMPP client. How cute is that?

Quit yo jibba jabba, sucka!

If you've found this article useful I'd appreciate recommendations at Working With Rails.

State of Ruby / Xen API's

Recently I've been doing a lot of work on creating a management interface for Xen VMs in one of my Rails applications. The current state of Xen API's is not well documented which makes it rather hard to implement. In fact, just about the only comparison that I can find of the API's appears to be on the Xen mailing list in an email from Ewan Mellor.

  • xend-http-server: Very old and totally broken HTML interface and legacy, generally working SXP-based interface, on port 8000.
  • xend-unix-server: Ditto, using a unix domain socket.
  • xend-unix-xmlrpc-server: Legacy XML-RPC server, over HTTP/unix, the recommended way to access Xend in 3.0.4.
  • xend-tcp-xmlrpc-server: Ditto, over TCP, on port 8006.
  • xen-api-server: All new, all shiny Xen-API interface, available in preview form now, and landing for 3.0.5.
-- Ewan Mellor, 2007-01-24 in http://lists.xensource.com/archives/html/xen-api/2007-01/msg00006.html

As far as I can tell, if you use Xen <= 3.0.4 the best option is to use the Ruby-Xen gem to work with the legacy Xen XML-RPC API. If you're using Xen >= 3.0.5 the preferred method of integration appears to be the almost-undocumented Xen API*, which as far as I can tell has no existing Ruby client.

A paper which talks about the Xen API is available at http://research.iu.hio.no/theses/pdf/master2007/ingard.pdf which seems to suggest that the new Xen API also uses XML-RPC, with a sample script available at http://folk.uio.no/ingardm/thesis/xensource.xeninfo.pl.

In the following weeks I hope to setup some modern Xen dom0's and begin to document exactly what is required to get the Xen API setup and accessible from a host within the same network as the dom0.

* Update, 2008-03-15

I found a draft definition of the new XML-RPC API on the XenSource Wiki. A huge cake is the prize for anyone that writes a BSD or MIT licenced Ruby interface against the specification given there.

The end of the world is nigh

According to Ruby, the world ends (or, at least, is reconfigured) on Tuesday the 19th of January 2038 at 7 seconds past 3.14am.

>> Time.utc(2038, 1, 19, 3, 14, 7, 999999)
=> Tue Jan 19 03:14:07 UTC 2038
>> Time.utc(2038, 1, 19, 3, 14, 7, 999999).succ
=> Fri Dec 13 20:45:52 UTC 1901

This is an artefact of representing time as an integer counting from the epoch, but given that a Fixnum will automatically roll over into a Bignum when it reaches 2**30, it's somewhat surprising that a similar transformation isn't performed internally in Time to allow us to represent dates and times beyond 2038-01-19.

Class, Instance and Singleton methods

There are three types of methods in Ruby and I always get them confused. Here, for my future reference, is what I currently think they mean.

Class methods

Methods which can be called directly on a class.

Time.now
Monkey.find(:all)

Instance methods

Methods which can be called on any instance of that class.

@widget.to_s
Time.now.to_f

Singleton methods

Methods which can be called only on a specific instance of that class.

chicken = Chicken.new
class << chicken
  def hide
    # ...
  end
end
chicken.hide

Showing multiple message types with the flash

Traditionally the Rails flash is used to store only one message, usually in flash[:message] but I like to be able to differentiate between warnings and information in my view rather than displaying them both in the same place in the same style.

The flash is a HashWithIndifferentAccess so we can use any key we want to store messages and pull them back out later, in the view.

In your controller you simply use whichever key is most appropriate.

if @widget.save
  flash[:info] = "Your widget has been saved."
  flash[:notice] = "There are now #{Widget.count} widgets."
  redirect_to @widget and return
else
  flash.now[:warning] = "I couldn't save your widget."
  render :action => "edit"
end

In the view you can now iterate over the flash and pick out the key and the message.

<%= flash.sort.collect do |level, message|
  content_tag(:p, message, :class => "flash #{level}", :id => "flash_#{level}")
end.join %>

Say the @widget got saved earlier, you'd end up with some easily understood and styled markup.

<p class="flash info" id="flash_info">Your widget has been saved.</p>
<p class="flash notice" id="flash_notice">There are now 29 widgets.</p>

Of course, in the interest of readability the flash loop in the view should be extracted to a helper, but that's left as an exercise for the reader.

About the boy

A picture of Craig in grayscale

Craig Webster is a software engineer living in London. He usually works with Ruby although sometimes he sneaks in some Erlang or JavaScript. He's into rock climbing, snowboarding, skating, photography and fencing. Yes, this does mean he has a sword.

Near here you'll find Craig's homepage, contact details, PGP key and keysigning policy, and talks.

Licence

The entire content of this blog is public domain. Use it however you fancy. You don't even need to attribute it to me, although it would be nice if you did. Just don't sue me and we'll all be happy.

I Work With Rails

Recommend Me

My Travels

I go places. Do you go places too? Let's meet up!.