Archive for October, 2007

alias_method_chain weirdness

Monday, October 22nd, 2007

Short story: When you’ve got a module doing this:

module Taglist
  def self.included(base)
    base.class_eval do
      [:save, :save!].each do |method|
        alias_method_chain method, :taglist
      end
    end
  end
...

Ensure that you protect against including it twice, like so:

class BuildJob < ActiveRecord::Base
  include Taglist unless self.include?(Taglist)
...

Why? Because otherwise you'll waste hours trying to understand why your code works fine in development but starts failing with StackLevelTooDeep errors when you run rake tests.

Bleh.

Pan-Seared Mahi-Mahi with Oranges and Olives & Wilted Spinach with Roasted Garlic

Sunday, October 21st, 2007

From Bon Appetit, November 2007, p. 72

NB: Roast the garlic first.

Wine: Pinot Gris, 2006 Chateau Ste. Michelle

Mahi-Mahi (serves 6):

6 6-ounce mahi-mahi fillets (about 1″ thick)
2 tbsp olive oil
2 tsp finely-grated orange peel
.75 cup fresh orange juice

2 tbsp (.25 stick) butter
1 large shallot, chopped (~ .33 cup)
.5 tsp crumbled saffron threads
.75 cup green olives, pitted, halved
3 oranges, peeled, cut into segments

.33 cup thinly sliced fresh basil
1 tbsp chopped fresh chives

Preheat oven to 400F. Place fish in shallow bowl or glass baking dish. Drizzle with 1 tbsp oil, sprinkle with orange peel, and pour juice over; turn to coat.

Melt butter with 1 tbsp oil in large nonstick ovenproof skillet over high heat. Remove fish from marinade, reserving marinade; sprinkle with salt and pepper. Cook fish until light brown, about 3 minutes. Turn fish over; add shallot. Cook 1 minute. Stir saffron into reserved marinade; pour marinade over fish. Add olives and half of orange segments. Transfer skillet to oven; roast fish until cooked through, about 6 minutes.

Transfer fish to platter; top with remaining orange segments. Spoon sauce with oranges and olives around fish. Sprinkle with basil and chives and serve.

Spinach (serves 6):

12 garlic cloves, peeled
4 tbsp olive oil

3 6oz bags fresh spinach

Preheat oven to 400F. Place garlic on foil; drizzle with 1 tbsp oil. Wrap garlic in foil; roast until soft, about 20 minutes.

Heat remaining 3 tbsp oil in large pot over medium-high heat. Add spinach and garlic with oil from foil packet. Saute until spinach is wilted, about 2 minutes. Season with salt and pepper. Transfer spinach to bowl and serve.

Notes:

Wow! This was definitely worth the work. The smell alone is enough to justify all the chopping. I think I overcooked the fish just a bit… Next time, a bit less searing before the oven. Still, though, the wild mixture of flavors and smells was incredible.

The wine was quite nice. A little tart, which complemented the orange very well. Also, just a bit fizzy, which was somewhat unexpected for a Pinot Gris.

video games == sweat?!?

Sunday, October 21st, 2007

I am reasonably certain that I’ve never before worked up a sweat while playing a video game. Not even in the hairiest parts of DOOM.

Today, I discovered Wii Sports Boxing. I was not prepared for the level of fun inherent in playing this game. Even my daughter got into it… Which isn’t quite so interesting until you know that she was wearing a Di$ney Princess costume while throwing jabs and dodging gloves.

Imagine, if you will, a 4yo girl wearing a Belle (from Beauty & The Beast) costume and throwing a perfect left-left-right-cross combo that knocks her opponent down.

Awesome.

Moroccan Lamb with Garbanzo Bean Mash & Herb-Roasted Eggplant with Tomatoes and Feta

Saturday, October 20th, 2007

From Bon Appetit, November 2007, pp. 70, 73-34

NB: Make the eggplant first, then reheat it to serve with the lamb.

Wine: Shiraz, 2004 “2 up” (South Australia)

Moroccan Lamb (serves 6):

1 1.5lb butterflied boneless leg of lamb, trimmed
2 tbsp good olive oil
.5 cup dry red wine
6 tbsp fresh orange juice
1 tbsp grated peeled fresh ginger
2 tsp finely grated orange peel
2 tsp ground cumin
1 tsp ground coriander
1 tsp ground cinnamon
4 tbps chopped fresh cilantro

2 cups chopped red onions
6 garlic cloves, chopped

2 15.5oz cans garbanzo beans (chickpeas), rinsed, drained
1 cup chicken broth

Place lamb in shallow bowl or glass baking dish; coat with 1 tbsp oil. Add wine, orange juice, ginger, and orange peel to bowl. Mix to coat. Mix cumin, coriander, and cinnamon in small bowl. Sprinkle spice mixture over lamb, turning to coat evenly. Sprinkle with 2 tbsp cilantro. Cover and marinate at room temperature 1 hour or chill up to 4 hours.

