MountainWest RubyConf videos (including my lightning talk on GuessMethod)

Confreaks have started putting up more video from MountainWest RubyConf, and today saw the addition of the lightning talks from day one. I’d like to draw particular attention to the second entry of this list of who talked when about what:

  1. Binary Lottery in Shoes - Mike Moore - 0:10
  2. GuessMethod - Chris Shea (me!) - 4:35
  3. ActiveScaffold - Ed Moss - 8:51
  4. Migration Concordance - Josh Susser - 12:39
  5. Framework Components - Jeremy McAnally - 18:05
  6. Systems Building Systems with Puppet - Andrew Schafer - 20:43
  7. Multi-image Uploading with SWFUpload and Rails - David South - 27:14
  8. RubyAmp: Ruby Dev in TextMate Amplified - Tim Harper - 31:55
  9. Build a GUI App in 5 Minutes - David Koontz - 37:46

Lots of interesting stuff in there. And I encourage everyone to look at other videos from the conference. It was great.

Comments

GuessMethod 0.2.1

I gave a lightning talk about GuessMethod at MountainWest RubyConf, and then I looked at the code again. I figured it could use a little cleaning, so I took my steel wool to it.

I fixed up some of the documentation and the website, made the specs run again (they always passed, they just stopped running at some point… rspec change?), and rewrote the code that finds close matches of missing methods and constant names. And that last change (based on a naïve benchmark) doubled its speed (of course, iterating over an array once instead of twice will sort of do that).

So, if you need aggressive spell-checking in your irb or script/console sessions check it out:

001:0> Strin.tos
attention: replacing non-existant constant Strin with String for Object
attention: sending to_s instead of tos to String:Class
“String”

Or, if you sometimes can’t be bothered to correctly remember rake task names (and rake -T takes too long), use grake!

mvb:~/hot-rails-app cms$ grake db:migrat
(in /Users/cms/hot-rails-app)
attention: invoking task db:migrate instead of db:migrat
...

It might show up in Giles Bowkett’s utility_belt at some point, in case you’re into that and you can’t be bothered with this:

sudo gem install guessmethod

Comments

Salt Lake City

I’m in Salt Lake City for MountainWest RubyConf. Say hello. I’ll be wearing an Orioles cap and two hooded sweatshirts the whole time.

Comments (2)

Speeding up gem on OS X Leopard

Update: gem has changed for the better, and this is no longer necessary. Go ahead and gem update --system.

After upgrading to Leopard from Tiger, I noticed that certain network operations for some processes suddenly took forever. Most noticeably, when gem would update metadata. And I’m not the only one.

It’s not so bad anymore, and all I had to do was grab something from the standard library.

In gem (mine’s at /usr/local/bin/gem), add this line before any other code:

require 'resolv-replace'

Done. Seriously. gem will work much faster. Not as fast as you remember from Tiger, but getting there.

Discussions I’ve had suggest that Leopard isn’t caching DNS requests from certain processes, and letting Ruby handle that itself gets around this.

Update: Here’s the current state of my /usr/local/bin/gem:

#!/usr/local/bin/ruby
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++

require 'resolv-replace'

require 'rubygems'
require 'rubygems/gem_runner'

required_version = Gem::Requirement.new ">= 1.8.2"

unless required_version.satisfied_by? Gem::Version.new(RUBY_VERSION) then
  abort "Expected Ruby Version #{required_version}, was #{RUBY_VERSION}"
end

# We need to preserve the original ARGV to use for passing gem options
# to source gems.  If there is a -- in the line, strip all options after
# it...its for the source building process.
args = !ARGV.include?("--") ? ARGV.clone : ARGV[0...ARGV.index("--")]

Gem::GemRunner.new.run args

Comments (8)

You might be able to stop trying to call methods on nil

Correct me if I’m mistaken. Please.

The people behind andand, try, do_or_do_not, NilClass#method_missing, SafeNil, maybe, etc. seem to be trying to beautifully solve the problem of calling methods on objects that might be nil and not having Ruby complain about it. They might be chaining methods a lot or, when they check if an object is nil, they use the ternary conditional and (rightly) think it’s ugly.

As for these ternary conditional users, you can do this: @person.name if @person instead of @person ? @person.name : nil. It does what you need, looks great, and is highly readable.

Of course, that doesn’t help you if you’re chaining methods.

But (and this is what’s been on my mind), I suspect that all of these solutions value terseness over readability and flexibility. It’s a trade off I’m willing to make sometimes, but I think it’s unnecessary here.

For example, Reg Braithwaite’s example:

@phone = Location.find(:first, ...elided... ).andand.phone

We’re trying to get the phone number of a location, but maybe we won’t find the location. Seems normal on the surface. But these are the questions that run through my mind looking at this:

  1. All you need is the phone number?
  2. You’re sure you don’t need anything else from the Location object? (address, url, hours, etc.)
  3. It doesn’t mean anything if the location isn’t found?

I find it hard to believe that the answer to all these questions is “Yes”. I know this is example code, but it must be inspired from something, right?

I’d bet that instead of @phone = Location.find(:first, ...elided... ).andand.phone, later on you’d really wish you had this:

@location = Location.find(:first, ...elided... )

Maybe you’d discover your view (Reg’s example is ActiveResource-related, I’m running with that) needs more than just the phone number (who are you calling?). Or maybe you’d discover that if the location isn’t found, that that means something. Maybe you need to alert the user (unless @location ...).

