Ruby And Rails
Updated over 1 year ago
GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
This is the company weblog of thoughtbot, a web development business based in Boston, MA.
Go-arrow
Over 1 Year Ago

A few of us participated in the Node Knockout this weekend. Our team built a real-time, multiplayer tower defense game named after this very site.

We’re all fairly new to Node, but it was a breeze working with express, a Sinatra like framework, and socket.io, a socket library with several transports for cross-browser support. The client uses canvas, CSS and JavaScript with some fancy graphics. We’re really looking forwarding to working on it more after the voting ends this Friday.

If you can find time between games, vote for it and the other awesome entries. And if you participated this weekend in an attempt to win some awesome gloves let us know what libraries you used.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Over 1 Year Ago

We had a lot of fun last week on the Cape. Here’s a small recap of the mini-projects, open source work, and more that we managed to get done between hot tubbing, s’mores on the beach, and some Rock Band.

Code Katas

Dan Croak and Brian Mulligan sparred with Uncle Bob’s Bowling Game Kata as a TDD exercise. The fruits of their labor are on github. In related news, our pals at EdgeCase started Ruby Koans, a great site to be enlightened about Ruby.

Node.js Websocket Mice Explosion

I wanted to learn more about Node.js, and this blog post was a great inspiration. I ended up getting the small example from the Jeff’s blog working and showed it off during our lightning talks.

who

Mike Burns took a look at how we write controller actions and how conditionals are handled in languages like Smalltalk or the Either type in Haskell. He thinks we can do better in Rails. A little taste:

class UsersController < ApplicationController
  def create
    User.create_from(params[:user]) do |result|
      result.success(redirect_to_object_with("User created!"))
      result.failure do |user|
        flash[:error] = "You fail."
        redirect_to root_url
      end
    end
  end
end

This is still incubating but hopefully we’ll see it soon!

CSS3 Ralph

Our newest designer Phil LaPier whipped up an all CSS version of Thoughtbot’s mascot, Ralph. Feast your eyes upon its magnificence here. Make sure to bump the font size, too!

Loxosceles

Josh Clayton started learning C during the trip and got his toes wet by starting a graph database called Loxosceles. Also of interest: NoSQL Summer Boston is discussing graph transversals tonight, if you’re up for a swim.

Layer Tennis match

Inspired by the pros, Kevin and Phil decided to have their own match. Check out the results here.

I hope this will be a more regular event for our designers, how about you?

paperclip storage refactoring

Jon Yurek has a lot of requests for other storage engines with Paperclip, other than the filesystem and using S3. A new gem isn’t released yet, but the necessary refactoring was done to allow this to happen. Review the patch here!

GIT INCEPTION

I tossed together a little talk about some underused git tricks. My favorite from this set: git notes, which is now supported by GitHub too. Plant some ideas in your mind.

Gemcutter Hacking

Matt Mongeau and I worked on pushing out some changes on Gemcutter/RubyGems.org. We finally got around to deploying changes to move over to the fantastic Fog library for talking to S3, and also a new endpoint to hopefully speed Bundler up with some Redis magic. A small sample of what we whipped up is here.

PORM

Mike Burns and Harold Gimenez took a crack at a Ruby ORM for PostgreSQL that fully embraces PostgreSQL’s features making it Postgres-specific. They spent a few hours working on the DDL, INSERT, and SELECT aspects (which later influenced Who, mentioned above) before working on the Postgres fun: check constraints for validation, not null, unique, and foreign key constraints. They spent some time adding support for most postgres data types, and took a crack at supporting table inheritance.

The following is working code, which creates a users table and an admins table which inherits from users. No more STI.

class User
  include Porm::Table

  attributes do |t|
    t.string    :login, :null  => false
    t.integer   :age,   :check => '(age >= 18)'
    t.ip        :last_ip_address
  end
end

class Admin < User
  attributes do |t|
    t.boolean :super_user
  end
end

effigy on rails 3

Joe Ferris worked on Rails 3 integration for Effigy, his chaos-free view engine. Check out the README for more info on using it and integrating it with your app.

Awesome StyleSheets

Tristan Dunn cooked up a new alternative to CSS to ease the pain. Here’s some of the syntax his parser supports:

:button {
  font-size: 36px;
}
:button < div h1 {
  font-size: 72px;
}
div {
  background: #4C4;  
  padding: 16px;
}
div h1 {
  color: complementary;
}

Nothing’s released here yet, but if you’re interested let us know!

See you next time!

We’d love to see you at the next capeco.de, so make sure to stay posted by following us here. We had a lot of fun last week and we hope you’ll be along for the next one.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

This upcoming Monday, September 20th is the Mongo Boston conference at the beautiful NERD center - with the best view of Boston in the area.

Mongo Boston 2010
We have been developing a MongoDB-backed Rails application that has grown past the “small app” level. I will be presenting a talk about the state of MongoDB Ruby libraries, how we’ve used them, how we’ve integrated with testing frameworks and other libraries like Paperclip, and some of the pitfalls to look out for. There will be code.

There are many interesting talks lined up, a hack session for many languages including Ruby, and many talks around MongoDB schema design, indexing and other great topics. At only $20, don’t miss out!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Over 1 Year Ago

Being a designer, I’ve got a style I’m comfortable using to get the job done. This is a style that everyone is pleased with and can always be my go to method for creating a successful design. As fun as producing the same great design that impresses everyone is, I often want to explore new styles and try new things.

A predicament that I’ve always had with new projects is whether to try new things or stick with my tried and proven method of creating quality work. I’ve done both and never had a project fail or succeed because of the approach I chose to take. I have been happier with the result when using my standard styles and sticking inside my comfort zone. However, when experimenting with new styles I’ve grown as a designer and learned quite a bit. So which is the right path to take? In the end I definitely think the design path I choose to take needs to cater to the project specifics but that’s not to say the aesthetics won’t be influenced by styles I’m comfortable using.

Another issue that plays a factor is the thoughtbot design style. Do we have one? Do clients hire us in part because they like our specific style or just because they trust that we are good at design? I know some agencies like Happy Cog and MetaLab have very recognizable styles and are probably often hired because a client wants that style. If thoughtbot is hired to produce our style and I experiment with new things but still succeed in producing great design, is this a disservice to our clients?

I feel that since I’m a designer, I’m a creative individual and as long as it’s in the best interest of the project, I’m going to try whatever I want. I’m not a machine that just outputs good work, I am a constantly evolving source of fresh design. If you continue to produce the same exact work in any walk of life, you’ll never get better and it’s always a goal of mine to be constantly improving.

Jesse Bennett-Chamberlain had something very interesting to say about his recent Steinway project:

One of my personal goals for this project was to get away from relying on the same techniques that I’ve become comfortable with over the years… and it was actually harder than I thought it would be. I found myself resorting to the same button styles, shading techniques, etc… and having to force myself to go back and change things up. I figured that if these guys can spend a year making a single piano, I could probably spend an extra couple hours here and there on refining these details.

It’s nice to know that other designers make an effort to veer away from their comfort zones in an attempt to make the client happier. As a designer, how do you feel about experimenting during client projects? Do you always stick to your comfort zone or do you try new stuff? As a client how do you feel about a designer’s approach? I’ve always been curious about these things and look forward to hearing some responses in the comments.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

When you’re viewing content on a mobile device one of the most common uses is to zoom in on content to view it larger so it’s easier to read or tap. This makes text look crisp and beautiful, especially on the latest high resolution mobile screens, but a side effect is something terrible: images look much much worse. Why should mobile users be punished for zooming in?

Normal image resolution

In the example below, we have a screenshot from an app prototype we’re working on. It has a normal sized (55px) user image which would look nice on a desktop machine, but be pixelated and ugly if you zoomed in.

screenshot

Below is how it would usually look when a user in a mobile browser zooms in to read text. It’s blurry, pixelated and often plagued with compression artifacts.

screenshot

Increase the resolution!

In this case, however, the image is actually 3X the size it should be. It’s secretly 165x165, but the height and width are set at 55px, so when you zoom in on a mobile device you are rewarded by seeing a sharper image that looks nice next to the enlarged text.

screenshot

The effect may seem trivial at first but in practice it gives the user a sense that the page is a fluid, organic thing and not pixels on a screen. It’s a simple and effective trick that’s easy to implement until CSS3 methods become usable.

In a real world test with an actual photograph I found a 55px JPEG was around 6k and a 165px image was 16k. That’s a file size increase I can live with.

screenshot

When not to increase resolution

This technique works particularly well for small images and logos. For images that are already large enough to look decent (around 500 pixels wide) this technique could add unnecessary loading times for some users. Use it sparingly and your mobile visitors will be rewarded with a unique and special experience.

Photos by Jamie Beck

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

With an open API, Hoptoad, the app error app, can track errors from any sort of app you want: Rails, Sinatra, Rack, PHP, C#… the list goes on. We didn’t want to leave front-end developers in the dark when their Javascript throws errors, so we’ve added a Hoptoad notifier for Javascript!

I’m sure you’re asking, “How do I participate in this awesomeness?”. It’s easy.

If you’re using the hoptoad_notifier gem, upgrade to the latest version (2.3.3) and update your config/initializers/hoptoad.rb:

HoptoadNotifier.configure do |config|
  config.api_key = 'YOUR-PROJECT-API-KEY'
  config.js_notifier = true # ADD ONE LINE FOR JAVASCRIPT ERROR TRACKING!
