giant robots smashing into other giant robots

Written by thoughtbot

kylehasmanypixels

Getting all your ducks in a column

While redesigning the thoughtbot site, I gave myself a challenge: create an easily editable and maintainable baseline grid. I figured that there had to be an easier way to do this with Sass than with plain old CSS. 

I started off going to the wonderful modulargrid.org to create my grid. I grabbed the png and threw it into the background so I could be certain everything is aligning perfectly. Then I created 3 variables based on the grid; one for the column unit width, the gutter width and line-height. This made things conceptually easier for me instead of trying to balance numbers.

$line-height: 20px; /*line-height for the site*/
$unit: 60px; /*column size*/
$gutter: 25px; /*gutter size*/

Getting some layout

Almost as soon as I had started, Sass 3.1 came out touting functions and in the change log examples I found this gem:

@function grid-width($n) {
  @return $n * $unit + ($n - 1) * $gutter;
}

This function calculates that width of the column by giving it the number of units you want it to span. Now all I needed to do is drop in grid-width() for any width and it will conform to the column sizes in my grid. For example, I wanted the grid to be 12 units wide, so on the wrapper for the site I put:

width: grid-width(12);

Of course this isn’t always perfect, the box model adds padding and the border to the width of the box and it breaks the grid. Bah. But Sass is wonderful and can do simple math and can apply that even to functions. So on a box that spans 4 columns but has 20px of padding on both sides I can subtract the padding to get the correct width of the column.

padding: 20px;
width: grid-width(4)-(20px*2);

Dealing with the baseline

The background image that I had for the grid also gave me guidelines for where the baseline should fall. Then all vertical spacing for the design uses the line-height variable. Everything from padding to the margin to the height of the images can all be declared with the line-height variable and some multiplication. 

height: $line-height*6;
margin-top: $
line-height*3;
padding: $
line-height;

Again, I ran into scenarios with the box model where I didn’t want to use the whole line-height. These changes and exceptions were easy to accommodate with a little math just like the grid-width().

padding: ($line-height*2)-(1) 0; /*Account for 1px border*/

Throughout all the Sass I’ve successfully avoided doing any real calculations and feed Sass a bunch of math problems. Updating and maintaining the layout is easy, anyone who goes into the Sass won’t have to figure out what the width of a 3 columns would be. It also has the ability to be easily adaptable to having a fluid layout.

Bonus

The grid-width function has also been incorporated into Bourbon, our default set of mix-ins and functions. Use variables $gw-column and $gw-gutter and then you are all set. No need to set up the function. 

If you’d like to get some more Sass and advanced HTML & CSS tricks come to my workshop on Sept. 26th & 27th.

jdclayton

FactoryGirl 2.1.0 Brings the Heat

Factory Girl has seen a handful of changes over the past six weeks since we released 2.0.0. Some of the highlights:

Simple association syntax

factory :user do
  name "John doe"
end

factory :post do
  author :factory => :user
end

Traits

trait :male do
  gender "Male"
end

trait :admin do
  admin true
end

factory :user do
  factory :male_user,       :traits => [:male]
  factory :admin,           :traits => [:admin]
  factory :male_admin_user, :traits => [:male, :admin]
end

Transient attributes

factory :user do
  rockstar(true).ignore
  name { "Johnny#{" Rockstar" if rockstar}" }
end

> FactoryGirl.create(:user).name                     # "Johnny Rockstar"
> FactoryGirl.create(:user, :rockstar => false).name # "Johnny"

Change associations to build instead of create

factory :profile do
  sequence(:username) {|n| "user-#{n}" }
end

factory :user do
  profile :method => :build
end

Factory modification

FactoryGirl.define do
  factory :user do
    name "John Doe"
    sequence(:email) {|n| "user-#{n}@example.com" }
  end
end

FactoryGirl.modify do
  factory :user do
    email { "#{name.downcase.underscore}@example.com" }
  end
end

Factory reloading (handy in a console)

FactoryGirl.reload # reloads all factories, sequences, and traits

Apart from these features, we’ve ensured that Factory Girl processes different attributes (static attributes, dynamic attributes) in a reasonable order, verified it works on Rails 3.1.0, upgraded the test suite to use Mocha + Bourne instead of RR (we at Thoughtbot love Mocha), and a handful of other handy bug fixes.

Grab Factory Girl 2.1.0 and make testing easier!

dancroak

Recipe: A/B testing with KISSMetrics and the split gem

A/B testing can turn skeptics into believers. Jamie, a designer at 37signals, recently shared a great case study of A/B testing Highrise.

So, what are the mechanics of actually setting up an A/B test in a Rails app?

One approach we’re trying right now on Trajectory is using the split gem with KISSMetrics. For the first few months of Trajectory’s life, we felt it was necessary to introduce Trajectory and explain why we made it in the face of existing tools.

Now, it’s time to explain its benefits on its own merits and see how well that resonates with potential users versus the old copy. This recipe will show that example. We’ll also be testing more dramatic layouts, which this combination can also handle.

Setup

Gemfile:

gem "split"

config/initializers/split.rb:

Split.redis = ENV['REDISTOGO_URL'] || 'redis://localhost:6379'
Split.redis.namespace = "split:trajectory"

Red

Let’s use the Cucumber directory convention.

features/visitor/views_homepage.feature:

Scenario: Original landing page
  When I go to the home page with the "original" alternative for the "landing_page" experiment
  Then I should see "Over the past 8 years, we've used many tools"
  And KISSmetrics receives the following properties:
    | property     | value    |
    | landing_page | original |

Scenario: New copy on landing page
  When I go to the home page with the "new_copy" alternative for the "landing_page" experiment
  Then I should see "One gorgeous tool that everyone actually LIKES to use"
  And KISSmetrics receives the following properties:
    | property     | value    |
    | landing_page | new_copy |

The split gem’s documentation provides a way to override the alternatives.

features/support/paths.rb:

when /^the home page with the "([^"]+)" alternative for the "([^"]+)" experiment/
  "/?#{$2}=#{$1}"

We can figure out the expected Javascript from the KISSMetrics API documentation.

features/step_definitions/kissmetrics_steps.rb:

Then /^KISSmetrics receives the following properties:$/ do |table|
  table.hashes.each do |hash|
    property = hash['property']
    value = hash['value']
    expected_javascript = %Q{_kmq.push(["set","#{property}","#{value}"]);}

    page.should have_content(expected_javascript)
  end
end

Green

In this example, we set up two partials that simply contain a translation.

The translation uses the Rails i18n API and is backed by Copycopter in order to make changes to it without re-deploying.

app/views/homes/_new_copy.html.erb:

<%= t(".letter-new-copy", :default =&gt; %{
  <p><strong>Less frustration, more joy: there's a better way to build software.</strong></p>
  <p>You know that your software planning tools aren't perfect.  It's not clear what to do next.  Things gets lost in the shuffle when you copy stuff around between different tools - tools for having product discussions, reviewing wireframes and usability test results, building to-do lists for developers, and keeping track of bugs.</p>
  <p>We had the same problems.  We’re thoughtbot, a web design and development agency, and that's why we made Trajectory.</p>
  <p>Imagine if you could build better software, faster.  Imagine your teammates not waiting on each other, having a clear sense of what to do next.  Imagine if everything you needed to plan and build was in one place,  with no friction or overhead in the process.  One gorgeous tool that everyone actually LIKES to use - managers, clients, designers, and developers alike.</p>
  <p>It's super easy to try out for free.  Hit the ground running by importing your Pivotal Tracker project or invite your current team members.</p>
}) %>

app/views/homes/_original.html.erb:

<p><strong>Hi, we&rsquo;re thoughtbot, a web design and development agency.</strong></p>
<%= t(".letter", default: %{
  <p>Over the past 8 years, we've used many tools for project communication and planning.</p>
  <p>Basecamp was great for discussion and communication. Pivotal Tracker was great for user stories and emergent planning.</p>
  <p>We've grown tired of having one tool that designers love, one tool that developers love, and no tool that clients love.</p>
  <p>We created Trajectory to solve our own problems. We now use it on all of our projects. Maybe it can solve your problems too.</p>
}) %>

Using split is pretty simple.

app/views/homes/show.html.erb:

<%= render partial: ab_test("landing_page", "original", "new_copy") %>

split provides a web interface but we have all our funnel metrics in KISSMetrics so we want to send the data there.

app/views/shared/_javascript.html.erb:

<script type="text/javascript">
  <% Split::Experiment.all.each do |experiment| %>
    _kmq.push(['set', { '<%= experiment.name %>': '<%= ab_test(experiment.name, *experiment.alternative_names) %>' }]);
  <% end %>
</script>

Putting A/B testing in context

This is a recipe for “how” to A/B test but if you’re interested in “when” to test, see our A/B testing page in our playbook.

Written by .

dancroak

How to back up a Heroku production database to staging

It’s right there in the docs but I didn’t notice it until recently:

heroku pgbackups:restore DATABASE `heroku pgbackups:url --remote production` --remote staging

Boom! It transfers the production Postgres database to staging.

It’s much faster than db:pull, then db:push, which is what I used to do (like a sucker).

Setup:

git remote add staging git@heroku.com:my-staging-app.git
git remote add production git@heroku.com:my-production-app.git
heroku addons:add pgbackups --remote staging
heroku addons:add pgbackups --remote production

Create a database backup at any time:

heroku pgbackups:capture --remote production

View backups:

heroku pgbackups --remote production

Destroy a backup:

heroku pgbackups:destroy b003 --remote production

gabebw

Custom formats for DateTime

Date::DATE_FORMATS is quite helpful. It lets you do this:

Date.today.to_s(:custom)

with only this code in config/initializers/date_format.rb:

Date::DATE_FORMATS[:custom] = "%Y-%m-%d"

To do the same thing for DateTime instances, like created_at columns, use Time::DATE_FORMATS.

Time::DATE_FORMATS

To set the default format for either one, set DATE_FORMATS[:default]. This will cause <%= item.created_at %> to output “2011-08-16”, with no extra work from you.