Photo-1 Photo-2 Photo-3 Photo-4 Photo-5 Photo-6

@brentmc79

@brentmc79

Full-time web developer. Part-time smart ass.

I'm Brent Collier.

After a year and a half as an engineer on Twitter's Trust & Safety team, I'm looking for my next gig. Contact me if you know of something interesting.

#

form_for funkiness with ActiveResource

Posted on 03/17/2009

The combination of a running nose, scratchy throat, and this ActiveResource bug made for a lovely afternoon.

The form_for method in ActionView::Helpers::FormHelper, when given an ActiveRecord object as an argument, uses the new_record? method to determine whether the form action should be POST or PUT.  Well, today I was building a controller to consume a restful web service, so I was using ActiveResource instead of ActiveRecord.

Everything was hunky-dory until I began doing a little testing.

Whenever I would submit my 'new' form, it would blow up on a before_filter method.  The odd part was that the before_filter method was declared as such...

before_filter :do_stuff, :except => [:new, :create]

WTF? It should not have been calling the do_stuff method before the 'create' action.  Well, as it turns out, it wasn't.  A little debugging helped me realize that it was actually hitting the 'update' action.  Again, WTF?

I checked the form_for method call.  I checked the generated html.  I checked the routes file.  Everything looked kosher.  I was stumped.

When all else fails, google that shit.

I did, and what did I find?  This Lighthouse ticket describing how form_for no worky with ActiveResource in Rails 2.2.1 (and 2.2.2 evidently since that what I was running).  So what's the solution?

alias :new_record? :new?

Add that to your resource model.  ActiveResource defines a new? method which works exactly like ActiveRecord's new_record? method. 

I had actually checked the new_record? method when I was debugging the issue, but for some reason no bells and whistles went off when @thing.new_record? returned nil.  WTF Brent?  I blame it on the medication.

#

Temporarily disable activerecord callbacks

Posted on 03/12/2009

ActiveRecord callbacks can be super-handy, but every once in a while, they get in the way.

I was recently working on an Intridea client project and I had to create a rake task to import a large set of data from a spreadsheet.  One of the models that was being imported had an after_save callback that sent out an email notification.  I didn't really want 3500 emails to be sent out whenever this rake task was ran, so I needed to disable the callback while the import task was running.

A former coworker, Blake, from NT showed me this trick...

Say you've got a model like so...

class Thing < ActiveRecord::Base
      before_save :do_stuff
    
      def do_stuff
        raise "This thing is doing stuff..."
      end
    end
    

In the config/initializers directory, I created a file called extensions.rb.  You can call it whatever you like.  I chose 'extensions' because I use the same file for any minor Ruby or Rails class extensions.  In it, I put this...

class ActiveRecord::Base
      def self.without_callback(callback, &block)
        method = self.send(:instance_method, callback)
        self.send(:remove_method, callback)
        self.send(:define_method, callback) {true}
        yield
        self.send(:remove_method, callback)
        self.send(:define_method, callback, method)
      end
    end
    

What this does is it grabs the callback and stores it in the method variable.  Remember, this code only circumvents the 'do_stuff' method, not anything declared as a before_save callback.  It then redefines the method to merely return true, yields to the block, and then redifines the the method with the contents of the method variable. 

Yielding to the block allows you to not have to worry about maintaining the method contents, or restoring it once you're done.  Also, if you were so inclined, you could easily modify this code to accept an array of method names and disable all of them

Once the extension is in place, you can do this...

Thing.without_callback(:do_stuff) do
      thing = Thing.new
      thing.save
    end
    

...without 'do_stuff' ever being called.

Some people will probably argue that if you need to disable your callback, then your model should be defined differently, but I say that's bullshit.  I think this is perfectly acceptable in many cases.  Granted you probably wouldn't want to do this all throughout your application code, but I see no problem with using this technique in a test, or data migration, or in my case, a rake task.

[UPDATE]

WOOT! Now there's a plugin. Check it out... without_callbacks

#

Dead simple task scheduling in Rails

Posted on 02/13/2009

So I was working on an Intridea project where I needed to send out a daily digest email.  The emails needed to be sent out on a scheduled interval, not as the result of some user action.  This meant that it would need to be handled outside of the normal request/response cycle.

Typically in the Rails world, when someone mentions long-running, or background tasks, you think either BackgrounDrb or Starling/Workling.  Those tools are fine and all -- actually, don't get me started on BackgrounDrb -- but I wanted something a bit more simple.

