Falling Into the Pit of Success with Web Components

Being able to build components (AKA “directives” if you’re in the Angular camp) has changed the way we structure web applications on the front end. This is a big move forward and provides a lot of power in addition to some much needed encapsulation.

However, even with the drastically improved tools we have at our disposal for building client side applications, I still see projects going down the wrong path. Code is tightly coupled and maintenance quickly becomes an issue.

The best way I’ve found to mitigate this is to separate concerns as much as possible. I’m going to say that again just because I think it’s that important: separate concerns of your components as much as possible.

We all start out having our components do too much, myself included. It’s hard to get into the habit, but our goal should be to make components as small and dumb as possible. This ties right in with other software development best practices like DRY and SRP, but it’s not something I see people doing a lot in client side applications.

Here are two big revelations I had about developing components that have made my life a lot easier, along with a an example from a recent project where I broke apart a component that was doing too much.

Pass In Your Data

When I started building components I often made them responsible for managing their own data. After they were instantiated, they would be responsible for making API calls to fetch and manipulate their data. This works for a while, but becomes difficult to maintain over time.

Passing data in to a component provides a lot of the same wins as dependency injection. It allows your components to be very small and focused which makes them easier to maintain and test.

This can be accomplished by creating containers, also components, that are solely responsible for managing data injecting it into their children. I’ve also done this by instantiating data through a router and exposing that to the view where the components are rendered.

If you’re coming from a framework like Angular, I’m not just talking about separating things from directives and controllers, either. It will be tempting to just toss some additional logic into your controller for managing data, but this should actually be injected into the component’s controller, so it needs to come from another source.

The main goal here is to make our components as dumb as possible so they become more reusable, testable and maintainable.

There’s No Such Thing As A Component That’s Too Small

At first it’s going to feel weird to break things down into smaller and smaller pieces. It’s common to think that it isn’t worth the overhead of creating a new component when evaluating where you might be able to break things apart. However, this is where you are able to set yourself up for success.

Let’s take a look at an example from a recent project I was working on. We had some widgets that stood alone as individual components.

Widget preview

We had a single controller and view backing this entire widget. It worked fine for a couple weeks, until we had requests to see this same data represented in similar but different forms throughout our app.

It was hard to decide whether to write a new component or try to modify the design to make our existing components fit. Neither of these options were ideal. In the end, this widget was broken out into 7 different components and composed of 10 total. I’ve annotated another screenshot to show the breakdown.

Annotated widget

We ended up breaking things out into a bunch of pieces that are very easy to maintain and test individually. Now we have all the building blocks we need to develop other complex interfaces with these same components. Another big win here is that we are passing all of our data in to the components so they can just worry about rendering. The data comes from the top level component, which is not annotated in the screenshot since it just handles data.

Conclusion

Breaking things down into small pieces that have a single responsibility is always a win. The story is no different for web components. Don’t forget, managing data is a responsibility.

If you have other techniques for writing maintainable components, please share them in the comments.

Angular Directive Testing Best Practices

Getting in the habit of testing is hard, but I’ve found that a big turning point for me when I start testing in new languages and tools is to identify patterns in how tests are written. Once I know a pattern for testing something, it’s easier to walk through each step to write my tests. Just like anything else, it’s more approachable when you break it apart into smaller pieces.

When testing directives, this is the main pattern I see:

  1. Mock
  2. Compile
  3. Interact
  4. Assert

An Example Directive

I’m going to walk through each step in the directive testing pattern for a directive I wrote recently. Here is the source of the radioButton directive, which provides a way to use custom style and markup for a radio form control.

angular
  .module('kit.forms', [])
  .directive('radioButton', radioButtonDirective);

function radioButtonDirective() {
  return {
    require: '?ngModel',
    link: function(scope, element, attributes, ngModelCtrl) {
      element.addClass('button');

      ngModelCtrl.$render = function() {
        element.toggleClass('primary', angular.equals(ngModelCtrl.$modelValue, scope.$eval(attributes.radioButton)));
      };

      element.bind('click', function() {
        if (!element.hasClass('active')) {
          scope.$apply(function () {
            ngModelCtrl.$setViewValue(scope.$eval(attributes.radioButton));
            ngModelCtrl.$render();
          });
        }
      });
    }
  };
}

