Entries Tagged 'Software Development' ↓

Writing a Simple Rake Task - Part 1

I’ve got a bunch of little tasks that we do at work that we run through cron, and most of them involve our rails application.  We’ve run a lot of them through script/runner, which is a fine tool in its own right, but after doing a little research, I think turning our simple Ruby tasks into rake tasks makes more sense for a few reasons.  First it might be easier to grok for someone coming up to speed on our code if we can just tell them that once a week we run a rake task that sends out a report email to our sales guys, instead of having to explain that we run a Ruby script through script/runner to load our Rails environment, and possibly have to explain what script/runner even is (a lot of candidates we talk to haven’t really explored the Rails skeleton that you get with any new project… makes me kind of sad).  Second, if you forget what it should be, you can easily do a search with rake -T.

So, what I’m going to do here is show you how to write a simple rake task for deleting a plugin (script/plugin doesn’t have an uninstall option, which I find strange).  You could probably just extend script/plugin for this (I know you could… Rails is all about extensibility), but it’s a simple example with an actual useful bit of functionality so it will let you see how things evolve.  I’m a bit wordy, so this is going to be broken into two posts for convenience sake.

First, let’s start with an empty rails project.  So, go to wherever you keep your one-off code, and type:

$ rails rake_test_example
$ cd rake_test_example

Alright, so, I’m assuming some familiarity with Rails, rake, and Ruby here, so if you don’t know what’s going on at this point, you probably should start somewhere else.  If you’re still here, I’m assuming you’re ready for the straight-talk.  So, when writing your own rake task, the first thing you should know is where they go.  In your Rails application, there is a directory called “lib”, and inside of lib is another directory called “tasks”.  This is where you put your custom rake tasks.  Any files you put in here called “some_file_name”.rake will be picked up by the rake executable, and including in the list of available tasks.  How does rake do this?  Glad you asked.   In your Rails install directory, you can find the salient code in “railties/lib/tasks/rails.rb”, and the line that actually does it is:

