attr vs method vs define_method
Eric Hodel | Tue, 07 Mar 2006 00:59:00 GMT
There are four different ways to define a method in Ruby. The two most common is the def keyword and the Module#attr family of methods. The last two ways use Module#define_method, define_method with a block and define_method with a Method object.
Ruby's interpreter handles methods created with each of these constructs differently and you can really notice it when benchmarking them:
user system total real attr_writer 0.880000 0.010000 0.890000 ( 0.919265) regular method 1.370000 0.000000 1.370000 ( 1.485922) define_method w/method 2.470000 0.010000 2.480000 ( 2.636708) define_method w/block 3.030000 0.020000 3.050000 ( 3.268494)Read more...
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.
Bob's Favorite Scripting Language
Eric Hodel | Sun, 26 Feb 2006 00:39:00 GMT
Perl is so incomprehensible. I definitely like Ruby better than Perl as my favorite scripting language….
—Bob
Rubygems + ri
Eric Hodel | Thu, 23 Feb 2006 11:42:00 GMT
I’ve almost finished doing what has previously been claimed as impossible. I’ve cleanly integrated ri and Rubygems so that you can use ri to search your installed gems’ documentation.
The first part was simple, tell Rubygems to generate ri data for its gems. Rather than have Rubygems install a gem’s ri data mixed-in with the standard library’s data it installs it into a per-gem directory.
The unfinished part is getting a patch into Ruby that makes ri go looking in the gem ri data directories. That patch is in [ruby-core:7423]. Hopefully I can push it into 1.8 so it will be usable with Rubygems 0.9.
Undefined method for Symbol on OS X
Eric Hodel | Mon, 20 Feb 2006 02:02:00 GMT
For the past few months people have been getting messages looking like undefined method `push’ for :compact!:Symbol (NoMethodError) when using DRb on OS X.
Eventually a simpler example of the problem showed up and in [ruby-core:7305] Mauricio Fernandez thought it could be either OS X’s malloc() alignment or the address at which malloc() first returns memory.
Today I decided to figure out what the real problem was. Apple’s documentation showed that malloc() gives you regions of memory aligned on 16 byte boundaries removing the possibility of an alignment problem.
That left only the second possibility, that malloc() on OS X returns memory lower than on most *NIX platforms.
It turns out that low addresses returned by OS X’s malloc() is the culprit. In Ruby a Symbol’s ID is mapped onto a VALUE in memory by sym = ID << 8 | 0x0e so a Symbol’s object_id ends up being a low address, typically outside the range of valid VALUEs.
On FreeBSD it takes many Symbols for a Symbol to overlap a real VALUE since malloc() first returns addresses up around 0×400000. OS X, however, returns addresses around 0xd0000 so it is already overlapping the built-in set of symbols:
p :x.object_id.to_s(16)
p Object.new.object_id.to_s(16)
On OS X:
<samp>"26e90e" "e7bce"</samp>
The object_id of a newly created objects is already less than the first newly created Symbol. (New objects will first decrease in object_id, then jump up when the next Ruby heap section is allocated.)
On FreeBSD:
<samp>"26910e" "403b536"</samp>
Here the first object is well above the first Symbol, so we won’t have to worry about anything until we’ve generated a lot of Symbols.
The full example is in [ruby-core:7401].
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.
ri for Rubygems will be easy!
Eric Hodel | Fri, 17 Feb 2006 08:44:00 GMT
Something simple as:
begin
require 'rubygems'
Dir["#{Gem.path}/gems/*/ri"].each do |path|
RI::Paths::PATH << path
end
rescue LoadError
end
FakeMutex
Eric Hodel | Wed, 11 Jan 2006 01:23:00 GMT
Adding Mutex makes things slower, but you don’t always need it. For our memcache library I’d like to avoid performing locking when we’re running single-threaded (since that’s what happens most of the time). So, I came up with the idea of FakeMutex that simply yields inside its #synchronize method.
FakeMutex doesn’t give back all the performance of not having locking at all, but it does prevent the code from being messed up with lots of tests to see if we should try to lock or not.
Read more...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
Array#pack vs Tiger defeats ruby-growl
Eric Hodel | Tue, 15 Nov 2005 05:53:00 GMT
Tiger’s Ruby thinks its running on an x86, so I had to release ruby-growl 1.0.1.
The meat of the change was
pack_format.gsub!(/n/, 'v') if BROKEN_PACK where BROKEN_PACK = [1].pack("n") != "\000\001"
Thanks to Aslak Hellesoy for bringing this up, I’ve been running my own Ruby and only using ruby-growl from a FreeBSD box so never would have known it was broken.

Articles