Ryan Lanciaux

Random programming blog.

Thoughts on Microsoft Surface

Last fall, I won a Microsoft Surface 2 as part of the Surface Remix Project contest. I always love to win gadgets but this was a bit more exciting to me as I am a hobby music producer (shameless link to some of my music). I was initially planning on using the device for the music app/remix blade, however, after I had used the device a little over a week, I realized that there was a lot more to the Surface than just another device trying to make waves in the tablet market. I have since purchased a Surface Pro (1) and am really liking it.

I want to be very clear here I’m stepping into a boundary that could make me sound very fanboy-ish. While I am generally a bit more fond of Microsoft technology than some (.NET developer by trade), I try to avoid using a gadget / language / whatever simply based on the brand. To put it another way, I am more of a fan of technology than any particular company – I like the advances that each competitor brings because overall it helps the consumer.

Now that I said all that, I want to discuss my initial thoughts on what I think Microsoft is bringing to the table with the Surface and where I hope that’s going…

Hybrid OS

Initially upgrading to Windows 8 at home had resulted in me switching to Ubuntu until 8.1 came out. My reaction may have been a bit extreme, but I really was not a fan of how many aspects of the OS. While 8.1 is a ton better, seeing the operating system on the tablet really made Windows feel a bit more like it was likely intended. On my desktop I found myself using the Windows UI (or UI style formerly known as Metro) as a task launcher and using mostly desktop apps. On the Surface, however, I kind of wish I could turn desktop mode off entirely. That wouldn’t workout so well on the Pro, but it would be cool if that could be kind of a combination of the two – just Windows UI when no keyboard/dock is attached and more like the desktop when docked.

I had always hoped that there would be a day when I would have one device that could function as my computer and phone (I guess kind of like the Ubuntu phone concept). While the Surface is not entirely where I would like this type of technology to end up, it is definitely a step in the right direction. As I said before, if it were entirely up to me, there would be some changes I would make to Windows 8 but it seems a step closer to making this a reality (still Windows phones would need to run the same OS – not just same kernel).

Niche Markets

As stated above, I won the Surface as part of a contest that Microsoft was having to promote their yet-to-be released Remix Cover. I think it’s fantastic that music producers are given a first-class experience in the Surface world. The remix blade feels like it’s a natural part of the Surface – not an add-on. I would love to see more things like this for the device.

Mobility

The weight of the Surface pales in comparison to any laptop I’ve ever owned – it’s almost an after-thought to pack it up and bring it when traveling. The type cover feels more natural to me than any iPad keyboard I’ve used and works well to protect the screen.

Combined with a dock, such as the Plugable UD-3900, I can run multiple monitors and hook up to a real keyboard / mouse. When I need to head out, I simply can unplug the dock from the USB port and use it as a tablet or laptop.

Processor

The Surface 2 felt pretty zippy but the fact that it ran Windows RT was a bit of a negative for me as a developer. The pro one has been fast enough so far for most web development tasks I’ve thrown at it. I wouldn’t necessarily play VM Inception with it but it’s worked out okay for me so far. I imagine the 2 with 8gb of RAM would fair even better.

Wrapping Up

I started this post in November – left it for a couple months and finally decided to finish it. My feelings toward the Surface are still the same. The pro seems like a fantastic developer machine (if you are in the windows realm) and the ability to have a specialized experience for niche applications makes it a great little device.

Fake Popovers for Angular-xeditable

I was recently working on a project with AngularJS and xeditable (if you’re not familiar, xeditable is an awesome library for inline editing). There is an Angular version of xeditable but the popover editing functionality is not implemented yet (it’s in the roadmap). Instead of using the original version of xeditable and implementing custom directives or try to add the popover functionality to the project, I decided to see if I could make the popover using just CSS – this happened to be more inline with my timeframe.

Take a look at the original (non-angular) popover:

Starting out, I noticed that clicking on the link of an xeditable element was showing an input element (and buttons) in a form and hiding the link. To mimic the popover, the link and the form should both be visible when the form was activated, however, the form should be positioned a bit higher than the link. Working with a forked version of vitalets’ jsfiddle example, I wrapped the initial links in <span class='item-wrapper'></span> – from there, I edited the link and the form’s CSS as follows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.item-wrapper a{
    /* make the link always show up */
    display: inline !important;
}

.item-wrapper{
    /* make absolutely positioned children constrained to this box*/
    position: relative;
}

