Memetape 2

Push through the afternoon, folks. Five o’clock is just around the bend.

Kauffman Stadium, day 2

Dashing off another quick batch of pictures from the Good Guys disappointing loss at The K.

As expected, Francisco Liriano’s return was a bit rocky.

The King is back.

Hot chocolate and baseball, two great tastes…well, during playoff baseball.

Hot Cocoa

Hard to tell, but the sun did come out for moments today. One particularly fun moment was when the sun came out for 10 seconds right as the Royals shortstop, Tony Pena, Jr., looked up to catch a popup. Well, he didn’t catch it after that flash of sun.

This picture is a field view from just after that play.

Sun!

Jose Guillen topped that error in the first inning by plain dropping a fly ball in the outfield. The Royals fans hate Guillen, by the way. Probably because he’s not hitting right now. And they say only the east coast fans drift from day-to-day.

I was disappointed in the quality of fans at KC. Everywhere we sat involved scream-at-the-top-of-their-lungs loudmouths behind us yelling gems like “hit the ball, Tehan, the object of the game is to hit the ball!”, “Yeah, sit down! That’s it, sit down!” (to Twins fans), and “Yeaaaahhhhhhhhhhhhhhhhhhhhh!!!” (for about 15 seconds after a mid-inning prize).

Royals child-fans go shirtless for their moment of fame.

Shirtless Royals kids.

While Liriano needed to go out anyway, it appeared quite clear that 90 pitches was his pre-game-determined limit:

Thanks, F-bomb.

Royals fans mainly prefer beachballs to baseball. And who can blame them?!

Beachball.

Cold Kauffman Stadium

Some lighter fare. Phone pictures from 37 degree Saturday night at Kauffman Stadium.

Our starting seats:

Right field line.

Second seats with The Dad:

Behind the dugout

Particularly obnoxious crowd (both Twins and KC fans). Particularly obnoxiously run jersey promotion. 20k jerseys, 1.5k people in front of us at Gate D. Out of jerseys before we arrive at the gate. Yay.

Great stadium (under construction, so kind of a mess).

A fateful bases loaded, nobody out, score zero runs “rally”:

Bases juiced

Breathtaking new scoreboard (from Daktronics, I believe). They’re adding a crown on the top later:

Scoreboard

A Rails way to track additional schema info

Baked NYC
Photo by rachel is …

Right now at Harvest we are baking a few pans of Twitter integration. We’re not sure if they’ll turn out moist and delicious, but hopefully they stop short of dry and forgettable. It’s been a fun, and somewhat frustrating, process getting this feature cooking.

The key interaction Harvest is using is Twitter’s direct message. Send Harvest a message and we’ll pick it up and do something with it. Through the Twitter API, one can grab all direct messages since a certain direct_message.id. In this case, direct_message.id is used as a chronology identifier - the larger the id, the newer the message.

In order to go out and grab a batch of messages, the previous max direct_message.id needed to be tracked somewhere. To me, this felt similar to Rails’ schema_info table - direct_message_info we’ll call it. So with all of those Rails opinions, how do you set up such a schema-tracking table? First, you migrate:

class CreateDirectMessageInfo < ActiveRecord::Migration
  def self.up
    create_table :direct_message_info, :id => false do |t|
      t.column :last_id, :integer
    end
    ActiveRecord::Base.connection.insert("INSERT INTO direct_message_info VALUES(28700000)")
  end

  def self.down
    drop_table :twitter_direct_message_info
  end
end

This migration is fairly simple. When creating the table, we specify that there is no need for the Rails-default id column. Also, insert an initial starting value. I suggest playing with the API and selecting a value reasonably close to the latest id numbers for Twitter’s direct messages.

Then, throw your ActiveRecord model out the window for models/direct_message_info.rb:

class DirectMessageInfo
 
  class << self
    def get_most_recent_id
      ActiveRecord::Base.connection.select_one("SELECT last_id FROM direct_message_info")["last_id"].to_i
    end
   
    def set_recent_id(last_id)
      ActiveRecord::Base.connection.update("UPDATE direct_message_info SET last_id = #{last_id}") if last_id
    end
  end
 
end

Two pretty simple class methods for getting and setting your new schema info value.

Technorati Tags: ,

ActiveResource HTTP basic auth with email address

As I’ve started to play around here and there with ActiveResource, I’ve noticed lots of complaints about how ActiveResource’s implementation of HTTP basic auth leaves a lot to be desired. Specifically with regard to its lack of support for email addresses as usernames. This won’t work:

class Expense < ActiveResource::Base
  self.site = "https://barry@bjhess.com:password@website.com"
end

Turns out it’s fairly simple to authorize via email address in ActiveResource. Just send the header:

class Expense < ActiveResource::Base
  self.site = "https://website.com"
  @headers  = { ‘Authorization’ => "Basic (#{Base64.encode64(’barry@bjhess.com:password’).strip})" }
