Module: Toys::DSL::Tool

Defined in:
lib/toys/dsl/tool.rb

Overview

This class defines the DSL for a Toys configuration file.

A Toys configuration defines one or more named tools. It provides syntax for setting the description, defining flags and arguments, specifying how to execute the tool, and requesting mixin modules and other services. It also lets you define subtools, nested arbitrarily deep, using blocks.

Simple example

Create a file called .toys.rb in the current directory, with the following contents:

tool "greet" do
  desc "Prints a simple greeting"

  optional_arg :recipient, default: "world"

  def run
    puts "Hello, #{recipient}!"
  end
end

Now you can execute it using:

toys greet

or try:

toys greet rubyists

Instance Method Summary collapse

Instance Method Details

#acceptor(name, validator = nil, converter = nil, &block) ⇒ Toys::DSL::Tool

Create an acceptor that can be passed into a flag or arg. An acceptor validates and/or converts a string parameter to a Ruby object. This acceptor may, for the current tool, be referenced by the name you provide when you create a flag or arg.

An acceptor contains a validator, which parses and validates the string syntax of an argument, and a converter, which takes the validation results and returns a final value for the context data.

The validator may be either a regular expression or a list of valid inputs.

If the validator is a regular expression, it is matched against the argument string and succeeds only if the expression covers the entire string. The elements of the MatchData (i.e. the string matched, plus any captures) are then passed into the conversion function.

If the validator is an array, the string form of the array elements (i.e. the results of calling to_s on each element) are considered the valid values for the argument. This is useful for enums, for example. In this case, the input is converted to the original array element, and any converter function you provide is ignored.

If you provide no validator, then no validation takes place and all argument strings are considered valid. The string itself is passed on to the converter.

The converter should be a proc that takes as its arguments the results of validation. For example, if you use a regular expression validator, the converter should take a series of strings arguments, the first of which is the full input string, and the rest of which are captures. If you provide no converter, no conversion is done and the input string is considered the final value. You may also provide the converter as a block.

Parameters:

  • name (String)

    The acceptor name.

  • validator (Regexp, Array, nil) (defaults to: nil)

    The validator.

  • converter (Proc, nil) (defaults to: nil)

    The validator.

Returns:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/toys/dsl/tool.rb', line 113

def acceptor(name, validator = nil, converter = nil, &block)
  cur_tool = DSL::Tool.current_tool(self, false)
  return self if cur_tool.nil?
  accept =
    case validator
    when ::Regexp
      Definition::PatternAcceptor.new(name, validator, converter, &block)
    when ::Array
      Definition::EnumAcceptor.new(name, validator)
    when nil
      Definition::Acceptor.new(name, converter, &block)
    else
      raise ToolDefinitionError, "Illegal validator: #{validator.inspect}"
    end
  cur_tool.add_acceptor(accept)
  self
end

#alias_tool(word, target) ⇒ Toys::DSL::Tool

Create an alias in the current namespace.

Parameters:

  • word (String)

    The name of the alias

  • target (String)

    The target of the alias

Returns:



205
206
207
208
# File 'lib/toys/dsl/tool.rb', line 205

def alias_tool(word, target)
  @__loader.make_alias(@__words + [word.to_s], @__words + [target.to_s], @__priority)
  self
end

#desc(str) ⇒ Toys::DSL::Tool Also known as: short_desc

Set the short description for the current tool. The short description is displayed with the tool in a subtool list. You may also use the equivalent method short_desc.

The description is a Utils::WrappableString, which may be word- wrapped when displayed in a help screen. You may pass a Utils::WrappableString directly to this method, or you may pass any input that can be used to construct a wrappable string:

  • If you pass a String, its whitespace will be compacted (i.e. tabs, newlines, and multiple consecutive whitespace will be turned into a single space), and it will be word-wrapped on whitespace.
  • If you pass an Array of Strings, each string will be considered a literal word that cannot be broken, and wrapping will be done across the strings in the array. In this case, whitespace is not compacted.

For example, if you pass in a sentence as a simple string, it may be word wrapped when displayed:

desc "This sentence may be wrapped."

To specify a sentence that should never be word-wrapped, pass it as the sole element of a string array:

desc ["This sentence will not be wrapped."]

Parameters:

Returns:



281
282
283
284
285
# File 'lib/toys/dsl/tool.rb', line 281

def desc(str)
  cur_tool = DSL::Tool.current_tool(self, true)
  cur_tool.desc = str if cur_tool
  self
end

#disable_argument_parsingToys::DSL::Tool

