{ |one, step, back| } 27 to 36 of 177 articles WikiSyndicate: full/short

Color Blind   02 May 07
[ print link all ]

Presented a talk on Test First Design today at work.

I did a Lunch & Learn presentation today at work on the topic of unit testing and Test First Design, included a discussion of the Red / Green / Refactor technique. Unfortunately the red gun on the video projector was bad, ruining all the careful color coding I put in to my slides. So the technique will now be officially called Black / Yellow / Refactor.

Is This a Bad Idea?   23 Apr 07
[ print link all ]

This idea occured to me as I was reviewing a Rakefile recently.

Ugly

Rakefile task comments are a bit ugly. Since I want Rake to report task descriptions, I can’t use regular Ruby comments (which would be invisible to Rake). Instead, Rake will use the desc command, as in:

desc "This task will do something" 
task :do_something do ... end

An Alternative Syntax

While I was reviewing a Rakefile recently, it occured to me it would be entirely possible to write a task comment like this:

-- "This task will do something" 
task :do_something do ... end

You can think of this as a kind of marriage between Ada comments (using—) and Smalltalk comments (using ’”’).

The Implementation

  class String
    def -@
      desc self
      0
    end
  end

Yep, its that simple.

Fortunately, my better judgement took over at that point and this comment style will not make it into Rake. Unfortunately, it didn’t take over in time to prevent me from blogging about it.

Custom Rake Applications   21 Apr 07
[ print link all ]

It’s odd how ideas come to you. I was working on a script unrelated to Rake, when a different way of using Rake occurred to me.

Start / Stop Script

Today a coworker passed on a shell script for staring and stopping a server on a local box. The script was pretty typical. You would type “server start” and “server stop” to bring the aforementioned server up and down. It looked something like this:

#!/bin/sh

SERVER_DIR=/path/to/server/directory

case $1 in
    start)
        pushd $SERVER_DIR
        ./startServer.sh >out.log &
        popd
    ;;

    debug)
        pushd $SERVER_DIR_
        ./startServerDEBUG.sh >out.log &
        popd
    ;;

    stop)
        pushd $SERVER_DIR
        sh stopServer.sh
        popd
    ;;

  *) echo "invalid" ;;
esac

There were a few more options, but you get the drift. Anyways, I wanted to to customize the script a bit and realized that my shell scripting abilities were a bit rusty. So I translated the whole thing to Ruby …

The Ruby Version

The translation was pretty straight forward. The guts of the script looked like this:

ARGV.each do |arg|
  case arg
  when 'start'
    Dir.chdir(WLS) do
          system "./startWebLogic.sh >out.log &" 
    end

  when 'debug'
    Dir.chdir(WLS) do
          system "./startWebLogicDEBUG.sh >out.log &" 
    end

# [... code elided ...]

and so on.

After about the second or third “when” clause in the “case” statement, I realized that I was essentially writing tasks. And we have a perfectly good Ruby tool for managing task based software: Rake!

So why wasn’t I writing this as a Rake script? One reason is that Rake depends on the presence of a Rakefile in the current (or parent) directories. I needed to be able to run any of my server commands from anywhere in the file system, not just from the directory containing the Rakefile.

Fair enough. But there’s nothing preventing me from writing a ruby script that includes the Rake library. We saw something similar in the FindInCode script earlier. The only additional thing we need to do is include the task definitions directly in the script, and then explicit invoke the tasks we need.

The Initial Rake Version

So I abandoned my first cut at a Ruby version and went with this:

#!/usr/bin/env ruby

require 'rake'

SERVER_DIR = /path/to/server/directory

task :start do
  Dir.chdir(SERVER_DIR) do
    sh "./startServer.sh >out.log &" 
  end
end

task :stop do
  Dir.chdir(SERVER_DIR) do
    sh "./stopServer.sh >out.log &" 
  end
end

# [... more task defintions go here ...]

