April 23, 2009

Mapping what makes me happy

April 23, 2009 - Categories: life, sketches

Thinking about what makes you happy is a good way to tweak your life so that you do more of the things that make you happier.

Here’s an incomplete, not-to-scale map of things that make me happy. I started by brainstorming lots of things, then moving them around in the Inkscape drawing program (it’s like magnetic poetry with an infinite refrigerator door!) until order emerged. Also, reading the book Back of the Napkin helped.

I’ve divided into things I do with other people and things I do with myself, and I’ll add more as other things occur to me.

Happiness map - click for full size

What’s your happiness map? Here’s how you can figure it out:

  1. Take a whole bunch of sticky notes, index cards, or other things you can write ideas on.
  2. List all the things you do that you enjoy.
  3. Move them around until an order makes sense. I sorted mine in order of increasing happiness, and then I grouped them by type.

Drupal staging and deployment tips: It’s all code

April 23, 2009 - Categories: drupal

As I talk to more and more developers about practices for working with Drupal, I get the idea that the staging and deployment process adopted by my team isn’t widespread.

Many developers make their changes directly through the web-based interface of a testing server, or even on the production site itself. I think that’s both tedious and scary. =)

Putting all of our behavior-related changes in update function in the install file makes it easier to merge changes and repeatedly test upgrades. Editorial changes (fixing typos, etc.) can happen on the site, but if it’s behavior-related code, it should be in the code repository. In moments of weakness, we’ve made web-based changes to our site, and we almost always regret those–either right away because things broke, or when we try to reconstruct our changes.

The Module Developer’s Guide documents how to write .install files for Drupal 5 and Drupal 6, but doesn’t go into much detail about what else you can put in the update functions. Maybe that’s why many developers use install files only for database-related changes. But you can do so much more: creating nodes, adding permissions, enabling other modules, and so on. 

Watch out for these potential pitfalls:

  • If your update works if you apply them one at a time, but not if you apply all of them in one go, look for functions that cache information in static variables. You may need to modify the source code to add an argument that allows you to reset the cache or find another way to deal with the static variables. Static variables are a pain.
  • Another reason why batch updates may fail while incremental updates work has to do with the order that the update functions are called in. Modules are processed according to weight and alphabetical order, and all applicable update functions within one module are run before moving onto the next. If you have a set of related modules, put update functions that affect the related modules into the module with the heaviest weight.
  • .install files may get really long if you create _update_N functions for every change and you frequently deploy to a test server. You can refactor your update functions if other developers know that they should test the updates from a fresh copy of the production database instead of from incremental updates to their current system. Make sure you don’t add code below the update level on the production server.
  • Don’t forget to return an array containing results, or even just array().
  • Here are some general tips on how to find out the programmatic equivalent of a web-based action:

    And here are some examples of programmatically doing things:

    Setting a variable
    variable_set(‘yourvariable’, ‘yourvalue’);

    Enabling modules
    include_once(‘includes/install.inc’);
    module_rebuild_cache();
    drupal_install_modules(array(‘module1′, ‘module2′));

    Disabling modules
    db_query(“UPDATE {system} SET status=0 WHERE type=’module’ AND name=’%s'”, ‘modulename’);

    Creating nodes 
    global $user;
    $old_user = $user;
    $user = user_load(array(‘uid’ => 1));
    $session = session_save_session();
    $session_save_session(FALSE);
    // Do the work
    $node = new stdClass();
    $node->type = ‘page';
    $node->title = ‘Title';
    $node->body = ‘Body';
    node_save($node);
    // Restore the user
    $user = $old_user;
    session_save_session($session);

    Deleting nodes
    global $user;
    $old_user = $user;
    $user = user_load(array(‘uid’ => 1));
    $session = session_save_session();
    session_save_session(FALSE);
    // Do the work
    node_delete($nid);
    // Restore the user
    $user = $old_user;
    session_save_session($session);

    Updating the list of blocks
    global $theme_key;
    $theme_key = ‘yourtheme';
    _block_rehash();

    Convenience functions for working with permissions

    function _add_permissions($roles, $permissions) {
      $ret = array();
      foreach ($roles as $rid) {
        if (is_numeric($rid)) {
          $role = db_fetch_array(db_query("SELECT rid, name FROM {role} WHERE rid=%d",
    				      $rid));
        }
        else {
          $role = db_fetch_array(db_query("SELECT rid, name FROM {role} WHERE name='%s'",
    				      $rid));
        }
        $role_permissions =
          explode(', ',
    	      db_result(db_query('SELECT perm FROM {permission} WHERE rid=%d',
    				 $role['rid'])));
        $role_permissions = array_unique(array_merge($role_permissions, $permissions));
        db_query('DELETE FROM {permission} WHERE rid = %d', $role['rid']);
        db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')",
    	     $role['rid'],
    	     implode(', ', $role_permissions));
        $ret[] = array('success' => true,
    		   'query' => "Added " . implode(', ', $permissions)
    		   . ' permissions for ' . $role['name']);
      }
      return $ret;
    }
    
    function _remove_permissions($roles, $permissions) {
      $ret = array();
      foreach ($roles as $rid) {
        if (is_numeric($rid)) {
          $role = db_fetch_array(db_query("SELECT rid, name FROM {role} WHERE rid=%d",
    				      $rid));
        }
        else {
          $role = db_fetch_array(db_query("SELECT rid, name FROM {role} WHERE name='%s'",
    				      $rid));
        }
        $role_permissions =
          explode(', ',
    	      db_result(db_query('SELECT perm FROM {permission} WHERE rid=%d',
    				 $role['rid'])));
        $role_permissions = array_diff($role_permissions, $permissions);
        db_query('DELETE FROM {permission} WHERE rid = %d', $role['rid']);
        db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')",
    	     $role['rid'],
    	     implode(', ', $role_permissions));
        $ret[] = array('success' => true,
    		   'query' => "Removed " . implode(', ', $permissions)
    		   . ' permissions for ' . $role['name']);
      }
      return $ret;
    }
    

    Use update functions for all of your behavioral changes. Use source code control. Write regression tests. These practices won’t take all the challenges out of Drupal development, but they certainly make it less stressful–and more fun. =)