Beerlington

The Blog

Building a Strong Foundation for Vermont’s Technology Future

I went to a VTTA Lunch and Learn meeting last week focused on “Marketing Vermont as a technology state”. I’ve had some time to reflect on this objective and wanted to talk about some of the frustrations I’ve felt in trying to address this very issue.

During the lunch session, David Parker of Dealer.com described how Vermont’s greatest opportunity to grow our economy is through the technology sector. Vermont’s tourism revenue accounts for a small minority of its GDP, yet our state’s marketing is disproportionately focused on tourism. In his presentation, Parker expressed that we need to get better at educating people in science and technology, finding startup capital, and retaining a young energetic workforce. Instead of proposing concrete actions though, he suggests “changing the rhetoric” by talking about what Vermont has to offer. While I agree with his message, unfortunately talk is cheap and ideas are free, and neither is worth shit unless you actually stand up and do something.

Unfortunately, getting the word out about these great jobs and companies is not enough to attract people to our state. One individual at the meeting went as far as suggesting that every ad in Vermont Life magazine should feature a cellphone or laptop. Are you kidding me? Vermont is a beautiful place, let’s not change that. Can there not be a balance between technology and Vermont’s simple beauty without the two interfering with the other?

One topic that was missing from the lunch discussion was the role that community plays in this endeavour. Yes, technology jobs and businesses are undeniably important, but community is what gives people a sense of belonging. Do artists and musicians live here because Vermont has one of the lowest unemployment rates in the country? No, they live here because there is a community that supports them. The role of community in the technology sector is no different.

Community is an Investment, Not an Overnight Success

Many Americans prefer band-aids to solve problems and don’t consider future repercussions of today’s decisions. Why should we invest in renewable energy when we can solve our immediate energy needs with fossil fuels? Why should we pay higher taxes when corporations can sponsor our highways? Vermonters are different. We don’t want billboards taking away from Vermont’s beauty. We would rather pay $15 for a hamburger made from locally raised cows than $4 for a Big Mac. More importantly though, our small communities thrive because they’re not looking for short term payoff. Community, like renewable energy, is a long term investment and not a band-aid.

I’m organizing a software developer conference this summer called the Burlington Ruby Conference. Last year was our first time hosting the event and it attracted 80 developers for a weekend of networking and education in Burlington. Only about 25% of the attendees were from Vermont. The rest were from Boston, Montreal, New York, and other parts of New England. We even had two guys from Denmark and one from Mississippi. We also had a small number of women attend, but this year we have been putting a lot of energy into increasing that number. We reached out to the female developer and speaker communities, published a diversity statement and code of conduct, and have teamed up with Girl Develop It to offer low cost training in conjunction with the conference. What other technology event in Vermont can make similar claims?

This year we’ve reached out to numerous Vermont companies soliciting sponsorship for the conference and have been met with the same response each time. “Sorry, not interested. We don’t do Ruby, we do _____”. Fill in the blank with Java, .Net, LOLCODE, whatever. That’s great, but these companies are obviously missing the point. When was the last time there was a Java or .Net conference of this caliber in Vermont? This is an opportunity to support the very community that these companies are trying to hire from. Just because a developer writes Java code to pay the bills, doesn’t necessarily mean he or she wants to do this in their free time. If you look at the speakers we had last year, it clearly wasn’t all about Ruby. We had talks on building location based apps, database scaling, and multiple JavaScript talks from some of the most respected people in these fields.

Why have a Ruby conference then you ask? Simple. The Ruby community as a whole represents the type of community that we know will thrive in Vermont. The people are passionate, helpful, and strong advocates for diversity. Ruby developers also tend to be at the forefront of best practices. Combined, these characteristics lead to a vibrant, successful community. Just look at the number of other regional Ruby conferences that have sprung up in the last few years. Ruby may not be the cool language anymore, but the community is thriving. In my years of being a Ruby developer, I’ve never met someone who said they only use Ruby because it pays the bills.

This conference is going to happen whether we have local support or not. We are fortunate to live in a city where our costs to organize the event will be covered by ticket sales alone. We’ve also added a crowd-sourced fundraising effort called ”Burlington Ruby Cats” that is not only raising money for the conference, but also for the Humane Society of Chittenden County. Having sponsors would allow us to do things above and beyond what a typical conference can offer such as scholarships for students and underrepresented groups like women and minorities. Equally as important though, having support from local sponsors shows that these companies actually care about our community. We are not organizing this conference to make money, infact we will be lucky if we break even given everything we want to do. We are hosting a lakeside BBQ and providing professional recordings of the talks free of charge. We are organizing the conference because we want to invest in our community. We want people to think about this conference when they think of Burlington, Vermont; thereby helping to achieve our goal of “Marketing Vermont as a technology state”.

