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 syntax sugar (6)

Saturday
Jan072012

Syntax Sugar #5 - A Quick CodeIgniter Caching Helper Function

Caching your data in CodeIgniter? Good. You should be.

I use memcached lots. CI has a fantastic caching driver built in (as of 2.0). Normally, the code you'll write looks like this:

$accounts = $this->cache->memcached->get('user.' . $user_id . '.accounts');

if (!$accounts)
{
    $accounts = $this->db->where('user_id', $user_id)->get('accounts')->results();
    $this->cache->memcached->save('user.' . $user_id . '.accounts', $accounts);
}

$this->load->view('accounts/index', array( 'accounts' => $accounts ));

However this quickly becomes repetitive, boring and cluttering. With a bit of PHP5.3 magic, we can create a really nice little caching helper function that cleans this code up in no time:

function cache($key, $data) {
    $CI =& get_instance();
    $cache = $CI->cache->memcached->get($key);

    if (!$cache) {
        // There's been a miss, so run our data function and store it
        $cache = $data($CI);
        $CI->cache->memcached->save($key, $cache);
    }

    return $cache;
}

Now, our cluttered caching code becomes a commendable concoction of coding culinary craft:

$accounts = cache('user.' . $user_id . '.accounts', function(&$ci){
    return $ci->db->where('user_id', $user_id)->get('accounts')->results();
});