ARGV.each do |arg| Rake::Task[arg].invoke end

Now I have a version that can handle all the task dependency stuff that Rakefiles do, but can be run from anywhere in the file system. Assuming I named the script “server”, I can say:

server start

and the right code gets run. I can specify dependencies between the tasks in the script (e.g. “task :bounce => [:stop, :start]“), and do all the other great stuff that building on Rake allows.

The Improved Rake Version

There are a couple of downsides to the above script. Even though I can invoke the server script as if it were a rake-like command, it doesn’t do everything that rake does. For example, there is no way to get a list of all the defined tasks (.e.g rake -T). In fact, no rake command line options are support in the above code. Neither are the environment variable parameters (e.g. TEST=test_file_name.rb). Furthermore, the error messages are not trapped and handled the way that Rake does it.

Now, all of the above could be manually added to the server script, but a better way is to slightly refactor Rake to better support customer rake applications. The init and top_level public methods were added to the Rake Application class, so now we can write the above script as:

#!/usr/bin/env ruby

gem 'rake', '>= 0.7.3'
require 'rake'

Rake.application.init('server')

SERVER_DIR = /path/to/server/directory

task :start do
  Dir.chdir(SERVER_DIR) do
    sh "./startServer.sh >out.log &" 
  end
end

task :stop do
  Dir.chdir(SERVER_DIR) do
    sh "./stopServer.sh >out.log &" 
  end
end

# [... more task defintions go here ...]

Rake.application.top_level

All we did was add a call to Rake.application.init to initialize the command line parameter information in the Rake application. You can specify an optional application name to init to allow the Rake software to correctly report the application name when handling a -T command line option.

The second thing we did was change the explicit loop through all the parameters at the end of the script into a single call to top_level. The top_level method not only handles the invocation of the top level tasks, but also is smart enough about the Rake command line options to properly handle them.

One final note about the “gem” command line near the top. The init and top_level methods were introduced in version 0.7.3 of Rake. By using the gem command we can ensure that are using a compatible version of the Rake software.

Summary

When should you use a normal Rakefile, and when should you write a custom Rake application? A Rakefile works best for per-project type of commands, such as those used to build and test your individual projects. You are (almost) always somewhere in the project directory tree when invoking Rake, and so it just does the right thing.

Custom Rake applications work well when the command are not per-project, and you need to run them anywhere in the file system. A custom application gives you the freedom to run the command anywhere and not need a Rakefile first.

Here’s another quick example where I found a custom Rake application to be useful. Lately I have been doing a lot of small Ruby scripts. I have setup TextMate to autorun the tests via rake, but that means before I can test any of my mini-ruby program, I need to take the time to write a Rakefile to run my tests. Normally I would use rake to automate such a repetitive task, but rake isn’t effective until I have a Rakefile, so we’re in a kind of Catch-22 situation. But with a custom Rake application, I can say something like “proj rakefile” ... and have proj do all the heavy lifting for me without writing an explicit Rakefile first.

Ain’t automation grand?

FlexMock 0.6.0 Released   17 Apr 07
[ print link all ]
FlexMock version 0.6.0 was just released over the weekend. You can read the release announcement below for all the new features and enhancements.

FlexMock is a flexible mocking library for use in unit testing and behavior specification in Ruby. Version 0.6.0 introduces a number of API enhancements to make testing with mocks even easier than before.

