Why Using VIM Will Improve Productivity

April 29, 2015 11:44 AM

I’ve always been a casual VI/VIM user, I cut my teeth on FreeBSD as a server operating system and it was a guarantee that it would be installed.  I learnt the basics of navigating around and editing and I did okay updating configuration files and paging through log files.  I never adopted it as an editing tool, in fact up until recently I wrote off editing platforms that didn’t offer the power that a fully integrated IDE brings.  In short, no auto-complete then I wasn’t interested.

Then I started seeing people switching to VIM on my social media feeds more and more and I gave it one more go, except this time with feeling.  It took two days before I really started to feel the difference but if you’re a complete novice then I would suggest giving it a week.  Don’t quit from the steep learning curve, don’t quit because you initially think it’s slowing you down, it probably is, but perseverance will result in a faster editing workflow.

What properties of VIM lead to this, in summary, you don’t use the mouse.  Using the mouse is the biggest inefficiency of getting your code into the machine.  If we just take a moment to look at the cognitive steps of, selecting all content inside a function prototype’s parameters and overtyping:

  1. Find mouse with hand
  2. Identify where the cursor is
  3. Identify where the function is on the screen
  4. Position mouse at start of parameters
  5. Click and drag to the end
  6. Start typing, unless you need to delete more due to inaccurate selection

Too much time is spent identifying the location of items, in the real and virtual worlds and with the inaccuracies of selection on computers you end up doing additional typing, careful selecting or reselecting.  If we compare this with the equivalent task in VIM:

  1. Move caret within function prototype parameters, doesn’t matter where.
  2. Type “ci )”
  3. Start typing

VIM is just full of these quick and intelligent ways of performing tasks and it’s all of these that you need to learn to get the most out of it.  It’s this daunting process that puts most people off.  Assuming that I’ve convinced you to give it a go, let me outline how I approached enhancing my VIM knowledge to a more productive level.

Firstly, don’t try to learn it all at once, don’t download the cheat-sheet and scare yourself.  Learn what you need to know as you need to know it.  Firstly learn how to move around your code, enter insert mode and delete code.  These are the basics and once you’re up and running and the keys are familiar then start questioning how you’re working, chances are there’s a better way.  Like the example above, you could delete all the characters one and a time or you can issue a command that wipes everything in the brackets and puts you in edit mode.  When you think you’re being inefficient, skip the thought that VIM is rubbish, do a quick search, no doubt you’ll find a much better way of doing it, i.e.

  • Multi-line indent (2>>)
  • Overtype all content until the next whatever character (ct ,)
  • Overtype all content within quotes, brackets or angle brackets (ci “)
  • Remove the carriage return between two lines (J)

These are all things that have greatly improved my productivity writing code.

As an aside, there have been a couple of other things I’ve discovered on my VIM journey:

  • My classes are becoming smaller due to the fact I don’t want pages of code to page through when navigating with a keyboard.
  • VIM supports many plugins, which include auto-complete and projects
  • Someone has ported VIM to Java and made a plugin for Netbeans

The last one I’m loving at the moment, as I am getting all the power and familiarity of Netbeans while learning and editing using VIM syntax.  In the end I could look at more and more plugins for VIM so that editing on a remote machine is as good as using an IDE, but one thing at a time.

TL;DR

  • VIM will ultimately improve your productivity
  • Learn it gradually
  • Every time you’re thinking it’s slower than reaching for the mouse, search for a solution.

I'll start this off with a quick caveat that this is something really simple; that if you're accustomed to Drupal then you'll think, "of course, you don't need to blog this".  However being fairly new to working with Drupal, this caught me out for too long, so if this helps just one other person I'll be happy!

The scenario was a fairly simple one, I have a page that isn't powered directly by any data in the system but a MENU_CALLBACK function that needs to output a custom HTML template to integrate a popup with no header, footer and specifically no Drupal javascript.  I wanted to use templates and not just spit it out in code for a more integrated solution.

So I had the module menu:

function products_menu() {
    $items = array();

    $items['products/%/%/360'] = array(
        'page callback' => 'products_360_view',
        'page arguments' => array(1, 2),
        'type' => MENU_CALLBACK
    );

    return $items;
}

And the appropriate callback:

function products_360_view($c, $p) {
    // Stuff happens in here
    // Variables have been changed to protect the innocent
}

So all was good, I just needed to get the right templates being used, as these had no internal nodes to hook into and the URL is dynamic this makes using the normal theme_hook_suggestions invalid, as they will generate template names based on the URL.  theme_hook_suggestions are Drupal's method for resolving the current thing being rendered to a template file, the Drupal site talks about it here: https://www.drupal.org/node/1089656.

We know that the HOOK_menu allows us to define a URL alias to function map and that that function will, by default, go through the usual Drupal rendering system.  This system will for each component being rendered use a theme_hook_suggestion map that defines which template files to use to render that component.  We just need to get our new ones into the theme_hook_suggestions and this is done in the THEME_preprocess_HOOK functions, so I created the following template files:

  • html--products--products-360.tpl.php
  • page--products--products-360.tpl.php

