ActiveSupport for PHP - Ruby style

One of the things I miss most from Rails is ActiveSupport, the module that modifies Ruby’s core classes (numbers, strings, more) with handy utility methods. They tie so well into the language, most Rails developers don’t realize they aren’t core methods.

Here’s a few examples:

>> 'hello_world'.camelize
=> 'HelloWorld'

>> 7.days.ago
=> Sun Aug 05 20:53:12 -0400 2007

>> 'purple people eater'.ends_with?('eater')
=> true

Is it possible to port this left-to-right style of coding to PHP?

Bedtime reading: Joyent Connector is now open source

Joyent recently open sourced both Slingshot, their toolset/library for developing offline Rails apps, and Connector, an online suite of collaboration tools for small businesses.

The Connector source code particularly interests me. There’s already plenty of great open source Rails projects out there (Beast and Mephisto come to mind), but as far as I can tell, this is the first instance of a commercial Rails application being open sourced, by a reputable organization like Joyent no less. IMHO, there’s no better way to learn Rails than from finished, deployed software, and Connector is no exception: it’s composed of slick, well-written code.

Here’s a look at Connector in action:

Stepping off the Rails with Rack, the modular web server interface for Ruby

What’s Rack?

Rack is a modular web server interface for Ruby. Instead of coding your application for a particular server interface like CGI or Ruby’s WEBrick, you adapt your application instead to the Rack interface specification, and invoke the appropriate server handler. With just a minuscule amount of work, you can easily deploy your app using any of WEBrick, Mongrel, FCGI or CGI.

You can install Rack as a Ruby gem:

gem install rack --include-dependencies

Simple Example

To get started with Rack, the entry point for your application must be an object that responds to a call method and takes a single parameter: a hash of environment variables. Your call method must return an array containing the HTTP response code, a set of headers including the content type, and finally the response body.

This code probably explains it best:

# hello_world.rb

require 'rubygems'
require 'rack'

class HelloWorld
  def call(env)

    [ 200, # HTTP Response Code
      { "Content-Type"=>"text/plain" }, # HTTP Headers
      [ "Hello, World!" ] # Body
    ]
  end
end

# Instantiate your app
hello_world_app = HelloWorld.new

# Deploy using WEBrick handler
Rack::Handler::WEBrick.run hello_world_app, :Port => 3000

You then start your web application simply by calling it:

# ruby hello_world.rb
[2007-06-17 20:42:31] INFO WEBrick 1.3.1
[2007-06-17 20:42:31] ruby 1.8.5 (2006-08-25) [i386-mswin32]
[2007-06-17 20:42:31] INFO WEBrick::HTTPServer#start: pid=5894 port: 3000

Even Simpler

Since most of us don’t want to deal with the raw request and response bodies ourselves, Rack supplies easy-to-use Request and Response classes. Here’s an example app that takes a single GET param:

...

class HelloWorld
  def call(env)
    req = Rack::Request.new(env)

    name = req.GET['name'] # post: req.POST['name']

    # Default content-type is text/html
    # Default status is 200
    Rack::Response.new.finish do |res|
       res.write "Hello, #{name}!" 
    end
  end
end

# This time, deploy using plain-old CGI through Apache/Lighttpd/etc.
hello_world_app = HelloWorld.new
Rack::Handler::CGI.run hello_world_app

Along these lines, Rack also has helpers for URL routing, cookie handling, and more.

Contact Form Example

For fun, I’ve taken this simple CGI contact form written in Ruby from my first Stepping off the Rails article and converted it to Rack. It can now be deployed using any of the handlers mentioned above, or still as plain CGI. Talk about flexibility.

Review: Ruby on Rails E-Commerce

Beginning Ruby on Rails E-Commerce: From Novice to Professional … separates itself from the oncoming barrage of introductory Rails texts by guiding readers through the development of an online bookstore from start-to-finish.

The rest of the article can be read at Dr. Dobb’s.

A look at ActiveRecord association setters

If you’ve been working in Rails for more than a short while, you should be familiar with ActiveRecord class methods, and how they modify your model definitions with helpful magic methods.

