Craic with the Dublin Ruby User Group

March 14, 2007 @ 03:25 PM Posted by octopod

I attended my first Dublin Ruby User Group meet last night and really enjoyed it. This months event was organized by Qamir Hussain, and I hope the format will be a template for future events. Eleven people attended, Ana Nelson and Olivier Ansaldi gave presentations on Radiant and REST respectively and then we relocated to a very nice bar in town for further discussions (and wine).

Ana's presentation was a great intro to Radiant and it's capabilities. She's currently using it in a very clever way to generate a static site for her company. She gave a tour of the various features, showed some small but useful examples of how to create your own tags and gave some tips on getting the best out of it.

Olivier's presentation covered REST both as a concept and as implemented by rails. It was like a cool remixed short version of DHHs keynote at RailsConf Chicago, involving lots of pictures of people sleeping on trains. It provoked a quite lively discussion on the concepts and ins and outs of REST that I think will continue over further meetings.

It was good to meet other rubyists in Ireland, and I'm looking forward to the next meeting. Though I think I may have agreed to do a presentation…

UPDATE: I created an IRC channel for Irish Rubyists, #ruby.ie on irc.freenode.net, come and hang out!

A pretty and useful zsh prompt

February 17, 2007 @ 08:06 PM Posted by octopod

Some discussion on zsh PS1 settings yesterday lead to me posting a picture of mine:

It’s a very slightly modified version of a zsh prompt created by Phil Gregory available here.

The caboose blog is back!

February 14, 2007 @ 06:59 PM Posted by octopod

The caboo.se blog is back in action after a lull. Check out Corey Donohue's article on getting rid of bloat in your tests .

Finding out which Rails revision caused a test to fail

September 07, 2006 @ 10:37 PM Posted by octopod

I’ve been upgrading an app from 1.1.6 to edge recently and ended up with failing tests. To get to the bottom of why a particular test was failing, and find the revision of Rails that caused it I wrote the following little script:

UPDATED: Yes, this should have been a binary search from the start :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

#!/usr/bin/env ruby
require 'optparse'

KNOWN_BAD_REVISIONS = [4634, 4635]

class Args < Hash
  def initialize(args) 
    super()
    self[:quiet] = false
     
    opts = OptionParser.new do |opts| 
      opts.banner = "Usage: #$0 [options]"
      opts.on('-s', '--start_revision NUMBER', Integer,
              'start from revision NUMBER') do |revision| 
        self[:start_revision] = revision 
      end 
      opts.on('-f', '--finish_revision [NUMBER]', Integer,
              'finish on revision [NUMBER]', 
              '(defaults to latest revision of',
              ' http://dev.rubyonrails.org/svn/rails/trunk)') do |revision| 
        self[:end_revision] = revision 
      end 
      opts.on('-t', '--test_file STRING', 
              'path to test file to run') do |test_file| 
        self[:test_file] = test_file 
      end 
      opts.on('-n', '--name STRING', 
              'name of test to run') do |test_name| 
        self[:test_name] = test_name 
      end 
      opts.on('-b', '--know_bad_revisions LIST',
              'Comma separated list if known bad revisions to skip',
              'These are added to the list', 
              "current list: #{KNOWN_BAD_REVISIONS.inspect}") do |extra_known_bad_revisions|
         self[:extra_known_bad_revisions] = extra_known_bad_revisions.split(",").map { |r| r.to_i }
      end
      opts.on('-q', '--quiet', 
              "don't output each revision as it is tested") do  
        self[:quiet] = true
      end 
      opts.on_tail('-h', '--help', 'display this help and exit') do 
        show_help(opts)
      end 
    end 
    opts.parse!(args)
    self[:extra_known_bad_revisions] ||= []
    show_help(opts) if self[:start_revision].nil? || self[:test_file].nil? || self[:test_name].nil?
  end
  
  def show_help(opts) 
    puts opts 
    puts "\n***Assumes a checkout of rails trunk in vendor/rails***"
    exit 
  end
end

