Making Workling::Return::Store predictable

This took me about 4 hours to figure out today, so I figured I might as well blog about it.

I’m using the Workling / Starling combo to handle background processing tasks on Cashboard for various things.

One of those things is the Cashboard / Basecamp project sync, which provides a really nice progress bar while it does it’s thing.

I was encountering unpredictable results when I started refactoring some code from the worker into various models, and stumbled upon this oddity.

>> Workling.return.set('foo', 'bar')
=> "STORED\r\n" 
>> Workling.return.get('foo')
=> "bar" 
>> Workling.return.get('foo')
=> nil

I expected Workling.return.get to be a non-destructive action, but I was dead wrong. It turns out that the default Memcache Workling::Return::Store client acts like a first-in-first-out (FIFO) stack on each key.

Adding these methods allow me to use any Workling::Return::Store as I initially expected. This will add the methods to the default MemoryReturnStore (for testing), and the StarlingReturnStore (for dev/production).

Now I can run the following code with no problem.

>> Workling.return['foo'] = 'bar'
=> "STORED\r\n" 
>> Workling.return['foo']
=> "bar" 
>> Workling.return['foo']
=> "bar" 

Hopefully this helps someone bashing their head against the wall, like I was. Don’t hurt yourself.

seth, Sun, 25 Oct 2009 02:56:00 GMT
no comments

Cashboard sponsors Rails Rumble 09

Each year since its inception we’ve sponsored the Rails Rumble by offering comped Cashboard accounts for each member of the winning teams. This year is no different as we’re providing free DYNAMIC accounts for all winners.

The Rumble is getting ready to kick off this weekend, with over 200 teams competing for top prizes from many different companies.

If you’re not familiar with the Rails Rumble, here’s a primer

The Rails Rumble is a 48 hour web application development competition. As a contestant, your team gets one weekend to design, develop, and deploy the best web property that you can, using the awesome power of Ruby and Rails.

As a judge, you get to review some fun new micro applications and help determine which teams fare best in a number of categories. Along the way, you might discover a new service that’s really useful or fun.

Oh yeah, and there are prizes too. For both contestants and judges. Sounds great, eh?

There have been many excellent entries in the past, some going on to become full fledged commercial services. If I were starting a new app this would be a great way to get some press for it, especially with the attention the event is getting from the business world.

I’m excited to see what this year’s contestants come up with.

seth, Fri, 21 Aug 2009 16:25:00 GMT
no comments

Using concerned_with and autotest for Rails models

Cashboard is growing to be a huge software project, over 11k lines of code, written in Rails.

Some of the models have gotten extremely fat using Jamis Buck’s advice about "skinny controllers, fat models". One obese file in particular was causing me a ton of stress due to it’s massive content, even though I comment liberally and tend to break up sections in the code with large ASCII dividers.

To compound the stress, my unit tests were also getting extremely bloated and hard to read. I couldn’t tell if I’d tested a particular behavior at a glance. Things were becoming extremely hard to manage on both fronts.

I ended up searching out ways to break chunks of logic into separate files and finally settled on using the concerned_with trick for ActiveRecord.

Autotest problems

I’m a firm believer in using autotest while developing. There’s no way I could manage a huge project like Cashboard myself without it. I’ve come to rely on it.

The concerned_with trick is great to break up logic into multiple files for one model, but I ran into some issues with the trick and autotest.

All of my tests for a model were still crammed into one huge file and the unit test didn’t even run when I saved one of my new "concern" files.

Solutions

Through some trial and error, I stumbled across a wonderful way to structure all of my files and hooked up autotest to recognize them all properly. Hopefully it helps someone else out there fighting with the same issues.

Right now I’ve got my project structured like so.


  app/models/
  * account.rb
  * account/
  ** billing.rb
  ** validation.rb
  ** ...
  test/units
  * account_test.rb
  * account/
  ** billing_test.rb
  ** validation_test.rb
  

 

Using this layout, I’d expect the billing_test.rb file to get run by autotest if I saved the billing.rb file. This is quite easily accomplished by adding a .autotest file to the root directory of your Rails projectas described in this article.

The contents of my .autotest file automatically map my concerned_with files to my custom unit tests for model behavior.

seth, Wed, 06 May 2009 19:10:00 GMT
1 comment

Import production SQL to your development box with Capistrano

I got sick of using Navicat to import production data from our servers in order to test it locally on a development box.

Here’s a quick Capistrano task that imports production data from your server to your development box locally.

This is great way to debug problems that are happening on your production server. It gives you the ability to mess with things locally without the risk of fucking up someone’s important data ;)

seth, Tue, 09 Dec 2008 21:48:00 GMT
no comments

Test destruction of your Rails associations

Here’s a handy test helper I wrote this afternoon during some Cashboard testing.

It ensures that all associations are destroyed that you specify. Great for those ":dependent => :destroy" calls.

def assert_associations_destroyed(item, associations=[])
  ids = {}
  associations.each do |assoc|
    ids[assoc] = eval("item.#{assoc}.find(:all, :select => 'id')")
  end
  ids.each do |key, things|
    assert things.size > 0, "#{key} doesn't have more than 1 item."
  end

  item.destroy

  ids.each do |key, things|
    assert_equal 0, key.to_s.classify.constantize.find(
      :all, 
      :conditions => ["id IN (?)", things.collect { |thing| thing.id }]
    ).size
  end
end
seth, Wed, 22 Oct 2008 19:07:00 GMT
no comments

Substruct 1.0.a4

Substruct, the Ruby on Rails e-commerce and content management software just tagged and bagged the last release before we go 1.0.

The version number is seriously misleading as the project has been active for over 2 years. Oh well…

Major changes

  • Update to Rails 2.1.0
  • Lots of bug fixes and updates from our community

Get the good stuff

