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
-
#acceptor(name, validator = nil, converter = nil, &block) ⇒ Toys::DSL::Tool
Create an acceptor that can be passed into a flag or arg.
-
#alias_tool(word, target) ⇒ Toys::DSL::Tool
Create an alias in the current namespace.
-
#desc(str) ⇒ Toys::DSL::Tool
(also: #short_desc)
Set the short description for the current tool.
-
#disable_argument_parsing ⇒ Toys::DSL::Tool
Disable argument parsing for this tool.
-
#disable_flag(*flags) ⇒ Toys::DSL::Tool
Mark one or more flags as disabled, preventing their use by any subsequent flag definition.
-
#expand(template_class, *args) {|template| ... } ⇒ Toys::DSL::Tool
Expand the given template in the current location.
-
#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.
-
#include(mod, *args) ⇒ Object
Specify that the given module should be mixed into this tool, and its methods made available when running the tool.
-
#include?(mod) ⇒ Boolean?
Determine if the given module/mixin has already been included.
-
#load(path) ⇒ Toys::DSL::Tool
Load another config file or directory, as if its contents were inserted at the current location.
-
#long_desc(*strs) ⇒ Toys::DSL::Tool
Set the long description for the current tool.
-
#mixin(name, &block) ⇒ Toys::DSL::Tool
Create a named mixin module.
-
#optional_arg(key, default: nil, accept: nil, display_name: nil, desc: nil, long_desc: nil) {|arg_dsl| ... } ⇒ Toys::DSL::Tool
(also: #optional)
Add an optional positional argument to the current tool.
-
#remaining_args(key, default: [], accept: nil, display_name: nil, desc: nil, long_desc: nil) {|arg_dsl| ... } ⇒ Toys::DSL::Tool
(also: #remaining)
Specify what should be done with unmatched positional arguments.
-
#required_arg(key, accept: nil, display_name: nil, desc: nil, long_desc: nil) {|arg_dsl| ... } ⇒ Toys::DSL::Tool
(also: #required)
Add a required positional argument to the current tool.
-
#static(key, value = nil) ⇒ Toys::DSL::Tool
Set an option value statically.
-
#template(name, &block) ⇒ Toys::DSL::Tool
Create a named template class.
-
#to_run(&block) ⇒ Toys::DSL::Tool
Specify how to run this tool.
-
#tool(word, &block) ⇒ Toys::DSL::Tool
(also: #name)
Create a subtool.
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.
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.
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."]
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_parsing ⇒ Toys::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.
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.
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.
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 (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.) 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.
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.
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.
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.
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."]
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.
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.
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.
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.
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.
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.
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.
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.
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 |