How can I get involved?

Don’t get me wrong, the Vermont software community as it stands today is awesome. I participated in (and won) the 2nd annual Vermont Hackathon last October. This event was targeted toward Vermont developers and the amount of support that local businesses gave was incredible. This was a great success for our community, but we can’t limit ourselves to a handful of these events a year, or only focus on the business side of Vermont’s technology economy.

I also help organize a number of different software related user groups in the Burlington area. Our first group, the Burlington Web Application Group (BTVWAG) typically has 30-50 people at any given meetup, and our latest group, Burlington JS, filled all 35 of its slots in the first day it was announced. These groups are run by volunteers and represent exactly what our technology community is about.

People ask me after every meetup how they can get involved, but then I never hear back from them. Step up folks! Acknowledging that we have a problem is the first step, but taking action is the only way that things will change. There are a number of community members who get this and have devoted countless hours of their free time to building the community. Aside from the hackathon, Vermont Code Camp, Vermont Tech Jam, and BTVGig are other examples of events and initiatives led by passionate Vermonters who want to see our community grow. We also have folks like Jen Mincar at Office Squared who provide places for these groups to gather, free of charge.

Vermont has jobs and an incredible opportunity for growth through technology, so now is the time to start investing in our community, and investing in our future. Please reach out if you want to join us in building a strong foundation for Vermont’s technology future.

A Simplified Query Interface for Relationships in Active Record 4

1
2
3
4
5
6
7
8
9
# Rails 4 now lets you simplify "belongs_to" association queries:

# Before
Post.where(author_id: @author)
Image.where(imageable_id: @cat, imageable_type: 'Cat')

# After
Post.where(author: @author)
Image.where(imageable: @cat)

About six months ago I added a new feature to Active Record that allows you to write simpler queries across associated models. Compared to many of the other new features in Rails 4, this isn’t a significant change, however I think it’s pretty handy so I wanted to talk about it and show some examples. Other than a few small bug fixes, this was my first real contribution to Rails, and the experience was enlightening (saving that for another post).

It’s All About the Interface

Prior to this change, any queries that used a foreign key column were required to specify the actual column name as the hash key in the query. The biggest problem I had with this approach is that it wasn’t consistent with other Active Record APIs. When I’m working with models, I tend to be thinking in terms of relationships and objects as opposed to database columns.

Take the following example from Rails 3.x where you can build new objects using the belongs_to relationship, but you cannot query with this relationship. When you are querying, you need to shift into an object + database hybrid mindset:

1
2
3
4
5
6
7
8
9
10
11
12
class Post < ActiveRecord::Base
  belongs_to :author
end

# Creating related objects using the model association:
Post.new(author: Author.first)

# Does NOT work in Rails 3.x
Post.where(author: Author.first) # => NOPE!

# Must specify foreign key to make query work in Rails 3.x
Post.where(author_id: Author.first) # => Yup!

In the above example, I am specifying the foreign key on one side, and the object on the other. I think it can be hard to remember when you can use the associations and when you can’t. Obviously a more practical approach would be to use Author.first.posts, but this gives flexibility in cases where you might not have both sides of the relationship fully setup.

The above example may seem trivial so here’s an example using a non-conventional relationship:

1
2
3
4
5
6
7
8
9
class Post < ActiveRecord::Base
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

# Ah crap... I forgot what my foreign key was called!
Post.where(writer_id: Author.first) # => NOPE!

# Must still specify foreign key column here
Post.where(author_id: Author.first) # => Yup!

This issue becomes even more apparent when working with polymorphic relationships:

1
2
3
4
5
6
7
8
9
class Cat < ActiveRecord::Base
  has_many :images, as: :imageable, dependent: :destroy
end

class Image < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true
end

Image.where(imageable_id: Cat.first, imageable_type: 'Cat')

Striving for Consistency

After seeing a few different issues get opened, I realized I wasn’t the only one who felt this inconsistency was unintuitive. It was causing enough confusion that people were reporting it as a bug, convinced that it “used to work”. This clearly wasn’t a bug, and at some point an unintuitive interface needs to be addressed.

