Runtime Era

Monday, April 14, 2014

DRYer Ruby Class Definitions w/ Struct

Many web developers subscribe to a principle know as the DRY principle. It translates to Don't Repeat Yourself. I try my best to adhere to the DRY principle, but sometimes I repeat snippets of code here and there, especially if the footprint is small. Well, today, a coworker showed me a clever way to DRY up some of my "small footprint" repeated code. It's probably some age-old Ruby technique, but I just discovered it today, and I'm really excited about it! So, I'll share it.

A Myriad of Class Definitions

Let's say we need to implement a Pizza application for a friend. After much white-boarding, we finally decide on a code architecture and a series of fancy design patterns we're gonna use to make our Pizza site come to life! Inevitably, we are going to have several class definitions in our codebase:
class Pizza
  attr_accessor :cheese, :sauce, :toppings

  def initialize(cheese, sauce, toppings)
    @cheese = cheese
    @sauce = sauce
    @toppings = toppings
  end
  
  # ... more implementation
end
class Soda
  attr_accessor :type, :is_diet

  def initialize(type, is_diet)
    @type = type
    @is_diet = is_diet 
  end

  # ... yes, more implementation
end
class Topping
  attr_accessor :name, :cost

  def initialize(name, cost)
    @name = name
    @cost = cost
  end

  # ... you get it
end

The Boilerplate

It's pretty obvious there are repeated elements of code in each of our classes. It's small, but repetitive nonetheless! Any DRY refactors we can do to reduce the code-smell would be an improvement.

When I'm writing class definitions, I frequently find myself writing two elements:
  • getters/setters for public attributes
  • a constructor with parameterss for initial attribute values

The DRY way to get getters and setters is to use attr_accessor.
attr_accessor :cheese, :sauce, :toppings
To set initial attribute values upon construction we use an initialize method with parameters. This is basic Ruby function. Within the initialize method, we explicitly define which attribute gets which parameter.
def initialize(cheese, sauce, toppings)
  @cheese = cheese
  @sauce = sauce
  @toppings = toppings
end

Reduce the Repetition with Struct

We can get rid of BOTH of these snippets of code by inheriting from a Struct instance! Well, kind of. A Struct generates an instance of Class. The new Class instance will have predefined attributes, along with accessor methods for them. By inheriting from the generated class, we essentially get all of the goodies we want in a single line of code!

Our Pizza class from above now becomes:
class Pizza < Struct.new(:cheese, :sauce, :toppings)  
  # no more initialize, just our implementation!
end
Subsequently, our entire codebase (however tiny) becomes:
class Pizza < Struct.new(:cheese, :sauce, :toppings)  
end
class Soda < Struct.new(:type, :is_diet)
end
class Topping < Struct.new(:name, :cost)
end

Mind. Blown. Anyway, I hope this helps someone as much as it has helped me. Comments or questions? Let me know!

Friday, January 17, 2014

Cleaner Rails JSON API Controller Specs with OpenStruct


As many of us know, Ruby on Rails makes it really easy to write RESTful APIs. Paired with a rich client-side framework, we can create applications with slick user interfaces. A common approach for this is to write JSON APIs on the server for consumption by a Javascript front-end framework. To test my APIs, I like to use RSpec and build specs for the actions on each of the controllers. The goal of the tests is to make sure each JSON response returns the correct information in the proper structure.

TL;DR Example code on Github

How about an example? Let's say we have a model named Article:
class Article < ActiveRecord::Base
  attr_accessible :title, :body
end
Following standard TDD practices, we begin by writing a test. Here's a first crack at a controller spec describing the create action on our ArticlesController:
describe ArticlesController do
  describe '#create' do
    let(:title)  { 'New Title' }
    let(:body)   { 'New Body' }
    let(:attrs)  {{ title: title, body: body }} # our new Article
    let(:params) {{ format: :json, article: attrs }}

    before { post :create, params } # make the request

    it 'creates a new Article' do
      Articles.all.count.should == 1
    end

    it 'returns the title' do
      response.body.should include(title)
    end

    it 'returns the body' do
      response.body.should include(body)
    end
  end
end
The first assertion makes sure an Article was actually created. The next two assertions check the response. A successful creation should return the new Article's attributes in a JSON object. With the spec written, we can implement our controller:
class ArticlesController < ApplicationController
  respond_to :json
  
  def create
    @article = Article.new(params[:article])

    if @article.save
      render json: @article # { title: 'New Title', body: 'New Body' }
    end
  end
end
When we run our test, it passes. However, our test is incorrect! What if, for some odd reason, the title and body were actually swapped in the response (title has body, body has title)? The test would still pass! While our spec ensures the correct values are in the response, it doesn't ensure the proper structure. Let's try again:
describe ArticlesController do
  describe '#create' do
    let(:title)  { 'New Title' }
    let(:body)   { 'New Body' }
    let(:attrs)  {{ title: title, body: body }}
    let(:params) {{ format: :json, article: attrs }}

    before { post :create, params }

    # removed Article count for brevity

    subject { JSON.parse(response.body) }

    it 'returns the title' do
      subject['title'].should == title
    end

    it 'returns the body' do
      subject['body'].should == body
    end
  end