Dir["#{RAILS_ROOT}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each /
{ |ext| load ext }

See, no magic, just some Ruby code. Anyway, glad you’re curious, but let’s move on.  So, when you’re writing your own plugin, the first thing to remember is that you put it in “lib/tasks/” and the filename needs to have the “.rake” extension.  What goes in a rake file you ask?  Well, let’s continue.  Create a new file “lib/tasks/example.rake” and open it in a text editor.  Type in the following:

task :hello_rake do
puts "Hello, Rake"
end

Save the file and let’s run our rake task now.  At the command line run

$ rake hello_rake
(in .../projects/code/rails/engines_demo)
Hello, Rake

And voila, you now have written your first rake task.  If you now tell rake to list its tasks though, you won’t find your entry in the list.  Searching for it won’t yield you any more luck either.  (Give it a shot here)

$ rake --tasks
$ rake -T hello_rake

No results. All we need to do is add one line to our custom task and we’re good to go.

desc "This is our hello world task"
task :hello_rake do
puts "Hello, Rake"
end

I personally find this a little unintuitive (opposed to putting the desc block inside the task you’re documenting), but if you think of it as more of an annotation it sort of makes sense. Anyway, if we run our command to search for the task, we now have an entry for our custom task

$ rake -T hello_rake
(in .../projects/code/rails/engines_demo)
rake hello_rake  # This is our hello world task

Another thing that you often see in rake tasks is namespacing.  For instance, you see a lot of tasks like db:migrate, db:rollback, spec:models, etc.  Namespacing is a good way of keeping things neat so that you can hierarchically organize similar tasks.  Adding namespacing to our custom tasks is almost as easy as adding our documentation for the task.  Open up example.rake and change the code like so:

namespace :custom do
desc "This is our hello world task"
task :hello_rake do
puts "Hello, Rake"
end
end

After we save that and run our command to search for the task, we now get this:

$ rake -T hello_rake
(in .../projects/code/rails/engines_demo)
rake custom:hello_rake  # This is our hello world task

Ok, so now that we’ve got the basics of rake down, let’s move on to doing something a little bit more useful.  script/plugin is a great tool for grabbing and installing plugins from just about anywhere, but it doesn’t have an obvious way of deleting them (that I’ve found yet.  Someone prove me wrong, please!).  We could just modify script/plugin ourselves, but instead we’re going to write our own custom rake task to remove a plugin.  There’s not much to it, but we’re going to expand it a little bit so that if the plugin directory is under source control through SVN or Git we’ll remove it from our repositories before we delete the actual files, so they don’t hang around.  Starting at the top, we’re going to create a task called “remove and put it in a namespace called “plugins”.  So let’s open an editor, and create a file in lib/tasks called plugin.rake.  Inside plugin.rake we’re going to then add these lines first:

namespace :plugins do
desc "Removes an unwanted plugin"
task :remove do
end
end

After we save, let’s run the rake command to search for our new task to make sure it’s actually there.  As an aside, I’m going to be doing this in very small, iterative steps with a lot of testing in between.  I don’t know how you might test-drive a rake task, but if anyone has any information on it I would really appreciate it.  Since I don’t posses it right now though, baby steps!

$ rake -T plugins:remove
(in .../projects/code/rails/engines_demo)
rake plugins:remove  # Removes an unwanted plugin

Success!  Now, the next thing we’re going to want to do is to make sure the user provides an argument that is the name of the plugin we’re looking for.  To do that, rake adds any key-value pairs passed in on the command line to the ENV array that you get for free.  We’re going to follow Occams Razor here and name our parameter for the plugins name that we want to remove “name”.  This is how it would look on the command-line.

$rake plugins:remove name=our_great_plugin

Let’s open up our plugin.rake file and add a little bit of code to check if it exists, and if not prints out an error to the user.  If it does, let’s confirm what the parameter is:

namespace :plugins do
desc "Removes an unwanted plugin"
task :remove do
unless plugin_name = ENV["name"]
puts “You must provide a plugin name”
else
puts “plugin_name = #{plugin_name}”
end
end
end

We now have two functional scripts to run to test our script (with and without a ‘name=’ parameter).  Let’s verify our results:

$ rake plugins:remove
(in .../projects/code/rails/engines_demo)
You must provide a plugin name

$ rake plugins:remove name=demo_plugin
(in .../projects/code/rails/engines_demo)
plugin_name = demo_plugin

Success!  Tomorrow we’ll continue with this and add the functionality to actually remove the plugin.

Introducing phrack

Meet phrack, my completely unimplemented rack based web framework (I’m hoping to have a basic implementation done by the end of the week).  It is entirely a pet project to learn more about the implementation of a web framework, to better understand the application stack while not getting too attached to any one implementation (Merb and Ruby being the heavy-weights in my day-to-day right now).  I’d like to thank Pratik Naik for his great articles on the subject (Ruby On Rack #1, Ruby On Rack #2).  If you want to start digging in and working with Rack they are a great place to start.

Two other items.  I’ve pushed some updates to my fork of the Screw.Unit Server javascript testing framework.  You can grab the code here.  My changes include the ability to pass spec names to your before/after blocks for debugging purposes (this combined with Blackbirds profile debugger can give you awesome performance metrics on your javascript code), and top-level describe blocks now collapse if all of its contained specs pass (except when running a focused spec, for obvious reasons).  It’s a good first pass, but I’m going to shift around some code to improve performance when I get a chance.  The other item is that I’m officially starting to work on my book (finally), tentatively titled “Test-Driven Development with Javascript”.  This is something that I feel very strongly in as a development practice, and I think is an area that could use some guides to help developers see that a) yes, you can test your javascript and b) that there are several options for doing so.  I’ll be putting updates about the status of the book here as I make progress.  Also, interested parties may be tapped for helping review my early drafts to make it as useful as it possibly can be.