class BadRevisionFinder

  def initialize(options)
    @options = options
    @end_revision = @options[:end_revision] ||= latest_rails_revision
    @start_revision = @options[:start_revision]
    @known_bad_revisions = KNOWN_BAD_REVISIONS + options[:extra_known_bad_revisions]
    puts "Known bad revision list: #{@known_bad_revisions.inspect}"
  end
  
  def find_bad_revision
    sanity_check
    while (@end_revision - @start_revision) > 1
      check_revision = ((@start_revision + @end_revision) / 2.0).floor
      while @known_bad_revisions.include?(check_revision)
        check_revision += 1
      end
      if revision_bad?(check_revision)
        puts "bad revision"
        @end_revision = check_revision
      else
        puts "good revision"
        @start_revision = check_revision
      end
    end
    bad_revision = revision_bad?(@start_revision) ? @start_revision : @end_revision
    puts "r#{bad_revision} caused #{@options[:test_name]} to fail! http://dev.rubyonrails.org/changeset/#{bad_revision}"
  end
  
  def sanity_check
    # make sure it's passing at the start revision
    if revision_bad?(@start_revision)
      puts "Hey, this test fails with the start revision #{@start_revision}!"
      exit
    end
    # make sure it's failing on end revision
    unless revision_bad?(@end_revision)
      puts "Hey, this test passed with the end revision #{@end_revision}!"
      exit
    end
  end
  
  def revision_bad?(revision)
    puts "checking r#{revision}" unless @options[:quiet]
    `svn up -r #{revision} vendor/rails`
    `ruby #{@options[:test_file]} -n #{@options[:test_name]}`
    $? != 0
  end
  
  def latest_rails_revision
    puts "Finding latest edge revision number"
    `svn info http://dev.rubyonrails.org/svn/rails/trunk`.match(/Revision: (\d*)/m).captures[0].to_i
  end
end

BadRevisionFinder.new(Args.new(ARGV)).find_bad_revision
 

Save the above as find_culprit.rb in root of your application, then run with something like:

1
2
3

      find_culprit.rb -r 4191 -t test/unit/user.rb -n test_something_that_should_still_be_working 
 

Start at whatever revision you know your app is good at. It requires a checkout of rails trunk to vendor/rails.

NB: This can be quite a slow process, as it’s doing an svn up each time. I’ve finished using it now, but next time I’m going to see if I can use svk to setup my own mirror of the rails repo locally to speed things up

Managing multiple local Mephisto repos with svk

August 19, 2006 @ 04:07 PM Posted by octopod

I’m loving using Mephisto as my blog engine. Justin has done great work on the interface and Rick’s code is of his usual standard. There’s a lot more great stuff to come which I’m really looking forward to. In fact I’m loving Mephisto so much I’ve decided to use it as the blogging engine on a client project. This project doesn’t require very advanced blogging, but it does require that it’s integrated with the main site with seamless user registration and multiple sites. I’ll write more about that when I do it, but first I want to show how I setup my development environment so I can track changes to Mephisto trunk, make any local changes I require and deploy using Capistrano.

My requirements were:

  • Have multiple local copies of Mephisto I can make versioned changes to
  • Merge upstream changes from the Mephisto trunk into my local versions
  • Be able to deploy with Capistrano

I initially tried this with darcs, but came to the conclusion that svk was a better choice as it works better with Subversion. I’ll go through the process of how I set this up here, I’m assuming there’s no local SVK repos already setup. If there is you may have to modify the steps below to fit.

First up is setting up an svk mirror of the Mephisto trunk. I ran the following to setup the mirror to pull from:

1
2
3
4
5
6
7
8
    $ svk mkdir //pull_repositories
    Repository /Users/chris/.svk/local does not exist, create? (y/n)y
    $ svk mkdir //pull_repositories/mephisto
    $ svk mirror http://svn.techno-weenie.net/projects/mephisto/trunk  //pull_repositories/mephisto/trunk
    $ svk sync //pull_repositories/mephisto/trunk
    Syncing http://svn.techno-weenie.net/projects/mephisto/trunk    
    ... lots of output ...
  

Syncing the Mephisto repository will take a while the first time you do it. The next step is to create a local svk branch to use. I tested this process out with my personal blog so I’m going to create a local branch called personal.

1
2
3
4
    $ svk mkdir //local
    $ svk mkdir //local/mephisto
    $ svk cp //pull_repositories/mephisto/trunk //local/mephisto/personal
  

So now there’s a local copy you can checkout with svk co //local/mephisto/personal, make changes to and checkin with svk ci. See the installation instructions on the Mephisto wiki for how to proceed.

We won’t be able to deploy this yet, as Capistrano doesn’t support svk. What’s needed is the ability to push the local branch out to another subversion repository that Capistrano can use for deployments. You’ll need to make a new subversion repo wherever you usually keep them.

