Question

I'm deeping into ruby metaprogramming and have next question. Example:

module ExampleAliaser
  def do_example_alias(prefix=:origin)

    class_eval  <<-EOS
       class << self 
           alias_method :#{prefix}_example, :example
           def example
              puts "in aliase will call :#{prefix}_example"
              #{prefix}_example
           end  
        end
    EOS

  end   
end  

class Example1
 def self.example
    puts "Example"
 end 
end


Example1.extend(ExampleAliaser)

class Example1 
 do_example_alias(:origin)
end
class Example2 <  Example1
 do_example_alias(:origin)
end



     Example1.example
    in aliase will call :origin_example
    Example
     => nil 

     Example2.example
in aliase will call :origin_example
in aliase will call :origin_example
in aliase will call :origin_example
    SystemStackError: stack level too deep
        from /Users/igorfedoronchuk/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/irb/workspace.rb:80
    Maybe IRB bug!!

So when mixin used 2 times it causes error. What is the best way to fix such things? How to determine that mixing exists and remove it before new mixing

Was it helpful?

Solution

Follow the definition of methods to see why this is happening.

You first define Example1::example in the class definition of Example1. It writes a string to the console.

Then you extend ExampleAliaser. When you call Example1::do_example_alias, you then alias the method example to origin_example and redefine the method example to write a different string to the console and call origin_example.

Then you define the class Example2 to inherit from Example1, which now has two methods defined on it: origin_example and example. When you call Example2::do_example_alias, you alias the method example to origin_example. But remember that example was already redefined to call origin_example. So effectively, Example2::example will call itself until you run out of room on the stack.


If you want to avoid double-aliasing, you could include some kind of guard in do_example_alias:

def do_example_alias(prefix = :origin)
  unless methods.include?("#{prefix}_example")
    # do the aliasing
  end
end

You can also undef :method_name in subclasses to remove methods that you no longer want defined.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top