Preheat oven to 400F. Heat 1 tbsp oil in large ovenproof skillet over medium-high heat. Remove lamb from marinade, reserving marinade. Add lamb to skillet; sprinkle with salt and pepper. Cook until light brown, about 3 minutes per side. Push lamb to 1 side of skillet. Add onions and garlic to skillet; saute until onions are light brown, about 3 minutes. Pour reserved marinade over lamb. Transfer to oven; roast until instant-read thermometer inserted into center registers 130F for medium-rare, about 15 minutes. Remove lamb from pan, reserving onions and garlic in skillet. Let lamb rest 5 minutes. Slice lamb, transfer to platter, and tent with foil to keep warm.

Place garbanzo beans and broth in medium saucepan; simmer over medium heat 10 minutes. Using slotted spoon, add onions and garlic from skillet to garbanzo bean mixture. Stir; simmer 5 minutes. Remove from heat; add 2 tbsp cilantro. Using potato masher, mash to coarse puree. Season with salt and pepper.

Place scoop of garbanzo bean mash on each plate. Top with lamb slices. Drizzle with juices from skillet and serve.

Eggplant (serves 6):

1 1.75lb eggplant, cut into 1-inch cubes
4 large plum tomatoes, cored, quartered lengthwise
3 tbsp olive oil
2 tbsp Sherry wine vinegar
2 tbsp plus 2 tsp chopped fresh oregano
.5 cup crumbled feta cheese

Preheat oven to 450F. Place eggplant and tomatoes on rimmed baking sheet; toss with oil and vinegar. Sprinkle with 2 tbsp oregano, salt, and pepper. Roast until eggplant is tender and golden brown, stirring occasionally, about 40 minutes. Transfer eggplant and tomatoes to platter. Sprinkle with feta and 2 tsp oregano and serve.

Notes:

This was a nice dish.  The eggplant was quite tasty.  I love the combination of eggplant and oregano.  Fresh oregano is an experience…  That dried stuff in your spice rack?  A pale imitation.  Really.  Try some of the fresh stuff.

The lamb was tasty, but I didn’t get the timing right and it wound up being a bit cool.  I’m still learning how to coordinate the various parts of the meal so that they’re all ready (or at least all warm) at the same time.

D said that it was the best lamb she’d ever had, and that she liked it better than beef.  I dunno about better than beef (nothing like a good T-bone), but it was very flavorful.

Using mocks in Rails for fun and profit

Wednesday, October 17th, 2007

Well, maybe fun, anyhow. Possibly profit.

One of the reasons I had put off doing any real testing of this Rails project was that I didn’t feel like dealing with figuring out how to avoid messing with the SLURM stuff when doing testing. I really don’t want to kick off fourty real build jobs when I run my tests, but I really should be testing all the goop that leads up to and follows after the interaction with SLURM.

The answer is so simple that I didn’t believe it at first. All I needed was to drop a file in test/mocks/test with the same name as my SLURM library (happens to be slurm.rb). In this file, I simply require the original library, and then stub out the run_slurm_command subroutine. Et voila! I can now safely pretend to interact with the SLURM system without actually doing anything to it.

Incidentally, this works just fine for modules. In other words, it will work regardless of what you are mocking. I make this note because after reading the documentation I had the impression that it would only work for mock objects. My SLURM library is just a collection of subroutines, so I was thinking that I needed to make it a class. In fact, the Mock infrastructure is just Ruby’s built-in metaprogramming — I can open up an existing module or class, monkey around with its innards, and then send it on its way. Sweet!

Unit testing in rails

Tuesday, October 16th, 2007

OK, OK… I’ll admit it. My first real Rails app, and I ignored testing.

sloccount tells me that I have 1,536 LOC in my app/ subdir, and until today, I had no working unit tests. *hangs head in shame*

Yesterday I was working on allowing my app to receive cvs-commits emails, so that I can kick off builds and tests for monitored CVS branches. I realized after a while that what I was doing in the Rails console was essentially re-typing unit tests, over and over. Even worse, I was then throwing them away when I was finished! Having never really worked with the Rails testing framework, I had basically ignored it because I felt I didn’t have time to do testing.

Fortunately, I haven’t been bitten too badly by my earlier decision, but after yesterday and today I’ve realized that once you get past a few hundred LOC, you just can’t make changes and trust that you’ve remembered what those changes will affect.

Now that my project has come out of skunkworks mode, I need to make sure that it’s always production-worthy. I’ve got executive buy-in now, so I’ll take the time to ensure that obvious bugs don’t creep in while I’m hacking. In the future, I’ll try to be a bit more test-driven, since it’s basically how I work anyhow. I’ve just been doing it all by hand.


