Automating AWS EC2 deployments with GitHub Actions and Systems Manager

This blog is hosted on an AWS EC2 instance; the code for it is stored in a GitHub repository. In order to update the code on the EC2 instance I previously would manually connect to the EC2 instance via SSH client, pull the code from the GitHub repository and then execute the necessary command to redeploy the code. While this got the job done, it always felt rather clunky and tedious. Last week I finally got around to automating the process. In today’s post I’ll be discussing the solution I came up with.

Essentially my approach involves integrating GitHub Actions with AWS Systems Manager to deploy the code on the EC2 instance on a push of the code to a branch of the GitHub repository. The GitHub Actions workflow consists of two steps, which are as follows:

  1. Configuring the AWS credentials
  2. Executing the deployment

The rest of this post will go into a bit more detail about each of these steps.

I’ll start by stubbing out the GitHub Actions workflow I’m using:

name: Deploy to EC2

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Configure AWS credentials
    ...
    - name: Execute deployment script on EC2 instance
    ...

In the “name” section I specify a name for the workflow. In the “on” section I specify that I want the workflow to run on pushes to the master branch of the repository. Finally in the “jobs” section I outline the “deploy” process–“runs-on” specifies the runner for GitHub Actions to use; “steps” specifies the steps for GitHub Actions to execute.

To configure AWS credentials I use AWS’s official configure-aws-credentials action. This action needs to be configured with the following data:

  1. The IAM user’s access key ID
  2. The IAM user’s secret access key
  3. The region of the EC2 instance to which the code is being deployed

This presupposes of course that the IAM user and an EC2 instance already exist. In my case the latter did but the former didn’t so I just went ahead and created an IAM user to represent GitHub Actions. With these things in place the complete step ended up as follows:

- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v1
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
(So as not to expose the AWS credentials publicly I store them as secrets in the repo’s Security settings.)
To execute the deployment process on the EC2 instance I use a custom action built around System Manager’s send-command function. The docs reveal the function to be highly configurable; the parameters that are relevant to my use case are as follows:
  1. document-name – The name of the Amazon Web Services Systems Manager document (SSM document) to run.
  2. targets – An array of search criteria that targets managed nodes using a key-value combination that you specify.
  3. parameters – The required and optional parameters specified in the document being run.
  4. timeout-seconds – If this time is reached and the command hasn’t already started running, it won’t run.

For document-name I specify “AWS-RunShellScript”–this is a shared resource available via Systems Manager Documents that enables Systems Manager to run a shell script.

For targets, I specify “instanceids” as “Key” and the instance ID of my EC2 instance as “Values.”

For parameters, I specify a string in the following form (where <command> represents a specific instruction to provide to the EC2 instance):

'commands=[
  "<command>"
]'

Finally for timeout-seconds, I specify a value of 600 (10 minutes).

With these things in place the complete step ended up as follows:

- name: Execute deployment script on EC2 instance
  run: |
    aws ssm send-command \
      --document-name "AWS-RunShellScript" \
      --targets "Key=instanceids,Values=${{ secrets.EC2_INSTANCE_ID }}" \
      --parameters 'commands=[
        "<command>"
      ]' \
      --timeout-seconds 600

(Similar to before I store the EC2 instance’s ID as a secret in the repo’s security settings so as not to expose it publicly.)

So this is pretty much it. A push of the code to the master branch of the repo now results in the code being deployed automatically to the EC2 instance via the GitHub Action. Handily the results of the execution are available in Command History under Systems Manager > Run Command.

Clicking into the detail of a command exposes further info such as output and error logging, plus the ability to re-run the command from Systems Manager itself.

All in all this was a fun little project that took a day or two of tinkering to get working.

Accessing an AWS EC2 instance via Session Manager

This blog is currently hosted on AWS EC2. Until recently I would always connect to my EC2 instance via SSH client. An alternative approach I learned of recently is to connect via Session Manager, a feature of AWS Systems Manager. A main benefit of Session Manager is that it removes the need to open inbound ports to the instance or manage SSH keys as Session Manager handles these security details for you.

Using Session Manager involves a few prerequisites, which can be reduced to the following three-step process:

  1. Provisioning the EC2 instance with the SSM* agent
  2. Provisioning the EC2 instance with an IAM role
  3. Restarting the SSM agent to detect the IAM role

* Simple Systems Manager

Detailed instructions follow. Note that these instructions are specific to Ubuntu 14.04, which I appreciate is quite outdated at time of writing. Steps 1 and 3 require you to be connected to an EC2 instance (for example via SSH client). Step 2 requires you to be logged in to the AWS Management Console.

Provisioning the EC2 instance with the SSM agent

The first main step toward connecting to an EC2 instance via Session Manager is to install the SSM agent on the EC2 instance. For my OS this involved running the following commands against the instance:

// Update the OS package index
sudo apt-get update

// Download the SSM agent package
wget https://s3.amazonaws.com/amazon-ssm-us-east-1/latest/debian_amd64/amazon-ssm-agent.deb

// Install the SSM agent package
sudo dpkg -i amazon-ssm-agent.deb

// Start the SSM agent
sudo start amazon-ssm-agent

// Verify the SSM agent status
sudo status amazon-ssm-agent

This last command should produce output like the following:

amazon-ssm-agent start/running, process 4180

Provisioning the EC2 instance with an IAM role

The second main step toward connecting to an EC2 instance via Session Manager is to provision the EC2 instance with an IAM role granting permission to Session Manager to connect to the instance. This step involves (1) creating the IAM role and (2) attaching the role to the instance.

Create an IAM role for the EC2 instance

From the AWS Management Console go to IAM. From the left nav click Roles and from the top-right click “Create Role.” You should be taken to a three-step wizard for creating an IAM role.

The first step is to select the trusted entity for the role. For “Trusted Entity Type” choose “AWS Service.” For “Use Case” choose EC2 as Service and “EC2 Role for AWS Systems Manager” as “Use Case.” You can then proceed to the next step of the wizard.

The second step is to add permissions to the role. All you should need to do for this step is to verify that the relevant policy (AmazonSSMManagedInstanceCore) is attached to the role, which the use case chosen in the previous step should take care of automatically. You can then proceed to the next step of the wizard.

The last step is to name, review, and create the role. Under “Role details” add a name and description for the role. Then create the role and verify that it was created successfully.

Attach the IAM role to the EC2 instance

Still in the AWS Management Console go to EC2. From the left nav click Instances and from the Instances pane select the relevant instance. From the Security submenu of the Actions menu select “Modify IAM Role.” From the “IAM role” menu select the role you created in the previous step. Then click “Update IAM role.”

From the Instances pane select the relevant instance again (assuming it’s not already selected). Verify that the role is attached to the instance–it should be listed under the “IAM Role” heading in the Details tab of the Instances pane.

Still in the Instances pane and with the relevant instance still selected, click Connect. From the “Connect to instance” page select the “Session Manager” tab. You should be presented with a page that resembles the following screenshot:

Note (a) the disabled Connect button and (b) the warning about the instance not being connected to Session Manager–both would be expected at this stage since it’s necessary to restart the SSM agent in order for the instance to detect the updated IAM role.

Restarting the SSM agent to detect the IAM role

The last main step, then, toward connecting to an EC2 instance via Session Manager is to restart the SSM agent in order for EC2 to detect the updated IAM role. For my OS this involved running the following command against the instance:

sudo restart amazon-ssm-agent

Back in the AWS Management Console refreshing the “Connect to instance” page should result in a page that resembles the following screenshot:

Note that (a) the warning about the instance not being connected to Session Manager has disappeared and (b) the Connect button has been enabled. If you go ahead and click the Connect button you should be presented with a browser-based terminal from which you can run commands against the EC2 instance.

