require 'rprogram/options' require 'rprogram/option_list' module RProgram class Task include Options # # Creates a new Task object with the given _options_. If a block is # given, it is passed the newly created Task object. # # Task.new(:test => 'example', :count => 2, :verbose => true) # # Task.new(:help => true) do |task| # ... # end # def initialize(options={},&block) @subtasks = {} @options = options block.call(self) if block end # # Returns the compiled _arguments_ from a Task object created # with the given _options_ and _block_. # # MyTask.arguments(:verbose => true, :count => 2) # # MyTask.arguments do |task| # task.verbose = true # task.file = 'output.txt' # end # def self.arguments(options={},&block) self.new(options,&block).arguments end # # Returns an array of _arguments_ generated from all the leading # non-options of the task and it's sub-tasks. # def leading_non_options args = [] # add the task leading non-options @options.each do |name,value| non_opt = get_non_option(name) if (non_opt && non_opt.leading) args += non_opt.arguments(value) end end # add all leading subtask non-options @subtasks.each_value do |task| args += task.leading_non_options end return args end # # Returns an array of _arguments_ generated from all the options # of the task and it's sub-tasks. # def options args = [] # add all subtask options @subtasks.each_value do |task| args += task.arguments end # add the task options @options.each do |name,value| opt = get_option(name) args += opt.arguments(value) if opt end return args end # # Returns an array of _arguments_ generated from all the tailing # non-options of the task and it's sub-tasks. # def tailing_non_options args = [] # add all tailing subtask non-options @subtasks.each_value do |task| args += task.tailing_non_options end # add the task tailing non-options @options.each do |name,value| non_opt = get_non_option(name) if (non_opt && non_opt.tailing) args += non_opt.arguments(value) end end return args end # # Returns an array of _arguments_ compiled from the _arguments_ of # the tasks leading non_options, options and tailing non-options. # def arguments leading_non_options + options + tailing_non_options end protected # # Defines a sub-task of _name_ and class of _task_. # # subtask :extra, ExtraTask # def self.subtask(name,task) class_eval %{ def #{name}(options={},&block) if @subtasks[#{name.dump}] @subtasks[#{name.dump}].options.merge!(options) block.call(@subtasks[#{name.dump}]) if block else @subtasks[#{name.dump}] = #{task}.new(options,&block) end return @subtasks[#{name.dump}] end } end # # Defines a non-option with the given _opts_. # # non_option :name => 'input_file', :tailing => true # # non_option :name => 'file', :tailing => true, :multiple => true # def self.non_option(opts={}) name = opts[:name].to_sym self.non_options[name] = NonOption.new(opts) class_def(name) do if opts[:multiple] @options[name] ||= [] else @options[name] end end class_def("#{name}=") do |value| @options[name] = value end end # # Defines a long-option with the specified _opts_. # # _opts_ must contain the following keys: # :flag:: The flag to use for the option. # # _opts_ may also contain the following keys: # :name:: The name of the option. Defaults to the # flag_namify'ed form of opts[:flag], if not # given. # :multiply:: Specifies that the option may appear multiple # times in the arguments. # :sub_options:: Specifies that the option contains multiple # sub-options. # # long_option :flag => '--output' # # long_option :flag => '-f', :name => :file # def self.long_option(opts={},&block) opts[:name] ||= Task.flag_namify(opts[:flag]) define_option(opts,&block) end # # Defines a short_option with the specified _opts_. # # _opts_ must contain the following keys: # :name:: The name of the option. # :flag:: The flag to use for the option. # # _opts_ may also contain the following keys: # :multiply:: Specifies that the option may appear multiple # times in the arguments. # :sub_options:: Specifies that the option contains multiple # sub-options. # # short_option :flag => '-c', :name => :count # def self.short_option(opts,&block) define_option(opts,&block) end # # Defines an option with the specified _opts_ and the given _block_. # # _opts_ must contain the following keys: # :name:: The name of the option. # :flag:: The flag to use for the option. # # _opts_ may also contain the following keys: # :multiply:: Specifies that the option may appear multiple # times in the arguments. # :sub_options:: Specifies that the option contains multiple # sub-options. # def self.define_option(opts,&block) method_name = opts[:name].to_sym self.options[method_name] = Option.new(opts,&block) class_def(method_name) do if opts[:sub_options] @options[method_name] ||= OptionList.new elsif opts[:multiple] @options[method_name] ||= [] else @options[method_name] end end class_def("#{method_name}=") do |value| if opts[:sub_options] @options[method_name] = OptionList.new(value) else @options[method_name] = value end end end # # Converts a long-option flag to a Ruby method name. # # Task.flag_namify('--output-file') # => "output_file" # def Task.flag_namify(flag) flag = flag.to_s # remove leading dashes if flag =~ /^--/ method_name = flag[2..-1] elsif flag =~ /^-/ method_name = flag[1..-1] else method_name = flag end # replace remaining dashes with underscores return method_name.gsub(/[-.]/,'_') end end end