New in 0.6.0

  • Better integration with Test::Unit (no need to explicitly include FlexMock::TestCase).
  • Integration with RSpec (version 0.9.0 or later of RSpec is required).
  • The flexmock method will now create both regular mocks and partial mocks.
       flexmock()          # => a full mock
       flexmock(person)    # => a partial mock based on person
    

    (flexstub is still included for backwards compatibility).

  • Quick and simple mocks my now be created using an expectation hash. For example:
      flexmock(:foo => 10, :bar => "Hello")
    

    will create a mock with two methods, :foo and :bar,defined. :foo will return 10 when invoked, and :bar will return "Hello".

  • The should_receive method will now allow multiple methods (with the same constraints) be defined in a single call. For example, the following declares that both :read and :write need to be called at least one time each on the mock object.
      flexmock.should_receive(:read, :write).at_least.once
    
  • should_recieve now will allow expectation hashes as arguments. This is similar to the list of methods, but allows each defined method to have its own return value.
      flexmock.should_receive(:name => "John", :age => 32)
    
  • In addition to using a block for defining constrains, constraints may now be applied directly to the return value of new_instances. Combined with the expectation hashes supported by should_receive, simple mocking scenarios have become much more succinct. For example:
        flexmock(Person).new_instances.should_receive(:name => "John", :age => 32)
    
  • Improved implementation, allowing for more flexible use and greater consistency between full mock and partial mocks.
  • Version 0.6.0 also includes a fix for an incompatibility with some older versions of RCov. The FlexMock Rakefile now includes a RCov task (and we have 100% code coverage).

What is FlexMock?

FlexMock is a flexible framework for creating mock object for testing. When running unit tests, it is often desirable to use isolate the objects being tested from the "real world" by having them interact with simplified test objects. Sometimes these test objects simply return values when called, other times they verify that certain methods were called with particular arguments in a particular order.

FlexMock makes creating these test objects easy.

Features

  • Easy integration with both Test::Unit and RSpec. Mocks created with the flexmock method are automatically verified at the end of the test or example.
  • A fluent interface that allows mock behavior to be specified very easily.
  • A "record mode" where an existing implementation can record its interaction with a mock for later validation against a new implementation.
  • Easy mocking of individual methods in existing, non-mock objects.
  • The ability to cause classes to instantiate test instances (instead of real instances) for the duration of a test.

Example

Suppose you had a Dog object that wagged a tail when it was happy. Something like this:

  class Dog
    def initialize(a_tail)
      @tail = a_tail
    end
    def happy
      @tail.wag
    end
  end

To test the Dog class without a real Tail object (perhaps because real Tail objects activate servos in some robotic equipment), you can do something like this:

require ‘test/unit’ require ‘flexmock/test_unit‘

  class TestDog < Test::Unit::TestCase
    def test_dog_wags_tail_when_happy
      tail = flexmock("tail")
      tail.should_receive(:wag).once
      dog = Dog.new(tail)
      dog.happy
    end
  end

FlexMock will automatically verify that the mocked tail object received the message wag exactly one time. If it doesn’t, the test will not pass.

See the FlexMock documentation at flexmock.rubyforge.org for details on specifying arguments and return values on mocked methods, as well as a simple technique for mocking tail objects when the Dog class creates the tail objects directly.

Availability

You can make sure you have the latest version with a quick RubyGems command:

  gem install flexmock    (you may need root/admin privileges)

Otherwise, you can get it from the more traditional places:

Download:rubyforge.org/project/showfiles.php?group_id=170

You will find documentation at: flexmock.rubyforge.org.

— Jim Weirich

Extended FlexMock Example Using Google4R   15 Apr 07
[ print link all ]
I recently helped a friend use FlexMock to do some testing on code that was written ot use the Google4R checkout APIs. I thought it might be interesting to share some of the details here. Note that this code uses the recently released FlexMock version 0.6.0.

Google4R is a simple Ruby wrapper around the Google APIs. In this extended example, we will use FlexMock to test software that uses the Google APIs, without every communicating with Google itself.

Purchase.rb

Here is the bit of code that we will be testing…

  require 'google4r/checkout'
  require 'item'

  class Purchase

    def initialize(config)
      @frontend = Frontend.new(config)
      @frontend.tax_table_factory = TaxTableFactory.new
    end

    # Purchase the +quantity+ items identified by +item_id+.  Return the
    # confirmation page URL.
    def purchase(item_id, quantity=1)
      item = Item.find(item_id)
      checkout = @frontend.create_checkout_command
      checkout.cart.create_item do |cart_item|
        cart_item.name = item.name
        cart_item.description = item.description
        cart_item.unit_price = item.unit_price
        cart_item.quantity = quantity
      end
      response = checkout.send_to_google_checkout
      response.redirect_url
    end

  end