end
In this version, we make our test more descriptive by declaring our response object as the subject. We also parse our response into a Hash by using JSON.parse() in the standard Ruby JSON library. Now, our assertions make sure we get the correct attributes from the proper keys in the response object. We also get the added bonus of ensuring a valid JSON response by parsing it in our subject.

Our test is now correct, but it can still be improved. When I'm testing JSON responses, I like to use OpenStruct to clean up my assertions. In simple terms, an OpenStruct takes a Hash and returns an object with methods named according to each key in the Hash. So, we can make the keys in our JSON response behave like methods on an object! Using OpenStruct, here is a prettier version of our test:
describe ArticlesController do
  describe '#create' do
    let(:title)  { 'New Title' }
    let(:body)   { 'New Body' }
    let(:attrs)  {{ title: title, body: body }}
    let(:params) {{ format: :json, article: attrs }}

    before { post :create, params }

    subject { OpenStruct.new(JSON.parse(response.body)) }

    its(:title) { should == title }

    its(:body) { should == body }
  end
end
Compared to our first draft, this spec is more concise, correct, and straightforward.

How do you like to use OpenStructs? Any thoughts, tips, and tricks are appreciated. Let me know in the comments!

Thursday, November 28, 2013

"I Am Thankful For…" from a Web Dev

The Thanksgiving holiday is a time to reflect and appreciate the gifts in our lives. Of course, I'm thankful for friends and family, but I'd like to take some time to share some of the "geeky" stuff I really appreciate. As a Ruby on Rails developer, I use many third-party tools and techniques to aid in my daily workflow and side projects. Since I've started using them, they've become absolutely essential! So, here are some of the developer-related gifts I'm thankful for this year.

Github

Back in college, I stored all of my side projects on an external hard drive and carried it around in my bag. If I made a change to the code and broke something, there was no way to revert back to a working state other than hitting undo several times in each file. With an online version control service like Github, I have the ability to revert back to any previous state. I also have a log of who made any changes, why they did, and when. Since Github offers free online repositories, I use it as remote backup for my projects, in case my computer craps out. I couldn't imagine my life without a service like Github, and I'm never going back to storing projects on a hard drive to carry in my bag!


Vim

The learning curve can be pretty steep, but after taking some time to learn it, Vim is one of the most productive text editors out there. Since adopting Vim, I find myself longing for the key-bindings in any application where I have to edit text! Rather than clicking around or reaching for the arrow keys, I can easily reposition the cursor by switching to command/visual mode. All without leaving the comfort of home row! Vim commands also force me to plan out my actions prior to manipulating any text. I quickly brainstorm what needs to be done, type the proper command, and execute. This makes the act of editing text feel a little more like programming, which keeps my mind in a problem-solving state while writing code.


Tmux

Tmux is a terminal multiplexer. In short, it lets users manage several terminal sessions within a single terminal window. It supports cool things like window splitting and tabbing, which lets developers customize their own layouts. While those features are awesome, I really appreciate the ability to attach and detach from individual Tmux sessions. If I'm in the middle of working on a Rails feature, and I'm suddenly interrupted, I can detach from my current session and create a new one. Once I'm done tending to the interruption, I can reattach to my previous session and continue where I left off. My terminal layout and running processes are preserved!


Chrome Dev Tools

At my first job, I was completely new to web development. I was familiar with HTML and CSS, but I had very little experience with HTTP and Javascript. My web development knowledge began to grow as I spent more time exploring websites with the Chrome Developer Tools. The ability to inspect HTML elements and styles as they exist in the DOM is extremely helpful. The Javascript debugger is very essential, especially since the web is becoming more dynamic. I can set breakpoints and use the console to evaluate Javascript at any point in the execution. There are other great features available, but these have been the most influential in improving my understanding of the web.


TDD

I started working with Ruby on Rails and test-driven development a couple of years ago without any prior experience with either. Two years later, I am convinced that a well-written test suite is essential to any project. TDD is a process that relies on developers writing automated tests prior to any production code. By following this paradigm, I'm forced to think about the design of my objects before I implement the code. Later down the road, when I inevitably refactor those objects, my tests help ensure that my code changes don't break the original contract. That is, the objects' original behavior and interface remain the same. TDD and testing provide a nice safety net for any project, why be without it?


HackerNews

HN is my go-to resource for anything and everything related to software and tech. The concept is simple: users share links to articles about anything from hot new web frameworks to clever programming techniques. In the comments, participants can share their experiences and insights. They also often have fiery debates, giving readers several different perspectives on any number of issues affecting the software community. Overall, HN gives me the content and community I need to make myself better at what I love to do.



So, with all that said, I want to give a big thank you to the creators and maintainers of the aforementioned tools and techniques! You've made my job and hobby easier and more enjoyable!

What are you thankful for this year? Let me know in the comments!