Thor Documentation really needs to be improved. The following is gleaned from hours of reading code, specs, issues, and google-fu. I can't say that this is the way it's supposed to work, but it certainly will work when setup this way.
When a class inherits from Thor, it gains a few important Class methods.
- register. This allows you to register a new subcommand as a task
- class_options. This gives you a hash of all the class options.
- tasks. This gives you a hash of all the defined tasks.
We can use those to include tasks from many classes into a single runner.
I included a few extra files so that you could see an entire working thor app. Grantesd it doesn't do much...
#############################################################
#my_app/bin/my_app #
# #
#This file is the executable that requires the MyApp module,#
#then starts the runner. #
#############################################################
#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include(File.dirname(__FILE__) + '/../lib')
require "rubygems" # ruby1.9 doesn't "require" it though
require "my_app"
MyApp::Runner.start
########################################################
#my_app/lib/my_app.rb #
# #
#This is the main module, used to control the requires #
#the my_app requires should be done last to make sure #
#everything else is defined first. #
########################################################
require 'thor'
require 'thor/group'
module MyApp
#include other helper apps here
require 'my_app/runner' #first so all subcommands can register
require 'my_app/more'
require 'my_app/config'
end
###################################################################
#my_app/lib/my_app/runner.rb #
# #
#This is the main runner class. #
#ALL class_methods should be defined here except for Thor::Groups #
###################################################################
class MyApp::Runner < ::Thor
class_option :config, :type => :string,
:desc => "configuration file. accepts ENV $MYAPP_CONFIG_FILE",
:default => ENV["MYAPP_CONFIG_FILE"] || "~/.my_apprc"
method_option :rf, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "foo","prints foo"
def foo
puts "foo" * options.rf
end
end
#######################################################################
#my_app/lib/my_app/more.rb #
# #
#A Thor Group example. #
#Class_options defined for a Thor Group become method_options when #
#used as a subcommand. #
#Since MyApp::Runner is already defined when this class is evaluated #
#It can automatcially register itself as a subcommand for the runner, #
#######################################################################
class Revamp::Init < ::Thor::Group
class_option :repeat, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "prints woot"
def woot
puts "woot! " * options.repeat
end
desc "prints toow"
def toow
puts "!toow" * options.repeat
end
#This line registers this group as a sub command of the runner
MyApp::Runner.register MyApp::More, :more, "more", "print more stuff"
#This line copies the class_options for this class to the method_options of the :more task
MyApp::Runner.tasks["more"].options = MyApp::More.class_options
end
#####################################################################
#my_app/lib/my_app/config.rb #
# #
#For normal Thor classes, each task must be registered individually #
#####################################################################
class MyApp::Config < Thor
method_option :dr, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "show_default", "show the default config"
def show_default
puts "default " * options.dr
end
MyApp::Runner.register MyApp::Config, :show_default, "show_default", "print default"
MyApp::Runner.tasks["show_default"].options = MyApp::Config.tasks["show_default"].options
method_option :cr, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "show_config", "show the config"
def show_config
puts "config " * options.cr
end
MyApp::Runner.register MyApp::Config, :show_config, "show_config", "print config"
MyApp::Runner.tasks["show_config"].options = MyApp::Config.tasks["show_config"].options
end