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.

#

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.

#

Git status in your prompt

Posted on 01/17/2009

I've been using Git for almost a year now, but I didn't really start using Git until recently when I began working for Intridea.  Once I started using Git on a daily basis, dealing with multiple projects, and multiple branches per project, I would occasionally make the mistake of changing code on the wrong branch.  While annoying, it was easily fixed by stashing the changes and applying the stash to the proper branch.

As much as I love git stash, this began to get old, and constantly hitting up 'git status' to check the current branch wasn't cutting it.  After a bit of googling, I found this.  It describes how to add the current branch name and its clean/dirty status to you terminal prompt.

Just add this to your .bash_profile: 

function parse_git_dirty {
      [[ $(git status 2> /dev/null | tail -n1) != "nothing to commit (working directory clean)" ]] && echo "*"
    }
    function parse_git_branch {
      git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/[\1$(parse_git_dirty)]/"
    }
    
    export PS1='\u:\[\033[31;40m\]\w\[\033[0;33m\]$(parse_git_branch)\[\e[0m\]$ ' 
    

And you should end up with something that looks like this:

My terminal prompt

As you can see, I like to use a bit of color to help things stand out.

So far this has been immensely helpful.  With this info at a glance, I always know where I am and how I last left things.

#

gem install mysql FAIL

Posted on 11/09/2008

This is nothing new or monumental, but I thought I'd post it anyway.  I recently picked up a new Macbook Pro, and while setting it up for Rails development, I ran into a little issue while installing the mysql gem.  I'm sure this little tid-bit is probably posted a million other places on the intarwebs, but one more can't hurt.

So here's the problem.  You attempt to run a rake db task, and you get this...

!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.
rake aborted!
no such file to load -- mysql

So you do as it says and attempt to install the gem...

Building native extensions.  This could take a while...
ERROR:  Error installing mysql:
    ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb install mysql
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... no
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
    --with-mysql-config
    --without-mysql-config
    --with-mysql-dir
    --without-mysql-dir
    --with-mysql-include
    --without-mysql-include=${mysql-dir}/include
    --with-mysql-lib
    --without-mysql-lib=${mysql-dir}/lib
    --with-mysqlclientlib
    --without-mysqlclientlib
    --with-mlib
    --without-mlib
    --with-mysqlclientlib
    --without-mysqlclientlib
    --with-zlib
    --without-zlib
    --with-mysqlclientlib
    --without-mysqlclientlib
    --with-socketlib
    --without-socketlib
    --with-mysqlclientlib
    --without-mysqlclientlib
    --with-nsllib
    --without-nsllib
    --with-mysqlclientlib
    --without-mysqlclientlib


Gem files will remain installed in /Library/Ruby/Gems/1.8/gems/mysql-2.7 for inspection.
Results logged to /Library/Ruby/Gems/1.8/gems/mysql-2.7/gem_make.out

Well shit.  That didn't work.  So here's what you need to do...

sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config

which should yield...

Building native extensions.  This could take a while...
Successfully installed mysql-2.7
1 gem installed

Yay.