Disable argument parsing for this tool. Arguments will not be parsed and the options will not be populated. Instead, tools can retrieve the full unparsed argument list by calling Tool#args.

This directive is mutually exclusive with any of the directives that declare arguments or flags.

Returns:



561
562
563
564
# File 'lib/toys/dsl/tool.rb', line 561

def disable_argument_parsing
  DSL::Tool.current_tool(self, true)&.disable_argument_parsing
  self
end

#disable_flag(*flags) ⇒ Toys::DSL::Tool

Mark one or more flags as disabled, preventing their use by any subsequent flag definition. This can be used to prevent middleware from defining a particular flag.

Parameters:

  • flags (String...)

    The flags to disable

Returns:



574
575
576
577
# File 'lib/toys/dsl/tool.rb', line 574

def disable_flag(*flags)
  DSL::Tool.current_tool(self, true)&.disable_flag(*flags)
  self
end

#expand(template_class, *args) {|template| ... } ⇒ Toys::DSL::Tool

Expand the given template in the current location.

The template may be specified as a class or a well-known template name. You may also provide arguments to pass to the template.

Parameters:

  • template_class (Class, String, Symbol)

    The template, either as a class or a well-known name.

  • args (Object...)

    Template arguments

Yields:

Returns:



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/toys/dsl/tool.rb', line 233

def expand(template_class, *args)
  cur_tool = DSL::Tool.current_tool(self, true)
  return self if cur_tool.nil?
  name = template_class.to_s
  if template_class.is_a?(::String)
    template_class = cur_tool.resolve_template(template_class)
  elsif template_class.is_a?(::Symbol)
    template_class = @__loader.resolve_standard_template(name)
  end
  if template_class.nil?
    raise ToolDefinitionError, "Template not found: #{name.inspect}"
  end
  template = template_class.new(*args)
  yield template if block_given?
  class_exec(template, &template_class.expander)
  self
end

#flag(key, *flags, accept: nil, default: nil, handler: nil, report_collisions: true, desc: nil, long_desc: nil) {|flag_dsl| ... } ⇒ Toys::DSL::Tool

Add a flag to the current tool. Each flag must specify a key which the script may use to obtain the flag value from the context. You may then provide the flags themselves in OptionParser form.

If the given key is a symbol representing a valid method name, then a helper method is automatically added to retrieve the value. Otherwise, if the key is a string or does not represent a valid method name, the tool can retrieve the value by calling Tool#get.

Attributes of the flag may be passed in as arguments to this method, or set in a block passed to this method. If you provide a block, you can use directives in Flag within the block.

Parameters:

  • key (String, Symbol)

    The key to use to retrieve the value from the execution context.

  • flags (String...)

    The flags in OptionParser format.

  • accept (Object) (defaults to: nil)

    An acceptor that validates and/or converts the value. You may provide either the name of an acceptor you have defined, or one of the default acceptors provided by OptionParser. Optional. If not specified, accepts any value as a string.

  • default (Object) (defaults to: nil)

    The default value. This is the value that will be set in the context if this flag is not provided on the command line. Defaults to nil.

  • handler (Proc, nil) (defaults to: nil)

    An optional handler for setting/updating the value. If given, it should take two arguments, the new given value and the previous value, and it should return the new value that should be set. The default handler simply replaces the previous value. i.e. the default is effectively -> (val, _prev) { val }.

  • report_collisions (Boolean) (defaults to: true)

    Raise an exception if a flag is requested that is already in use or marked as unusable. Default is true.

  • desc (String, Array<String>, Toys::Utils::WrappableString) (defaults to: nil)

    Short description for the flag. See #desc for a description of the allowed formats. Defaults to the empty string.

  • long_desc (Array<String,Array<String>,Toys::Utils::WrappableString>) (defaults to: nil)

    Long description for the flag. See #long_desc for a description of the allowed formats. (But note that this param takes an Array of description lines, rather than a series of arguments.) Defaults to the empty array.

Yield Parameters:

  • flag_dsl (Toys::DSL::Flag)

    An object that lets you configure this flag in a block.

Returns:



357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/toys/dsl/tool.rb', line 357

def flag(key, *flags,
         accept: nil, default: nil, handler: nil,
         report_collisions: true,
         desc: nil, long_desc: nil,
         &block)
  cur_tool = DSL::Tool.current_tool(self, true)
  return self if cur_tool.nil?
  flag_dsl = DSL::Flag.new(flags, accept, default, handler,
                           report_collisions, desc, long_desc)
  flag_dsl.instance_exec(flag_dsl, &block) if block
  flag_dsl._add_to(cur_tool, key)
  DSL::Tool.maybe_add_getter(self, key)
  self