I didn't need queueing, or jobs, or anything like that.  I had only one task to manage.  I just want something that will say, "Once a day, do [task]", and that's all.

Also, I really didn't want to have to monkey with deployment tasks, be it with Vlad or Capistrano or whatever.  I wanted to just deploy as usual, and have it all just work, no extra processes to start or tasks to run.

This seemed like a tall order, but I began my search anyway.  Google led me to a number of pages about a number of tools, all of which included one or more of the things I was trying to avoid.  Of all places, I finally ended up on this page from the Ruby On Rails wiki.

Despite having only a one line mention near the bottom of the page, I decided to check out rufus-scheduler, and it turned out to be exactly what I was looking for.  There was no database table, queueing mechanism, or separate process to manage.  Just a simple scheduler to call out to your existing ruby code.

It couldn't get any simpler.  Here's how I set it up...

First, install the gem.

sudo gem install rufus-scheduler

I also froze it into vendor/gems by declaring the gem in the initializer block of environment.rb and then running rake gems:unpack.

Then I created a file called task_scheduler.rb in my config/initializers directory.   Inside this file is where the magic happens.  This is where it all goes down.  Are you ready?  Here it is...

scheduler = Rufus::Scheduler.start_new
    
    scheduler.every("1m") do
      DailyDigest.send_digest!
    end
    

Yeah, that's it. Seriously.

I was in disbelief myself until I started up the server and watched it send me an email.  In it's current state, it would send the digest every minute, which is not what I wanted.  Fortunately, the rufus-scheduler provides several ways to schedule your tasks.  Once I was ready, I changed it to more of a cron-style scheduler.

scheduler = Rufus::Scheduler.start_new
    
    # Send the digest every day at noon
    scheduler.cron("0 12 * * *") do
      DailyDigest.new.send_digest!
    end
    

You could also use scheduler.in or scheduler.at for one time tasks set to run at some point in the future.

I know a guy who's from "the future", but that's another story.

Anyway, there you have it.  Dead simple task scheduling in Rails.  It really doesn't get any easier than that.

#

uninitialized constant User::Authentication

Posted on 01/22/2009

I've hosted acts-as-blogr on Slicehost for a while now, but I recently signed up for shared hosting through Dreamhost.  They had a promotion of 2 years of hosting for $22 which I couldn't pass up now that they support Passenger for Rails apps.  Anyway, I finally got around to deploying an app with Passenger and I had a little problem.

Upon doing the initial 'cap deploy:migrations', it failed with this error

  * executing "cd /home/brentmc79/brentmc79.com/releases/20090122070240; rake RAILS_ENV=production  db:migrate"
        servers: ["brentmc79.com"]
        [brentmc79.com] executing command
     ** [out :: brentmc79.com] (in /home/brentmc79/brentmc79.com/releases/20090122070240)
     ** [out :: brentmc79.com] rake aborted!
     ** [out :: brentmc79.com] uninitialized constant User::Authentication
     ** [out :: brentmc79.com] 
     ** [out :: brentmc79.com] (See full trace by running task with --trace)
        command finished
    failed: "sh -c \"cd /home/brentmc79/brentmc79.com/releases/20090122070240; rake RAILS_ENV=production  db:migrate\"" on brentmc79.com
    

This was clearly something to do with restful_authentication since it was complaining about User::Authentication.  I did a bit of googling, but I didn't come up with much.  Somebody mentioned renaming the plugin to not have a hyphen, but that just seemed silly.  Someone else mentioned some session/cookie issue, but I hadn't even made it to the browser.  It was failing on db:migrate.  Finally, someone mentioned that they had forgotten to include the plugin in their repo.  This got me to thinkin...

I knew that I had included the plugin, but I decided to take a look at the Github repo in the browser.  Here's what I saw:

 

The restful_authentication plugin was actually a cloned repo sitting inside my working copy.  I'm sure there's probably some clever way to enable a repo inside of a repo, but it was 2am and I just wanted it to work.  So I just rm'd the restful_authentication plugin from the vendor directory, download the tarball from Github, and extracted it back into vendor/plugins.  Using the tarball instead of the clone gives you all the code without all the git info.

I committed/pushed, then ran 'cap deploy:migrations' again and the database migrated with no uninitialized constant error.   Viola!  Unfortunately, then I just moved on to the next issue...

After a couple more tweaks and deploys, I finally had it up and running.