end

Technorati Tags: , ,

Memetape

HOWTO commit an incremental Rails upgrade to SVN

Suppositoire ou Viagra des champs?
Photo by LeChatMachine

My relationship with Subversion is an ever growing one. Right now it is growing itself straight into git. I’m not sure how happy Subversion is with me. It’s complicated.

But it is nowhere near as complex as my process for committing a Rails upgrade to SVN used to be. First I would delete my current Rails version from the repository. Then I would rake rails:freeze:edge TAG=rel_2-0-2. Finally I would commit the latest Rails version to the repository.

I admit, that doesn’t sound too complex. Problem is this process leads to a completely undeployable revision sitting in SVN. And I’ve also seen very wonky results when looking at svn status for several minutes after completing such a process. I began to wonder if it wasn’t just coincidence that I sometimes convert the phrase “Subversion repository” into “suppository.” I was feeling it.

rsync, friend to the command-line idiot

Enter rsync, the file copy when cp just won’t do. If you’ve ever tried to simply overwrite your vendor/rails directory with a new version, either by using the freeze rake command, a cp command or a simple drag-and-drop in your GUI, you know how much that can mess with your SVN-chi.

Just. Don’t. Do it.

A simple rsync command will take a source directory (a new version of Rails) and copy all new and updated files to a destination directory (an old version of Rails). It will not copy directories themselves, just files. The expectation when using rsync is that you’re dealing with two similar directory trees.

Here is my current process for upgrading an incremental Rails release. From app root:

cd vendor
mv rails rails\_2-0-1
rake rails:freeze:edge TAG=rel_2-0-2
mv rails rails\_2-0-2
cp -rf rails\_2-0-1 rails
rsync -arv --delete-after --exclude='.svn/' rails_2-0-2/ rails

And now a blow-by-blow description of this process:

cd vendor

Don’t ask.

mv rails rails_2-0-1

Move your current version of Rails for safe-keeping.

rake rails:freeze:edge TAG=rel_2-0-2

Freeze your new version of Rails. If you didn’t move vendor/rails in the previous step, well, now you need to start over. Sorry, buddy.

mv rails rails_2-0-2

Now move your new version of Rails for safe-keeping.

cp -rf rails_2-0-1 rails

Put the original version of Rails back in place. Don’t be your own worst enemy. Use a copy command so you have a recovery path that doesn’t involve recreating the working copy of your app from SVN.

rsync -av --delete-after --exclude='.svn/' rails_2-0-2/ rails

This is the secret sauce of the process. The last two parameters are the source (rails_2-0-2/) and destination (rails). The trailing forward-slash on the source parameter is important. If you forget that, rsync will basically copy your rails_2-0-2 directory into rails/rails_2-0-2. Not all that useful.

The other options:

  • -a - archive mode (want recursion and preserve everything)
  • -v - verbose output
  • --delete-after - After merging the directory trees, delete from the destination what didn’t exist in the source
  • --exclude='.svn/' - Don’t even look at .svn directories. Without this, all of your SVN hooks will be destroyed

After completing everything, running tests, fixing it all, committing to SVN, etc you can delete those two safe-keeping copies of Rails in your vendor directory. Now don’t you feel so fresh and so clean?

NOTE: I revert to the first process I describe above with major releases. Jumping from Rails 1.2.* to Rails 2 was jarring enough that there was no real benefit in keeping revision history of such drastic changes. In fact, I’m not entirely sure my new procedure would even work in such a situation.

Technorati Tags: , ,

Move server configuration files into your repository

Dung repository
Photo by Chambo25

Are you a programmer? Do you hate any process that involves SSH’ing into a server and editing files directly in production? Would you like to get rid of this non-source controlled hackery? Damn right you would!

If you’re deploying an application to a server, get those app-specific configuration files in your source control system! I happen to be a Ruby on Rails programmer, so the two files I’m looking to control are the Apache and Mongrel configuration files. Here are two sample locations:

/usr/local/apache2/conf/apps/appname.conf
/etc/mongrel_cluster/appname.yml

Meat

Via terminal, browse into your local Rails app directory. Then, create a couple directories to store this configuration:

mkdir ./config/apache
mkdir ./config/mongrel

Then copy the configuration files you have sitting on your server into the local directory (I’m using SCP and assuming you know the rest). Again, locally:

scp bjhess@server_or_ip:/usr/local/apache2/conf/apps/appname.conf ./config/apache/
scp bjhess@server_or_ip:/etc/mongrel_cluster/appname.yml ./config/mongrel/

Potatoes

Now you’re all ready to commit these configuration repositories to your SVN/git/mercurial repository. But wait, you also want to actually use these versions of the configuration files in production, no? Well, let’s do it. Rename the original configuration files on the remote server:

mv /usr/local/apache2/conf/apps/appname.conf /usr/local/apache2/conf/apps/appname.conf.bak
mv /etc/mongrel_cluster/appname.yml /etc/mongrel_cluster/appname.yml.bak