end

#include(mod, *args) ⇒ Object

Specify that the given module should be mixed into this tool, and its methods made available when running the tool.

You may provide either a module, the string name of a mixin that you have defined in this tool or one of its ancestors, or the symbol name of a well-known mixin.

Parameters:

  • mod (Module, Symbol, String)

    Module or module name.

  • args (Object...)

    Arguments to pass to the initializer



603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
# File 'lib/toys/dsl/tool.rb', line 603

def include(mod, *args)
  cur_tool = DSL::Tool.current_tool(self, true)
  return if cur_tool.nil?
  mod = DSL::Tool.resolve_mixin(mod, cur_tool, @__loader)
  if included_modules.include?(mod)
    raise ToolDefinitionError, "Mixin already included: #{mod.name}"
  end
  if mod.respond_to?(:initialization_callback) && mod.initialization_callback
    cur_tool.add_initializer(mod.initialization_callback, *args)
  end
  if mod.respond_to?(:inclusion_callback) && mod.inclusion_callback
    class_exec(*args, &mod.inclusion_callback)
  end
  super(mod)
end

#include?(mod) ⇒ Boolean?

Determine if the given module/mixin has already been included.

You may provide either a module, the string name of a mixin that you have defined in this tool or one of its ancestors, or the symbol name of a well-known mixin.

Parameters:

  • mod (Module, Symbol, String)

    Module or module name.

Returns:

  • (Boolean, nil)

    A boolean value, or nil if the current tool is not active.



630
631
632
633
634
# File 'lib/toys/dsl/tool.rb', line 630

def include?(mod)
  cur_tool = DSL::Tool.current_tool(self, false)
  return if cur_tool.nil?
  super(DSL::Tool.resolve_mixin(mod, cur_tool, @__loader))
end

#load(path) ⇒ Toys::DSL::Tool

Load another config file or directory, as if its contents were inserted at the current location.

Parameters:

  • path (String)

    The file or directory to load.

Returns:



217
218
219
220
# File 'lib/toys/dsl/tool.rb', line 217

def load(path)
  @__loader.load_path(path, @__words, @__remaining_words, @__priority)
  self
end

#long_desc(*strs) ⇒ Toys::DSL::Tool

Set the long description for the current tool. The long description is displayed in the usage documentation for the tool itself.

A long description is a series of descriptions, which are generally displayed in a series of lines/paragraphs. Each individual description uses the form described in the #desc documentation, and may be word-wrapped when displayed. To insert a blank line, include an empty string as one of the descriptions.

Example:

long_desc "This is an initial paragraph that might be word wrapped.",
          "This next paragraph is followed by a blank line.",
          "",
          ["This line will not be wrapped."]

Parameters:

Returns:



308
309
310
311
# File 'lib/toys/dsl/tool.rb', line 308

def long_desc(*strs)
  DSL::Tool.current_tool(self, true)&.append_long_desc(strs)
  self
end

#mixin(name, &block) ⇒ Toys::DSL::Tool

Create a named mixin module. This module may be included by name in this tool or any subtool.

You should pass a block and define methods in that block.

Parameters:

  • name (String)

    Name of the mixin

Returns:



140
141
142
143
144
145
146
147
148
149
150
# File 'lib/toys/dsl/tool.rb', line 140

def mixin(name, &block)
  cur_tool = DSL::Tool.current_tool(self, false)
  if cur_tool
    mixin_mod = ::Module.new do
      include ::Toys::Mixin
    end
    mixin_mod.module_eval(&block)
    cur_tool.add_mixin(name, mixin_mod)
  end
  self
end

#optional_arg(key, default: nil, accept: nil, display_name: nil, desc: nil, long_desc: nil) {|arg_dsl| ... } ⇒ Toys::DSL::Tool Also known as: optional

Add an optional positional argument to the current tool. You must specify a key which the script may use to obtain the argument value from the context. If an optional argument is not given on the command line, the value is set to the given default.

If the given key is a symbol representing a valid method name, then a helper method is automatically added to retrieve the value. Otherwise, if the key is a string or does not represent a valid method name, the tool can retrieve the value by calling Tool#get.

Attributes of the arg may be passed in as arguments to this method, or set in a block passed to this method. If you provide a block, you can use directives in Arg within the block.

