Chad Woolley's blog
Evan Phoenix at Mountain West Ruby Conf
I just attended (and thoroughly enjoyed) the Mountain West Ruby Conf, where Evan Phoenix gave a powerful keynote speech.
He talks about the status of Rubinius, and makes some profound observations on modern open source culture and community.
Here's some highlights, but if you participate in open source, and especially if you help run an open source project, I highly recommend that you watch the video:
- Community: Rubinius' Giant Spec Suite, and its value not only as a living language specification for different implementations of Ruby itself, but as a "gateway drug" which provides a low barrier to get new contributors addicted to open source.
- Trust: Asking for forgiveness vs. permission, and the Rubinius commit policy, where any accepted patch gets you commit rights. You can always roll back a change, and debate is healthy.
- Worth: The impact of annoying fifteen year olds who make a lot noise versus "core" contributors.
- Ego: You are not the project, Mr. Ego! The importance of being wrong and admitting your faults in public.
- Innovation: Fostering innovation and debate vs closely holding the mythical "Keys to the Castle".
ruby-debug in 30 seconds (we don't need no stinkin' GUI!)
Many people (including me) have complained about the lack of a good GUI debugger for Ruby. Now that some are finally getting usable, I've found I actually prefer IRB-style ruby-debug to a GUI.
There's good tutorial links on the ruby-debug homepage, and a very good Cheat sheet, but I wanted to give a bare-bones HOWTO to help you get immediately productive with ruby-debug.
Install the latest gem
$ gem install ruby-debug
Install the cheatsheet
$ gem install cheat
$ cheat rdebug
Set autolist, autoeval, and autoreload as defaults
$ vi ~/.rdebugrc
set autolist
set autoeval
set autoreload
Run Rails (or other app) via rdebug
$ rdebug script/server
Breakpoint from rdebug
(rdb:1) b app/controllers/my_controller.rb:10
Breakpoint in source
require 'ruby-debug'
debugger
my_buggy_method('foo')
Catchpoint
(rdb:1) cat RuntimeError
Continue to breakpoint
(rdb:1) c
Next Line (Step Over)
(rdb:1) n
Step Into
(rdb:1) s
Continue
(rdb:1) c
Where (Display Frame / Call Stack)
(rdb:1) where
List current line
(rdb:1) l=
Evaluate any var or expression
(rdb:1) myvar.class
Modify a var
(rdb:1) @myvar = 'foo'
Help
(rdb:1) h
There are many other commands, but these are the basics you need to poke around. Check the Cheat sheet for details.
This can also be used directly from any IDE that supports input into a running console (such as Intellij Idea).
That should get you started. So, before you stick in another 'p' to debug, try out ruby-debug instead!
The Power of Versions (Monkey Patches Targeted with Friggin Laser Beams!)
We all love to Monkey Patch Rails and other Ruby apps. However, we sometimes want to target these patches to the specific versions where they are needed.
Here's the easiest way to do this, via RubyGem's built-in version requirement support. The version 0.11.0 should indeed be greater than version 0.9.0:
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> Gem::Version::Requirement.new(['> 0.9.0']).satisfied_by?(Gem::Version.new('0.11.0'))
=> true
Notice that you can't do this with string comparison, because with a per-character comparison,1 is not greater than 9:
irb(main):001:0> '0.11.0' > '0.9.0'
=> false
Here's a little class which puts some helper and example methods around this approach (these methods are all in real use for some of our multipart mailer hacks):
module Pivotal
class VersionChecker
def self.current_rails_version_matches?(version_requirement)
version_matches?(Rails::VERSION::STRING, version_requirement)
end
def self.version_matches?(version, version_requirement)
Gem::Version::Requirement.new([version_requirement]).satisfied_by?(Gem::Version.new(version))
end
def self.rails_version_is_below_2?
result = Pivotal::VersionChecker.current_rails_version_matches?('<1.99.0')
result
end
def self.rails_version_is_below_rc2?
Pivotal::VersionChecker.current_rails_version_matches?('<1.99.1')
end
def self.rails_version_is_1991?
Pivotal::VersionChecker.current_rails_version_matches?('=1.99.1')
end
end
end
(note: some angle brackets changed due to code formatting bug)
Here's an example of how you'd use this:
if Pivotal::VersionChecker.rails_version_is_below_2?
# do some backward compatibility stuff
# or handle bugs that have been fixed in Rails > 2
end
Note that this is only possible now that Rails has started using a more sensible strategy for versioning edge gems and improved support for using advanced versioning with RAILS_GEM_VERSION.
For many projects, this may be overkill. It is useful at Pivotal, though, where many various projects may be on different rails versions, but still want to use the latest common core libraries (and monkey patches) without having to upgrade Rails for their app.
This isn't only useful for monkey patching. It can be handy for any library that wants to be backward- or forward-compatible with its dependencies. I've used this approach at Pivotal and on my personal projects to have Continuous Integration automatically run my tests against multiple dependency versions, without having to change anything other than the CI project name:
There are numerous other related topics for discussion in this area, such as the power of versions or the wisdom of freezing, but I'll save those for future posts. Even if you do freeze the trunk of Rails/plugins/gems, since the version is included in the source, this approach should work barring any conflicts with trunk changes since the last release.
Happy Versioning!
Using Search and Replace Regular Expressions to Convert from Test::Unit to Rspec
I was just converting some Test::Unit tests to Rspec, and these regexps were handy. In one file, they handled 51 out of 53 lines, saving my fingers a lot of work. Tests can take an infinite variety of formats, so these obviously won't apply to everything, but they do illustrate how to use regexp substitution. This is using TextMate, your regexp implementation may vary...
from -> to
search string
replace string
def test_foo -> it "test_foo" do
def (test_[a-z_]*)
it "$1" do
assert !foo -> foo.should_not be_true
assert !(.*)$
$1.should_not be_true
assert foo -> foo.should be_true
assert (.*)$
$1.should be_true
assert_equal foo, bar -> bar.should == foo
assert_equal (.*), (.*)$
$2.should == $1
Avoiding Constants in Rails
In his post "Redefining Constants" ( http://www.pivotalblabs.com/articles/2007/04/14/redefining-constants ), Brian Takita describes how to redefine Rails constants at test time. He points out that "it's all dirty", and that "...maybe the storage service can be an attribute that can be changed for individual tests.".
In a comment, I suggested that a global configuration object would be a better approach, and here's an example. It still uses a constant (as opposed to a global singleton object), but the constant is an object (a hash) which contain other values and objects. This avoids the need to redefine constants to use different values at test-time.
Create a sample Rails app
$ rails railsdi $ cd railsdi/ $ ls $ script/generate controller Sample $ # create development/test databases
Declare the configuration hash
First, add a constant in boot.rb. Just ignore the warning to not modify boot.rb - it's not talking about you. Put this at the beginning, right after the section that defines RAILS_ENV
boot.rb
REGISTRY = {}
Set per-environment defaults
Set any values or objects you want in the registry:
development.rb
REGISTRY[:key] = "development_value"
test.rb
REGISTRY[:key] = "test_value"
production.rb
REGISTRY[:key] = "production_value"
Verify that the correct values are used in each environment
Make a simple controller and view to verify the values are set per-environment:
sample_controller.rb
class SampleController < ApplicationController
def index
@registry_value = REGISTRY[:key]
end
end
sample/index.rhtml
Rails Environment: <%= RAILS_ENV %> Registry Value: <%= @registry_value %>
Start up the app in development and production environments, and hit http://localhost:3000/sample
Verify that registry values can be overridden at test time
sample_controller_test.rb
def test__can_redefine_registry_value
REGISTRY[:key] = 'overridden_value'
get :index
assert_equal 'overridden_value', assigns['registry_value']
end
Summary
I think this is a pretty good approach, and it feels a lot like testing in an app that uses a Dependency Injection/Registry architecture (in other words, simple to override anything you want). I'd be interested to hear if there are any situations that could not use this approach, and would have to fall back to defining constants in the environment files.
It would also be interesting to hear if anyone has had success integrating a Rails application with a Dependency Injection approach (using Needle or a home-grown solution).
-- Chad
Subversion gotcha - deleted folders not shown in diff by default
Say you have two tags you want to diff, and one has a deleted directory. If you do an 'svn diff', you won't see the deleted directory UNLESS you give the '--summarize' option:
svn diff --summarize http://host/project/tags/old_version http://host/project/tags/old_version