end

Yes, that’s it.

The gem injects Javascript right after your tag. If you’re not using the hoptoad_notifier gem, you’ll just need to include a couple of lines (at the top of your ):



You may be proclaiming, “Oh no, Javascript at the top of ! What are you thinking?” I’ll let you in on a little secret: we want our Javascript loaded before any other Javascript. That way, if one of those libraries throws an exception, we’re there to let you know!

The nitty-gritty details: we’re currently only supporting browsers that support the onerror event (Firefox 2+, IE6+). You’ll also be including your project API key on the page. This is different from your user API key, so there’s no need to worry about someone deleting your projects or users. We’ll continue to expand this feature and let you know as we progress. If you have any suggestions or spot a bug, let us know!

So, what’re you waiting for? It takes 30 seconds to sign up; go grab an account and start tracking your Javascript errors now!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

thoughtbot is pairing up with Ben Orenstein to present a Vim course, this time at WorkBar. As part of this collaboration WorkBar is offering $50 off your first month’s membership if you register for the Vim course.

One thing you will discover at this course is the history of the command `grep’. This all starts back with `ed’, the original unix editor. ed was a command-line editor that worked identically to the colon-commands in vi and vim—in fact, you can press Q to get into ed mode (then type vi to get back into vim). From the ed editor you can issue common line-oriented commands like s, d, y, and so on:


:%s/rdoc/docr/g
:/include/d

Mr. Ed

If you’re following along at home you may wonder “how can I see the contents of this file?” Well if you just want to see every line you can use %p (% is “the whole file”, and p is “print to screen”), though it’s more likely that you want to see a small series of lines. 10,20p will show you lines 10 through 20, but maybe it makes more sense to see all lines matching a regular expression.

From ed, to see all lines matching “include”, use g/include/p. To see all lines matching “docr” use g/docr/p. In general, to see all lines matching the regular expression “re”, use g/re/p.

… And that’s how grep got its name.

Sign up now for the Vim course at WorkBar with Ben Orenstein on September 1st from 5PM to 9PM.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Node.js Knockout is a programming competition, similar to the Rails Rumble, focused on the increasingly popular node.js. As a company and individuals we’ve been participating in and sponsoring the Rumble from the beginning. And we’re happy to announce we’re also sponsoring and participating in the Knockout.

Introducing below, from Lenexa, KS, the not-made-for-actual-boxing, image-not-representative-of-actual-prize, zero-time champion of the world:

Let us know what you plan on building in the comments. See you in the ring.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Background

When we start on new client projects or internal apps, we don’t always start the HTML/CSS buildout the same way. Sometimes a client has sketches; sometimes wireframes; sometimes they have a brand/identity in place already - there’s a big variety in what materials they come to us with. Likewise, when we start our design process, we don’t always use the same exact things we did the last time. Part of this is a result of wanting to constantly experiment and make each time better than the last by learning - and part of it is just the unique style of each designer and their thought process.

Regardless of how we got there though, one major pain point was always in the first few days of HTML/CSS building. Would we use a css “framework”? Which one? Should we put a “reset” in place but no framework? Should we start with literally nothing and let the site dictate as we go? Should we start with specific CSS rules and stay narrow, or start with very general CSS rules and assume we’ll have re-use down the road?

Depending on the team we had working on an application, the answers to these questions were always different.

Goals

We decided that we did not want to build a new CSS framework, but that regardless of any particular approach, we wanted to have these things on each project:

  • A consistent set of CSS styles that every project could start with
  • A consistent internal mindset about “start general; then get specific”
  • The ability for developers to create “normal html” and not get burned by some weird CSS float rule to get unexpected results before putting something in front of a client.
  • The ability to show designers and developers (and maybe clients) a “style guide” type of page which would show how elements would be displayed unless they were overridden.

One thing maybe worth noting is that we put the entire HTML/CSS skillset into the “designer” bucket at thoughtbot. We don’t have a “front end developer” position to do HTML/CSS/JS exclusively. We attempt to hire developers with experience and comfort working with front end technologies, but we primarily consider it a design responsibility to get HTML and CSS work done correctly. Even with our growing design team, design time is usually at a premium and we want to grease the wheels as much as possible to maximize efficiency.

Features

To that end, we created flutie. Flutie is a Rails 3 Engine, distributed as a gem. Note that this is NOT a css framework or grid system. It is very opinionated about what basic type size, form styles, list/table style and so on should be — but it is very unopinionated about how you go about creating an overall site layout, how you make grids, how you position things, etc. There are dozens of other tools for those purposes. This is really a “how can I start with something that doesn’t look awful” tool. We want to ensure that when a developer with no specific CSS or design knowledge creates some HTML in an application, they produce something that we’re not embarrassed about.

Flutie provides…

  • A collection of stylesheets which we believe are a reasonable starting point for general layout and relative size.
  • Pretty basic form styles, and styles for the rails flash.
  • A page at the /styleguides URL in development mode to view this default state - or to view all the styles within the application layout. We render within your own layout first, and you can click “default layout” to see flutie by itself.
  • The ability for plugins and the main application to insert partials into the styleguide, to view sample styling.
  • CSS which is generally simple to override.

We also discovered a rails feature that we decided to take advantage of. You can “register” symbols which point to arrays and will be expanded when using stylesheet_link_tag. For example, this will expand to include the 7 stylesheets in flutie alongside the “admin” stylesheet, and cache them all together.

<%= stylesheet_link_tag :flutie, ‘admin’, :cache => true %>

Install

To get flutie in place in a rails3 app, just add it to your Gemfile…

gem 'flutie',           '1.0.3'

For rails 2.x apps, you can pull from the rails2 git branch…

script/plugin install git://github.com/thoughtbot/flutie.git -r rails2

Once installed, you should run a setup rake task

rake flutie:install

…which will copy some stylesheets into the public directory of the application.

After that, put the :flutie symbol somewhere in your stylesheet_link_tag or equivalent area.

<%= stylesheet_link_tag :flutie, 'screen', :cache => true %>

After that, start your server, browse to /styleguides, and check it out.

What does this look like?

Click through for full screenshot…

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Information Overload

One of our newest projects here at thoughtbot is a taxonomic naming app called GNITE which we’re building for the Marine Biological Lab. As designer on this project my first challenge was to get a basic understanding of the subject matter and the goals of the project, but I found the information to be complicated especially for someone new to taxonomy. We had the pleasure of visiting the Marine Biology Lab in Woods Hole, MA on Cape Cod recently and in the meetings I dusted off an old note taking trick I frequently used in college but with new tools — an iPad and Adobe Ideas app.

Taking notes vs. absorbing information

Design is often about reducing complex systems to their simplest form. Our meetings at the Marine Biology Lab could have produced pages and pages of comprehensive notes but my goal was to organically absorb as much information as possible without the distracting task of transcribing information. Using Adobe Ideas, an iPad and a stylus, I took visual notes, writing down key pieces of information, embellishing them, and organizing them visually. The embellishment is the key part - it leaves your ears open to what’s being discussed. When looking back at my notes I can clearly remember who was talking, what the sentiment was, and other subtle things that could be forgotten during normal note taking. The visual nature of the notes jogs my memory in a way pure text or handwriting doesn’t. The outcome is an understanding of a simplified gestalt which isn’t burdened by details.

Click each image to view it larger:

notes

Adobe Ideas allows fractalesque zooming. You are able to use vast scale differences to communicate importance as well as benefit from a very flexible canvas, so you almost never run out of space taking notes.

notes

Check out that mushroom! Someone said “fungi” so I drew a mushroom. As my theory goes, I absorbed whatever was said after that (it was an anecdotal tale of conflicting fungi names, I believe).

notes

But won’t you miss important details?

The drawback to taking notes like this is that many details will never get written down. If those details are critical bring a voice recorder. In my case there were two developers in the meetings as well, so I was confident our team-like approach would yield all necessary information (and it did).

Life after NDA’s

One of the most exciting aspects to this project is it’s open source which opens the possibility to documenting the building process in ways we usually can’t with most client work. This is the first in what I hope will be a series of posts documenting the design process all the way through to the final product.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Still without plans for August? Did you know that August is our nation’s warmest and most-traveled month? Looking for a nice trip? Well…have I got a deal for you!

Come relax on the sandy shores of Massachusetts’ own wicked beautiful Cape Cod at our previously announced event: CAPE CODE! Join thoughtbot’s team of designers and developers and stiff management types while we bask in the glorious New England sun. It’s the most relaxed you can be and learn about web programming at the same time.

Hey, maybe you can dig up some clams while you’re there.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Suspenders is a Rails project template used as a base app for all of our client work. It has gone through a few changes that better reflect our development stack and also allow us to test changes to suspenders itself.

RSpec

As you may know, we are spec’ing our apps with RSpec along with shoulda matchers now. This along with cucumber for integration tests is the testing stack that we’ve been using for a while. RSpec replaces Test::Unit in suspenders.

jQuery

For our front-end behavior we’re using jQuery. The ecosystem around jQuery Core, jQuery UI, and the slew of third-party plugins help bring life to our applications. Using jQuery also gives us an easy transition for mobile web app development with tools like jQTouch. jQuery replaces prototype/scriptaculous in suspenders.

Dependencies