.item-wrapper form {
    background: #FFF;
    border: 1px solid #AAA;
    border-radius: 5px;
    display: inline-block;
    left: 50%;

    /* half the width */
    margin-left: -110px;
    padding: 7px;
    position: absolute;
    top: -55px;
    width: 220px;
    z-index: 101;
}

It’s a step in the right direction, however, doesn’t really look exactly like we want. To get the triangle to show up below the pop-up, I thought it would be good to use the technique for creating a triangle on css-tricks as an :after filter (please check the link for more info on that because how it works is a bit outside the scope of this post).

This works but it looks funny because the popover has a border but the triangle is just a solid color. Additionally, we cannot just toss a border on the :after filter since we’re using the border to create the triangle. What I ended up doing is using a :before filter with a width of 10px and a background color the same as the border color followed by an :after filter 1px narrower and the same background color as the popover.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.item-wrapper form:before{
    content:"";
    width: 0;
    height: 0;
    border-left: 10px solid transparent;
    border-right: 10px solid transparent;
    border-top: 10px solid #AAA;
    position:absolute;
    bottom:-10px;
    left:100px;
}

.item-wrapper form:after{
    content:"";
    width:0;
    height:0;
    border-left: 9px solid transparent;
    border-right: 9px solid transparent;
    border-top: 9px solid #FFF;
    position:absolute;
    bottom:-9px;
    left:101px;
}

There is a jsfiddle of the example available here. A few things to note… I am only using this with the Angular-xeditable dropdowns and text boxes so the other controls may or may not work. Additionally, I added some javascript (not in the examples) to hide any visible popovers when displaying a new one. I was running into some issues displaying multiple (or displaying the same one multiple times).

Learning AngularJS III: Routes

So far we’ve covered the basics of using AngularJS to interact with RESTful services and Filtering / Ordering views in AngularJS. Using AngularJS Routes, we are going to add a bit of structure to this example app.

If you have not already, please take a look at Part 1 and Part 2 as we will be working with the app we have started there…

First off, lets open our index.ejs file. As you may notice this file is an unstructured mess. We want to break apart the controllers and templates into their own files so our architecture of our demo app is a bit more clear. When we’re done, we will have the following files:

  • app.js under /assets/js/angular/
  • controllers.js under /assets/js/angular/
  • list.html under /public/templates/ – there is a better way to use Angular with Sails, however, for the sake of example this is okay
  • detail.html under /public/templates/
  • edit.html under /public/templates/

app.js

App.js is where we’re storing our module definition (that we added in Part 1), our factory defintion and our routes. The factory is exactly the same as before except we’ve added an update endpoint.

Resource

1
2
3
foodApp.factory('Food', ['$resource', function($resource){
    return $resource('/food/:id', {id:'@id'}, { update: {method:'PUT' } } );
}]);

By default, the Angular resource module has get/save/query/remove/delete methods but no update. What’s more, we want to make sure we are using a PUT method for storing our modified food items so Sails knows that we’re trying to modify an existing record. Thankfully, we can add custom actions (as you may have noticed above) by simply adding a hash after our route parameters object in the resource defintion like so { update: {method:'PUT' } }. Since this is just a hash, you can add as many definitions as you would like (e.g. { update: {method: 'PUT' }, somethingelse: {method: 'DELETE'} }).

Routing

In Part 1 we are showing/hiding a form based on a $scope variable on our controller. While this works, it may be a bit cleaner to use routing and separate our views by their function. Routing in Angular is pretty straight forward – especially if you have routing experience in other frameworks.

1
2
3
4
5
6
7
8
foodApp.config(['$routeProvider', function($routeProvider) {
  $routeProvider
    .when('/food', {templateUrl: '/templates/list.html', controller: FoodController})
    .when('/food/edit/:id', {templateUrl: '/templates/edit.html', controller: FoodController})
    .when('/food/create', {templateUrl: '/templates/edit.html', controller: FoodController})
    .when('/food/:id', {templateUrl: '/templates/detail.html', controller: FoodController})
    .otherwise({redirectTo: '/food'});
}]);

When the URL matches one of the route values, the visitor will be directed to the template and given controller (you will notice that we’re using the same controller for all our routes). Additionally, the routes that have :id will have a route parameter of id available in the controller (more on this later). If none of the routes are matched we default to /food. We won’t focus too much on the views becuase they are mostly the same as our old index.ejs, however, they are available in the gist created for this post.

controllers.js

Our controller is mostly the same as before except we’re no longer maintaining which page we’re showing. The whole controller is available as a gist however, some of the more interesting parts are as follows:

1
2
3
4
5
6
if($routeParams.id){
 $scope.currentFood = Food.get({id: $routeParams.id});
} else {
  $scope.currentFood = new Food();
  $scope.food = Food.query();
}

This is checking for the route parameter that we are setting in our route – if it’s there, we get the individual food item with that ID. When the parameter is not there, we get all the food items to be displayed in a list (and initialize a Food item for creates).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$scope.addFood = function(){
      if ($scope.currentFood.id && $scope.currentFood.id != 0){
        Food.get({id: $scope.currentFood.id}, function(food){
            food.type = $scope.currentFood.type;
            food.name = $scope.currentFood.name;
            food.percentRemaining = $scope.currentFood.percentRemaining;
            food.quantity = $scope.currentFood.quantity;

            food.$update({}, function(){
              $location.path( "/" );
            });
        });
      } else {
        $scope.currentFood.$save();
        $location.path( "/" );
      }
};

In this method we are adding our food item or updating an existing food item. We start by checking the food item’s id. If it has an id, we go ahead and get the server version and update the properties with the form values. If it doesn’t have an id, we save the food item and redirect to the list view. Food.$save is calling the built in resource action where Food.$update is calling the custom resource action we created above – both of these actions then interact with the Sails API on the server.

Wrapping Up

So there we have it. While this is still an example app – it’s way more organized than the previous iterations. The code files are available in this gist.

Learning AngularJS II : Filtering / Ordering

Last time I wrote about some basic AngularJS functionality for interacting with a RESTful API. We’re going to continue where with left off with our food inventory app to add some filtering/sorting . Check out the first post if you missed it, as we will be depending heavily on what is covered there.

Filtering

Lets say we want to search through our food inventory for something specific like oranges. We first need to open the index.ejs (that we created in Part 1) and add the following right before our table definition.

1
2
3
4
<div class="filter">
  <label for="filter">filter:</label>
  <input type="text" name="filter" ng-model="filter" />
</div>

The div isn’t entirely necessary, however, it could be useful for applying styling (it’s pretty ugly as it sits). Now that the filter definition is complete, we need to go back to our repeater definition and pipe the results through the filter as so:

1
<tr class="row" ng-repeat="f in food | filter:filter">

In a console in your project directory – fire off a sails lift command, navigate to http://localhost:1337 in your browser of choice and start typing in the filter input box. You’ll notice that all of the model bound columns are available to be filtered (e.g. entering fruit displays only food items that were classified as fruit – typing in orange shows only any records with orange in the name etc. etc.). Also, you may notice that this is not case sensitive.

Ordering

Now lets add the ability to sort the data in our table. If we followed the basic example on the AngularJS docs site, we could simply create a sort variable that we would modify in the table headers and reference in the orderBy of our repeater. The value of the sort property should be the names of one of our columns.

1
2
3
<th><a ng-click="sort='name'">Name</a></th>
...
<tr class="row" ng-repeat="f in food | filter:filter | orderBy:sort">

To handle ascending / descending we could do something like this (however, as we’ll see in a minute this may not be an ideal solution):

1
2
3
<th><a ng-click="sort='name'; reverse=!reverse">Name</a></th>
...
<tr class="row" ng-repeat="f in food | filter:filter | orderBy:sort:reverse">

Unfortunately, the reverse value would be shared across all columns. That means that if I click the ‘Name’ column and sort it descending and then click the ‘Type’ column – we will notice that it is sorting in ascending order. The problem is that the shared reverse variable is never getting reset when sorting by a different column.

To get around this, lets move our sorting functionality to the controller so we’re not duplicating a lot of code:

1
2
3
4
5
6
7
8
9
10
11
12
$scope.sort = "name";
$scope.reverse = false;

$scope.changeSort = function(value){
    if ($scope.sort == value){
      $scope.reverse = !$scope.reverse;
      return;
    }

    $scope.sort = value;
    $scope.reverse = false;
}

We’re creating the sort and reverse properties that are referenced in the orderBy of the repeater (orderBy:sort:reverse) as well as a function to manage whether to change the sort column or simply change the value of reverse. If you click the ‘Name’ column several times, the sort will not change, however, the reverse value will (which wil trigger ascending / descending order).

Next we need to change our table headers so they call this function when clicked. As before, the column’s property name will be passed as a parameter to this function:

1
2
3
<th><a ng-click="changeSort('name')">Name</a></th>
<th><a ng-click="changeSort('type')">Type</a></th>
<th><a ng-click="changeSort('expiration')">Expiration</a></th>