FrontEnd is a Google4R class that provides a lot of the front end work for talking to the Google APIs. The config object given to the Purchase initializer is simply a hash of values defining the merchant_id, merchant_key and sandbox flag. To use the real Google checkout APIs, you will need to obtains a merchant id and key from Google. Since we will be mocking the Google interaction, we can use dummy values in our test.

The tax table factory is required by the Google4R software. We provide the following simplified one. Read the Google API documents for more information.

  class TestTaxTableFactory
    def effective_tax_tables_at(time)
      tax_free_table = TaxTable.new(false)
      tax_free_table.name = "default table"
      tax_free_table.create_rule do |rule|
        rule.area = UsCountryArea.new(UsCountryArea::ALL)
        rule.rate = 0.0
      end
      return [tax_free_table]
    end
  end

Item is simply an ActiveRecord class that we are using to hold our purchase item information. It should respond to the name, description and unit_price messages.

Testing Without Using External Resources

Our first test attempt will be to run the purchase method without talking to either the live Google web services, or hitting an actual ActiveRecord database.

Mocking Active Record

The ActiveRecord part is easy to mock. The following will handle it:

  flexmock(Item).should_receive(:find).with(1).and_return(
    flexmock("guitar",
      :name => "Deschutes",
      :description => "Deschutes model Guitar",
      :unit_price => Money.new(2400.00)))

We have mocked out the find method on Item so that whenever we call find with an integer argument of 1, we will return a mock item that will report its name, description and unit_price. This gives us an item for testing without actually reading the database.

Mocking the Google Web Services Call

Next we want to prevent the Google4R API from actually talking to the live web service. Everything that happens in the purchase method is all done locally except for the final call to send_to_google_checkout. All we need to do is mock out that one method.

  flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
    instance.should_receive(:send_to_google_checkout).once.
      and_return(flexmock(:redirect_url => "http://google.response.url"))
  end

When we ask FrontEnd to create a check out command, it returns an instance of Google4R::Checkout::CheckoutCommand. We then use flexmock to specify that when Google4R::Checkout::CheckoutCommand creates a new instance, it should actually return a partial mock of that instance. The block given to the new_instances method allows us to configure the mocked checkout command. We tell it return a response object (yes, another mock) that report our dummy response URL.

The Final Result

Here is the complete unit test:

  def test_buying_a_guitar
    # Setup
    flexmock(Item).should_receive(:find).with(1).and_return(
      flexmock("guitar",
        :name => "Deschutes",
        :description => "Deschutes model Guitar",
        :unit_price => Money.new(2400.00)))

    flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
      instance.should_receive(:send_to_google_checkout).once.
        and_return(flexmock(:redirect_url => "http://google.response.url"))
    end

    # Execute
    p = Purchase.new({
      :merchant_id => 'dummy_id',
      :merchant_key => 'dummy_key',
      :use_sandbox => true })
    url = p.purchase(1)

    # Assert
    assert_equal "http://google.response.url", url
  end

Testing the Details

The above test is fine as far as it goes. It demonstrates how to use mocks to avoid talking to external resources such as databases and web services. But as a unit test, it is sorely lacking in several areas.

All the test really demonstrates is that the send_to_google_checkout method is called. There are no tests to ensure that the right item descriptions and prices are correctly stored in the cart. In fact, if we rewrote the purchase method as follows:

  def purchase(item_id, quantity=1)
    @frontend.create_checkout_command.send_to_google_checkout.redirect_url
  end

it would still pass the unit test we designed, even though the rewrite is obviously an incorrect implementation.