1
2
3
4
5
    $ svk mkdir //push_repositories
    $ svk mkdir //push_repositories/mephisto
    $ svk mirror (url to your svn repo) //push_repositories/mephisto/personal
    $ svk smerge --baseless //local/mephisto/personal //push_repositories/mephisto/personal
  

You can checkout the subversion repo with svn and you’ll see a copy of your local branch. You use this repository as your repo in your deploy.rb.

To help with the svk stuff and all for automatically pushing the latest local changes to the subversion repo when deploying, I created a lib/tasks/svk.rake that looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    app_name = File.basename(File.dirname(Dir.pwd))
    install_name = File.basename(Dir.pwd)

    local_repo = "//local/#{app_name}/#{install_name}"
    push_repo = "//push_repositories/#{app_name}/#{install_name}"

    namespace :svk do
      desc "Get latest from upstream"
      task :pull do
        system("svk pull")
      end

      desc "Push local svk branch to remote svn"
      task :push do
        system("svk smerge --incremental --log #{local_repo} #{push_repo}")
      end
    end    
  

And added the following task to my deploy.rb1

1
2
3
4
5
    desc "Push the latest svk code to the svn repo and set the svn revision number"
    task :before_update_code do
      system("rake svk:push")
    end
  

That should be everything setup. You can now hack away on Mephisto to your hearts content in as many different branches as you want. Have a read of the svk manual to see what else is possible. For example, if you’ve some patches to contribute back, you can create a new local branch, apply just those patches to it, make sure the tests pass, then svk diff to create a patch.

1 Due to the way I have my svn setup I needed to do some other stuff to stop it trying to find the latest revision. If your deploy gets stuck in a loop trying mention it in the comments and I’ll add that code to the article.

RailsConf 2006 - What a load of CRUD

June 28, 2006 @ 01:39 PM Posted by octopod

I’m back in Dublin after the fantastic RailsConf, jetlagged and tired but very glad I went. Many other people have blogged about the conference itself, Phil Hagelburg set up Planet RailsConf to track it. I’m just going to write about the part of the conference I found most interesting, DHH’s keynote on CRUD. This has been covered by several different people. While I love the concept having recently finished reading Domain Driven Design, the bit that worried me about the example David showed was the lack of error checking and the case/switch like block statement:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class PeopleController < ActionController::Base
  def create
    @person = Person.create(params[:person])

    respond_to do |format|
      format.html { :redirect_to :action => "index" }
      format.js # renders create.rjs
      format.xml do
        headers["Location"] = person_url(@person)
        render :nothing => true
      end
    end
  end
end

Seeing this made me think error handling when you’re responding to a lot of different formats could get messy, so I had a look at the bloction code from Rick, Courtenay and Justin’s Railsday project. Rick mentioned this as a good example of a RESTful app. Reading over the code they managed errors by using exceptions as in the example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

def create
  @bid = @product.bids.build(:total => params[:bid][:total], :user_id => current_user.id)
  high_bidder = @product.bidder
  @bid.save!
  BidMailer.deliver_outbid_notice(high_bidder, @product.reload) if high_bidder && high_bidder.id.to_s != @bid.user_id.to_s
  respond_to do |format|
    format.html do
      flash[:notice] = "Your bid was sent."
      redirect_to product_url(@product.user, @product)
    end
    format.js do
      @bids = @product.bids.find(:all, :limit => 4)
      unless @highest_bid = @bids.shift
        @highest_bid = @product.bids.build(:amount => @product.starting_amount, :user => @product.user)
        @highest_bid.save_without_validation
      end        
    end
  end
rescue ActiveRecord::RecordInvalid
  respond_to do |format|
    format.html { render :partial => 'new' }
    format.js do 
      render :update do |page|
        page.replace_html 'bid_messages', "Your bid was invalid. Please fix and try again." + @bid.errors.full_messages.join('<br />')
      end# todo: show the invalid partial and a reason why
    end
  end
rescue Bid::InvalidBidder
  respond_to do |format|
    format.html { render :text => "You can't bid on your own auction!" }
  end
rescue Bid::OutOfMoney
  respond_to do |format|
    format.html do
      flash[:notice] = "You don't have enough money to bid that much!  Maybe you want to transfer some more."
      redirect_to user_url(current_user)
    end
    format.js do
      render :update do |page|
        page.replace_html 'bid_messages', :partial => 'users/out_of_money'
      end
    end
  end
end