It could be that I’m more hung up on this idea of meaning. The example for Ben’s maybe is:

if(customer && customer.order && customer.order.id==newest_customer_id)

I look at that and think: if there’s no customer, that means one thing; if there’s no order for the customer, that means something else; if the customer’s order’s id isn’t newest_customer_id, that means something else entirely; and if all these things are different, maybe they should be handled separately.

If you think about it that way, there’s no need to fiddle with nil or add methods to Object.

(I completely understand that this might just be an aesthetic difference, or that sometimes this apparent loss of flexibility is desirable, or that I might be wrong. Please narrow it down for me in the comments. Thanks.)

Comments (14)

A better try — chaining methods and nil

Of #try, #andand, and #do_or_do_not, the one I like best (and I don’t even like any of them that much, but that’s my next post) is #try.

What they are all basically for is chaining method calls when one of methods might return nil, resulting in a NoMethodError. Here’s Chris Wanstrath’s implementation:

class Object
  ##
  #   @person ? @person.name : nil
  # vs
  #   @person.try(:name)
  def try(method)
    send method if respond_to? method
  end
end

It’s almost right!

First of all, I’d argue that if #try is trying to be better than something, it had better be better than @person.name if @person. That looks and reads so nicely I can scarcely see why it needs replacing. @person.name if @person is so obviously better than @person ? @person.name : nil that I hope people are using that before they start thinking about monkey patching Object (which I hear is destroying Ruby). Anyway, I’d fix the documentation first.

But I think the biggest problem with #try is that it’s not doing what it says it’s doing. It’s not a replacement for @person ? @person.name : nil. That’s certainly not what the method is doing. Here’s what I’d do:

class Object
  ##
  #   @person.name if @person
  # vs
  #   @person.try(:name)
  def try(method)
    self.send(method) if self
  end
end

See? Much clearer; it does what it says. Using respond_to? means that the original #try is about something else; it’s about whether or not an object responds to a message. This #try is about sending a message to an object unless it’s nil or false, which is closer to what the original use case was… right?

But then it occurred to me that the issue is nil, not nil or false. The problem isn’t getting false in the middle of method chains, it’s nil. So maybe what #try should really be is this:

class Object
  ##
  #   @person.name unless @person.nil?
  # vs
  #   @person.try(:name)
  def try(method)
    self.send(method) unless self.nil?
  end
end

I might even use that.

Comments (7)

Boy, what ISN’T destroying Ruby these days?

What else?

(Note: Charles Oliver Nutter is helping when he tells us that Thread#raise, Thread#kill, and timeout.rb are broken.)

Comments (7)

Pyre 0.3.0: Because Campfire doesn’t have an API (really)

I’ve released version 0.3.0 of Pyre, an alternative ruby library for interacting with Campfire from 37signals. Pyre now supports uploading files to a room! Observe:

require 'rubygems'
require 'pyre'  # it rhymes!

Pyre::Campfire.new('pyre') do |campfire|
  campfire.login('bot@seriouslyreal.com', '12345')
  campfire.room('Hot Chat') do |room|
    room.upload('/path/to/animated.gif')
    room.speak('What a cat!')
  end
end

Also, now there’s a contrib directory which will hold examples of things to do with Pyre. Currently, the only thing in there is a Subversion repository post-commit hook for putting really nice messages about commits into a Campfire room. It’s really nice. Out of the box they look like this:

cms commited revision 104
*************************
Fixed all spelling mistaeks in the README
*************************
U   pyre/trunk/README

I’m hoping to expand that contrib dir. I’m hoping people will help too.

Anyway, get it and use it.

sudo gem install pyre

Comments (2)

Pyre, an alternative library for Campfire

I’ve recently had reason to use 37signals’ Campfire, and thus a need for a Campfire bot. Tinder does a fine job, but I was running into issues (minor annoyances), so of course I decided to write my own library for interacting with Campfire.

And Pyre was born.

As of right now, you can log in, log out, join and leave rooms, speak and paste to rooms, and that’s it. But for something like a svn post-commit hook, that’s all you need. Pyre uses Mechanize (as opposed to Tinder’s ActiveSupport, Hpricot, and regular expressions combo) to deal with Campfire, so it was pretty easy to throw together.

Sample code? Here’s what the end of an svn post-commit hook might look like (after the message has been built):

Pyre::Campfire.new('subdomain', :ssl => true) do |campfire|
  campfire.login('bot@email', 'botpassword')
  campfire.room('Development') do |room|
    room.paste(message)
  end
end

Pretty straightforward.

For the most part I tried to stay close to Tinder’s interface so that porting to Pyre would be easy, but this block style is new (and, I think, nice).

I’ll be implementing listening in a room soon enough I wager.

So go ahead and get it!

sudo gem install pyre

Comments

MountainWest RubyConf

I finally ponied up the meager $100 for my spot at MountainWest RubyConf. Last year was good, and it looks like this year will be better. Pat Eyler says, “One of the sponsors has let me know that they’re planning something really special for attendees.” I’m looking forward to receiving this “special” mousepad. (It’s going to be a mousepad, right?)

If you need to harass me in person about how GuessMethod and CSSes aren’t suited for production, I’ll be wearing two hooded sweatshirts and a Baltimore Orioles cap. See you there.

Comments (1)

« Previous entries · Next entries »