tj / commander

The complete solution for Ruby command-line executables
http://visionmedia.github.com/commander
MIT License
1.09k stars 64 forks source link

Can't figure out how to use commander using the "Modular" style #76

Closed rmiesen closed 10 years ago

rmiesen commented 10 years ago

I'm trying to write a CLI program with commander using the "Modular" style, but I can't figure out how to convert a program from "classic style" to "modular style". I tried doing a "copy and paste" of the "classic style" program contents and calling something like MyApplication.new.run, but it only works when everything is in the same ruby file. When I try and move the class definition to a separate file, I start getting the following errors from the ruby interpreter:

/usr/lib/ruby/vendor_ruby/commander/runner.rb:365:in `block in require_program': program version required (Commander::Runner::CommandError)

Could someone please give me a full example of how to properly write a "Modular style" commander CLI program?

Full example of broken code:

require 'rubygems'
require 'commander/import'
require 'commander/methods'

class MyApplication
  include Commander::Methods

  def run
    program :name, 'Foo Bar'
    program :version, '1.0.0'
    program :description, 'Stupid command that prints foo or bar.'

    command :foo do |c|
      c.syntax = 'foobar foo'
      c.description = 'Displays foo'
      c.action do |args, options|
        say 'foo'
      end
    end

    command :bar do |c|
      c.syntax = 'foobar bar [options]'
      c.description = 'Display bar with optional prefix and suffix'
      c.option '--prefix STRING', String, 'Adds a prefix to bar'
      c.option '--suffix STRING', String, 'Adds a suffix to bar'
      c.action do |args, options|
        options.default :prefix => '(', :suffix => ')'
        say "#{options.prefix}bar#{options.suffix}"
      end
    end
  end
end

MyApplication.new.run if $0 == __FILE__
ggilder commented 10 years ago

The problem is with these two lines:

require 'commander/import'
require 'commander/methods'

Those should be replaced with:

require 'commander'

Hope that helps!

rmiesen commented 10 years ago

Hi Gabriel. Thank you for your prompt reply.

I tried doing that, but when I make the changes you suggest, the Ruby interpreter gives me the following error:

in `<class:Program>': uninitialized constant Commander::Methods (NameError)

For reference purposes, I'm using version 4.2.0 of Commander. To facilitate the debugging process, I re-ran the program in question with the --debug command argument. Ruby gave me this output as a result:

Exception `LoadError' at /usr/lib/ruby/1.9.1/rubygems.rb:1273 - cannot load such file -- rubygems/defaults/ruby
Exception `LoadError' at /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36 - cannot load such file -- termios
Exception `LoadError' at /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:63 - cannot load such file -- termios
Exception `LoadError' at /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36 - cannot load such file -- ffi-ncurses
Exception `LoadError' at /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:63 - cannot load such file -- ffi-ncurses
/usr/lib/ruby/vendor_ruby/commander/user_interaction.rb:170: warning: shadowing outer local variable - statement
/usr/lib/ruby/vendor_ruby/commander/user_interaction.rb:342: warning: shadowing outer local variable - str
Exception `LoadError' at /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36 - cannot load such file -- growl
Exception `LoadError' at /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:63 - cannot load such file -- growl
/usr/lib/ruby/vendor_ruby/commander/runner.rb:157: warning: `*' interpreted as argument prefix
/usr/lib/ruby/vendor_ruby/commander/runner.rb:332: warning: `*' interpreted as argument prefix
/usr/lib/ruby/vendor_ruby/commander/runner.rb:400: warning: `*' interpreted as argument prefix
/usr/lib/ruby/vendor_ruby/commander/runner.rb:402: warning: `*' interpreted as argument prefix
/usr/lib/ruby/vendor_ruby/commander/runner.rb:407: warning: `*' interpreted as argument prefix
Exception `NameError' at /home/rmiesen/src-personal/Projects/Active/CodeJig/lib/CodeJig.rb:7 - uninitialized constant Commander::Methods
/home/rmiesen/src-personal/Projects/Active/CodeJig/lib/CodeJig.rb:7:in `<class:Program>': uninitialized constant Commander::Methods (NameError)
    from /home/rmiesen/src-personal/Projects/Active/CodeJig/lib/CodeJig.rb:6:in `<module:CodeJig>'
    from /home/rmiesen/src-personal/Projects/Active/CodeJig/lib/CodeJig.rb:5:in `<top (required)>'
    from ./bin/codeJig:10:in `require_relative'
    from ./bin/codeJig:10:in `<main>'

Is there anything else that could be helpful in getting to the bottom of this problem?

Thanks again for your help Gabriel!

ggilder commented 10 years ago

Thanks for your bug report, it turns out there is an error in the readme! I've updated the docs, but basically what you need to do is add a call to run! at the end of your class run method.

However, I think the backtrace you posted above is due to loading the incorrect version of commander. You may be able to verify this with the following code:

require 'rubygems'
require 'commander'

class MyApplication
  puts Commander::VERSION
  include Commander::Methods
  # rest of file should be the same as before, except put run! at the end of your run method
rmiesen commented 10 years ago

Hi Gabriel.

You are indeed correct on both the needing to add run! to my class definition and there being a versioning issue with the commander plugin I have installed. Using some rudimentary metaprogramming techniques, I was able to determine that the version of the commander plugin was in the /usr/lib/ruby/vendor_ruby/ folder rather than the /usr/lib/gems/ folder. I did some more digging and this turned out to be caused by me installing the commander plugin from both the gem package manager and from Ubuntu's package manager!

After removing the Ubuntu package, everything worked as it should. So this should resolve the issues I was having.

Thanks again for all of your help!

ggilder commented 10 years ago

Cool, glad that helped!