Note: This example shows some of the power of ngModelController, which I think is pretty cool. If you’re interested in learning more about what you can do with ngModel, send me an email or let me know in the comments.

Mock

The first thing we need to do is mock out our dependencies. In this example, we are mocking out the kit.forms module and injecting a new instance of $rootScope to use later in our tests.

describe('Forms module', function() {
  var element, scope;

  beforeEach(module('kit.forms'));

  beforeEach(inject(function($rootScope) {
    scope = $rootScope.$new();
  }));

  // ...
});

Compile

Directives need to be compiled in order to make assertions on the HTML they produce. This can be done by injecting the $compile service and running a string of HTML through it. After it is compiled, the digest cycle needs to be kicked off with a call to $digest() from the $scope instance created earlier.

describe('[required]', function() {
  beforeEach(inject(function($compile) {
    element = angular.element(
      '<div class="button-group">' +
        '<label ng-model="model" radio-button="'yes'">Yes</label>' +
        '<label ng-model="model" radio-button="'no'">No</label>' +
        '<label ng-model="model" radio-button="'maybe'">Maybe</label>' +
      '</div>'
    );

    $compile(element)(scope);
    scope.$digest();
  }));

  // ...
});

Interact

Now you can script some interactions on the compiled HTML before you make assertions. Storing a reference to the element(s) you want to manipulate and make assertions against at the beginning of your specs is usually helpful.

it('applies the "primary" class when clicked', function() {
  var radios = element.find('.button');

  radios.eq(1).click();
  // ...

  radios.eq(2).click();
  // ...
});

Assert

Finally, we can make assertions that the markup has changed the way it’s expected to.

it('applies the "primary" class when clicked', function() {
  var radios = element.find('.button');

  radios.eq(1).click();
  expect(radios.eq(1)).toHaveClass('primary');
  expect(radios.eq(0)).not.toHaveClass('primary');
  expect(radios.eq(2)).not.toHaveClass('primary');

  radios.eq(2).click();
  expect(radios.eq(2)).toHaveClass('primary');
  expect(radios.eq(1)).not.toHaveClass('primary');
});

Sometimes the interaction step isn’t necessary for making assertions. For example, it’s usually best to assert that a directive initially compiles the way you expect it to.

it('adds the "button" class', function() {
  expect(element.find('.button').length).toBe(3);
});

E2E Testing Directives

When I first started testing directives I thought I was doing it wrong because I was testing the DOM but I wasn’t using a true integration testing tool, like Protractor. However, I realized a lot of times it’s hard to know the context a directive is going to be used in so it’s difficult to end-to-end test effectively. In most cases it’s easier to avoid an integration testing tool for an individual directive test and just rely on that for flexing the main flows of your SPAs.

Other Patterns

Directives are easily the most difficult thing to test in Angular, but I’ll be sharing some other Angular testing patterns and best practices I use over the next few weeks. Make sure to subscribe to email updates if you want to keep up with this series of articles on Angular testing and please share any other patterns you’ve seen in the comments.

Getting Started with Laravel 4

With the release of Laravel 4 just around the corner a lot of people are trying to decide whether to start a new project with a stable 3.2 build or wait for the new version. I found myself in that position earlier this week so I decided to pull down Laravel 4 and work off that since I figured the transition would be smoother once a stable version 4 is released.

There isn’t really any documentation on how to get started with Laravel 4, and if you aren’t familiar with Composer, you might not know where to begin. That’s why I’m writing this. If you want to get started with Laravel 4 today, here’s how.

Laravel, Illumniate and Composer Packages

Laravel 4 breaks each core component of the framework into a Composer package. These packages are currently grouped together under the “Illuminate” organization on GitHub. All of these components are used by the Laravel framework, but now that they are stored as separate packages, they can easily be pulled into other projects with Composer.

Note: Familiarity with Composer isn’t necessary for this tutorial, but if you want to learn more, check out Easy Package Management With Composer by Phil Sturgeon.