This could get a little ugly if every action had to respond to every format, but I don’t think this is going to be the case. Plus, if you follow David’s lead on keeping things as CRUDdy as possible, you’re going to have smaller easy to manage controllers doing the same conceptual work on different nouns in your domain language. To me this means less to think about for each controller, so it shouldn’t be too hard to keep the error handling you have to do in your head.

If the case/switch like respond_to block statement becomes overly complex, perhaps it will be suited to the Replace conditional with polymorphism pattern. You could have a base controller that does the common work, then subclasses for each of the formats you want to respond to. I’ve no idea how that would work, or even if it would be less code but it’s a possibility I will keep in mind as I make things more RESTful in my own apps.

Having recently started a new project that’s greenfield development, I hope to introduce REST ideas as I go and see how I get along with them.

Converting Typo to Mephisto

June 12, 2006 @ 04:28 PM Posted by admin

I’ve recently changed to using Mephisto as my blog engine rather than Typo. Mephisto is yet another product of Rick Olsen, Rails core member and uber plugin guy. I’ve wanted to switch for a while for several reasons, but have only got around to it now. I was looking for an engine that supports multiple sites per installation, can import my old Typo posts, has CMS features, and is extensible via plugins and Mephisto fits the bill. It’s not quite finished yet, but has enough features to be usable right now and is a lot easier to hack on than Typo.

Mephisto uses liquid templates, editable in it’s admin section meaning you’ve got total control over the look of your site. I found it a bit hard initially editing the templates in the browser, but I’m on a Mac and TextMate’s Edit In TextMate… feature helped with that. I’ve got a bit more used to it now, though I’d still like something similar to Vision so I could edit themes.

This post describes the process I used, the idea being I might get some more people interested in switching from Typo and maybe hacking on the Mephisto code to get it really production ready.

Set up a base Mephisto install

I like to have a local copy of the code to work on and use Capistrano to put it live. This doesn’t work so well yet with Mephisto, as they design is stored in the database. However I’m still going to do this so I can use my capistrano-runit-tasks library. As this probably isn’t what you want to do, see the example deploy.rb in the svn source for Mephisto and use that or just set it up live on the server. A basic live setup would be something like:

  1. Checkout the Mephisto trunk to where you’re going to run your site from
  2. Checkout/export a revision of rails into vendor/rails
  3. Set up a database and a database.yml pointing to it. Run mephisto in production mode from the start, so you only need a production database for now.
  4. Run rake db:bootstrap to do the initial mephisto setup
  5. Point your webserver of choice at the new setup and check it’s all working. (You should see an incredibly basic layout)

Initial configuration

Once you’ve got it all up and running on your environment, login to the admin section at http://<your site>/admin. The rake db:bootstrap sets up an admin user, but I couldn’t figure out the password so I changed it via script/console on the live server. (Update: the password is test)

The first thing you’ll want to do is hit the settings tab on the top right and change them to what you want. Make sure and set the filter to whatever you used in your old blog so the articles import correctly.

Import your old Typo articles

To do this you need to:

  1. edit config/database.yml to include a typo section pointing to your typo database
  2. from the console run
    script/runner "Mephisto.convert_from :typo" -e production

You should see a count of the users, articles and comments. Typo uses http://<site name>/articles/... for permalinks, so we’ll need to add a route to support that. So add the following code to routes.rb. (Update: I don’t actually do the routes thing below anymore, I have the webserver configured to 301 redirect the old urls to the new ones)

1
2
3
4

  # Support old Typo urls
  m.article 'articles/:year/:month/:day/:permalink', :action => 'show',    
    :year => /\d{4}/, :month => /\d{1,2}/, :day => /\d{1,2}/
This should go under the existing:
1
2
3

  m.article ':year/:month/:day/:permalink', :action => 'show',    
    :year => /\d{4}/, :month => /\d{1,2}/, :day => /\d{1,2}/

Create the design

Click on the design tab in the admin interface, down the right hand side you’ll see a list of templates. The first one we’ll do is the layout, which is used like views/layouts/application.rhtml in a rails app.

I couldn’t design my way out of a wet paper bag, so I headed over to oswd.org and looked around for one I liked. I eventually chose this one, and am using it slightly modified. The rest of this section assumes you’ve got something similar to what you’d get from oswd, i.e. a html mockup of the design.

The first thing is to edit the layout template in Mephisto. Clicking on the layout link on the right hand side will show you the current liquid template for the layout. You need to replace this with your layout, and add the liquid tags you need.

