PHP vs. RUBY: What’s the Point?

Dave Kennedy
Share

You will no doubt have read one of the many articles out there comparing the merits of PHP against Ruby, or more commonly to my exasperation, PHP vs Rails. I hope, like me, you find such articles pointless exercises and nothing short of language trolling. It’s just not a level playing field.

Somehow developing for the web brings these two different beasts together for comparison. PHP a.k.a Personal Home Page Tools (thanks wikipedia) is a language/framework developed specifically for the web. Ruby on the other hand is a general purpose language conceived by one man Matz, in a quest to create a utopian programming language.

Starting out in PHP, you will probably create a few pages with code laced in the HTML. Then after realising how painful that can be to maintain, you will start abstracting your business logic and presentation layers using something like Smarty. You will get a bit more object orientated and no doubt grab something like Zend Framework or CodeIgniter to develop your own applications. Hopefully, that all sounds familiar.

When it comes to Ruby, so many developers ignore such a sensible path of development. I know I certainly did. Where do most people start with Ruby, myself included? Rails, of course. We watch the “build a blog” video and, presto, we are all sold. I would never discourage anyone from picking up Rails and running with it, but it’s not Ruby for beginners. When you use Rails, a lot of great developers have spent a lot of time abstracting all the horrible nitty gritty stuff away. Migrations just work, Routing just works, logging and testing and right there for you to use. Rails is a framework that gets out the way and lets you focus on the problem you want to solve.

Hello Rack

One good reason to start with Rails is, when it comes to developing for the web, Ruby on its own is nothing short of intrusive. Sure we can use the standard library CGI class, upload the file to the server, make it executable and we are done. Compare that with a PHP script.

Even DHH blogged about the immediacy of PHP. Your gratification is instant. Want to test a quick bug fix? Just hit refresh on the browser. None of this restarting mongrel, Passenger, or whatever is required.

So how can we get to that kind of instant Ruby web apps without resorting to rails s. Well, how about we use the framework Rails itself uses? Rack.

Rack is the interface between Rails apps and the HTTP protocol. It is also the basis of pretty much all Ruby web frameworks, Sinatra & merb included.

Rack incorporates all that low level code that framework developers were duplicating across projects. It basically scoops up any web server available and uses it to serve your apps (by web server we are talking mongrel, WEBrick, thin and so on).

To get started with Rack it’s simply a case of installing the gem, creating a rackup file (*.ru), and starting the app.

The hello world of Rack looks like the following (hello.ru):

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/html"}, ["<h1>Hello world!</h1>"]]
  end
end

run HelloWorld.new

Then in the console, rackup hello.ru. You will see a bit of server output with the port the server has started on (usually 9292). Just navigate to http://localhost:9292 and see the glory.

To dissect this simple application (and it is an application), basically we have a method named call that receives the environment and returns an array of three things, status, headers and body. By environment we are not talking staging, production etc. instead it’s the more CGI set of variables we see in PHP’s $_SERVER super global, REQUEST_METHOD etc.

The status codes are pretty self explanatory and the contents of the headers hash will also be familiar. The body we see in the hello world example is an array, or more specifically, it must respond to each. Finally, at the end of the file we see the run method spinning up an instance of our hello world application.

So the basic rules of a Rack application are it must have a method call that accepts the environment hash and returns status, headers and body, and body must respond to each.

Echo ‘Hello World’

We have seen a basic Rack application, but how does that compare to the simplicity of:

<? php
echo "<h1>Hello World</h1>";
?>

At face value, it certainly seems more convoluted, so let’s look at what Rack gives you to make it more attractive.

The Builder

When it comes to hello world PHP is pretty hard to beat. Luckily for the Rubyist in us, hello world applications are in low demand, and Rack comes with a whole lot more than wrapping servers. Rack itself ships with many micro Rack applications that will assist us in building our frameworks. They include all the helpful stuff like logging, sessions, url mapping and so on.

One of the absolute gems of Rack has to be Rack::Builder. This is a Domain Specific Language (DSL) that allows to construct and mash together Rack applications easily. Consider building a PHP application that has a public page and a secret page. In PHP we could create two files that perform these duties. An alternative would be to create a .htaccess file that directs all incoming requests to a single file like so.

RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ /index.php?page=$1 [L]
<?php
if ($_SERVER['REQUEST_URI'] == '/secret') {
  echo "Shhhh";
} else {
  echo "This is public";
}
?>

Not too bad, its implementation using Rack::Builder could look something like:

app = Rack::Builder.new do
  map "/" do
    run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["This is public"]] }
  end

  map "/secret" do
    run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhh"]] }
  end
end

run app

Pretty neat? What we have done here is implement Builder to map urls to given actions. These actions are just Proc just now as we find our feet, they return the golden trio we seen in our hello world app.

This is infinitely scalable as well. For example, let’s look at a path such as ‘/secret/files’. Our PHP version gets hairy enough to warrant a rethink (we dont want to go down the line of adding files to relative directories do we?), in Rack we simply nest some map blocks.

map "/secret" do
  map "/" do
    run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhhh"]] }
  end

  map "/files" do
    run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Here be dragons"]] }
  end
end

Hopefully, you are nearly sold on Rack. While we are still feeling the love, let’s spice it up a bit by adding some more kinky Rack toys.

Rack = Damn Sexy

We mentioned before that Rack is more that a server interface. It comes with a wealth of “components” which are themselves Rack applications. Now, we will look at how we can implement a couple of these.

I always find logging helpful when developing applications.

require 'logger'

  app = Rack::Builder.new do
  use Rack::CommonLogger
  Logger.new('my_rack.log')

  map "/" do
    run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["This is public"]] }
  end

  map "/secret" do
    map "/" do
      run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhhh"]] }
    end

  map "/files" do
    run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Here be dragons"]] }
  end
end

run app

We have been talking about secret areas and dragons, so we better lock all that up. HTTP Basic Authentication is always good for securing things.

require 'logger'

  app = Rack::Builder.new do
  use Rack::CommonLogger
  Logger.new('my_rack.log')

  map "/" do
    run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["This is public"]] }
  end

  map "/secret" do
    use Rack::Auth::Basic do |user, password|
      user == 'super_user' && password == 'secret'
    end

    map "/" do
      run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhhh"]] }
    end

    map "/files" do
      run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Here be dragons"]] }
    end

  end
end

run app

That was incredibly easy, I remember the days of setting up .htpasswd files and so on.

Rackup

I started this article as if it were a PHP vs Ruby nonsense. And throughout the article I have made references about how we could do this in PHP and compared it to Rack. It was all a cunning rouse, preying on the language troll in all of us. Fact is, both have merits and even comparing Rack with PHP is hardly fair.

I hope this has given you a taste of how flexible, maintainable, and joy-inspiring using Rack is. It’s a great place to start when learning Ruby because there is enough ‘magic’ to keep our interest, but not enough to obscure learning.

We have not finished there though. You will remember all the big frameworks are built on top of Rack. We can actually implement mystical middlewares using Rack that intercept the normal flow of our applications and temporarily hand control to Rack applications. Could be scary, but Rails loves it.

CSS Master, 3rd Edition