A more complete test is a bit more complicated. Here are the details.

Mocking Active Record

Our incorrect version of purchase never calls the find method of Item. We can easily test for that by adding a once constraint one that mock specification. Since find is a read-only method, we don’t really care if it is called multiple times, as long as it is called at least one time, so we will add an at_least modifier as well.

Finally, we are going to break the guitar mock out into its own declaration. The reason will become obvious in a bit.

  mock_guitar = flexmock("guitar",
    :name => "Deschutes",
    :description => "Deschutes model guitar",
    :unit_price => Money.new(2400.00))

  flexmock(Item).should_receive(:find).with(1).at_least.once.
    and_return(mock_guitar)

Mocking a Cart Item

The next bit is a wee bit complicated, but we will handle it a little bit at a time so that it doesn’t become overwhelming.

There are three main objects in the Google checkout API that we deal with in the next section.: (1) the checkout command object returned by the front end, (2) the cart object returned by the checkout command, and (3) the item passed to the block in the create_item call.

We will tackle them in reverse order, starting with the item objects given to the create_item block. The item must respond to four attribute assignments. This is straightforward to mock, just make sure you include the once constraint so that the assignments are required.

  mock_item = flexmock("item")
  mock_item.should_receive(:name=).with(mock_guitar.name).once
  mock_item.should_receive(:description=).with(mock_guitar.description).once
  mock_item.should_receive(:unit_price=).with(mock_guitar.unit_price).once
  mock_item.should_receive(:quantity=).with(1).once

Notice how we used the mock_guitar object defined earlier to provide values in the with constraint. This way we don’t have to repeat the explicit strings and values we are checking. (Keep it DRY!).

Mocking the Cart

The mock cart object will pass the mock_item to a block when the create_item method is called. We specify that with the following:

  mock_cart = flexmock("cart")
  mock_cart.should_receive(:create_item).with(Proc).once.and_return { |block|
    block.call(mock_item)
  }

FlexMock objects can handle blocks passed to them by treating them as the final object in the calling list. Use Proc in the with constraint to match the block and then invoke the block explicitly via block.call(…) in the and_return specification.

Mocking the Checkout Command

Finally, we tie it all together by mocking the checkout command. As before, we use new_instances to force newly created checkout commands to be stubbed. This time we not only mockout the send_to_google method, but we also mock the cart command to return the carefully crafted mock_cart object from the previous section.

  flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
    instance.should_receive(:cart).with().once.and_return(mock_cart)
    instance.should_receive(:send_to_google_checkout).once.
      and_return(flexmock(:redirect_url => "http://google.response.url"))
  end

The Final Test Method

Here is the complete detailed version of the test method.

  def test_buying_a_guitar_with_details
    # Setup
    mock_guitar = flexmock("guitar",
      :name => "Deschutes",
      :description => "Deschutes model guitar",
      :unit_price => Money.new(2400.00))

    flexmock(Item).should_receive(:find).with(1).at_least.once.
      and_return(mock_guitar)

    mock_item = flexmock("item")
    mock_item.should_receive(:name=).with(mock_guitar.name).once
    mock_item.should_receive(:description=).with(mock_guitar.description).once
    mock_item.should_receive(:unit_price=).with(mock_guitar.unit_price).once
    mock_item.should_receive(:quantity=).with(1).once

    mock_cart = flexmock("cart")
    mock_cart.should_receive(:create_item).with(Proc).once.and_return { |block|
      block.call(mock_item)
    }

    flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
      instance.should_receive(:cart).with().once.and_return(mock_cart)
      instance.should_receive(:send_to_google_checkout).once.
        and_return(flexmock(:redirect_url => "http://google.response.url"))
    end

    # Execute
    p = Purchase.new({
      :merchant_id => 'dummy_id',
      :merchant_key => 'dummy_key',
      :use_sandbox => true })
    url = p.purchase(1)

    # Assert
    assert_equal "http://google.response.url", url
  end