Let’s start in the head. Mephisto provides some liquid blocks for standard stuff, and one of these is the tags. replace the and in your layout with {% head } and { endhead %}, and somewhere in between do {{ head.feed }}. This sets up the rss auto discovery tag. You’ll want to have your site’s title in the title of the page, so add {{ site.title }}

The site is setup in application.rb and made available to liquid. To see what other attributes are available, have a look at the to_liquid method in app/models/site.rb.

Once you’ve got the HTML/Liquid the way you want it, put {{ content_for_layout }} where you want the content to go.

You’ll need to create a new template for your stylesheet. Click on design again, paste the css into the big box, set the name to whatever is required (not including .css) and set the type to CSS. You may need to edit your layout to make sure the link tag is pointing to /stylesheets/.css. You’ll need to upload the images for your template using the form underneath the list of templates. These will be uploaded to images/ so make sure any img links and url() stuff in your layout / stylesheet are pointing to the right place.

The next template you’ll need to edit is the ‘home’ template. This is used for the main page of your blog. Mine looks something like:

1
2
3
4
5
6
7
8
9
10
11
12

{% for article in articles %}
<h2>{{ article | link_to_article }}</h2>
<div class="meta">
  <span class="date">{{ article.published_at | format_date: 'standard' }}</span>
  <span class="postedBy">Posted by {{ article.author.login }}</span>
</div>
{{ article.body }}
<div class="comments">
  {{ article | link_to_comments }}
</div>
{% endfor %}

Mephisto makes the articles collection available, again have a look at the to_liquid method in the Article and Author class to see what else is available to you.

Next is the single article display, which uses the ‘single’ template and allows the user to add comments. Mine looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

<h2>{{ article.title }}</h2>
<div class="article">
  <div class="meta">
    <span class="date">{{ article.published_at | format_date: 'standard' }}</span>
    <span class="postedBy">Posted by {{ article.author.login }}</span>
  </div>
  {{ article.body }}
</div>

{% for comment in comments %}
<div class="article-comments">
  <div class="article-comments-meta">
    <span class="date">{{ comment.created_at | format_date: 'standard' }}</span>
    <span class="postedBy">Comment by {{ comment.author }}</span>
  </div>
  {{ comment.body }}
</div>
{% endfor %}

<div id="article-comment-form">
<h3>Got something to add?</h3>
{% commentform %}
<p>Your name:<br/> {{ form.name }}</p>
<p>Your email:<br/> {{ form.email }}</p>
<p>Your blog:<br/> {{ form.url }}</p>
<p>Your comment:<br/> {{ form.body }}</p>
{{ form.submit }}
{% endcommentform %}
</div>

Notice the comments are available as a separate collection and how Mephisto provides some liquid tags for your comment form.

Handle Typo’s RSS feed URL

Typo and Mephisto have different URLs for their feeds. I added these routes to support people already subscribed:

1
2
3
4

  map.typo_atom_feed 'xml/atom/feed.xml', :controller => 'feed', :sections => "",  :action => 'feed'
  map.typo_rss_feed 'xml/rss/feed.xml', :controller => 'feed', :sections => "",  :action => 'feed'
  map.typo_rss2_feed 'xml/rss20/feed.xml', :controller => 'feed', :sections => "",  :action => 'feed'

These all redirect to the atom feed, which may cause some problems for people but I posted an article explaining what was happening so I reckon anyone who’s still interested in reading shouldn’t be affected.

Creating other pages

So that’s the basic blog running. Mephisto also has lite CMS features, perfect for adding some more information like pages for projects I work on and contact information. This is done by adding a new section and setting the type to “Area with multiple pages (one article per page)”.

The content in these sections is displayed using the ‘page’ template, so that needs to be edited to display the correct information. Mine looks like:

1
2
3
4
5

<h2>{{ article.title }}</h2>
<div class="article">
  {{ article.body }}
</div>

Summary

So that’s pretty much what I did to get this blog switched over. I’ve set it up so that http://octopod.info and http://outside-thoughts.octopod.info are the same now. Let me know thoughts / problems and comments below.

Switching to Mephisto

June 12, 2006 @ 04:15 PM Posted by octopod

I’m nearly finished switching this blog over to Mephisto. More details when I’ve actually switched. One casualty of moving over will be only providing an atom feed. A lot of the readers here use rss2, so if you’re still interested, switch your reader to use this link, which I’ll still support.

Rails at Reboot 8

June 02, 2006 @ 06:49 AM Posted by octopod

I’m here at the very interesting Reboot 8 where Jarkko Laine hosted a great discussion after dinner with a theme of:

