Optimize method_missing usage
Ruby
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib/epublishing
spec
.gitignore
CHANGELOG
LICENSE
Manifest
README.rdoc
Rakefile

README.rdoc

MemoizableMethodMissing - Optimizes method_missing use

Introduction

We Rubyists love method_missing, but it isn't always the most efficient way to do things. Following ActiveRecord's lead, we want to have the ability to create a method definition for a given missing method (that would otherwise be handled by method_missing logic) so that subsequent requests to that method do not have to get handled by method_missing.

Notes

NOTE: This can be used in conjunction with method_missing in the case that there is some method_missing_logic that is dependant on the args and not just the method name.

NOTE: The return value of the memoize_method_missing block should be one of:

1) String -- which can be evaled into the method definition
2) Proc -- which can be set as the method definition
4) nil -- meaning the call should be forwarded along to method_missing

NOTE: Profiling with ruby-prof has shown speed improvements of 40-60% in using memoize_method_missing over just method_missing (after the initial cost of creating the method on the fly). The test was minimal and only involved a simple regex. More complex logic may result in even greater performance gains. The performance difference between using an eval'ed String and a Proc (see NOTE above) was negligible.

Installation

gem install memoizable_method_missing

Git Repository

github.com/ePublishing/memoizable_method_missing

Usage

class Foo

  extend Epublishing::MemoizableMethodMissing

  def method_missing(method, *args, &block)
    if method.to_s =~ /^by_array_(.*)/ and args.first == :all
      do_something($1.to_sym, args.last)
    else
      super
    end
  end

  memoize_method_missing do |method|
    case method.to_s
    when /^by_xyz_(.*)$/
      "do_something #{$1.inspect}, args.first, yield"
    when /^by_id_(\d+)$/
      id = $1.to_i
      lambda { do_something(id) }
    when /^by_name_(.*)$/
      name = $1
      lambda do |a1, a2, &block|
        do_something(name, a1 * a2, block.call)
      end
    end
  end

end

Foo.new.by_xyz_bar(42) { :foo }      => do_something("bar", 42, :foo)
Foo.new.by_id_33                     => do_something(33)
Foo.new.by_name_chris(3, 2) { :foo } => do_something('chris', 6, :foo)
Foo.new.by_array_foo(:all, 7)        => do_something(:foo, 7)
Foo.new.by_array_foo(:bogus)         => NoSuchMethodError

Author

David McCullars <dmccullars@ePublishing.com>

Copyright

(C) 2011 ePublishing

Licence

GPL