🤯 50% Off! 700+ courses, assessments, and books

Confessions of a Converted PHP Developer: Ugly Code

Mal Curtis

I’ve been a PHP developer for nearly a decade, and in January this year I started developing in Ruby. While initially a sceptic, I’m now a convert—and I’m here to share my experiences with you. I’m far from claiming to be a gun Rails developer; I’m new to Rails, which is kind of the point of me writing this. I may not call everything by the official name, but you’ll get the gist of it.

The idea of change can often be scary. Moving to a new language from the safety of the language you know and trust shouldn’t be done lightly. I’d like to think of myself as a fairly good PHP developer, and the idea of moving to Rails was both scary and exciting. I made the plunge because of a new website we’ve developed at work and I can now confidently say I’m a convert to the Ruby way. The language and structure of Ruby is beautiful, and the speed of development that Rails provides makes my life that much easier.

It hasn’t been an easy transition. My colleagues can attest that I’ve had a fair few days swearing blinding at my laptop in frustration, usually while trying to do simple tasks. In Ruby’s defence, these are frustrations that are well made up for with the delights of finding out how easy other things are to do—and any learning process has its frustrations. It’s easy enough for me to wax lyrical about this, but let’s look at an actual example.

Ruby: Objectified

Everything in Ruby is an object. Everything. Even nothing is an object; nil is an instance of NilClass. This means that rather than the procedural “nesting,” or endless variable creation you’d have to perform in PHP, you can simply chain your method calls when dealing with any sort of variable. “Oh, that’s great, but what does that mean for ME?” I hear you ask. Well, read on.

Say that I have a string of comma-separated email addresses, and I want a list of all the unique domain names from that string. It would turn into quite a substantial process in PHP:

  // Our list of emails
  $string = "mal@sitepoint.com, mal@learnable.com, kevin@sitepoint.com";
  // Explode into an array of emails
  $emails = explode(', ', $string);
  // Create a function to remove anything which isn’t the domain portion of the email
  $leave_domain = function($subject){
      return preg_replace("/^(.*)@/","", $subject);
  // Apply the function we just created to the emails array
  $domains = array_map($leave_domain, $emails);
  // Remove any repeated domains
  $unique_domains = array_unique($domains);

Which gives us:

    [0] => sitepoint.com
    [1] => learnable.com

So I start by exploding our string into an array; then I map a function that replaces anything except the domain part of the email, ensuring we only have unique values.

Note: I used an anonymous function for the array_map call. This would only work in PHP 5.3. You could use a foreach loop as an alternative.

That’s a fair bit of code for a simple concept. You could bring it all into one command by nesting the functions; however, this would be at the cost of readability:

  $string = "mal@sitepoint.com, mal@learnable.com, kevin@sitepoint.com";
  $unique_domains = array_unique( array_map( function($subject){ return preg_replace("/^(.*)@/","", $subject); }, explode(', ', $string)));

Now that’s some damn ugly code. Try explaining that to someone without giving them a headache! The main issue here is that it has to either be nested and lose readability, or contain a veritable bunch of variables to separate out each part of the process.

In Ruby, because everything is an object, we can chain the function calls together:

string = "mal@sitepoint.com, mal@learnable.com, kevin@sitepoint.com"
unique_domains = string.split(', ').map{ |e| e.gsub(/^(.*)@/,'') }.uniq

So now it’s quite obvious what’s happening. We split the string into an array, and map our replacement to each element in the array (gsub is the Ruby regular expression replacement method). Then we return only the unique values.

The beauty is that the process that occurs is the same as how you read the line. Admittedly I’m making a huge assumption here, but it reads like your normal language. Because the string variable contains an instance of the string object, we just start calling methods that belong to the string class.

This is one of the fundamental differences between Ruby and PHP, and one of the reasons why Ruby is such a great language to work with.

Will this work?

One of the biggest frustrations I’ve had while learning Ruby is my sheer lack of knowledge when it comes to knowing which functions are available, and their exact specifications or requirements. When I was learning PHP, the easiest way for me to figure this out was by coding as I wrote; that is, by trial and error. This was a relatively laborious task, and really is an efficient way to code.

Once you have Ruby installed, you get access to the wonderful Interactive Ruby Shell, or IRB for short. Anyone who’s looked into Ruby has most likely heard about it, but you may not realize how freakin’ useful it is. It’s the default place you go to answer your simple “Will this work?” questions. You can easily tap into an IRB session by typing into your console the magic three letters `irb`, and you’ll land at a command prompt. Here you can basically do anything you’d do in a normal Ruby script, one line at a time. If anything go wrong, just press “up” and fix your problem! PHP lacks anything like this out of the box, so this is a new concept to most PHP developers.

While writing the above examples, I used IRB to make sure the syntax was correct. I started a new IRB session, and set my string variable:

irb > emails = "mal@sitepoint.com, mal@learnable.com, kevin@sitepoint.com"
 => "mal@sitepoint.com, mal@learnable.com, kevin@sitepoint.com"

The first part there, all the way up to and including the >, is simply my IRB prompt. Next up, I’ve set the variable `emails` to a string, pressed enter, and IRB has outputted the response of the previous line; this simply tells me it’s a string.

My next action was to split the string into emails, and replace the name part of the email string:

irb > emails.split(', ').gsub(/^(.*)@/,'')
NoMethodError: undefined method `gsub' for ["mal@sitepoint.com", "mal@learnable.com", "kevin@sitepoint.com"]:Array

Two things happened here. The first is that I’m using the variable I previously created, which is all good—that’s available to me for this session. Second, I ran split on the string variable, and then tried the gsub method on it.

Ruby has subsequently, and correctly, spewed out an error at me: gsub isn’t a method that you can run on an array. You need to run gsub on each of the elements in the array, rather than the array itself. Luckily, arrays do have a function that can help me: map. The map function takes a block and runs it on each of the elements in an array.

irb > unique_domains = emails.split(', ').map{ |e| e.gsub(/^(.*)@/,'')}
 => ["sitepoint.com", "learnable.com", "sitepoint.com"]

Voilà! Using the map value, we’ve correctly created an array containing only the domains that were in the email addresses. From there it was easy enough to add the uniq method, and boom—unique domains from a string of email addresses.

By doing this through the IRB, I was able to easily see the answers to my “Will this work?” questions and debug the code until it did.

Note: Blocks are cool. They’re one of the great features of Ruby, because they’re powerful and flexible, but they’re outside the scope of what I’m talking about. I’ll tackle blocks in an upcoming blog on this site, so if you don’t know about them already, stay tuned to RubySource.

If you want to test out code specific to Rails, you can use the Rails console; this is similar, except that it loads the entire Rails environment. In your console window, make sure you’re in a Rails project directory and type `rails console` or the alias `rails c`. It’ll take a second to load, but once you’re in, you’ll have access to all your project’s models, helpers, and so forth in the Rails environment.

mal@mal:~/my/rails/project$ rails c
Loading development environment (Rails 3.0.5)
ruby-1.9.2-p180 :001 >

This is just one tip that I’ve got about starting out in Ruby when coming from PHP. I have a few more up my sleeve. Have you started in Ruby or Rails recently? If so, what tips can you share?