Summary

Testing with mock objects can get complex. We used seven different mock or partial mock objects in testing the interaction of our code with the Google checkout API. Most testing scenarios won’t require that many, but anytime your code touches something external, it might require a mock object for testing.

We should stop and ask ourselves: was it worth it? It seems like an awful lot of work just to test a very simple purchase method. Wouldn’t it just be easier to just use the Google API directly for testing and forget about the mocks?

Perhaps, but using mock objects have several definite advantages:

  • You can run the test at any time without worrying whether Google, the internet, or anything else is up and connected.
  • You can easy test for error conditions using mock objects. For example, does your code correctly handle the case where you get an exception when connecting to google? Mocks can easily create those error conditions that are difficult to achieve with real objects.

    E.g.

       instance.should_receive(:send_to_google_checkout).once.
         and_return { raise Google4R::Checkout::GoogleCheckoutError }
    

Some might point out that in the final test method we are hardly using Google4R software at all, most of the code we interact with are mock objects. Doesn’t that defeat the purpose of testing?

The answer is simple. Always keep in mind what you are testing. The goal of the TestPurchase test case is not the make sure the Google4R code is correct, but that our Purchase class correctly interoperates with it. We do that by carefully stating what methods are called with what arguments and what they return. The test just checks that we are using to external software as we expect it to. We don’t actually care about the Google4R software itself in this test case (presumably we do have tests that cover Google4R, but those are different tests).

In the end, mock objects are a power tool to have in your testing toolbox.

Multicasting in Ruby   30 Nov 06
[ print link all ]

It’s a bit hard to dig up, but I did figure out how to do UDP packet multicasting in Ruby.

Multicasting

I’ve been playing around with some network peer to peer discovery techniques and wanted to try some of them out in Ruby. The trick to self discovery is the ability to send network packets without knowing the address of the receiver. To do this, you either have to broadcast or multicast.

Because broadcasted packets are received by every device on the local network, it is generally considered good manners to not use broadcasted packets.

Multicasting, on the other hand, is only received by hosts that explicitly express an interest in the multicast address. Although abuse of multicasting is still bad manners, it is a better option than broadcasting.

Sending Multicast Packets

The IP addresses 224.0.0.0 through 239.255.255.255 are reserved for multicast messages. Simply sending a UDP messsage to an address in that range is sufficient.

One minor refinement is to set the TTL (Time To Live) option on the socket. My understanding is that this controls how far the packet is propagated. As polite net-citizens, we don’t want our multicast packets travelling too far abroad, so we set the TTL value to 1 (we need the ugly packing stuff because the value is passed directly to the C level setsockopt() function with no interpretation by Ruby).

So the Ruby code to send a multicast message is:

require 'socket'
MULTICAST_ADDR = "225.4.5.6" 
PORT= 5000
begin
  socket = UDPSocket.open
  socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, [1].pack('i'))
  socket.send(ARGV.join(' '), 0, MULTICAST_ADDR, PORT)
ensure
  socket.close 
end

Receiving Multicast Messages

Receiving a multicast message is a bit tricker. Since multicast messages are generally ignored unless someone has explicitly registered an interest in a particular address, there is a bit of setup that needs to be done.

Here’s the code for receiving multicast messages:

require 'socket'
require 'ipaddr'
MULTICAST_ADDR = "225.4.5.6" 
PORT = 5000
ip =  IPAddr.new(MULTICAST_ADDR).hton + IPAddr.new("0.0.0.0").hton
sock = UDPSocket.new
sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ip)
sock.bind(Socket::INADDR_ANY, PORT)
loop do
  msg, info = sock.recvfrom(1024)
  puts "MSG: #{msg} from #{info[2]} (#{info[3]})/#{info[1]} len #{msg.size}" 
end