At this point fire up the page and see how everything is looking. The sorting / filtering is all working as intended. I have created a gist of the newly created index.ejs file that you can view here. In the next part of this series we will look at routes and editing our data.

Note to Self: Escape LiquidTags With Jekyll Raw Tag Plugin

When reviewing my recent AngularJS post, I noticed that none of my template display bindings were showing up. LiquidTags use the same {{ }} style bindings as AngularJS (and was trying to render my Angular expressions as LiquidTags). Luckily, there is a jekyll plugin called ‘raw tag’ for rendering data that you do not want interpreted as a LiquidTag / etc. Simply place this raw content between {% raw %} and {% endraw %}

For more information check out this Stackoverflow Post

Learning AngularJS

I have recently been playing around with some of the client-side MV_ frameworks. I have avoided Angular for quite some time because I was not a fan of using ng-click (etc.) HTML attributes to fire off js events. The more I’ve used the framework, however, the less of an issue that has been to me.

As noted in a previous post, another framework I have been using a bit is SailsJS. While this post is not about Sails, I am using it as the REST endpoints of the AngularJS application.

First off, sails needs to be installed. Assuming you have node installed, just npm install -g sails From there, create a new sails project from the command line sails new foodinventory and cd into that directory. Once in the directory, create a model sails generate model food and a controller so we can access the model sails generate controller food. Open up api/models/Food.js and edit the file to look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
/*---------------------
  :: Food
  -> model
---------------------*/
module.exports = {
  attributes  : {
    name: 'STRING',
    type: 'STRING',
    expiration: 'DATE',
    quantity: 'STRING', //for sake of example, ignore that this is a string...
    percentRemaining: 'INTEGER'
  }
};

Now go back to the console and type sails lift. You should be presented with a cool ascii sailboat and a message stating that sails is running on port 1337. Fire up your browser and head to http://localhost:1337 – just to see the initial sails page. From there, navigate to /food – this should return an empty array, which is the JSON representation what’s currently stored in our food list.

Add a couple of records to the Food list by visiting http://localhost:1337/food/create?name=Spinach&type=Vegetable&expiration=2013-06-20&quantity=16oz&percentRemaining=75 and /food/create with other properties. Now that you have a couple records in, lets start with the Angular stuffs.

Please note that there are better ways to utilize the sails framework to write single page applications with Angular (see here and here) – For the sake of example we’re setting everything up in very few files.

Open up your Layout file which is located in views/layout.ejs and add the following includes before <%- assets.js() %>:

1
2
3
4
 <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
 <script src="http//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
 <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular-resource.min.js"></script>

From here we’re going to edit the default view. We can keep the same div structure as the sails default view but we’re stripping out all the CSS and other content – we should have something like this:

1
2
3
4
5
6
7
8
9
 <h1 id="header">
   Food Inventory
 </h1>
 <div id="content">
    Some Content Goes Here
 </div>
 <div id="footer">
    <a target="_blank" href="http://sailsjs.com" class="copyright">Built with Sails.js</a>
 </div>

Upon refreshing this page, you can see that not much is going on; we should totally do something about that. First, we want to add the ng-app directive to the html tag in layout.ejs (it should now look like <html ng-app="foodApp"). This bootstraps (not to be confused with Bootstrap) our application, stating that the html tag is the Angular root. We could techincally add this directive to any element but the html tag is as good as any for this example.

Next, we’re going to flip back to our index view (under /home) and add an Angular factory for our food app:

1
2
3
4
5
var foodApp = angular.module('foodApp', ['ngResource']);

foodApp.factory('Food', ['$resource', function($resource){
    return $resource('/food/:id', {id:'@id'});
}]);

Using the factory, we can now access Query / Get / Save / Delete functions off our Food model (the REST API under /food). By using the :id, we are stating that we can alternatively pass an id (e.g. /Food/1).

You may notice that we’re injecting ‘ngResource’ into our module – this is the Angular library (that we’ve already included in the layout page) that is used to interact with our sails REST API that we created above.

Now that we can access the data, lets add a controller.. The controller is responsible for managing the state of the application. There are plenty of tutorials that deal with how to create a controller in Angular, so lets just jump right in..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function FoodController($scope, Food){
  $scope.food = Food.query();

  $scope.isFormActive = false;

  $scope.toggleForm = function(){
    if ($scope.isFormActive){
        $scope.isFormActive = false;
        return;
    }

    $scope.isFormActive = true;
    $scope.editableFood = new Food();
  };

  $scope.addFood = function(){
        $scope.editableFood.$save();
        $scope.food.push($scope.editableFood);
        $scope.toggleForm();
  };
};

