Reflections on Ruby from a PHP Developer

Reflections on Ruby from a PHP Developer October 10th, 2016, by Matthew Setter

I’ve been looking to break away from PHP and web-based development for some time now. But doing so has been a case of easier said than done. Recently, though, I’ve managed to make a breakthrough.

I’ve been talking about being a polyglot, primarily as I’ve spent so much time using PHP since near the start of my professional career.

Like many things in life, especially for me, talk can be more present than action. But yesterday I had reason to put the words into action, by having an itch to scratch.

Specifically, I had the situation where I was refactoring a shell/Ansible provisioning process and had to upgrade the installation of Ansible to a minimum version of 2.0 if the installed version wasn’t already there.

At first glance, this might not seem like a reason to start learning a language. But it was enough; here’s why. To find out the currently installed version of Ansible, I ran the following shell script:

dpkg -s ansible | grep 'Version' | awk '{print $2}'

If you’re not familiar with the Linux command line, or some of those commands, what it does is use dpkg, which handles installing, removing, and providing information about .deb packages, to get the status of the current package.

This contains quite a lot of information about it, but all I need is the version. The version is rendered on a line beginning with the string Version, and looks something like this: Version 2.2.0.0-1ppa~trusty.

So the package status is piped to grep to filter out anything other than that line. This, in turn, is piped to awk, as I only want the actual version details, not the Version prefix.

At that point, I have the version. But, since it’s not a numeric value, I can’t ask a question such as “Is this at at least version 2?”.

There Are Other Ways To Solve This Situation

On thinking about it, there are a whole host of ways in which I could have done this, such as refactoring the original command to store the value, like this:

version=$(dpkg -s ansible | grep 'Version' | awk '{print $2}’)

With the string stored as a variable, I could then have use substring expansion to validate whether it was at a minimum version of 2, such as in the following test:

[[ ${version:0:1} -ge 2 ]]

But I’m a developer and felt that it was worth investing a bit of extra effort. For what it’s worth, it was quite a minimal amount of effort.

And — sometimes — you have to go beyond your normal, safe, happy confines and try new things. So for a mix of reasons, I did.

Let’s Give Ruby A Go

Given that the provisioner already used Ruby, I decided to take a shot at learning Ruby. Doing some quick searches on already installed packages in the virtual machine, it seemed that the package version naming convention followed semantic versioning.

If you’re not familiar, semantic versioning represents a package version using the format MAJOR.MINOR.PATCH: which means:

  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards-compatible manner, and
  • PATCH version when you make backwards-compatible bug fixes. Additional labels for pre-release, and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

Given that, I did a quick search for a Ruby package (gem) that would take care of most of the legwork for me. The first result, and the one I used, was SemVersion.

SemVersion parses, validates, modifies, and compares semantic version strings. Perfect!

Running gem install sem_version made it available. Now, to code up a quick solution using it. If you’re interested, you can find it on GitHub.

The Ruby Script - In a Nutshell

This is a very basic Ruby script which supports one function, satisfies, which compares one version string to another, which you can see below.

def satisfies(required_version, current_version)

  satisfies_version = false

  if SemVersion.valid?(current_version)
    satisfies_version = SemVersion.new(current_version)
      .satisfies?(">= #{required_version}")
  else
    ver = /(\d\.\d*\.\d*)/.match(current_version)

    if ver != nil
      satisfies_version = SemVersion.new(ver[0])
        .satisfies?(">= #{required_version}")
    end
  end

  satisfies_version

end

It first checks if the version string supplied is a valid semantic version. If it is, then it uses SemVersion’s satisfies method to see if it’s greater than or equal to, the required version.

If it’s not a valid semantic version string, then it uses Ruby’s regular expression functionality to check if the string supplied starts with three numbers which are dot-separated.

If it does, then it extracts it into a new variable, called ver, and calls SemVersion’s satisfies method, as before.

If, however, the string doesn’t match the expression, indicated by ver set to nil, then the version’s unable to be determined.

As I said at the start, the script’s not designed to be comprehensive. It was developed to scratch an itch I had at the time.

Given that, if you look at it with a critical eye, you’ll see a lot of room for improvement, aside from many other ways to achieve the same result; that’s fine by me.

What I Was Out To Achieve

As a, primarily, PHP developer coming to Ruby I was, personally, much more interested in learning the basics of Ruby, such as:

  • How you name methods, classes, variables, and packages
  • How it implements the standard lexicographical constructs
  • Idioms
  • Code styling

By having a project which had enough depth, one able to be completed quickly, yet not overly demanding, I was able to achieve this.

Quick note: while I developed the code in VIM, I have to give a hat-tip to the inspections in RubyMine, from JetBrains, for helping make the code that much cleaner.

It indicated that I could simplify expressions, use single, instead of double, quotes, and avoid unnecessary statements.

If you’ve not used a JetBrains product yet, you don’t know what you’re missing! No, I’m not paid to endorse them.

Reflections on Ruby from a PHP Perspective

Coming from PHP, I have a set of preconceived ideas and ways of working, a set of norms that frame my outlook on code. While I don’t have any issues with that, looking at code through the eyes of a different language was very refreshing.

Here are some of the things I learned.

1. Braces And Semicolons

I don’t miss having to use opening and closing braces all the time, nor having to terminate an expression with semicolons. Sure, there’s end’s everywhere where the closing brace would be. But the code feels a little cleaner.

2. Everything Is An Object

That everything is an object is also handy, because you can then call a standard set of functions on the objects, in a fluent-style, without having to add that functionality yourself. Take the code below as a reference point:

version = open(filename).read.strip

Here, I’ve been able to:

  1. Open a file, which contained a semantic version string
  2. Read the contents into the variable version
  3. Strip off newline characters

In PHP, I’d likely have used something like this:

$version = trim(preg_replace(
  '/\s\s+/', '', file_get_contents(filename)
));

While it’s not that much longer, the Ruby version is extremely compact and efficient.

At this point, I’d understand if you, quite fairly, said that this isn’t a fair comparison. You’d be right. It’s but a small snippet, clearly biased in Ruby’s favour.

But in that small snippet, and in the process of writing the code which I’ve stepped through I’m pretty impressed.

3. Ruby’s Very Expressive, Very Fluid

There seems to be a more fluid, expressive nature to how you write Ruby code, which I don’t find when writing PHP. The process seems more intuitive.

What’s more, being able to use external code via Gems makes life very easy too. It’s very analogous to PHP’s Composer. From here,

The Journey’s Just Beginning

I’m hoping to learn a lot more about how Ruby packages code, I guess Gems do that. I’m hoping that as part of the process, I can get rid of the require statements.

That said, I’m not going to make any further comparisons, as I don’t have near enough of a feel to do so fairly.

A Quick Thank You

With all of this said, a special thank you has to go to Learn Ruby The Hard Way. That was my go-to resource, continuously open while I was writing the code. From what I’ve seen, it’s an excellent resource. It’s very well laid out, and very well structured.

The other thank you has to go to the RSpec package. If you want to learn a language, an excellent way to do so is by writing test-driven code.

I didn’t write exhaustive tests, but I did include some, using RSpec, which you can find in the repository.

In Conclusion (tl;dr)

If you’re a PHP developer and looking to learn another language, to approach software challenges from a different perspective, then I whole-heartedly encourage you to learn Ruby.

It’s a well-designed language, one which (at least from what I’ve learned) is very rich, very expressive, and yet very compact.


Like That?

Don’t miss my next post. Drop your email in the box below, and get it straight to your inbox, PLUS exclusive content only available by email. No spam, and you can unsubscribe at any time.