The tricky part was figuring out the right setsockopt options and values needed to register interest in our multicast address. I had to do a little reading in the Unix man pages on the C level setsockopt() function call. The third option to the C function is a structure that contains two 4-byte IP addresses. The first IP address is the multicast address, and the second IP address is the address of the local host adapter that we wish to use to listen for the multicast. The 0.0.0.0 address means use any of the local network adapters. IPAddr handles parsing the human readable form of the IP address and returns a string of 4 bytes in the order needed by the C level setsockopt() function.

Usage

Save the above code in files named send.rb and rcv.rb. In one console window, type:

ruby rcv.rb

In another console window on the same or different machine (on the same local network), type:

ruby send.rb This is a test.

For more fun, bring up several receive windows and all will receive the messages send by the send script.

I can think of all kinds of fun things to do with this.

Update

I added the Time To Live option on send.

Using Rake as a Library -- Update   31 Aug 06
[ print link all ]

Why Not Find?

Why Not Find?

After posting FindInCode, someone legitimately asks: “Why not just use Find?”

Good question! Find is a Ruby supplied module to recursively search directories. So why not use it instead of Rake. Then your script won’t have a dependency on Rake.

Well, in short:

  1. Rake provides two functions, recursive directory searching and line by line searching (note that egrep is a FileList method provided by Rake, and is not the same as the grep method provide by Ruby arrays). Find only handles the directory search thing.
  2. I almost never use a Ruby installation without Rake installed as well. So the extra dependency is not a concern to me.
  3. Since I use Rake all the time, the syntax is at my fingertips. I don’t use Find often enough to use it without checking RI.
  4. The Find version of the script is several times longer than the Rake version (included below for comparison).

However …

The find version has one advantage over the Rake version in that it does not need to pull in the entire list of files into memory at once. That may be important to you.

Comparisons

Here’s the original version again:

  require 'rake'
  FileList["**/*.rb"].egrep(Regexp.new(ARGV.first))

And here is the Find version:

  require 'find'
  RE = Regexp.new(ARGV[0])
  Find.find('.') do |fn|
    next if fn =~ /(^(\.svn|CVS)$)/
    next unless fn =~ /\.rb$/
    open(fn) do |file|
      lines = 0
      file.each do |line|
        lines += 1
        puts "#{fn}:#{lines}:#{line}" if line =~ RE
      end
    end
  end

Update (31/Aug/06)

James Edward Grey II points out that my find example could use a little makeover. First, the regex that causes .svn directories to be skipped is incorrect, it only skips the actual directory, not the contents of the directory. Removing the ”$” from the match will fix that, but he provides a better solution. Replace that entire line with:

    Find.prune if fn =~ /(^(\.svn|CVS)$)/ 

I totally forgot about prune.

A minor correction is the use of the lines counter. He suggests either using each_with_index (I should have thought of that), or better yet, use file.lineno.

Thanks James!

Using Rake as a Library   26 Aug 06
[ print link all ]

I’ve been exploring using Rake as a Ruby Library.

Find In Code

Lately, I’ve been experimenting with using Rake as a normal library in regular Ruby scripts (rather than in Rakefiles). It turns out there are some very useful things you can do.

For example, to locate a particular piece of code in a not so small Ruby project, I often use the following command line:

    $ find . -name '*.rb' | xargs grep -ni "snippet of code" 

This quickly searches all my Ruby files and displays matching lines with file names and line numbers. However, the line is long to type and won’t work in windows (unless you have cygwin installed).

So I wrote the following Ruby program and saved it in a file called “fic” (Find In Code):

  #!/usr/bin/env ruby
  require 'rake'
  FileList["**/*.rb"].egrep(Regexp.new(ARGV.first))

An Enhanced Version

This proved so useful, that I enhanced the code to allow for searching for files ending in arbitrary extendsions and to handle more matching options on the command line.

