Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I dynamically define an alias method for a class method?

Tags:

ruby

I have a module lets call "Calculator" that I want to include in a class "Product". Calculator will extend "Product" which will copy the class methods onto Product. One of these class methods is "memoize". The idea being that I can do something like this:

module Calculator
  def self.extended(base)
    base.memoize :foo_bar
  end
end

With the purpose of memoizing the method (specifically a class method) :foo_bar. Inside of memoize I call the method "alias_method" which attempts to alias a class method to a different name (here :foo_bar). This fails. Memoize looks something like:

module Calculator (the extended module)
  def memoize(name)
    alias_method "memoized_#{name}", name
  end
end

When this is called via memoize :foo_bar, the alias_method line kicks an error saying Product has no method "name".. my understanding is this is because alias_method will attempt to alias instance methods not class methods.. (I don't know why but ok no big deal)..

I can reopen the eigenclass like so

module Calculator
  def memoize(name)
    class << self
      alias_method "memoized_#{name}", name
    end
   end
end

This would work but name is not available to the scope of the class << self definition. People have mentioned using self.class_eval and self.instance_eval but neither of these seem to work.. I'd like my cake and eat it too.. how can I keep alias_method dynamic but use it on class_methods?

like image 347
Inc1982 Avatar asked Nov 15 '12 23:11

Inc1982


2 Answers

So just learned that this will do the trick:

module Calculator
  def memoize
    define_singleton_method(name, method(name))
  end
end

Then when Calculator gets included in Product it will define the singleton method as I needed. I still don't know why alias_method needs to only work on instance methods.. and I don't know why class_eval or instance_eval didn't solve the problem.. but at least I have a solution..

like image 161
Inc1982 Avatar answered Oct 14 '22 18:10

Inc1982


What if you put the instance method to be aliased before the include/extend? If you're putting the extend right after the class name, the instance method will not yet have been defined.

module Foo
  def self.extended(base)
    base.memoize :flavor
  end

  def memoize(name)
    alias_method "memoized_#{name}", name
  end
end

class Bar
  def flavor
    puts "Orange"
  end

  extend Foo
end

if __FILE__==$0
  b = Bar.new
  b.memoized_flavor   #=> Orange
end
like image 21
calvin Avatar answered Oct 14 '22 16:10

calvin