Parameters:

  • key (String, Symbol)

    The key to use to retrieve the value from the execution context.

  • default (Object) (defaults to: nil)

    The default value. This is the value that will be set in the context if this argument is not provided on the command line. Defaults to nil.

  • accept (Object) (defaults to: nil)

    An acceptor that validates and/or converts the value. You may provide either the name of an acceptor you have defined, or one of the default acceptors provided by OptionParser. Optional. If not specified, accepts any value as a string.

  • display_name (String) (defaults to: nil)

    A name to use for display (in help text and error reports). Defaults to the key in upper case.

  • desc (String, Array<String>, Toys::Utils::WrappableString) (defaults to: nil)

    Short description for the flag. See #desc for a description of the allowed formats. Defaults to the empty string.

  • long_desc (Array<String,Array<String>,Toys::Utils::WrappableString>) (defaults to: nil)

    Long description for the flag. See #long_desc for a description of the allowed formats. (But note that this param takes an Array of description lines, rather than a series of arguments.) Defaults to the empty array.

Yield Parameters:

  • arg_dsl (Toys::DSL::Arg)

    An object that lets you configure this argument in a block.

Returns:



458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/toys/dsl/tool.rb', line 458

def optional_arg(key,
                 default: nil, accept: nil, display_name: nil,
                 desc: nil, long_desc: nil,
                 &block)
  cur_tool = DSL::Tool.current_tool(self, true)
  return self if cur_tool.nil?
  arg_dsl = DSL::Arg.new(accept, default, display_name, desc, long_desc)
  arg_dsl.instance_exec(arg_dsl, &block) if block
  arg_dsl._add_optional_to(cur_tool, key)
  DSL::Tool.maybe_add_getter(self, key)
  self
end

#remaining_args(key, default: [], accept: nil, display_name: nil, desc: nil, long_desc: nil) {|arg_dsl| ... } ⇒ Toys::DSL::Tool Also known as: remaining

Specify what should be done with unmatched positional arguments. You must specify a key which the script may use to obtain the remaining args from the context.

If the given key is a symbol representing a valid method name, then a helper method is automatically added to retrieve the value. Otherwise, if the key is a string or does not represent a valid method name, the tool can retrieve the value by calling Tool#get.

Attributes of the arg may be passed in as arguments to this method, or set in a block passed to this method. If you provide a block, you can use directives in Arg within the block.

Parameters:

  • key (String, Symbol)

    The key to use to retrieve the value from the execution context.

  • default (Object) (defaults to: [])

    The default value. This is the value that will be set in the context if no unmatched arguments are provided on the command line. Defaults to the empty array [].

  • accept (Object) (defaults to: nil)

    An acceptor that validates and/or converts the value. You may provide either the name of an acceptor you have defined, or one of the default acceptors provided by OptionParser. Optional. If not specified, accepts any value as a string.

  • display_name (String) (defaults to: nil)

    A name to use for display (in help text and error reports). Defaults to the key in upper case.

  • desc (String, Array<String>, Toys::Utils::WrappableString) (defaults to: nil)

    Short description for the flag. See #desc for a description of the allowed formats. Defaults to the empty string.

  • long_desc (Array<String,Array<String>,Toys::Utils::WrappableString>) (defaults to: nil)

    Long description for the flag. See #long_desc for a description of the allowed formats. (But note that this param takes an Array of description lines, rather than a series of arguments.) Defaults to the empty array.

Yield Parameters:

  • arg_dsl (Toys::DSL::Arg)

    An object that lets you configure this argument in a block.

Returns:



509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/toys/dsl/tool.rb', line 509

def remaining_args(key,
                   default: [], accept: nil, display_name: nil,
                   desc: nil, long_desc: nil,
                   &block)
  cur_tool = DSL::Tool.current_tool(self, true)
  return self if cur_tool.nil?
  arg_dsl = DSL::Arg.new(accept, default, display_name, desc, long_desc)
  arg_dsl.instance_exec(arg_dsl, &block) if block
  arg_dsl._set_remaining_on(cur_tool, key)
  DSL::Tool.maybe_add_getter(self, key)
  self
end

#required_arg(key, accept: nil, display_name: nil, desc: nil, long_desc: nil) {|arg_dsl| ... } ⇒ Toys::DSL::Tool Also known as: required

Add a required positional argument to the current tool. You must specify a key which the script may use to obtain the argument value from the context.

If the given key is a symbol representing a valid method name, then a helper method is automatically added to retrieve the value. Otherwise, if the key is a string or does not represent a valid method name, the tool can retrieve the value by calling Tool#get.

