kirby gets a del.icio.us account

I added link posting to Kirby. Any url that begins with http:// mentioned in the channel will get silently posted to a delicious account, which you can configure on the command line.

cronjob example

* * * * * /path/to/kirby.rb [-d | -no-d] nick chan server \
  delicious_user delicious_pass [--silent] 2&>1 > /dev/null

--silent (or actually, any 7th argument) will disable every feature but the delicious posting.

Kirby is now considered feature-locked.

to get

Download the latest version from Fauna, or check out from svn as described here.

kirby spies your svn

I added svn commit watching to Kirby. Add a repository url with the add_svn keyword. Kirby will poll more often when people are talking, less when the channel is idle, and announce the details of each commit.

remote access considered fine

Because it is not callback-based, you can watch any repository that has anonymous checkout. If you need Kirby to authenticate, you can set up an ssh key and use an svn+ssh:// url.

Download the latest version from Fauna, or check out from svn as described here.

code story

The original methods, borrowed from Matzbot, were hackish, threaded, and large, and used regular expressions for parsing. I rewrote these to an unthreaded 16-line procedure. Then, noticing that svn log has an --xml parameter, I rewrote it again to parse with Hpricot. 11 lines total, at the small cost of another dependency.

Hpricot makes xml parsing a joy.

daemons

Daemons is a nice gem for writing background processes. Just call Daemons.daemonize when you are ready to detach.

warning

Daemons likes to change your working directory to /, for no clear reason. My file accesses were silently failing, and this was why. Also, you can use the :ontop => true key to help debug a daemonized program. :ontop keeps the process in the foreground and leaves STDERR attached to the console.

a ruby eval bot for irc: kirby

Kirby bolts together _why’s tryruby interface with a minimal IRC listener, giving you a sandboxed irb-style read-eval-print loop, and a few other things.

update

See the latest documentation here.

demo

  <evn> >> a = %w(big bad bob)
<kirby> => ["big", "bad", "bob"]
  <evn> >> 3.times {puts a[rand(a.size)]}
<kirby> bob
<kirby> bad
<kirby> bad
<kirby> => 3
  <evn> reset_irb
<kirby> Began new irb session
  <evn> >> a
