String interpolation in ruby2c
Eric Hodel | Sat, 04 Mar 2006 07:27:00 GMT
The toolset zenspider and I built into ParseTree for rewriting Ruby’s AST allows us to make features work that otherwise would be very difficult by rewriting them into easier to implement features.
One feature that makes Ruby so clean is string interpolation but currently ruby2c doesn’t support it. In Ruby there are two types of String nodes, a plain :str node ([:str, "mystring"]) for regular strings and a :dstr node that contains all the parts of the dynamic string which the interpreter concatenates ([:dstr, "val: ", [:vcall, :val]]).
Implementing :dstr in a runtime would be too hard to be worthwhile so I used SexpProcessor to rewrite a :dstr into a series of calls to #<< and #to_s that append to the leading :str node.
Now a dynamic string like:
[:dstr,
"a",
[:vcall, :x],
[:str, "b"],
[:vcall, :y],
[:str, "c"]]
Gets transformed into this ugly sexp:
s(:call,
s(:call,
s(:call,
s(:call,
s(:str, "a"),
:<<,
s(:arglist,
s(:call, s(:call, nil, :x, nil), :to_s, nil))),
:<<,
s(:arglist,
s(:call, s(:str, "b"), :to_s, nil))),
:<<,
s(:arglist,
s(:call, s(:call, nil, :y, nil), :to_s, nil))),
:<<,
s(:arglist,
s(:call, s(:str, "c"), :to_s, nil)))
I don’t know about the inside-outness of it though, I may have to fix that.
Markov Chain
Eric Hodel | Sun, 26 Feb 2006 00:39:00 GMT
The back of my brain has been wanting a Markov chain based random-text generator for some time. I found some decently explanatory pseudo-code in the Generating Text section of the online Programming Pearls by Jon Bently.
What I really wanted was something that illustrated the concept well so I could tell what it was doing. The perl, python and ruby versions I found weren’t simple enough for me to see that. I also wanted to adjust the amount of state in the chain, but the only implementations that supported that were far too complicated to figure out.
Working from Jon Bently’s pseudo-code I ended up with the following implementation. Read more...
I was right!
Eric Hodel | Fri, 17 Feb 2006 10:28:00 GMT
Super-easy!
Except that Rubygems has methods that don’t get called when you think they should.
And that there’s a strange bug in RDoc when you run it twice.
httpdump
Eric Hodel | Mon, 23 Jan 2006 07:34:00 GMT
I wrote a nifty little combination of a WEBrick servlet and a ruby-pcap http grabbing example that lets you see the latest HTTP requests that have crossed a network card interface.
All so I can spy on my neighbors (but they can also spy on me).
Open-uri makes tests easy
Eric Hodel | Mon, 21 Nov 2005 01:28:00 GMT
Neither of the two Ruby Flickr APIs do what I want. One stomps all over the global namespace (Photo will be a model class, it adds a Photo class at the toplevel) and doesn’t have the photo taken date as one of its properties. The other doesn’t support the search method (you get back an XML blob instead of a collection) or the photo taken date.
Neither has tests, so I wasn’t going to figure out how to add what I wanted to either. Instead, I decided to wrap as much of the API as I needed with something that I could test.
On a whim, I decided to use open-uri instead of Net::HTTP. This made testing super-easy. I don’t have to touch the network at all, other than to get a blob of XML to feed into the test:
class Flickr
attr_accessor :responses, :uris
def open(uri)
@uris << uri
yield StringIO.new(@responses.shift)
end
end
class FlickrTest < Test::Unit::TestCase
def setup
@flickr = Flickr.new 'API_KEY'
@flickr.responses = []
@flickr.uris = []
end
Then a test simply adds the XML blobs it is supposed to receive from flickr up-front. After performing the request, I assert it attempted to fetch the correct URLs along with the rest of the stuff the method was supposed to do. (Search is paginated, and I wanted to fetch all the photos without making things clumsy for the user.)
def test_photo_get_info
@flickr.responses << <<-EOF
...
EOF
info = @flickr.photo_get_info :photo_id => 59864477
assert_equal 1, @flickr.uris.length
assert_equal 'http://flickr.com/services/rest/...',
@flickr.uris.first
assert_equal Time.at(1131153707), info[:date_taken]
assert_equal Time.at(1131153708), info[:date_uploaded]
end
Rails Functional TestCase
Eric Hodel | Tue, 18 Oct 2005 07:26:00 GMT
Its a shame that Rails doesn’t define its own Test::Unit::TestCase subclasses. I’ve taken that into my own hands. This one puts Test on the front because that’s the Test::Unit way.
require 'test/unit'
def Object.path2class(klassname)
klassname.split('::').inject(Object) { |k,n| k.const_get n }
end
class FunctionalTestCase < Test::Unit::TestCase
def setup
self.class.name =~ /\ATest(.*)\Z/
return unless $1
controller_klass = Object.path2class $1
@controller = controller_klass.new
controller_klass.send(:define_method, :rescue_action) { |e| raise e }
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@deliveries = []
ActionMailer::Base.deliveries = @deliveries
end
def test_stupid
end
end
Easy model urls for Rails
Eric Hodel | Tue, 18 Oct 2005 07:19:00 GMT
One part url params
class Player < ActiveRecord::Base
def url_params
return { :controller => 'players', :action => 'info', :id => username }
end
end
One part url_for override
class ApplicationController < ActionController::Base
def url_for(options, *params)
if options.include? :model then
options = options.delete(:model).url_params.merge options
end
return super(options, *params)
end
end
[Simplified due to some insight from zenspider]
Then just add :model => AR_object to anything that accepts url params:
<ul>
<% @players.each do |player| -%>
<li><%= link_to player.username, :model => player %>
<% end -%>
</ul>
And ba-bam!
<ul>
<li><a href="/players/info/herbert">herbert</a>
<li><a href="/players/info/joseph">joseph</a>
</ul>

Articles