April Ruby Roundup

Posted by Matt Thu, 15 Apr 2010 16:08:00 GMT

It's time for the April edition of the Ruby Roundup.

Global

Local

March Ruby Roundup

Posted by Matt Wed, 10 Mar 2010 23:34:00 GMT

These are the link notes for the March Ruby Roundup given at Indy.rb. If you didn't see the presentation, it's probably not a lot of use.

global

local

&[:array, :toproc, :ftw]

Posted by Joel Thu, 04 Mar 2010 19:18:00 GMT

About a week ago, I finally decided to clear something out of the EB complaint stream that has been papercutting me for ~10 months.

You've probably seen the great shortcut added by activesupport (and now a part of ruby): Foo.all.map(&:id).

I wanted to be able to pass multiple symbols and get back an array of information pertaining to each item in a collection. So: Foo.all.map(&[:id, :bar, :baz])

Why?

It is often the case that when you are debugging or trying to run down a problem in production, you have popped into console and need 2 or 3 pieces of information from a large number of items.

One way is to select them specifically in the AR call: Foo.all(:select => "id, bar, baz")

I don't enjoy writing those, mostly because as a technique, you end up being unable to use it exactly the same way outside of initial grab from the db.

Here's another: Foo.all.map{|x| [x.id, x.bar, x.baz]}

I like this better because I can use the technique on any collection, not just activerecord/resource, but I don't enjoy having all the extra syntax noise around the information I want to get out. It also doesn't map well to how I think about the data.

How do we get to Foo.all.map(&[:id, :bar, :baz])?

First, we have to know what Foo.all.map(&:id) actually does. The & calls to_proc on the symbol :id and then runs that proc against each member of the collection. You can check it out (in the rails source) at symbol.rb. And raganwald has a more detailed explanation and some cool observations. The comments are also excellent.

So, to make &[:id, :bar] do what we want, we just need to put a to_proc method on array that calls send for each method in the array.

I implemented this myself before I thought to look for a rails ticket. Turns out someone came up with a better implementation than my first. Here's the ticket and the code:

class Array
  # will allow stuff like foo.bars.map(&[:x, :y, :z])
  def to_proc
    Proc.new do |obj, *args|
      self.map { |method| obj.send(method) }
    end
  end
end

Now everything is more awesome.

Why wouldn't I use it?

Read the comments on the lighthouse ticket I linked. Mostly, some people would expect the array syntax to call one method with args, which is totally not the common case for me.

Additionally, you might not like adding functionality to core classes. It hasn't bitten me, yet!

Addendum

Did you read that article at raganwald yet? Did you read the comments?

Twitter Powered Self-Improvement

Posted by Matt Fri, 26 Feb 2010 22:56:00 GMT

Complaining to /dev/null

Complaining is the cornerstone of good programming. I mean, can you believe the features that are missing from this plugin? Or your crappy environment problems? Or how little you knew 6 months ago? I mean, come on. Seriously.

Come to think of it, you've seen these problems before. You should really write a plugin/submit a patch/start a project/write a snarky blog post, but you don't have time for that. Deadlines loom threateningly on the horizon, like a hydrogen-filled blimp powered by static electricity. Later. Yes! Later! Once doomsday is over, there'll be saw-sharpening like the world has never seen.

But let's be serious. That week never comes. You are forever stuck flying the Hindenburg while rubbing balloons together and you probably feel guilty about it. In Real Life(tm), you get chances to sharpen the saw sporadically: an hour here, two hours there. By the time you actually do have a chance to fix one of those problems, you can't even remember what most of them were. You'd keep a list, but you don't want to clutter up the bug tracker, you don't know where they go, etc. Never fear, Twitter is here!

Twitter To The Rescue

No, seriously, Twitter can help you. It's the perfect random, quick, shared idea notepad. Here's how we use it:

1) Make a twitter account (and protect the tweets, if you want)
2) Share credentials with the people on your team (or use birdherd.com) and follow the new account
3) Setup the global shortcut in Tweetie (or some other super easy way of tweeting)
4) Complain about everything

The global shortcut means that no matter what you're doing, you can slap a keystroke and pound out a thought without interrupting what you're doing for more than a few seconds. When you have some time to make an improvement, go look at the tweets and pick the thing that bothers you the most.

Why Twitter

Twitter works really well for this because

  1. You're used to it - You already use Twitter all the time anyway. Slapping one more account on there won't change your workflow much at all.
  2. Cathartic sharing - Twitter exists for the purpose of sharing quick ideas with people. Why not have an account specifically for technical frustrations among your team? It'll make you feel better.
  3. 140 character limit - No need to worry about babbling on; it's short-and-sweet or nothing.
  4. API access - Twitter's API is super easy. If you need to do something with the list later, a quick script will do it. In our case, we made a script that turns the tweets into tasks in Project Burndown for later scheduling.

Alternatives To Twitter

Maybe Etherpad with a text file? A wiki? Even if you don't like the Twitter implementation, the idea is sound. Keeping a list of all the things that bother you and slowly chipping away at them can give you confidence that things are getting better.

A Quick Tip For Tweeting Faster

Posted by Matt Fri, 26 Feb 2010 22:34:00 GMT

If you tweet a lot and you use Tweetie, here's a quick tip to make things faster. Set the global shortcut for "new tweet" to control-option-command-space.

I know that looks like a lot of keys, but try placing your hands there now. You can't miss, right? You just kind of flop you hand down and up comes the tweet box.

Tweetie -> Preferences -> Advanced -> Global Shortcuts, then click record and flop your hand down. You're all set.

Fun with Object#send...

Posted by Jason Fri, 19 Feb 2010 05:26:00 GMT

And by fun, I mean unexpected detours!

Background

If you're familiar with ruby you're probably familiar with the send method. It's great for generating method calls dynamically, or calling private methods from outside a particular object.

I've been having a good time recently whipping up Cucumber tests that use Culerity and Celerity. Together they allow me to emulate a browser that will actually run my javascript. It's awesome because it's headless, and far more reliable then Selenium (you can read more about it in Our Testing Stack). But, I ran into an issue the other day, exposing an interesting facet of ruby that others might find it useful to know.

We run Celerity in a separate process from the one that is actually running our tests (in Cucumber). Culerity makes this super easy by proxying our Celerity methods between our Cucumber process and our Celerity process. Culerity does this with method_missing and Drb, catching all the methods you call on a proxy object ($browser), and sending them over Drb to the Celerity process.

This allows you to do something like:

$browser.div(:text => /I'm in a div/)
$browser.p(:text => /Welcome to paragraph town/)

to find DIV or P tags with some given content.

How the proxy object works

So, when we boil this down to some example code, we get:

Nothing too crazy going on here. Now we want to find SPANs in addition to DIVs.

Everything's good, we're find elements left and right. Let's add p tags to the mix.

Gwa? The first two are the same, but sending "p" resulted in only the arguments being displayed. Here's where you scratch your head until you remember that "p" is a method on Kernel, that gets mixed into Object, that is the parent class of ProxyObject.

But why doesn't our culerity's $browser.p fail in the same way?

Turns out "p" is a private method that prints its arguments, and because send hits private methods, send finds the p method before method_missing.

The Solution

So, how can we make this act like a normal method call?  Eval to the rescue: Now what about making your arguments dynamic? We don't always want to find "foo", but we can't pass our arguments directly into eval because we would need to turn them into strings first. That won't work for most complicated objects, not to mention trying to serialize blocks. No, all we need to do is wrap our eval up in a little method and let ruby take care of the arguments and block itself.

TLDR

And there you have it. Using eval, we're calling the method without using send, so it will behave like a regular method call.

We can't afford information.gov

Posted by Matt Mon, 08 Feb 2010 23:22:00 GMT

I caught Mark Drapeau's piece on the new data.gov website (thanks to Sarah Evans). He points out, correctly, that what most people really want is information. Where he misses a step is in assuming that the government should be making information.gov instead. It's an easy step to miss.

I'm pretty sure I qualify as one of Mark's "technical elite", but I promise you I'm not excited about data.gov because it gives me political power. I'm excited about data.gov for the same reason I'm constantly excited about open source software: many hands make light work. In an ideal world, the government would have elite software developers working 'round the clock to make information.gov a reality. And they would fail. Not because they aren't smart enough or agile enough, but because it's way easier to collect data than it is to turn it into information.

As a conservative estimate, the cost of producing information is about one order of magnitude higher than just making data available. Sometimes though, it can be several orders of magnitude. As an example, take any of Hans Rosling's excellent TED talks. His GapMinder software analyzes data that's been publicly available from the United Nations for quite a while. So much data was available that key discoveries were missed. After years spent working on GapMinder, Rosling and his team have produced amazing insights into poverty, HIV and many other topics. By contrast, it takes the UN no time at all to make the data available.

I'm sure you saw this coming, but this is not an ideal world. The government can't afford to hire every great programmer to turn data into information. Not even close. The good news is they don't have to hire every great programmer. They don't have to hire any programmers at all. Free data will turn into free information for the same reasons that great open source projects like Firefox and Ruby On Rails come to be. We want to know. We want to scratch our itch. The open data movement is new. Be patient and I promise you that data.gov will turn into information.gov without the government having to lift a finger.

Big Dots + Redgreen for the Win

Posted by Joel Wed, 03 Feb 2010 20:09:00 GMT

Redgreen is a gem that colours your test output in very useful ways. You should use it. It makes your test output look like this (though hopefully it's all green):

So, that's awesome. Makes it much easier to figure out what's going on in your tests.

If you read yesterday's blog, you would have seen that I use redgreen, but my big dots weren't showing up coloured.

Here is a patch for redgreen that will colour those, too.

Make sure you have the patch from yesterday and have these lines in test_helper. (The “require 'test/unit/ui/console/testrunner'” bit is redundant because of redgreen, but I'll leave it there anyway.)

And make sure you have the redgreen gem in your test.rb environment file.

Thanks you @mediocretes for some debugging help last night on this problem.

Enjoy!

Make Slow Test::Unit Tests Stand Out

Posted by Joel Tue, 02 Feb 2010 18:55:00 GMT

Sometimes you have slow unit tests. It sucks, and they probably shouldn't be slow, but you have them.

I wanted a way to differentiate any test that took longer than a second to run on the command line. A little peek into the source for test runner showed me I could easily add a differentiator.

Here's the gist:

You can save that file somewhere convenient. I use 'RAILS_ROOT/lib/patches/'. Then put something like the following in test_helper or wherever is appropriate for what you are doing.

So, now your tests print a large dot (okay, it's a smiley face, but it looks like a big dot unless you turn the font size way up) when a test passes but took longer than a second. Clearly you could adapt this to print lightning bolts, or frowny faces, or whatever you want, or to employ a number of symbols depending on how long things take.

Cucumber-style Tagging for Test::Unit Tests

Posted by Joel Sun, 31 Jan 2010 22:09:00 GMT

So, you love yourself some cucumber. But you also love unit testing.

Some would argue that every time you make a change in a unit test, you should run your entire suite. But sometimes that isn't what you want. I found myself constantly wanting to tag T::U tests and only run a couple at a time, and to ignore some tests altogether.

I whipped up a patch that does just that.

Here's a sample test script showing the tagging on the tests. It's all ruby, instead of a DSL like cucumber, so it uses rubyisms. The following will run all the tests marked “plot” that are not marked “slow” or “truthy”: Here's the patch. I am in RoR, and I put it at lib/patches/activesupport.rb. Also had to add this to my test_helper file in RoR. Enjoy!