Our controller is just a plain old javascript function that takes $scope (used to sync data between controller and the view) and our Food item as parameters. The very first line in the controller is obtaining a list of all of the Food items (essentially the same as going to /Food and copying the JSON) and storing it in the $scope.food array.

The isFormActive property is used to determine whether or not we want to show the create form. The toggle form action is used to change this active property as well as create a new model object to use with the form.

Finally, the addFood method posts the newly created food item to our API. This editableForm ‘pattern’ was something I first came across on K. Scott Allen’s website.

Now lets write some HTML that utilizes this controller… We’ll start this process by specifying that the #content area in our layout page corresponds to the FoodController with the ng-controller directive – Our modified #content tag should look like this: <div id="content" ng-controller="FoodController">. All of our $scope.___ properties are now available to use in expressions within the #content div.

Next we’re going to add a repeater to show the food items.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<table>
  <thead>
    <tr class="row">
      <th>Name</th>
      <th>Type</th>
      <th>Expiration</th>
      <th>Quantity</th>
      <th>Percent Remaining</th>
    </tr>
  </thead>
  <tbody>
    <tr class="row" ng-repeat="f in food">
        <td>{{f.name}}</td>
        <td>{{f.type}}</td>
        <td>{{f.expiration}}</td>
        <td>{{f.quantity}}</td>
        <td class="progress"><div class="bar" style="width: {{f.percentRemaining}}%"</td>
    </tr>
  </tbody>
</table>

The tr element inside the tbody is where we are telling angular to loop through all our food items. We are going to reference the current item as f and display all of its properties. You may notice in the last column that we’re using Twitter Bootstrap’s progress bar in addition to an Angular expression. The expressions in Angular can be quite powerful and seem to have a bit more functionality than many of the other templating frameworks. Go ahead and refresh your page to see how everything is looking so far.

Next we want to add the ability to add new items from this page

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  <button ng-click="toggleForm()" ng-hide="isFormActive">Add One</button>
  <button ng-click="toggleForm()" ng-show="isFormActive">Hide Form</button>
  <form ng-show="isFormActive">
      <hr />
      <label for="name">Name:</label>
      <input name="name" ng-model="editableFood.name" />
      <br />

      <label for="type">Type:</label>
      <input name="type" ng-model="editableFood.type" />
      <br />

      <label for="expiration">Expiration</label>
      <input name="expiration" ng-model="editableFood.expiration" />
      <br />

      <label for="quantity">Quantity</label>
      <input name="quantity" ng-model="editableFood.quantity" />
      <br />

      <label for="percentRemaining">Percent Remaining</label>
      <input name="percentRemaining" ng-model="editableFood.percentRemaining" /><br />

        <div class="span4 text-right">
          <div class="row">
            <button ng-click="toggleForm()">Cancel</button>
            <button ng-click="addFood()">Add</button>
          </div>
        </div>
  </form>

There is a bit more going on with the Angular directives in this snippet of HTML. First, you’ll notice that several of our elements have ng-click directives; these directives are calling functions on the controller when the user clicks on element. In addition to the click functions, we are using the ng-show / ng-hide directives to show or hide content based on a given condition. For instance, the button for ‘Add One’ is only visible when isFormActive = false and the ‘Hide Form’ button is only visible when isFormActive = true.

We are using the ng-model directive to bind a form element to $scope.editableFood (which is being created in the toggleForm method of the FoodController). Clicking on ‘Add’ will call the addFood method on the FoodController – this will post the new record to our /Food/Create and push the value into our $scope.food array. The UI will update automatically because Angular $scope properties are observed by default.

So far, we’ve created a simple REST API and added / retrieved data from this API with AngularJS. The full contents of the modified files are available over here. I plan on writing another part in this series where we will focus on searching / ordering / filtering the displayed data and in-place editing on the food list.

Less Alt+tab With SublimeText

Back in April, I jokingly lamented not having the ability to run a console from directly within SublimeText – apparently I did not do quite enough searching. There is a plug-in called SublimeREPL that lets you run ruby, node and a whole slew of other environments from within SublimeText.

To install SublimeREPL:

  1. Fire up the Command Palette (ctrl+shift+p) and type “Package Control: Install Package”
  2. Search for and install SublimeREPL
  3. Restart SublimeText