I hadn’t spend a ton of time in the Active Record internals so I decided to dive in and see if I could change the API so it worked with the relationship name. I knew that each Active Record model tracks its relationships with other models using “reflections”. Each reflection stores various properies such as the relationship name and macro (ie belongs_to, has_many, etc).

1
2
3
4
1.9.3-p327 :007 > Author.reflections
 => {:posts=>#<ActiveRecord::Reflection::AssociationReflection:0x007f8cf391dc28 @macro=:has_many, @name=:posts, @options={:extend=>[]}, @active_record=Author(id: integer, name: string, created_at: datetime, updated_at: datetime), @plural_name="posts", @collection=true>}
1.9.3-p327 :008 > Post.reflections
 => {:author=>#<ActiveRecord::Reflection::AssociationReflection:0x007f8cf4c1afb0 @macro=:belongs_to, @name=:author, @options={}, @active_record=Post(id: integer, body: text, author_id: integer, created_at: datetime, updated_at: datetime), @plural_name="authors", @collection=false>}

As I dove further into the Active Record internals, I found the ActiveRecord::PredicateBuilder which is used for building the “WHERE” clause of every Active Record query. It was not the most straightforward class I’ve worked with, but the integration tests were good so I could do some exploratory testing and know when I had broken something. After a few days of discussing with the Rails core team about what behavior should be implemented, I had some working code.

Conclusion

In the end, the changes I made to the predicate builder allow you to query across a belong_to relationship without specifying the foreign key. Does it enable you to do something you couldn’t do before? Not really, however, bringing more consistency to the API was my main goal.

Now that Rails 4 beta has been released I encourage people to download it today and try out some simple examples:

1
2
Post.where(author: @author)
Image.where(imageable: @cat) # => Polymorphic!

I also recommend checking out the Active Record tests for some more complex examples of how it can be used with polymorphic relationships and single-table inheritance.

Reading Books Is Fun

I picked up a Kindle Paperwhite a few weeks ago to replace the one I “borrowed” from my wife. It has turned out to be a fantastic piece of hardware, and I agree with pretty much everything John Gruber has said about it. I’ve never considered myself much of a reader, but I’ve made it through some great (and not so great) books over the last couple months, and wanted to share a my thoughts on them.

I’m Currently Reading:

Confident Ruby

If this book was actually finished, I would be done with it. It is currently in alpha and only about 80 pages, but the content so far is outstanding. What I enjoy about Avdi Grimm’s writing is that he can take subjects like exception handling and objects and describe them in a way you wouldn’t think possible. After taking some time off from writing Confident Ruby to focus on his RubyTapas screencast series, Avdi finally sent out another update this morning. It’s a great book for anyone who has been working with Ruby for a while and feels like they’ve mastered the basics.

The Passionate Programmer

We’ve all been shit on by managers and coworkers at one point or another. Chad Fowler has been through it all too, but instead of just complaining, he wrote a book about what he learned. According to my Kindle, I’m only about 55% done, but I’m really enjoying it so far. The short, targeted chapters make it easy to pick up and read when you only have a couple minutes to spare. I would recommend this book for anyone who thinks they know everything and that managers are always wrong.

Recent readings:

JavaScript: The Good Parts

If you’ve been a developer for a while but never really taken the time to properly learn JavaScript, start with this book. It will give you a new appreciation for the language and leave you longing for more. The funny thing about this book is that it’s only 176 pages (compared to the Definitive Guide’s 1,100), and a large portion is spent discussing the parts of JavaScript you should avoid or be cautious of. Since reading it, I’ve been watching some of Pluralsight’s JavaScript courses and I feel like it gave me a solid foundation for learning more.

Practical Object Oriented Design in Ruby

Not only is this one of the best Ruby books I’ve ever read, it’s also one of the best programming books period. Sandi Metz is a master of analogies, and the way she explains the principals of object oriented design has me convinced I could learn anything from her. I committed to giving a presentation on Object Oriented Programming in Ruby back in September and had been working on it for a few solid weeks. This book came out about a week before my presentation, I read it in three days, and decided to completely scrap my presentation and start over. This book is perfect for Ruby developers who want to learn more about object oriented design, but do it in a language that is familiar to them.

Service Oriented Design with Ruby and Rails

I picked this one up after receiving a fantastic reply to a question I posted on Stack Overflow regarding Rails SOA resources. While slightly outdated by the time I read it, it has actually aged fairly well compared to other technology-specific software books. The book provides a thorough overview for people who’ve never built or consumed web services in Ruby, and a great refresher for experienced developers who have. For anyone who has read it, I’d recommend listening to a recent Ruby Rogues podcast featuring author Paul Dix. They discuss some of the changes in technology and practices since its release, and Paul reflects on how we would write it today if given the opportunity.

Shipping Greatness

This book was a big departure from other tech books I’ve read in that it was written specifically for managers and not software developers. I feel like I was getting management “insider information” while reading it, and definitely have a new appreciation for the work done by non-coders. Written from the perspective of a former product leader at Google and Amazon, it was perhaps too “enterprisey” for some folks, but as someone who has worked in startups my whole career, it was fascinating to read how large companies build and ship products. Other than a couple sections I skipped such as “Understand How to Communicate with Designers”, this was a very enjoyable read.

Confessions of a Public Speaker

Another departure from the coding genre, Confessions offers a glimpse into the life of a professional public speaker. I was certainly not expecting to become a better public speaker from simply reading this book, but was interested in hearing about someone else’s experiences on the subject. I’d recommend it for anyone who has done any amount of public speaking regardless of whether you want to do more or absolutely hated the experience. Communication, whether it’s public speaking or otherwise, is an important skill that very few software developers take time to learn.

Domain Specific Languages

I did not enjoy this book at all, infact, I didn’t even finish reading it. It is pretty much the exact opposite of Practical Object Oriented Design in terms of readability. It is too lengthy and far too comprehensive for my taste. Unless you’re planning on creating the next Cucumber or you’re writing your thesis on DSLs, save yourself some time and read something else. The only reason I picked it up was because I was interested in building internal DSLs in Ruby and InformIT had it on sale for for $9.99. I might revisit it someday, but I have a fairly long list of books I want to get through, so that’s not likely to happen any time soon.

On my reading list (in no particular order):

Unfortunately I buy books faster than I read them so here’s a list of those on my immediate reading list (ie they’re already on my Kindle):

  • The Art of Community: Building the New Age of Participation
  • Being Geek: The Software Developer’s Career Handbook
  • Team Geek: A Software Developer’s Guide to Working Well With Others
  • The dRuby Book: Distributed and Parallel Computing with Ruby
  • Distributed Programming with Ruby
  • The Connected Company
  • Maintainable JavaScript

How We Won Hack|VT

This post describes how our team, Datamorphosis, won the HackVT hackathon, and the strategy we used to ensure that if we didn’t win, we’d at least be proud of what we built. Read on to find out how we formed our team, brainstormed an idea, researched and planned our application, executed during the event, which technologies we used, and how we presented our winning application: The Vermont Business Landscape.

Insert App screenshot

Forming a Team

Hackathons are not just about writing code. They are about solving a problem in the most succinct way possible, and effectively communicating the problem and solution in a remarkably short period of time. Having the right team is critical to accomplishing that goal. There are very few people in this world who alone can brainstorm an idea, aggregate complex data, build APIs, design a usable interface, and present their work in front of a judging audience. The rest of us are mere mortals. Forming a solid team is about understanding the strengths and weaknesses of each member, and how they will fit together.

Our Team:

Combined, our team was highly skilled in web design and development, data analysis, user experience, and communication; precisely the skills needed to succeed in a hackathon. Almost every member of our team had previously worked with another member at some point in the past. We understood each other’s strengths and weaknesses and knew exactly how each person could contribute. This allowed us to trust one another and stay focused on the tasks that we were most qualified for.

Brainstorming and Planning

When selecting our data set, there was one theme that seemed to resonate with our team: Incorporating data that was challenging to aggregate and analyze. The reason for this is that many successful business models are built around simplifying a tedious task. Our team believes in the value of small businesses to Vermont’s economy, and in doing research, we discovered that the state has a need for aggregating business data from disparate data sources. The Community & Economic Development Office receives frequent inquiries from prospective new business owners as to the health of various sectors throughout Vermont.

Once we had a vague idea that we wanted to build a tool to assist new business owners, we began to research and plan our application. Our team met a few times during the week leading up to the hackathon, and used Flowdock to discuss ideas (even when we were in the same room).

Here are a few of the areas that we researched:

  • Market need - If you’re trying to solve a problem for someone other than yourself, find a real person who has this problem. Talk to them and see how passionate they are about it. In our case, the person we spoke with at the Community & Economic Development Office was ecstatic about our proposed solution. This validated our idea and gave us confidence going into to the competition.
  • Judges - Information about each judge was available on the HackVT website. We discussed as a team what sort of things they might be looking for, and planned to account for this in our application. For example, Justin Cutroni works on the Google Analytics team, and we assumed that analyzing and simplifying complex data was something he is passionate about.
  • Technology - Spending time up front discussing and researching technologies is well worth the effort. After choosing an idea, we made a list of all the potential tools we might need to build it. Members of our team had overlapping skill sets so we were able to make some important decisions before the competition began.
  • How to win a hackathon - We even spent some time researching how other people won hackathons.

Execution

Given the amount of upfront work we did, we were able to start the hackathon with confidence and direction. We knew who was doing what, and essentially hit the ground running. We made a couple mistakes along the way and had to pivot a few times, but overall we were able to stay focused on our goal and finish within the allotted time.

Datamorphosis team at work

What we learned:

  • Start early - Though the hackathon didn’t officially kick off until 6PM, they allowed teams to set up and start coding at 3pm. These early hours in the competition are critical. Everyone is awake and can make reasonable decisions, and being there early makes things a little less stressful.
  • Avoid perfection - This is a hackathon, and it’s ok to write hacky code. People who know me or have seen some of my presentations know that I’m a stickler for good code. Building a production application for your customers is one thing, but hacking something in 24 hours for a three minute presentation is another. Put aside your pride and just get it done.
  • Focus on the core - Since you will likely run into unexpected hiccups along the way, focus on building the core first and iterate on it. Will your application still function and be presentable without feature X? If so, it’s a low priority. In our case, showing data on a time-lapse map was the core of our application. Without this, it would not have met our goals. We tracked a list of our priorities on a whiteboard and delegated each task in order of importance. By focusing on these must-have features, we were able to have a simple application that was easy to finish and demonstrate.
  • Don’t panic - When we are tired and under stress, we tend to make bad decisions. Our team encountered a serious bug in the final hour and realized we didn’t have time to fix it, so we prepared our presentation accordingly. We spent our last 30 minutes planning a path to walk through the application that we knew we could demonstrate with confidence.
  • Know when to cut a feature - This goes along with focusing on core functionality. Some features will take up too much of your time and don’t add enough value. If it doesn’t feel right, cut your losses and move on. We had a few awesome features that we decided to axe at the last minute. If you’ve prioritized your todo list, the tasks ranked as “nice to have” will likely get pulled.
  • Design for presentation - Chances are, your application is going to look 100x worse on a large, lower contrast projector screen during the presentation. We planned for this by using large, readable fonts and designing simple interface. If the judges can’t make out what it is, it will just be visually distracting.
  • Asynchronous communication - Even when we were sitting at the same table during the event, we continued using Flowdock to communicate. We found this asynchronous form of communication to be vital to our productivity, allowing people to propose ideas, share links, and notify others about code changes, without breaking from their workflow.
  • Don’t bother with user authentication - No one cares that you added a 3rd party authentication system to your app. If your application isn’t secure for the presentation, this is actually a good thing because you won’t be wasting valuable time trying to remember your password.

Presentation

In planning for the event, we anticipated having at least five minutes to present our idea. It turned out we only had three. Three minutes is not a lot of time for anything, let alone present a problem, solution and walk through a software demo. Every second is valuable and should be spent selling your idea to the judges.

presenting at HackVT

Here are some tips for nailing the presentation:

  • Get some rest - With a five person team, we had the luxury of making sure the people representing our team were well rested. Only two of my teammates actually coded through the night. It’s easy to make stupid mistakes when you’re tired, and you don’t want to blow all your hard work in the last 3 minutes.
  • Practice your pitch - Our presenters spent hours preparing for the demo. They practiced what they were going to say and how everything was going to flow. This extra effort definitely paid off in the end.
  • Don’t talk about the technology - You’re here to brag about your application, not about the tools you used. If people are interested in what you built it in, they will ask.
  • No one built a perfect application - I’m sure everyone’s application had at least one embarrassing bug. Ours had at least three that we were aware of. Don’t apologize and bring attention to these faults, the judges probably won’t even notice. We planned our demo to avoid these issues, and no one besides us knew they existed.
  • Focus on your product - Don’t rely on presentation slides to help sell your app. Though most of the judges looked well rested, everyone else is tired and just wants to see your application in action.
  • Just because you built it, doesn’t mean you have to show it - There were quite a few features we developed that did not even get mentioned during our demo. They would have been great to show off if we had a few more minutes, but they were not polished and did not really add core value.

Technologies Used

Final Thoughts

Winning the hackathon means so much more to our team than the cash or prizes. HackVT was by far the most well organized Vermont event I have ever attended. Every last detail, from the quality of food to the mounted, decorative data sets, made the developers feel welcome and comfortable during our 24 hour stay. This event demonstrated the level of commitment that companies like MyWebGrocer and Dealer.com have made to Vermont, ensuring our state becomes a sustainable technology hub and attract talented individuals for years to come. Thank you to all the volunteers, sponsors, and participants who made it happen. See you again in 2013!

Building a Custom CSV Renderer in Rails 3

This post will walk you through creating a simple renderer in Rails 3.

What are renderers?

A renderer in Rails is a way of customizing how content is rendered for the browser or any client interacting with your web service. Rails has a handful of built in rendering formats such as html, xml, and json, and exposes an effortless method for adding additional custom rendering functionality that can be shared among your controllers and applications.

Custom renderers provide a standard, reusable interface for rendering content, in turn allowing you to DRY up your application logic architecture. This post will show you how to add a custom renderer that converts ActiveRecord collections to a downloadable CSV format.

Below is an example of how the final CSV renderer will be used:

CSV rendering from a controller - app/locations_controller.rb
1
2
3
4
5
6
7
8
9
10
class LocationsController < ApplicationController
  def index
    @locations = Location.all

    respond_to do |format|
      format.html
      format.csv  { render :csv => @locations, :except => [:id] }
    end
  end
end

Some background info:

Before getting started with our CSV renderer, let’s look at the Rails source to see how it defines its own custom renderers:

actionpack/lib/action_controller/metal/renderers.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module ActionController
  def self.add_renderer(key, &block)
    Renderers.add(key, &block)
  end

  module Renderers
    # Lots of ommitted code...

    def self.add(key, &block)
      define_method("_render_option_#{key}", &block)
      RENDERERS[key] = block
    end

    # More ommitted code...

    add :xml do |xml, options|
      self.content_type ||= Mime::XML
      xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
    end
  end
end

At the top you see an add_renderer method, which will be our interface to add a custom renderer. Ignoring the fact that I’ve removed some of the implementation details, further down you see a class method called add which does two things: 1) It defines an internal method used by the rendering stack, and 2) stores the key (:xml, :json, etc) in a hash.