Then simply update your deployment script to create symlinks from the expected configuration file location to the new location. I’m using Capistrano (version OLD), so adjust accordingly:

desc "Tasks to complete after code update"
task :after_update_code do
  #            LINK TARGET                                   LINK SOURCE
  run "ln -nfs #{current_release}/config/apache/appname.conf /usr/local/apache2/conf/apps/appname.conf"
  run "ln -nfs #{current_release}/config/mongrel/appname.yml /etc/mongrel_cluster/appname.yml"
end

Conclusion

This is great, isn’t it? The programmer in you loves having limitless history of configuration changes, not to mention the ability to use whatever local editor you choose. Wonderful day!

But what about the whole-server configuration files? Unless you commit to one app per server, I don’t think base Apache config files or server-level logrotate config files should be in a specific app’s repository. Perhaps a new repository could be setup to deal with these files? I imagine Capistrano could even be used for the bare-bones deployment.

Or maybe just backing up these general files is enough to keep your mind at ease. Just be sure to think about it. That’ll put you one step ahead of the rest

Technorati Tags: , , , ,

Twitter has rewrite problems

This past week I did some work on Scrawlers to align any search credit received from links to both the scrawlers.com and www.scrawlers.com addresses. I was reminded of this “must do” by Patrick McKenzie over at MicroISV on a Shoestring. Naturally it took me a few months to deal with the issue, but it’s more-or-less done.

More recently we’ve started exploring some ways to make use of Twitter on Harvest. To get familiar with Twitter, and at the encouraging of Eirik Solheim, I signed up for a Twitter account under my moniker as well for Scrawlers.

For some reason I first entered the Twitter URL with those pesky ‘w’s.

Twitter

I was not surprised to see that Twitter rewrites that URL without the “www”. In fact, it made me realize I’m rewriting my Scrawlers URL’s in the wrong direction! What did surprise me is that their URL rewriting is causing an additional forward-slash to make its way into the URL.

Twitter

This doesn’t happen when entering a simple http://www.twitter.com. However, take any more-complex Twitter URL and prepend it with a “www” and you’ll see what I mean. Doesn’t seem to impact usage of the site, but it certainly was something I didn’t expect to see on a heavily trafficked site like Twitter.

Calling MailBuild AddWithCustomFields with Ruby

Today I had a fun trial-and-error exploration of Ruby SOAP messaging. By “fun” I mean “hair-rippingly excruciating.”

Basic SOAP calls are pretty easy with the wsdlDriver inherent in Ruby. Here’s how you’d wrap MailBuild’s Subscriber.Add function in Ruby:

require ’soap/wsdlDriver’

class MailBuildWrapper

  attr_accessor :api_key, :list_id

  def initialize(api_key, list_id)
    @api_key = api_key
    @list_id = list_id
  end
 
  def add_subscriber(user)
    soap     = wsdl.create_rpc_driver
    response = soap.addSubscriber \
                 :ApiKey       => api_key,
                 :ListID       => list_id,
                 :Email        => user.email,
                 :Name         => user.full_name
    soap.reset_stream
    response.subscriber_AddWithCustomFieldsResult
  end

  private

    def wsdl
      SOAP::WSDLDriverFactory.new("http://iridesco.createsend.com/api/api.asmx?WSDL")
    end

end

Calling a simple add subscriber is, well, simple.

u  = User.find(:first)
mb = MailBuildWrapper.new
mb.add_subscriber(u)

Adding with custom fields is different. This is because an array of “SubscriberCustomField” objects is necessary. Finding the magic Ruby incantation to build this SOAP array was a trick. I never found anywhere that said how to do this - Google was not my friend. Here is an example MailBuildWrapper method adding a subscriber with an ‘is_admin’ custom field:

def add_with_is_admin_custom_field(user)
  soap     = wsdl.create_rpc_driver
  response = soap.addSubscriberWithCustomFields \
               :ApiKey       => api_key,
               :ListID       => list_id,
               :Email        => user.email,
               :Name         => user.full_name,
               :CustomFields => {:SubscriberCustomField =>
                 [{:Key => ‘is_admin’, :Value => user.is_admin?.to_s}]}
  soap.reset_stream
  response.subscriber_AddWithCustomFieldsResult
end

So the ‘CustomFields’ hash item needed to point to a hash with a ‘SubscriberCustomField’ item, which itself points to an array of hashes containing ‘Key’ and ‘Value’. So multiple custom fields would be specified as follows:


:CustomFields => {:SubscriberCustomField =>
                    [ {:Key => ‘is_admin’, :Value => user.is_admin?.to_s},
                      {:Key => ‘is_loser’, :Value => user.is_loser?.to_s}]}

Some useful links from my first experience consuming a SOAP web service with Ruby:

Technorati Tags: , ,