High Quality and Affordable Shared Hosting for Rails Apps: WebFaction

Most of the Rails projects I work on end up running on either a dedicated server or virtual server where I have root access.  This gives me full control over the stack and usually guarantees that I have no issues deploying the applications.  But from time to time, I work on smaller projects which have a smaller budget. and the answer of course is shared hosting.  Which sucks.   Usually.

In the past, I recommended Dreamhost to my clients as they support modern versions of rails and ruby and allow ssh login (which means things like git and installing your own binaries is possible).  But the performance of the apps on Dreamhost was borderline – sometimes it was acceptable, and sometimes it wasn’t.  And a couple of times, I had to request they move our account to a different server in order to fix that.  It got embarrassing after a while to show clients their brand new, shiny site/app on a slow server like this.

Next, I used HostingRails.com, and that did the trick for a few years.  The performance was better than Dreamhost, no reliability issues, and clients were happy.  They even support Rails 3 now.  However last month I was finishing up a Rails 3.1 app (on Ruby 1.9.2) for a client and was hard-pressed to find a shared host that supported that.  After several sessions of Googling, I found WebFaction.  They had ssh access and supported Rails 3.1 according to their knowledge base.  Those were my main criteria, and the rave reviews I kept finding sealed the deal.

The pricing for WebFaction is very simple – $9.50/month for a month-to-month billing plan, and by pre-paying, you can bring it down to as low as $5.50/month (that’s for a 5-year payment).  My client signed up and we got an email from WebFaction with all our login and password shortly after.

Upon logging in, I saw their homegrown control panel.  It  was very simple and easy to navigate – a welcome change from cpanel (blech!).  The main concepts I was interested in were “Domains / websites” and “Databases”.  Under “Domains / websites”, I saw my client’s domain and websites was already setup (a domain can have multiple web sites inside it, each mapped to a different subdomain).  Then there are “Applications”, which get mounted inside a website.   You can mount multiple “Site apps” inside your website – a custom Rails app at “/”, AWStats at “/awstats”, a WordPress blog at “/blog”, etc.

The way this works is that WebFaction has preconfigured templates for Django, Drupal, PHP, Redmine, Rails, and WordPress to name a few.  And of course you can choose “Custom” to do things that they don’t support out of the box.  I chose Rails for the “App Category”, then chose “Rails 3.1 (nginx 1.0.6/Passenger 3.0.9/Ruby Enterprise Edition 1.8.7) as my app was Rails 3.1.  You are basically choosing which stack configuration you want your app to run on.

After I clicked “Create”, I logged in via ssh to check out the directory structure.   There’s a  ”webapps” directory in your home directory containing each of the apps you configure through the control panel (in my case it was called “rails”).  Inside webapps/rails was a sample rails app, which I deleted and I then did a git clone of my project to put the app inside webapps/rails.

So far, so good.  I setup my database in the control panel, put that info into database.yml, and ran “RAILS_ENV=production bundle exec rake db:setup”.  And that’s when the trouble started.  If you recall, my app was developed to run on Rails 3.1 and Ruby 1.9.2.  The WebFaction stack was using Ruby Enterprise Edition 1.8.7.  I’d run into this a few times before, and it came down to changing some of the rails-generated config files to use ruby 1.8.7 hash syntax instead of the new 1.9 syntax.  Once I made those changes, we were good to go.

I ran the webapps/rails/bin/restart command to restart my nginx instance, and then the site was working.  And it was fast, REALLY fast.  WebFaction makes sure your application is running via a cronjob, so there’s no waiting around for Rails to startup if nobody has visited your site in a while.  But of course I couldn’t leave well enough alone – I wanted to run ruby 1.9.2 on WebFaction.  They’ve since release a new application template for Rails 3.2.1/ruby-1.9.3, but that did not exist at the time I was deploying my app, so I had to install my custom ruby in my account using RVM.  Fortunately, it was pretty easy to do.  Here’s the steps required:

  1. Install rvm under my ssh account.
  2. Install ruby 1.9.2 in rvm
  3. Create an rvm gemset for my app
  4. Create an rvm wrapper to run ruby 1.9.2 with that gemset (rvm wrappers are a really useful concept, check the rvm github page for more info.)
  5. Modify nginx config to use the new wrapper as it’s “passenger_ruby”.
  6. Install same version of the passenger gem into ruby 1.9.2 as ree 1.8.7 has.