Our projects depend on a number of gems. Some have native extensions and therefore cannot be vendored in the project - I’m talking about our friends nokogiri and RedCloth. In the past we would check if the gems where installed, and installed them if they weren’t. Now that suspenders-gem is the preferred way of installing and using suspenders, we have added these as rubygem dependencies on our gemspec.

Testing suspenders

The creation of suspenders-gem affords us the opportunity to write integration tests for suspenders. In the past, testing updates to suspenders was a manual process because of the nature of the template. But from now on we can write cucumber scenarios that verify that the suspenders project starts in a clean state.

Take her for a ride!

Suspenders ride

Along with these major changes, versions of all other dependencies were bumped to the latest stable versions. Take suspenders for a ride and let us know what you think:

gem install suspenders
suspenders create billion_dollar_idea
GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Has your database gone down recently? What about timeouts from third-party services? Exceptions can be raised anywhere in your app, and Hoptoad’s here to track them for you. Problem is, these exceptions can manifest themselves in your controllers or models, and that’s not helpful when you have to sort through all of them because of Hoptoad’s grouping logic.

To help, we’ve introduced global error classes for all paid accounts. Per project, you can list exception classes that you’d like to be grouped together in Hoptoad. We handle the nitty-gritty so your life is easier when you’re trying to resolve these issues that come up. Since we’re not ones to leave you high and dry, we’ve started everyone off with a list of common global exceptions from Rails projects:

  • Mysql::Error
  • MemCache::MemCacheError
  • Mongrel::TimeoutError
  • ThinkingSphinx::ConnectionError
  • Net::HTTPFatalError
  • OpenURI::HTTPError
  • ActiveResource::ResourceNotFound
  • ActiveResource::TimeoutError
  • ActiveResource::ServerError
  • Net::SMTPServerBusy
  • Net::SMTPFatalError
  • Net::SMTPSyntaxError

To change the list of global errors, go edit a project.

Edit a project

Under the name of the project, you’ll see a list of Global Error Classes (again, prepopulated with a handful of common errors). We separated by new lines, but spaces or commas will work as well.

List of global error classes

Once those are set up, you’re ready to roll! Here’s a snapshot of how a global error shows up after adding RuntimeError to the list.

RuntimeError as a global error

That’s about all there is to it! Note that this isn’t retroactive, so your old errors will still be in the same groups as they were before. Another reminder: if you’re on our Egg plan, be sure to upgrade; your first month is free!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Things are humming along and we’re ready to add another designer to the team.

We’re looking for someone who can produce great designs, really cares about the quality of their HTML and CSS code, and wants to be a part of a kick ass team that builds great stuff.

If this is you, and you’re either in Boston or willing to relocate, get in touch, because we want to talk to you.

designer work

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Testing with times and dates have come a long way, now that Timecop is around. Prior to Timecop, we would use a stubbing library like Mocha to stub out just what we needed, but why do that when Van Damme could beat the right values out of Time?

The Bomb

I wrote this Cucumber feature, models have been changed to protect the innocent.

  Scenario: List latest posts under a tag
    Given the following posts exist:
      | Post   | Tag list | Created on   | Expires on   |
      | post1  | yankees  | May 1, 2010  | July 1, 2010 |
      | post2  | yankees  | June 1, 2010 | July 1, 2010 |
      | post3  | yankees  | May 5, 2010  | July 1, 2010 |
      | post4  | yankees  | May 30, 2010 | July 1, 2010 |
      | post5  | yankees  | May 29, 2010 | July 1, 2010 |
    When I visit the tags page
    And I follow "yankees"
    Then I should see the following posts in order:
      | title  |
      | post2  |
      | post4  |
      | post5  |
      | post3  |
      | post1  |

Most of these steps are from factory_girl and webrat, except for the last one, which parses the page using Nokogiri and looks for the titles in the order listed. This feature passed when I wrote it (June 30th). Of course, on July 1st, I ran the test again, and FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.

Timecop wasn’t too helpful here, because the code actually used MySQL’s NOW() function instead of accepting a date parameter for today. So that’s one thing I could have fixed instead, but I didn’t want to touch any other code in the app.

Another obvious solution: bump up the expired date by a year, forget about it until next year. If you’re thinking of this solution, try imagining this guy kicking you in the face:

Future-proofing

Van Damme’s given us another chance. Let’s refactor this feature.

   Scenario: List latest posts under a tag
    Given the following posts with tags exist:
      | title  | tag list | created              |
      | post1  | yankees  | 2.months.ago         |
      | post2  | yankees  | 1.month.ago + 4.days |
      | post3  | yankees  | 1.month.ago          |
      | post4  | yankees  | 1.month.ago + 2.days |
      | post5  | yankees  | 1.month.ago + 1.day  |
    When I visit the tags page
    And I follow "yankees"
    Then I should see the following posts in order:
      | title  |
      | post2  |
      | post4  |
      | post5  |
      | post3  |
      | post1  |

Using a bit of ActiveSupport’s time helpers, we can future proof this step by always moving forward instead of locking to a specific date. The step definition then uses eval to get the real dates back:

Given /^the following posts with tags exist:$/ do |table|
  table.hashes.each do |row|
    created_on = eval(row['created'])
    Factory(:post, :title      => row['title'],
                   :tag_list   => row['tag list'],
                   :created_on => created_on,
                   :expires_on => created_on + 1.year)
  end
end

KABOOM

So this is a simple way of avoiding time bombs in your tests. There’s no real way to prevent them always, you just need to consider how you’re dealing with time based code. If you’ve left time bombs in your test suites, feel free to own up and share your story too. Before Van Damme gets you.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Sketching on an iPadPhoto by Jamie Beck

Sketching on an iPad

When I first held an iPad I knew it would have significance to a web designer as a sketching tool. It’s portable, connected, and friendly, and with the apps that have emerged since launch we now have many options to choose from based on what needs and wants we might have. I’m reviewing three apps: Adobe Ideas, Draft, and Sketchbook Pro from the perspective of a web designer, focusing on tasks such as layout sketching. I aim to help you decide which tool or tools is best suited for you and your working environment.

Tools of the trade

Before discussing the apps I should point out I do all sketching on the iPad using a stylus, specifically the Pogo Sketch, so unless you find your index finger particularly capable, I suggest purchasing a stylus.

icon

Adobe Ideas

Free (app store link)

Adobe Ideas is a basic sketching app that is geared towards clean results and simple, quick usage. You have one marker tool at your disposal which can be adjusted with radius, color and opacity controls via a vertical slider. When sketching, the UI is limited to a slim control bar on the left which can be hidden, but it’s unobtrusive enough that it doesn’t distract.

Adobe Ideas’ files are vector based which comes with two fantastic upsides: you’re able to zoom in and out easily and to extraordinary levels, and the exported file is a lightweight .PDF which can be manipulated or printed easily. The resolution independence means you can magnify to add small, sophisticated details, and pan around the canvas in case your drawing ends up bigger than you intended. Sketches are auto-saved so you don’t have to worry about losing work or saving files. I use Adobe Ideas on a daily basis and since it’s free I highly recommend giving it a try.

Pros: Free, simple but powerful toolset, lightweight vector export

Cons: none

icon

icon

Draft

$9.99 (app store link)

When 37 Signals released Draft I was curious how their pragmatic approach would translate in to an app, particularly one in a realm I am highly interested in. Draft is task oriented: the tools are extremely limited (one brush radius, and only white and red) and one eraser. The advantages to this approach are a consistent output, and with no options to monkey with you are kept from getting lost worrying about style. This is, in essence, a grand equalizer - someone with artistic abilities and someone without will produce very similar sketches using Draft. Draft has a static canvas which can become a problem if you run out of space, but I quickly got used to the limitation. At the time of this writing, Draft only has one level of undo which can be frustrating given the longstanding prevalence of multiple undos in most creative apps.

Draft’s killer feature, however, is integration with 37 Signals’ own chat app, Campfire. Through a sharing button you’re two taps away from posting the image instantly in a Campfire chat room — and if you use Campfire as extensively as we do at thoughtbot, this feature will blow your mind. Draft is all about communicating ideas quickly, and if you think of it as a portable, miniature dry erase board you will be satisfied.

Pros: Task oriented approach, excellent Campfire integration, consistent output

Cons: $9.99 is potentially prohibitive to some users given limited features, only one level of undo

icon

icon

Sketchbook Pro

$9.99 ($3.99 until July 4th)( app store link)

Autodesk’s Sketchbook Pro is a different breed of sketching app from the previous two. It’s a derivative of a desktop version, and it inherits many of the features and capabilities. And when I say “capabilities” I mean a shocking feature set that is almost hard to believe it’s available on an iPad - such as layers, natural brushes, .PSD export, and a professional color picker. When you’re sketching on Sketchbook Pro it feels very close to sketching on paper with traditional media. Despite the iPad’s lack of pressure sensitivity, brush strokes can taper off at the end giving a very natural and satisfying experience. There is also the ability to magnify the document, but since it’s a bitmap image zooming exposes pixelization.

For replicating natural media there is no comparison - Sketchbook Pro is incredible, but for web design and communication purposes the wide variety of features and brushes can often leave you tweaking and stylizing, instead of focusing on the task. It has a deep UI that requires a learning curve but mastery can open some exciting possibilities and ease of use. For those experienced in traditional media and who may feel stifled by Adobe Ideas’ uniform tools, Sketchbook Pro will go a long way towards satisfying your delicate artistic sensibilities.