The anonymous method you pass through will only be executed if the cache misses (i.e. there's no data under that key, or it has expired). It will be saved into the cache and return. Delicious.

Monday
Sep122011

Syntax Sugar #4 - CoffeeScript one-liners

I'm a sucker for short, concise syntax. One of the beautiful features about CoffeeScript - the JavaScript/Ruby lovechild that I'm slowly learning to adore - is its single line function declaration. Using this powerful syntax one can write really expressive functions with terse code.

Let's demonstrate this with a quick function. Let's say we want to remove a specific element from an array. My first guess would be to write:

var arr = [ 1, 2, 3, 4 ];
delete arr[0]; // [ nil, 2, 3, 4 ]

The issue with this, of course, is that we are left with a stray nil. That's not particularly convenient if we're using the array a lot. Instead, let's write a function and patch it onto the Array class.

Array::remove = ( element ) -> 
    if (@indexOf(element)) > -1
        @splice @indexOf(element), 1

We're using the built-in splice() method to remove the element at the index of our element. That isn't such a bad first attempt, but it's not particularly terse or pleasant. Let's remove the repetition and move index's assignment into the conditional:

Array::remove = ( element ) ->
    if (index = @indexOf(element)) > -1
        @splice index, 1

We can use CoffeeScript's keyword syntax and bracketless method calling to clean it up a bit further:

Array::remove = ( element ) ->
    if (index = @indexOf element) isnt -1
        @splice index, 1

And finally we can push it all into one line:

Array::remove = ( e ) -> @splice i, 1 if (i = @indexOf e) isnt -1

If we compare this to the JavaScript it generates:

Array.prototype.remove = function(e) {
    var i;
    if ((i = this.indexOf(e)) !== -1) {
        return this.splice(i, 1);
    }
};

…it's a no-brainer. CoffeeScript is a wonderful way of making JavaScript cleaner, more concise and altogether much more pleasant to work with. I'd highly recommend downloading a copy and trying it out.

Monday
Jul252011

Syntax Sugar #3 - Easily Parsing HTML

The PHP SimpleXML extension makes parsing and using XML documents in your code a piece of cake. Unfortunately, HTML rarely complies as a well-formed XML document.

Using SimpleXML combined with DOMDocument, we can parse a reasonably badly formatted HTML document in very few lines of code. The trick here is using the DOMDocument::$strictErrorChecking variable to ensure that the source is parsed as dodgey HTML.

<?php
// Some HTML string...
$html = file_get_contents("http://codeigniter.com");

// Create a new DOMDocument and set strictErrorChecking to FALSE
$dom = new DOMDocument();
$dom->strictErrorChecking = FALSE;

// Load the HTML into the DOMDocument
$dom->loadHTML($html);

// Load the DOMDocument into SimpleXML... and win!
$obj = simplexml_import_dom($dom);
Saturday
Apr092011

JavaScript Iterators

Whenever I’m using any other programming language I really miss PHP’s foreach loop. It seems like a brilliantly elegant solution to the problem of looping through an array of elements and pulling out the key and value.

One of the things I’ve always hated in JavaScript is exactly that; looping through an array of elements and doing something.

Let’s say we have this array of data:

var codeigniters = [
    'Jamie Rumbelow', 'Jeremy Gimbel', 'Phil Sturgeon', 'Elliot Haughin'
];

I used to use the native JavaScript for in loop, which looks like this:

for (var index in codeigniters) {
    $("#codeigniter_masters").append(
        "<li>" + codeigniters[index] + "</li>"
    );
}

This works fine, but it’s horribly verbose. I also despise the fact that it assigns the iterating variable to the index of the current array element. It’s stupid. I will generally never want just the index. Give me the element itself, for goodness sake.

I’m embarrassed, but I’ve only just discovered there’s a much better way of doing this. Arguably, JavaScript’s most powerful feature is the anonymous function, and it comes to our rescue here in the form of jQuery.each();

$.each(codeigniters, function(index, codeigniter){
    $("#codeigniter_masters").append("<li>" + codeigniter + "</li>");
});

That is much, much nicer. It is cleaner, easier to read and makes more sense. And after all, the first of Rumbelow’s rules of good code is every line should make sense.

Wednesday
Jan262011

Syntax Sugar #2 - Model Scoping

I've written about model scoping before, but I thought I'd revisit it with some lessons I've learnt since last time. The essence of model scoping is that you can chain named methods together that add certain parameters onto your query. In a similar way that CodeIgniter's ActiveRecord allows you to chain methods together to build queries, you can use model scoping to incrementally add onto your model query.

The trick to writing a scope is to return $this. This lets PHP chain the methods onto one-another, resulting in a nice syntax. Here's an example:

class User_model extends CI_Model {
    public function __construct() {
        parent::__construct();
    }

    public function confirmed() {
        $this->db->where('confirmed', 1);
        return $this;
    }

    public function sorted($column = 'date') {
        $this->db->order_by($column);
        return $this;
    }

    public function get() {
        return $this->db->get('users')->result();
    }
}

With these methods defined, we can produce some nice, readable code to pull the results from the database.

$this->user_model->confirmed()
                 ->sorted()
                 ->get();

Comparing this to what we'd write if we didn't define those lookups as scopes:

$this->db->where('confirmed', 1)
         ->order_by('date')
         ->get('users')
         ->result();

It's obvious that the former is much cleaner and more readable. We can add or remove as many as we like, and they can be as comprehensive or as simple as we like. It doesn't even have to be limited to database lookups. We're sharing an instance of the class, so we can add things to an instance variable which we can then print, or we can perform other data-related processes all scoped out by our method scopes.

While this is generally all rosy, you may come across something like this:

public function favourited() {
    // Get the favourites
    $fav = $this->db->select('id')->get('favourites')->result();
    $ids = array();

    foreach ($fav as $row) {
        $ids[] = $row->id;
    }

    // Restrict the current query
    $this->db->where_in('id', $ids);
    return $this;
}

This will only work if it's called first. You can easily get conflicts in SQL queries when you're chaining and making separate queries inside the scopes. You can start to look for columns that don't exist, or affect the query in some other bad way.

The solution to this problem is to isolate the query. Take the current values from the class you're accessing (in our case the CodeIgniter ActiveRecord class), cache them, clear them, make the query, and then reset the variables to their original state. This makes sure the query starts with a blank slate.

public function favourited() {
    // Cache the CodeIgniter ActiveRecord values
    $ar_values = array(
        'ar_select', 'ar_distinct', 'ar_from', 'ar_join', 'ar_where', 'ar_like', 
        'ar_groupby', 'ar_having', 'ar_limit', 'ar_offset', 'ar_order', 'ar_orderby', 
        'ar_set', 'ar_wherein', 'ar_aliased_tables', 'ar_store_array'
    );
    foreach ($ar_values as $val) { $$val = $this->db->$val; $this->db->$val = null; }

    // Get the favourites
    $fav = $this->db->select('id')->get('favourites')->result();
    $ids = array();

    foreach ($fav as $row) {
        $ids[] = $row->id;
    }

    // Restore the AR values
    foreach ($ar_values as $val) { $this->db->$val = $$val; }

    // Restrict the current query
    $this->db->where_in('id', $ids);
    return $this;
}

Now we can use the favourited() scope without fear!

$this->user_model->confirmed()
                 ->sorted()
                 ->favourited()
                 ->get();

As you can see, scopes are a powerful and flexible way of creating really, really nice semantic and clean code. I encourage you to start using them today.