Congratulations! You’ve now successfully connected to an EC2 instance via Session Manager.

Creating queues with ES6

In my last post I discussed creating a binary search tree with ES6. In this post I’ll be discussing a different type of data structure: queues. Once again I’ll be leaning on Data Structures and Algorithms With JavaScript by Michael McMillan for insight.

What is a queue?

A queue is a linear data structure that stores items in the order in which they are generated. A queue is rather like a list where items are added to the end and removed from the beginning. This type of data structure is known as a “first-in, first-out” data structure. It may help to think of a queue as a line at a grocery store where customers join at the back and check out at the front.

Creating a queue

Creating a queue requires a single class. The class should have one property for storing the data along with several standard methods for working with the data, e.g., adding items to the queue, removing items from the queue and querying the queue. Exact property and method names may vary but such a class may be designed as follows:

// A basic queue
class Queue {
  // Creates the data store
  constructor(dataStore = []) {
    this.dataStore = dataStore;
  }
  // Adds an element to the back of the queue
  push(element) {
    this.dataStore.push(element);
  }
  // Removes an element from the front of the queue
  shift() {
    this.dataStore.shift();
  }
  // Inspects the first element in the queue
  peekFront() {
    return this.dataStore[0];
  }
  // Inspects the last element in the queue
  peekBack() {
    return this.dataStore[this.dataStore.length - 1];
  }
  // Checks to see if the queue is empty
  isEmpty() {
    return !this.dataStore.length;
  }
  // Outputs the contents of the queue
  toString() {
    let str = '';
    for (var i = 0; i < this.dataStore.length; i++) {
      str += `${this.dataStore[i]}\n`;
    }
    return str;
  }
}

This simple class essentially proxies native array properties and methods in order to work with the data. For example the push() method that adds items to the queue proxies Array.prototype.push(); the shift() method that removes items from the queue proxies Array.prototype.shift(); and the isEmpty() method that checks to see if the queue is empty proxies Array.length. The class also has methods for inspecting the first and last elements in the queue (peekFront() and peekBack()), and outputting the contents of the queue (toString()).

Let’s now create a queue and add some items to it:

const queue = new Queue();
queue.push('George Washington');
queue.push('John Adams');
queue.push('Thomas Jefferson');
queue.push('James Madison');
queue.push('James Monroe');

Outputting the contents of the queue should return the following:

George Washington
John Adams
Thomas Jefferson
James Madison
James Monroe

Notice how each new item has been added to the back of the queue?

Let’s now remove an element from the queue using queue.shift(); and see how this affects the output:

John Adams
Thomas Jefferson
James Madison
James Monroe

Notice how the first item has been removed from front of the queue?

Let’s now inspect the first and last items in the queue:

queue.peekFront(); // John Adams
queue.peekBack(); // James Monroe

So far, so predictable.

Creating a double-ended queue

A more specific kind of queue is called a double-ended queue or “deque” (pronounced “deck”). In a deque items can be added to and removed from both the front and the back of the queue. Creating a deque requires us to extend our basic queue with a couple of extra methods: an unshift() method for adding items to the front of the queue and a pop() method for removing items from the back of the queue. Again these methods proxy the native array methods Array.prototype.unshift() and Array.prototype.pop().

class Deque extends Queue {
  ...
  // Adds an element to the front of the queue
  unshift(element) {
    this.dataStore.unshift(element);
  }
  // Removes an element from the back of the queue
  pop() {
    this.dataStore.pop();
  }
  ...
}

Let’s now create a deque and add some items to it:

const deque = new Deque();
deque.unshift('George Washington');
deque.unshift('John Adams');
deque.unshift('Thomas Jefferson');
deque.unshift('James Madison');
deque.unshift('James Monroe');

Outputting the contents of the queue should return the following:

James Monroe
James Madison
Thomas Jefferson
John Adams
George Washington

Notice how adding the items to the front of the queue affects the order?

Let’s now remove an item from the queue with deque.pop(); and see how this affects the output:

James Monroe
James Madison
Thomas Jefferson
John Adams

Notice how the item has been removed from the back of the queue?

Let’s now inspect the first and last elements in the queue:

deque.peekFront(); // James Monroe
deque.peekBack(); // John Adams

Straightforward enough!

Creating a priority queue

Another more specific kind of queue is called a priority queue. In a priority queue items are removed based on a manually defined “priority” as opposed to an automatically defined position (first or last).

As an example let’s take the line of succession to the U.S. presidency, in which the successor to the office is based on a set order of priority. A simple data model for a successor could look like this:

office: String // office to which successor belongs
priority: Number // order of priority

Creating a line of succession class once again requires us to extend our basic queue with a few methods: a special implementation of the shift() method for removing items from the queue, a special implementation of the toString() method for outputting the contents of the queue, and a count() method for returning the number of items in the queue.

class LineOfSuccession extends Queue {
  // Removes an element from the queue based on priority 
  shift() {
    let order = 0;
    for (var i = 1; i < this.count(); ++i) {
      if (this.dataStore[i].order < this.dataStore[order].order) {
        order = i;
      }
    }
    return this.dataStore.splice(order, 1);
  }
  // Outputs the contents of the queue
  toString() {
    let retStr = ``;
    for (var i = 0; i < this.dataStore.length; i++) {
      retStr += `${this.dataStore[i].office}\n`;
    }
    console.log(retStr);
  }
}

The shift() method works by returning the item with the highest priority from the queue. It does this by looping through all the items in the queue and upon encountering a higher priority item than the current highest priority item making the former the new highest priority item.

Let’s now create a line of succession:

const los = new LineOfSuccession([
  {office: 'Speaker of the House of Representatives', order: 2},
  {office: 'Vice President', order: 1},
  {office: 'Secretary of the Treasury', order: 5},
  {office: 'Secretary of State', order: 4},
  {office: 'President pro tempore of the Senate', order: 3}
]);

Notice how this time we’re passing the data into the queue’s constructor rather than adding the items manually with queue.push()? Also notice how the data is in no particular order as it’s being passed in? Outputting the contents of the queue should return the following:

Speaker of the House of Representatives
Vice President
Secretary of the Treasury
Secretary of State
President pro tempore of the Senate

Now let’s create a successor variable and start pulling (removing) successors from the queue.

let successor;
successor = los.shift();
successor[0].office // Vice President;
successor = los.shift();
successor[0].office // Speaker of the House of Representatives;
successor = los.shift();
successor[0].office // President pro tempore of the Senate;
successor = los.shift();
successor[0].office // Secretary of State;
successor = los.shift();
successor[0].office // Secretary of the Treasury;

Notice how each successor is being removed from the queue based on priority?

Conclusion

In this post I’ve described the basic idea of the queue data structure and, to see how it works in practice, used ES6 to implement a few different kinds of queue: a basic queue, a double-ended queue and a priority queue. The main differences between these kinds of queue can be summarized as follows:

  • In a basic queue items are added to the back and removed from the front.
  • In a doubled-ended queue items can be added to and removed from both the front and the back.
  • In a priority queue items are removed based on a manually defined priority.

Creating a binary search tree with ES6

I recently started reading Data Structures and Algorithms With JavaScript by Michael McMillan. Not having an academic background in computer science I’ve tended to shy away from this subject. With front-end development becoming an ever more complex endeavor, however, I felt it was about time to dive in and see what I’ve been missing. This and somebody recently asked me a question about binary search trees, about which I was utterly clueless. Guilt can be a good motivator, I guess.

What are trees?

McMillan defines a tree as a “nonlinear data structure that is used to store data in a hierarchical manner.” In this context a nonlinear data structure can be defined as a data structure in which data is arranged randomly, while a hierarchical data structure can be defined as a data structure in which data is organized into levels. A specific terminology is used when discussing trees. Some terms I’ll be using in this post include:

  • Root
  • Child
  • Parent
  • Leaf
  • Edge
  • Path
  • Level
  • Depth
  • Key value