Pros: Amazing natural media capabilities, professional toolset, .PSD export

Cons: UI takes getting used to, numerous features can distract from task oriented sketching

icon

What else?

While using these apps I’ve experimented and looked for useful ways to shoehorn them into my work routine. Both Adobe Ideas and Sketchbook Pro allow for importing of images which you can draw on top of - as an example I’ve drawn on a browser window and utilized a grid system:

very useful

Conclusion

All three of these apps are well designed and well executed, so choosing one (or a tag team of two) depends on which best fits in to your workflow. If flat, smooth sketches and vector-flexibility is key to you, Adobe Ideas is a fantastic solution. If you’re an avid Campfire user or value a focused approach, Draft is a great choice. If you’re a suppressed artist living in a designer’s body, Sketchbook Pro will let you embellish sketches with an imbued sense of creativity.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

There’s been a bunch of hullabaloo about our recent deprecation of the Shoulda macros should_change and should_not_change. A lot of people have asked, “why remove it?” Short answer: we don’t use it anymore. Long answer: it’s confusing and may encourage bad programming practices.

Consider this example of should_change:

context "updating a post" do
  setup do
    @post = Post.create(:title => "old")
    put :update, :post => {:title => "new"}, :id => @post.to_param
  end

  should_change("the post title", :from => "old", :to => "new") { @post.title }
end

This reads well and seems to be fairly straightforward. Sadly, this doesn’t work because of how should_change works internally (hence the confusing part). It would actually need to be written like this:

context "given a post" do
  setup do
    @post = Post.create(:title => "old")
  end

  context "updating" do
    setup do
      put :update, :post => {:title => "new"}, :id => @post.to_param
    end

    should_change("the post title", :from => "old", :to => "new") { @post.title }
  end
end

The @post instance variable needs to be assigned a context above the context containing should_change. I told you it was confusing.

Now, onto the bad practice aspect. This tests that the post was saved! Come on, we don’t want to do that here. That’s why we write Cucumber scenarios! We’re testing the controller, right? If that’s the case, then we shouldn’t care whatsoever if update_attributes worked with specific data passed to it, just that it was called and that we handle the result accordingly.

context "updating a post" do
  setup do
    @post = Factory :post
    Post.stubs(:find => @post)
  end

  context "when the update is successful" do
    setup { @post.stubs(:update_attributes => true) }

    should set_the_flash.to(/updated/)
    should redirect_to(posts_path)
  end

  context "when the update is unsuccessful" do
    setup { @post.stubs(:update_attributes => false) }

    should render_template(:edit)
    should assign_to(:post).with(@post)
  end
end

This is more inline with what we really want to test; that the controller handled update_attributes correctly when the update worked and when it didn’t. Leave the model logic and whether it should save or not in the model (and its tests), and test the controller to ensure it handles whether the model was updated.

I mentioned before that Cucumber scenarios should cover overall integration. Something like these would work:

Scenario: Update a post successfully
  Given I am signed in
  And I authored a post titled "Great Scott"
  When I go to edit the "Great Scott" post
  And I fill in "Title" with "Willard Scott"
  And I press "Update"
  Then I should see the flash message "Your post was updated successfully"
  And I should have authored "Willard Scott"

Scenario Example: Update a post unsuccessfully
  Given I am signed in
  And I authored a post titled "Great Scott"
  When I go to edit the "Great Scott" post
  And I fill in "" with ""
  And I press "Update"
  Then I should see error messages
  When I fill in "" with ""
  And I press "Update"
  Then I should see "Your post was updated successfully"
  Examples:
    | required field | valid value       |
    | Title          | Awesome Post Name |
    | Body           | Awesome story     |

I’m sure this isn’t the only way people were using should_change, but hopefully this clarifies how deprecating the methods will encourage better tests.

Do you use should_change and think it’s by far the best way to test certain pieces of code? I’d like to see some people who’ll sorely miss it post some real code (Pastebin or Gist).

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS


Ben Orenstein is a Ruby buddy of ours in Boston who is teaching our vim training on Friday afternoon/early evening.

Check out this video he made giving a few suggestions for reaching vim mastery. I think you’ll find Ben is a good teacher who thinks deeply about how people learn.

It’s not too late to register, and you have ample time to register for the next session in September. Hope to see you there!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

To celebrate the redesign of this very blog we’re sharing some GIANT ROBOTS themed desktop wallpaper. They’re available in a few common screen sizes for your convenience.

thoughtbot wallpaper giant robots

1440x900
1920x1200
2560x1600

thoughtbot wallpaper giant robots

1440x900
1920x1200
2560x1600

thoughtbot wallpaper giant robots

1440x900
1920x1200
2560x1600

thoughtbot wallpaper giant robots

1440x900
1920x1200
2560x1600
GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Last week we started on a blog redesign, it’s live now in case you hadn’t noticed (get in here RSS subscribers!). It was the last of our products that needed an overhaul to get up to date with some of our current styles. First we did the Hoptoad and Widgetfinger (RIP) skins, then we did thoughtbot.com and finally our training app. Giant Robots was the last on our list and I finally had a little downtime to work on it.

We approached it similar to the thoughtbot.com redesign with two designers having a go, and taking the best of both ideas for a final version. Kyle and I both took a few stabs at the design. He was tasked with taking a more minimal approach because I knew I’d be trying an over the top, elaborate design. Our only goal was to match the brand/feel of thoughtbot.com so it wasn’t such a harsh transition when you came to the blog. Other goals included making the content area a little wider, getting rid of a lot of extraneous stuff, and to update the look and feel to a more modern styled blog.

Review and Compare

Blog Redesign

When we compared two of our multiple versions, there were things from both that helped contribute to the final design. When I looked at them I definitely knew I liked Kyle’s approach more, sticking with the bare minimums and keeping it as clean as possible. The main things that persisted from my design were the colors and the body styles. From Kyle’s came the minimal approach. His simplicity and elegant typography really struck me.

Amidst the design process Kevin said something that stuck with me and became a goal of mine when putting the final design together. We had been looking at a blog post on twitter’s blog and Kevin mentioned that the content was his only focus. It’s what he initially was drawn to, and what he stayed focused on. I wanted this to happen with the new Giant Robots. This became my first goal, to have the content be the primary focus, and nothing else. I know this should always be the case, but sometimes we veer off the content is king path a little to make things look snazzy, I know at least I do. The main method of achieving this was to strip everything else from around the site. Every time I thought I had finished stripping things away, I realized we could take more. There’s even been things removed since the design was pushed live less than 24 hours ago. We think the new design is a success, let us know how you feel.

We know some of the styles are still a little whacky and there may be some bugs. We’re working on some of the tweaks so please bear with us :)

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

factory_girl has always had little Rails-specific code. It only depends on Ruby, and in order to use it in your application, it asks three things of your models:

  • They can be instantiated without parameters
  • All properties have writer methods
  • They respond to #save! (this is only necessary for the “create” strategy)

However, despite this simple API, there’s one feature in factory_girl that has traditionally caused some issues with Rails: automatic factory definition loading.

The definition loader has simple rules: if it finds any of test/factories.rb, spec/factories.rb, test/factories/**/*.rb, or spec/factories/**/*.rb, it will load those files in no particular order. For most applications, this just makes things a little nicer.

Hacking around Rails dependency management

In early versions of factory_girl, it looked for definitions as soon as you loaded factory_girl.rb. Unfortunately, this broke down when Rails 2.1 introduced gem dependency management. Loading factory_girl during the application initialization phase meant that the factory definitions might reference your models before all the gems and plugins they depended on were fully loaded. Contributer technicalpickles fixed that by detecting Rails and deferring definition loading until Rails was initialized. This solution worked effectively for the entire Rails 2.x series of releases.

Bundler: effective but strict dependency management

The upcoming Rails 3 release uses a new library for managing application dependencies: bundler. Bundler’s behavior is more predictable and understandable than Rails 2’s dependency manager ever was. However, with the introduction of Bundler, two important changes were introduced that impacted many libraries that integrate silently with each other:

  • Rails is no longer initialized by the time your gem is loaded
  • Load order is no longer configurable, so you can’t depend on another gem’s constants being defined when you integrate with it

Yehuda Katz, the author of Bundler, discussed this topic in depth on his blog. In his discussion, he suggests a solution that could be added to Rubygems:

s.integrates_with "rails", "~> 3.0.0.beta2", "haml/rails" 

However, this functionality doesn’t exist yet, so we’ve decided to create a separate factory_girl_rails gem. This way, we can declare an explicit dependency on Rails, including the version, and automatic definition loading still takes place for Rails apps without any additional application code.

What this means for you

This doesn’t change much for an application that uses factory_girl. It boils down to this:

  • If you’re using Rails 2, just depend on the factory_girl gem, and Rails 2 will pick up the existing rails/init.rb and load your definitions.
  • If you’re using Rails 3, just add factory_girl_rails to your Gemfile. This gem depends on factory_girl and will set up definition loading for you.

A lesson on “easy to add” features