As we move to the bottom of this file, you’ll see where add is being used to define an XML renderer. This method takes a block with two arguments. The first argument is an object that responds to to_xml, and the second is a set of options that are passed as arguments to to_xml. If for some reason the object doesn’t respond to to_xml then it simply returns itself.

If you’ve ever used the Rails scaffold generator, you will notice that it includes code for responding to XML requests:

default scaffolded controller.rb
1
2
3
4
5
6
7
8
def show
  @location = Location.find(params[:id])

  respond_to do |format|
    format.html # show.html.erb
    format.xml  { render :xml => @location }
  end
end

This is exacly what adding the XML renderer has provided, a clean syntax for allowing the server to respond with XML formatted data.

Why should I use it?

Let’s say you have a Location model with the following schema:

db/schema.rb
1
2
3
4
5
6
7
8
9
10
11
ActiveRecord::Schema.define(:version => 20110726022558) do
  create_table "locations", :force => true do |t|
    t.string   "name"
    t.string   "address"
    t.string   "city"
    t.string   "state"
    t.string   "zip"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
end

Although your main application may use all of these fields, you might need to import this data into some sort of enterprise/exchange-powered legacy system in CSV format. You could easily define a to_csv class method on your Location model and just call that from the controller, but that presents a few problems. The Location model shouldn’t know anything about converting itself to a CSV format because this violates the rule that components should have a single, well-defined purpose. Also, what if you want to download other models in CSV format? These are problems that respond_to and render aim to solve.