Binary trees and binary search trees are special kinds of tree. In a binary tree, a node can have no more than two child nodes; in a binary search tree (BST), lesser values are stored in left nodes and greater values are stored in right nodes. The following diagram depicts a binary search tree.

A binary search tree with three levels. The root has a key value of 4 and has children with key values of 2 and 6. Both these nodes also have children of their own: The node with a key value of 2 is parent to nodes with key values of 1 and 3; the node with a key value of 6 is parent to nodes with key values of 5 and 7. All nodes on level 2 are leaves.

In this post I’ll be creating this BST using ES6 and adding some methods to it for adding and retrieving data. The code for my creation is available on CodePen.

Creating the BST

Creating the empty BST turns out to be relatively straightforward. All that’s needed is a class to represent a node and a class to represent the BST. A node holds references to the data it’s supposed to store as well as to its children (left and right nodes). The BST holds a reference to the root, which starts out as null. The basic classes end up looking like this:

class Node {
  constructor(data, left = null, right = null) {
    this.data = data;
    this.left = left;
    this.right = right;
  }
}

class BST {
  constructor() {
    this.root = null;
  }
}

Notice how the values of a node’s children are initialized using ES6 default parameters. Creating the BST is a simple matter of instantiating the BST class: const bst = new BST();.

Adding nodes to the BST

So far so good but an empty tree isn’t much use to anyone. In order to add nodes to the tree we’re going to need a method for doing so. Following is the insert() method McMillan defines, translated to ES6 from his ES5:

class BST {
  ...
  insert(data) {
    const node = new Node(data);
    if (this.root === null) {
      this.root = node;
    } else {
      let current = this.root;
      let parent;
      while(true) {
        parent = current;
        if (data < current.data) {
          current = current.left;
          if (current === null) {
            parent.left = node;
            break;
          }
        } else {
          current = current.right;
          if (current === null) {
            parent.right = node;
            break;
          }
        }
      }
    }
  }
}

The insert() method works by creating a new node and passing any data it was passed into the new node’s constructor. The method then does one of two things:

  1. If the BST doesn’t have a root, it makes the new node the root.
  2. If the BST does have a root, it traces a path through the BST until it finds an insertion point for the new node. Essentially this involves determining whether the new node should be inserted as the left or right child of a given parent. This is based on whether the new node’s value is lesser or greater than the parent’s value.

So let’s go ahead and insert some nodes and see how this works in practice.

bst.insert(4);
bst.insert(2);
bst.insert(6);
bst.insert(1);
bst.insert(3);
bst.insert(5);
bst.insert(7);

Following is a table that illustrates the inner workings of the insert() method for each of the values we’re inserting. (A key to the column headings follows the table.)

1234567
4nulln/an/an/an/ainsert
244trueleftnullinsert
644falserightnullinsert
144trueleft2iterate
n/a42trueleftnullinsert
344trueleft2iterate
n/a42falserightnullinsert
544falseright6iterate
n/a46trueleftnullinsert
744falseright6iterate
n/a46falserightnullinsert
  1. New node value
  2. Root node value
  3. Current node value
  4. New node value < current node value?
  5. New node should be inserted to left or right?
  6. Value of node at insertion point
  7. Result

Retrieving the minimum and maximum values from the BST

Two important implications of the insert() method are that:

  • The minimum value in the BST is always the leftmost value in the BST.
  • The maximum value in the BST is always the rightmost value in the BST.

Given these rules, defining methods to retrieve these values becomes fairly trivial.

Retrieving the minimum value

Let’s define a getMin() method for retrieving the minimum value from the BST:

class BST {
  ...
  getMin() {
    let current = this.root;
    while(current.left !== null) {
      current = current.left;
    }
    return current;
  }
}

The method can be called with a simple bst.getMin();. The following table illustrates the method’s inner workings:

Current nodeLeft nodeResult
42iterate
21iterate
1nullreturn

Retrieving the maximum value

Let’s now define a getMax() method for retrieving the maximum value from the BST:

class BST {
  ...
  getMax() {
    let current = this.root;
    while(current.right !== null) {
      current = current.right;
    }
    return current;
  }
}

This method can be called with a simple bst.getMax();. The following table illustrates the method’s inner workings:

Current nodeRight nodeResult
46iterate
67iterate
7nullreturn

Finding a specific node in the BST

Finding a specific node in the BST is a matter of tracing a path through the BST until either a value is found that matches the requested value or a value of null is found, in which case it can be safely said that the BST does not contain the requested value. Following is the find() method McMillan defines, once again translated to ES6 from his ES5:

class BST {
  ...
  find(data) {
    let current = this.root;
    while (current.data !== data) {
      if (data < current.data) {
        current = current.left;
      } else {
        current = current.right;
      }
      if (current === null) {
        return null;
      }
    }
    return current;
  }
}

Let’s try to find the node with a value of 3 by calling the method with bst.find(3);. Following is a table that illustrates the method’s inner workings. (A key to the column headings follows the table.)

123456
4falsetrueleft2iterate
2falsefalseright3iterate
3truen/an/an/areturn
  1. Current node value
  2. Is the current node value equal to the requested node value equal?
  3. Is the requested node value less than the current node value?
  4. Is the new current node to the left or right of the existing current node?
  5. New current node value
  6. Result

Conclusion

In this post we learned to differentiate between trees, binary trees and binary search trees (BSTs). We also created a BST using ES6 and added some methods to it for adding and retrieving data. Unfortunately we didn’t have time to cover some more advanced BST topics such as tree traversal and removing nodes–maybe this can be the subject of a future post.

URL routing with React

In my last post I demonstrated a possible approach to hash-based URL routing with Marionette. In this post I’ll demonstrate a possible approach to the same issue with React. To do this I’ll recreate the tabbed UI I developed last time, this time using React and React Router.

React Router

React Router is a routing library built on top of React. It allows a developer to implement URL routing in a React app using a variety of components. For my demo I’ll be using the following components:

Demo app with React

The code for the app is available on CodePen. Before diving into the implementation details I’ll first provide a brief overview of the requirements, data model and code design.

Requirements

The requirements for the React version of the app will be the same as those for the Marionette version.

Data model

The data model for the React version of the app will be similar to that of the Marionette version. The only difference is that the model will no longer require an active attribute. In the Marionette version this attribute was used by the tabs and tab content views to determine the visible state of a tab or tab panel. In the React version this job will be performed by comparing a parameter contained in the route to the id attribute of the model (see Creating the tabs and Creating the tab content).

Code design

In a previous post I demonstrated how React apps can be composed from a series of nested components. For example my ticking clock app was composed from two sub-components (Clock and AppHeader) nested within a top-level component (App). The top-level component was then rendered to the DOM with the ReactDOM render method. This app will be composed in a similar fashion and will consist of the following sub-components:

  • AppRouter
  • React stateless functional component
    Provides URL routing. Wraps ReactRouter.Router.
  • App
    React stateless functional component
    Serves as a container for the rest of the application code.
  • Tabs
    React stateless functional component
    Represents a collection of tabs.
  • TabContent
    React stateless functional component
    Represents a collection of tab panels.

It will also leverage a similar version of the loadInitialData function I created last time for loading tab content into the UI.

Setting up

Our app has a few dependencies: React, ReactDOM, React Router and jQuery. In the CodePen I add these dependencies from a CDN via the JavaScript Pen Settings. As a convenience, I also save shortcuts to the specific React Router components using destructuring assignment:

const {Router, Route, Link, hashHistory} = ReactRouter;

Loading the data

