Ruby - - By Mal Curtis

Confessions of a Converted PHP Developer: Animal Abuse

Sometimes the functionality of a library or set of classes that you’re working with is 99% perfect for the job, but the last 1% requires modification of some of the core assumptions made in the code. Altering code can cause maintenance frustrations down the track and extending code can cause maintenance frustration right away, but Ruby provides us with a flexible way to modify code with less damage.

In converting to Ruby I realized how strict PHP is in its class hierarchy. This surprised me as I’d always viewed PHP as a very loose language, mainly due to its loose typing and lack of formal code structure guidelines. The latter being inherently, in my opinion, due to the way that a lot of developers learn the language. I find most PHP developers learn starting by using inline PHP as a low learning curve entry point into dynamic web languages and then move on to fuller, more complex, applications.

With PHP, your class hierarchy is very much a linear progression, with the occasional injection from the side. What I mean by that is you will start with a class, possibly declared abstract, and work your way up to an end class with extension after extension, and along the way you might implement some interfaces (that’s the injection from the side bit). This is a very straight forward structure, without any way of dynamically altering this process.

Ruby provides the same extension inheritance structure, but it also allows you various not so rigid ways to alter classes — as we’ve seen in the past with the mixin structure. But Ruby goes even further and allows you to redeclare classes. If you did this in PHP, you’d get a rather strict telling off.

<?PHP
class foo
{
	public function hello_world()
	{
		echo "Hi";
	}
}

#somewhere else in my codebase
class foo
{
	public function hello_world()
	{
		echo "Hello world!";
	}
}

To start with I’ve created a class called foo, with a hello_world method. But then elsewhere in my code, I want to change my foo class and its hello_world method to output a slightly different message. Unfortunately, the above code will output PHP Fatal error: Cannot redeclare class foo. Not good. To get this behaviour, we would need to extend our class and, in doing so, introduce a lot more complexity.

<?PHP
class bar extends foo
{
	public function hello_world()
	{
		echo "Hello world!";
	}
}

Voila, we have now got a class called bar which has the correct hello_world method. This is perfect yea? No — it’s not.

The problem with this style of coding is that to acheive the outcome we want, we’ve had to create a completely new class to use. If we already have a codebase that is using the foo class, we’re up a creek without a paddle and would have to find / replace throughout the entire codebase. While this might be trivial for a small codebase, it could turn into quite an exercise for a larger codebase. Let’s have a look at a common scenario with a request / response situation — ignore the fact that this code calls functions which clearly don’t exist and wouldn’t work if they did!

<?PHP
class API_Request
{
	public function get($resource)
	{
		$content = magically_get_resource_content($resource);
		$status_code = magically_get_resource_status_code($resource);
		return	$this-&gt;_build_response($content, $status_code);
	}

	protected function _build_response($content, $code)
	{
		return new API_Response($content, $code);
	}
}

class API_Response
{
	protected $_response;
	protected $_status_code;
	public function __construct($content, $status_code)
	{
		$this-&gt;_response = json_decode($content);
		$this-&gt;_status_code = $status_code;
	}

	public function is_successful()
	{
		return $this-&gt;_status_code == 200;
	}
}

Here we can call get on API_Request, with a URL resource string, and get back an instance of API_Response with an is_successful method. This method will tell us if the status code of the http request was 200 or not. Now, say we wanted to change the functionality of the API_Response class to match the status code definitions of HTTP/1.1, and tell us that any status code in the 200’s is a success. To do this we’d have to either edit the underlying API_Response class, or extend it, and extend the API_Request class to call the newly extended API_Response class. All of this is a hell of a lot of work for such a simple change, especially when there’s the possibility of having to update code that uses these classes throughout your project.

Here I’m going to do this, replacing API_Response with API_Better_Response, and API_Request with API_Better_Request.

<?PHP
class API_Better_Response
{
	public function is_successful()
	{
		return $this-&gt;_status_code &gt;= 200 &amp;&amp; $this-&gt;_status_code &lt;= 299;
	}
}

class API_Better_Request
{
	protected function _build_response($content, $code)
	{
		return new API_Better_Response($content, $code);
	}
}

Now we have overwritten the _build_response method to correctly return the API_Better_Response class, but we’d also have to go through our application code and find all instances of API_Request and replace it with API_Better_Request.

Ruby to the rescue: Monkey Patching

Ruby has the ability to reopen a class anywhere, and override or add methods. It’s an extremely easy process — you simply declare the class again. Let’s have a look at the equivalent Request and Response classes in Ruby — again ignoring the magical mystery code which doesn’t exist.

module API
	class Request
		def get(resource)
			content = magically_get_resource_content(resource)
			status_code = magically_get_resource_status_code(resource)
			API::Response.new(content, status_code)
		end
	end

	class Response
		def initialize(content, status_code)
			@response, @status_code = ActiveSupport::JSON.decode(content), status_code
		end

		def is_successful
			@status_code == 200
		end
	end
end

Here we’ve got the same code, with the same incorrect is_successful method. Instead of doing any annoying class extension and alteration like we had to in PHP, we can simply reopen the class and change the method to do what we really want it to do.

module API
	class Response
		def is_successful
			(200...300) === @status_code
		end
	end
end

Now is_successful will return true if any 2xx http status code is returned. We’re asking if the range 200-300 (not including 300) loosely equals the status code of the request. Note that we haven’t touched any of the other methods and they will still be in their original form — we just overwrote the is_successful method.

This type of dynamic class alteration is often called Monkey Patching or, according to Wikipedia, Duck Punching. Apparently the naming is in the form of [animal] [past participle verb]. You can read up on the origins of the name at Wikipedia.

So we’ve seen that Ruby allows for easy and flexible dynamic alteration of classes at runtime, which is extremely useful. You can also make multiple monkey patches, but only calls to the code after it is patched will be affected.

I must note that Monkey Patching is often a quick fix solution that can create headaches for future developers (or for yourself, if your memory is like mine) for several reasons:

  • Class logic that is expected to be in the original file, is no longer where it’s anticipated to be — and is difficult to track down.
  • Code that you patch may no longer work if the underlying library or code is updated and the methods you patch are removed, or their behaviour changed.

I guess what I’m trying to say here is: Use at your own risk! You may find that with a little thought and refactoring you can achieve the same results in a cleaner way, and save future you, or future other developers from a few headaches along the way.

Do you use monkey patching in your code? Avoid it at all costs? Feel free to comment below with your reasons.

Sponsors