Step 6 was kind of subtle – I was able to install passenger under ruby 1.9.2, but was unable to compile the nginx passenger module.  So I reused the one that was already there (by leaving the passenger_root nginx configuration alone) figuring it would work fine.  And it did, but I used the same version in case the ruby part of the passenger gem was connected to the nginx module.

Another nginx restart, and it was all running under ruby 1.9.2.

So, to recap: WebFaction is great.  They have a simple control panel, intelligent, built-in stack templates, and the ability to customize everything if the templates don’t work for you.    WebFaction will be my goto solution when I need shared hosting for as long as they’re in business.

 

Filling PDF forms with Ruby and pdftk

From time to time, I need to dynamically generate a pdf with personalized content.  Rather than writing code in something like Prawn to create the pdf from scratch, I usually get a pdf from my client which can be used as a template.  By using a pdf Acroform and the insanely useful pdftk tool, you can easily generate a personalized pdf based on a template file, similar to how word processors handle a mail merge.  As an example, let’s say we want to generate a letter which has a user’s first and last name in it.  Here’s how we do it:

Take the original pdf and embed an acroform using Adobe LiveCycle (included with Acrobat Professional) or a number of 3rd party applications.  Acroforms can contain a variety of field types (text fields, radio buttons, etc.) but we’re only going to focus on basic text fields right now.  How do create the forms is outside the scope of this article, but I will say that you have to be careful to make sure your fields are defined large enough to handle the data you plan to stuff in to them.  Let’s say you save that file as “template.pdf”.

Next (and this the tricky part), you need to create a separate file in FDF (Forms Data Format) format which contains your data.  FDF was originally a binary only format, similar to PDF, but luckily there is also XFDF which is an XML-based format.  Below is a simple XFDF file (you can read the full XFDF spec here) with the first and last name fields:

<?xml version="1.0" encoding="UTF-8"?>
  <xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">

  <field name="First Name">
  <value>Greg</value>
  </field>

  <field name="Last Name">
  <value>Lappen</value>
  </field>
</xfdf>

Let’s call that file “data.xfdf”.  We’re ready to do a trial run now:

> pdftk template.pdf fill_form data.xfdf output personalized_letter.pdf flatten

That should produce the final file “personalized_letter.pdf”.  Open it up and behold your masterpiece!

So far, we’ve created the files manually run the command manually.  What we really want is to be able to do this automatically from within a Rails app (or the Ruby web framework of choice).  Here’s the simple class I came up with to assist with that:

require 'builder'
require 'open3'

class PdfStamper
  def self.stamp(input_pdf, fields)
    stdin, stdout, stderr = Open3.popen3("pdftk #{input_pdf} fill_form - output - flatten")
    stdin << generate_xfdf(fields, File.basename(input_pdf))     
    stdin.close
    yield stdout
    stdout.close
    stderr.close
  end   

  def self.generate_xfdf(fields, filename)
    xml = Builder::XmlMarkup.new
    xml.instruct!
    xml.xfdf("xmlns" => "http://ns.adobe.com/xfdf/", "xml:space" => "preserve") {
      xml.f :href => filename
      xml.fields {
        fields.each do |field, value|
          xml.field(:name => field) {
            if value.is_a? Array
              value.each {|item| xml.value(item.to_s) }
            else
              xml.value(value.to_s)
            end
          }
        end
      }
    }
    xml.target!
  end
end

This class is basically executing the command from above, with a few tweaks to use stdout and stdin rather than littering temporary fdf and pdf files all over the place.  I put this pdf_stamper.rb file in the lib directory of my Ramils app. Then, I can use it like this:

PdfStamper.stamp("template.pdf", {'First Name'=>'Greg', 'Last Name'=>'Lappen'}) do |pdf_io|
  pdf_content = StringIO.new(pdf_io.read)
  send_data pdf_content, :filename=>'your_letter.pdf', :disposition=>'inline', :type=>'application/pdf'
 end

I hope that this is helpful to someone – odds are that I’ll tighten this up a bit, add some specs, better error handling, and release it as a ruby gem….stay tuned.