const loadInitialData = () => {
  const dfd = $.Deferred();
  dfd.resolve([
    {id: 1, title: 'Tab one', description: 'This is the first tab.'},
    {id: 2, title: 'Tab two', description: 'This is the second tab.'},
    {id: 3, title: 'Tab three', description: 'This is the third tab.'}
  ]);
  return dfd.promise();
};

The data for the app is loaded in a very similar fashion to last time: To mimic loading the data from a server, a function simply returns a promise of the data. This time the data is exposed as a plain JavaScript array instead of a Backbone collection. The Backbone collection worked nicely with the Backbone-based Marionette views I used last time; the JS array will work nicely with the stateless functional components I’ll be using for the React version’s views.

Creating the router

const AppRouter = (props) => {
  return (
    <Router history={hashHistory}>
      <Route path="/" component={App} {...props}>
        <Route path="/tabs/:tab" component={App} />
      </Route>
    </Router>
  );
};

To create the router I wrap a Router component in a stateless functional component (AppRouter). A stateless functional component is simply a function that takes the component’s props as an argument. I then use the Router’s history prop to instruct React Router to keep track of application state using hashHistory. Finally I use two Route configuration components to define the routes into the app:

  1. The first route matches the top-level path (/) and is associated with the yet-to-be-defined App component. Associating a route with a component basically just means that the component will be rendered whenever the route’s path is matched. This route also receives any props passed into AppRouter. Using spread syntax passes in any and all props.
  2. The second route matches a nested path (/tabs/:tab). This route is also associated with the App component.

Creating the app

const App = ({route, params}) => {
  const activeTab = Number(params.tab) || route.defaultTab;
  return (
    <div>
      <div><h1>URL routing with React</h1></div>
      <Tabs tabsCollection={route.tabsCollection} activeTab={activeTab} />
      <TabContent tabsCollection={route.tabsCollection} activeTab={activeTab} />
    </div>
  );
};

To create the App component I once again use a stateless functional component. Since App is associated with a route it receives route and params as props. The route prop corresponds to the route object that is rendering the component; the params prop corresponds to any URL params included in the route’s path.

App’s first responsibility is to identify the active tab. This information comes either from the URL params or, if no corresponding param is present, from the route object. It then returns the component’s element, which in this case comprises a simple heading and the sub-components Tabs and TabContent. Both sub-components take tabsCollection and activeTab as props.

Creating the tabs

const Tabs = ({tabsCollection, activeTab}) => {
  return (
    <ul>
      {tabsCollection.map(({id, title, description}) => {
        let tab;
        if (id === activeTab) {
          tab = <span>{title}</span>;
        } else {
          tab = <Link to={`/tabs/${id}`}>{title}</Link>;
        }
        return <li key={`tab-${id}`}>{tab}</li>
      })}
    </ul>
  );
};

A stateless functional component can also be used to create the Tabs component. The component returns an unordered list representing the collection of tabs with each list item corresponding to an individual tab. To determine whether an individual tab should be presented in an active or inactive state, the component uses conditional rendering . It does this by iterating over tabsCollection and comparing the tab model’s id attribute with the value of activeTab. If these values are the same, the tab will be rendered without a hyperlink so that it can’t be clicked; if they’re not the same, the tab will be rendered with a hyperlink so it can be clicked.

To render the hyperlink itself the component uses a Link component, a “location-aware” component used for navigation. In this case the value of the hyperlink’s href attribute is passed into the Link using the latter’s to prop. Notice how the prop’s value, /tabs/${id}, matches the nested path, /tabs/:tab, on the Router.

Finally the Tabs component returns the list item itself, adding a key prop to uniquely identify the element among its siblings.

Creating the tab content

const TabContent = ({tabsCollection, activeTab}) => {
  return (
    <div>
      {tabsCollection.map(({id, title, description}) => {
        if (id === activeTab) {
          return (
            <div key={`tab-panel-${id}`}>
              <h2>{title}</h2>
              <p>{description}</p>
            </div>
          );
        }
      })}
    </div>
  );
};

Almost the same process that was used to create the Tabs component can be used to create the TabContent component. Once again a stateless functional component returns a root element (in this case a DIV) that represents a collection of items (in this case tab panels). Inside the root element a JSX expression determines whether an individual item should be rendered in an active or inactive state. Again this is done by comparing the value of the tab model’s id attribute with the value of the activeTab prop. If these values are the same the item is shown; if they’re different the item is hidden. Once again a key prop is added to each item to uniquely identify the element among its siblings.

Acceptance testing

To test that the app meets the requirements, export the CodePen to a ZIP, unzip the archive and open index.html in a browser.

Conclusion

React, in conjunction with React Router, ostensibly makes it easier to implement hash-based URL routing than Marionette. The general approach facilitated by React of declaring and composing components–especially stateless functional components–cuts down on some of the procedural cruft around the instantiation of objects necessitated by Marionette. As such I find myself liking React’s approach, if for no other reason than it appears to require less code. In my experience, the less code, the better!

URL routing with Marionette

In my last post I compared some basic ways in which Marionette and React make it possible to develop single-page applications (SPAs). In my next posts I’ll compare the ways in which they facilitate URL routing within SPAs. To demonstrate the routing capabilities of the libraries I’ll develop a simple app in each. The app will take the form of a tabbed UI in which each tab can be loaded via the URL. My next post will concentrate on React; this post will concentrate on Marionette.

URL routing

What exactly is URL routing? A Simple Introduction to URL Routing provides the following definition:

URL routing means that you when click on a link, instead of being routed to another page, you stay on the same page and the content changes. When this happens, usually a “hash” will be appended to your current URL so that the user can go directly to the content they need as well as using back and forward buttons in the browser.

As this definition suggests URL routing is important because, at the risk of stating the obvious, content within an SPA is typically loaded on a single page. (This contrasts with traditional server-side applications in which any given user interaction, e.g., a form submission, typically loads a new page from the server.) Without URL routing SPAs break the back and forward buttons in the browser, making apps harder to navigate. In order to address such problems many contemporary client-side libraries and frameworks provide URL routing implementations.

Marionette.AppRouter

Marionette’s URL routing implementation comes in the form of the AppRouter class. Using this class it’s possible to define a pattern (route) that matches a URL and a callback function (route handler) that is invoked whenever a route is matched. AppRouter offers two approaches for defining routes and route handlers:

  1. appRoutes: When using this approach the route handler must be present on a “controller” object provided to the router.
  2. routes: When using this approach the route handler must be present on the router itself.

Demo app with Marionette

The demo app will use the routes approach, in which the route handler must be present on the router. The router will define routes and route handlers such that when a route is matched the the appropriate tab will be loaded into the UI. The code for the app is available on CodePen. Before diving into the implementation details I’ll first provide a brief overview of the requirements, data model and code design.

Requirements

First let’s define some basic requirements for the app:

  1. Visiting the page for the first time should load the first tab.
  2. It should be possible to load tabs by clicking on links.
  3. It should be possible to load tabs by changing the URL hash to match a route. For example a route of “tabs/2” should load the second tab.
  4. It should be possible to load tabs using the browser’s back and forward buttons.
  5. Refreshing the page should preserve the URL and load the corresponding tab. For example if the second tab was loaded prior to refreshing the page, the second tab should still be loaded after refreshing the page.

Data model

The app employs a simple data model to represent the idea of a tab.

id: Number
title: String
description: String
active: Boolean

The model’s id attribute uniquely identifies the tab. The active attribute indicates whether a tab is in an active or inactive state. The title and description attributes are just for display purposes.

Code design