“What is exactly the sweet spot Ruby on Rails has managed to hit? What in Rails makes a developer tick? The Railways is an open discussion where Rails hackers share the love and tell newcomers what in the Rails paradigm concretely made their views about web development turn somersaults.”

Some very interesting people turned up to share their experiences. The Rails users were people at small startups, people who’ve been running Rails apps for well over a year and people who work for large consultancies. The interested folks included .Net and J2EE developers, designers who wanted to know more about what their developers are excited about, and people who’ve been tracking Rails for a while without diving in and using it.

I’ll try to summarize here, and provide some information and tips that came out of the discussion. Apologies if I’ve missed anything.

People who are already using Rails are using it because they:

  • Enjoy it.
  • Get products and solutions out the door faster.
  • Appreciate how the tools that come along with Rails make development and deployment easier.
  • Appreciate how beautiful Ruby code can be made.

People who are interested in Rails but aren’t using it yet are either:

  • Locked into other frameworks because of their clients.
  • Unable to find documentation they can use either about the framework or about setting up production servers.
  • Worried about whether time spent learning a new language and framework will be a good investment.

So what exactly did we talk about?

Speed of development

Try things out, fail or succeed faster, use less people to get the same things done so you don’t need as much or even any funding. This was illustrated earlier in the day by Jesper Rønn-Jensen’s talk about using Rails for prototyping in CapGemini. They’ve put some of their prototypes into production, and turned others into Java/.Net code after using Rails to work out the use cases with their clients.

Good abstractions

Lars Pinds talked about how the abstraction in Rails are at the right level. Rails abstracts the things that are fixed, e.g. the HTTP protocol, AJAX, SQL and leaves the rest up to the developer. He talked about the problems other frameworks had trying to abstract non-generic things like groups and users into a generic framework and why that didn’t work.

Ease of deployment

People who’d come from J2EE specifically mentioned Capistrano being a joy. Ben Griffith shared his secret for finding Java developers who want to become Rails developers. He took out adwords for terms like “J2EE deployment problems” that said “Stuck in J2EE hell?” and got a very good response!

Migrations

Several people mentioned this as something that other frameworks just don’t have. Anyone who has used migrations knows it would be hard to do it any other way.

script/console

Having a console on your production database means you don’t have to write lots of scripts to do DB stuff. Knowing you’re going to be working this way means you tend to have a richer domain model.

Testing is easy

As Rails comes with a testing framework built in it’s easy to be test infected.

Ruby

Ruby allows you to write beautiful code that is closer to the language of the domain. Ben actually gets his non techies to read the code!

Fun / Passion

Reno Marconi, one of the original developers of Java in Sun talked about how he hasn’t seen this much buzz since Java first came out.

Future directions

People wondered whether Rails would turn into the committee based mess of J2EE and were reassured that DHH’s benevolent delegator style of leadership would ensure this wouldn’t happen.

A few people talked about running Ruby inside the CLR or JVM. Jon Lam is working on making the CLR and Ruby talk together. Other people made the point that the JVM is a really nice piece of engineering, it’s J2EE that’s the mess on top of it.

Documentation

A couple of people mentioned this as an area they found lacking, both for Rails itself and getting your app running in production. rails.outertrack.com is a fairly new site aiming to be something like php.net for Rails. Several upcoming books were mentioned that will help improve documentation in other areas. Someone also made the point that when Tomcat first came out, sometimes the only way to figure out what was going on was to read the code.

Keeping up with what’s happening

We talked about how to keep up with what’s happening in the Rails world. A good set of RSS feeds to keep in your reader are:

The Rails weblog has mentioned some interesting blogs recently, have a look back through that to find some more.

Switching to runit from spinner/spawner/reaper

March 18, 2006 @ 12:09 PM Posted by octopod

I’ve recently switched to runit from spinner/spawner/reaper for fcgi supervision. This turned out to be a pretty painless process, and reduced the load on my VPS from 2 – 2.5 back down to 0 – 0.5 as it wasn’t swapping anymore.

In the process, I created capistrano-runit-tasks, a library for Capistrano that handles setting up the service directories and controlling the tasks. If you’re in a low memory situation, or just don’t like the spinner way of doing things give I’d highly recommend runit.

Keeping your apps on the edge without using svn:externals

March 14, 2006 @ 10:55 AM Posted by octopod
desc “Get a revision of rails to ~/dev/rails_revisions defaults to head, use REVISION=x to get a specific revision” task :get_edge do get_edge(ENV[‘REVISION’]) end

