Search code examples
rubydelegation

Getting Method Alias to Work


I'm trying 'borrow' some code from the Ruby Commander Gem. In the example in the Readme, they show a number of method calls that you place in your program like this:

require 'commander/import'
program :name, 'Foo Bar'

The method program is in the Commander module, Runner class. If you follow the require links you will come to the following module:

module Commander
module Delegates
%w(
  add_command
  command
  program
  run!
  global_option
  alias_command
  default_command
  always_trace!
  never_trace!
).each do |meth|
  eval <<-END, binding, __FILE__, __LINE__
    def #{meth}(*args, &block)
      ::Commander::Runner.instance.#{meth}(*args, &block)
    end
  END
end

  def defined_commands(*args, &block)
    ::Commander::Runner.instance.commands(*args, &block)
  end
end
end

In the Commander module, Runner class, this is the pertinent code:

def self.instance
  @singleton ||= new
end

def program(key, *args, &block)
  if key == :help && !args.empty?
    @program[:help] ||= {}
    @program[:help][args.first] = args.at(1)
  elsif key == :help_formatter && !args.empty?
    @program[key] = (@help_formatter_aliases[args.first] || args.first)
  elsif block
    @program[key] = block
  else
    unless args.empty?
      @program[key] = (args.count == 1 && args[0]) || args
    end
    @program[key]
  end
end

I have copied this code into my own program and it doesn't appear to work as I get a method not found error on program. If I instantiate Runner as runner and call runner.program, it works fine.

In my version, which is all in one file, I have

module Repel
  class Runner
    # the same methods as above
  end

  module Delegates
    def program(*args, &block)
      ::Repel::Runner.instance.program(*args, &block)
    end
  end
end
module Anthematic
  include Repel
  include Repel::Delegates

  #runner = Runner.new
  #runner.program :name, 'Anthematic'

  program :name, 'Anthematic'
  ...
end

The error I get is:

: undefined method `program' for Anthematic:Module (NoMethodError)

The commented out code works when it is uncommented.

How do I get the code to work or, alternatively, is there a better way to do it? I have no idea what's going on in the rest of the eval statement. I'm aware that the number of arguments are off in the program def. I have the same problem with another method where they align.


Solution

  • Instead of

    include Repel::Delegates
    

    which includes module methods as instance methods, you should

    extend Repel::Delegates
    

    which will extend the class methods.