If you are using rbenv, (I assume something similar applies to rvm as well) you will need to edit your SublimeREPL settings (Preferences -> Package Settings -> SublimeREPL -> Settings - User) so your rbenv ruby is used.

1
2
3
{
  "default_extend_env": { "PATH": "{HOME}/.rbenv/shims:{PATH}" }
}

Finally, SublimeREPL uses pry to power the ruby REPL. If you don’t have the pry gem installed, you will need to install it prior to running ruby console.

Now that everything is setup you can launch a REPL and test code before you add it to your project, use rake thru shell, etc. One note, I would be careful about running servers – I was messing around with one of my favorite new frameworks and realized I could not shut down the server (this occurs with servers and other long running operations). That being said, SublimeREPL is definitely a plug-in I would recommend.

Tig: Great Git Command Line Tool

I have been using git on most of my hobby projects for quite some time. I prefer the command line when possible, however, it is nice to visualize certain operations. SourceTree and Github’s Windows and Mac apps are very nice but I’m mostly on Ubuntu when using git. Gitk is a decent option but sometimes tools such as this and the aforementioned GUI tools can be a bit over-the-top for a command line junkie such as myself. Something between the command line and full blown windowed GUI applications would be great; this seems to be where tig comes in.

Tig has been around for a little while now but it just came across my radar via an excellent post on Atlassian’s blog by Antoine Büsch (as a side note, it’s kinda cool to see this post on Atlassian’s blog considering they make SourceTree). Antoine listed some compelling reasons for using tig but I was sold when he related tig to vim versus other development tools :)

To install tig on Ubuntu just open up a command line and run the obvious apt-get install tig. From there, navigate to a directory under source control (with git) and type tig – you should see the main log view. Pressing ‘h’ will bring up help to see a list of available commands. You can stage changes by pressing ‘c’. For a more exhaustive list of commands, check out the official tig manual.

Ikea Standing Desk

Over the past several years, I have grown quite accustomed to using a standing desk at home (I have an adjustable Ergotron that I absolutely love). I decided it was about time to make the switch at work, however, I didn’t want to spend a lot of money on a desk. Thankfully, I had come across a blog post by Colin Nederkoorn on a standing desk he and a coworker built for $22. I like the design, however, I wanted more room for monitors. One of the comments on the blog led me to Jinyoung Kim’s modification of the Ikea desk which used a wider table than Colin’s design. I ultimately based my desk plans off Jinyoung’s design, since it allowed for multiple monitors, laptops and other gadgets.

The main components of the desk are as follows (they should cost about $45 total):

As noted on Jinyoung’s site, the shelf needs to be sawed down a bit to fit the dimensions of the coffee table. I like the brackets I used since it hides the rougher side of the shelf that was cut down. I would also echo his sentiment about using bolts to hold up the shelf as they are sturdier than screws. Finally, as someone who has used standing desks for a couple years now I would HIGHLY recommend having a gel kitchen mat to stand on; especially if you’re over hardwood or concrete flooring.

Goodreads Status Widget Available on Github

Goodreads is currently one of my favorite sites; I love that I can see what my friends are reading and find new books to read based on their recommendations. Noticing that many other blog platforms have sidebar widgets (asides), I decided to create one for Octopress. The first thing I did was make sure I was not duplicating effort. Goodreads has some great sidebar widgets, however, I preferred to generate the aside in the standard rake generate process (as well as keep the same look-and-feel of the other asides).

The Goodreads API is pretty robust but I was a little concerned about the possibility of a user’s API key getting checked into github. Thankfully, Goodreads also provides an RSS feed for status updates – which seemed a bit safer to use for publicly available code (and with the RSS feed it’s not necessary to obtain an API key). I assumed that if someone didn’t want their status updates public via RSS, they probably wouldn’t be installing an aside to display their status.

I created an aside and a subsequent liquid tags plugin that grabbed and formatted the RSS data. I used REXML to process the xml because it wouldn’t require users to install additional gems. One thing I still need to figure out is how to pass data from the config.yml into the plugin via the aside. I realize I have access to the config properties via site.whatever variables but I’m not entirely sure how to pass that info along to the tag plugin. Currently, the aside needs to be modified with the user’s Goodreads unique id and the max number of results to display. I noticed some others on the 3rd party plugins list on the Octopress wiki are doing the same thing, however, I would prefer to use the config file for the config data :P

All-in-all, it was pretty fun creating the plugin. If you use Octopress and would like to install the Goodreads plugin, take a look at the github repo.