<kirby> NameError: undefined local variable or method `a' for main:Object

If you have an IRC channel for your local Ruby users group or your company, put Kirby in it for instant collaborative learning (otherwise known as futzing around).

features

  • irb-like interface
  • safe, sandboxed environment
  • svn repository commit watching
  • silent del.icio.us link saving
  • tolerable channel fault recovery

download

Download all 95 lines of version 3 from the Snax Fauna, or check out the project from svn with:

svn co svn://rubyforge.org/var/svn/fauna/kirby/kirby

Kirby is now feature-locked.

usage

You need Rubygems, the daemons and hpricot gems, and OpenSSL support compiled into Ruby.

Run kirby.rb [nick] [channel] [server]. Alternatively, you can add a line like the following to your crontab to start Kirby in daemon mode and keep it started through reboots and high water:

 * * * * * /path/to/kirby.rb [-d | -no-d] nick chan server \
  delicious_user delicious_pass [--silent] 2&>1 > /dev/null

If you set up the cron task on a server somewhere, you can just forget about it and Kirby will always be around. The code is pretty clear if you are unsure of a command-line switch, or need to hack a different feature.

license

Kirby is copyright 2007 Cloudburst, LLC, and licensed as AFL 3. Kirby is based on Matzbot, an unreleased ball of mud. Matzbot is partially based on public domain code from here and there.

hacking activerecord’s automatic timestamps

ActiveRecord’s automatic timestamping is handy. But what if we want some attributes to be exempt? Here’s how.

why do we need this?

Say we have a full-text search client that periodically updates the index for every record. We want to record the last time a record was indexed, so we make an indexed_at field. But setting indexed_at shouldn’t change updated_at; that wouldn’t make sense. We don’t need to timestamp our timestamps.

if i only had a brain

I think we can take for granted that our feature belongs in the model, since it’s a type of attribute update. But if we are too cool for that, we could bash it into the controller.

class VegetableController < ApplicationController
  def read_for_index
    @obj = Vegetable.find(params[:id])
    Vegetable.record_timestamps = false
    @obj.update_attribute(:indexed_at, Time.now)
    Vegetable.record_timestamps = true
  end
end

Omg! Never do that. We are messing with the entire model class for the sake of a single controller action.

the brain-man cometh

In my opinion, the best way to implement this is to override only the accessor:

class Vegetable < ActiveRecord::Base
  def indexed_at= when
    self.class.record_timestamps = false
    self[:indexed_at] = when
    save!
    self.class.record_timestamps = true
  end
end

This is obvious and fast. However, this works reliably only because Rails is not-thread safe. If we had threaded Rails, there is the possibility for a race condition if two record updates are performed at the same time. An unrelated update that was supposed to stamp, but occurred exactly between the true/false flip, would not stamp correctly. This is true even if it’s in another class, since the @@record_timestamps setting belongs to ActiveRecord::Base.

thread-safe version

What if DHH shows up sloshed at our New Year’s party and gives us Threaded Rails as a present?

class Vegetable < ActiveRecord::Base
  def indexed_at= when
    class << self
      def record_timestamps; false; end
    end
    self[:indexed_at] = when
    save!
    self.remove_method :record_timestamps
  end
end

This is thread-safe because it changes the record_timestamps on the instance’s metaclass, rather than the instance’s real class. No other instance in any other thread or context will be affected.

a derailment about meta issues

What’s the deal with remove_method? It’s because instances with modified metaclasses can’t be marshalled. That means that we couldn’t store the record in the session after calling indexed_at=. We would have to reload it from the DB.

We can’t use undef_method either. undef_method overwrites the method with a method that always raises NoMethodError. Contrast this to remove_method, which removes the method entirely, restoring the original method inheritance chain. The “real” record_timestamps() is inherited from ActiveRecord::Base (which includes Timestamp).

How come record_timestamps() is an instance method to begin with, though? It’s because @@record_timestamps is defined with cattr_accessor, which adds accessors to the instances as well as to the class.

There are a couple of ridiculous workarounds to the class problem. First, we could define the class-level record_timestamps() of Vegetable, instead of ActiveRecord::Base, and limit the scope to at least only Vegetables. Second, we could override Rails’ update_with_timestamps method on the instance instead, and then remove_method that. Or, lastly and horribly, we could clone the parent class, change its record_timestamps(), and use evil.rb to temporarily reparent the instance for the duration of the save (I hope that last suggestion made you gag).

callback on me

Ok, forget about threads. If we set up our callbacks properly, we can let anyone update indexed_at any way they please (accessors, attributes hash, update_attribute, create):

class Vegetable < ActiveRecord::Base
  def before_save
    unless self.new_record?
      # skip timestamping if indexed_at is the only changed field
      self.record_timestamps = false if self.attributes ==
        self.class.find(self.id).attributes.merge(:indexed_at => indexed_at)
    end
  end

  def after_save
    # it's always safe to re-enable at this point
    self.record_timestamps = true
  end
end

This is kind of cool. Unfortunately it requires an extra trip to the database to see if only indexed_at is changed (because otherwise timestamps really do need to happen). We could even move it into ActiveRecord::Base if we wanted to, and support per-class configuration with subclass (or instance) constants.

agggh too much brains

What if we want any arbitrary action to be performed without timestamp updating? This is probably bad; it would be better to make regular fields to contain the data we are trying to conditionally shoehorn into the timestamp. Ignoring that:

class ActiveRecord::Base
  def skip_stamp
    self.record_timestamps = false
    yield
    self.record_timestamps = true
  end
end

Oh man. Too easy. If we wanted to be really ridiculous, we could create a record_timestamps field in the model itself, and override ActiveRecord to look at that per instance to determine whether to timestamp. We would need to default to ActiveRecord::Base#record_timestamps() in case some models don’t have such a field.

That, though, is a total deviation from the original use case. Before you ever implement anything, ask: what’s the use case?

rv, a tool for luxurious camping

I’ve been using a little tool named rv for months now to robustly manage camping apps in deployment. It consists of an init.d script, a default mongrel harness you can adjust to taste, and a .yml file for each app.

download and installation

Download version 2 from the Snax Fauna, or check out the project from svn with:

svn co svn://rubyforge.org/var/svn/fauna/rv/rv

Next, make a folder /etc/rv/.

Copy etc/init.d/rv to the /etc/init.d/ folder. Change the USER='httpd' near the top to the user the daemons should run as (never root!).

Install as a boot service (on Ubuntu) with:

$ sudo /usr/sbin/update-rc.d rv defaults

If you are not using Ubuntu you may have to do something different. Redhat uses chkconfig. Gentoo uses something else yet probably. On OS X you can try to get launchd to work, if you are bold.

usage

Copy the included harness/rv_harness.rb to your camping application’s folder. Edit it appropriately for your app and database.

Then, copy the example etc/rv/my_blog.yml file into /etc/rv. Rename and edit it appropriately for your app. You need to set the path, proxy port, and listen interface. For more apps, you can just add more .yml files.

Now you can manually start, stop or restart all your camping daemons by calling:

$ sudo /etc/init.d/rv start
$ sudo /etc/init.d/rv stop
$ sudo /etc/init.d/rv restart

apache setup

In most situations, you will also need an Apache vhost entry in httpd.conf to reference the camping proxy started by rv. Here’s one for Apache 2.2:

<VirtualHost *:80>
  ServerName myblog.j2ee.com

  ProxyRequests Off
  ProxyPass / http://127.0.0.1:3055/
  ProxyPassReverse / http://127.0.0.1:3055/
  ProxyPreserveHost On

  #Fix for Apache bug 39499
  SetEnv force-proxy-request-1.0 1
  SetEnv proxy-nokeepalive 1
</VirtualHost>

postscript

It would be really nice if someone could write a setup script for this, so you don’t have to copy stuff around by hand. It could ask you questions on initial setup, and then each time you want to add a new app.

resources for keeping up with ruby

Having trouble keeping up with the state of Ruby? Dynamic language police keep catching you ridin’ dirty? Here are some top-secret resources I use to stay 2.0 steps ahead.

they see me bloggin…

Ruby has more than a few celebrity blogs. But there are many smaller precious stones in the ether. Some more obscure ones:

You can read all my feeds tagged with Ruby in your browser, or you can snag my ruby.opml file.

Technorati lets me see what other blogs are saying about my own projects. It seems not to search non-English blogs though.

aggregators

Rubylicious, my favorite, is a curated Ruby feed aggregator. Quality is consistently very high. Rubycorner is its harder-hitting uncurated cousin from the streets.

The Rubyforge news feed is critical for finding out about new or active Ruby libraries. I usually just click through to the project home, since the news entry itself assumes I’m already familiar with the project, which I’m not.

delicious diving and pastie stalking

Watching the del.icio.us tags flow by is a good time: ruby, ruby+camping, rails. Less common tag combinations can get you more interesting results, such as ruby+dylan, or ruby+machinelearning. Also, I add people whose work I appreciate to my delicious network.

You can find odd things in the pastebins, too. Bigbold has more fleshed-out snippets, while Pastie is more random.

mailing lists and irc

I don’t really hang out on the mailing lists, but for those who do, Ruby Forum is the easiest to use gateway (better than Google Groups).

I am on IRC all the time, but tend to avoid the standard Ruby channels. They can still be great resources, though, especially for beginners. Hacking out a proof-of-concept live in an eval bot is an excellent way to learn syntax tricks from other Rubyists.

across the sea

And then there are the Japanese. I really wish a Japanese speaker would start a “this week in Japan” series that gave English summaries of interesting posts. My German is not so useful against kyōtsūgo.

Excite, however, has a Japanese-to-English translator which is sometimes acceptable. It’s better than Google’s, at least.

You want the form to look like this:

As a start, here’s Rubyist Magazine in English.

postscript

This the Play-N-Skillz when we out and cruisin

Got certs in every language except Dylan

But I’m still ain’t losin’.

debugging with ruby-debug

I wanted Rails-ish breakpoints in my standard, console-driven, unthreaded Ruby apps. For some reason I didn’t know how to do this.

ruby-debug

You will need to install the ruby-debug gem. Then, at the start of your application:

require 'rubygems'
require 'ruby-debug'
Debugger.start

Then to get a breakpoint:

debugger

When you run your code, you’ll drop into an old-school style stepping debugger at the debugger call points.

$ ruby some_app.rb
/Users/eweaver/p/some_app/some_app.rb:40: url = nil
(rdb:1) 

Now you can muck around in the prompt, kick over objects, cause havoc.

some handy commands

next     # run the next line and break
cont     # run to the next breakpoint
up       # jump up the stack one level, completing
         #   the current block
p        # inspect an object
display  # mark an object for automatic inspection
finish   # just run the rest of the code

It would be nice to be able to drop into an IRB instance, too, but I don’t know how to do that.

update

As of version 0.5.1, you can enter an IRB instance from the debug prompt by typing irb.

benchmark

The built-in profiler, and even ruby-prof, add a lot of overhead to the interpreter. This reduces their usefulness. If I don’t know why my program is slow, I use ruby-prof. However…

benchmark, i choose you!

Sometimes I already know the source of the problem, and just need to see if my changes are making a difference or not. So I added a little guy to my .irbrc file:

def benchmark
  cur = Time.now
  result = yield
  print "#{cur = Time.now - cur} seconds"
  puts " (#{(cur / $last_benchmark * 100).to_i - 100}% change)" rescue puts ""
  $last_benchmark = cur
  result
end

Now I can benchmark {} any block and get the wall clock running time, as well as the percent change (+ or -) from the last run. As they say on the street: schwing!