Attributes of the arg may be passed in as arguments to this method, or set in a block passed to this method. If you provide a block, you can use directives in Arg within the block.

Parameters:

  • key (String, Symbol)

    The key to use to retrieve the value from the execution context.

  • accept (Object) (defaults to: nil)

    An acceptor that validates and/or converts the value. You may provide either the name of an acceptor you have defined, or one of the default acceptors provided by OptionParser. Optional. If not specified, accepts any value as a string.

  • display_name (String) (defaults to: nil)

    A name to use for display (in help text and error reports). Defaults to the key in upper case.

  • desc (String, Array<String>, Toys::Utils::WrappableString) (defaults to: nil)

    Short description for the flag. See #desc for a description of the allowed formats. Defaults to the empty string.

  • long_desc (Array<String,Array<String>,Toys::Utils::WrappableString>) (defaults to: nil)

    Long description for the flag. See #long_desc for a description of the allowed formats. (But note that this param takes an Array of description lines, rather than a series of arguments.) Defaults to the empty array.

Yield Parameters:

  • arg_dsl (Toys::DSL::Arg)

    An object that lets you configure this argument in a block.

Returns:



406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/toys/dsl/tool.rb', line 406

def required_arg(key,
                 accept: nil, display_name: nil,
                 desc: nil, long_desc: nil,
                 &block)
  cur_tool = DSL::Tool.current_tool(self, true)
  return self if cur_tool.nil?
  arg_dsl = DSL::Arg.new(accept, nil, display_name, desc, long_desc)
  arg_dsl.instance_exec(arg_dsl, &block) if block
  arg_dsl._add_required_to(cur_tool, key)
  DSL::Tool.maybe_add_getter(self, key)
  self
end

#static(key, value = nil) ⇒ Toys::DSL::Tool

Set an option value statically.

If the given key is a symbol representing a valid method name, then a helper method is automatically added to retrieve the value. Otherwise, if the key is a string or does not represent a valid method name, the tool can retrieve the value by calling Tool#get.

Parameters:

  • key (String, Symbol)

    The key to use to retrieve the value from the execution context.

  • value (Object) (defaults to: nil)

    The value to set.

Returns:



536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/toys/dsl/tool.rb', line 536

def static(key, value = nil)
  cur_tool = DSL::Tool.current_tool(self, true)
  return self if cur_tool.nil?
  if key.is_a?(::Hash)
    cur_tool.default_data.merge!(key)
    key.each_key do |k|
      DSL::Tool.maybe_add_getter(self, k)
    end
  else
    cur_tool.default_data[key] = value
    DSL::Tool.maybe_add_getter(self, key)
  end
  self
end

#template(name, &block) ⇒ Toys::DSL::Tool

Create a named template class. This template may be expanded by name in this tool or any subtool.

You should pass a block and define the template in that block. You do not need to include Toys::Template in the block. Otherwise, see Template for information on defining a template. In general, the block should define an initialize method, and call to_expand to define how to expand the template.

Parameters:

  • name (String)

    Name of the template

Returns:



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/toys/dsl/tool.rb', line 165

def template(name, &block)
  cur_tool = DSL::Tool.current_tool(self, false)
  if cur_tool
    template_class = ::Class.new do
      include ::Toys::Template
    end
    template_class.class_eval(&block)
    cur_tool.add_template(name, template_class)
  end
  self
end

#to_run(&block) ⇒ Toys::DSL::Tool

Specify how to run this tool. Typically you do this by defining a method namd run. Alternatively, you can pass a block to this method. You may want to do this if your method needs access to local variables in the lexical scope.

Returns:



587
588
589
590
# File 'lib/toys/dsl/tool.rb', line 587

def to_run(&block)
  define_method(:run, &block)
  self
end

#tool(word, &block) ⇒ Toys::DSL::Tool Also known as: name

Create a subtool. You must provide a block defining the subtool.

If the subtool is already defined (either as a tool or a namespace), the old definition is discarded and replaced with the new definition.

Parameters:

  • word (String)

    The name of the subtool

Returns:



186
187
188
189
190
191
192
193
194
195
# File 'lib/toys/dsl/tool.rb', line 186

def tool(word, &block)
  word = word.to_s
  subtool_words = @__words + [word]
  next_remaining = Loader.next_remaining_words(@__remaining_words, word)
  subtool_class = @__loader.get_tool_definition(subtool_words, @__priority).tool_class
  DSL::Tool.prepare(subtool_class, next_remaining, @__path) do
    subtool_class.class_eval(&block)
  end
  self
end