How does it work?

Looking back to the first code snippet I posted, you can see that the CSV render syntax in my controller is very similar to the XML render syntax. The only difference is that our CSV format allows you to specify which columns (attributes) you want to include or exclude in the downloaded CSV file. This detail has almost nothing to do with the renderer itself, and is implemented by the object’s Array class, as shown below.

To understand how this works, let’s look at our custom CSV renderer:

csv_renderer.rb
1
2
3
4
5
require 'action_controller/metal/renderers'

ActionController.add_renderer :csv do |csv, options|
  self.response_body = csv.respond_to?(:to_csv) ? csv.to_csv(options) : csv
end

You will notice a few differences between our custom renderer and the XML renderer defined by Rails. First, we are using add_renderer as opposed to just add. This is a personal preference as you can also use: ActionController::Renderers.add :csv. Both methods accomplish exactly the same thing, but I find add_renderer to be slightly more readable. Second, we are not setting the content_type. The reason for this is because Rails automatically adds a CSV Mime Type within the action_pack library. I’m not going to go into the details of how that works, but feel free to explore the complete list of Mime Types. The last detail to note is that we are requiring action_controller/metal/renderers which provides us access to the add_renderer method. You could alternatively require any module that inclues this file, such as action_controller/base if you need access to methods like send_data.