Followed by the preprocess page hook:

function products_preprocess_page(&$variables, $hook) {
    if (/*I'm on the 360 product page*/) {
        $variables['theme_hook_suggestions'][] = 'page__products__products_360';
    }
}

What we're saying here is that, when rendering page and the URL is going to be handled by my MENU_CALLBACK then add my template to the list.  Note that the underscores are changed to hyphens before looking for template files.  Now, if we reference this page https://www.drupal.org/node/171194 it shows how the template files are used for the different layers of the template system.  Notice how page is a template layer, it's the relationship between the hooks and the template layers that I missed!  THEME_preprocess_page() is called before page is rendered, likewise THEME_preprocess_region() will exist to be called before a region is rendered.  So to set a theme_hook_suggestions for the HTML, you guessed it THEME_preprocess_html().

function products_preprocess_html(&$variables, $hook) {
    if (/*I'm on the 360 product page*/) {
        $variables['theme_hook_suggestions'][] = 'html__products__products_360';
    }
}

And now we have a custom html layout for this one page!  It all makes sense now I've done it of course and if this saved you time, I'm glad!

It all started so simple, a basic competition mechanic quickly became two different mechanics with more variations in the future.  It could have quickly became a mess of code, multiple controllers each supporting different mechanics and with shared code being copied and pasted.  Very easy to go down that road, especially when the time scales are short.  A little bit of thinking and a look at what is available in the Yii framework soon led us away from this route.  By combining features we developed a flexible system that enabled code re-use, these were:

  • CAction - you extend this class to create stand-alone classes that are responsible for one action and can be re-used in many controllers.
  • CBehaviour - provides support for the mixin pattern, allowing the alteration of behaviour at runtime.

 

We created one action class for each of the steps that can happen across every mechanic, this would allow us to switch which actions are available based on the mechanic that is currently live.  These actions fire events before and after the action that allow logic that are mechanic specific to be defined in the code responsible for co-ordinating one mechanic.  The actions would look like:

class EnterCompetition extends CompetitionActionBase
{
    public function run()
    {
        if ( ! $this->onBeforeAction() )
        {
            return false;
        }

        // Action logic

        $this->onAfterAction( $eventParams );
    }
}

CompetitionActionBase defines the before and after action event handling code, this is boiler plate code and in Yii's documentation.

Then we created a behaviour for each mechanic, the behaviour has the sole responsibility of providing logic related to the mechanic and defining which actions are available.  This was achieved by defining a CController::actions() compatible method that the controller will call in its implementation.  This method defines what CActions are loaded in and what urlPath they will respond to.  If the CAction has properties these can also be defined here, such as event handlers.  The behaviour would look like:

class BasicCompetitionBehaviour extends CBehavior
{
    public function actions()
    {
        return array(
            'index' => 'alias.to.actions.CompetitionHome',
            'enter' => array(
                'class' => 'alias.to.actions.EnterCompetition',
                'afterAction' => array( $this, 'onAfterEnter' )
            ),
            'thanks' => 'alias.to.actions.CompetitionThanks'
        );
    }

    public function onAfterEnter( CEvent $event )
    {
        // Handle the post enter logic, i.e. redirect to thanks.
    }
}

The actions method returns as documented in the framework docs.  Notice the afterAction key for enter, this binds the onAfterEnter method of the behaviour as an event listener for afterAction, this code gets run after the action has done its logic.  This is how we decouple the mechanic from the action, one mechanic may just redirect to thanks while another perhaps runs some other logic and redirects to another page.  If the user had won an instant prize and you needed to take address details from them that you wouldn't take on the first step.

Finally the controller brings the logic together, it loads the correct behaviour and proxies the actions() call that the framework makes to it to the behaviour.  As actions() is defined in a parent class you can't just let the framework automatically call the behaviour as it would do for a method undefined in the controller class.  The code would look like:

class CompetitionController extends Controller
{
    public function init()
    {
        $competition = $this->getCompetitionModel();
        $this->attachBehaviour(
            'competitionBehaviour',
            array(
                'class' => $competition->behaviour
            )
        );
    }

    public function actions()
    {
        return $this->competition->actions();
    }
}

The end result being a tiny controller whose behaviour is completely defined by the type of competition the user is entering and it's all loosely-coupled.  Much better than where we could have ended up!

Making Your Search Forms Keep State

February 27, 2012 1:36 PM

We roll out so many CRUD (Create Read Update Delete) pages in the course of our projects and life for that matter.  And so often there's a list or grid view for all the existing entries and of course, there's the obligatory search form at the top.  How this should behave is probably up for debate but in my opinion the best way is that while you're in that section of the site the search data should remain.  So you filter the list, edit one of the records and then when you go back to the list the search data is still there.  If you go elsewhere and come back, well, the search data resets.

