The weirdest code I've seen recently
I was kicking around in ActiveResource::Base recently and it took me a good solid ten minutes to figure out why the prefix method didn’t recur infinitely.
def prefix(options={})
default = site.path
default << '/' unless default[-1..-1] == '/'
# generate the actual method based on the current site path
self.prefix = default
prefix(options)
end
def prefix_source
prefix # generate #prefix and #prefix_source methods first
prefix_source
end
def prefix=(value = '/')
# Replace :placeholders with '#{embedded options[:lookups]}'
prefix_call = value.gsub(/:\w+/) { |key| "\#{options[#{key}]}" }
# Redefine the new methods.
code = <<-end_code
def prefix_source() "#{value}" end
def prefix(options={}) "#{prefix_call}" end
end_code
silence_warnings { instance_eval code, FILE, LINE }
rescue
logger.error "Couldn't set prefix: #{$!}\n #{code}"
raise
end
Do you see it? When prefix is called, it calls prefix=, which redefines prefix and returns. prefix in turn returns by calling not itself but the newly-created method of the same name.
I am not known for my low tolerance of metalanguage hackery (exhibit). But I do have my limits, and this exceeds them; calling it needlessly obfuscatory would be kind. Or am I wrong? Is there no better way to provide a method with the same semantics?
Speeding up ActiveResource
I’ve been knocking around ways to speed up ActiveResource recently. My colleague Tim Cochran discovered that you take a big performance hit from Hash.from_xml (which uses XmlSimple to transform the parsed XML into a hash with a particular structure) and I’ve knocked up a couple of alternatives, one for libxml-ruby and one for Hpricot. I haven’t packaged them up into a plugin yet, but I’ll attach the raw source, and hopefully that’ll help someone.
There’s already at least one other attempt to adapt libxml to ActiveResource floating around, but as far as I can tell it doesn’t work: it doesn’t place the resulting XML in a form that allows ActiveSupport to successfully undasherize the keys, deserialize primitives by their type attribute, and place the result in an ActiveResource object (though it is of course possible that I configured the library incorrectly). I’ve tested the parsers below against the corresponding XmlSimple call and I know that they succesfully deserialize into the desired end result.
- libxml (extends XML::Document with a to_hash method, roughly 5-10x faster than XmlSimple)
- hpricot (extends Hpricot::Doc with a to_hash method, roughly 3-5x faster than XmlSimple)
Use them like this:
module ActiveSupport
module CoreExtensions
module Hash
module Conversions
module ClassMethods
# libxml
def from_xml(xml)
xml.gsub!(/\s*\n\s*/, '')
typecast_xml_value(undasherize_keys(XML::Parser.string(xml).parse.to_hash))
end
# hpricot
def from_xml(xml)
typecast_xml_value(undasherize_keys(Hpricot.XML(xml).to_hash))
end
end
end
end
end
end
Posted in Professional | no comments |