The ticking clock app I developed for my last post demonstrated how a Marionette app can be composed by piecing together a number of the library’s classes. For example it used the LayoutView class to contain nested views, the Region class to contain the layout and the Application class to contain the rest of the code. The app I’ve developed for this post reuses some of these classes and introduces some new ones. The basic building blocks of the app are as follows:

  • loadInitialData
    Function
    Loads the data that provides the content for the UI.
  • Tabs
    Extends Marionette.CollectionView
    Represents a collection of tabs.
  • Tab
    Extends Marionette.ItemView
    Represents an individual tab. Clicking a tab loads the corresponding tab panel and changes the URL, engaging the router.
  • TabContent
    Extends Marionette.CollectionView
    Represents a collection of tab panels.
  • TabPanel
    Extends Marionette.ItemView
    Represents an individual tab panel.
  • Layout
    Extends Marionette.LayoutView
    Serves as a container for the tabs and tab content views.
  • Router
    Extends Marionette.AppRouter
    Defines the route and route handler for the application. A matching route invokes the route handler; the route handler loads the correct tab into the page.
  • App
    Extends Marionette.Application
    Serves as a container for the rest of the application code.

The remainder of this post discusses how these building blocks fit together.

Loading the data

The initial set of data for the app is loaded via the aptly named function loadInitialData.

const loadInitialData = () => {
  const dfd = $.Deferred();
  dfd.resolve(
    new Backbone.Collection(
      [
        {id: 1, title: 'Tab one', description: 'This is tab one.'},
        {id: 2, title: 'Tab two', description: 'This is tab two.'},
        {id: 3, title: 'Tab three', description: 'This is tab three.'}
      ]
    )
  );
  return dfd.promise();
};

The function creates a Backbone collection based on the data model. To mimic asynchronous loading the function uses a jQuery Deferred Object to expose the data. Invoking the deferred’s promise method exposes another deferred method, then, which allows an additional handler to be attached. The handler receives the loaded data as input. The then method itself can be chained to the invocation of loadInitialData.

loadInitialData().then((initialData) => {
  // Use initialData here
});

Creating the tabs

Since the data is exposed as a Backbone collection the app can use a Marionette CollectionView for rendering the models. From the Marionette docs:

The CollectionView will loop through all of the models in the specified collection, render each of them using a specified childView, then append the results of the child view’s el to the collection view’s el.

As this definition implies, a CollectionView actually consists of two separate views: a parent view for rendering the collection and a child view for rendering each of the individual models. Within the app these views are represented by Tabs and Tab respectively.

const Tab = Marionette.ItemView.extend({
  tagName: 'li',
  getTemplate() {
    return _.template((!this.model.get('active')) ? '<a href="#tabs/<%= id %>"><%= title %></a>' : '<%= title %>');
  }
});

const Tabs = Marionette.CollectionView.extend({
  childView: Tab,
  tagName: 'ul',
  collectionEvents: {
    'change': 'render'
  }
});

The Tabs class identifies the Tab class as its child view using the aptly named childView property. It also declares its root HTML element (UL) with tagName. Finally it uses the collectionEvents property to instruct Marionette to re-render the collection whenever the latter’s change event fires.

Meanwhile the Tab class declares its own root HTML element (LI) with tagName. It also leverages the getTemplate method to decide upon a template to use depending on the state of the model. An individual tab exists in one of two possible states: active or inactive. When the tab is active, the model’s title should be rendered without a hyperlink so that it can’t be clicked; when the tab is inactive, the model’s title should be rendered with a hyperlink so that it can be clicked.

Creating the tab content

The process for creating the tab content resembles the process for creating the tabs: A parent view renders the collection and an associated child view renders each of the individual models. These views are represented in the app by TabContent and TabPanel respectively.

const TabPanel = Marionette.ItemView.extend({
  template: _.template('<div><h2><%= title %></h2><p><%= description %></p></div>'),
  onBeforeAttach() {
    return (!this.model.get('active')) ? this.$el.hide() : null;
  }
});

const TabContent = Marionette.CollectionView.extend({
  childView: TabPanel,
  collectionEvents: {
    'change': 'render'
  }
});

The TabContent class identifies the TabPanel class as its child view and, just like the Tabs class, instructs Marionette to re-render the collection whenever the collection’s change event fires. Unlike the Tabs class, TabContent doesn’t explicitly declare its root HTML element–in this case Marionette uses a default (DIV).

Meanwhile the TabPanel class defines its template and, using the onBeforeAttach lifecycle method, decides whether a given panel should be shown or hidden in the DOM. Just as a tab exists in an active or inactive state, so too do tab panels. When a panel is active it should be shown; when inactive, hidden.

Creating the layout

Since the UI has two main views–represented by Tabs and TabContent–a Marionette LayoutView can be used to contain them. The Marionette docs again:

A LayoutView is a hybrid of an ItemView and a collection of Region objects. They are ideal for rendering application layouts with multiple sub-regions managed by specified region managers.

Sounds ideal! All that remains is to define the class.

const Layout = Marionette.LayoutView.extend({
  template: _.template('<div><h1>URL routing with Marionette</h1></div><div id="tabs-region"></div><div id="tab-content-region"></div>'),
  regions: {
    tabsRegion: '#tabs-region', 
    tabContentRegion: '#tab-content-region'
  },
  initialize(options) { 
    this.tabs = options.tabs; 
    this.tabContent = options.tabContent; 
  }, 
  onShow() { 
    this.tabsRegion.show(this.tabs);
    this.tabContentRegion.show(this.tabContent);
  }
});

The Layout class defines a template with elements corresponding to the regions defined in the regions hash. Then, after the template has been added to the DOM during the onShow lifecycle event, it adds the views to the DOM with region.show(view).

Creating the router

The next step is to define the Router class that will be responsible for loading the correct tab into the UI.

const Router = Marionette.AppRouter.extend({
  routes: {
    "tabs/:tab": "tab",
    "*path": "default"
  },
  initialize(options) {
    this.collection = options.collection;
    this.defaultTab = options.defaultTab;
  },
  tab(tab) {
    const tab_ = --tab;
    this.collection.set(this.collection.map((model, index) => {
      model.set('active', tab_ === index);
      return model;
    }));
  },
  default() {
    this.tab(this.defaultTab);
  }
});

Router’s routes property maps a route (tabs/:tab) to a route handler (changeTab). The route handler works by updating the Backbone collection that provides the data to the UI. Specifically it sets the active attribute to true on the model whose id matches the parameter contained in the route (:tab). Updating the collection in this way causes the collection’s change event to fire, which in turns causes the Tabs and TabContent views to re-render (see Creating the tabs and Creating the tab content).

Creating the application

With both the router and the views defined all that remains is to create the top-level application for containing the rest of the code.

const App = Marionette.Application.extend({
  initialize(options) {
    this.rootElement = options.rootElement;
    this.defaultTab = options.defaultTab;
    this.collection = options.collection;
  },
  onBeforeStart() {
    this.router = new Router({
      collection: this.collection,
      defaultTab: this.defaultTab
    });
    this.layout = new Layout({
      tabs: new Tabs({
        collection: this.collection
      }),
      tabContent: new TabContent({
        collection: this.collection
      })
    });
    this.region = new Marionette.Region({
      el: this.rootElement
    });
  },
  onStart() {
    Backbone.history.start();
    this.region.show(this.layout);
  }
});

The App class employs three methods, all native to Marionette Application:

  1. initialize: This method simply saves references to any parameters passed into it. The rootElement parameter corresponds to the id attribute of the HTML element in which the layout view is embedded: <div id="app"></div>.
  2. onBeforeStart: This method instantiates several of the classes required by the application, including Router, Layout, Tabs and TabContent. Notice how the collection loaded by loadInitialData is passed into Router, Tabs and TabContent. Since all three classes reference the same collection, all three classes receive Router’s updates to the collection, the updates being necessary for re-rendering.
  3. onStart: This method sets the initial state of the app. Specifically it monitors for “hashchange” events via Backbone.history.start() and adds the layout view to the DOM.