Here’s the enhanced version:

  #!/usr/bin/env ruby
  # -*- ruby -*-

  exts = ['.rb']
  if ARGV[0] =~ /^\.[a-zA-Z0-9]+$/
    exts = []
    while ARGV[0] =~ /^\.[a-zA-Z0-9]+$/
      exts << ARGV.shift
    end
  end

  ext = "{" + exts.join(',') + "}" 

  if ARGV.size < 1
    puts "Usage: #{File.basename($0)} [.ext ...] pattern ..." 
    exit 1
  end

  require 'rake'
  FileList["**/*#{ext}"].egrep(Regexp.new(ARGV.join('|')))

And if you are using Emacs …

If you are using Emacs, and you have my “visit source” code loaded, all you need to do is put the cursor on the line you are interested in and press F2.

Oh, you would like to see the elisp code for “visit source”? Let’s see, where did I put that?

  $ cd .elisp/
  $ fic .el jw-visit-source
  ini/ini-cust.el:131:(defun jw-visit-source ()
  ini/ini-zkeys.el:7:(global-set-key [f2] 'jw-visit-source)    

Now, I move the cursor to the first line and press F2, and I find:

  ;;; Source File Visiting =============================================

  (defun jw-current-line ()
    "Return the current line." 
    (let
        ((bol (save-excursion (beginning-of-line)(point)))
         (eol (save-excursion (end-of-line)(point))))
      (buffer-substring bol eol) ))

  (defun jw-extract-file-lines (line)
    "Extract a list of file/line pairs from the given line of text." 
    (let*
        ((unix_fn "[^ \t\n\r\"'([<{]+")
         (dos_fn  "[a-zA-Z]:[^\t\n\r\"'([<{]+")
         (flre (concat "\\(" unix_fn "\\|" dos_fn "\\):\\([0-9]+\\)"))
         (start nil)
         (result nil))
      (while (string-match flre line start)
        (setq start (match-end 0))
        (setq result
              (cons (list 
                     (substring line (match-beginning 1) (match-end 1))
                     (string-to-int (substring line (match-beginning 2) (match-end 2))))
                    result)))
      result))

  (defun jw-select-file-line (candidates)
    "Select a file/line candidate that references an existing file." 
    (cond ((null candidates) nil)
          ((file-readable-p (caar candidates)) (car candidates))
          (t (jw-select-file-line (cdr candidates))) ))

  (defun jw-visit-source ()
    "If the current line contains text like '../src/program.rb:34', visit 
  that file in the other window and position point on that line." 
    (interactive)
    (let* ((line (jw-current-line))
           (candidates (jw-extract-file-lines line))
           (file-line (jw-select-file-line candidates)))
      (cond (file-line
             (find-file-other-window (car file-line))
             (goto-line (cadr file-line)) )
            (t 
             (error "No source location on line.")) )))

Tuning Performance   26 Aug 06
[ print link all ]

Uncle Bob has a couple examples of why performance tuning almost always has surprises in store for you.

Not What You Expect!

Bob Martin (Uncle Bob) relates a couple of stories on performance tuning and why your expectations are nearly always wrong.

The moral of the story … measure, measure, measure whenever you have a performance problem. The root cause is prabably not where you expect it to be.

Announcing the RailsEdge Conference   08 Aug 06
[ print link all ]

Rails Edge

This is exciting. I have been wanting to talk about this for some time. Well, the time has finally come and I’m happy to announce that the Pragmatic Programmers are putting together a series of regional conferences called The Rails Edge.

The conference will be a single track, so all the speakers at the conference will be available for interaction at all the talks, not just during their own presentation. The speaker selection is great (I feel humbled to be a member of the group). We are hoping that it will a dynamic experiance with the speakers interacting, not only with the audience, but with the other members of the staff.

So, if you didn’t make it into the “Lucky 250” club going to RubyConf this year, here is another chance to meet and greet Rubyists in your area.

I hope to see you there.

Update: You can read the official Pragmatic Programmer’s Announcment too.

 

Formatted: 17-May-08 10:22
Feedback: jim@weirichhouse.org