desc “Link sites in dev to a specific revision from ~/dev/revisions, use APPS=a,b,c REVISION=x Does nothing if any of the apps given do not exist. Defaults to HEAD if REVISION not used. Will get the revision if it doesn’t exist locally” task :link_apps_to_edge do if ENV[‘APPS’].nil? puts ‘Must specify some apps with APPS=a,b,c’ exit end apps = ENV[‘APPS’].split(’,’) exit unless apps_exist?(apps)

edge_path = get_edge(ENV['REVISION'])
apps.each do |app_path|
  Dir.glob("#{app_path}/**/vendor").each do |vendor_path|
    next if vendor_path.include?("_darcs")
    rails_path = File.join(vendor_path, 'rails')
    if File.symlink? rails_path
       FileUtils.rm rails_path
    end
    if File.exists? rails_path
      puts "A non-linked version of rails exists in app #{app_path}, dir #{rails_path}. Not doing anything" 
    else
      FileUtils.ln_s File.expand_path(edge_path), rails_path
      puts "linked #{edge_path} to #{rails_path}" 
    end
  end
end

end

def get_edge(revision) revision ||= `svn info http://dev.rubyonrails.org/svn/rails/trunk`.match(/Revision: (\d+)/).captures0 export_path = “rails_revisions/#{revision}” if File.exists? export_path puts “Revision #{revision} already exists, doing nothing” else puts “About to export #{revision} to #{export_path}” puts `svn export -r #{revision} http://dev.rubyonrails.org/svn/rails/trunk #{export_path}` end export_path end

def apps_exist?(apps) non_existant_paths = apps.collect { |app_path| app_path unless File.exists? app_path }.compact if non_existant_paths.empty? true else non_existant_paths.each { |path| puts ”#{path} does not exist” } puts “Nothing done” false end end

My initial VPS backup script

February 24, 2006 @ 09:29 AM Posted by octopod

This article should have my first working version of the backup script I use at Quantact attached. It’s very rough, it was the first Ruby code I wrote after I got back. I’d like to do a lot of work to it to make it nice, but it’s not a high priority right now. Aniero asked to see it on irc, so here it is. Read the comments and look for <<...>> for places you need to put in your own info. The Subversion part assumes you have all your Subversion repositories running under one user.

I run this in a root cron job. Feel free to improve and send back to me :)

TextMate and Keyref, the TextMate shortcut reference PDF generator

February 23, 2006 @ 06:13 PM Posted by octopod

Switching

So I’m well behind on this one, having had a Mac since June and going a whole six months without switching. I couldn’t help it, I was (and still am) really comfortable in Vim and if it integrated better with OS X i’d still be there. TextMate is a very nice editor, it integrates incredibly well with OS X and has a great bunch of Ruby and Rails shortcuts (among others). I’m getting more comfortable with it every day, though of course I still miss certain things about Vim.

The problem with switching is, of course, you don’t know what the editor can do and what all the shortcut keys are straight away. TextMate provides a list of them if you want, but that’s not very handy when you’re in the middle of an editing session. I used to have this problem in Vim until I discovered the Vim quick reference card, so I wanted something similar for TextMate.

Making a TextMate PDF reference card

The list TextMate shows is a HTML table, and when you copy and paste somewhere it turns in to something like a tab separated value file. Hmm… My thoughts turned to Ruby, and I reckoned with a little scripting I should be able to scrape the HTML and turn it into a PDF. I’ve noticed Austin Ziegler’s PDF::Writer before, but haven’t had a chance to use it, so here was the perfect opportunity.

After installation, I had a look through the documentation and the demo files and saw the Ruby language and Ruby library quick reference cards. These were written by Ryan Davis, aka zenspider, who wrote another quick reference I find invaluable, available here (in HTML form). Peering at the code, the first thing that jumped out was the: require ‘pdf/quickef’ It turns out PDF::Writer includes code to generate three column quick reference cards, making my life very much easier! One scraper and some glue to hook it into PDF:QuickRef later, I had a PDF version of my TextMate shortcuts to make a printed reference card with.

Turning it into a Rails app – Keyref

I had been trying to think of a nice simple app I could write to get back into Rails after my trip. and thought that if I found making a PDF useful perhaps other people would too. So Keyref was born. Not only would it be something straightforward enough to write, but it would allow me to check out SwitchTower and set up my process of deploying an app from scratch to my VPS.

