The Year of Living Dangerously

by James Britt

Francis Hwang once posted an item about modifying Ruby’s require method so that you can load files over HTTP (or, really, pretty much any file transfer protocol).

It’s really quite clever. I think, though, that having to explicitly include the protocol scheme and path when calling require spoils half the fun.

My take on this is to alter Kernel#require so that it knows to look for requested files outside of the local file system.


# File: hyperactive-require.rb
require 'open-uri'

def require( resource )
begin
super
rescue LoadError
$:.each do |lp|
if lp =~ /http:\/\//i
begin
lp << '/' unless lp =~ /\/$/
s = open( "#{lp}#{resource}" ) { |f| f.read}
eval s
return
rescue; end
end
end
raise LoadError.new( "Cannot find '#{resource}'")
end
end


The trick here is that the load path must be given one or more Web addresses; these sites will then become just more places to look for code. Aside from that, client code does not need to know if some required file is local or remote.

A nice side-effect: No more installations. Well, maybe fewer, simpler installations. Distribute a basic script, define the appripriate URLs, and have it require the app libs as if they were local. You could also switch library version by changing LOAD_PATH URLs to fetch from different repositories.

Dangerous? Um, perhaps. But c’mon, it’s a new year. Live it up!

Besides, everyone knows how dangerous Ruby is . So dangerous that, in the spirit of the season, I even dreamed up Yet Another Ruby Motto.

Ruby: You'll Shoot Your Eye Out.


So let’s try an example and see if we lose an eye or something.


# File: trusted-sites.rb
# I trust these guys!
%w{
http://www.30secondrule.com/living-dangerously/
}.each { |uri|
$:.push uri
}

#!/usr/local/bin/ruby
# File: example.rb
# See if we can grab my vSocial.com RSS feed

require 'hyperactive-require'
require 'trusted-sites'
require 'vsocial'

vs = VSocial.new( 'jamesbritt' )
puts vs.rss


Whew! Scary, but no ocular mishaps. It's certainly no worse than running with scissors.

One side detail: The hyperactive-require code issues an HTTP request for a file with no extension, but it is likely that the actual source file on disk at the server will end in .rb. I used the MultiViews option with Apache2 to allow the server to return a disk file with a .rb extension even if the request URL did not specify that.

Oh, and note, too, that file requests going through an intermediary process opens the door for all sorts of entertainment. You could, for example, dynamically assemble the code returned, perhaps returning different versions based on the client’s IP address. Or have the server invoke a CVS or Subversion checkout to grab the latest code. Or send back a new version of Kernel#require or another set of LOAD_PATH URLs.

Or something. Just don’t hurt anyone.

3 Comments

Ryan Leavengood
2006-01-03 11:11:40
That is pretty cool.


It might also be nice to have client-side caching and only update the code when it has changed. That way if someone has no internet access (it happens) the scripts will still work (as long as they have been run at least once.)

James Britt
2006-01-03 11:39:39
It might also be nice to have client-side caching and only update the code when it has changed.


Indeed. I bet one could easily hack up Mousehole to be such a cache; the code server could return a 304 status code when nothing has changed, and the cache proxy would then just return the what it has.

Greg
2006-01-03 13:04:24
> Or have the server invoke a CVS or Subversion checkout to grab the
> latest code.


We recently were talking about this at NYC.rb
This might be VERY handy for scriptable MMO games.