Enjoy…

seth, Sat, 23 Aug 2008 01:51:00 GMT
no comments

Substruct v1.0a3

Substruct v1.0.a3 was just tagged and bagged.

Lots of bugfixes and a mostly working test harness. Next step, fully working tests :)

Go to the home page to download, or get your fix directly from here.

seth, Wed, 09 Apr 2008 16:24:00 GMT
no comments

Find out what ports your mongrels are using

I like mongrel. I deploy all of our Rails apps using it.

But…I got sick of opening and reading each of my mongrel_cluster.yml config files when adding a new mongrel to the server.

I cooked up this quick script to list all Rails apps on a production box – and their mongrel ports. It assumes all of your apps live in /var/www/(app_name)/current. You might have to do some modifications if you store your Rails apps in a different place.

Maybe you’ll find it useful as well.

list_mongrel_ports.rb

#!/usr/bin/env ruby

#
# Loops through all rails apps and compiles port information
# for mongrels.
#
# Great for figuring out what ports are in use when deploying a new app.
#
# Written by seth @ subimage llc - http://sublog.subimage.com
#
require 'fileutils'

APP_DIR = '/var/www/'

def get_mongrel_config_info(app)
  config_f = File.join(APP_DIR, app, 'current', 'config', 'mongrel_cluster.yml')
  if File.exists?(config_f)
    starting_port, servers = ''
    File.open(config_f, 'r') do |f|
      while line = f.gets
        if line.include?('port:')
          starting_port = line.gsub(/port:|"| /, '')
        elsif line.include?('servers:')
          servers = line.gsub(/servers:| /, '')
        end
      end
    end
    servers ||= 1
    ports = []
    servers.to_i.times do |i|
      ports << starting_port.to_i+i
    end
    return ports.join(', ')
  else
    return nil
  end
end

Dir.open(APP_DIR).each do |app|
  info = get_mongrel_config_info(app)
  puts "#{app}\n  ports: #{info}" unless info.nil?
end

Sample output

-> ./list_mongrel_ports.rb 
cashboard
  ports: 8500, 8501, 8502, 8503, 8504
getcashboard
  ports: 8010, 8011
cbinfo
  ports: 8700
cbforum
  ports: 8705

Hrm…looks like I could do a little reorganization of my ports :)

seth, Thu, 20 Mar 2008 19:46:00 GMT
no comments

Automated testing is the shit

Up until a week ago I never really understood the draw of automated testing.

I come from the school of manual test scripts that you run through before every new release.

Write down things you should do. Walk through the app, click on stuff, make sure it works.

Slow, error prone, and time consuming – but it’s what I knew. That all changed last week when I dusted off my copy of Extreme Programming Installed and burned through the remainder I had failed to read previously.

I finally jumped into unit testing in Ruby on Rails with Mocha and Autotest. I’m steadily catching up on tests for Cashboard and it’s amazing the things you find when you start automated testing.

Mocha

Mocha is a library useful for “mocking” or “stubbing” code that you want to verify executes, but not really run. Useful for network calls, sending emails, and all those other things you want to make sure get called by your test code, but don’t actually run.

Autotest

Autotest is part of the ZenTest suite. You run it from the root of your project and it monitors your code for any changes made.

I keep it running in a terminal next to TextMate and watch it spring into action and run my tests immediately after I save them. Major time saver.

For extra nerd points you can even get Autotest to display notifications with the dude from DOOM, letting you know if your tests passed or failed.

Nice that DOOM guy is now boosting my productivity, instead of draining it in years past. (How many hours did I waste blasting monsters in that thing?)

Security in testing

I’ve already cornered a few long-standing bugs via test code, and I’m on my way to full coverage for all units.

It feels good, and secure. If you’re programming in RoR and aren’t testing today…make the jump. Dig in, learn it. It’s worth the headache and trouble you’ll save in the long run.

Side note

Does anyone else buy massive amounts of books from Amazon, read a few chapters, then let them sit? I’ve got to stop doing that and finish off the ones I purchase.

seth, Tue, 04 Mar 2008 02:57:00 GMT
1 comment

Substruct v0.97

...Just got done prepping a fairly major release of Substruct, the first and only open-source RoR e-commerce platform.

This release updates Substruct to Rails 2 / Engines 2, replaces file_column with attachment_fu, AND is the initial release that will be distributed in archive form. Sorry no PayPal IPN support yet – within the next few days hopefully…

Shouts out to Luke Ludwig for his contribution of the attachment_fu patch.

From now on, all new people to Substruct are recommended to download the latest tar/gzipped archive avaliable here.

New install instructions have been placed on the site…and are much easier than before.

Now the project even includes a rake task that checks if you have the correct gems installed.

I have only tested here on my OS X box, so hopefully things will go smooth on Windows as well.


Version 0.97
---------------------------------------
Added: 
  1. Added nice installer rake task
  2. Upgrade to Rails 2.0.2 / Engines v2
  3. Images and uploads switched from using file_column and rmagick to 
     using attachment_fu and mini_magick.

Upgrade steps:
    1. Make sure you have the mini_magick and mime-types gems installed.
        a. sudo gem install mini_magick
        b. sudo gem install mime-types
            * Note: mime-types gem is only needed to upgrade, but is not
              needed after the upgrade is complete.
    2. Back up your database just in case.
        * mysql example:  mysqldump -u USERNAME -p DATABASE_NAME > backup.sql
    3. script/generate plugin_migration
    4. rake db:migrate
        * All of your uploads and images will be copied to a new location.
          Since it is a copy it is non-destructive, leaving the originals
          in their place.
    5. Check out the new environment.rb file in the 
       vendor/plugins/substruct/config directory.
seth, Fri, 08 Feb 2008 06:10:00 GMT
no comments
Older posts: 1 2