I had fun coding up the app, I found a simple design on oswd I liked and started from there. After a bit of a detour through a totally Ajax interface, considering writing a proper parser for the HTML and seeing if PDF::Writer would do UTF-8 characters1 and use an external font2, it ended up in the form it is now.

You can paste the key shortcuts into a form in the app, it parses (ok, scrapes) them and presents you with a list of available bundles from which you can pick eight to be on your card. This uses some Prototype / script.aculo.us magic to enable you to drag and drop between the lists and re-order the bundles you want printed.

I limited it to eight as PDF::Writer can use a lot of memory as it tries to find the best layout for the card and I didn’t want to kill my 96Mb VPS. Multiple requests at the same time would also be a problem, so I decided to store the parsed data in the database and generate the PDFs one at a time. A really cut down version of the prag progs order system if you like. I’ll write about the generator and mailer part of the system at a later date.

Keyref is up and running, TextMate users feel free to give it a go and let me know what you think.

1 It won’t, yet, the PDF spec only uses UTF16-BE which PDF:Writer supports (just about) and I couldn’t do what I wanted to do

2 I got this working, but the letter spacing was all wrong.

Getting root

February 22, 2006 @ 06:12 PM Posted by octopod

So with the decision to move made, there were a few things to sort out:

  • Where to go to
  • What OS to run
  • How best to get everything set up
  • How to handle backups

So it was research time!

Choosing a provider

Happily several people had just made the same kind of move, so I got some good advice. It boiled down to a choice between RimU and Quantact. I ended up deciding on Quantact, they were slightly cheaper, the main guy seemed nice on irc and, to be honest “rim-u” made me laugh every time I said or thought the name. I’m currently using their basic service, 96Mb of RAM, 4500Mb of disk and 40Gb/month data transfer. It’s only costing me a few dollars more per month than TextDrive, and they’ve proved to be very solid over the past few weeks. My host has only been down for half an hour because of a RAID issue, and I got plenty of warning that was going to happen.

They use Xen for virtualization, and I’ve been really impressed with how it does it. I’m guaranteed memory and disk, no problems with lack of file descriptors etc. and the scheduler means that no one can hog all the processing power. It just seems a much better way of organising things.

OS and Setup

This was actually a really easy decision in the end, after I saw the excellent guide Ezra Zygmuntowicz wrote for setting up the Rails stack on Debian. He’s turning it into a book which I’m really looking forward too. Debian is a pretty small distro, meaning less space taken up by the OS, and less to back up. It doesn’t have the latest and greatest, which can occasionally be a pain but backports help with that.

I pretty much followed the guide to the letter and in no time had transferred my sites and pointed the DNS to their new home.

Backup

This is the one thing I knew I was going to miss from TextDrive, they have excellent backup there. I had a look at various backup solutions, but in the end decided that as they’d been so good before, TextDrive’s Strongspace would be the best solution. They have a 4Gb package which is a perfect fit, as I’m not planning on using up anywhere near my 4500Mb on Quantact. After much reading around about what was important to back up and not I ended up with my own Ruby based solution using Rake and FileUtils. With that in the crontab I’m much happier. One of the next things on my sysadmin list is actually restoring that backup somewhere else as no backup strategy can be considered working until you’ve restored onto a new system and got everything going again!

Why the move?

February 22, 2006 @ 05:21 PM Posted by octopod

I moved for a couple of reasons

I don’t think shared hosting works

I’ve come to the conclusion that shared hosting really doesn’t work. All those people trying on the same machine is bound to lead to resource issues. This might have been fine in TextDrive’s early days when the people there knew a lot about what exactly they were doing, but the more people you get on board, the less fully clued up people you’re going to have. It only takes one clueless person / idiot to start using all the resources and everybody on the box is screwed.

I had to restart my sites when I was away

TextDrive didn’t provide an easy way to ensure your sites were restarted when they rebooted the servers. If you put an entry in cron the process got zapped before it had a chance to start. Daedalus, which they’d originally recommended was banned and I couldn’t find any replacement.

Keeping up with what was best practice

I felt that I had to read the forums every day if I wanted to make sure I wasn’t doing anything wrong. The instructions on the best way of doing things were often buried in the middle of some topic, and if they got updated there was no way of knowing unless you kept your eyes peeled.

I want to learn for myself

Being on a shared host brings with it necessary restrictions, and also means the sysadmins can choose to change something and you have to go digging around to find out why. I wanted to learn for myself without the restrictions so when I saw other people looking around for a VPS where for all intents and purposes you get your own machine I decided it was time to make the switch.