giant robots smashing into other giant robots

We are thoughtbot, a web design and development agency in Boston, MA.

Tagged:

Comments (View)

A Tmux Crash Course

I’ve been using Tmux for about six months now and it has become just as essential to my workflow as vim. Pane and window management, copy-mode for navigating output, and session management make it a no-brainer for those who live in the terminal (and especially vim). I’ve compiled a list of tmux commands I use daily to help me work more efficiently.

Tmux Panes

If a tmux command I mention is bound to a keyboard shortcut by default, I’ll note that in parenthesis.

Session Management

Sessions are useful for completely separating work environments. I have a ‘Work’ session and a ‘Play’ session; in ‘Work’, I keep everything open that I need during my day-to-day development, while in ‘Play’, I keep open current open-source gems or other work I hack on at home.

tmux new -s session_name
creates a new tmux session named session_name
tmux attach -t session_name
attaches to an existing tmux session named session_name
tmux switch -t session_name
switches to an existing session named session_name
tmux list-sessions
lists existing tmux sessions
tmux detach (prefix + d)
detach the currently attached session

Windows

Tmux has a tabbed interface, but it calls its tabs “Windows”. To stay organized, I rename all the windows I use; if I’m hacking on a gem, I’ll name the window that gem’s name. The same thing goes for client applications. That way, I can recognize windows by context and not what application it’s running.

tmux new-window (prefix + c)
create a new window
tmux select-window -t :0-9 (prefix + 0-9)
move to the window based on index
tmux rename-window (prefix + ,)
rename the current window

Panes

Panes take my development time from bland to awesome. They’re the reason I was able to uninstall MacVim and develop solely in iTerm2. I don’t have to switch applications to switch contexts (editing, reading logs, IRB, etc.) - everything I do, I do in a terminal now. People argue that OS X’s Cmd+Tab is just as fast, but I don’t think so.