I originally added this feature to factory_girl because it was convenient, and seemed easy to add. However, this trivial feature has turned out to be the most difficult to maintain part of factory_girl, and is the single feature that is most likely to outright break an application. I plan on maintaining this feature because we’re used to having it and we’ve worked out most of the kinks at this point, but next time you consider adding an “easy” feature to your own library, also consider whether or not it’s essential to your library’s core mission.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

We recently decided our CI server needed an overhaul. I really enjoyed Integrity as a build server, but after trying out Hudson it’s hard to say I want to go back. Hudson has several huge advantages:

  • Totally self contained. Integrity was a constant mashup. I don’t want to maintain the codebase for my CI server anymore.
  • Live console output. When your builds take 20-30 minutes, this is really nice.
  • Metrics/graphing built in! None so far on the other side of homegrown Ruby CI servers.
  • Kickass plugin installing, don’t need to hack a Gemfile or fix bad code (so far).
  • Building multiple branches in the same project is no longer a hassle.
  • Jobs won’t stomp over each other, and I don’t need separate configuration/database for them.

Here’s the details of what I went through to get our new CI server up and running. Be warned, these are probably specific to our projects and needs, but the general process can definitely help anyone who’s looking to get started.

Basic Loadout

We started with a fresh Ubuntu 9.04 LTS install. First up was a basic iptables setup, all the commands you need are on this SliceHost article.

Next up we needed to install Hudson. Follow the instructions here: http://hudson-ci.org/debian/

The nice part about this install (instead of custom compiling it) is that it comes with an init script that is really handy to stop/start the server. There’s more tips on installing here.

If apt-get starts whining, just run sudo apt-get install -f. The hudson package should install the Java dependencies you’ll need for now.

Let’s also install some other basics we’ll need eventually. You may disagree with my choices here, but too bad, I’m the one maintaining CI:

sudo apt-get install zsh vim git-core build-essential postfix ack-grep curl
chsh /usr/bin/zsh

The postfix install will prompt you for your server’s host name, we’ll want it eventually for emails. If you’re confused, check this article out.

By now you should be able to visit http://YOUR_SERVER_IP:8080 and Hudson should be alive and kicking.

Apache Setup

That’s annoying though, we want to serve requests on port 80. Supposedly doing this with Hudson itself is a bad idea, since it would need to run as root. Apache does a better job, and this tutorial goes over the reasons why.

Here’s what I had to do to get this working:

sudo apt-get install apache2 libapache2-mod-proxy-html
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2dissite 000-default

That will install apache2, get the proxy mod enabled to forward requests along to hudson from the root domain, and disable the default site. Next step is editing /etc/apache2/apache2.conf and adding the following directive to the bottom:

# where your server lives!
ServerName ci.thoughtbot.com

Next up we’ll need to actually make a virtual host for apache to serve (thanks to this StackOverflow question for the right incantations). Create /etc/apache2/sites-available/hudson with the following:


  ProxyPass         /  http://localhost:8080/
  ProxyPassReverse  /  http://localhost:8080/
  ProxyRequests     Off

  # Local reverse proxy authorization override
  # Most unix distribution deny proxy by default 
  # (ie /etc/apache2/mods-enabled/proxy.conf in Ubuntu)
  
    Order deny,allow
    Allow from all
  

And the final touch to enable hudson and fire up apache:

sudo a2ensite hudson
sudo /etc/init.d/apache2 restart

Hudson Configuration

Port 80 is much better. Now to continue with the configuration! We’re just using a standard security setup with one user. The process is a bit confusing, you have to enable authorization, then go out and sign up to set the password. After that, make sure you set the anonymous user permissions so they can see jobs on the home page.

If you’re an idiot like me and locked yourself out while doing this, disabling security is pretty simple.

As for plugins, we chose so far:
  • Git SCM (allows us to check out from git)
  • GitHub (links files in builds on github)
  • Campfire (You’ll need Maven locally to build this, the default one does not work.)

After installing the plugins and restarting, I disabled a bunch we didn’t need too, like Maven, CVS, etc. I would also check the “Prevent CSRF” button, that’s just a ridiculous option not to have on.

Databases

Let’s get mysql and postgres installed first.
sudo apt-get install mysql-server mysql-client libmysqlclient15-dev
sudo apt-get install postgresql postgresql-client postgresql-contrib

Redis is up next. We’re running the release candidate of 2.0, which is a bit of a pain to set up so far since make install was removed. I came up with an upstart scriptto run it instead.

sudo adduser redis
sudo su - redis
curl -O http://redis.googlecode.com/files/redis-2.0.0-rc1.tar.gz
tar zxvf redis-2.0.0-rc1.tar.gz
cd redis-2.0.0-rc1.tar.gz
make
logout
sudo bash -c "curl http://gist.github.com/raw/444033/9784f426402d5bece09894fcc6f98a33db5f5d15/redis.upstart > /etc/event.d/redis"
sudo start redis

Finally, Ruby!

Now that RVM exists, we can completely isolate projects from each other using gemsets. First we’re going to need even more system packages to deal with ruby and the other gems we’ll be installing eventually (nokogiri, paperclip, etc):

sudo apt-get install bison openssl libreadline5 libreadline-dev zlib1g zlib1g-dev libssl-dev                                                                                                            libsqlite3-0 libsqlite3-dev sqlite3 libreadline5-dev libreadline6-dev                                                                                                              libxml2-dev subversion autoconf libxslt-dev imagemagick
Jump in as the hudson user now and set up RVM:
sudo su - hudson
bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
echo "[[ -s $HOME/.rvm/scripts/rvm ]] && source $HOME/.rvm/scripts/rvm" > .profile
echo "---\ngem: --no-ri --no-rdoc" > .gemrc

Install the rubies you need now. Our starting lineup:

rvm install 1.8.6
rvm install 1.8.7
rvm install ree
rvm install jruby

Maybe not finally.

We’ll need to get the hudson user set up with git and github too. Make sure to upload your public key ~/.ssh/id_rsa.pub to github so you can start cloning repos.

ssh-keygen -t rsa
git config --global user.email "hudson@thoughtbot.com"
git config --global user.name "hudson"

For each app, create the gemset, databases, and get the gems you need. Bundler would make this easier, but we’re not on it for all projects yet.

sudo su - hudson
rvm gemset create hoptoad
rvm gemset use hoptoad
mysql -u root
> create database hoptoad_development;
> create database hoptoad_test;
gem install mysql nokogiri json

Make a new Hudson job, check from SCM periodically, enable the post build notifications you want. Here’s the build script we’re using for hoptoad so far:

bash -l -c "rvm use 1.8.6-p287 && rvm gemset use hoptoad && rake db:migrate db:test:prepare default"

Debug, rinse, and repeat until it’s green.

Made it!

RVM made this process a lot easier, I used to have to compile multiple versions of Ruby on my own and it was just a pain to manage. Gemsets are a killer feature too, and it’s easy to move them around to another Ruby version with the copy command:

rvm gemset copy 1.8.6@factory_girl 1.8.7@factory_girl

Overall the process was pretty smooth, and like any CI solution this took a while to set up and has its share of annoyances:

  • Campfire plugin was obnoxious to figure out. Not sure why they ship a broken one, but the custom one from GitHub works great.
  • Unfortunately, I couldn’t figure out how to make GitHub’s post-receive URLs trigger a build. It worked with a GET request, but POST requests seem to need some special authentication. Hudson has a cron-job style SCM polling, so that works instead.
  • Lots of clicking around to configure everything, my vim-senses are tingling for a command line interface.

I hope these guidelines help those new to continuous integration with Hudson or otherwise. Of course, there’s plenty of other options in the Ruby-verse, but hopefully you won’t pass up what Hudson has to offer.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Shoulda has long been one of our most useful and popular open source projects, and it continues to serve us well as we use it daily. However, there have been some changes over the past year in the way that we use Shoulda, and these changes have led to some decisions about its future. In our post about the Rails 3 roadmap, we briefly covered the changes we made in Shoulda 2.11 as well as our intentions for Shoulda 3. However, we wanted to go over these changes in depth and explain some of our motivation.

Putting things in context

Shoulda has two main components: a test context framework, and a set of helpers for testing Rails applications. The context framework was written because we found the method-based style of Test::Unit tedious, but certain objections were raised to the way RSpec was implemented, and we had existing test suites that could not easily be converted to RSpec. The Rails helpers were written in the form of Shoulda-based “macros” for convenience.

While this approach worked great for those of us using Test::Unit, this meant that the Rails helpers were only accessible in Shoulda-based test suites despite relying on very little in Test::Unit or the Shoulda context framework. In order to bring Shoulda’s Rails helpers to a wider community, they were rewritten as a set of RSpec-compatible matchers, and the existing “macros” were rewritten to use them.

A foot in the other camp

As things in the Cucumber and RSpec community started to pick up, we found more and more that Test::Unit users weren’t getting the latest and greatest. Most of the new assertion libraries we found were written as RSpec expectations, and several improvements to RSpec’s context and matcher DSL made it look too appealing to ignore. We tried out RSpec on a few projects, and found that we really didn’t have to give anything up - Shoulda’s macros were fully supported as matchers - and we found ourselves enjoying RSpec more than Test::Unit.

