About Jamie on Software

Jamie on Software is the online journal of web developer and writer Jamie Rumbelow.

Jamie likes books, guitars, programming, open source and food. He writes about these things too. This is where he puts the things he writes.

Tags
Tweets
Feeds
We Love
Powered by Squarespace

Entries in programming (4)

Monday
Apr302012

Open source (pour over pasta)

I've been making a real effort to tidy up my open source portfolio recently; getting issues closed, code tidied up, unit test suites written and on the fantastic Travis and people happier!

As part of this transition, I've decided to retire Sparkplugs and open source the code. Big changes are happening in my professional and personal life over the next few months and I feel it would be disingenuous to pretend that I could carry on supporting and developing my formerly commercial add-ons. After all, there haven’t been any updates to my add-ons for a while now, and support has been flakey. The honest truth is I lost my passion.

But onto bigger and better things!

Exciting things are going on over at my GitHub page (https://github.com/jamierumbelow). You’ll find my two most popular CodeIgniter libraries, MY_Model and MY_Controller. You'll find a recent experiment with node.js, Postmaster, an SMTP testing server I'm remarkably proud of. 

Also, you'll find the source to Sparkplugs' two commercial add-ons, Taggable and MojoBlog. I’m sure I’ll still be involved and am happy to answer any support questions through the issue trackers on the GitHub repositories.

It's an exciting time to be in open source and I've realised that I enjoy the atmosphere so much more than the pressures of a commercial add-on retailer.

So go on. Check out my GitHub profile :)

Friday
Apr012011

A radical new shift in data storage; Rumblestorage

Last night I had an epiphany. I was working with one of those old, cruddy databases, and it got me thinking. Why isn't there a better solution? Why am I doomed to use legacy systems such as MySQL and MongoDB for the rest of eternity? So, I began researching alternatives. And when I couldn't find any, I chose to write my own.

I am proud to announce Rumblestorage. From now on, every project of mine will be using Rumblestorage as its data storage medium. I hope that you too can see the light and understand that, frankly, databases just don't cut it any more.

Rumblestorage utilises the native capabilities of your language and stores everything in a flat text file; a simple and elegant solution. It brings your data into memory at class instantiation and saves it on destruction. It has a beautifully usable API. It's clear that in this 21st century world, databases are just far too restrictive. Your storage engine should be built specifically for your programming language; it should be fast, easy to use and above all flexible.

Best of all, Rumblestorage is free and open source, available on GitHub. I'm already in talks with the CodeIgniter Reactor team as to integrate Rumblestorage as a replacement for the old and tired database class.

Access the source code at GitHub, and feel free to fork and make your contribution to the future of data storage!

Tuesday
Jan112011

Enabling multisite search: params in ExpressionEngine 2

For a customer of Taggable, I investigated the age-old problem of searching across MSM sites using the {exp:channel:entries} tag and the search: parameter. It turned out to be a relatively simple fix, so I wrote a little how-to guide over on the ExpressionEngine forums. Check it out and let me know how you get on!

Monday
Dec202010

You can deprecate *anything*

I've been reading through this very interesting list of feature requests for PHP, and one of the reasons of denial that the PHP team keep giving to some of these crucial ideas is that they "wouldn't be backward compatible". Now, this may be total conjecture, but my honest, educated opinion is that it is possible to make anything backward compatible. Aside from obscure circumstances, it's generally easy. In fact, I'd go as far as to say that deprecating code is one of the easiest things programmers can do.

Let's take a few bits of code and build a few examples to let me illustrate my point. Assume we're building some sort of CMS or other application that has a community surrounding it. This could be pretty much any open-source project, it really doesn't matter. Now, version 1 of our application has an emailing class that looks something like this:

class Email {
    public function send_email($email) {
        $this->_verify_email_format($email);
        $headers = $this->_build_headers($email);

        return mail($email['to'], $email['subject'], $email['message'], $headers);
    }

    protected function _verify_email_format($email) {
        foreach (array('to', 'subject', 'message', 'from') as $field) {
            if (!isset($email[$field]) || empty($email[$field])) {
                die("Error: the '$field' field is required");
            }
        }
    }

    protected function _build_headers($email) {
        $headers = array();

        foreach (array('to', 'subject', 'message') as $field) {
            unset($email[$field]);
        }

        foreach ($email as $key => $value) {
            $headers[] = $key . ": " . $value;
        }

        return implode("\r\n", $headers);
    }
}