tmux split-window (prefix + ")
splits the window into two vertical panes
tmux split-window -h (prefix + %)
splits the window into two horizontal panes
tmux swap-pane -[UDLR] (prefix + { or })
swaps pane with another in the specified direction
tmux select-pane -[UDLR]
selects the next pane in the specified direction
tmux select-pane -t :.+
selects the next pane in numerical order

Helpful Tmux commands

tmux list-keys
lists out every bound key and the tmux command it runs
tmux list-commands
lists out every tmux command and its arguments
tmux info
lists out every session, window, pane, its pid, etc.
tmux source-file ~/.tmux.conf
reloads the current tmux configuration (based on a default tmux config)

Must-haves

These are some of my must-haves in my tmux config:

# remap prefix to Control + a
set -g prefix C-a
unbind C-b
bind C-a send-prefix

# force a reload of the config file
unbind r
bind r source-file ~/.tmux.conf

# quick pane cycling
unbind ^A
bind ^A select-pane -t :.+

Workflow

During the day, I’ll work on one or two Rails apps, work on my dotfiles, run irssi, and maybe run vim in another window to take notes for myself. As I mentioned, I run all of this inside one tmux session (named work) and switch between the different windows throughout the day.

When I’m working on any Ruby work specifically, I’ll have a 75%/25% vertical split for vim and a terminal so I can run tests, interact with git, and code. If I run tests or ‘git diff’ and want to see more output than the 25% allots me, I’ll use tmux to swap the panes and then move into copy mode to see whatever I need to see.

Finally, I run iTerm2 in full-screen mode. Switching between OS X apps for an editor and a terminal is for chumps!

Tagged:

Comments (View)

Ask thoughtbot: What is Kickstart?

Kristof Bardos of Digital Natives asks:

Dear Thoughtbot,
I read about ‘Web Business Kick Start’ as one of your services. My question is, are you acting as a startup incubator, or merely like a kind of consultancy of tech and other business areas?

The Kickstart is somewhere in-between a startup incubator and our normal development engagement where we do agile, iterative development from concept to production.

In addition to the normal application development we usually do, we give our customer office space in our office and assist them in hiring a team to replace us. The people they eventually hire work right alongside us, being trained in Rails if necessary, our process for developing software, and the application itself.

After launch, and when the team is ready, we step away and the company goes on its own.

The Kickstart differs from a more traditional incubator in that we charge our normal application development rates, and we don’t assist with the legal setup and organization of the company.  However, there are no additional charges for any of the other services.  We don’t typically take equity in the kickstart companies, but it is something we are open to and evaluate on a case by case basis.

Building a successful business is about more than just the software we build. As with all of our customers, we take an interest in helping them succeed. Introducing them to our other contacts and potential investors who might help to make their dream a reality.

The majority of businesses we work with come to us with little more than the concept, typically in the form of a pitch-deck or wireframes. They are typically a single founder or a pair of co-founders who are either self-funded or have a small amount of angel investment. Most applications are launched, to production, in 4-8 weeks from the start of the project, and then go onto ongoing iterations from there.

Take a look at a video we produced with two of our customers.

I hope that answers some of the common questions we get about the Kickstart service.

Do you have a question about how we do something?  Get in touch with us and we might feature your question here on the blog.

Tagged:

Comments (View)

Get Your C On

This year, whyday happened to fall on the first day of Capeco.de. I’d been interested in playing with C for a while, so I decided to sit down and start learning. The K&R helped a lot, but digging through both Redis and Potion were a lot more insightful of how C could actually be used.

Since then, I’ve been trying to get some C in my Ruby by writing gems with C extensions. This isn’t hard to do, per se, but it was difficult to find a comprehensive list of requirements (as well as guidelines for organizing the code). I ended up looking at one of the most-used Ruby gems with C - Nokogiri.

Where to Start

You’ll want a way to test the code, as well as the rake-compiler gem. I don’t test my C explicitly - think of it as a handful of private methods. You’ll want to test the public methods on whatever classes and modules you write, so a C testing framework is overkill. Finally, you’ll want to become acquainted with ruby.h.

I decided to start simple and write a Sieve of Eratosthenes. I’d written one in pure Ruby and wanted a basic translation to C. I was also interested in benchmarking the code since I knew C would be a lot faster in this instance.

The sieve gem can be found here and its source here.

Directory Structure

A C extension’s directory structure is very similar to other Ruby gems; the only addition is an ext directory that will store files necessary for generating a Makefile and compiling the code. This is where your C files and their headers will go. In my case, these files are located in ext/sieve.

extconf.rb

extconf.rb is what will generate your Makefile. You’ll need to require "mkmf" and then call create_makefile("your_gem/your_gem"). The mkmf documentation is an excellent resource if you want to include other libraries or customize anything. My gem is straightforward so all I did was ensure the Makefile was created.

sieve.h

My sieve.h is very straightforward and doesn’t really need any explanation.

sieve.c

This is the meat and potatoes of the gem.

At the top of the file, you’ll need to #include <ruby.h> as well as any other headers you need.

You’ll also need an Init_your_gem() function that will be called similarly to main(). This is where I create the structure of my classes and modules for my sieve. I create a Sieve module, add a sieve instance method, and then have the Numeric class include Sieve.

Finally, there’s the sieve function itself. It returns a Ruby object which is of type VALUE. It also accepts a Ruby object (self), which is also of type VALUE. An important reminder is to make sure you free any memory you allocate or your gem will leak memory, just as you would in C.

The /lib directory

Although most of the actual work is done in C, we’ll want to have a bit of Ruby in the /lib directory. I’ve written a scaffold of the module at lib/sieve.rb, which has a require "sieve/sieve" at the top of the file (remember in extconf.rb when we passed a string to create_makefile? That’s it.).

The Rakefile

Being able to compile the gem and run the tests is important, which is why I mentioned the rake-compiler gem earlier. After requiring rubygems, rake, and your library, you’ll want to require "rake/extensiontask". That’ll give you a couple of handy rake tasks, namely clean and compile.

I like to set my default task to run tests, but you’ll want a couple prerequisites to that task: clean and compile. This will ensure that you’re rebuilding your gem and running with the latest compiled version.

Since I’m using Cucumber, it looks like this:

require "cucumber/rake/task"
Cucumber::Rake::Task.new(:cucumber => [:clean, :compile]) do |t|
  t.rcov = true
end

task :default => :cucumber

I also have my benchmark task here so I can find out how much more performant this library is compared to a pure Ruby implementation.

Testing

You’ll want to test your C extension just like any other Ruby gem. I prefer Cucumber but anything will do. I used a scenario outline for some of the basic primes and then found a file of the first one million primes for some heavy-duty lifting. I also tested that if enough memory couldn’t be allocated, it would raise a Ruby NoMemoryError exception.

Since you’re going to want to run the features against the latest changes of your gem (and not a version of the gem that’s installed), you’ll want to modify the load path within your test helper (test/test_helper.rb, spec/spec_helper.rb, or features/support/env.rb). My env.rb looks like this:

$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
require "sieve"
require "spec/expectations"

Building the gem

The gemspec for a Ruby C extension is fairly straightforward. The only thing you’ll need to add is to set the spec’s extensions attribute to the path to the extconf.rb file.

Gem::Specification.new do |s|
  s.require_paths = ["lib"]
  s.extensions = ["ext/sieve/extconf.rb"]
  # ... rest of the gemspec
end

As with any gemspec, you’ll want to make sure that you list the .c and .h files within files.

Results

Armed with this, you should be able to go and write Ruby C extensions to your hearts content. As for my Sieve experiment, here’s the pure-Ruby implementation of the sieve:

# usage:
#   >> sieve 100
#   => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
def sieve(n)
  numbers = (0..n).map {|i| i }
  numbers[0] = numbers[1] = nil
  numbers.each do |num|
    next unless num
    break if num**2 > n
    (num**2).step(n, num) {|idx| numbers[idx] = nil }
  end
  numbers.compact
end

My benchmarks were running the sieve on numbers from zero to one million in steps of 100,000. No memoization is used for either form.

On Ruby 1.8.7, here are the results from my benchmark:

                   user     system      total        real
sieve method   4.460000   0.060000   4.520000 (  4.522069)
Numeric#sieve  0.040000   0.000000   0.040000 (  0.046349)

Ruby 1.9.2 is significantly faster, but still doesn’t hold a candle to the C extension:

                   user     system      total        real
sieve method   2.410000   0.060000   2.470000 (  2.468430)
Numeric#sieve  0.050000   0.000000   0.050000 (  0.049053)

What I Learned

Writing C is both fun and can enhance performance of number-crunching and other fun things. It has it’s place and is a great addition to any Rubyist’s toolbox. Have you written any C extensions purely for performance gains? If you’re open to sharing the context, I’d love to hear about it!

Tagged:

Comments (View)

Hoptoad and Javascript, Sitting in a Tree, S-E-N-D-I-N-G

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.7 at the time of this update) 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 <head> tag. Done!

Not using hoptoad_notifier gem?

If you’re not using the hoptoad_notifier gem, you’ll just need to include a couple of lines (at the top of your <head>):

<script type="text/javascript" src="http://hoptoadapp.com/javascripts/notifier.js"></script>
<script type="text/javascript">
  Hoptoad.setKey('YOUR-PROJECT-API-KEY');
</script>

The details

You may be proclaiming, “Oh no, Javascript at the top of <head>! 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!

Tagged:

Comments (View)

Capturing Errors on a Global Scale

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!