Since Laravel 4 is dependent on Composer, you already have access to any package from the Packagist directory anytime you start a new project. That means you have thousands of packages at your fingertips and it eliminates the need for proprietary package management systems like Laravel Bundles and CodeIgniter Sparks.

Getting Started

There’s not much to this, it’s just 3 simple steps. I work on a Mac, but I’d imagine these instructions are pretty similar for other operating systems.

1. Pull Down the Application Template

First thing you need to do is pull down the Laravel application template. This is the starting point for Laravel 4 projects. The template is pretty empty, and if you’re coming from Laravel 3, you’ll immediately notice the absence of the laravel, bundles and storage directories.

2. Install Composer

Run the following script to install Composer into the root of your project:

curl -s http://getcomposer.org/installer | php

You should see a success message if everything runs correctly.

Troubleshooting

I received the following error when I tried to install Composer the first time:

Some settings on your machine make Composer unable to work properly.
Make sure that you fix the issues listed below and run this script again:

The detect_unicode setting must be disabled.
Add the following to the end of your `php.ini`:
    detect_unicode = Off

If you encounter this error, just do as it says and add detect_unicode = Off to the end of your php.ini file at /private/etc/php.ini.

3. Install the Laravel Components

After composer is installed, you can use it to install all of the other Laravel components.

php composer.phar install

Running this command will install the packages that are specified in the composer.json file. Here’s what that looks like:

{
    "require": {
        "illuminate/foundation": ">=1.0.0"
    },
    "autoload": {
        "classmap": [
            "app/controllers",
            "app/domain",
            "app/tests/TestCase.php"
        ]
    },
    "minimum-stability": "dev"
}

This doesn’t seem like much considering how many packages get installed when you run php composer.phar install, but that’s because each package can have its own dependencies. If we take a look at Illuminate’s foundation package, we can see that it has a whole bunch of its own dependencies that get installed too.

{
    "name": "illuminate/foundation",
    "description": "A web application foundation.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "authors": [
        {
            "name": "Taylor Otwell",
            "email": "taylorotwell@gmail.com"
        }
    ],
    "require": {
        "php": ">=5.3.0",
        "illuminate/auth": ">=1.0.0",
        "illuminate/cache": ">=1.0.0",
        "illuminate/config": ">=1.0.0",
        "illuminate/container": ">=1.0.0",
        "illuminate/cookie": ">=1.0.0",
        "illuminate/database": ">=1.0.0",
        "illuminate/encryption": ">=1.0.0",
        "illuminate/events": ">=1.0.0",
        "illuminate/exception": ">=1.0.0",
        "illuminate/filesystem": ">=1.0.0",
        "illuminate/http": ">=1.0.0",
        "illuminate/pagination": ">=1.0.0",
        "illuminate/profiler": ">=1.0.0",
        "illuminate/redis": ">=1.0.0",
        "illuminate/routing": ">=1.0.0",
        "illuminate/session": ">=1.0.0",
        "illuminate/support": ">=1.0.0",
        "illuminate/translation": ">=1.0.0",
        "illuminate/validation": ">=1.0.0",
        "illuminate/view": ">=1.0.0",
        "symfony/browser-kit": "2.1.0",
        "symfony/css-selector": "2.1.0",
        "symfony/dom-crawler": "2.1.0",
        "symfony/http-kernel": "2.1.0",
        "symfony/translation": "2.1.0",
        "underscore/Underscore.php": "1.3.1"
    },
    "require-dev": {
        "mockery/mockery": "0.7.*"
    },
    "autoload": {
        "psr-0": {
            "IlluminateFoundation": "src/"
        },
        "files": [
            "src/helpers.php"
        ]
    },
    "minimum-stability": "dev"
}

Start Your App

That’s everything! You can go visit your app just like you normally would and developing for Laravel is pretty much the same as it was before. Setup some controllers or start defining some routes!

Let me know if you have any troubles with this and I’ll be sure to revise this article or provide more troubleshooting tips!

Extending ActiveModel via ActiveSupport::Concern