Finally, let’s look at the to_csv Array method. The reason this method is defined on Array is because ActiveRecord converts collection queries such as Location.where(:state => 'vt') or Location.all to arrays.

array.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Array

  # Converts an array to CSV formatted string
  # Options include:
  # :only => [:col1, :col2] # Specify which columns to include
  # :except => [:col1, :col2] # Specify which columns to exclude
  # :add_methods => [:method1, :method2] # Include addtional methods that aren't columns
  def to_csv(options={})
    return '' if empty?
    return join(',') unless first.class.respond_to? :column_names

    columns = first.class.column_names
    columns &= options[:only].map(&:to_s) if options[:only]
    columns -= options[:except].map(&:to_s) if options[:except]
    columns += options[:add_methods].map(&:to_s) if options[:add_methods]

    csv = [columns.join(',')]
    csv.concat(map {|v| columns.map {|c| v.send(c) }.join(',') })

    csv.join("\n")
  end
end

Above we can see that to_csv takes a hash of options which are used to determine which columns should be included in the CSV file. One option :only => [:col1, :col2] allows you to specify the exact columns you want included, another option, :except => [:col1, :col2], allows you to specify which columns you DO NOT want to include, and the last option, :add_methods => [:method1, :method2] allows you to add data defined in methods that aren’t saved in the database. Calling to_csv without any arguments will include all columns.