It was at this point that we decided that Shoulda’s future would focus on RSpec. However, we weren’t going to ditch Test::Unit. We had many existing projects that used Test::Unit and Shoulda, and we certainly didn’t want to abandon the fantastic Shoulda community. After several experiments, we decided not to convert our existing Shoulda test suites to RSpec, largely because they didn’t take advantage of all that RSpec had to offer and didn’t come out any better in the end. This meant that we wanted to keep the context framework functioning. However, we also found ourselves reluctant to spend time and effort on a context framework that we didn’t want to use. Therefore, we needed to come up with a way to keep the matchers working with Test::Unit and make sure the context framework continued to function reasonably, but prevent the overhead of maintaining compatibility with two frameworks.

Rails 3

Rails 3 presented another challenge. Although some of the existing matchers just worked, the general strategy for testing them had to change with Rails 3, and some matchers required conditional forks in the implementations and tests. This was more complicated in the existing macro test suite, and maintaining two test Rails roots for backwards compatibility could potentially slow us down and make it harder to add new features. We preferred the unit-style tests in place for the matchers, and wanted to avoid the rails_root-style test going forward if possible.

Embracing matchers

After refactoring all of the macros to use matchers, we found that the macros all followed a basic pattern: convert a number of hash options into RSpec’s “fluid” syntax, and then create a test that performed one assertion based on a matcher. Based on this discovery, we changed Shoulda’s should method so that it could accept a matcher instead of a test name:

# macro style
should_have_many :users
should_ensure_length_at_least :name, 3
should_not_allow_values_for :isbn, "bad"
should_set_the_flash_to /thank you/i
should_not_assign_to :user

# matcher style
should have_many(:users)
should ensure_length_of(:name).is_at_least(3)
should_not allow_value("bad").for(:isbn)
should set_the_flash.to(/thank you/i)
should_not assign_to(:user)

Converting to this syntax would mean that we no longer needed to support both a set of macros and a set of matchers - all matchers essentially function as macros. In fact, this includes most non-Shoulda macros. We currently support both syntaxes, but you’ll receive a deprecation warning for the older macro style. We’ll also be removing the macros entirely starting with version 3, so now is definitely the time to start embracing matchers.

Separating Shoulda’s contexts

We’re committed to using RSpec, so we don’t want to spend time adding features to Shoulda’s context framework. However, we need it to continue to function for our older test suites, and we understand that some users still prefer the lighter weight Test::Unit framework.

Therefore, we plan on separating Shoulda’s context framework into a separate library starting with version 3. The contexts will still function as always, but we have no plans to further improve this aspect of Shoulda.

Putting it all together

There are a lot of changes here, but they’re not too hard to understand:

  • Shoulda is now Rails 3 compatible!
  • Shoulda is focusing on the RSpec/Shoulda combination and will primarily support that combination of tools, moving away from Test::Unit
  • Shoulda’s “macro” methods are deprecated in favor of using the new should/matcher syntax
  • Shoulda’s context framework is moving into a separate library

We’re looking forward to Rails 3 and RSpec 2, and we hope you’re looking forward to Shoulda 3.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Being at RailsConf 5 has given us the opportunity to finalize a lot of the work we’ve done to prepare our plugins and gems for Rails 3. Thankfully, for many of the most popular gems, we’ve been able to maintain both Rails 3 and Rails 2.3.x compatibility in one gem.  However, we’re taking this opportunity to say goodbye to some of our less widely used plugins, and some we plan on dropping Rails 2 support for altogether.

Obviously, Rails 3 isn’t actually out yet, so what we’re talking about here is Rails 3 beta 4.  We’ll continue to keep things up to date and tested as we all move toward the release of Rails 3.  Your help and patches are more than welcome.

So here is a comprehensive overview of the current status of the projects for both Rails 3 beta 4 and Rails 2.

Paperclip

We released Paperclip 2.3.3 a few days ago. This new version of Paperclip will work with Rails 3. Thanks to the investigation of nragaz and help from isaac and joeljunstrom on github, we worked out the kinks and it should be working with the Rails 2.3.x line, and Rails 3-beta 4.  For the latest version of Paperclip, we’re no longer officially supporting Rails 2.0.x. The earliest version that will work is Rails 2.1.0.  If you need support for an older version of Rails than that, you can use Paperclip 2.3.1.1.

hoptoad_notifier

A few days ago we released hoptoad_notifier 2.2.6 with includes support for Rails 3-beta 4 as well as all versions of Rails 2.x and Rails 1.2.6. 

shoulda

We just released shoulda 2.11.  Along with Rails 3 support, we’re maintaining support for Rails 2.3.x in this latest release.  However, the latest version of shoulda will not support versions of Rails less than 2.3.  If you need support for a version of Rails older than that, you can use a previously released version.

In addition to the Rails 3 support, shoulda 2.11 introduces some dramatic changes to shoulda, including a new way of interacting with all shoulda macros.  The previous way has been deprecated and will be removed in shoulda 3.0.  We’ll make a separate blog post detailing many of the very cool changes to shoulda and more details about the future of shoulda soon, but for now, take a look at the README for the latest information on setting up and using shoulda.

Factory Girl

We just pushed factory_girl 1.3 and factory_girl_rails 1.0.  This new version adds Rails 3 support.  Because of the way that Rails 3 loading has changed, we’ve decided to make a separate factory_girl_rails gem that will be used for when you want to use factory_girl with Rails.  The existing factory_girl gem is used by factory_girl_rails and would be used if you’re using factory_girl outside of Rails.  If you want to use factory_girl with Rails 2 you can continue to use the base factory_girl gem.

Clearance

We just released Clearance 0.9.0.rc1.  This is a release candidate for Clearance 0.9.0.  This new version adds support for Rails 3 but drops support for Rails 2.  Don’t fret, if you won’t be upgrading to Rails 3, you can use a previously released version of the gem (0.8.8).  We’re doing this one as a release candidate because of the dropping of backwards compatibility and the fact that we haven’t had a chance to test the new version in a variety of Rails 3 apps using clearance.

Please flex this release candidate with your Rails 3 apps and let us know how it goes.

Suspenders

Suspenders is currently at 2.3.5 (we haven’t been able to upgrade to 2.3.8 because of bugs we’ve seen with mongrel, webrat, and rack).  We anticipate that Suspenders will be upgraded to Rails 3 a little after Rails 3 final comes out.  But to be honest, we’re actually not sure yet what the upgrade path will look like for applications that are currently tracking Suspenders.  It may be impossible to do without so many conflicts that its not worthwhile.  We’re going to have to work on this more and keep you posted.  Additionally, we’re in the process of making some fairly dramatic changes to Suspenders.  Watch it on github and stay tuned here for more.

High Voltage

Fire in the Disco! We’ve also released High Voltage 0.9.0 which supports Rails 3 and is now a gem (it was previously just a plugin).  The new version also drops support for Rails 2.  If you need the previous, Rails 2 plugin there is a rails2 branch you can retrieve it from.

Pacecar

We also just released Pacecar 1.3 which supports Rails 3 and drops support for Rails 2.  As in the other cases where we’ve done this, you can use the previous version of the gem, version 1.2.0 with Rails 2, or track the rails2 branch.

Squirrel

Squirrel was born out of a desire to make a new query syntax that was dynamic while being clean and simple.  With Rails 3’s introduction of the New Active Record chainable query language, that goal has now been achieved in Rails.  As a result, we’ll no longer be maintaining Squirrel.  It was a fun ride.

Mile Marker

Over time, our workflow slightly changed for how we built applications and we haven’t used Mile Marker ourselves for some time now.  As a result, we’re taking this opportunity to cease maintenance of this plugin and bid it farewell.

Moving on down the road

We’ve gotten more and more familiar with Rails 3 during moving all these gems to it.  Many of the new features it offers are great, and existing features have been improved and cleaned up.  We’re looking forward to Rails 3 finally being released in the coming weeks.  Now that our plugins are up and running it should help us all to transition smoothly and quickly.

Thanks to the core team and various other railsconf attendees for spending time with us this week working on some of this - we’re looking forward to the final version of rails3!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

Over the last year, we have seen a mass adoption of the @font-face property by all of the major browser vendors. Most type foundries won’t license their typefaces to be used on the web because there is no openly supported closed font file type. They all fear that their files will be widely distributed for free and their licensing system will fail. This obstacle has lead to several font hosting providers. Each service seems to have a different take on how to deliver type on the web and how to charge for it.

Typekit font selecton

Typekit

As the major player in the market, they’ve set a standards for delivering fonts online. Their font library is huge; with typefaces from FontFont, Underware, TypeTogether, and P22. They even got FontFont to rehint their fonts for the screen, which were hinted for print. 

Their engineers have done a ton to eliminate FOUT (flash of unstyled text) to deliver your fonts as fast as possible. The javascript makes the service easy to use but fonts should only have to rely on CSS to render them. They consistently update their font files and their code to comply with all the fun quirks of browsers and you don’t have to do a thing. Since you dont actually own a license and files, you lose the ability to use the typefaces in anything other than a browser.

Their Browser Samples tool lets you see the typeface rendered in different sizes, in different browsers, in different operating systems. Otherwise you would be testing the type rendering like you do your CSS but have less control. It comes in really handy to say the least. 

Their yearly rate might be hard for you to sell to your client or boss and many designers are used to paying for a font license upfront and are hesitant to have a yearly font cost. You’ll be relying on them to host your files for you.

Font Squirrel & Font Spring