The App class is instantiated after the deferred object created by loadInitialData has been resolved:

loadInitialData().then((initialData) => {
  const app = new App({
    rootElement: '#app',
    defaultTab: '1',
    collection: initialData
  });
  app.start();
});

Acceptance testing

To test that the app meets the requirements stated above, export the CodePen to a ZIP, unzip the archive and open index.html in a browser.

Conclusion

Such requirements would not be as easy to implement without AppRouter, Marionette’s implementation of URL routing. In my next post I’ll attempt to recreate the exact same app with the exact same behaviors using React. Not that AppRouter made it too hard but let’s see if React makes it any easier!

Comparing Marionette and React

The JavaScript landscape is changing rapidly. If you’re anything like me you’ll know that trying to keep current amidst the churn can be frustrating. We can but try, however. To this end I’ve been learning about React and thinking about how it compares to another UI library I’ve used in the past: the Backbone-based library, Marionette.js.

Why React?

  1. React is relatively well-liked. In the 2016 State of JavaScript “Frameworks” survey 3044 respondents claimed to have used React before, 92% of whom claimed they would use it again. React achieved the highest satisfaction rating, Vue finishing a close second with 89%. Vue had only a fraction of React’s users, however: Only 577 respondents claimed to have used it before. Angular 2 (1092/65%), Ember (850/48%) and Angular (3391/47%) lagged significantly behind React and Vue in one or both regards.
  2. Marionette is relatively obscure. In the same survey Marionette was mentioned by just 41 respondents. No data was given for whether these respondents had used it before or would use it again. Meanwhile Backbone, upon which Marionette is based, acquired a relatively low satisfaction rating: Although a relatively high number of users (2077) claimed to have used it before, only about one-third (32%) claimed they would use it again.
  3. React is more library than framework. Unlike full-blown JavaScript frameworks such as Angular, React is mainly a view library. In this sense it it tempting to see React as a more direct replacement for Marionette, much of whose value in my opinion comes from the views it provides. In principle I tend to prefer libraries over frameworks, chiefly because the latter tend to be more prescriptive in terms of how apps should be constructed.

In summary React appears to be a pretty safe choice that should provide a natural progression from Marionette. Let’s put that to the test by creating an app. We’ll implement a simple ticking clock (featured in the React docs) doing it in both Marionette and React so we can compare.

Creating an application

When creating a JS app it’s nice to have a single object that serves as an entry point to the rest of the UI. Both Marionette and React embrace this concept: Marionette via a dedicated Application class; React via a top-level Component that serves as a parent to other, “child” components. So let’s begin implementing our ticking clock by creating a simple application.

Requirements

  • Output “The time according to {library} is:” to the page

Code

Marionette implementation

To implement the requirement in Marionette:

  1. We created a callback function (onStart) for adding a view containing the output to the DOM. This function runs when the application starts. It first creates a region for showing the view, then creates the view, then shows the view in the region.
  2. We created an instance of the Application class.
  3. We attached an event listener (“start”) to the application instance, passing it the callback function defined in #1.
  4. We started the application via the “start” method on the application instance.

React implementation

To implement the requirement in React:

  1. We created a component for adding the output to the DOM. In this case the component is a functional component but as we’ll see later it could also have been a class component.
  2. We added the output to the DOM via ReactDOM.render. This method basically just merges our component with the DOM, similar to Marionette’s region.show method in #1 above.

Analysis

We can see that React makes life a little easier for getting an app off the ground. While Marionette introduces several different concepts right off the bat (Applications, Regions and Views), React introduces just one (Component). React also appears to do things a little more automatically, e.g., no need to officially start the application or create a listener for the start event. React’s syntax also ends up being cleaner and more terse.

Character count

  • Marionette: 296
  • React: 121

Nesting views

Another essential feature of a JS view library is the ability to nest one view within another view. Nested views reflect the treelike structure of the DOM tree and help to encapsulate functionality within clearly defined modules. Both Marionette and React embrace the concept of nested views: Marionette via its dedicated LayoutView class; React via components. So let’s enhance our fledgling app to illustrate how nested views work in each case.

Requirements

  • Add a view for displaying the title
  • Add a view for displaying the clock

Code

Marionette implementation

To implement the requirements in Marionette:

  1. We added two regions to our LayoutView: one for the title, one for the clock. The LayoutView is a special type of view provided by Marionette distinctly for layout purposes.
  2. We created a callback function (onShowLayoutView) for adding views to the regions defined in the layout view. This function runs after the layout view has been added to the DOM.
  3. We attached an event listener (“show”) to the layout view, passing it the callback function defined in #2. Waiting for the parent view to be added to the DOM before attempting to add the child views ensures Marionette will not try to add the latter before the former is ready.

React implementation

To implement the requirements in React:

  1. We created two user-defined components (AppHeader and AppBody) to represent the nested views.
  2. We rendered the nested components in the parent component using the latter’s “render” method.

Analysis

React again seems to make things easier for us. Marionette introduces yet another concept (ItemView); React allows us to reuse an existing one (Component). Marionette makes us do more work (e.g., we have to manually instantiate all new objects); React does this work for us (we can simply define a component declaratively and React will create it for us). React’s syntax again ends up being cleaner and more terse.

CHARACTER COUNT

  • Marionette: 639
  • React: 252

Managing state

The concept of state as it applies to JS apps is admittedly rather new to me: With hindsight I suppose it’s just one of those things I’ve been doing with JS/jQuery/Backbone/Marionette without really thinking too much about how I’m doing it.

To be going on with let’s just define managing state as managing change. In the context of an app the source of change could be manual or automatic. For example:

  • A user enters a value in a text box (manual)
  • A timer updates a value every so often (automatic)

What these cases have in common is that something happens in the app and the app has to respond somehow. In other words, the app must undergo a change in state.

Once again let’s illustrate this by way of our app, this time by implementing the ticking behavior of the clock.

Requirements

  • Store the date so that it can be updated
  • Set a timer to update the date every second
  • Display the date (and redisplay it whenever it’s updated)
  • Clear the timer if the component is ever removed from the page

Code

Marionette implementation

To implement the requirements in Marionette:

  1. We created an ItemView to represent the clock.
  2. We created a Backbone model to store the date, passing the model into the view.
  3. We created a timer (setInterval) for updating the date and set it to update the model’s date attribute every second. We did this within view’s constructor function (initialize).
  4. We created an Underscore template for displaying the date, saving it to the view’s template property.
  5. We instructed Marionette to re-display the view whenever the model’s date attribute changes. For this we used the view’s modelEvents property.
  6. We instructed Marionette to clear the timer if the view is ever removed from the DOM.

React implementation

To implement the requirements in React:

  1. We created a component to represent the clock.
  2. We stored the date in the component’s local state.
  3. We created the timer for updating the date and set it to update the component’s local state every second. We did this in the component’s componentDidMount lifecycle method. This method is called after the component has been rendered to the DOM.
  4. We created a JSX expression for displaying the date, outputting it right there in the component’s render method.
  5. We instructed React to clear the timer if the component is ever removed from the DOM. We did this in the component’s componentWillUnmount lifecycle method.

Analysis

Marionette and React are a little more in line with each other here. There are a similar number of steps involved in implementing this functionality in either framework. The only major difference appears to be related to re-displaying the date. We have to instruct Marionette explicitly to listen for changes to the model so that it knows when to re-display the view. React, on the other hand, propagates changes in state to the component via props.

CHARACTER COUNT

  • Marionette: 976
  • React: 552

Conclusion