Usage examples:

Tying that all together, we can now use this renderer in our controllers to let users or clients download CSV data. Here are a few examples:

Generate a CSV that includes every column:

app/controllers/location_controller.rb
1
2
3
4
5
6
7
8
9
class LocationsController < ApplicationController
  def index
    @locations = Location.all

    respond_to do |format|
      format.csv  { render :csv => @locations }
    end
  end
end

Generate a CSV that includes every column except the id:

app/controllers/location_controller.rb
1
2
3
4
5
6
7
8
9
class LocationsController < ApplicationController
  def index
    @locations = Location.all

    respond_to do |format|
      format.csv  { render :csv => @locations, :except => [:id] }
    end
  end
end

Generate a CSV that includes only the state and zipcode:

app/controllers/location_controller.rb
1
2
3
4
5
6
7
8
9
class LocationsController < ApplicationController
  def index
    @locations = Location.all

    respond_to do |format|
      format.csv  { render :csv => @locations, :only => [:state, :zip] }
    end
  end
end

Generate a CSV that adds a model method:

app/controllers/location_controller.rb
1
2
3
4
5
6
7
8
9
class LocationsController < ApplicationController
  def index
    @locations = Location.all

    respond_to do |format|
      format.csv  { render :csv => @locations, :add_methods => [:my_method] }
    end
  end
end

Finishing up

This post walked you through a simple yet practical approach to building a custom Rails renderer in Rails 3. While it may be overkill for one-off uses in a small code base, it shows it’s true potential in large applications by helping prevent redundant code, and for building APIs that may need to respond to formats other than JSON and XML.

Additional Resources

All the code used in this post was developed for a RubyGem called render_csv. For more information about renderers and the Rails rendering stack, I highly recommend Crafting Rails Applications by José Valim. Also, the Layouts and Rendering Rails Guide provides a comprehensive overview of rendering in controllers as well as views.