Search
Me

Developer for Forward. Former Technical Lead at ThoughtWorks.

Recent Comments
Elsewhere

Entries in rails (42)

Thursday
08Nov2007

Watch out for the Monkey Patch

The project I’m currently working on uses both the Asset Packager and Distributed Assets to ensure we have only a few external assets, and that we can load assets across more than one host - all so that the pages for our site load nice and quick.

Unfortunately, wiring in the Asset Packager plugin caused the Distributed Assets plugin to break, and I spent an hour or two tracking it down yesterday. The cause? Asset Packager redefines the compute_public_path method.

# rewrite compute_public_path to allow us to not include the query string timestamp
# used by ActionView::Helpers::AssetTagHelper
def compute_public_path(source, dir, ext=nil, add_asset_id=true)
source = source.dup
source << ".#{ext}" if File.extname(source).blank? && ext
unless source =~ %r{^[-a-z]+://}
source = "/#{dir}/#{source}" unless source[0] == ?/
asset_id = rails_asset_id(source)
source << '?' + asset_id if defined?(RAILS_ROOT) and add_asset_id and not asset_id.blank?
source = "#{ActionController::Base.asset_host}#{@controller.request.relative_url_root}#{source}"
end
source
end

Distributed Assets works by chaining compute_public_path - decorating the calculated path, adding the asset host prefix onto the url. But, Asset Packager works by defining the method into ActionView::Base. So, when DistributedAssets::AssetTagHelper is included with ActionView::Helpers::AssetTagHelper, it chains a (now) hidden method.

But, the only places that use the new compute_public_path code inside the Asset Packager Helper (which just avoids using the query string timestamp) is within Asset Packager itself.

So, I tweaked the implementation of AssetPackageHelper to

def compute_public_path_for_packager(source, dir, ext, add_asset_id=true)
path = compute_public_path(source, dir, ext)
return path if add_asset_id
path.gsub(/\?\d+$/, '')
end

def javascript_path(source)
compute_public_path_for_packager(source, 'javascripts', 'js', false)
end

Beware the monkey patch.

Monday
29Oct2007

DTrace in Leopard with Ruby probes

I’d read a while back that Apple were going to include Sun’s DTrace tool in the now newly released Leopard - underpinning Instruments (formerly known as Xray).

What I hadn’t noticed was that the build of Ruby 1.8.6 included in Leopard (patchlevel 36 with some extras) also includes Ruby probes from the lovely folks at Joyent. Apple have ported everything. So, out of the box, Ruby works with DTrace! Sweet!

From Sun’s page:

DTrace is a comprehensive dynamic tracing facility that is built into Solaris and can be used by administrators and developers to examine the behavior of both user programs and of the operating system itself. With DTrace you can explore your system to understand how it works, track down performance problems across many layers of software, or locate the cause of aberrant behavior.

Joyent have got some sample scripts in their Subversion repository, and, what looks like some nice extensions for Rails (DTrace probes can be fired from within Ruby). Blimey!

For those (running Leopard) and want to just see something, try this out. It’s a script that traces the number of times a method is called. Once downloaded, all you need to do is

$ chmod +x functime.d
$ sudo ./functime.d -p <pid>

Replacing <pid> with the process id for your target Ruby process. Once you stop tracing (or your target process ends you’ll see some nice text formatted output. Including something like the following (from my current project):

Hash                     key?                     25123         15       384174
Module each_object 1 390824 390824
Module included_in_classes 1 391025 391025
Module reloadable_classes_with 1 391203 391203
Module reloadable_classes 1 391835 391835
ActionController::Routin load_routes! 1 394864 394864
ActionController::Routin reload 1 398364 398364
Class reset_application! 1 398947 398947
Class reset_after_dispatch 1 400474 400474
ActionController::Routin connect 39 13192 514506
Array include? 3353 166 556605
ActionController::Routin build 47 13116 616459
ActionController::Routin add_route 47 13329 626488
Array each 874 780 682423
Object load 2 376432 752864
Module silence 2 391586 783172
Kernel gem_original_require 28 43460 1216884
Array select 316 4106 1297580
Array collect 351 4124 1447524
Module local_constants 91 17120 1557986
Module new_constants_in 25 93885 2347139
Object require 50 58495 2924783

The columns (from left to right) are: Class, Method, Count (number of times method was called) and then the average and total micro seconds. The d script was one of the Joyent examples. Arstechnica also have a very cool example showing the call stack from a system call.

What’s more amazing is this doesn’t require any recompilation, special flags, running in special environments. It’s available from the off!

If all that’s whetted your appetite (and I’m not sure how it couldn’t), head on over to the DTrace how to for a little more explanation of how it all works.

Time to get stuck in and see whether anything emerges about my current project. Very cool stuff!

Monday
20Aug2007

Testing Rails' page caching in RSpec

In preparation for a Ruby project I wanted to play around with Rails’ page caching a little over the weekend. Naturally, I wanted to find a way to ensure that my controller was writing files correctly so that Apache (or any other HTTP server) can then serve the static content directly (without hitting my Rails processes). Since I’m also digging RSpec at the moment, I wanted to find a way of expressing it nicely in my specifications. This is what I ended up with.

Here’s the relevant controller’s specification:

describe UsersController, "caching" do
include CachingExampleHelper

it "should cache new users page" do
requesting {get :new}.should be_cached
end

it "should not cache the activation page" do
requesting {get :activate, :activation_code => 'blah'}.should_not be_cached
end
end

The key part is the requesting line. That’s my extension that encapsulates the request that should be cached, so that we can both make the request and check that it ended up with a file being written to the filesystem.

module CachingExampleHelper
ActionController::Base.public_class_method :page_cache_path
ActionController::Base.perform_caching = true

def requesting(&request)
url = request.call.request.path
ActionController::Base.expire_page(url)

request.call
end

module ResponseHelper
def cached?
File.exists? ActionController::Base.page_cache_path(request.path)
end
end

ActionController::TestResponse.send(:include, ResponseHelper)
end

Note that we have to expose the pagecachepath method so we can calculate the location for where the cached files will get written, and then also explicitly enable caching. When we call requesting giving a block to the request we want to make (such as get :index to invoke the index action), we capture the block as a Proc, invoke it and then read the request’s path from the generated TestResponse object. This lets us then first expire the page (in case it was cached previously), and then make the request. Finally, we then check whether the cached file exists by mixing in a cached? method to the Response.

Unfortunately, the code above will make 2 requests per assertion. But, I’d rather have it read nicely for the time being. Any suggestions on how you could tidy this up further would be well received!

I’m not sure I’d want to run these tests all the time, maybe something a little lighter to ensure that we tell Rails to caches_page for relevant actions in our controllers, but as a definite re-assurance it seems ok.

Thursday
03May2007

A Neat Little Rails Testing Pattern

Inside my tests I try and keep everything I need inside the test, rather than depending on too many fixtures. That way I ensure they’re as readable as possible, and more importantly, obvious.

But, take the following example:

def test_total_is_due_now_if_return_is_less_than_14_days
booking = Booking.new(
:customer => customers(:valerie),
:collect_at => 1.day.from_now.to_date,
:return_at => 13.days.from_now.to_date,
:boxes => 2,
:collection_address => addresses(:one))
assert_equal Date.today.to_s, booking.final_payment_due_date.to_s
end

def test_payment_due_14_days_from_now
booking = Booking.new(
:customer => customers(:valerie),
:collect_at => 1.day.from_now.to_date,
:return_at => 20.days.from_now.to_date,
:boxes => 2,
:collection_address => addresses(:one))

assert_equal 6.days.from_now.to_date.to_s, booking.final_payment_due_date.to_s
end

Well, everything’s in the test, but there’s a fair bit of duplication. What would be nicer, is to take the prototype booking above and just override certain values.

Instead we might write something like

def test_total_is_due_now_if_return_is_less_than_14_days
booking = new_booking_with(:return_at => 13.days.from_now.to_date)
assert_equal Date.today.to_s, booking.final_payment_due_date.to_s
end

def test_payment_due_14_days_from_now
booking = new_booking_with(:return_at => 20.days.from_now.to_date)
assert_equal 6.days.from_now.to_date.to_s, booking.final_payment_due_date.to_s
end

private
def new_booking_with(args)
Booking.new(args.reverse_merge({
:customer => customers(:valerie),
:collect_at => 1.day.from_now.to_date,
:return_at => 15.days.from_now.to_date,
:boxes => 2,
:collection_address => addresses(:one)
}))
end

Much nicer (to me anyway), particularly once you start adding more and more tests (and especially validation related ones).

Wednesday
18Apr2007

Checking Object Equality In Mocha

I’m working on a little pet Rails project and wanted to use Mocha to isolate my controller tests a little from what is usually encouraged, especially in light of what happened last time. I figured I’d give Mocha a go.

I wanted to verify that my service was called with objects that look equal to what I was expecting. Ordinarily, it will compare instances - are they the same object. I didn’t want that, but rather wanted to test that the values of my (ActiveRecord) objects were the equal. Easy. Mocha allows with to be called with a block that is evaluated to compare the objects.

My tests look something like this

def test_should_ask_service_to_calculate_new_prices_for_booking
address = Address.new(:postcode => "W1 1QE")
booking = create_booking

OrderBookingService.expects(:calculate_price).with(booking, address) do |b, a|
b.attributes == booking.attributes && a.attributes == address.attributes
end

post :calculate_price, :booking => booking, :collection_address => address
end

It’s something I’ve had to do many times in the past with JMock and NMock - I want to test equality for an aspect of some other object but it’s always meant writing rather a lot of code. Neat.