In a project I’ve been working on we have some functionality that is shared across multiple models – assigning a default record on a has_many association. Obviously we didn’t want to duplicate the methods in each model, so we explored our options for sharing the logic. We figure we could:

  1. Setup a HasDefault base class that we could extend our models from (i.e. class CreditCard < HasDefault)
  2. Extend the functionality of ActiveRecord::Base

Inspired by how has_secure_password is setup, we decided to extend the functionality of ActiveRecord::Base so we could just call a method inside of a model we wanted to expose the default record assignment methods and callbacks to. Here’s what we ended up with:

File: lib/active_model_extensions.rb

module HasDefault
  extend ActiveSupport::Concern

  module ClassMethods
    def has_default
      attr_accessible :is_default

      before_create  :set_default_if_none_exists, unless: :is_default?
      before_save    :set_default,                if: :is_default?
      before_destroy :set_fallback_default,       if: :is_default?
    end
  end

  def set_default
    current_default = self.class.first conditions: { user_id: self.user_id, is_default: true }
    current_default.update_column(:is_default, false) if current_default
    self.update_column(:is_default, true) unless self.new_record?
  end

  def set_fallback_default
    if self.is_default?
      fallback = self.class.first conditions: { user_id: self.user_id, is_default: false }
      fallback.update_column(:is_default, true) if fallback
    end
  end

  def set_default_if_none_exists
    current_default = self.class.first conditions: { user_id: self.user_id, is_default: true }
    self.is_default = true unless current_default
  end
end

class ActiveRecord::Base
  include HasDefault
end

This allows us to call has_default in any model to expose the three callback methods for assigning the default record. Now all we need to do to in our model is

class CreditCard < ActiveRecord::Base
    has_default
end

and the default record assignment will be taken care of automatically. This assumes there is an is_default column in the table, but has_secure_password makes similar assumptions so that doesn’t really bother me.

The Breakdown

The big thing here is to make sure we are using ActiveSupport::Concern to extend the core Rails classes. This makes our lives a litter easier by helping us setup our class and instance methods. It takes everything inside the ClassMethods module and makes them available as, you guessed it, class methods. All the other methods in the HasDefault module get turned into instance methods.

From the code above, you can see we setup the has_default class methods

module ClassMethods
  def has_default
    attr_accessible :is_default

    before_create  :set_default_if_none_exists, unless: :is_default?
    before_save    :set_default,                if: :is_default?
    before_destroy :set_fallback_default,       if: :is_default?
  end
end

and then setup the methods used in the callbacks in the HasDefault module.

def set_default
  current_default = self.class.first conditions: { user_id: self.user_id, is_default: true }
  current_default.update_column(:is_default, false) if current_default
  self.update_column(:is_default, true) unless self.new_record?
end

def set_fallback_default
  if self.is_default?
    fallback = self.class.first conditions: { user_id: self.user_id, is_default: false }
    fallback.update_column(:is_default, true) if fallback
  end
end

def set_default_if_none_exists
  current_default = self.class.first conditions: { user_id: self.user_id, is_default: true }
  self.is_default = true unless current_default
end

Note: ActiveSupport::Concern used to support a module called InstanceMethods, but recently changed this to make anything outside of the ClassMethods module an instance method.

The last part of this example includes the HasDefault module in ActiveRecord::Base, which actually adds the module and exposes the has_default method to all classes extending ActiveRecord::Base, in other words, all of your models.

class ActiveRecord::Base
  include HasDefault
end

Why ActiveSupport?

It’s true, there are other ways to extend classes in Ruby, but using ActiveSupport::Concern makes things a little easier and definitely cleaner. It can also handle dependency resolution, which we didn’t need here, but is quite useful.

Further Reading

Work Faster with Basic Shell Commands

Writing your own shell scripts may seem a little intimidating at first, but it’s actually quite simple and can drastically improve your workflow. Any sequence of commands you would run from the command line you can put into a shell script.

For example, when I want to start coding on a project, I usually end up opening my editor from the terminal and a new tab in my browser to view the project. This doesn’t take very long, but since it’s something I do multiple times every day, I figured I should try to automate it.

Let’s Scriptify It

I wanted to be able to pull up my editor and open the new browser tab right from the terminal so I came up with the following script to do just that:

# !/bin/sh