Started
..........................................................
Finished in 0.907775 seconds.

58 tests, 91 assertions, 0 failures, 0 errors

It’s a good start. Now I’ve got to work on functional and integration testing.

Rails Observers

Friday, October 12th, 2007

I’d like to say a bit about how cool ActiveRecord Observers are.

I am writing a highly event-driven app in Rails (e.g. create a BuildJob, and kick off a SLURM job for said job). Now, the simplistic way to handle this might be to write a daemon that sits in a loop and watches for new BuildJob records. In fact, that’s essentially how the old implementation worked (I didn’t write it!).

As I was writing the application (and learning Rails at the same time), I discovered ActiveRecord::Observer. I was thunderstruck. Folks who have been programming “for real” for a while may laugh here, but this was the first time I’d seen a good use of the Observer pattern that I’d learned about way back when. I’ve been a sysadmin for most of my career, though, so cut me some slack.

Anyhow, instead of a clunky poll-based design, I now have a nice, clean, and logical design that tries to create a SLURM job whenever a new BuildJob is created. If the SLURM job creation fails, so does the BuildJob creation, things are rolled back in the DB, and all is safe.

That’s the kind of thing that makes programming really fun and gratifying. I love finding elegant solutions that solve multiple problems at the same time.

Oh, you want to see some code? Ok.

class BuildJobObserver < ActiveRecord::Observer
  include Slurm
  include LTS

  # We want to try to create a SLURM job before saving the BuildJob
  # record.  This way, we know that a BuildJob always has an associated
  # SLURM job.  The problem is that we don't yet have an id for the BuildJob
  # when we try to create the SLURM job, and so we can't tell the SLURM
  # job which BuildJob it's associated with.  The solution is to create
  # the SLURM job, but don't let it run until it's updated with the
  # BuildJob's id.
  #
  # before_create
  #   * create slurm job with status "held" (use SchedulerType=sched/wiki)
  #   * save slurm job id in lbats job
  # after_create
  #   * update slurm job name with lbats id for future linkage
  #   * update slurm job with priority > 0 to enable it

  def before_create(job)
    batch_options = {
      :command   => lbats_job2lts_build_cmd(job),
      :name      => "lbats_need_id",
      :features  => job.architecture.name,
      :partition => "#{job.architecture.name}_build",
    }

    job.logger.debug "command: #{batch_options[:command]}"

    if slurm_id = slurm_submit_batch_job(batch_options)
      job.resource_manager_id = slurm_id
      job.logger.info "Created new SLURM job: #{slurm_id}"
    else
      # So here's a problem, maybe...  If we submit 5/6 build jobs, and
      # the 6th fails, what about the 5 SLURM jobs that were submitted OK?
      # If we bomb out here, the BuildJobs won't be created in the DB, which
      # is good, but we've still got 5 SLURM jobs that are unassociated.
      # Not sure there's much we can do here but cross our fingers.  :/
      raise "Unable to create SLURM job"
    end
  end

  def after_create(job)
    slurm_update_job_attributes(job.resource_manager_id,
                                {:priority => job.build_request.priority.value,
                                 :name => "lbats_build_#{job.id}"})
  end

  def before_update(new_job)
    old_job = BuildJob.find(new_job)

    # check for a state change
    if old_job.job_state != new_job.job_state
      # tell the request about the state change
      new_job.build_request.register_job_state_change(new_job)
      # TODO: sanity-checking of all state transitions?
      case new_job.job_state
        when JobState.Canceled
          # NB: If the job is canceled outside of LBATS (i.e. someone
          # runs scancel), we'll be doing a double-cancel because
          # the jobcomp script will post an update to the LBATS job
          # and bring us here.  I think this is OK, because trying
          # to scancel an already-canceled job is not fatal.
          slurm_cancel_job(new_job.resource_manager_id)
      end
    end
  end

  def after_save(job)
    # tickle the build request's updated_at
    job.build_request.save
  end

  def before_destroy(job)
    # make sure we nuke the job from SLURM, too!
    slurm_cancel_job(job.resource_manager_id)
  end
end

speaking of metal…

Friday, October 12th, 2007

My copy of The Essential Iron Maiden came yesterday. I don’t know how I missed these guys all those years. After playing “The Trooper” a few times in Guitar Hero II, I knew I wanted to hear more.

“Paschendale” is just incredible. A clear counter to the trend of metal bands getting lame as they get older *cough*Metallica*cough*. We’ll see how the rest of the set sounds.

Now I know why those youth pastors always put Iron Maiden at the top of the list as an example of the dangers of heavy metal… They’re just too fucking awesome.

she bowled a 120 on her first game

Thursday, October 11th, 2007

My daughter, who turned 4 in July, just bowled a 120 in her first game of Wii Sports Bowling. That includes two strikes.

I think I got an 85 on my first game.