Clearly this comparison only touches on the basics of Marionette and React. Equally clearly our ticking clock app is simplistic to say the least. Nonetheless I think the exercise serves to illustrate some interesting differences between the two libraries, differences that for the most part come down in favor of React.

  • I like React’s declarative nature. React seems to do a bit more of the grunt work for us than Marionette. React allows us to declare components; Marionette makes us create objects.
  • I like React’s functional components. Functional code, characterized by pure functions and immutable data, in theory produces more reliable results than object-oriented code. React leans toward the former; Marionette, the latter.
  • I like React’s state management. With React, changes in state are automatically propagated to components via props. With Marionette, again there may be some manual work involved depending on your use case.

Site, resurrected

Lately I’ve been busy resurrecting this site, using Docker containers to automate the creation of the runtime environment. In case you haven’t heard of Docker containers, here’s what the official Docker site has to say about them:

Docker containers wrap a piece of software in a complete filesystem that contains everything needed to run: code, runtime, system tools, system libraries – anything that can be installed on a server. This guarantees that the software will always run the same, regardless of its environment.

While a technology like Docker is probably more likely to be used for enterprise applications, the need for software to “always run the same” is just as applicable to personal sites like this. In this post I’ll try to show you how I’ve leveraged Docker to bring some consistency to my site’s development and production environments.

Software requirements

A WordPress site such as this needs a few different pieces of software in order to run:

  • WordPress
  • Apache HTTPD (for serving WordPress files)
  • MySQL (for storing WordPress data)

Without something like Docker I would have to install and configure this software manually both on my local machine (for development) and on my remote machine (for production). With Docker, however, I can simply create some YAML-based config files and have the Docker Compose component do the work for me. Following’s how I did this.

Docker Compose config

To define the software requirements, or “services” in Dockerspeak, I created three config files: one config file per environment (development and production) and one config file for the attributes of the services that are common to these environments.

common-services.yml
Contains service definitions for development and production.
docker-compose.yml
Contains service definitions for development only. Extends services defined in common-services.yml via the extends config option.
docker-compose.production.yml
Contains service definitions for production only. Extends services defined in common-services.yml via the extends config option.

MySQL service definition

The MySQL service definition is identical in both development and production. As such it can be defined in common-services.yml and brought into a specific environment by extension.

Here’s the definition (from common-services.yml):

services:

...

  wordpressdb:

    image: mysql:5.7

    container_name: wordpressdb

    env_file: ./wordpressdb/env/wordpress.common.env

    volumes:

      - ./wordpressdb/cnf:/etc/mysql/conf.d/

      - ../data:/var/lib/mysql

...

volumes:

  data:

… and here’s the extension (from docker-compose.yml and docker-compose.production.yml):

  wordpressdb:

    extends:

      file: common-services.yml

      service: wordpressdb



MySQL service config options

image
Defines the MySQL image upon which to base the container.
container_name
Defines the name to use for the container. Not strictly necessary but without it Docker will create a name by itself.
env_file
Points to a plain-text file that defines environment variables that will be accessible within the container, e.g., the username and password for the database.
volumes
Defines directory mappings between host and container. One such mapping is a data volume that will be used to persist data to the host. (See top-level volumes config option in common-services.yml.)

WordPress service definition

The WordPress service definition has some differences between development and production. As such it can be defined in part in common-services.yml and extended in a specific environment.

Here’s the definiton (from common-services.yml):

  wordpress:

    image: wordpress:4.7-apache

    container_name: wordpress

    ports:

      - 80:80

… and here’s the extension (from docker-compose.yml and docker-compose.production.yml):

  wordpress:

    extends:

      file: common-services.yml

      service: wordpress

    volumes:

      - ../html:/var/www/html

      - ./wordpress/conf:/etc/apache2/sites-available

    links:

      - wordpressdb:mysql

    env_file:

      - ./wordpress/env/wordpress.common.env

      - ./wordpress/env/wordpress.${environment}.env

WordPress service config options

image
Defines the WordPress image upon which to base the container. This particular version of WordPress includes Apache HTTPD, so I don’t have to worry about creating a separate service just for this.
container_name
Defines the name to use for the container.
ports
Defines port mappings between host and container. In this case port 80 on the host is being mapped to the same port on the container. This allows me to run the site on the default HTTP port in both development and production.
volumes
Defines directory mappings between host and container. In this case the config tells the container where on the host it can expect to find the WordPress files and the Apache vhost config file.
links
Allows the WordPress container to access the MySQL container.
env_file
Points to a plain-text file that defines environment variables that will be accessible within the container. In this case there are two such files: one for environment variables that are common to environments and one for environment variables that are specific to an environment, e.g., WP_SITEURL, WP_HOME.

Building the services

With this configuration in place it becomes trivial to build the services for development and production.

To build for development I can simply update my repo on my local machine and enter the following command: docker-compose up --build -d. This command will combine the config from common-services.yml and docker-compose.yml and use the result.

To build for production I can simply update my repo on my remote machine and enter the following command: docker-compose -f docker-compose.production.yml --build -d. This command will combine the config from common-services.yml and docker-compose.production.yml and use the result.

Organizing Passwords, PII and Email

As I prepare to start a new job on Monday, the first new job I’ll have started for more than seven years (God, please don’t let me get beaten up behind the bike sheds on my first day!), I’ve been making an effort to organize the detritus of my online life.

Loyal readers of this blog—if indeed such emotionally troubled individuals are currently blessed with reading privileges in the various jailhouses and detention centers they occupy—will recall my inaugural post about a password manager application I’ve been using called 1Password, made by a Canadian software company called AgileBits. As the name “1Password” suggests, the (IMO truly magnificent) app allows you to create one strong, memorable password that gives you access to a database into which you can enter all your other Web site log-in credentials, the passwords for which can be as strong and unmemorable as you wish. As if this functionality were not useful enough, the app also gives you the ability to log in to your favorite Web sites automatically, which in effect frees you from the very 21st-century hassle of entering slight variants of the same credentials into Web-based log-in forms time after tedious time.

In more recent days I’ve also been exploring some of 1Password’s additional capabilities, such as the way it allows you to store personal information (e.g., bank account, credit card and driver’s license information), in what the app calls “Wallet items.” Given my increasing reliance on the Web for performing financial and commercial transactions, having this information stored centrally in such a fashion has been proving pretty useful. Now, for instance, I don’t have to search my coat pockets for my actual wallet whenever I need my credit card for Turnov’s Small and Short men’s shop, a card whose number, expiry date and security code have escaped—or indeed have never been stored in—my memory. This liberating upshot is all the more satisfying given the numerous types of coat I’ve had to wear this winter here in the environs of Alexandria, Va., where the weather has been changing as much as a chameleon with multiple personality disorder in a dressing room.

To turn to, or at least look askance at, the subject of email organization, I’ve also been striving to exert a measure of control over my hitherto untamed Gmail inbox. This effort has necessitated creating a large number of labels and a not small number of filters. I now have as many as 17 top-level labels for my received mail, each of which has sub-labels that allow for more specific content characterization. Without wishing to give you too much insight into my personal life, here are the top-level labels I’ve settled on to-date:

  • Banking
  • Car (“Pollution-Spewing Heap of Rust” would be more accurate)
  • Credit Cards
  • Entertainment
  • Health & Fitness (just the one email under this label so far)
  • Home
  • Insurance
  • Investments
  • Local Government
  • Meetups (nice to get out of Chez Smith from time to time)
  • Personal
  • Personal Finance
  • Shopping
  • Taxes (would “Death and Taxes” last longer?)
  • Travel & Transit
  • Web Services
  • Work

Readers who take their mobility for granted might argue that going to such organizational lengths as these evinces a certain anal-retentiveness in me. And while I wouldn’t render them entirely unable to walk for arguing this way, I would suggest that if the feeling one gets from having an organized inbox is preferable to the feeling one gets from having a disorganized inbox, then the better feeling alone justifies the effort one has made in organizing it. So there, with highly polished brass knobs on!