Fret Over the Team, Not the Product

I’ve had a lot of jobs in the past where my manager has admonished me to be thinking about the product all the time, to check it at home to make sure it’s still up and running, and to be constantly vigilant.  Sometimes they hand me a cell-phone or a pager and say that I’m now “on call” and should be available in case anything goes wrong.  I’m a deep sleeper, so this led to me waking up over any noise, no matter how big or how small, terrified the pager was going off in the middle of the night, or getting out of the shower and rushing to the table to make sure I hadn’t been called (happened once… I forgot about the phone until I got to the office, and my boss was pulling his hair out wondering why I hadn’t picked up the phone).

At the time, it seemed like standard operating procedure, but looking back it seems mighty frantic and usually the problem was something small that someone else had done who could have diagnosed it immediately instead of the 30 minutes to an hour that it would take us.  Not to mention the stress and how unhappy it made engineers to be chained to the office more than we already were (btw, compensation for suddenly being “on call” was a pipe dream).

The product I’m working on now has almost full test coverage, and everytime we make a commit to the source code, our Cruise.rb continuous integration environment picks up the change and runs the full test suite, and if everything passes cuts us a tag to deploy from.  Before we commit we run the suite of tests that correspond to the area of the site we work on (our rspec tests are fast, but our Screw.Unit and Selenium test suites take some time to run).  So, we as a team, are expected to have a fully deployable product everytime we check-in code.  We use an automated build tool to do our deploys that kindly rolls things back for us if there is some major configuration problem.   Our builds normally are 10 minute affairs, kicked off from one command, and our usual procedure is to make sure the environment comes up and that we can log in with our test accounts and do a 5-minute spot check.  After that we’re done.  For all intents and purposes we forget about it until the next time we have to a build, and then we spend another 15-20 minutes worrying about it.

So we have a bunch of processes that help us keep our code clean, those mentioned above and others as well that we find to be very effective for our team.  The business people seem to be way too happy in my experience, and don’t stress about the product.  So, why should the engineering team?  And this brings me to the crux of my post.  There are lots of ways that you can ensure that your code and your releases aren’t going to blow up requiring you to come in and fix some errant code that you rushed to get in to the next release.  If you aren’t using them, you probably should be.  You don’t have to use ours, find the ones that work for your team.  Instead of worrying about your product, worry about your team.  Worry about the relationship you have with the other parts of your organization, and how they view you.  This has a natural tendency to make the team want to do the things that will increase the reliability of your product, because that is one of the ways you are directly represented to other parts of your organization.  Worrying about the perception of your team lets you make decisions that focus on the long term payoff, such as continuous integration, automated test suites, etc, etc instead of fixing this one bug and heaving a sigh of relief because everything is O.K. FOR RIGHT NOW.  It also has the natural tendency to make you look at how you are fulfilling your role in the organization and working to improve that.  Quality in the product, and your role within the organization, is just a side effect.

You also won’t be waking up in the middle of the night because the cat knocked over your books, terrified that the site is throwing errors, the office is burning, and all the other nasty things that are happening that managers seem to think come together as the perfect storm that only you can fix.

Proc#arity strangeness

I was fooling around with some examples using blocks this morning before work, and I noticed that the ruby docs for Proc#arity say:

Proc.new {}.arity          #=>  0
Proc.new {||}.arity        #=>  0
Proc.new {|a|}.arity       #=>  1
Proc.new {|a,b|}.arity     #=>  2
Proc.new {|a,b,c|}.arity   #=>  3
Proc.new {|*a|}.arity      #=> -1
Proc.new {|a,*b|}.arity    #=> -2

But the ruby interpreter I’m using (MRI 1.8.6) actually returns these results:

Proc.new {}.arity          #=>  -1
Proc.new {||}.arity        #=>  0
Proc.new {|a|}.arity       #=>  1
Proc.new {|a,b|}.arity     #=>  2
Proc.new {|a,b,c|}.arity   #=>  3
Proc.new {|*a|}.arity      #=> -1
Proc.new {|a,*b|}.arity    #=> -2

Anyone know why the empty block returns -1 for its arity? Is it a known bug, or is it supposed to work like that and the documentation is wrong? In this case I think the documentation makes a lot more sense.

*** This appears to have been fixed in MRI 1.9 ***

Testing Restful Routes from the Console

While working on a problem recently, we realized that it would be extremely helpful to be able to test some of our restful routes via the console. So after a bit of hunting we found an excerpt from The Rails Way that did exactly what we needed it to. A few simple steps and you’ll be ready to go in no time. FYI, the following is tutorial-lite and assumes you already have a grasp of some of the basics of rails routing functionality. Onward…

Let’s create a simple project that we can use for demonstration purposes:

#: rails restful_routes
#: cd restful_routes

If you want to from here, fire up your rails app and make sure everything is kosher.

#: ruby script/server

(You can, of course, navigate from here to http://localhost:3000/ and get the rails start page if everything is good).

Alright, from here we’re going to start adding our RESTful routes to our rails application by editing config/routes.rb

Opening routes.rb let’s navigate down to the block below:

# Sample resource route (maps HTTP verbs to controller actions automatically):
# map.resources :products

Let’s insert the following simple route right underneath the previous code:

map.resources :users

Save the file and let’s fire up the console:

#: ruby script/console

To test our route on the command line, the first thing we need to do is include the ActionController::UrlWriter module:

>> include ActionController::UrlWriter

And from here all of our route helpers (*_path and *_url) are available from the console.

>> users_path
=> “/users”
>> new_user_path
=> “/user/new”
>> edit_user_path(:id => 1)
=> `”/user/1/edit”

Notice here that even though we don’t have a user with an id of 1 (or a User model, controller, or anything user related), Rails will still happily generate a route for us. All of the route generation faculties available from within rails are now available for you to test on the console, which will hopefully save you some of the hair we lost during our debugging session.

RSpec, and when it loads (objects, modules, etc)

Lately I’ve been playing around with RSpec, which is a fantastic testing tool that falls under the umbrella of what’s being called “Behavior Driven Development” (BDD).  What BDD advocates is that there should be a common language used to talk about your system, and that describing the behaviors of the system ensures that you are delivering what’s really valuable, instead of writing lots of code that may not be necessary.  If you are interested in learning more about BDD, I suggest you head over here and start reading, as it seems to be the resource to go to for more information. Anyway, I’m not here to talk about some quirks with RSpec in the hope that someone will stumble over here and provide some insight. Let’s start with some code and work from there towards my conclusions:

Here is a simple class representing a digital photo that you might store on a server, your computer, wherever. This is as simplified as I want it to be for the purposes of illustrating my points.

#!/usr/bin/ruby

class Photo
  attr_reader :height, :width

  def initialize( width, height )
    @width = width
    @height = height
  end

  def scale_absolute( width, height )
    @width = width
    @height = height
  end

  def scale_percentage( width, height )
    @width *= ( width / 100.0 )
    @height *= ( height / 100.0 )
  end
end

Our second code file is just a module of helper methods that I’ve creatively titled HelperMethods

module HelperMethods
  def nice_photo_resolution( photo )
  if photo.respond_to?( :width ) && photo.respond_to?( :height )
  return "#{photo.width}x#{photo.height}"
  end
  end
end

And here is our rspec code for testing the Photo class. We just simply use the describe method to create an ExampleGroup instance, setup some basic values, have a before method to instantiate an instance of a Photo, and then our it methods which create Example instances to be run.

#!/usr/bin/ruby

require 'photo'
require 'helper_methods'

describe Photo do
  include HelperMethods

START_HEIGHT = rand(640)
  START_WIDTH = rand(480)

before( :each ) do
  @photo = Photo.new( START_WIDTH, START_HEIGHT )
  end

it "should resize a photo from #{START_WIDTH}x#{START_HEIGHT}to 640x480" do
  @photo.scale_absolute( 640, 480 )
  @photo.width.should == 640
  @photo.height.should == 480
  end

it "should scale a photo 200% from #{START_WIDTH}x#{START_HEIGHT} to #{START_WIDTH * 2}x#{START_HEIGHT * 2}" do
  @photo.scale_percentage( 200, 200 )
  @photo.width.should == ( START_WIDTH * 2 )
  @photo.height.should == ( START_HEIGHT * 2 )
  end

it "should scale a photo 50% from #{START_WIDTH}x#{START_HEIGHT} to #{( START_WIDTH * 0.50 )}x#{( START_HEIGHT * 0.50)}" do
  @photo.scale_percentage( 50, 50 )
  @photo.width.should == ( START_WIDTH * 0.50 )
  @photo.height.should == ( START_HEIGHT * 0.50 )
  end

it "should rotate a photo 90 degrees such that the dimensions change from #{START_WIDTH}x#{START_HEIGHT} to #{START_HEIGHT}x#{START_WIDTH}" do
  @photo.rotate( 90 )
  @photo.width.should == START_HEIGHT
  @photo.height.should == START_WIDTH
  end
end

Now, as I’ve been playing around, I’ve noticed several things that have stuck in my craw. The first is that even though I’m including HelperMethods in the describe block, if I try to use it in the description argument passed to the it method, I get an error trying to run my spec test telling me that no such method exists. Take a look here to see what I mean:

describe Photo do
  include HelperMethods

START_HEIGHT = rand(640)
  START_WIDTH = rand(480)

before( :each ) do
  @photo = Photo.new( START_WIDTH, START_HEIGHT )
  end

it "should resize a photo from #{nice_photo_resolution( @photo )}to 640x480" do
  @photo.scale_absolute( 640, 480 )
  @photo.width.should == 640
  @photo.height.should == 480
  end

...
end

When I try to run the spec command on that ExampleGroup, I get an error saying the nice_photo_resolution method cannot be found. This leads me to believe RSpec is doing something with the it method before it builds the ExampleGroup instance from the describe method. Further supporting this theory is that when you try to instantiate instance level variables in a before method, they cannot be seen and are evaluated as nil in the code as its executed. Take a look here for an example:

describe Photo do
  include HelperMethods

before( :each ) do
  @start_width = rand( 640 )
  @start_height = rand( 480 )

@photo = Photo.new( @start_width, @start_height )
  end

it "should resize a photo from #{@start_width}x#{@start_height}to 640x480" do
  @photo.scale_absolute( 640, 480 )
  @photo.width.should == 640
  @photo.height.should == 480
  end
...
end

That example is a little contrived, but I think it gets the point across. Moving to a more realistic example, this produces the same error:

describe Photo do
  include HelperMethods

before( :each ) do
  @photo = Photo.new( rand( 640 ), rand( 480 ) )
  end

it "should resize a photo from #{@photo.width}x#{@photo.height}to 640x480" do
  @photo.scale_absolute( 640, 480 )
  @photo.width.should == 640
  @photo.height.should == 480
  end
...
end

This will cause the spec command to throw an error, because @photo is evaluating to nil, and trying to access the width or height instance fields of the object cause an error to be thrown.

I’m not entirely sure that my imperfect understanding of Ruby class instantiation is at fault for my confusion, but it doesn’t seem like it should be the case. I also haven’t cracked open the RSpec code yet, as I am just trying to learn the system now and don’t want to have to dive into the gory details until I understand how to use it to get work done. If anyone can point me in the right direction though I would truly appreciate it. These examples are fairly unrealistic, but I think it’s a short jump to seeing how not understanding the order of construction could lead to some problems when working on production-level code. I’d rather be testing my code than testing the RSpec system.