Version 1 of our application uses this code absolutely fine, and all the add-ons created for our wonderful system can utilise this too. However, one of our developers comes up with a better idea for an email class. We all agree that his class has a better API, better syntax and will make for much cleaner and concise code. His class looks like this:

class Email {
    public $to = '';
    public $from = '';
    public $subject = '';
    public $message = '';
    public $cc = '';
    public $bcc = '';
    public $reply_to = '';
    public $additional_headers = array();
    public $sent = FALSE;

    public function __construct($config = array()) {
        foreach ($config as $key => $value) {
            if (isset($this->$key)) {
                $this->$key = $value;
            } else {
                $this->additional_headers[] = $key . ": " . $value;
            }
        }
    }

    public function send() {
        $this->_check_required_fields();
        $headers = $this->_build_headers();

        $this->sent = mail($this->to, $this->subject, $this->message, $headers);
    }

    protected function _check_required_fields() {
        foreach (array('to', 'subject', 'message', 'from') as $field) {
            if (!isset($this->$field) || empty($this->$field)) {
                die("Error: the '$field' field is required");
            }
        }
    }

    protected function _build_headers() {
        $headers = array();

        $headers[] = ($this->cc) ? 'Cc: ' . $this->cc : '';
        $headers[] = ($this->bcc) ? 'Bcc: ' . $this->bcc : '';
        $headers[] = ($this->reply_to) ? 'Reply-to: ' . $this->reply_to : '';

        $headers = array_merge($additional_headers, $headers);

        return implode("\r\n", $headers);
    }
}

I think we can all agree that the latter, although slightly more verbose, is a much, much nicer alternative. Now, we're all happy and preparing to go and swap out the old class with the new class, when a solitary add-on developer, browsing through the development forum, alone at night with an all-encompassing feeling of sequestration, cries out:

All my add-ons that use this class are going to break. Anybody else using this class in their development projects are going to have to rewrite big portions of their applications to get this to work. You can't just swap out the classes, they've got to be backward compatible!

Oh, maverick developer, we can't let you down! We'll have to find a better solution to this problem. A solution that allows new add-ons to use the new class and older add-ons to continue using the old class in peace, without disturbing them. This way, since we're busy, we won't have to redevelop the core of the app either, if we can make the code backward compatible, we'll be able to leave the old system in place and convert it when we have more time.

But how would you convert an API that looks like this...

$email = array(
    'to'        => 'sjobs@apple.com',
    'from'      => 'jamie@jamierumbelow.net',
    'subject'   => 'iPhone 5',
    'message'   => "Steve, I am bored with being the lead developer on the iPhone 5.",
    'reply-to'  => 'jamie@jamierumbelow.net',
    'cc'        => 'jive@apple.com'
);

$sender = new Email();

if ($sender->send_email($email)) {
    die('Email sent!');
}

...to something that looks like this...

$email              = new Email();
$email->to          = 'sjobs@apple.com';
$email->from        = 'jamie@jamierumbelow.net';
$email->subject     = 'iPhone 5';
$email->message     = "Steve, I am bored with being the lead developer on the iPhone 5.";
$email->reply_to    = 'jamie@jamierumbelow.net';
$email->cc          = 'jive@apple.com';
$email->send();

if ($email->sent) {
    die('Email sent!');
}

...while managing to retain backward compatibility? Well, it's actually incredibly easy. Simply take your new Email class and add a quick compatibility layer. We want to re-implement the send_email() to mimic the old API but use the new emailing system. It needs to take exactly the same format array and return a boolean response. This way it'll still work for the time being.

public function send_email($email) {
    foreach ($email as $key => $value) {
        if (in_array($key, array('to', 'from', 'subject', 'message', 'cc', 'bcc'))) {
            $this->$key = $value;
        } else {
            $this->additional_headers[] = $key . ": " . $value;
        }
    }

    $this->send();
    return $this->sent;
}

Hey, take a look at that. That was very easy. We now have a perfectly backward compatible API. Both examples of code above will work flawlessly with the new system. And we got that from an extra 11 lines of code. Not too shabby.

The majority of the time when you're deprecating something, you intend to replace it with something else that does the same job. This means that there is a usually a way of mapping the functionality across with only a little effort, because you've got that one-to-one situation. If you're removing something for good and don't intend on replacing it, mark it as deprecated and tell developers that you're doing so. Leave it in for a few versions, and then after a while, remove it.

The fact remains that deprecating functionality is just not that difficult, and you really shouldn't miss out on improving whatever code you've got in place because you're worried about backward compatibility. There are always ways around these sorts of problems.