Font Squirrel promotes 100% free fonts for @font-face embedding. Their biggest resource is their @font-face kit and generator which is pure awesome. They generate all the pesky files types for you and produces the @font-face code recommended by Paul Irish for you. 

Free comes with a cost and in this case its quality. Some of the typefaces have horrible hinting and kerning tables or none at all. Some are just horrible. There are some diamonds in the rough though (see Exljbris and The League of Moveable Type) if you pick well. 

Font Spring is the paid version of Font Squirrel. They offer a more traditional way to buy a font license but for the web. The quality of fonts on Font Spring are a bit higher and none are very expensive.

In both cases you get total control over the files, how they are hosted, used and implemented, as long as you stay within the license.

Font Squirrel Type selection

Fontdeck

Fontdeck is another hosted solution, like Typekit, that was recently released out of a closed beta. Its anual charge is for the font that you select for a domain. Not for the whole library. They only have a select few foundries represented and a small library but I assume that they will be getting more on board soon. 

Google Font Directory

Google recently released its hosted solution with the help of Typekit. Thier collection is relatively small and doesn’t have much that I would call useable. Right now all of the typefaces are free Creative Commons typefaces including typefaces commissioned by Google (Droid, Droid Serif). It will be interesting to see what steps Google takes in this direction, if they take any.

Typotheque

A small foundry that offers a full (both web and print) and web license for their typefaces. They host their files but use CSS to deliver them to your site. They even will serve font files with selective character sets so that you save on download speed. You’ll be relying on their cloud hosting to keep their files up an you have no control on the way that they implement the @font-face. 

Web FontFonts

After teaming up with Typekit, FontFont is delivering their typefaces independent of Typekit. FontFont is one of the more forward thinking foundries, having already started hinting some of their collection for screen use. They are offering the font web license only for EOT (IE only) and WOFF (Right now only supported by Firefox 3.6 but hopefully more in the future) which could be segmenting out a bunch of your traffic. 

Type Front

Type Front hosts your already purchased font files that have a web license for them. It then gives you the ability to control what domains that you want the font to be distributed to with ease. It then produces the needed file formats and gives you the css that you will need to put into the head of your site. 

Kernest

A free open source hosted solution. If you don’t want to pay for Typekit or try using @font-face yourself this would be a good option.

Webinc Screenshot

Fonts LiveWebink, & Webfonts

All are hosted solutions run by Acender, Extensis and Monotype respectively. Each has been a big player in the desktop publishing world and now trying to get their hands in on the web. I’ve yet to try any of them but I’m sure each will have a large library but limiting implementation.

I can tell you that the folks at Extensis, Ascender, and Monotype are genuinely happy and enthusiastic about providing fonts for the web. But certainly they won’t be quick to point out that the delivery system for those fonts is crafted within the constraints of a clever but hacky DRM-like structure.

Web Fonts at the Crossing by Richard Fink

Best foot forward

If you feel comfortable hosting and delivering your own fonts look to Font Spring and Font Squirrel. Otherwise, Typekit seems to still be a few steps ahead of the other competition. The best questions to ask are what kind of control you want over the files, what kind of hassle are you willing to deal with and how you plan on selling it to your clients. Richard Fink has also created a checklist for hosted font solutions. Like almost everything else, it really depends on whats best for you and your situation. 

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
Almost 2 Years Ago

As in years past, the majority of thoughtbot will be at RailsConf this year.  In addition to just attending, we’re lucky to be involved in 3 sessions:

  • Avoiding and Fixing Rails AntiPatterns I’ll be running (along with Tammer Saleh) a tutorial session on Monday.  We’ll cover a variety of useful tips and practical advice from our in-progress Rails AntiPatterns book.
  • Building an API with Rails Joe Ferris will be on this panel session Tuesday along with representatives from Twitter, 37signals, Github, and the New York Times.
  • Lapidary: the Art of Gemcutting Nick Quaranto will be giving this talk Thursday, reviewing the past and future of Gemcutter and how it became the new central RubyGems host.

We hope you can attend these.  And if you see us around, feel free to say hi.  I’ll be the one wearing shin guards.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
2 Years Ago

In light of our recently announced Vim for Rubyists course, co-taught by Ben Orenstein, here are more vim tips from those in the trenches all day! For prior tips from us see part one and part two.

Viiiiiiiiiiiiiiiiiim

From Josh:

How does vim help me kick ass at life? It’s simple. I type less.

Vim allows you to map key combinations together to do wonderful things.

Do you absolutely hate trailing whitespace or tabs in your files?

nnoremap   :let _s=@/:%s/\s\+$//e:let @/=_s:nohl:retab

Hitting F5 will clean out all that junk for you.

A lot of times, I’ll be working on feature files and want to change the first word in a line (change a Given to a When, or whatever).

map   ^cw

Hitting +h will move the cursor to the beginning of the text on that line and change the first word.

Here’s a gem I stole from my coworker Joe:

vmap D y'>p

When you’re in visual mode (that is, you have text selected), hitting D will duplicate whatever’s selected directly below. This really comes in handy when you’re writing tests.

I use rails.vim, but there are a couple of other commands I wish it contained. Here are two that I added:

" Edit routes
command! Rroutes :Redit config/routes.rb
command! RTroutes :RTedit config/routes.rb

" Edit factories
command! Rfactories :Redit spec/factories.rb
command! RTfactories :RTedit spec/factories.rb

I open my routes and factories files a fair amount during the day - who wants to write out that path (even tab-completion is slow, in my opinion.) Write a command, set it and forget it!

The trick to writing code faster is realizing what you do over and over… and making it a couple of keystrokes away.

From Jon:

One of the simple things from cucumber.vim that makes me happy is -]. Usually reserved for jumping to a tag, in a cucumber feature it jumps to the step definition! No more trying to remember where each step is defined. Just hit -] and you’re there.

Getting back to the cucumber feature is different but similar. vim has the concept of the “alternate” file, which is what you were just editing. So when you switch to the step definitions, your feature becomes the alternate file. You can switch back to it with -^ (or -6). You can do this as much as you want and you’ll keep swapping between the feature and the step definition you’d just opened. Also handy for a class and its tests.

From Mike:

Vim can handle multiple files admirably using buffers, splits, and tabs.

Every file you open using :e or :tabe (or :E or gf or any other way of opening a new file) is added to your buffer list. See your buffer list with :buffers. You can switch to any buffer from that list with :buffer by specifying either the number or the file name (the file name will tab-complete). Close the current buffer and remove it from the list with :bd.

Go to the prior buffer with -^. In :buffers this file has a ‘#’ in front of it.

For example:

:e ~/.vimrc      " edit the file ~/.vimrc
:e ~/.zshrc      " edit the file ~/.zshrc
-6      " switch back to ~/.vimrc
:buffers         " view all open buffers
:buffer .z  " switch to the ~/.zshrc buffer
:bd              " close the ~/.zshrc buffer and remove it from the list

Well what if we want both buffers visible at once? For this we want a split!

Horizontally split your screen using :split (or just :sp); vertically split it using :vsplit. Rails.vim gives you extra love here: all those :Rcontroller, :Rview, etc. commands can instead be :RScontroller or :RVview (S = :split, V = :vsplit).

Jump between splits using ^w followed by a motion. ^wj will jump to the split below the current one; ^wl to the split to the right; and so on. ^w^w will jump to the prior split (hold down ^w to bounce between things). Move them with ^wJ, ^wL, and so on.

I have much more to say about tabs but I’m running low on space so you’ll have to come to the training for more. In place of that, here’s a fun prank:

noremap  
noremap  
noremap  
noremap  

Reminder

Vim course, co-taught by us, the evening of June 25!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

A recipe for adding searching and filtering to your Rails app.

Why?

Provide faster feedback for users. Increase the chance that they’ll find what they want.

Ingredients

The meal

Feature

Side note: This feature is testing the non-Javascript path through the app. I’m not going to show any Javascript testing in this recipe. The thoughtbot team has been trying to settle on a Javascript integration strategy that we’re happy with. We’ve tried some things but aren’t in love with anything yet.

Scenario: Search by fieldnotes
  Given a project exists with a name of "Big Dig"
  And the following reports exist:
    | name            | fieldnotes | project       |
    | Traffic Control | Barricade  | name: Big Dig |
    | Gases, Vapors   | Dust       | name: Big Dig |
  When I sign in as a safety manager of "Big Dig"
  And I go to the "Traffic Control" report page
  And I search for "Dust"
  Then I should be on the reports page
  And I should see "Gases, Vapors"
  And I should not see "Traffic Control"
  And the search field should contain "Dust"

The following #{factory_name.pluralize} exist: step definition comes for free with Factory Girl. Note the extra cleverness FG has with the name: #{project_name} field in the table. Real time-saver.

I know I’m going to be writing multiple search scenarios so I created some step definitions for search:

When /^I search for "([^\"]*)"$/ do |query|
  When %{I fill in "search-field" with "#{query}"}
  And %{I press "Search"}
end

Then /^the search field should contain "([^\"]*)"$/ do |query|
  field_with_id("search-field").value.should == query
end

I know I want search-field because the designer, Fred, has already sliced the HTML and CSS.

Another side note: field_with_id comes from Webrat::Locators but Joe is starting to advocate for a nicer abstraction called NamedElements that wouldn’t require writing your own step definitions.

HTML

The search box:

<% form_tag reports_path, :method => :get do %>
  <%= text_field_tag "query", params[:query], :id => "search-field" %>
  <%= submit_tag "Search", :id => "search-reports-button" %>
<% end %>

Only thing that might be of note is using params[:query] to get the ‘And the search field should contain “Dust”’ test to pass.

The filters:


The results:


  <%= render :partial => "report", :collection => @reports %>

Controller

The index action:

def index
  @reports = Report.search(params)

  if request.xhr?
    render :partial => "/reports/report", :collection => @reports
  end
end

There’s some authorization around scoping results to the current user’s project that I’ve removed for brevity.

Model specs

There’s about a dozen different ways this data can be filtered. There’s specs for each kind that look something like this:

it "should find reports assigned to any of a set of safety inspectors" do
  first  = Factory(:safety_inspector)
  second = Factory(:safety_inspector)
  third  = Factory(:safety_inspector)

  find_me = Factory(:report, :safety_inspector => first)
  me_too  = Factory(:report, :safety_inspector => second)
  not_me  = Factory(:report, :safety_inspector => third)

  reports = Report.search({ :safety_inspectors => "#{first.id},#{second.id}" })

  reports.should include(find_me)
  reports.should include(me_too)
  reports.should_not include(not_me)
end

There’s one for sending a text query, and a giant one that tests combinations.

Searchlogic

The method through which all searching and filtering passes:

def self.search(params)
  full_text_search(params[:query].to_s).
  location_in(params[:locations].to_s).
  safety_inspector_in(params[:safety_inspectors].to_s).
  safety_category_in(params[:safety_categories].to_s).
  created_after(params[:from].to_s).
  created_before(params[:to].to_s).
  severity_in(params[:severities].to_s).
  state_in(params[:states].to_s).
  descend_by_severity_and_created_at.
  distinct
end

Most items in this chain are custom class methods that wrap Searchlogic:

def self.location_in(locations)
  return no_op if locations.blank?
  location_id_equals_any(locations.to_array_of_ints)
end

named_scope :distinct, :select => "distinct reports.*"
named_scope :no_op, {}

The no_op is our way of maintaining chainability in the common cases where most searching and filtering criteria is blank. We don’t even want the SQL to be built for blank options. Maybe we should patch this upstream to Searchlogic?

The array of ints is a custom String extension:

class String
  def to_array_of_ints
    self.split(',').collect { |integer| integer.to_i }.to_a
  end
end

The idea here is to do all filtering on highly indexed integers, which Postgres handles quickly. It’s also easy to pass in comma-separated ids from jQuery as we’ll see later.

“Full text search” is just SQL LIKEing while avoiding SQL injection:

def self.full_text_search(query)
  return no_op if query.blank?
  text_search(query)
end

named_scope :text_search, lambda {|query|
  {
    :joins => "INNER JOIN locations ON locations.id = reports.location_id
               INNER JOIN users ON users.id IN (reports.safety_inspector_id, reports.supervisor_id, reports.subcontractor_id)",
    :conditions => ["(reports.fieldnotes ILIKE :query) OR
                     (reports.name ILIKE :query) OR
                     (locations.name ILIKE :query) OR
                     (users.name ILIKE :query)", { :query => "%#{query}%" }]
  }
}

jQuery

Bind all the necessary events:

$(document).ready(function() {
  $('#clear-filters-btn').click(function() {
    $('#filters :checked').attr('checked', false);
    $('#slider').slider('values', 0, $('#slider').slider('option', 'min'));
    $('#slider').slider('values', 1, $('#slider').slider('option', 'max'));
    searchReports();
  });

  $("#filter-list-btn").click(function(){
    $(this).toggleClass("active");
    $("#filter-list").slideToggle("500");
    return false;
  });

  $('#search-field').keyup(searchReports);

  $('#filters :checkbox').click(searchReports);
  $('#filters :text').focus(searchReports);
});

Build the Ajax call with jQuery.param and call it:

function searchReportsURL(){
  var params = {
    'query'             : escape($('#search-field').val()),
    'locations'         : checkedIdsForFilter('location'),
    'supervisors'       : checkedIdsForFilter('supervisor'),
    'safety_categories' : checkedIdsForFilter('category'),
    'severities'        : checkedIdsForFilter('severity'),
    'states'            : checkedIdsForFilter('state'),
    'risk_profiles'     : checkedIdsForFilter('risk-profile'),
    'from'              : $('.left-handle').text(),
    'to'                : $('.right-handle').text()
  }

  return '/reports?' + $.param(params) + '&' + (new Date()).getTime();
}

var searchReportsTimeout = null;

function searchReports() {
  if (searchReportsTimeout) {
    clearTimeout(searchReportsTimeout);
  }

  searchReportsTimeout = setTimeout(function() {
    $.get(searchReportsURL(),
      function(data) {
        $('#reports-tbody').html(data);
        $(document).trigger('stripeRows');
      }
    );
  }, 500);
}

For brevity, I’ve omitted some helpers like checkedIdsForFilter that build a comma-separated list of ids for each criteria. You can figure that out based on your own markup and Javascript’s replace() method.

I kept the timeout, however, because I think it’s important for the user experience. Without it, the search will happen too fast on every keyup(), resulting in a herky-jerky experience. There’s a half-second pause now, but that’s preferred over “strobe-lighting” the user.

Appending the date to the end of the URL is a cache-buster for Internet Explorer.

Bon appétit!

Dessert: jQuery class

I’m still in the 101 classes of jQuery culinary school, which is why I’m excited about thoughtbot teaming up with Bocoup to provide our first jQuery class in June.

If you’re not familiar with Bocoup, they’ve been setting Boston afire recently with their torrid pace of about one Javascript event a week at their loft, where this training will also be held.

Hope to see you there!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
2 Years Ago

Hoptoad’s growing up!

It’s been over two years since Hoptoad first splashed into our Basecamp project list, and now there are people all over the world reclaiming their inbox and tracking their app errors with us. Here’s a snip from our analytics:


The sun never sets on Atticus’ watch!

To celebrate, we’re offering a new, larger plan: “Bossfrog”

During these two years, we’ve been busy growing as a company, and we know that you have been, too. Congratulations! More and more often, we’re getting support requests asking for larger plans, with more users and more projects.

To support your growth, we’re pleased to offer a new larger plan on Hoptoad: the Bossfrog. We’d like to extend a tip of the hat to our friends at TWG for Atticus’ newest wardrobe addition.

Bossfrog gives you double the projects and double the users, with all the same great Toad taste (and features) of our previously largest plan. As always, you can take it for a spin free for a month. If you really need a lot of the Toad, there is a “Hoptoad behind your firewall” option, where you can run Hoptoad to your heart’s desire in your own data center.

And we measured the Toad.

When I was growing up, there was an area on the kitchen wall that was dotted with small graphite tick marks, and my family members’ names penciled in. My brother and I would often check (and re-check) our height through the years of our youth, arguing over the thickness of our socks and heights of our haircuts, eagerly awaiting the day our tickmarks would surpass those of our parents.

We want to remember and cherish these years in Atticus’ life too, so we’ve taken a few rough measurements to share with you and record for posterity. I’ve also included some reference samples, to help you anchor these numbers against more concrete and familiar things.

Number of smokes in the Valley of Ten Thousand Smokes: 10,000
Number of projects using Hoptoad: 10,000
Number of daily calories in the Michael Phelps Diet: 12,000
Number of British pounds required to purchase a chocolate Ferrari F2008: 12,000
Number of people using Hoptoad: 12,000
Number of matches in Michael Williams’ 10-year London’s Tower Bridge replica: 1.6 million
Number of individuals’ names blasted off to the moon via NASA’s Lunar Reconnaissance Orbiter in June 2009: 1.6 million
Number of acres set aside in 27 California counties as frog habitat: 1.6 million
Number of unique, different errors caught by Hoptoad: 1.6 million
Number of Chinese yuan invested to build a new Hamburger University in Shanghai: 250 million
Number of years before the earth is reconfigured into supercontinent “Pangaea Ultima”: 250 million
Number of individual errors sent to Hoptoad, to date: 250 million

We’re pleased as punch to see Hoptoad continue to grow, and are incredibly thankful for you, our customers, who have helped us grow, improve, and shape the direction of Hoptoad. Please continue to let us know how you’re using Hoptoad, what you’d like to see us change, and what we can do to serve you better. Ride the Toad!

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Go-arrow
2 Years Ago

Suspenders—as a reminder, Suspenders is our base Rails project that can be easily upgraded—has some issues. Specifically, two: you have to pull down all of the git repo in order to start a project, which itself requires pulling down all of the git repo again, and; Suspenders itself is untested.

Suspenders Boy

The solution to these problems are fairly straight-forward so I went ahead and did it. Presenting, today, the Suspenders gem!

gem install suspenders

Installing the gem will give you the suspenders command. I have grandiose plans for this command-line utility but for now you can create a new project:

suspenders create carbon_dating

You can upgrade a Suspenders-based project at any time, as usual:

rake git:pull:suspenders

As a reminder Suspenders will give you:

To run the new Suspenders test suite you will need the git repo for the gem:

git clone git@github.com:thoughtbot/suspenders-gem.git

Then go in and run rake. This will build a new project using Suspenders, set up the last touches (clearance stuff, mostly), and make sure that the project passes.

GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS
Displaying articles 1 - 30 of 43 in total