So, what's a good pattern for dealing with this requirement, a real simple way is to simply store these values in your flash data.  If you're unfamiliar with flash data, this is extremely volatile data that is only available during the current and next request.  So, we just need to set this data, with the user submitted values, when they do a search and then persist it while we remain in this section of the site.  If you're using MVC (Model View Controller) then this becomes simpler, in your action, set the data, examples here are based on development with Yii! :

public function actionIndex()
{
    $model = $user->getFlash( self::searchFlash, null, new SearchForm() );
    $form = new CForm( 'application.views.upload._forms.searchForm', $model );
    if ( isset( $_GET['SearchForm'] ) )
    {
        $model->attributes = $_GET['SearchForm'];
        Yii::app()->user->setFlash( self::searchFlash, $model );
    }

    ...

}

Then we persist this by running some code during controller initialisation:

public function init()
{
    parent::init();
    if ( Yii::app()->user->hasFlash( self::searchFlash ) )
    {
        $user->setFlash( self::searchFlash, $user->getFlash( self::searchFlash, null, false ) );
    }
}

 

Custom Yii Url Rules

January 10, 2012 9:31 PM

Recently I had the requirement to have a route that was powered by the database, the Yii docs cover this, but having the create and parse run at the same point was problematic.  The situation was that the route was just a controller and an action with search form parameters, of course the route parsed to createUrl() would be just "controller/action"; there was a normal "<controller>/<action>" rule as well.

If I wanted the createUrl() method to return the database powered slugs then the custom url rule would need to be above the generic rule.  The danger there is that if anyone entered a slug that matched an existing url this would prevent the normal page from displaying.  So I needed the createUrl() to be above the generic url but I needed the parseUrl() rule to be below it.  I had a couple of solutions, create two classes, or add an attribute that controlled behaviour, I opted for the attribute as it is a cleaner solution.

First, we create the class:

class DbUrlRule extends CBaseUrlRule
{
   public $connectionID = 'db';
   public $mode;

   public function createUrl( $manager, $route, $params, $ampersand )
   {
      if ( 'create' != $this->mode )
      {
         return false;
      }

      // Check the route and params and return the URL string if valid.
   }

   public function parseUrl( $manager, $request, $pathInfo, $rawPathInfo )
   {
      if ( 'parse' != $this->mode )
      {
         return false;
      }

      // Check the pathInfo and request to see if we match, if so set $_GET
      // variables and return route.
   }
}

Then, in our rules, we simply create two rules, same class but with different modes:

'urlManager' => array(
   ...
   'rules' => array(
      ...
      array( 'class' => 'DbUrlRule', 'mode' => 'create' ),
      '<controller>/<action>' => 'controller/action',
      array( 'class' => 'DbUrlRule', 'mode' => 'parse' ),
      ...
   )
)

And there you have it, one class that encapsulates the behaviour of the rule that can be used in multiple places in the rules.  One place to ensure that you get a nice URL if you createUrl() with the appropriate values and the parseUrl() in a place that ensures system integrity.

Holidays and Open Source

January 6, 2012 1:16 PM

I noticed over Christmas that open source projects seem to get work done when the developers are not at work.  This makes perfect sense but I'd not noticed it really until now, two projects that I use both got updates over the holidays.  K9 Mail, a mail client for Android, got updated, bugs fixed and features added that I never new I was missing.  The new feature to go to the next message when you delete is nice though, thanks for that K9 team.

The other project that got an update was the Yii Framework, a PHP Framework that brings us the good bits of Rails onto the PHP platform.  In this release are two bug fixes that I submitted patches for, take a look at the changelog, see those two entries with "mcheale" against them, those are mine.  Pretty chuffed that I've beed able to feed back into a project and that my patches were accepted.  These two bugs have been in the issue tracker for at least six months but no-one has had the time to look into them, until Christmas that is.

So there I was, Christmas Eve, and these two bugs were annoying me, rattling around my head.  So I sat down and investigated, came up with some solutions and added patch files to the issues.  So there you have it, open source, you get involved and then you find yourself putting in time when you're on holiday to make it better.  I guess it proves the pleasure/pain theory!

Site Development

December 7, 2011 4:33 PM

Well here we go then!  My site has languished with no love nor attention for many years.  To be honest I had very little use for it, nor any desire to spend time working on it.  Now, however, it's time it became something more than it was, for a start, my recent review at work said that I had to start blogging on tech related topics, so I had to get a blogging platform.  Here's the thing though, I just couldn't bring myself to just roll YAW, I'm not denying that Wordpress is a very good platform, it's just not one that I want to learn at the moment.  So instead, here we are in with a real simple CMS, build with the Yii! Framework, which I think it brilliant btw, and a shoddy looking design.

Regarding the design, I aimed for a layout that meets my requirements and I'm happy with that. I however can't do design work, I plan to try and convince my brother to give it a worthy skin!