For example, the has_many declaration gives you access to a host of accessors and setters that make it easy to manipulate associated child objects:

Class Family < ActiveRecord
  has_many :members
end

class Member < ActiveRecord
  belongs_to :family
end

# Create a new family and save it
family = Member.new(:name => “The Johnsons”)
family.save

# Add a family member through array insertion
family.members << Member.new(:name => “Sam”)

# Add a family member through association creation
family.members.create(:name => “Sally”)

In the example code above, we created our new family members through the “members” collection. By doing so, the family_id attribute/column is initialized for us. Otherwise, we’d have to set it manually like this:

sunny = Member.create(:name => “Sunny”, :family_id => family.id)

Okay, you might not find that impressive. I mean, we’re only saving a few dozen characters of code here, right?

Turns out there’s another convenient way of building associated objects that I feel doesn’t get enough press. It’s the collection=objects method, which takes an array of associated objects like so:

family.members = [ Member.new(:name => “Jean”), Member.new(:name => “Tom”) ]

You’ll find that for each of these objects the family_id attribute was set, just as they would have been above. As for Sam, Sally and Sunny? They’ve been usurped by Jean and Tom, the once-loyal babysitters. That’s right, in addition to creating new database records for Jean and Tom, the old records are deleted too. That’s a lot of good stuff going on for a single line of code.

Editor’s note—I need to come up with better examples.

It’s also worth mentioning this setter method also works when initializing ActiveRecord objects using a hash. The example code below will create a new family, add a couple family members to it, and save all three objects to the database.

Family.create :name => "The Johnsons", :members => 
  [ Member.new(:name => "Sam"), Member.new(:name => "Sally") ]

Okay, so this isn’t revolutionary, but it’s another solid tool to add to your Rails arsenal if you haven’t already.

Topfunky takes on hosting

I thought I’d mention Geoff Grosenbach’s article at Nuby on Rails comparing all the major Rails hosting providers. It’s probably the most comprehensive Rails hosting guide I’ve come across, based on Geoff’s own experiences – over the years, he’s used them all. I especially wanted to point out this quote, regarding Slicehost’s $20 VPS plan:

If you are a full-time Rails developer, I think you owe it to yourself to learn how to operate a server. Spending $20/month on your own education will be worthwhile and will help you make mistakes on your own before making them on a paying client project. Slicehost would be a great environment for that kind of self-education.

I couldn’t agree more – fooling around on my VPS has been an invaluable experience. Anyways, a great read if you’re in the market for a new online home.

Elevator, a new take on Rails scaffolding

A few weeks ago Joshua Wehner, Toronto-area Rails guy, announced Elevator 0.1, his personal take on Rails scaffolding.

The key improvement? Instead of generating a public facing CRUD interface, which you’ll inevitably toss out anyways, Elevator generates both admin-side and user-side scaffolding, complete with test suite(s). As an added bonus, you also get a nicer looking pages, without approaching the complexity of something like the Ajax Scaffold gem/plugin.

See also: TorRor Google Group.

Deploy your Rails app in a subdirectory with Apache and Mongrel

It’s not particularly hard to deploy your app in a subdirectory, but I’ve found that figuring out how to can be. So, I decided to put together this short little write-up.

It turns out Mongrel versions 0.3.14.x and up have a —prefix command line parameter for locating your Rails app at an arbitrary URL. Of course, you should already be running version 1.0 anyways. Here’s a single production Mongrel instance running on port 8000, prefixed with ‘subdir’:

mongrel_rails start -e production -p 8000 --prefix=/subdir

Your app should now be accessible at http://localhost:8000/subdir.

How does this work? Aside from prefixing all your routes with the ’/subdir’ path, the —prefix option also modifies the result of all url_for (and thus link_to) method calls to include the new path. That means that, given a religious use of the link_to method, you can arbitrarily relocate your program anywhere at the drop of a hat. Not bad.

url_for :controller=> 'admin', :action => 'login' # => '/subdir/admin/login'

I’m going to assume you’ve already figured out how to proxy Mongrel through Apache. As far as your subdirectory goes, you need the following two lines in your Apache conf file:

ProxyPass /subdir http://localhost:8000/subdir
ProxyPassReverse /subdir http://localhost:8000/subdir

The “tricky” part here is to include the subdirectory in both the source and destination URLs of your proxy commands.

Anyways, that’s it. Launch your Mongrel (cluster), restart Apache, and you’re good to go.

Keep your acts_as_attachment models DRY

I think acts_as_attachment is my favourite Rails plugin. Not only does it provide some fundamentally necessary functionality (read: upload and manage files), it does some handy stuff like validate content types, maintain proper parent-children relationships for thumbnails, and comes packaged with a pretty thorough set of test cases. It totally upstages its predecessor, the file_column plugin by Sebastian Kanthak, which isn’t being actively maintained these days anyways.

If there’s one thing I like about Kanthak’s plugin though, and keep in mind this is also its greatest flaw, is that file_column attachments have a minimal impact on your database schema. You just need an extra column for the models you’re attaching files to – totally simple. Acts_as_attachment, on the other hand, wants separate tables for each of your attachment models. Here’s two taken from an imaginary online video store, complete with customer profiles (they’ve jumped on the “community driven” bandwagon):

class DvdCover < ActiveRecord::Base
  belongs_to :dvd # through dvd_id
  acts_as_attachment :content_type => :image
end

class Mugshot < ActiveRecord::Base
  belongs_to :profile # through profile_id
  acts_as_attachment :content_type => :image
end

If you use acts_as_attachment this way, you’ll need two tables named dvd_covers and mugshots. Personally, I’ve got enough tables as it is, and since they’re really both just images, I’d like to store them in one. The solution? Subclass from a generic Image model using the power of Rails’ single table inheritance.

class Image < ActiveRecord::Base
end

class DvdCover < Image
  belongs_to :dvd, :foreign_key => :owner_id
  acts_as_attachment :content_type => :image
End

class Mugshot < Image
  belongs_to :profile, :foreign_key => :owner_id
  acts_as_attachment :content_type => :image
end

Pretty simple huh? Now you’ve got all your images stored in a single ‘images’ table. This may not be for everyone, but I think it’s tidy.

In case you’re wondering, here’s the full database migration for the Image model.

create_table :images do |t|
  t.column "owner_id", :integer   # generic owner
  t.column “type”, :string        # holds the class

  t.column "content_type", :string
  t.column "filename", :string     
  t.column "size", :integer
  t.column "parent_id",  :integer 
  t.column "thumbnail", :string
  t.column "width", :integer  
  t.column "height", :integer
end

There’s just one more thing – we’ve broken the relationship between these image models and their owners. We’ve already fixed up belongs_to to use the owner_id foreign key; we need to do the same for the corresponding has_[one|many] clauses for Dvd and Profile.

class Dvd < ActiveRecord::Base
  has_many :dvd_covers, :foreign_key => :owner_id
  ...
end

class Profile < ActiveRecord::Base
  has_one :mugshot, :foreign_key => :owner_id
  ...
end

Note: Your uploaded DvdCover files will still wind up in /public/dvd_covers, and Mugshots in /public/mugshots.

This technique could be re-used for any number of different attachment types – audio files, video files, etc. For that matter, you could make all your files subclass from a a single generic Attachment model, but that might be overkill.

Legacy Rails - beware of 'type' columns

Earlier this week I was writing an app that had models connected to a number of legacy databases. To do this, I followed PragDave’s example of subclassing from ‘gatekeeper’ models that establish separate database connections. Without going into too much detail, here’s an example:

class Finance < ActiveRecord::Base
  self.abstract_class = true
  establish_connection(
    ActiveRecord::Base.configurations["finance_#{ENV['RAILS_ENV']}"]
  )
end

class Receipt < Finance
end

class Income< Finance
end

Looks good, but I hit a snag – one of my legacy tables had a ‘type’ column defined. When Rails sees a column named ‘type’ for models that aren’t immediate children of ActiveRecord::Base, it assumes that column holds the class name associated with your model. This is how Rails implements single table inheritance, and if that’s not what you intended, you’ve got a little extra work ahead of you.