Erudition and Inanity

brian guthrie blogs

The weirdest code I've seen recently

Posted by Brian Guthrie Sat, 02 Aug 2008 22:15:00 GMT

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?

Posted in | 1 comment |

Speeding up ActiveResource

Posted by Brian Guthrie Fri, 30 May 2008 02:09:00 GMT

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 | no comments |