In conclusion, I think “personal information architecture” projects like these will increase in importance as our weird, wired world becomes more data-centric and IT-reliant. Instead of finishing with a supposition, however, I’ll finish with a question: Do you—my loyal, incarcerated readers—attempt to organize your inboxes? Or do you allow them to grow untamed, like the tangled hair of an aging hippie?

Creating an Interactive Number-Guessing Game With HTML, CSS and JavaScript (Part 2)

A diagram depicting the five user interface screens of my interactive number-guessing game.

This is the second in a series of posts on the creation of my interactive number-guessing game, which for the moment is called “Numbers Up”—I hope you like the name! The game is available on jsFiddle if you want to give it a whirl or tinker with the code.

A diagram depicting the game screen of my interactive number-guessing game.

In the first post of the series I discussed the creation of the basic wireframe for the game. To summarize, I decided that the game’s user interface required five screens:

  1. A screen for the game interface;
  2. A “splash” (or introductory) screen;
  3. A screen where the game’s settings could be adjusted;
  4. A screen that would display in the event of the player’s winning the game; and
  5. A screen that would display in the event of the player’s losing the game.

I also decided that the screen for the game interface should occupy the main window, while the four other screens should be represented by modal dialogs.

First I’ll discuss the game interface screen. The HTML structure for the screen appears as follows:

<div id="outer-wrapper">
  <div id="inner-wrapper">
    <div id="play">
      <h1>Numbers Up</h1>
      <div id="game-board">
        <div id="gauges">
          <div id="guess">
            <span class="label">Current</span>
            <span class="value">
              <!-- guess -->
            </span>
          </div>
          <div id="guess-accuracy">
            <span class="label">Low/High</span>
            <span class="value">
              <!-- guess accuracy -->
            </span>
          </div>
          <div id="guesses-allowed">
            <span class="label">Allowed</span>
            <span class="value">
              <!-- guesses allowed -->
            </span>
          </div>
          <div id="guesses-made">
            <span class="label">Made</span>
            <span class="value">
              <!-- guesses made -->
            </span>
          </div>
          <div id="guesses-remaining">
            <span class="label">Remaining</span>
            <span class="value">
              <!-- guesses remaining -->
            </span>
          </div>
        </div>
        <div id="tiles">
          <!-- tiles -->
        </div>
      </div>
      <div class="buttons">
        <a href="#splash" class="button button-red dialog">Quit</a>
      </div>
    </div>
  </div>
</div>

Notice the comments that appear in div#tiles (a container for the game board tiles) and the span.value elements (containers for the game board gauge values). These comments represent content that was to be inserted into the DOM dynamically via JavaScript. I’ll return to the subject of the gauge values in a future post. I’ll tackle the subject of the game board tiles right away, however, as these would need to be inserted into the DOM before any user events could be attached to them. My prepareTiles function does just this:

function prepareTiles () {
  var tiles = '',
	  tile,
	lowTile = settings.lowTile,
	highTile = settings.highTile;
  for (var i = lowTile; i < (highTile + 1); i++) {
	tile = '<a class="tile" href="#' + i + '">' + i + '</a>';
	tiles += '
‘ + tile + ‘

‘; } $(‘#tiles’).append(tiles); }

In this function I use a JS for loop to build an HTML string representing the game board tiles. The loop begins at the number of the lowest tile (lowTile) and ends at the number of the highest tile (highTile). After the loop has finished I use jQuery’s append method to insert the HTML string into the DOM. Notice that the entire HTML string is built before it’s appended to the DOM. This way the DOM needs to be accessed only once rather than the 100 times it would need to have been accessed had each tile been appended individually–an important performance consideration. The settings object I refer to during the function’s variable instantiation relates to an object I created to store information about the game, such as UI strings, overlay properties, and, in this case, the numbers of the lowest and highest tiles.

My next task was to prepare the modal dialogs. Each dialog is represented in the HTML structure by a div element with a class of “dialog”. To transform these frumpy HTML elements into sexy modal dialogs I used the Overlay component provided by the jQuery Tools UI library. My prepareDialogs function takes care of this:

function prepareDialogs () {
  $('div.dialog').each(function (index) {
	var el = $(this);
	(index === 0) ? el.overlay($.extend({}, overlays.attributes.basic, overlays.attributes.autoload)) : el.overlay(overlays.attributes.basic);
  });
}

In this function I use jQuery’s each method to loop through the collection of div.dialog elements. I then call jQuery Tools’ overlay method on each element in the collection to bestow it with modal-dialog functionality. The element that appears first in the collection (i.e., the element whose index is equal to zero)—in this case, the div element containing the splash screen content—is required to load automatically, while the other dialogs are required to load manually (i.e., upon the activation of some triggering element). To implement this distinct functionality, I use jQuery’s extend method to merge two separate configuration objects. The first object stores properties common to all overlays; the second object stores properties common to automatically triggered overlays. The following code snippet, which forms part of the settings object I introduced earlier, represents the configuration objects:

…
overlays: {
  attributes: {
    basic: {
      closeOnClick:false,
      mask: {
        color:'#333',
        loadSpeed:100,
        maskId: 'mask',
        opacity:0.75,
        zIndex:9998
      },
      oneInstance:false
    },
    autoload: {
      load:true
    }
  }
}
…

My next task was to prepare the elements that would act as triggers for the modal dialogs. If you examine the first of the UI diagrams you’ll see that six such elements exist:

  1. The splash button in the game-board screen
  2. The settings button in the splash dialog
  3. The first splash button in the settings dialog
  4. The second splash button in the settings dialog
  5. The splash button in the win dialog
  6. The splash button in the lose dialog

Each button is represented in the HTML structure by an a element with a class of “dialog.” My prepareDialogTriggers function bestows each element with its dialog-triggering functionality:

function prepareDialogTriggers () {
  $('a.dialog').each(function () {
	var el = $(this),
	  href = el.attr('href'),
	  id = href.slice(1);
	el.attr('rel', href).overlay(overlays.attributes.basic).bind('click', function (e) {
	  openOverlay(id);
	  e.preventDefault();
	});
  });
}

In this function I again turn to jQuery’s each method, this time to loop through the collection of a.dialog elements. The href variable saves the value of the a element’s href attribute; the id variable saves the value of the anchor’s href attribute excluding the hash mark (i.e., the value of the id attribute of the div element corresponding to the dialog to be triggered). For the sake of jQuery Tools’ overlay method, I then add to each anchor element a rel attribute using the value saved in the href variable. I then call jQuery Tools’ overlay method on the a element, passing a configuration object to the method specifying that the dialog should be loaded manually. Finally I use jQuery to attach a click event to the anchor element, specifying the requisite additional behavior in the event’s callback function. jQuery users will know that the preventDefault method, when attached to an event object, cancels the default behavior that occurs upon the firing of an event—in this case, it prevents the browser from resolving the URL found in the a element’s href attribute. My openOverlay function, meanwhile, specifies additional behavior that’s required for the purposes of my particular application:

function openOverlay (id) {
  $('div.dialog').hide();
  $.mask.getMask().show();
  $('div[id=' + id + ']').show();
}

In this function I use jQuery’s hide method to hide all other div.dialog elements. I then use the jQuery Tools API to retrieve and show the UI library’s overlay mask (the darkened background that appears when a modal dialog is opened). I then use jQuery’s show method to show the necessary dialog, i.e., the div element with an id attribute whose value corresponds to the value saved in the prepareDialogs function’s id variable, which is passed into openOverlay as its only argument.

With that said (and the word count of this post rapidly approaching the 1250 mark!), I’ll conclude this week’s installment. Be sure to come back next week for another!