open -g 'http://localhost/chris-schmitz.com' 
subl .

Breaking this down, here’s what each line does:

  1. # !/bin/sh – Invokes the shell, a standard starting line for a shell script
  2. open -g 'http://localhost/chris-schmitz.com' – Opens the URL for the project in a new tab of my default browser
  3. subl . – Opens the current directory in Sublime Text 2 (using TextMate the command would be mate .)

Now, assuming I saved the file as open.sh, I can run sh open.sh from the command line to open my editor and the browser tab. This was a great next step, but I thought I could do a little better.

A Shortcut for the Shortcut

I love TextExpander and any time I have text that I need to type on a regular basis I make a snippet for it. I figured this shell script is a perfect candidate for that.

I didn’t want to have to create a new file and then use a snippet to insert those three lines, so I use the following snippet to create the file with those three lines all in one step from the terminal.

echo "# !/bin/shnnopen -g 'http://localhost/%fill:site_name%'nsubl ." >; open.sh

It’s just a one-liner with some escaped characters and line breaks that get inserted into the open.sh file. I set the shortcut for the snippet to qlp, short for Quick Launch Project.

Now I can create these shell scripts with ease, but I still didn’t like having to type sh open.sh all the time. I decided I wanted to make it shorter, so I aliased that to dev with the following command:

alias dev='sh open.sh'

Note: I’m using ZSH in my terminal, but I believe this command is the same for Bash.

My New Workflow

Now when I create a new project, all I have to do to get setup is:

  1. cd to the directory
  2. Expand my qlp snippet
  3. Run the dev command, and I’m ready to go

The main takeaway from this shouldn’t be the actual code, but the idea of how easy it is to automate repetitive tasks. A few basic shell commands can go a long way.

If you want to learn more about shell scripting, I highly recommend the Bash Guide for Beginners.

The MacBook Air and Web Development

I migrated to a MacBook Air as my primary development machine a few months ago and I think it is the ideal computer for a web developer. I think it’s the future of personal computing and that everyone should do themselves a favor and pick one up the first chance they get.

There are already some great articles reviewing the specs of the computer and why it is well-suited for web development, but I just wanted to point out a few, hopefully less-obvious, benefits of the machine for others who are considering making the switch.

Pros

There are a lot of things I like about the machine, but here are the big ones.

Focus

I actually enjoy the restrictions of the smaller screen. I no longer need to worry about where I’m going to position windows. They are either full screen or half screen, no more lost time organizing my windows.

As a result of the more minimalistic setup I’ve become accustomed to, I’ve found that I am less tolerant of distractions. Now, I turn off anything that can potentially distract me from the task at hand and I’ve been much more productive as a result.

Efficiency

At first I didn’t like them, but I use spaces all the time now. I can switch back and forth between personal and work space very efficiently.

Speed

I’ve never had a computer with an SSD before, and I completely underestimated how big of a difference this makes. It’s amazing. I’ve found my Air to be much more responsive than the much more powerful computers I was using previously. Like I said, I don’t want to get too technical here, so if you want more details on this stuff, I would check out Apple’s Performance page for more details.

Portability

I really didn’t think I would care too much about the portability of the machine, but now that I have been carrying it with me everywhere for the past few months I don’t think I could ever go back to a MacBook Pro. Even my wife’s 13” MBP feels clunky (and slow) relative to my Air.

One thing a lot of people don’t seem to realize is that the resolution of the Air is actually the same as a 15” MBP. This makes it feel a lot bigger than it is, and it has a Thunderbolt port if you want to hook it up to a larger display.

Cons

None… just kidding, but they are fairly obvious. You’re clearly getting a machine with less screen real estate and a slower processor, but you will not notice the difference unless you are doing a lot of graphics/video editing or 3D modeling.

Conclusion

Computers are typically reviewed on specs, and if you compare the MacBook Air to a more powerful machine, the MacBookAir might look limited, but if you compare it based on the user experience provided by the machine, nothing comes close.

Note: I am a complete Apple fanboy, but I was a Windows user for the majority of my life. I’ve even had a Windows 7 laptop that I worked on for about a year so I am able to make a somewhat fair comparison.

Other Relevant Articles