toys-0.21.0/0000755000004100000410000000000015164300675012627 5ustar www-datawww-datatoys-0.21.0/bin/0000755000004100000410000000000015164300675013377 5ustar www-datawww-datatoys-0.21.0/bin/toys0000755000004100000410000000053215164300675014323 0ustar www-datawww-data#!/usr/bin/env ruby # frozen_string_literal: true ::ENV["TOYS_BIN_PATH"] ||= ::File.absolute_path(__FILE__) ::ENV["TOYS_LIB_PATH"] ||= ::File.absolute_path(::File.join(::File.dirname(__dir__), "lib")) $LOAD_PATH.delete(::ENV["TOYS_LIB_PATH"]) $LOAD_PATH.unshift(::ENV["TOYS_LIB_PATH"]) require "toys" exit(::Toys::StandardCLI.new.run(::ARGV)) toys-0.21.0/core-docs/0000755000004100000410000000000015164300675014505 5ustar www-datawww-datatoys-0.21.0/core-docs/toys/0000755000004100000410000000000015164300675015503 5ustar www-datawww-datatoys-0.21.0/core-docs/toys/source_info.rb0000644000004100000410000002437015164300675020351 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # Information about the source of a tool, such as the file, git repository, # or block that defined it. # # This object represents a source of tool information and definitions. Such a # source could include: # # * A toys directory # * A single toys file # * A file or directory loaded from git # * A file or directory loaded from a gem # * A config block passed directly to the CLI # * A tool block within a toys file # # The SourceInfo provides information such as the tool's context directory, # and locates data and lib directories appropriate to the tool. It also # locates the tool's source code so it can be reported when an error occurs. # # Each tool has a unique SourceInfo with all the information specific to that # tool. Additionally, SourceInfo objects are arranged in a containment # hierarchy. For example, a SourceInfo object representing a toys files could # have a parent representing a toys directory, and an object representing a # tool block could have a parent representing an enclosing block or a file. # # Child SourceInfo objects generally inherit some attributes of their parent. # For example, the `.toys` directory in a project directory defines the # context directory as that project directory. Then all tools defined under # that directory will share that context directory, so all SourceInfo objects # descending from that root will inherit that value (unless it's changed # explicitly). # # SourceInfo objects can be obtained in the DSL from # {Toys::DSL::Tool#source_info} or at runtime by getting the # {Toys::Context::Key::TOOL_SOURCE} key. However, they are created internally # by the Loader and should not be created manually. # class SourceInfo ## # The parent of this SourceInfo. # # @return [Toys::SourceInfo] The parent. # @return [nil] if this SourceInfo is a root. # attr_reader :parent ## # The root ancestor of this SourceInfo. This generally represents a source # that was added directly to a CLI in code. # # @return [Toys::SourceInfo] The root ancestor. # attr_reader :root ## # The priority of tools defined by this source. Higher values indicate a # higher priority. Lower priority values could be negative. # # @return [Integer] The priority. # attr_reader :priority ## # The context directory path (normally the directory containing the # toplevel toys file or directory). # # This is not affected by setting a custom context directory for a tool. # # @return [String] The context directory path. # @return [nil] if there is no context directory (perhaps because the root # source was a block) # attr_reader :context_directory ## # The source, which may be a path or a proc depending on the {#source_type}. # # @return [String] Path to the source file or directory. # @return [Proc] The block serving as the source. # attr_reader :source ## # The type of source. This could be: # # * `:file`, representing a single toys file. The {#source} will be the # filesystem path to that file. # * `:directory`, representing a toys directory. The {#source} will be the # filesystem path to that directory. # * `:proc`, representing a proc, which could be a toplevel block added # directly to a CLI, a `tool` block within a toys file, or a block within # another block. The {#source} will be the proc itself. # # @return [:file,:directory,:proc] # attr_reader :source_type ## # The path of the current source file or directory. # # This could be set even if {#source_type} is `:proc`, if that proc is # defined within a toys file. The only time this is not set is if the # source is added directly to a CLI in a code block. # # @return [String] The source path # @return [nil] if this source has no file system path. # attr_reader :source_path ## # The source proc. This is set if {#source_type} is `:proc`. # # @return [Proc] The source proc # @return [nil] if this source has no proc. # attr_reader :source_proc ## # The git remote. This is set if the source, or one of its ancestors, comes # from git. # # @return [String] The git remote # @return [nil] if this source is not fron git. # attr_reader :git_remote ## # The git path. This is set if the source, or one of its ancestors, comes # from git. # # @return [String] The git path. This could be the empty string. # @return [nil] if this source is not fron git. # attr_reader :git_path ## # The git commit. This is set if the source, or one of its ancestors, comes # from git. # # @return [String] The git commit. # @return [nil] if this source is not fron git. # attr_reader :git_commit ## # The gem name. This is set if the source, or one of its ancestors, comes # from a gem. # # @return [String] The gem name. # @return [nil] if this source is not from a gem. # attr_reader :gem_name ## # The gem version. This is set if the source, or one of its ancestors, # comes from a gem. # # @return [Gem::Version] The gem version. # @return [nil] if this source is not from a gem. # attr_reader :gem_version ## # The path within the gem, including the toys root directory in the gem. # # @return [String] The path. # @return [nil] if this source is not from a gem. # attr_reader :gem_path ## # A user-visible name of this source. # # @return [String] # attr_reader :source_name alias to_s source_name ## # Locate the given data file or directory and return an absolute path. # # @param path [String] The relative path to find # @param type [nil,:file,:directory] Type of file system object to find, # or nil (the default) to return any type. # @return [String] Absolute path of the resulting data. # @return [nil] if the data was not found. # def find_data(path, type: nil) # Source available in the toys-core gem end ## # Apply all lib paths in order from high to low priority # # @return [self] # def apply_lib_paths # Source available in the toys-core gem end ## # Create a SourceInfo. # # @private This interface is internal and subject to change without warning. # def initialize(parent, priority, context_directory, source_type, source_path, source_proc, git_remote, git_path, git_commit, gem_name, gem_version, gem_path, source_name, data_dir_name, lib_dir_name) # Source available in the toys-core gem end ## # Create a child SourceInfo relative to the parent path. # # @private This interface is internal and subject to change without warning. # def relative_child(filename, source_name: nil) # Source available in the toys-core gem end ## # Create a child SourceInfo with an absolute path. # # @private This interface is internal and subject to change without warning. # def absolute_child(child_path, source_name: nil) # Source available in the toys-core gem end ## # Create a child SourceInfo with a git source. # # @private This interface is internal and subject to change without warning. # def git_child(child_git_remote, child_git_path, child_git_commit, child_path, source_name: nil) # Source available in the toys-core gem end ## # Create a child SourceInfo with a gem source. # # @private This interface is internal and subject to change without warning. # def gem_child(child_gem_name, child_gem_version, child_gem_path, child_path, source_name: nil) # Source available in the toys-core gem end ## # Create a proc child SourceInfo # # @private This interface is internal and subject to change without warning. # def proc_child(child_proc, source_name: nil) # Source available in the toys-core gem end ## # Create a root source info for a file path. # # @private This interface is internal and subject to change without warning. # def self.create_path_root(source_path, priority, context_directory: nil, data_dir_name: nil, lib_dir_name: nil, source_name: nil) # Source available in the toys-core gem end ## # Create a root source info for a cached git repo. # # @private This interface is internal and subject to change without warning. # def self.create_git_root(git_remote, git_path, git_commit, source_path, priority, context_directory: nil, data_dir_name: nil, lib_dir_name: nil, source_name: nil) # Source available in the toys-core gem end ## # Create a root source info for a loaded gem. # # @private This interface is internal and subject to change without warning. # def self.create_gem_root(gem_name, gem_version, gem_path, source_path, priority, context_directory: nil, data_dir_name: nil, lib_dir_name: nil, source_name: nil) # Source available in the toys-core gem end ## # Create a root source info for a proc. # # @private This interface is internal and subject to change without warning. # def self.create_proc_root(source_proc, priority, context_directory: nil, data_dir_name: nil, lib_dir_name: nil, source_name: nil) # Source available in the toys-core gem end ## # Check a path and determine the canonical path and type. # # @private This interface is internal and subject to change without warning. # def self.check_path(path, lenient) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/mixin.rb0000644000004100000410000001173715164300675017165 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A mixin definition. Mixin modules should include this module. # # A mixin is a collection of methods that are available to be called from a # tool implementation (i.e. its run method). The mixin is added to the tool # class, so it has access to the same methods that can be called by the tool, # such as {Toys::Context#get}. # # ### Usage # # To create a mixin, define a module, and include this module. Then define # the methods you want to be available. # # If you want to perform some initialization specific to the mixin, you can # provide an *initializer* block and/or an *inclusion* block. These can be # specified by calling the module methods defined in # {Toys::Mixin::ModuleMethods}. # # The initializer block is called when the tool context is instantiated # in preparation for execution. It has access to context methods such as # {Toys::Context#get}, and can perform setup for the tool execution itself, # such as initializing some persistent state and storing it in the tool using # {Toys::Context#set}. The initializer block is passed any extra arguments # that were provided to the `include` directive. Define the initializer by # calling {Toys::Mixin::ModuleMethods#on_initialize}. # # The inclusion block is called in the context of your tool class when your # mixin is included. It is also passed any extra arguments that were provided # to the `include` directive. It can be used to issue directives to define # tools or other objects in the DSL, or even enhance the DSL by defining DSL # methods specific to the mixin. Define the inclusion block by calling # {Toys::Mixin::ModuleMethods#on_include}. # # ### Example # # This is an example that implements a simple counter. Whenever the counter # is incremented, a log message is emitted. The tool can also retrieve the # final counter value. # # # Define a mixin by creating a module that includes Toys::Mixin # module MyCounterMixin # include Toys::Mixin # # # Initialize the counter. Notice that the initializer is evaluated # # in the context of the runtime context, so has access to the runtime # # context state. # on_initialize do |start = 0| # set(:counter_value, start) # end # # # Mixin methods are evaluated in the runtime context and so have # # access to the runtime context state, just as if you had defined # # them in your tool. # def counter_value # get(:counter_value) # end # # def increment # set(:counter_value, counter_value + 1) # logger.info("Incremented counter") # end # end # # Now we can use it from a tool: # # tool "count-up" do # # Pass 1 as an extra argument to the mixin initializer # include MyCounterMixin, 1 # # def run # # Mixin methods can be called. # 5.times { increment } # puts "Final value is #{counter_value}" # end # end # module Mixin ## # Create a mixin module with the given block. # # @param block [Proc] Defines the mixin module. # @return [Class] # def self.create(&block) # Source available in the toys-core gem end ## # **_Defined in the toys-core gem_** # # Methods that will be added to a mixin module object. # module ModuleMethods ## # Set the initializer for this mixin. This block is evaluated in the # runtime context before execution, and is passed any arguments provided # to the `include` directive. It can perform any runtime initialization # needed by the mixin. # # @param block [Proc] Sets the initializer proc. # @return [self] # def on_initialize(&block) # Source available in the toys-core gem end ## # The initializer proc for this mixin. This proc is evaluated in the # runtime context before execution, and is passed any arguments provided # to the `include` directive. It can perform any runtime initialization # needed by the mixin. # # @return [Proc] The iniitiliazer for this mixin. # attr_accessor :initializer ## # Set an inclusion proc for this mixin. This block is evaluated in the # tool class immediately after the mixin is included, and is passed any # arguments provided to the `include` directive. # # @param block [Proc] Sets the inclusion proc. # @return [self] # def on_include(&block) # Source available in the toys-core gem end ## # The inclusion proc for this mixin. This block is evaluated in the tool # class immediately after the mixin is included, and is passed any # arguments provided to the `include` directive. # # @return [Proc] The inclusion procedure for this mixin. # attr_accessor :inclusion end end end toys-0.21.0/core-docs/toys/positional_arg.rb0000644000004100000410000001112315164300675021040 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # Representation of a formal positional argument # class PositionalArg ## # Create a PositionalArg definition. # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param type [Symbol] The type of arg. Valid values are `:required`, # `:optional`, and `:remaining`. # @param accept [Object] An acceptor that validates and/or converts the # value. See {Toys::Acceptor.create} for recognized formats. Optional. # If not specified, defaults to {Toys::Acceptor::DEFAULT}. # @param complete [Object] A specifier for shell tab completion. See # {Toys::Completion.create} for recognized formats. # @param display_name [String] A name to use for display (in help text and # error reports). Defaults to the key in upper case. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::ToolDefintion#desc} for a # description of the allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::ToolDefintion#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. # @return [Toys::PositionalArg] # def self.create(key, type, accept: nil, default: nil, complete: nil, desc: nil, long_desc: nil, display_name: nil) # Source available in the toys-core gem end ## # The key for this arg. # @return [Symbol] # attr_reader :key ## # Type of this argument. # @return [:required,:optional,:remaining] # attr_reader :type ## # The effective acceptor. # @return [Toys::Acceptor::Base] # attr_accessor :acceptor ## # The default value, which may be `nil`. # @return [Object] # attr_reader :default ## # The proc that determines shell completions for the value. # @return [Proc,Toys::Completion::Base] # attr_reader :completion ## # The short description string. # # When reading, this is always returned as a {Toys::WrappableString}. # # When setting, the description may be provided as any of the following: # * A {Toys::WrappableString}. # * A normal String, which will be transformed into a # {Toys::WrappableString} using spaces as word delimiters. # * An Array of String, which will be transformed into a # {Toys::WrappableString} where each array element represents an # individual word for wrapping. # # @return [Toys::WrappableString] # attr_reader :desc ## # The long description strings. # # When reading, this is returned as an Array of {Toys::WrappableString} # representing the lines in the description. # # When setting, the description must be provided as an Array where *each # element* may be any of the following: # * A {Toys::WrappableString} representing one line. # * A normal String representing a line. This will be transformed into a # {Toys::WrappableString} using spaces as word delimiters. # * An Array of String representing a line. This will be transformed into # a {Toys::WrappableString} where each array element represents an # individual word for wrapping. # # @return [Array] # attr_reader :long_desc ## # The displayable name. # @return [String] # attr_accessor :display_name ## # Set the short description string. # # See {#desc} for details. # # @param desc [Toys::WrappableString,String,Array] # def desc=(desc) # Source available in the toys-core gem end ## # Set the long description strings. # # See {#long_desc} for details. # # @param long_desc [Array>] # def long_desc=(long_desc) # Source available in the toys-core gem end ## # Append long description strings. # # You must pass an array of lines in the long description. See {#long_desc} # for details on how each line may be represented. # # @param long_desc [Array>] # @return [self] # def append_long_desc(long_desc) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/standard_mixins/0000755000004100000410000000000015164300675020672 5ustar www-datawww-datatoys-0.21.0/core-docs/toys/standard_mixins/highline.rb0000644000004100000410000000724215164300675023013 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # A mixin that provides access to the capabilities of the highline gem. # # This mixin requires the highline gem, version 2.0 or later. It will # attempt to install the gem if it is not available. # # You may make these methods available to your tool by including the # following directive in your tool configuration: # # include :highline # # A HighLine object will then be available by calling the {#highline} # method. For information on using this object, see the # [Highline documentation](https://www.rubydoc.info/gems/highline). Some of # the most common HighLine methods, such as `say`, are also mixed into the # tool and can be called directly. # # You can configure the HighLine object by passing options to the `include` # directive. For example: # # include :highline, my_stdin, my_stdout # # The arguments will be passed on to the # [HighLine constructor](https://www.rubydoc.info/gems/highline/HighLine:initialize). # module Highline include Mixin ## # Context key for the highline object. # @return [Object] # KEY = ::Object.new.freeze ## # A tool-wide [HighLine](https://www.rubydoc.info/gems/highline/HighLine) # instance # @return [::HighLine] # def highline # Source available in the toys-core gem end ## # Calls [HighLine#agree](https://www.rubydoc.info/gems/highline/HighLine:agree) # def agree(...) # Source available in the toys-core gem end ## # Calls [HighLine#ask](https://www.rubydoc.info/gems/highline/HighLine:ask) # def ask(...) # Source available in the toys-core gem end ## # Calls [HighLine#choose](https://www.rubydoc.info/gems/highline/HighLine:choose) # def choose(...) # Source available in the toys-core gem end ## # Calls [HighLine#list](https://www.rubydoc.info/gems/highline/HighLine:list) # def list(...) # Source available in the toys-core gem end ## # Calls [HighLine#say](https://www.rubydoc.info/gems/highline/HighLine:say) # def say(...) # Source available in the toys-core gem end ## # Calls [HighLine#indent](https://www.rubydoc.info/gems/highline/HighLine:indent) # def indent(...) # Source available in the toys-core gem end ## # Calls [HighLine#newline](https://www.rubydoc.info/gems/highline/HighLine:newline) # def newline # Source available in the toys-core gem end ## # Calls [HighLine#puts](https://www.rubydoc.info/gems/highline/HighLine:puts) # def puts(*args) # Source available in the toys-core gem end ## # Calls [HighLine#color](https://www.rubydoc.info/gems/highline/HighLine:color) # def color(*args) # Source available in the toys-core gem end ## # Calls [HighLine#color_code](https://www.rubydoc.info/gems/highline/HighLine:color_code) # def color_code(*args) # Source available in the toys-core gem end ## # Calls [HighLine#uncolor](https://www.rubydoc.info/gems/highline/HighLine:uncolor) # def uncolor(*args) # Source available in the toys-core gem end ## # Calls [HighLine#new_scope](https://www.rubydoc.info/gems/highline/HighLine:new_scope) # def new_scope # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_mixins/terminal.rb0000644000004100000410000001130115164300675023026 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # A mixin that provides a simple terminal. It includes a set of methods # that produce styled output, get user input, and otherwise interact with # the user's terminal. This mixin is not as richly featured as other mixins # such as Highline, but it has no gem dependencies so is ideal for basic # cases. # # You may make these methods available to your tool by including the # following directive in your tool configuration: # # include :terminal # # A Terminal object will then be available by calling the {#terminal} # method. For information on using this object, see the documentation for # {Toys::Utils::Terminal}. Some of the most useful methods are also mixed # into the tool and can be called directly. # # You can configure the Terminal object by passing options to the `include` # directive. For example: # # include :terminal, styled: true # # The arguments will be passed on to {Toys::Utils::Terminal#initialize}. # module Terminal include Mixin ## # Context key for the terminal object. # @return [Object] # KEY = ::Object.new.freeze ## # A tool-wide terminal instance # @return [Toys::Utils::Terminal] # def terminal # Source available in the toys-core gem end ## # Write a line, appending a newline if one is not already present. # # @see Toys::Utils::Terminal#puts # # @param str [String] The line to write # @param styles [Symbol,String,Array...] Styles to apply to the # entire line. # @return [self] # def puts(str = "", *styles) # Source available in the toys-core gem end alias say puts ## # Write a partial line without appending a newline. # # @see Toys::Utils::Terminal#write # # @param str [String] The line to write # @param styles [Symbol,String,Array...] Styles to apply to the # partial line. # @return [self] # def write(str = "", *styles) # Source available in the toys-core gem end ## # Ask a question and get a response. # # @see Toys::Utils::Terminal#ask # # @param prompt [String] Required prompt string. # @param styles [Symbol,String,Array...] Styles to apply to the # prompt. # @param default [String,nil] Default value, or `nil` for no default. # Uses `nil` if not specified. # @param trailing_text [:default,String,nil] Trailing text appended to # the prompt, `nil` for none, or `:default` to show the default. # @return [String] # def ask(prompt, *styles, default: nil, trailing_text: :default) # Source available in the toys-core gem end ## # Confirm with the user. # # @see Toys::Utils::Terminal#confirm # # @param prompt [String] Prompt string. Defaults to `"Proceed?"`. # @param styles [Symbol,String,Array...] Styles to apply to the # prompt. # @param default [Boolean,nil] Default value, or `nil` for no default. # Uses `nil` if not specified. # @return [Boolean] # def confirm(prompt = "Proceed?", *styles, default: nil) # Source available in the toys-core gem end ## # Display a spinner during a task. You should provide a block that # performs the long-running task. While the block is executing, a # spinner will be displayed. # # @see Toys::Utils::Terminal#spinner # # @param leading_text [String] Optional leading string to display to the # left of the spinner. Default is the empty string. # @param frame_length [Float] Length of a single frame, in seconds. # Defaults to {Toys::Utils::Terminal::DEFAULT_SPINNER_FRAME_LENGTH}. # @param frames [Array] An array of frames. Defaults to # {Toys::Utils::Terminal::DEFAULT_SPINNER_FRAMES}. # @param style [Symbol,Array] A terminal style or array of styles # to apply to all frames in the spinner. Defaults to empty, # @param final_text [String] Optional final string to display when the # spinner is complete. Default is the empty string. A common practice # is to set this to newline. # @return [Object] The return value of the block. # def spinner(leading_text: "", final_text: "", frame_length: nil, frames: nil, style: nil, &block) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_mixins/bundler.rb0000644000004100000410000001125015164300675022651 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # Ensures that a bundle is installed and set up when this tool is run. # # This is the normal recommended way to use [bundler](https://bundler.io) # with Toys. Including this mixin in a tool will cause Toys to ensure that # the bundle is installed and available during tool execution. For example: # # tool "run-rails" do # include :bundler # def run # # Note: no "bundle exec" required because Toys has already # # installed and loaded the bundle. # exec "rails s" # end # end # # ### Customization # # The following parameters can be passed when including this mixin: # # * `:static` (Boolean) Has the same effect as passing `:static` to the # `:setup` parameter. This is present largely for historical # compatibility, but it is supported and _not_ deprecated. # # * `:setup` (:auto,:manual,:static) A symbol indicating when the bundle # should be installed. Possible values are: # # * `:auto` - (Default) Installs the bundle just before the tool runs. # * `:static` - Installs the bundle immediately when defining the # tool. # * `:manual` - Does not install the bundle, but defines the methods # `bundler_setup` and `bundler_setup?` in the tool. The tool can # call `bundler_setup` to install the bundle, optionally passing # any of the remaining keyword arguments below to override the # corresponding mixin parameters. The `bundler_setup?` method can # be queried to determine whether the bundle has been set up yet. # # * `:groups` (Array\) The groups to include in setup. # # * `:gemfile_path` (String) The path to the Gemfile to use. If `nil` or # not given, the `:search_dirs` will be searched for a Gemfile. # # * `:search_dirs` (String,Symbol,Array\) Directories to # search for a Gemfile. # # You can pass full directory paths, and/or any of the following: # * `:context` - the current context directory. # * `:current` - the current working directory. # * `:toys` - the Toys directory containing the tool definition, and # any of its parents within the Toys directory hierarchy. # # The default is to search `[:toys, :context, :current]` in that order. # See {DEFAULT_SEARCH_DIRS}. # # For most directories, the bundler mixin will look for the files # ".gems.rb", "gems.rb", and "Gemfile", in that order. In `:toys` # directories, it will look only for ".gems.rb" and "Gemfile", in that # order. These can be overridden by setting the `:gemfile_names` and/or # `:toys_gemfile_names` arguments. # # * `:gemfile_names` (Array\) File names that are recognized as # Gemfiles when searching in directories other than Toys directories. # Defaults to {Toys::Utils::Gems::DEFAULT_GEMFILE_NAMES}. # # * `:toys_gemfile_names` (Array\) File names that are # recognized as Gemfiles when searching in Toys directories. # Defaults to {DEFAULT_TOYS_GEMFILE_NAMES}. # # * `:on_missing` (Symbol) What to do if a needed gem is not installed. # # Supported values: # * `:confirm` - prompt the user on whether to install (default). # * `:error` - raise an exception. # * `:install` - just install the gem. # # * `:on_conflict` (Symbol) What to do if bundler has already been run # with a different Gemfile. # # Supported values: # * `:error` - raise an exception (default). # * `:ignore` - just silently proceed without bundling again. # * `:warn` - print a warning and proceed without bundling again. # # * `:retries` (Integer) Number of times to retry bundler operations # (optional) # # * `:terminal` (Toys::Utils::Terminal) Terminal to use (optional) # # * `:input` (IO) Input IO (optional, defaults to STDIN) # # * `:output` (IO) Output IO (optional, defaults to STDOUT) # module Bundler include Mixin ## # Default search directories for Gemfiles. # @return [Array] # DEFAULT_SEARCH_DIRS = [:toys, :context, :current].freeze ## # The gemfile names that are searched by default in Toys directories. # @return [Array] # DEFAULT_TOYS_GEMFILE_NAMES = [".gems.rb", "Gemfile"].freeze end end end toys-0.21.0/core-docs/toys/standard_mixins/pager.rb0000644000004100000410000000230415164300675022314 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # A mixin that provides a pager. # # This mixin provides an instance of {Toys::Utils::Pager}, which invokes # an external pager for output. # # You can also pass additional keyword arguments to the `include` directive # to configure the pager object. These will be passed on to # {Toys::Utils::Pager#initialize}. # # @example # # include :pager # # def run # pager do |io| # io.puts "A long string\n" # end # end # module Pager include Mixin ## # Context key for the Pager object. # @return [Object] # KEY = ::Object.new.freeze ## # Access the Pager. # # If *no* block is given, returns the pager object. # # If a block is given, the pager is executed with the given block, and # the exit code of the pager process is returned. # # @return [Toys::Utils::Pager] if no block is given. # @return [Integer] if a block is given. # def pager(&block) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_mixins/fileutils.rb0000644000004100000410000000070015164300675023214 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # A module that provides all methods in the "fileutils" standard library. # # You may make the methods in the `FileUtils` standard library module # available to your tool by including the following directive in your tool # configuration: # # include :fileutils # module Fileutils include Mixin end end end toys-0.21.0/core-docs/toys/standard_mixins/git_cache.rb0000644000004100000410000000167515164300675023136 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # A mixin that provides a git cache. # # This mixin provides an instance of {Toys::Utils::GitCache}, providing # cached access to files from a remote git repo. # # @example # # include :git_cache # # def run # # Pull and cache the HEAD commit from the Toys repo. # dir = git_cache.get("https://github.com/dazuma/toys.git") # # Display the contents of the readme file. # puts File.read(File.join(dir, "README.md")) # end # module GitCache include Mixin ## # Context key for the GitCache object. # @return [Object] # KEY = ::Object.new.freeze ## # Access the builtin GitCache. # # @return [Toys::Utils::GitCache] # def git_cache # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_mixins/gems.rb0000644000004100000410000000431015164300675022150 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # Provides methods for installing and activating third-party gems. When # this mixin is included, it provides a `gem` method that has the same # effect as {Toys::Utils::Gems#activate}, so you can ensure a gem is # present when running a tool. A `gem` directive is likewise added to the # tool DSL itself, so you can also ensure a gem is present when defining a # tool. # # ### Usage # # Make these methods available to your tool by including this mixin in your # tool: # # include :gems # # You can then call the mixin method {#gem} to ensure that a gem is # installed and in the load path. For example: # # tool "my_tool" do # include :gems # def run # gem "nokogiri", "~> 1.15" # # Do stuff with Nokogiri # end # end # # If you pass additional options to the include directive, those are used # to initialize settings for the gem install process. For example: # # include :gems, on_missing: :error # # You can also pass options to the {#gem} mixin method itself: # # tool "my_tool" do # include :gems # def run # # If the gem is not installed, error out instead of asking to # # install it. # gem "nokogiri", "~> 1.15", on_missing: :error # # Do stuff with Nokogiri # end # end # # See {Toys::Utils::Gems#initialize} for a list of supported options. # module Gems include Mixin ## # A tool-wide instance of {Toys::Utils::Gems}. # @return [Toys::Utils::Gems] # def gems # Source available in the toys-core gem end ## # Activate the given gem. If it is not present, attempt to install it (or # inform the user to update the bundle). # # @param name [String] Name of the gem # @param requirements [String...] Version requirements # @return [void] # def gem(name, *requirements, **options) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_mixins/xdg.rb0000644000004100000410000000230015164300675021774 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # A mixin that provides tools for working with the XDG Base Directory # Specification. # # This mixin provides an instance of {Toys::Utils::XDG}, which includes # utility methods that locate base directories and search paths for # application state, configuration, caches, and other data, according to # the [XDG Base Directory Spec version # 0.8](https://specifications.freedesktop.org/basedir/0.8/). # # @example # # include :xdg # # def run # # Get config file paths, in order from most to least inportant # config_files = xdg.lookup_config("my-config.toml") # config_files.each { |path| read_my_config(path) } # end # module XDG include Mixin ## # Context key for the XDG object. # @return [Object] # KEY = ::Object.new.freeze ## # Access XDG utility methods. # # @return [Toys::Utils::XDG] # def xdg # Source available in the toys-core gem end end ## # An alternate name for the {XDG} module # Xdg = XDG end end toys-0.21.0/core-docs/toys/standard_mixins/exec.rb0000644000004100000410000010062315164300675022145 0ustar www-datawww-datamodule Toys module StandardMixins ## # **_Defined in the toys-core gem_** # # The `:exec` mixin provides set of helper methods for executing processes # and subcommands. It provides shortcuts for common cases such as invoking # a Ruby script in a subprocess or capturing output in a string. It also # provides an interface for controlling a spawned process's streams. # # You can make these methods available to your tool by including the # following directive in your tool configuration: # # include :exec # # This is a frontend for {Toys::Utils::Exec}. More information is # available in that class's documentation. # # ### Mixin overview # # The mixin provides a number of methods for spawning processes. The most # basic are {#exec} and {#exec_proc}. The {#exec} method spawns an # operating system process specified by an executable and a set of # arguments. The {#exec_proc} method takes a `Proc` and forks a Ruby # process. Both of these can be heavily configured with stream handling, # result handling, and numerous other options described below. The mixin # also provides convenience methods for common cases such as spawning a # Ruby process, spawning a shell script, or capturing output. # # The mixin also stores default configuration that it applies to processes # it spawns. You can change these defaults by calling {#configure_exec}. # # Underlying the mixin is a service object of type {Toys::Utils::Exec}. # Normally you would use the mixin methods to access this functionality, # but you can also retrieve the service object itself by calling # {Toys::Context#get} with the key {Toys::StandardMixins::Exec::KEY}. # # ### Stream handling # # By default, subprocess streams are connected to the corresponding streams # in the parent process. You can change this behavior, redirecting streams # or providing ways to control them, using the `:in`, `:out`, and `:err` # options. # # Three general strategies are available for custom stream handling. First, # you can redirect to other streams such as files, IO objects, or Ruby # strings. Some of these options map directly to options provided by the # `Process#spawn` method. Second, you can use a controller to manipulate # the streams programmatically. Third, you can capture output stream data # and make it available in the result. # # Following is a full list of the stream handling options, along with how # to specify them using the `:in`, `:out`, and `:err` options. # # * **Inherit parent stream:** You can inherit the corresponding stream # in the parent process by passing `:inherit` as the option value. This # is the default if the subprocess is run in the foreground. # # * **Redirect to null:** You can redirect to a null stream by passing # `:null` as the option value. This connects to a stream that is not # closed but contains no data, i.e. `/dev/null` on unix systems. This # is the default if the subprocess is run in the background. # # * **Close the stream:** You can close the stream by passing `:close` as # the option value. This is the same as passing `:close` to # `Process#spawn`. # # * **Redirect to a file:** You can redirect to a file. This reads from # an existing file when connected to `:in`, and creates or appends to a # file when connected to `:out` or `:err`. To specify a file, use the # setting `[:file, "/path/to/file"]`. You can also, when writing a # file, append an optional mode and permission code to the array. For # example, `[:file, "/path/to/file", "a", 0644]`. # # * **Redirect to an IO object:** You can redirect to an IO object in the # parent process, by passing the IO object as the option value. You can # use any IO object. For example, you could connect the child's output # to the parent's error using `out: $stderr`, or you could connect to # an existing File stream. Unlike `Process#spawn`, this works for IO # objects that do not have a corresponding file descriptor (such as # StringIO objects). In such a case, a thread will be spawned to pipe # the IO data through to the child process. Note that the IO object # will _not_ be closed on completion. # # * **Redirect to a pipe:** You can redirect to a pipe created using # `IO.pipe` (i.e. a two-element array of read and write IO objects) by # passing the array as the option value. This will connect the # appropriate IO (either read or write), and close it in the parent. # Thus, you can connect only one process to each end. If you want more # direct control over IO closing behavior, pass the IO object (i.e. the # element of the pipe array) directly. # # * **Combine with another child stream:** You can redirect one child # output stream to another, to combine them. To merge the child's error # stream into its output stream, use `err: [:child, :out]`. # # * **Read from a string:** You can pass a string to the input stream by # setting `[:string, "the string"]`. This works only for `:in`. # # * **Capture output stream:** You can capture a stream and make it # available on the {Toys::Utils::Exec::Result} object, using the # setting `:capture`. This works only for the `:out` and `:err` # streams. # # * **Use the controller:** You can hook a stream to the controller using # the setting `:controller`. You can then manipulate the stream via the # controller. If you pass a block to {Toys::StandardMixins::Exec#exec}, # it yields the {Toys::Utils::Exec::Controller}, giving you access to # streams. See the section below on controlling processes. # # * **Make copies of an output stream:** You can "tee," or duplicate the # `:out` or `:err` stream and redirect those copies to various # destinations. To specify a tee, use the setting `[:tee, ...]` where # the additional array elements include two or more of the following. # See the corresponding documentation above for more detail. # * `:inherit` to direct to the parent process's stream. # * `:capture` to capture the stream and store it in the result. # * `:controller` to direct the stream to the controller. # * `[:file, "/path/to/file"]` to write to a file. # * An `IO` or `StringIO` object. # * An array of two `IO` objects representing a pipe # # Additionally, the last element of the array can be a hash of options. # Supported options include: # * `:buffer_size` The size of the memory buffer for each element of # the tee. Larger buffers may allow higher throughput. The default # is 65536. # # ### Controlling processes # # A process can be started in the *foreground* or the *background*. If you # start a foreground process, it will inherit your standard input and # output streams by default, and it will keep control until it completes. # If you start a background process, its streams will be redirected to null # by default, and control will be returned to you immediately. # # While a process is running, you can control it using a # {Toys::Utils::Exec::Controller} object. Use a controller to interact with # the process's input and output streams, send it signals, or wait for it # to complete. # # When running a process in the foreground, the controller will be yielded # to an optional block. For example, the following code starts a process in # the foreground and passes its output stream to a controller. # # exec(["git", "init"], out: :controller) do |controller| # loop do # line = controller.out.gets # break if line.nil? # puts "Got line: #{line}" # end # end # # At the end of the block, if the controller is handling the process's # input stream, that stream will automatically be closed. The following # example programmatically sends data to the `wc` unix program, and # captures its output. Because the controller is handling the input stream, # it automatically closes the stream at the end of the block, which causes # `wc` to end. # # result = exec(["wc"], in: :controller, out: :capture) do |controller| # controller.in.puts "Hello, world!" # end # puts "Results: #{result.captured_out}" # # Otherwise, depending on the process's behavior, it may continue to run # after the end of the block. Control will not be returned to the caller # until the process actually terminates. Conversely, it is also possible # the process could terminate by itself while the block is still executing. # You can call controller methods to obtain the process's actual current # state. # # When running a process in the background, the controller is returned # immediately from the method that starts the process. In the following # example, git init is kicked off in the background and the output is # thrown away to /dev/null. # # controller = exec(["git", "init"], background: true) # # In this mode, use the returned controller to query the process's state # and interact with it. Streams directed to the controller are not # automatically closed, so you will need to do so yourself. Following is an # example of running `wc` in the background: # # controller = exec(["wc"], background: true, # in: :controller, out: :controller) # controller.in.puts "Hello, world!" # controller.in.close # Do this explicitly to cause wc to finish # puts "Results: #{controller.out.read}" # Read the entire stream # # ### Result handling # # A subprocess result is represented by a {Toys::Utils::Exec::Result} # object, which includes the exit code, the content of any captured output # streams, and any exeption raised when attempting to run the process. # When you run a process in the foreground, the method will return a result # object. When you run a process in the background, you can obtain the # result from the controller once the process completes. # # The following example demonstrates running a process in the foreground # and getting the exit code: # # result = exec(["git", "init"]) # puts "exit code: #{result.exit_code}" # # The following example demonstrates starting a process in the background, # waiting for it to complete, and getting its exit code: # # controller = exec(["git", "init"], background: true) # result = controller.result(timeout: 1.0) # if result # puts "exit code: #{result.exit_code}" # else # puts "timed out" # end # # You can also provide a callback that is executed once a process # completes. This callback can be specified as a method name or a `Proc` # object, and will be passed the result object. For example: # # def run # exec(["git", "init"], result_callback: :handle_result) # end # def handle_result(result) # puts "exit code: #{result.exit_code}" # end # # In foreground mode, the callback is executed in the calling thread, after # the process terminates (and after any controller block has completed) but # before control is returned to the caller. In background mode, the # callback is executed asynchronously in a separate thread after the # process terminates. # # Finally, you can force your tool to exit if a subprocess fails, similar # to setting the `set -e` option in bash, by setting the # `:exit_on_nonzero_status` option. This is often set as a default # configuration for all subprocesses run in a tool, by passing it as an # argument to the `include` directive: # # include :exec, exit_on_nonzero_status: true # # ### Configuration Options # # A variety of options can be used to control subprocesses. These can be # provided to any method that starts a subprocess. You can also set # defaults by passing them as keyword arguments when you `include` the # mixin. # # Options that affect the behavior of subprocesses: # # * `:env` (Hash) Environment variables to pass to the subprocess. # Keys represent variable names and should be strings. Values should be # either strings or `nil`, which unsets the variable. # # * `:background` (Boolean) Runs the process in the background if `true`. # # * `:unbundle` (Boolean) Disables any existing bundle when running the # subprocess. Has no effect if Bundler isn't active at the call point. # Cannot be used when executing in a fork, e.g. via {#exec_proc}. # # Options related to handling results # # * `:result_callback` (Proc,Symbol) A procedure that is called, and # passed the result object, when the subprocess exits. You can provide # a `Proc` object, or the name of a method as a `Symbol`. # # * `:exit_on_nonzero_status` (Boolean) If set to true, a nonzero exit # code will cause the tool to exit immediately with that same code. # # * `:e` (Boolean) A short name for `:exit_on_nonzero_status`. # # Options for connecting input and output streams. See the section above on # stream handling for info on the values that can be passed. # # * `:in` Connects the input stream of the subprocess. See the section on # stream handling. # # * `:out` Connects the standard output stream of the subprocess. See the # section on stream handling. # # * `:err` Connects the standard error stream of the subprocess. See the # section on stream handling. # # Options related to logging and reporting: # # * `:logger` (Logger) Logger to use for logging the actual command. If # not present, the command is not logged. # # * `:log_level` (Integer,false) Level for logging the actual command. # Defaults to Logger::INFO if not present. You can also pass `false` to # disable logging of the command. # # * `:log_cmd` (String) The string logged for the actual command. # Defaults to the `inspect` representation of the command. # # * `:name` (Object) An optional object that can be used to identify this # subprocess. It is available in the controller and result objects. # # In addition, the following options recognized by # [`Process#spawn`](https://ruby-doc.org/core/Process.html#method-c-spawn) # are supported. # # * `:chdir` (String) Set the working directory for the command. # # * `:close_others` (Boolean) Whether to close non-redirected # non-standard file descriptors. # # * `:new_pgroup` (Boolean) Create new process group (Windows only). # # * `:pgroup` (Integer,true,nil) The process group setting. # # * `:umask` (Integer) Umask setting for the new process. # # * `:unsetenv_others` (Boolean) Clear environment variables except those # explicitly set. # # Any other option key will result in an `ArgumentError`. # module Exec include Mixin ## # Context key for the executor object. # @return [Object] # KEY = ::Object.new.freeze ## # Set default configuration options. # # See the {Toys::StandardMixins::Exec} module documentation for a # description of the options. # # @param opts [keywords] The default options. # @return [self] # def configure_exec(**opts) # Source available in the toys-core gem end ## # Execute a command. The command can be given as a single string to pass # to a shell, or an array of strings indicating a posix command. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # ### Examples # # Run a command without a shell, and print the exit code (0 for success): # # result = exec(["git", "init"]) # puts "exit code: #{result.exit_code}" # # Run a shell command: # # result = exec("cd mydir && git init") # puts "exit code: #{result.exit_code}" # # @param cmd [String,Array] The command to execute. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec(cmd, **opts, &block) # Source available in the toys-core gem end ## # Spawn a ruby process and pass the given arguments to it. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # ### Example # # Execute a small script with warnings # # exec_ruby(["-w", "-e", "(1..10).each { |i| puts i }"]) # # @param args [String,Array] The arguments to ruby. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec_ruby(args, **opts, &block) # Source available in the toys-core gem end alias ruby exec_ruby ## # Execute a proc in a forked subprocess. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) # do not support this method because they do not support fork. # # ### Example # # Run a proc in a forked process. # # code = proc do # puts "Spawned process ID is #{Process.pid}" # end # puts "Main process ID is #{Process.pid}" # exec_proc(code) # # @param func [Proc] The proc to call. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec_proc(func, **opts, &block) # Source available in the toys-core gem end ## # Execute a tool in the current CLI in a forked process. # # The command can be given as a single string or an array of strings, # representing the tool to run and the arguments to pass. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) # do not support this method because they do not support fork. # # ### Example # # Run the "system update" tool and pass it an argument. # # exec_tool(["system", "update", "--verbose"]) # # @param cmd [String,Array] The tool to execute. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec_tool(cmd, **opts, &block) # Source available in the toys-core gem end ## # Execute a tool in a separately spawned process. # # The command can be given as a single string or an array of strings, # representing the tool to run and the arguments to pass. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # An entirely separate spawned process is run for this tool, using the # setting of {Toys.executable_path}. Thus, this method can be run only if # that setting is present. The normal Toys gem does set it, but if you # are writing your own executable using Toys-Core, you will need to set # it explicitly for this method to work. Furthermore, Bundler, if # present, is reset to its "unbundled" environment. Thus, the tool found, # the behavior of the CLI, and the gem environment, might not be the same # as those of the calling tool. # # This method is often used if you are already in a bundle and need to # run a tool that uses a different bundle. It may also be necessary on # environments without "fork" (such as JRuby or Ruby on Windows). # # ### Example # # Run the "system update" tool and pass it an argument. # # exec_separate_tool(["system", "update", "--verbose"]) # # @param cmd [String,Array] The tool to execute. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec_separate_tool(cmd, **opts, &block) # Source available in the toys-core gem end ## # Execute a command. The command can be given as a single string to pass # to a shell, or an array of strings indicating a posix command. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # ### Example # # Capture the output of an echo command # # str = capture(["echo", "hello"]) # assert_equal("hello\n", str) # # @param cmd [String,Array] The command to execute. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [String] What was written to standard out. # def capture(cmd, **opts, &block) # Source available in the toys-core gem end ## # Spawn a ruby process and pass the given arguments to it. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # ### Example # # Capture the output of a ruby script. # # str = capture_ruby("-e", "(1..3).each { |i| puts i }") # assert_equal "1\n2\n3\n", str # # @param args [String,Array] The arguments to ruby. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [String] What was written to standard out. # def capture_ruby(args, **opts, &block) # Source available in the toys-core gem end ## # Execute a proc in a forked subprocess. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) # do not support this method because they do not support fork. # # ### Example # # Run a proc in a forked process and capture its output: # # code = proc do # puts Process.pid # end # forked_pid = capture_proc(code).chomp # puts "I forked PID #{forked_pid}" # # @param func [Proc] The proc to call. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [String] What was written to standard out. # def capture_proc(func, **opts, &block) # Source available in the toys-core gem end ## # Execute a tool in the current CLI in a forked process. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # The command can be given as a single string or an array of strings, # representing the tool to run and the arguments to pass. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) # do not support this method because they do not support fork. # # ### Example # # Run the "system version" tool and capture its output. # # str = capture_tool(["system", "version"]).chomp # puts "Version was #{str}" # # @param cmd [String,Array] The tool to execute. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [String] What was written to standard out. # def capture_tool(cmd, **opts, &block) # Source available in the toys-core gem end ## # Execute a tool in a separately spawned process. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # The command can be given as a single string or an array of strings, # representing the tool to run and the arguments to pass. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # An entirely separate spawned process is run for this tool, using the # setting of {Toys.executable_path}. Thus, this method can be run only if # that setting is present. The normal Toys gem does set it, but if you # are writing your own executable using Toys-Core, you will need to set # it explicitly for this method to work. Furthermore, Bundler, if # present, is reset to its "unbundled" environment. Thus, the tool found, # the behavior of the CLI, and the gem environment, might not be the same # as those of the calling tool. # # This method is often used if you are already in a bundle and need to # run a tool that uses a different bundle. It may also be necessary on # environments without "fork" (such as JRuby or Ruby on Windows). # # ### Example # # Run the "system version" tool and capture its output. # # str = capture_separate_tool(["system", "version"]).chomp # puts "Version was #{str}" # # @param cmd [String,Array] The tool to execute. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [String] What was written to standard out. # def capture_separate_tool(cmd, **opts, &block) # Source available in the toys-core gem end ## # Execute the given string in a shell. Returns the exit code. # Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # ### Example # # Run a shell script # # exit_code = sh("cd mydir && git init") # puts exit_code == 0 ? "Success!" : "Failed!" # # @param cmd [String] The shell command to execute. # @param opts [keywords] The command options. See the section on # Configuration Options in the {Toys::StandardMixins::Exec} module # documentation. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for # the subprocess. See the section on Controlling Processes in the # {Toys::StandardMixins::Exec} module documentation. # # @return [Integer] The exit code # def sh(cmd, **opts, &block) # Source available in the toys-core gem end ## # Exit if the given status code is nonzero. Otherwise, returns 0. # # @param status [Integer,Process::Status,Toys::Utils::Exec::Result] # @return [Integer] # def exit_on_nonzero_status(status) # Source available in the toys-core gem end ## # Returns an array of standard verbosity flags needed to replicate the # current verbosity level. This is useful when you want to spawn tools # with the same verbosity level as the current tool. # # @param short [Boolean] Whether to emit short rather than long flags. # Default is false. # @return [Array] # def verbosity_flags(short: false) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/dsl/0000755000004100000410000000000015164300675016265 5ustar www-datawww-datatoys-0.21.0/core-docs/toys/dsl/positional_arg.rb0000644000004100000410000001253215164300675021627 0ustar www-datawww-datamodule Toys module DSL ## # **_Defined in the toys-core gem_** # # DSL for an arg definition block. Lets you set arg attributes in a block # instead of a long series of keyword arguments. # # These directives are available inside a block passed to # {Toys::DSL::Tool#required_arg}, {Toys::DSL::Tool#optional_arg}, or # {Toys::DSL::Tool#remaining_args}. # # ### Example # # tool "mytool" do # optional_arg :value do # # The directives in here are defined by this class # accept Integer # desc "An integer value" # end # # ... # end # class PositionalArg ## # Set the acceptor for this argument's values. # You can pass either the string name of an acceptor defined in this tool # or any of its ancestors, or any other specification recognized by # {Toys::Acceptor.create}. # # @param spec [Object] # @param options [Hash] # @param block [Proc] # @return [self] # def accept(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Set the default value. # # @param default [Object] # @return [self] # def default(default) # Source available in the toys-core gem end ## # Set the shell completion strategy for arg values. # You can pass either the string name of a completion defined in this # tool or any of its ancestors, or any other specification recognized by # {Toys::Completion.create}. # # @param spec [Object] # @param options [Hash] # @param block [Proc] # @return [self] # def complete(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Set the name of this arg as it appears in help screens. # # @param display_name [String] # @return [self] # def display_name(display_name) # Source available in the toys-core gem end ## # Set the short description for the current positional argument. The # short description is displayed with the argument in online help. # # The description is a {Toys::WrappableString}, which may be word-wrapped # when displayed in a help screen. You may pass a {Toys::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. # # ### Examples # # 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."] # # @param desc [String,Array,Toys::WrappableString] # @return [self] # def desc(desc) # Source available in the toys-core gem end ## # Add to the long description for the current positional argument. The # long description is displayed with the argument in online help. This # directive may be given multiple times, and the results are cumulative. # # 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 initial paragraph might get word wrapped.", # "This next paragraph is followed by a blank line.", # "", # ["This line will not be wrapped."], # [" This indent is preserved."] # long_desc "This line is appended to the description." # # @param long_desc [String,Array,Toys::WrappableString...] # @return [self] # def long_desc(*long_desc) # Source available in the toys-core gem end ## # Specify whether to add a method for this argument. # # Recognized values are true to force creation of a method, false to # disable method creation, and nil for the default behavior. The default # checks the name and adds a method if the name is a symbol representing # a legal method name that starts with a letter and does not override any # public method in the Ruby Object class or collide with any method # directly defined in the tool class. # # @param value [true,false,nil] # def add_method(value) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/dsl/internal.rb0000644000004100000410000000004315164300675020423 0ustar www-datawww-datamodule Toys module DSL end end toys-0.21.0/core-docs/toys/dsl/tool.rb0000644000004100000410000021165515164300675017601 0ustar www-datawww-datamodule Toys module DSL ## # **_Defined in the toys-core gem_** # # This module 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 # # The DSL directives `tool`, `desc`, `optional_arg`, and others are defined # in this module. # # Now you can execute it using: # # toys greet # # or try: # # toys greet rubyists # module Tool ## # Create a named acceptor that can be referenced by name from any flag or # positional argument in this tool or its subtools. # # An acceptor validates the string parameter passed to a flag or # positional argument. It also optionally converts the string to a # different object before storing it in your tool's data. # # Acceptors can be defined in one of four ways. # # * You can provide a **regular expression**. This acceptor validates # only if the regex matches the *entire string parameter*. # # You can also provide an optional conversion function as a block. If # provided, function must take a variable number of arguments, the # first being the matched string and the remainder being the captures # from the regular expression. It should return the converted object # that will be stored in the context data. If you do not provide a # block, the original string will be used. # # * You can provide an **array** of possible values. The acceptor # validates if the string parameter matches the *string form* of one # of the array elements (i.e. the results of calling `to_s` on the # array elements.) # # An array acceptor automatically converts the string parameter to # the actual array element that it matched. For example, if the # symbol `:foo` is in the array, it will match the string `"foo"`, # and then store the symbol `:foo` in the tool data. # # * You can provide a **range** of possible values, along with a # conversion function that converts a string parameter to a type # comparable by the range. (See the "function" spec below for a # detailed description of conversion functions.) If the range has # numeric endpoints, the conversion function is optional because a # default will be provided. # # * You can provide a **function** by passing it as a proc or a block. # This function performs *both* validation and conversion. It should # take the string parameter as its argument, and it must either # return the object that should be stored in the tool data, or raise # an exception (descended from `StandardError`) to indicate that the # string parameter is invalid. # # ### Example # # The following example creates an acceptor named "hex" that is defined # via a regular expression. It uses the acceptor to validate values # passed to a flag. # # tool "example" do # acceptor "hex", /[0-9a-fA-F]+/, type_desc: "hex numbers" # flag :number, accept: "hex" # def run # puts "number was #{number}" # end # end # # @param name [String] The acceptor name. # @param spec [Object] See the description for recognized values. # @param type_desc [String] Type description string, shown in help. # Defaults to the acceptor name. # @param block [Proc] See the description for recognized forms. # @return [self] # def acceptor(name, spec = nil, type_desc: nil, &block) # Source available in the toys-core gem end ## # Create a named mixin module that can be included by name from this tool # or its subtools. # # A mixin is a module that defines methods that can be called from a # tool. It is commonly used to provide "utility" methods, implementing # common functionality and allowing tools to share code. # # Normally you provide a block and define the mixin's methods in that # block. Alternatively, you can create a module separately and pass it # directly to this directive. # # ### Example # # The following example creates a named mixin and uses it in a tool. # # mixin "error-reporter" do # def error message # logger.error "An error occurred: #{message}" # exit 1 # end # end # # tool "build" do # include "error-reporter" # def run # puts "Building..." # error "Build failed!" # end # end # # @param name [String] Name of the mixin # @param mixin_module [Module] Module to use as the mixin. Optional. # Either pass a module here, *or* provide a block and define the # mixin within the block. # @param block [Proc] Defines the mixin module. # @return [self] # def mixin(name, mixin_module = nil, &block) # Source available in the toys-core gem end ## # Create a named template that can be expanded by name from this tool # or its subtools. # # A template is an object that generates DSL directives. You can use it # to build "prefabricated" tools, and then instantiate them in your Toys # files. # # A template is an object that defines an `expansion` procedure. This # procedure generates the DSL directives implemented by the template. The # template object typically also includes attributes that are used to # configure the expansion. # # The simplest way to define a template is to pass a block to the # {#template} directive. In the block, define an `initialize` method that # accepts any arguments that may be passed to the template when it is # instantiated and are used to configure the template. Define # `attr_reader`s or other methods to make this configuration accessible # from the object. Then define an `on_expand` block that implements the # template's expansion. The template object is passed as an object to the # `on_expand` block. # # Alternately, you can create a template class separately and pass it # directly. See {Toys::Template} for details on creating a template # class. # # ### Example # # The following example creates and uses a simple template. The template # defines a tool, with a configurable name, that simply prints out a # configurable message. # # template "hello-generator" do # def initialize(name, message) # @name = name # @message = message # end # attr_reader :name, :message # on_expand do |template| # tool template.name do # to_run do # puts template.message # end # end # end # end # # expand "hello-generator", "mytool", "mytool is running!" # # @param name [String] Name of the template # @param template_class [Class] Module to use as the mixin. Optional. # Either pass a module here, *or* provide a block and define the # mixin within the block. # @param block [Proc] Defines the template class. # @return [self] # def template(name, template_class = nil, &block) # Source available in the toys-core gem end ## # Create a named completion procedure that may be used by name by any # flag or positional arg in this tool or any subtool. # # A completion controls tab completion for the value of a flag or # positional argument. In general, it is a Ruby `Proc` that takes a # context object (of type {Toys::Completion::Context}) and returns an # array of completion candidate strings. # # Completions can be specified in one of three ways. # # * A Proc object itself, either passed directly to this directive or # provided as a block. # * A static array of strings, indicating the completion candidates # independent of context. # * The symbol `:file_system` which indicates that paths in the file # system should serve as completion candidates. # # ### Example # # The following example defines a completion that uses only the immediate # files in the current directory as candidates. (This is different from # the `:file_system` completion which will descend into subdirectories # similar to how bash completes most of its file system commands.) # # completion "local-files" do |_context| # `/bin/ls`.split("\n") # end # tool "example" do # flag :file, complete_values: "local-files" # def run # puts "selected file #{file}" # end # end # # @param name [String] Name of the completion # @param spec [Object] See the description for recognized values. # @param options [Hash] Additional options to pass to the completion. # @param block [Proc] See the description for recognized forms. # @return [self] # def completion(name, spec = nil, **options, &block) # Source available in the toys-core gem end ## # Create a subtool. You must provide a block defining the subtool. # # ### Example # # The following example defines a tool and two subtools within it. # # tool "build" do # tool "staging" do # def run # puts "Building staging" # end # end # tool "production" do # def run # puts "Building production" # end # end # end # # The following example uses `delegate_to` to define a tool that runs one # of its subtools. # # tool "test", delegate_to: ["test", "unit"] do # tool "unit" do # def run # puts "Running unit tests" # end # end # end # # @param words [String,Array] The name of the subtool # @param if_defined [:combine,:reset,:ignore] What to do if a definition # already exists for this tool. Possible values are `:combine` (the # default) indicating the definition should be combined with the # existing definition, `:reset` indicating the earlier definition # should be reset and the new definition applied instead, or # `:ignore` indicating the new definition should be ignored. # @param delegate_to [String,Array] Optional. This tool should # delegate to another tool, specified by the full path. This path may # be given as an array of strings, or a single string possibly # delimited by path separators. # @param delegate_relative [String,Array] Optional. Similar to # delegate_to, but takes a delegate name relative to the context in # which this tool is being defined. # @param block [Proc] Defines the subtool. # @return [self] # def tool(words, if_defined: :combine, delegate_to: nil, delegate_relative: nil, &block) # Source available in the toys-core gem end ## # Create an alias, representing an "alternate name" for a tool. # # Note: This is functionally equivalent to creating a tool with the # `:delegate_relative` option. As such, `alias_tool` is considered # deprecated. # # ### Example # # This example defines a tool and an alias pointing to it. Both the tool # name `test` and the alias `t` will then refer to the same tool. # # tool "test" do # def run # puts "Running tests..." # end # end # alias_tool "t", "test" # # Note: the following is preferred over alias_tool: # # tool "t", delegate_relative: "test" # # @param word [String] The name of the alias # @param target [String,Array] Relative path to the target of the # alias. This path may be given as an array of strings, or a single # string possibly delimited by path separators. # @return [self] # @deprecated Use {#tool} and pass `:delegate_relative` instead # def alias_tool(word, target) # Source available in the toys-core gem end ## # Causes the current tool to delegate to another tool, specified by the # full tool name. When run, it simply invokes the target tool with the # same arguments. # # ### Example # # This example defines a tool that runs one of its subtools. Running the # `test` tool will have the same effect (and recognize the same args) as # the subtool `test unit`. # # tool "test" do # tool "unit" do # flag :faster # def run # puts "running tests..." # end # end # delegate_to "test:unit" # end # # @param target [String,Array] The full path to the delegate # tool. This path may be given as an array of strings, or a single # string possibly delimited by path separators. # @return [self] # def delegate_to(target) # Source available in the toys-core gem end ## # Load another config file or directory, as if its contents were inserted # at the current location. # # @param path [String] The file or directory to load. # @param as [String] Load into the given tool/namespace. If omitted, # configuration will be loaded into the current namespace. # # @return [self] # def load(path, as: nil) # Source available in the toys-core gem end ## # Load configuration from a public git repository, as if its contents # were inserted at the current location. # # @param remote [String] The URL of the git repository. Defaults to the # current repository if already loading from git. # @param path [String] The path within the repo to the file or directory # to load. Defaults to the root of the repo. # @param commit [String] The commit branch, tag, or sha. Defaults to the # current commit if already loading from git, or to `HEAD`. # @param as [String] Load into the given tool/namespace. If omitted, # configuration will be loaded into the current namespace. # @param update [Boolean,Integer] Whether and when to force-fetch from # the remote (unless the commit is a SHA). Force-fetching will ensure # that symbolic commits, such as branch names or HEAD, are up to date. # You can pass `true` or `false` to specify whether to update, or an # integer to update if the last update was done at least that many # seconds ago. Default is false. # # @return [self] # def load_git(remote: nil, path: nil, commit: nil, as: nil, update: false) # Source available in the toys-core gem end ## # Load configuration from a gem, as if its contents were inserted at the # current location. # # @param name [String] Name of the gem # @param versions [Array] Version requirements for the gem. # @param version [String,Array] An alternate way to specify # version requirements for the gem. # @param path [String] Optional path within the gem to the file or # directory to load. Defaults to the root of the gem's toys directory. # @param toys_dir [String] Optional override for the gem's toys # directory name. If not specified, the default specified by the gem # will be used. # @param as [String] Load into the given tool/namespace. If omitted, # configuration will be loaded into the current namespace. # def load_gem(name, *versions, version: nil, path: nil, toys_dir: nil, as: nil) # Source available in the toys-core gem end ## # 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. # # ### Example # # The following example creates and uses a simple template. # # template "hello-generator" do # def initialize(name, message) # @name = name # @message = message # end # attr_reader :name, :message # expansion do |template| # tool template.name do # to_run do # puts template.message # end # end # end # end # # expand "hello-generator", "mytool", "mytool is running!" # # @param template_class [Class,String,Symbol] The template, either as a # class or a well-known name. # @param args [Object...] Template arguments # @return [self] # def expand(template_class, *args, **kwargs) # Source available in the toys-core gem end ## # 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 {Toys::WrappableString}, which may be word-wrapped # when displayed in a help screen. You may pass a {Toys::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. # # ### Examples # # 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."] # # @param str [Toys::WrappableString,String,Array] # @return [self] # def desc(str) # Source available in the toys-core gem end alias short_desc desc ## # Add to the long description for the current tool. The long description # is displayed in the usage documentation for the tool itself. This # directive may be given multiple times, and the results are cumulative. # # 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 initial paragraph might get word wrapped.", # "This next paragraph is followed by a blank line.", # "", # ["This line will not be wrapped."], # [" This indent is preserved."] # long_desc "This line is appended to the description." # # @param strs [Toys::WrappableString,String,Array...] # @param file [String] Optional. Read the description from the given file # provided relative to the current toys file. The file must be a # plain text file whose suffix is `.txt`. # @param data [String] Optional. Read the description from the given data # file. The file must be a plain text file whose suffix is `.txt`. # @return [self] # def long_desc(*strs, file: nil, data: nil) # Source available in the toys-core gem end ## # Create a flag group. If a block is given, flags defined in the block # belong to the group. The flags in the group are listed together in # help screens. # # ### Example # # The following example creates a flag group in which all flags are # optional. # # tool "execute" do # flag_group desc: "Debug Flags" do # flag :debug, "-D", desc: "Enable debugger" # flag :warnings, "-W[VAL]", desc: "Enable warnings" # end # # ... # end # # @param type [Symbol] The type of group. Allowed values: `:required`, # `:optional`, `:exactly_one`, `:at_most_one`, `:at_least_one`. # Default is `:optional`. # @param desc [String,Array,Toys::WrappableString] Short # description for the group. See {Toys::DSL::Tool#desc} for a # description of allowed formats. Defaults to `"Flags"`. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag group. See # {Toys::DSL::Tool#long_desc} for a description of allowed formats. # Defaults to the empty array. # @param name [String,Symbol,nil] The name of the group, or nil for no # name. # @param report_collisions [Boolean] If `true`, raise an exception if a # the given name is already taken. If `false`, ignore. Default is # `true`. # @param prepend [Boolean] If `true`, prepend rather than append the # group to the list. Default is `false`. # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup} # for the directives that can be called in this block. # @return [self] # def flag_group(type: :optional, desc: nil, long_desc: nil, name: nil, report_collisions: true, prepend: false, &block) # Source available in the toys-core gem end ## # Create a flag group of type `:required`. If a block is given, flags # defined in the block belong to the group. All flags in this group are # required. # # ### Example # # The following example creates a group of required flags. # # tool "login" do # all_required do # flag :username, "--username=VAL", desc: "Set username (required)" # flag :password, "--password=VAL", desc: "Set password (required)" # end # # ... # end # # @param desc [String,Array,Toys::WrappableString] Short # description for the group. See {Toys::DSL::Tool#desc} for a # description of allowed formats. Defaults to `"Flags"`. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag group. See # {Toys::DSL::Tool#long_desc} for a description of allowed formats. # Defaults to the empty array. # @param name [String,Symbol,nil] The name of the group, or nil for no # name. # @param report_collisions [Boolean] If `true`, raise an exception if a # the given name is already taken. If `false`, ignore. Default is # `true`. # @param prepend [Boolean] If `true`, prepend rather than append the # group to the list. Default is `false`. # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup} # for the directives that can be called in this block. # @return [self] # def all_required(desc: nil, long_desc: nil, name: nil, report_collisions: true, prepend: false, &block) # Source available in the toys-core gem end ## # Create a flag group of type `:at_most_one`. If a block is given, flags # defined in the block belong to the group. At most one flag in this # group must be provided on the command line. # # ### Example # # The following example creates a group of flags in which either one or # none may be set, but not more than one. # # tool "provision-server" do # at_most_one do # flag :restore_from_backup, "--restore-from-backup=VAL" # flag :restore_from_image, "--restore-from-image=VAL" # flag :clone_existing, "--clone-existing=VAL" # end # # ... # end # # @param desc [String,Array,Toys::WrappableString] Short # description for the group. See {Toys::DSL::Tool#desc} for a # description of allowed formats. Defaults to `"Flags"`. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag group. See # {Toys::DSL::Tool#long_desc} for a description of allowed formats. # Defaults to the empty array. # @param name [String,Symbol,nil] The name of the group, or nil for no # name. # @param report_collisions [Boolean] If `true`, raise an exception if a # the given name is already taken. If `false`, ignore. Default is # `true`. # @param prepend [Boolean] If `true`, prepend rather than append the # group to the list. Default is `false`. # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup} # for the directives that can be called in this block. # @return [self] # def at_most_one(desc: nil, long_desc: nil, name: nil, report_collisions: true, prepend: false, &block) # Source available in the toys-core gem end alias at_most_one_required at_most_one ## # Create a flag group of type `:at_least_one`. If a block is given, flags # defined in the block belong to the group. At least one flag in this # group must be provided on the command line. # # ### Example # # The following example creates a group of flags in which one or more # may be set. # # tool "run-tests" do # at_least_one do # flag :unit, desc: "Run unit tests" # flag :integration, desc: "Run integration tests" # flag :performance, desc: "Run performance tests" # end # # ... # end # # @param desc [String,Array,Toys::WrappableString] Short # description for the group. See {Toys::DSL::Tool#desc} for a # description of allowed formats. Defaults to `"Flags"`. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag group. See # {Toys::DSL::Tool#long_desc} for a description of allowed formats. # Defaults to the empty array. # @param name [String,Symbol,nil] The name of the group, or nil for no # name. # @param report_collisions [Boolean] If `true`, raise an exception if a # the given name is already taken. If `false`, ignore. Default is # `true`. # @param prepend [Boolean] If `true`, prepend rather than append the # group to the list. Default is `false`. # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup} # for the directives that can be called in this block. # @return [self] # def at_least_one(desc: nil, long_desc: nil, name: nil, report_collisions: true, prepend: false, &block) # Source available in the toys-core gem end alias at_least_one_required at_least_one ## # Create a flag group of type `:exactly_one`. If a block is given, flags # defined in the block belong to the group. Exactly one flag in this # group must be provided on the command line. # # ### Example # # The following example creates a group of flags in which exactly one # must be set. # # tool "deploy" do # exactly_one do # flag :server, "--server=IP_ADDR", desc: "Deploy to server" # flag :vm, "--vm=ID", desc: "Deploy to a VM" # flag :container, "--container=ID", desc: "Deploy to a container" # end # # ... # end # # @param desc [String,Array,Toys::WrappableString] Short # description for the group. See {Toys::DSL::Tool#desc} for a # description of allowed formats. Defaults to `"Flags"`. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag group. See # {Toys::DSL::Tool#long_desc} for a description of allowed formats. # Defaults to the empty array. # @param name [String,Symbol,nil] The name of the group, or nil for no # name. # @param report_collisions [Boolean] If `true`, raise an exception if a # the given name is already taken. If `false`, ignore. Default is # `true`. # @param prepend [Boolean] If `true`, prepend rather than append the # group to the list. Default is `false`. # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup} # for the directives that can be called in this block. # @return [self] # def exactly_one(desc: nil, long_desc: nil, name: nil, report_collisions: true, prepend: false, &block) # Source available in the toys-core gem end alias exactly_one_required exactly_one ## # 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 {Toys::Context#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 {Toys::DSL::Flag} within the block. # # ### Flag syntax # # The flags themselves should be provided in OptionParser form. Following # are examples of valid syntax. # # * `-a` : A short boolean switch. When this appears as an argument, # the value is set to `true`. # * `--abc` : A long boolean switch. When this appears as an argument, # the value is set to `true`. # * `-aVAL` or `-a VAL` : A short flag that takes a required value. # These two forms are treated identically. If this argument appears # with a value attached (e.g. `-afoo`), the attached string (e.g. # `"foo"`) is taken as the value. Otherwise, the following argument # is taken as the value (e.g. for `-a foo`, the value is set to # `"foo"`.) The following argument is treated as the value even if it # looks like a flag (e.g. `-a -a` causes the string `"-a"` to be # taken as the value.) # * `-a[VAL]` : A short flag that takes an optional value. If this # argument appears with a value attached (e.g. `-afoo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, the value # is set to `true`. The following argument is never interpreted as # the value. (Compare with `-a [VAL]`.) # * `-a [VAL]` : A short flag that takes an optional value. If this # argument appears with a value attached (e.g. `-afoo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, if the # following argument does not look like a flag (i.e. it does not # begin with a hyphen), it is taken as the value. (e.g. `-a foo` # causes the string `"foo"` to be taken as the value.). If there is # no following argument, or the following argument looks like a flag, # the value is set to `true`. (Compare with `-a[VAL]`.) # * `--abc=VAL` or `--abc VAL` : A long flag that takes a required # value. These two forms are treated identically. If this argument # appears with a value attached (e.g. `--abc=foo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, the # following argument is taken as the value (e.g. for `--abc foo`, the # value is set to `"foo"`.) The following argument is treated as the # value even if it looks like a flag (e.g. `--abc --def` causes the # string `"--def"` to be taken as the value.) # * `--abc[=VAL]` : A long flag that takes an optional value. If this # argument appears with a value attached (e.g. `--abc=foo`), the # attached string (e.g. `"foo"`) is taken as the value. Otherwise, # the value is set to `true`. The following argument is never # interpreted as the value. (Compare with `--abc [VAL]`.) # * `--abc [VAL]` : A long flag that takes an optional value. If this # argument appears with a value attached (e.g. `--abc=foo`), the # attached string (e.g. `"foo"`) is taken as the value. Otherwise, if # the following argument does not look like a flag (i.e. it does not # begin with a hyphen), it is taken as the value. (e.g. `--abc foo` # causes the string `"foo"` to be taken as the value.). If there is # no following argument, or the following argument looks like a flag, # the value is set to `true`. (Compare with `--abc=[VAL]`.) # * `--[no-]abc` : A long boolean switch that can be turned either on # or off. This effectively creates two flags, `--abc` which sets the # value to `true`, and `--no-abc` which sets the falue to `false`. # # ### Default flag syntax # # If no flag syntax strings are provided, a default syntax will be # inferred based on the key and other options. # # Specifically, if the key has one character, then that character will be # chosen as a short flag. If the key has multiple characters, a long flag # will be generated. # # Furthermore, if a custom completion, a non-boolean acceptor, or a # non-boolean default value is provided in the options, then the flag # will be considered to take a value. Otherwise, it will be considered to # be a boolean switch. # # For example, the following pairs of flags are identical: # # flag :a # flag :a, "-a" # # flag :abc_def # flag :abc_def, "--abc-def" # # flag :number, accept: Integer # flag :number, "--number=VAL", accept: Integer # # ### More examples # # A flag that sets its value to the number of times it appears on the # command line: # # flag :verbose, "-v", "--verbose", # default: 0, handler: ->(_val, count) { count + 1 } # # An example using block form: # # flag :shout do # flags "-s", "--shout" # default false # desc "Say it louder" # long_desc "This flag says it lowder.", # "You might use this when people can't hear you.", # "", # "Example:", # [" toys say --shout hello"] # end # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param flags [String...] The flags in OptionParser format. # @param accept [Object] An acceptor that validates and/or converts the # value. You may provide either the name of an acceptor you have # defined, one of the default acceptors provided by OptionParser, or # any other specification recognized by {Toys::Acceptor.create}. # Optional. If not specified, accepts any value as a string. # @param default [Object] 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`. # @param handler [Proc,nil,:set,:push] An optional handler that # customizes how a value is set or updated when the flag is parsed. # A handler is a proc that takes up to three arguments: the given # value, the previous value, and a hash containing all the data # collected so far during argument parsing. The proc must return the # new value for the flag. # You may also specify a predefined named handler. The `:set` handler # (the default) replaces the previous value (effectively # `-> (val) { val }`). The `:push` handler expects the previous value # to be an array and pushes the given value onto it; it should be # combined with setting the default value to `[]` and is intended for # "multi-valued" flags. # @param complete_flags [Object] A specifier for shell tab completion # for flag names associated with this flag. By default, a # {Toys::Flag::DefaultCompletion} is used, which provides the flag's # names as completion candidates. To customize completion, set this # to the name of a previously defined completion, a hash of options # to pass to the constructor for {Toys::Flag::DefaultCompletion}, or # any other spec recognized by {Toys::Completion.create}. # @param complete_values [Object] A specifier for shell tab completion # for flag values associated with this flag. This is the empty # completion by default. To customize completion, set this to the # name of a previously defined completion, or any spec recognized by # {Toys::Completion.create}. # @param report_collisions [Boolean] Raise an exception if a flag is # requested that is already in use or marked as unusable. Default is # true. # @param group [Toys::FlagGroup,String,Symbol,nil] Group for this flag. # You may provide a group name, a FlagGroup object, or `nil` which # denotes the default group. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::DSL::Tool#desc} for a # description of the allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::DSL::Tool#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. # @param display_name [String] A display name for this flag, used in help # text and error messages. # @param add_method [true,false,nil] Whether to add a method for this # flag. If omitted or set to nil, uses the default behavior, which # adds the method if the key is a symbol representing a legal method # name that starts with a letter and does not override any public # method in the Ruby Object class or collide with any method directly # defined in the tool class. # @param block [Proc] Configures the flag. See {Toys::DSL::Flag} for the # directives that can be called in this block. # @return [self] # def flag(key, *flags, accept: nil, default: nil, handler: nil, complete_flags: nil, complete_values: nil, report_collisions: true, group: nil, desc: nil, long_desc: nil, display_name: nil, add_method: nil, &block) # Source available in the toys-core gem end ## # 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 {Toys::Context#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 {Toys::DSL::PositionalArg} within the block. # # ### Example # # This tool "moves" something from a source to destination, and takes two # required arguments: # # tool "mv" do # required_arg :source # required_arg :dest # def run # puts "moving from #{source} to #{dest}..." # end # end # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param accept [Object] An acceptor that validates and/or converts the # value. You may provide either the name of an acceptor you have # defined, one of the default acceptors provided by OptionParser, or # any other specification recognized by {Toys::Acceptor.create}. # Optional. If not specified, accepts any value as a string. # @param complete [Object] A specifier for shell tab completion for # values of this arg. This is the empty completion by default. To # customize completion, set this to the name of a previously defined # completion, or any spec recognized by {Toys::Completion.create}. # @param display_name [String] A name to use for display (in help text # and error reports). Defaults to the key in upper case. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::DSL::Tool#desc} for a # description of the allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::DSL::Tool#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. # @param add_method [true,false,nil] Whether to add a method for this # argument. If omitted or set to nil, uses the default behavior, # which adds the method if the key is a symbol representing a legal # method name that starts with a letter and does not override any # public method in the Ruby Object class or collide with any method # directly defined in the tool class. # @param block [Proc] Configures the positional argument. See # {Toys::DSL::PositionalArg} for the directives that can be called in # this block. # @return [self] # def required_arg(key, accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil, add_method: nil, &block) # Source available in the toys-core gem end alias required required_arg ## # 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 {Toys::Context#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 {Toys::DSL::PositionalArg} within the block. # # ### Example # # This tool creates a "link" to a given target. The link location is # optional; if it is not given, it is inferred from the target. # # tool "ln" do # required_arg :target # optional_arg :location # def run # loc = location || File.basename(target) # puts "linking to #{target} from #{loc}..." # end # end # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param default [Object] 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`. # @param accept [Object] An acceptor that validates and/or converts the # value. You may provide either the name of an acceptor you have # defined, one of the default acceptors provided by OptionParser, or # any other specification recognized by {Toys::Acceptor.create}. # Optional. If not specified, accepts any value as a string. # @param complete [Object] A specifier for shell tab completion for # values of this arg. This is the empty completion by default. To # customize completion, set this to the name of a previously defined # completion, or any spec recognized by {Toys::Completion.create}. # @param display_name [String] A name to use for display (in help text # and error reports). Defaults to the key in upper case. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::DSL::Tool#desc} for a # description of the allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::DSL::Tool#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. # @param add_method [true,false,nil] Whether to add a method for this # argument. If omitted or set to nil, uses the default behavior, # which adds the method if the key is a symbol representing a legal # method name that starts with a letter and does not override any # public method in the Ruby Object class or collide with any method # directly defined in the tool class. # @param block [Proc] Configures the positional argument. See # {Toys::DSL::PositionalArg} for the directives that can be called in # this block. # @return [self] # def optional_arg(key, default: nil, accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil, add_method: nil, &block) # Source available in the toys-core gem end alias optional optional_arg ## # 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 {Toys::Context#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 {Toys::DSL::PositionalArg} within the block. # # ### Example # # This tool displays a "list" of the given directories. If no directories # ar given, lists the current directory. # # tool "ln" do # remaining_args :directories # def run # dirs = directories.empty? ? [Dir.pwd] : directories # dirs.each do |dir| # puts "Listing directory #{dir}..." # end # end # end # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param default [Object] 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 `[]`. # @param accept [Object] An acceptor that validates and/or converts the # value. You may provide either the name of an acceptor you have # defined, one of the default acceptors provided by OptionParser, or # any other specification recognized by {Toys::Acceptor.create}. # Optional. If not specified, accepts any value as a string. # @param complete [Object] A specifier for shell tab completion for # values of this arg. This is the empty completion by default. To # customize completion, set this to the name of a previously defined # completion, or any spec recognized by {Toys::Completion.create}. # @param display_name [String] A name to use for display (in help text # and error reports). Defaults to the key in upper case. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::DSL::Tool#desc} for a # description of the allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::DSL::Tool#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. # @param add_method [true,false,nil] Whether to add a method for these # arguments. If omitted or set to nil, uses the default behavior, # which adds the method if the key is a symbol representing a legal # method name that starts with a letter and does not override any # public method in the Ruby Object class or collide with any method # directly defined in the tool class. # @param block [Proc] Configures the positional argument. See # {Toys::DSL::PositionalArg} for the directives that can be called in # this block. # @return [self] # def remaining_args(key, default: [], accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil, add_method: nil, &block) # Source available in the toys-core gem end alias remaining remaining_args ## # Set option values statically and create helper methods. # # If any 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 {Toys::Context#get}. # # ### Example # # tool "hello" do # static :greeting, "Hi there" # def run # puts "#{greeting}, world!" # end # end # # @overload static(key, value) # Set a single value by key. # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param value [Object] The value to set. # @return [self] # # @overload static(hash) # Set multiple keys and values # @param hash [Hash] The keys and values to set # @return [self] # def static(key, value = nil) # Source available in the toys-core gem end ## # Set option values statically without creating helper methods. # # ### Example # # tool "hello" do # set :greeting, "Hi there" # def run # puts "#{get(:greeting)}, world!" # end # end # # @overload set(key, value) # Set a single value by key. # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param value [Object] The value to set. # @return [self] # # @overload set(hash) # Set multiple keys and values # @param hash [Hash] The keys and values to set # @return [self] # def set(key, value = nil) # Source available in the toys-core gem end ## # Enforce that all flags must be provided before any positional args. # That is, as soon as the first positional arg appears in the command # line arguments, flag parsing is disabled as if `--` had appeared. # # Issuing this directive by itself turns on enforcement. You may turn it # off by passsing `false` as the parameter. # # @param state [Boolean] # @return [self] # def enforce_flags_before_args(state = true) # Source available in the toys-core gem end ## # Require that flags must match exactly. That is, flags must appear in # their entirety on the command line. (If false, substrings of flags are # accepted as long as they are unambiguous.) # # Issuing this directive by itself turns on exact match. You may turn it # off by passsing `false` as the parameter. # # @param state [Boolean] # @return [self] # def require_exact_flag_match(state = true) # Source available in the toys-core gem end ## # 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 {Toys::Context#args}. # # This directive is mutually exclusive with any of the directives that # declare arguments or flags. # # ### Example # # tool "mytool" do # disable_argument_parsing # def run # puts "Arguments passed: #{args}" # end # end # # @return [self] # def disable_argument_parsing # Source available in the toys-core gem end ## # 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. # # ### Example # # This tool does not support the `-v` and `-q` short forms for the two # verbosity flags (although it still supports the long forms `--verbose` # and `--quiet`.) # # tool "mytool" do # disable_flag "-v", "-q" # def run # # ... # end # end # # @param flags [String...] The flags to disable # @return [self] # def disable_flag(*flags) # Source available in the toys-core gem end ## # Set the shell completion strategy for this tool's arguments. # You can pass one of the following: # # * The string name of a completion defined in this tool or any of its # its ancestors. # * A hash of options to pass to the constructor of # {Toys::ToolDefinition::DefaultCompletion}. # * `nil` or `:default` to select the standard completion strategy # (which is {Toys::ToolDefinition::DefaultCompletion} with no extra # options). # * Any other specification recognized by {Toys::Completion.create}. # # ### Example # # The namespace "foo" supports completion only of subtool names. It does # not complete the standard flags (like --help). # # tool "foo" do # complete_tool_args complete_args: false, complete_flags: false, # complete_flag_values: false # tool "bar" do # def run # puts "in foo bar" # end # end # end # # @param spec [Object] # @param options [Hash] # @param block [Proc] # @return [self] # def complete_tool_args(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Specify how to run this tool. # # Typically the entrypoint for a tool is a method named `run`. However, # you can change this by passing a different method name, as a symbol, to # {#to_run}. # # You can also alternatively pass a block to {#to_run}. You might do this # if your method needs access to local variables in the lexical scope. # However, it is often more convenient to use {#static} to set those # values in the context. # # ### Examples # # # Set a different method name as the entrypoint: # # tool "foo" do # to_run :foo # def foo # puts "The foo tool ran!" # end # end # # # Use a block to retain access to the enclosing lexical scope from # # the run method: # # tool "foo" do # cur_time = Time.now # to_run do # puts "The time at tool definition was #{cur_time}" # end # end # # # But the following is approximately equivalent: # # tool "foo" do # static :cur_time, Time.now # def run # puts "The time at tool definition was #{cur_time}" # end # end # # @param handler [Proc,Symbol,nil] The run handler as a method name # symbol or a proc, or nil to explicitly set as non-runnable. # @param block [Proc] The run handler as a block. # @return [self] # def to_run(handler = nil, &block) # Source available in the toys-core gem end alias on_run to_run ## # Specify how to handle interrupts. # # You can provide either a block to be called, a Proc to be called, or # the name of a method to be called. In each case, the block, Proc, or # method can optionally take one argument, the Interrupt exception that # was raised. # # Note: this is equivalent to `on_signal("SIGINT")`. # # ### Example # # tool "foo" do # def run # sleep 10 # end # on_interrupt do # puts "I was interrupted." # end # end # # @param handler [Proc,Symbol,nil] The interrupt callback proc or method # name. Pass nil to disable interrupt handling. # @param block [Proc] The interrupt callback as a block. # @return [self] # def on_interrupt(handler = nil, &block) # Source available in the toys-core gem end ## # Specify how to handle the given signal. # # You can provide either a block to be called, a Proc to be called, or # the name of a method to be called. In each case, the block, Proc, or # method can optionally take one argument, the SignalException that was # raised. # # ### Example # # tool "foo" do # def run # sleep 10 # end # on_signal("QUIT") do |e| # puts "Signal caught: #{e.signm}." # end # end # # @param signal [Integer,String,Symbol] The signal name or number # @param handler [Proc,Symbol,nil] The signal callback proc or method # name. Pass nil to disable signal handling. # @param block [Proc] The signal callback as a block. # @return [self] # def on_signal(signal, handler = nil, &block) # Source available in the toys-core gem end ## # Specify how to handle usage errors. # # You can provide either a block to be called, a Proc to be called, or # the name of a method to be called. In each case, the block, Proc, or # method can optionally take one argument, the array of usage errors # reported. # # ### Example # # This tool runs even if a usage error is encountered, by setting the # `run` method as the usage error handler. # # tool "foo" do # def run # puts "Errors: #{usage_errors.join("\n")}" # end # on_usage_error :run # end # # @param handler [Proc,Symbol,nil] The interrupt callback proc or method # name. Pass nil to disable interrupt handling. # @param block [Proc] The interrupt callback as a block. # @return [self] # def on_usage_error(handler = nil, &block) # Source available in the toys-core gem end ## # Specify that the given module should be mixed into this tool, and its # methods made available when running the tool. # # You can 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. # # ### Example # # Include the well-known mixin `:terminal` and perform some terminal # magic. # # tool "spin" do # include :terminal # def run # # The spinner method is defined by the :terminal mixin. # spinner(leading_text: "Waiting...", final_text: "\n") do # sleep 5 # end # end # end # # @param mixin [Module,Symbol,String] Module or module name. # @param args [Object...] Arguments to pass to the initializer # @param kwargs [keywords] Keyword arguments to pass to the initializer # @return [self] # def include(mixin, *args, **kwargs) # Source available in the toys-core gem end ## # Determine if the given module/mixin has already been included. # # You can 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. # # @param mod [Module,Symbol,String] Module or module name. # # @return [Boolean] Whether the mixin is included # @return [nil] if the current tool is not active. # def include?(mod) # Source available in the toys-core gem end ## # Return the current source info object. # # @return [Toys::SourceInfo] Source info. # def source_info # Source available in the toys-core gem end ## # Find the given data path (file or directory). # # Data directories are a convenient place to put images, archives, keys, # or other such static data needed by your tools. Data files are located # in a directory called `.data` inside a Toys directory. This directive # locates a data file during tool definition. # # ### Example # # This tool reads its description from a text file in the `.data` # directory. # # tool "mytool" do # path = find_data("mytool-desc.txt", type: :file) # desc IO.read(path) if path # def run # # ... # end # end # # @param path [String] The path to find # @param type [nil,:file,:directory] Type of file system object to find. # Default is `nil`, indicating any type. # # @return [String] Absolute path of the data. # @return [nil] if the given data path is not found. # def find_data(path, type: nil) # Source available in the toys-core gem end ## # Return the context directory for this tool. Generally, this defaults # to the directory containing the toys config directory structure being # read, but it may be changed by setting a different context directory # for the tool. # # @return [String] Context directory path # @return [nil] if there is no context. # def context_directory # Source available in the toys-core gem end ## # Return the current tool config. This object can be queried to determine # such information as the name, but it should not be altered. # # @return [Toys::ToolDefinition] # def current_tool # Source available in the toys-core gem end ## # Set a custom context directory for this tool. # # @param dir [String] Context directory # @return [self] # def set_context_directory(dir) # Source available in the toys-core gem end ## # Applies the given block to all subtools, recursively. Effectively, the # given block is run at the *end* of every tool block. This can be used, # for example, to provide some shared configuration for all tools. # # The block is applied only to subtools defined *after* the block # appears. Subtools defined before the block appears are not affected. # # ### Example # # It is common for tools to use the `:exec` mixin to invoke external # programs. This example automatically includes the exec mixin in all # subtools, recursively, so you do not have to repeat the `include` # directive in every tool. # # # .toys.rb # # subtool_apply do # # Include the mixin only if the tool hasn't already done so # unless include?(:exec) # include :exec, exit_on_nonzero_status: true # end # end # # tool "foo" do # def run # # This tool has access to methods defined by the :exec mixin # # because the above block is applied to the tool. # sh "echo hello" # end # end # def subtool_apply(&block) # Source available in the toys-core gem end ## # Remove lower-priority sources from the load path. This prevents lower- # priority sources (such as Toys files from parent or global directories) # from executing or defining tools. # # This works only if no such sources have already loaded yet. # # @raise [Toys::ToolDefinitionError] if any lower-priority tools have # already been loaded. # def truncate_load_path! # Source available in the toys-core gem end ## # Get the settings for this tool. # # @return [Toys::ToolDefinition::Settings] Tool-specific settings. # def settings # Source available in the toys-core gem end ## # Determines whether the current Toys version satisfies the given # requirements. # # @return [Boolean] whether or not the requirements are satisfied # def toys_version?(*requirements) # Source available in the toys-core gem end ## # Asserts that the current Toys version against the given requirements, # raising an exception if not. # # @return [self] # # @raise [Toys::ToolDefinitionError] if the current Toys version does not # satisfy the requirements. # def toys_version!(*requirements) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/dsl/base.rb0000644000004100000410000000326515164300675017532 0ustar www-datawww-data## # Create a base class for defining a tool with a given name. # # This method returns a base class for defining a tool with a given name. # This is useful if the naming behavior of {Toys::Tool} is not adequate for # your tool. # # ### Example # # class FooBar < Toys.Tool("Foo_Bar") # desc "This is a tool called Foo_Bar" # # def run # puts "Foo_Bar called" # end # end # # @param name [String] Name of the tool. Defaults to a name inferred from the # class name. (See {Toys::Tool}.) # @param base [Class] Use this tool class as the base class, and inherit helper # methods from it. # @param args [String,Class] Any string-valued positional argument is # interpreted as the name. Any class-valued positional argument is # interpreted as the base class. # def Toys.Tool(*args, name: nil, base: nil) # Source available in the toys-core gem end module Toys ## # **_Defined in the toys-core gem_** # # Base class for defining tools # # This base class provides an alternative to the {Toys::DSL::Tool#tool} # directive for defining tools in the Toys DSL. Creating a subclass of # `Toys::Tool` will create a tool whose name is the "kebab-case" of the class # name. Subclasses can be created only in the context of a tool configuration # DSL. Furthermore, a class-defined tool can be created only at the top level # of a configuration file, or within another class-defined tool. It cannot # be a subtool of a tool block. # # ### Example # # class FooBar < Toys::Tool # desc "This is a tool called foo-bar" # # def run # puts "foo-bar called" # end # end # class Tool < Context end end toys-0.21.0/core-docs/toys/dsl/flag.rb0000644000004100000410000002706515164300675017535 0ustar www-datawww-datamodule Toys module DSL ## # **_Defined in the toys-core gem_** # # DSL for a flag definition block. Lets you set flag attributes in a block # instead of a long series of keyword arguments. # # These directives are available inside a block passed to # {Toys::DSL::Tool#flag}. # # ### Example # # tool "mytool" do # flag :value do # # The directives in here are defined by this class # flags "--value=VAL" # accept Integer # desc "An integer value" # end # # ... # end # class Flag ## # Add flags in OptionParser format. This may be called multiple times, # and the results are cumulative. # # Following are examples of valid syntax. # # * `-a` : A short boolean switch. When this appears as an argument, # the value is set to `true`. # * `--abc` : A long boolean switch. When this appears as an argument, # the value is set to `true`. # * `-aVAL` or `-a VAL` : A short flag that takes a required value. # These two forms are treated identically. If this argument appears # with a value attached (e.g. `-afoo`), the attached string (e.g. # `"foo"`) is taken as the value. Otherwise, the following argument # is taken as the value (e.g. for `-a foo`, the value is set to # `"foo"`.) The following argument is treated as the value even if it # looks like a flag (e.g. `-a -a` causes the string `"-a"` to be # taken as the value.) # * `-a[VAL]` : A short flag that takes an optional value. If this # argument appears with a value attached (e.g. `-afoo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, the value # is set to `true`. The following argument is never interpreted as # the value. (Compare with `-a [VAL]`.) # * `-a [VAL]` : A short flag that takes an optional value. If this # argument appears with a value attached (e.g. `-afoo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, if the # following argument does not look like a flag (i.e. it does not # begin with a hyphen), it is taken as the value. (e.g. `-a foo` # causes the string `"foo"` to be taken as the value.). If there is # no following argument, or the following argument looks like a flag, # the value is set to `true`. (Compare with `-a[VAL]`.) # * `--abc=VAL` or `--abc VAL` : A long flag that takes a required # value. These two forms are treated identically. If this argument # appears with a value attached (e.g. `--abc=foo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, the # following argument is taken as the value (e.g. for `--abc foo`, the # value is set to `"foo"`.) The following argument is treated as the # value even if it looks like a flag (e.g. `--abc --def` causes the # string `"--def"` to be taken as the value.) # * `--abc[=VAL]` : A long flag that takes an optional value. If this # argument appears with a value attached (e.g. `--abc=foo`), the # attached string (e.g. `"foo"`) is taken as the value. Otherwise, # the value is set to `true`. The following argument is never # interpreted as the value. (Compare with `--abc [VAL]`.) # * `--abc [VAL]` : A long flag that takes an optional value. If this # argument appears with a value attached (e.g. `--abc=foo`), the # attached string (e.g. `"foo"`) is taken as the value. Otherwise, if # the following argument does not look like a flag (i.e. it does not # begin with a hyphen), it is taken as the value. (e.g. `--abc foo` # causes the string `"foo"` to be taken as the value.). If there is # no following argument, or the following argument looks like a flag, # the value is set to `true`. (Compare with `--abc=[VAL]`.) # * `--[no-]abc` : A long boolean switch that can be turned either on # or off. This effectively creates two flags, `--abc` which sets the # value to `true`, and `--no-abc` which sets the falue to `false`. # # @param flags [String...] # @return [self] # def flags(*flags) # Source available in the toys-core gem end ## # Set the acceptor for this flag's values. # You can pass either the string name of an acceptor defined in this tool # or any of its ancestors, or any other specification recognized by # {Toys::Acceptor.create}. # # @param spec [Object] # @param options [Hash] # @param block [Proc] # @return [self] # def accept(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Set the default value. # # @param default [Object] # @return [self] # def default(default) # Source available in the toys-core gem end ## # Set the optional handler that customizes how a value is set or updated # when the flag is parsed. # # A handler is a proc that takes up to three arguments: the given value, # the previous value, and a hash containing all the data collected so far # during argument parsing. It must return the new value for the flag. You # You may pass the handler as a Proc (or an object responding to the # `call` method) or you may provide a block. # # You may also specify a predefined named handler. The `:set` handler # (the default) replaces the previous value (effectively # `-> (val) { val }`). The `:push` handler expects the previous value to # be an array and pushes the given value onto it; it should be combined # with setting the default value to `[]` and is intended for # "multi-valued" flags. # # @param handler [Proc,:set,:push] # @param block [Proc] # @return [self] # def handler(handler = nil, &block) # Source available in the toys-core gem end ## # Set the shell completion strategy for flag names. # You can pass one of the following: # # * The string name of a completion defined in this tool or any of its # ancestors. # * A hash of options to pass to the constructor of # {Toys::Flag::DefaultCompletion}. # * `nil` or `:default` to select the standard completion strategy # (which is {Toys::Flag::DefaultCompletion} with no extra options). # * Any other specification recognized by {Toys::Completion.create}. # # @param spec [Object] # @param options [Hash] # @param block [Proc] # @return [self] # def complete_flags(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Set the shell completion strategy for flag values. # You can pass either the string name of a completion defined in this # tool or any of its ancestors, or any other specification recognized by # {Toys::Completion.create}. # # @param spec [Object] # @param options [Hash] # @param block [Proc] # @return [self] # def complete_values(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Set whether to raise an exception if a flag is requested that is # already in use or marked as disabled. # # @param setting [Boolean] # @return [self] # def report_collisions(setting) # Source available in the toys-core gem end ## # Set the short description for the current flag. The short description # is displayed with the flag in online help. # # The description is a {Toys::WrappableString}, which may be word-wrapped # when displayed in a help screen. You may pass a {Toys::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. # # ### Examples # # 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."] # # @param desc [String,Array,Toys::WrappableString] # @return [self] # def desc(desc) # Source available in the toys-core gem end ## # Add to the long description for the current flag. The long description # is displayed with the flag in online help. This directive may be given # multiple times, and the results are cumulative. # # 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 initial paragraph might get word wrapped.", # "This next paragraph is followed by a blank line.", # "", # ["This line will not be wrapped."], # [" This indent is preserved."] # long_desc "This line is appended to the description." # # @param long_desc [String,Array,Toys::WrappableString...] # @return [self] # def long_desc(*long_desc) # Source available in the toys-core gem end ## # Set the group. A group may be set by name or group object. Setting # `nil` selects the default group. # # @param group [String,Symbol,Toys::FlagGroup,nil] # @return [self] # def group(group) # Source available in the toys-core gem end ## # Set the display name for this flag. This may be used in help text and # error messages. # # @param display_name [String] # @return [self] # def display_name(display_name) # Source available in the toys-core gem end ## # Specify whether to add a method for this flag. # # Recognized values are true to force creation of a method, false to # disable method creation, and nil for the default behavior. The default # checks the name and adds a method if the name is a symbol representing # a legal method name that starts with a letter and does not override any # public method in the Ruby Object class or collide with any method # directly defined in the tool class. # # @param value [true,false,nil] # def add_method(value) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/dsl/flag_group.rb0000644000004100000410000003300715164300675020742 0ustar www-datawww-datamodule Toys module DSL ## # **_Defined in the toys-core gem_** # # DSL for a flag group definition block. Lets you create flags in a group. # # These directives are available inside a block passed to # {Toys::DSL::Tool#flag_group}, {Toys::DSL::Tool#all_required}, # {Toys::DSL::Tool#at_most_one}, {Toys::DSL::Tool#at_least_one}, or # {Toys::DSL::Tool#exactly_one}. # # ### Example # # tool "login" do # all_required do # # The directives in here are defined by this class # flag :username, "--username=VAL", desc: "Set username (required)" # flag :password, "--password=VAL", desc: "Set password (required)" # end # # ... # end # class FlagGroup ## # Add a flag to the current group. 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 {Toys::Context#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 {Toys::DSL::Flag} within the block. # # ### Flag syntax # # The flags themselves should be provided in OptionParser form. Following # are examples of valid syntax. # # * `-a` : A short boolean switch. When this appears as an argument, # the value is set to `true`. # * `--abc` : A long boolean switch. When this appears as an argument, # the value is set to `true`. # * `-aVAL` or `-a VAL` : A short flag that takes a required value. # These two forms are treated identically. If this argument appears # with a value attached (e.g. `-afoo`), the attached string (e.g. # `"foo"`) is taken as the value. Otherwise, the following argument # is taken as the value (e.g. for `-a foo`, the value is set to # `"foo"`.) The following argument is treated as the value even if it # looks like a flag (e.g. `-a -a` causes the string `"-a"` to be # taken as the value.) # * `-a[VAL]` : A short flag that takes an optional value. If this # argument appears with a value attached (e.g. `-afoo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, the value # is set to `true`. The following argument is never interpreted as # the value. (Compare with `-a [VAL]`.) # * `-a [VAL]` : A short flag that takes an optional value. If this # argument appears with a value attached (e.g. `-afoo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, if the # following argument does not look like a flag (i.e. it does not # begin with a hyphen), it is taken as the value. (e.g. `-a foo` # causes the string `"foo"` to be taken as the value.). If there is # no following argument, or the following argument looks like a flag, # the value is set to `true`. (Compare with `-a[VAL]`.) # * `--abc=VAL` or `--abc VAL` : A long flag that takes a required # value. These two forms are treated identically. If this argument # appears with a value attached (e.g. `--abc=foo`), the attached # string (e.g. `"foo"`) is taken as the value. Otherwise, the # following argument is taken as the value (e.g. for `--abc foo`, the # value is set to `"foo"`.) The following argument is treated as the # value even if it looks like a flag (e.g. `--abc --def` causes the # string `"--def"` to be taken as the value.) # * `--abc[=VAL]` : A long flag that takes an optional value. If this # argument appears with a value attached (e.g. `--abc=foo`), the # attached string (e.g. `"foo"`) is taken as the value. Otherwise, # the value is set to `true`. The following argument is never # interpreted as the value. (Compare with `--abc [VAL]`.) # * `--abc [VAL]` : A long flag that takes an optional value. If this # argument appears with a value attached (e.g. `--abc=foo`), the # attached string (e.g. `"foo"`) is taken as the value. Otherwise, if # the following argument does not look like a flag (i.e. it does not # begin with a hyphen), it is taken as the value. (e.g. `--abc foo` # causes the string `"foo"` to be taken as the value.). If there is # no following argument, or the following argument looks like a flag, # the value is set to `true`. (Compare with `--abc=[VAL]`.) # * `--[no-]abc` : A long boolean switch that can be turned either on # or off. This effectively creates two flags, `--abc` which sets the # value to `true`, and `--no-abc` which sets the falue to `false`. # # ### Default flag syntax # # If no flag syntax strings are provided, a default syntax will be # inferred based on the key and other options. # # Specifically, if the key has one character, then that character will be # chosen as a short flag. If the key has multiple characters, a long flag # will be generated. # # Furthermore, if a custom completion, a non-boolean acceptor, or a # non-boolean default value is provided in the options, then the flag # will be considered to take a value. Otherwise, it will be considered to # be a boolean switch. # # For example, the following pairs of flags are identical: # # flag :a # flag :a, "-a" # # flag :abc_def # flag :abc_def, "--abc-def" # # flag :number, accept: Integer # flag :number, "--number=VAL", accept: Integer # # ### More examples # # A flag that sets its value to the number of times it appears on the # command line: # # flag :verbose, "-v", "--verbose", # default: 0, handler: ->(_val, count) { count + 1 } # # An example using block form: # # flag :shout do # flags "-s", "--shout" # default false # desc "Say it louder" # long_desc "This flag says it lowder.", # "You might use this when people can't hear you.", # "", # "Example:", # [" toys say --shout hello"] # end # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param flags [String...] The flags in OptionParser format. # @param accept [Object] 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. # @param default [Object] 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`. # @param handler [Proc,nil,:set,:push] An optional handler that # customizes how a value is set or updated when the flag is parsed. # A handler is a proc that takes up to three arguments: the given # value, the previous value, and a hash containing all the data # collected so far during argument parsing. The proc must return the # new value for the flag. # You may also specify a predefined named handler. The `:set` handler # (the default) replaces the previous value (effectively # `-> (val) { val }`). The `:push` handler expects the previous value # to be an array and pushes the given value onto it; it should be # combined with setting the default value to `[]` and is intended for # "multi-valued" flags. # @param complete_flags [Object] A specifier for shell tab completion # for flag names associated with this flag. By default, a # {Toys::Flag::DefaultCompletion} is used, which provides the flag's # names as completion candidates. To customize completion, set this # to the name of a previously defined completion, a hash of options # to pass to the constructor for {Toys::Flag::DefaultCompletion}, or # any other spec recognized by {Toys::Completion.create}. # @param complete_values [Object] A specifier for shell tab completion # for flag values associated with this flag. This is the empty # completion by default. To customize completion, set this to the # name of a previously defined completion, or any spec recognized by # {Toys::Completion.create}. # @param report_collisions [Boolean] Raise an exception if a flag is # requested that is already in use or marked as unusable. Default is # true. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::DSL::Tool#desc} for a # description of the allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::DSL::Tool#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. # @param display_name [String] A display name for this flag, used in help # text and error messages. # @param add_method [true,false,nil] Whether to add a method for this # flag. If omitted or set to nil, uses the default behavior, which # adds the method if the key is a symbol representing a legal method # name that starts with a letter and does not override any public # method in the Ruby Object class or collide with any method directly # defined in the tool class. # @param block [Proc] Configures the flag. See {Toys::DSL::Flag} for the # directives that can be called in this block. # @return [self] # def flag(key, *flags, accept: nil, default: nil, handler: nil, complete_flags: nil, complete_values: nil, report_collisions: true, desc: nil, long_desc: nil, display_name: nil, add_method: nil, &block) # Source available in the toys-core gem end ## # Set the short description for the current flag group. The short # description is displayed as the group title in online help. # # The description is a {Toys::WrappableString}, which may be word-wrapped # when displayed in a help screen. You may pass a {Toys::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. # # ### Examples # # 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."] # # @param desc [String,Array,Toys::WrappableString] # @return [self] # def desc(desc) # Source available in the toys-core gem end ## # Add to the long description for the current flag group. The long # description is displayed with the flag group in online help. This # directive may be given multiple times, and the results are cumulative. # # 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 initial paragraph might get word wrapped.", # "This next paragraph is followed by a blank line.", # "", # ["This line will not be wrapped."], # [" This indent is preserved."] # long_desc "This line is appended to the description." # # @param long_desc [String,Array,Toys::WrappableString...] # @return [self] # def long_desc(*long_desc) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/utils/0000755000004100000410000000000015164300675016643 5ustar www-datawww-datatoys-0.21.0/core-docs/toys/utils/terminal.rb0000644000004100000410000002143015164300675021003 0ustar www-datawww-databegin require "io/console" rescue ::LoadError # TODO: alternate methods of getting terminal size end module Toys module Utils ## # **_Defined in the toys-core gem_** # # A simple terminal class. # # ### Styles # # This class supports ANSI styled output where supported. # # Styles may be specified in any of the following forms: # * A symbol indicating the name of a well-known style, or the name of # a defined style. # * An rgb string in hex "rgb" or "rrggbb" form. # * An ANSI code string in `\e[XXm` form. # * An array of ANSI codes as integers. # class Terminal ## # **_Defined in the toys-core gem_** # # Fatal terminal error. # class TerminalError < ::StandardError end ## # ANSI style code to clear styles # @return [String] # CLEAR_CODE = "\e[0m" ## # Standard ANSI style codes by name. # @return [Hash{Symbol => Array}] # BUILTIN_STYLE_NAMES = { clear: [0], reset: [0], bold: [1], faint: [2], italic: [3], underline: [4], blink: [5], reverse: [7], black: [30], red: [31], green: [32], yellow: [33], blue: [34], magenta: [35], cyan: [36], white: [37], on_black: [30], on_red: [31], on_green: [32], on_yellow: [33], on_blue: [34], on_magenta: [35], on_cyan: [36], on_white: [37], bright_black: [90], bright_red: [91], bright_green: [92], bright_yellow: [93], bright_blue: [94], bright_magenta: [95], bright_cyan: [96], bright_white: [97], on_bright_black: [100], on_bright_red: [101], on_bright_green: [102], on_bright_yellow: [103], on_bright_blue: [104], on_bright_magenta: [105], on_bright_cyan: [106], on_bright_white: [107], }.freeze ## # Default length of a single spinner frame, in seconds. # @return [Float] # DEFAULT_SPINNER_FRAME_LENGTH = 0.1 ## # Default set of spinner frames. # @return [Array] # DEFAULT_SPINNER_FRAMES = ["-", "\\", "|", "/"].freeze ## # Returns a copy of the given string with all ANSI style codes removed. # # @param str [String] Input string # @return [String] String with styles removed # def self.remove_style_escapes(str) # Source available in the toys-core gem end ## # Create a terminal. # # @param input [IO,nil] Input stream. # @param output [IO,Logger,nil] Output stream or logger. # @param styled [Boolean,nil] Whether to output ansi styles. If `nil`, the # setting is inferred from whether the output has a tty. # def initialize(input: $stdin, output: $stdout, styled: nil) # Source available in the toys-core gem end ## # Output stream or logger # @return [IO,Logger,nil] # attr_reader :output ## # Input stream # @return [IO,nil] # attr_reader :input ## # Whether output is styled # @return [Boolean] # attr_reader :styled ## # Write a partial line without appending a newline. # # @param str [String] The line to write # @param styles [Symbol,String,Array...] Styles to apply to the # partial line. # @return [self] # def write(str = "", *styles) # Source available in the toys-core gem end ## # Read a line, blocking until one is available. # # @return [String] the entire string including the temrinating newline # @return [nil] if the input is closed or at eof, or there is no input # def readline # Source available in the toys-core gem end ## # This method is defined so that `::Logger` will recognize a terminal as # a log device target, but it does not actually close anything. # def close # Source available in the toys-core gem end ## # Write a line, appending a newline if one is not already present. # # @param str [String] The line to write # @param styles [Symbol,String,Array...] Styles to apply to the # entire line. # @return [self] # def puts(str = "", *styles) # Source available in the toys-core gem end alias say puts ## # Write a line, appending a newline if one is not already present. # # @param str [String] The line to write # @return [self] # def <<(str) # Source available in the toys-core gem end ## # Write a newline and flush the current line. # @return [self] # def newline # Source available in the toys-core gem end ## # Ask a question and get a response. # # @param prompt [String] Required prompt string. # @param styles [Symbol,String,Array...] Styles to apply to the # prompt. # @param default [String,nil] Default value, or `nil` for no default. # Uses `nil` if not specified. # @param trailing_text [:default,String,nil] Trailing text appended to # the prompt, `nil` for none, or `:default` to show the default. # @return [String] # def ask(prompt, *styles, default: nil, trailing_text: :default) # Source available in the toys-core gem end ## # Confirm with the user. # # @param prompt [String] Prompt string. Defaults to `"Proceed?"`. # @param styles [Symbol,String,Array...] Styles to apply to the # prompt. # @param default [Boolean,nil] Default value, or `nil` for no default. # Uses `nil` if not specified. # @return [Boolean] # def confirm(prompt = "Proceed? ", *styles, default: nil) # Source available in the toys-core gem end ## # Display a spinner during a task. You should provide a block that # performs the long-running task. While the block is executing, a # spinner will be displayed. # # @param leading_text [String] Optional leading string to display to the # left of the spinner. Default is the empty string. # @param frame_length [Float] Length of a single frame, in seconds. # Defaults to {DEFAULT_SPINNER_FRAME_LENGTH}. # @param frames [Array] An array of frames. Defaults to # {DEFAULT_SPINNER_FRAMES}. # @param style [Symbol,Array] A terminal style or array of styles # to apply to all frames in the spinner. Defaults to empty, # @param final_text [String] Optional final string to display when the # spinner is complete. Default is the empty string. A common practice # is to set this to newline. # @return [Object] The return value of the block. # def spinner(leading_text: "", final_text: "", frame_length: nil, frames: nil, style: nil) # Source available in the toys-core gem end ## # Return the terminal size as an array of width, height. # # @return [Array(Integer,Integer)] # def size # Source available in the toys-core gem end ## # Return the terminal width # # @return [Integer] # def width # Source available in the toys-core gem end ## # Return the terminal height # # @return [Integer] # def height # Source available in the toys-core gem end ## # Define a named style. # # Style names must be symbols. # The definition of a style may include any valid style specification, # including the symbol names of existing defined styles. # # @param name [Symbol] The name for the style # @param styles [Symbol,String,Array...] # @return [self] # def define_style(name, *styles) # Source available in the toys-core gem end ## # Apply the given styles to the given string, returning the styled # string. Honors the styled setting; if styling is disabled, does not # add any ANSI style codes and in fact removes any existing codes. If # styles were added, ensures that the string ends with a clear code. # # @param str [String] String to style # @param styles [Symbol,String,Array...] Styles to apply # @return [String] The styled string # def apply_styles(str, *styles) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/utils/standard_ui.rb0000644000004100000410000001453515164300675021475 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # An object that implements standard UI elements, such as error reports and # logging, as provided by the `toys` command line. Specifically, it # implements pretty formatting of log entries and stack traces, and renders # using ANSI coloring where available via {Toys::Utils::Terminal}. # # This object can be used to implement `toys`-style behavior when creating # a CLI object. For example: # # require "toys/utils/standard_ui" # ui = Toys::Utils::StandardUI.new # cli = Toys::CLI.new(**ui.cli_args) # class StandardUI ## # Create a Standard UI. # # By default, all output is written to `$stderr`, and will share a single # {Toys::Utils::Terminal} object, allowing multiple tools and/or threads # to interleave messages without interrupting one another. # # @param output [IO,Toys::Utils::Terminal] Where to write output. You can # pass a terminal object, or an IO stream that will be wrapped in a # terminal output. Default is `$stderr`. # def initialize(output: nil) # Source available in the toys-core gem end ## # The terminal underlying this UI # # @return [Toys::Utils::Terminal] # attr_reader :terminal ## # A hash that maps severities to styles recognized by # {Toys::Utils::Terminal}. Used to style the header for each log entry. # This hash can be modified in place to adjust the behavior of loggers # created by this UI. # # @return [Hash{String => Array}] # attr_reader :log_header_severity_styles ## # Convenience method that returns a hash of arguments that can be passed # to the {Toys::CLI} constructor. Includes the `:error_handler` and # `:logger_factory` arguments. # # @return [Hash] # def cli_args # Source available in the toys-core gem end ## # Returns an error handler conforming to the `:error_handler` argument to # the {Toys::CLI} constructor. Specifically, it returns the # {#error_handler_impl} method as a proc. # # @return [Proc] # def error_handler # Source available in the toys-core gem end ## # Returns a logger factory conforming to the `:logger_factory` argument # to the {Toys::CLI} constructor. Specifically, it returns the # {#logger_factory_impl} method as a proc. # # @return [Proc] # def logger_factory # Source available in the toys-core gem end ## # Implementation of the error handler. As dictated by the error handler # specification in {Toys::CLI}, this must take a {Toys::ContextualError} # as an argument, and return an exit code. # # The base implementation uses {#display_error_notice} and # {#display_signal_notice} to print an appropriate message to the UI's # terminal, and uses {#exit_code_for} to determine the correct exit code. # Any of those methods can be overridden by a subclass to alter their # behavior, or this main implementation method can be overridden to # change the overall behavior. # # @param error [Toys::ContextualError] The error received # @return [Integer] The exit code # def error_handler_impl(error) # Source available in the toys-core gem end ## # Implementation of the logger factory. As dictated by the logger factory # specification in {Toys::CLI}, this must take a {Toys::ToolDefinition} # as an argument, and return a `Logger`. # # The base implementation returns a logger that writes to the UI's # terminal, using {#logger_formatter_impl} as the formatter. It sets the # level to `Logger::WARN` by default. Either this method or the helper # methods can be overridden to change this behavior. # # @param _tool {Toys::ToolDefinition} The tool definition of the tool to # be executed # @return [Logger] # def logger_factory_impl(_tool) # Source available in the toys-core gem end ## # Returns an exit code appropriate for the given exception. Currently, # the logic interprets signals (returning the convention of 128 + signo), # usage errors (returning the conventional value of 2), and tool not # runnable errors (returning the conventional value of 126), and defaults # to 1 for all other error types. # # This method is used by {#error_handler_impl} and can be overridden to # change its behavior. # # @param error [Exception] The exception raised. This method expects the # original exception, rather than a ContextualError. # @return [Integer] The appropriate exit code # def exit_code_for(error) # Source available in the toys-core gem end ## # Displays a default output for a signal received. # # This method is used by {#error_handler_impl} and can be overridden to # change its behavior. # # @param error [SignalException] # def display_signal_notice(error) # Source available in the toys-core gem end ## # Displays a default output for an error. Displays the error, the # backtrace, and contextual information regarding what tool was run and # where in its code the error occurred. # # This method is used by {#error_handler_impl} and can be overridden to # change its behavior. # # @param error [Toys::ContextualError] # def display_error_notice(error) # Source available in the toys-core gem end ## # Implementation of the formatter used by loggers created by this UI's # logger factory. This interface is defined by the standard `Logger` # class. # # This method can be overridden to change the behavior of loggers created # by this UI. # # @param severity [String] # @param time [Time] # @param _progname [String] # @param msg [Object] # @return [String] # def logger_formatter_impl(severity, time, _progname, msg) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/utils/pager.rb0000644000004100000410000001052515164300675020271 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # A class that invokes an external pager. # # @example Using a pager for regular output # # Toys::Utils::Pager.start do |io| # io.puts "A long string\n" # end # # @example Piping output from a command # # exec_service = Toys::Utils::Exec.new # Toys::Utils::Pager.start(exec_service: exec_service) do |io| # exec_service.exec(["/bin/ls", "-alF"], out: io) # end # class Pager ## # Creates a new pager. # # @param command [String,Array,boolean] The command to use to # invoke the pager. May be specified as a string to be passed to the # shell, an array of strings representing a posix command, the value # `true` to use the default (normally `less -FIRX`), or the value # `false` to disable the pager and write directly to the output # stream. Default is `true`. # @param exec_service [Toys::Utils::Exec] The service to use for # executing commands, or `nil` (the default) to use a default. # @param fallback_io [IO] An IO-like object to write to if the pager is # disabled. Defaults to `$stdout`. # @param rescue_broken_pipes [boolean] If `true` (the default), broken # pipes are silently rescued. This prevents the exception from # propagating out if the pager is interrupted. Set this parameter to # `false` to disable this behavior. # def initialize(command: true, exec_service: nil, fallback_io: nil, rescue_broken_pipes: true) # Source available in the toys-core gem end ## # Runs the pager. Takes a block and yields an IO-like object that passes # text to the pager. Can be called multiple times on the same pager. # # @yieldparam io [IO] An object that can be written to, to pass data to # the pager. # @return [Integer] The exit code of the pager process. # # @example # # pager = Toys::Utils::Pager.new # pager.start do |io| # io.puts "A long string\n" # end # def start # Source available in the toys-core gem end ## # The command for running the pager process. May be specified as a string # to be passed to the shell, an array of strings representing a posix # command, or `nil` to disable the pager and write directly to an output # stream. # # @return [String,Array,nil] # attr_accessor :command ## # The IO stream used if the pager is disabled or could not be executed. # # @return [IO] # attr_accessor :fallback_io class << self ## # A convenience method that creates a pager and runs it once by calling # {Pager#start}. # # @param command [String,Array,boolean] The command to use to # invoke the pager. May be specified as a string to be passed to the # shell, an array of strings representing a posix command, the value # `true` to use the default (normally `less -FIRX`), or the value # `false` to disable the pager and write directly to the output # stream. Default is `true`. # @param exec_service [Toys::Utils::Exec] The service to use for # executing commands, or `nil` (the default) to use a default. # @param fallback_io [IO] An IO-like object to write to if the pager is # disabled. Defaults to `$stdout`. # @param rescue_broken_pipes [boolean] If `true` (the default), broken # pipes are silently rescued. This prevents the exception from # propagating out if the pager is interrupted. Set this parameter to # `false` to disable this behavior. # @return [Integer] The exit code of the pager process. # # @example # # Toys::Utils::Pager.start do |io| # io.puts "A long string\n" # end # def start(command: true, exec_service: nil, fallback_io: nil, rescue_broken_pipes: true, &block) # Source available in the toys-core gem end end end end end toys-0.21.0/core-docs/toys/utils/git_cache.rb0000644000004100000410000002761615164300675021112 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # This object provides cached access to remote git data. Given a remote # repository, a path, and a commit, it makes the files available in the # local filesystem. Access is cached, so repeated requests for the same # commit and path in the same repo do not hit the remote repository again. # # This class is used by the Loader to load tools from git. Tools can also # use the `:git_cache` mixin for direct access to this class. # class GitCache ## # **_Defined in the toys-core gem_** # # GitCache encountered a failure # class Error < ::StandardError ## # Create a GitCache::Error. # # @param message [String] The error message # @param result [Toys::Utils::Exec::Result] The result of a git # command execution, or `nil` if this error was not due to a git # command error. # def initialize(message, result) # Source available in the toys-core gem end ## # @return [Toys::Utils::Exec::Result] The result of a git command # execution, or `nil` if this error was not due to a git command # error. # attr_reader :exec_result end ## # **_Defined in the toys-core gem_** # # Information about a remote git repository in the cache. # # This object is returned from {GitCache#repo_info}. # class RepoInfo include ::Comparable ## # The base directory of this git repository's cache entry. This # directory contains all cached data related to this repo. Deleting it # effectively removes the repo from the cache. # # @return [String] # attr_reader :base_dir ## # The git remote, usually a file system path or URL. # # @return [String] # attr_reader :remote ## # The last time any cached data from this repo was accessed, or `nil` # if the information is unavailable. # # @return [Time,nil] # attr_reader :last_accessed ## # A list of git refs (branches, tags, shas) that have been accessed # from this repo. # # @param ref [String,nil] If provided, return only entries matching # this ref name. If omitted, return all entries. # @return [Array] # def refs(ref: nil) # Source available in the toys-core gem end ## # A list of shared source files and directories accessed for this repo. # # @param sha [String,nil] If provided, return only entries matching # this SHA. If omitted, entries for all SHAs are included. # @param git_path [String,nil] If provided, return only entries # matching this git path. If omitted, entries for all paths are # included. # @return [Array] # def sources(sha: nil, git_path: nil) # Source available in the toys-core gem end ## # Convert this RepoInfo to a hash suitable for JSON output # # @return [Hash] # def to_h # Source available in the toys-core gem end ## # Comparison function # # @param other [RepoInfo] # @return [Integer] # def <=>(other) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # Information about a git ref used in a cache. # class RefInfo include ::Comparable ## # The git ref # # @return [String] # attr_reader :ref ## # The git sha last associated with the ref # # @return [String] # attr_reader :sha ## # The timestamp when this ref was last accessed # # @return [Time,nil] # attr_reader :last_accessed ## # The timestamp when this ref was last updated # # @return [Time,nil] # attr_reader :last_updated ## # Convert this RefInfo to a hash suitable for JSON output # # @return [Hash] # def to_h # Source available in the toys-core gem end ## # Comparison function # # @param other [RefInfo] # @return [Integer] # def <=>(other) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # Information about shared source files provided from the cache. # class SourceInfo include ::Comparable ## # The git sha the source comes from # # @return [String] # attr_reader :sha ## # The path within the git repo # # @return [String] # attr_reader :git_path ## # The path to the source file or directory # # @return [String] # attr_reader :source ## # The timestamp when this ref was last accessed # # @return [Time,nil] # attr_reader :last_accessed ## # Convert this SourceInfo to a hash suitable for JSON output # # @return [Hash] # def to_h # Source available in the toys-core gem end ## # Comparison function # # @param other [SourceInfo] # @return [Integer] # def <=>(other) # Source available in the toys-core gem end end ## # Returns whether shared source files are writable by default. # Normally, shared sources are made read-only to protect them from being # modified accidentally since multiple clients may be accessing them. # However, you can disable this feature by setting the environment # variable `TOYS_GIT_CACHE_WRITABLE` to any non-empty value. This can be # useful in environments that want to clean up temporary directories and # are being hindered by read-only files. # # @return [boolean] # def self.sources_writable? # Source available in the toys-core gem end ## # Access a git cache. # # @param cache_dir [String] The path to the cache directory. Defaults to # a specific directory in the user's XDG cache. # def initialize(cache_dir: nil) # Source available in the toys-core gem end ## # The cache directory. # # @return [String] # attr_reader :cache_dir ## # Get the given git-based files from the git cache, loading from the # remote repo if necessary. # # The resulting files are either copied into a directory you provide in # the `:into` parameter, or populated into a _shared_ source directory if # you omit the `:into` parameter. In the latter case, it is important # that you do not modify the returned files or directories, nor add or # remove any files from the directories returned, to avoid confusing # callers that could be given the same directory. If you need to make any # modifications to the returned files, use `:into` to provide your own # private directory. # # @param remote [String] The URL of the git repo. Required. # @param path [String] The path to the file or directory within the repo. # Optional. Defaults to the entire repo. # @param commit [String] The commit reference, which may be a SHA or any # git ref such as a branch or tag. Optional. Defaults to `HEAD`. # @param into [String] If provided, copies the specified files into the # given directory path. If omitted or `nil`, populates and returns a # shared source file or directory. # @param update [Boolean,Integer] Whether to update non-SHA commit # references if they were previously loaded. This is useful, for # example, if the commit is `HEAD` or a branch name. Pass `true` or # `false` to specify whether to update, or an integer to update if # last update was done at least that many seconds ago. Default is # `false`. # @param timestamp [Integer,nil] The timestamp for recording the access # time and determining whether a resource is stale. Normally, you # should leave this out and it will default to the current time. # # @return [String] The full path to the cached files. The returned path # will correspond to the path given. For example, if you provide the # path `Gemfile` representing a single file in the repository, the # returned path will point directly to the cached copy of that file. # def get(remote, path: nil, commit: nil, into: nil, update: false, timestamp: nil) # Source available in the toys-core gem end alias find get ## # Returns an array of the known remote names. # # @return [Array] # def remotes # Source available in the toys-core gem end ## # Returns a {RepoInfo} describing the cache for the given remote, or # `nil` if the given remote has never been cached. # # @param remote [String] Remote name for a repo # @return [RepoInfo,nil] # def repo_info(remote) # Source available in the toys-core gem end ## # Removes caches for the given repos, or all repos if specified. # # Removes all cache information for the specified repositories, including # local clones and shared source directories. The next time these # repositories are requested, they will be reloaded from the remote # repository from scratch. # # Be careful not to remove repos that are currently in use by other # GitCache clients. # # @param remotes [Array,:all,nil] The remotes to remove. If set # to :all or nil, removes all repos. # @return [Array] The remotes actually removed. # def remove_repos(remotes) # Source available in the toys-core gem end ## # Remove records of the given refs (i.e. branches, tags, or `HEAD`) from # the given repository's cache. The next time those refs are requested, # they will be pulled from the remote repo. # # If you provide the `refs:` argument, only those refs are removed. # Otherwise, all refs are removed. # # @param remote [String] The repository # @param refs [Array] The refs to remove. Optional. # @return [Array,nil] The refs actually forgotten, or `nil` if # the given repo is not in the cache. # def remove_refs(remote, refs: nil) # Source available in the toys-core gem end ## # Removes shared sources for the given cache. The next time a client # requests them, the removed sources will be recopied from the repo. # # If you provide the `commits:` argument, only sources associated with # those commits are removed. Otherwise, all sources are removed. # # Be careful not to remove sources that are currently in use by other # GitCache clients. # # @param remote [String] The repository # @param commits [Array] Remove only the sources for the given # commits. Optional. # @return [Array,nil] The sources actually removed, or `nil` # if the given repo is not in the cache. # def remove_sources(remote, commits: nil) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/utils/gems.rb0000644000004100000410000001433715164300675020133 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # A helper class that activates and installs gems and sets up bundler. # # This class is not loaded by default. Before using it directly, you should # `require "toys/utils/gems"` # class Gems ## # **_Defined in the toys-core gem_** # # Failed to activate a gem. # class ActivationFailedError < ::StandardError end ## # **_Defined in the toys-core gem_** # # Failed to install a gem. # class InstallFailedError < ActivationFailedError end ## # **_Defined in the toys-core gem_** # # Need to add a gem to the bundle. # class GemfileUpdateNeededError < ActivationFailedError ## # Create a GemfileUpdateNeededError. # # @param requirements_text [String] Gems and versions missing. # @param gemfile_path [String] Path to the offending Gemfile. # def initialize(requirements_text, gemfile_path) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # Failed to run Bundler # class BundlerFailedError < ::StandardError end ## # **_Defined in the toys-core gem_** # # Could not find a Gemfile # class GemfileNotFoundError < BundlerFailedError end ## # **_Defined in the toys-core gem_** # # The bundle is not and could not be installed # class BundleNotInstalledError < BundlerFailedError end ## # **_Defined in the toys-core gem_** # # Bundler has already been run; cannot do so again # class AlreadyBundledError < BundlerFailedError end ## # **_Defined in the toys-core gem_** # # The bundle contained a toys or toys-core dependency that is # incompatible with the currently running version. # class IncompatibleToysError < BundlerFailedError end ## # The gemfile names that are searched by default. # @return [Array] # DEFAULT_GEMFILE_NAMES = [".gems.rb", "gems.rb", "Gemfile"].freeze ## # Activate the given gem. If it is not present, attempt to install it (or # inform the user to update the bundle). # # @param name [String] Name of the gem # @param requirements [String...] Version requirements # @return [void] # def self.activate(name, *requirements) # Source available in the toys-core gem end ## # Create a new gem activator. # # @param on_missing [:confirm,:error,:install] What to do if a needed gem # is not installed. Possible values: # # * `:confirm` - prompt the user on whether to install # * `:error` - raise an exception # * `:install` - just install the gem # # The default is `:confirm`. # # @param on_conflict [:error,:warn,:ignore] What to do if bundler has # already been run with a different Gemfile. Possible values: # # * `:error` - raise an exception # * `:ignore` - just silently proceed without bundling again # * `:warn` - print a warning and proceed without bundling again # # The default is `:error`. # # @param default_confirm [Boolean] The default confirmation result, if # `on_missing` is set to `:confirm`. Defaults to true. # @param terminal [Toys::Utils::Terminal] Terminal to use (optional) # @param input [IO] Input IO (optional, defaults to STDIN) # @param output [IO] Output IO (optional, defaults to STDOUT) # @param suppress_confirm [Boolean] Deprecated. Use `on_missing` instead. # def initialize(on_missing: nil, on_conflict: nil, terminal: nil, input: nil, output: nil, suppress_confirm: nil, default_confirm: nil) # Source available in the toys-core gem end ## # Activate the given gem. If it is not present, attempt to install it (or # inform the user to update the bundle). # # @param name [String] Name of the gem # @param requirements [String...] Version requirements # @return [void] # def activate(name, *requirements) # Source available in the toys-core gem end ## # Search for an appropriate Gemfile, and set up the bundle. # # @param groups [Array] The groups to include in setup. # # @param gemfile_path [String] The path to the Gemfile to use. If `nil` # or not given, the `:search_dirs` will be searched for a Gemfile. # # @param search_dirs [String,Array] Directories in which to # search for a Gemfile, if gemfile_path is not given. You can provide # a single directory or an array of directories. # # @param gemfile_names [String,Array] File names that are # recognized as Gemfiles, when searching because gemfile_path is not # given. Defaults to {DEFAULT_GEMFILE_NAMES}. # # @param retries [Integer] Number of times to retry bundler operations. # Optional. # # @return [void] # def bundle(groups: nil, gemfile_path: nil, search_dirs: nil, gemfile_names: nil, retries: nil) # Source available in the toys-core gem end # @private def self.find_gemfile(search_dir, gemfile_names: nil) # Source available in the toys-core gem end @global_mutex = ::Monitor.new # @private def self.synchronize(&block) # Source available in the toys-core gem end @delete_at_exit_mutex = ::Mutex.new @delete_at_exit_list = nil # @private # This is a class method so the at_exit block doesn't hold onto an # instance for the duration of the Ruby process def self.delete_at_exit(path) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/utils/completion_engine.rb0000644000004100000410000000363215164300675022672 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # Implementations of tab completion. # # This module is not loaded by default. Before using it directly, you must # `require "toys/utils/completion_engine"` # module CompletionEngine ## # **_Defined in the toys-core gem_** # # Base class for shell completion engines that use a # `COMP_LINE` / `COMP_POINT` protocol. # # Subclasses must implement the private methods `#shell_name` and # `#output_completions`. # class Base ## # Create a completion engine. # # @param cli [Toys::CLI] The CLI. # def initialize(cli) # Source available in the toys-core gem end ## # Perform completion in the current shell environment, which must # include settings for the `COMP_LINE` and `COMP_POINT` environment # variables. Prints out completion candidates and returns a status code # indicating the result. # # * **0** for success. # * **1** if completion failed. # * **2** if the environment is incorrect (e.g. expected environment # variables not found) # # @return [Integer] status code # def run # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A completion engine for bash. # class Bash < Base ## # Create a bash completion engine. # # @param cli [Toys::CLI] The CLI. # def initialize(cli) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A completion engine for zsh. # class Zsh < Base end class << self end end end end toys-0.21.0/core-docs/toys/utils/xdg.rb0000644000004100000410000003405715164300675017763 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # A class that provides tools for working with the XDG Base Directory # Specification. # # This class provides utility methods that locate base directories and # search paths for application state, configuration, caches, and other # data, according to the [XDG Base Directory Spec version # 0.8](https://specifications.freedesktop.org/basedir/0.8/). # # Tools can use the `:xdg` mixin for convenient access to this class. # # ### Example # # require "toys/utils/xdg" # # xdg = Toys::Utils::XDG.new # # # Get config file paths, in order from most to least important # config_files = xdg.lookup_config("my-config.toml") # config_files.each { |path| read_my_config(path) } # # ### Windows operation # # The Spec assumes a unix-like environment, and cannot be applied directly # to Windows without modification. In general, this class will function on # Windows, but with the following caveats: # # * All file paths must use Windows-style absolute paths, beginning with # the drive letter. # * Environment variables that can contain multiple paths (`XDG_*_DIRS`) # use the Windows path delimiter (`;`) rather than the unix path # delimiter (`:`). # * Defaults for home directories (`XDG_*_HOME`) will follow unix # conventions, using subdirectories under the user's profile directory # rather than the Windows known folder paths. # * Defaults for search paths (`XDG_*_DIRS`) will be empty and will not # use the Windows known folder paths. # class XDG ## # **_Defined in the toys-core gem_** # # An error raised in certain cases when a lookup fails. # class Error < ::StandardError end ## # Create an instance of XDG. # # @param env [Hash{String=>String}] the environment variables. Normally, # you can omit this argument, as it will default to `::ENV`. # def initialize(env: ::ENV) # Source available in the toys-core gem end ## # Returns the absolute path to the current user's home directory. # # @return [String] # def home_dir # Source available in the toys-core gem end ## # Returns the absolute path to the single base directory relative to # which user-specific data files should be written. # # Corresponds to the value of the `$XDG_DATA_HOME` environment variable # and its defaults according to the XDG Base Directory Spec. # # @return [String] # def data_home # Source available in the toys-core gem end ## # Returns the absolute path to the single base directory relative to # which user-specific configuration files should be written. # # Corresponds to the value of the `$XDG_CONFIG_HOME` environment variable # and its defaults according to the XDG Base Directory Spec. # # @return [String] # def config_home # Source available in the toys-core gem end ## # Returns the absolute path to the single base directory relative to # which user-specific state files should be written. # # Corresponds to the value of the `$XDG_STATE_HOME` environment variable # and its defaults according to the XDG Base Directory Spec. # # @return [String] # def state_home # Source available in the toys-core gem end ## # Returns the absolute path to the single base directory relative to # which user-specific non-essential (cached) data should be written. # # Corresponds to the value of the `$XDG_CACHE_HOME` environment variable # and its defaults according to the XDG Base Directory Spec. # # @return [String] # def cache_home # Source available in the toys-core gem end ## # Returns the absolute path to the single base directory relative to # which user-specific executable files may be written. # # Returns the value of `$HOME/.local/bin` as specified by the XDG Base # Directory Spec. # # @return [String] # def executable_home # Source available in the toys-core gem end ## # Returns the set of preference ordered base directories relative to # which data files should be searched, as an array of absolute paths. # The array is ordered from most to least important, and does _not_ # include the data home directory. # # Corresponds to the value of the `$XDG_DATA_DIRS` environment variable # and its defaults according to the XDG Base Directory Spec. # # @return [Array] # def data_dirs # Source available in the toys-core gem end ## # Returns the set of preference ordered base directories relative to # which configuration files should be searched, as an array of absolute # paths. The array is ordered from most to least important, and does # _not_ include the config home directory. # # Corresponds to the value of the `$XDG_CONFIG_DIRS` environment variable # and its defaults according to the XDG Base Directory Spec. # # @return [Array] # def config_dirs # Source available in the toys-core gem end ## # Returns the absolute path to the single base directory relative to # which user-specific runtime files and other file objects should be # placed. # # Corresponds to the value of the `$XDG_RUNTIME_DIR` environment variable # according to the XDG Base Directory Spec. # # **Important:** Returns `nil` if the `$XDG_RUNTIME_DIR` environment # variable is unset or invalid. In such a case, it is the caller's # responsibility to determine a fallback strategy, as this library cannot # by itself implement a compliant fallback without OS help. # # @return [String,nil] # def runtime_dir # Source available in the toys-core gem end ## # Returns the absolute path to the single base directory relative to # which user-specific runtime files and other file objects should be # placed. # # Corresponds to the value of the `$XDG_RUNTIME_DIR` environment variable # according to the XDG Base Directory Spec. # # Raises {Toys::Utils::XDG::Error} if the `$XDG_RUNTIME_DIR` environment # variable is unset or invalid. Unlike {#runtime_dir}, does not return # nil. # # @return [String] # def runtime_dir! # Source available in the toys-core gem end ## # Searches the data directories for an object with the given relative # path, and returns an array of absolute paths to all objects found in # all data directories (i.e. {#data_home} and {#data_dirs}), in order # from most to least important. Returns the empty array if no suitable # objects are found. # # If multiple objects are found, the caller should implement its own # logic to resolve them. For example, it can select the first (most # important) object, or implement logic to combine the contents. # # @param path [String] Relative path of the object to search for # @param type [String,Symbol,Array] The type(s) of objects # to find. You can specify any of the types defined by # [File::Stat#ftype](https://ruby-doc.org/core/File/Stat.html#method-i-ftype), # such as `file` or `directory`, or the special type `any`. Types can # be specified as strings or the corresponding symbols. If this # argument is not provided, the default of `file` is used. # @return [Array] # def lookup_data(path, type: :file) # Source available in the toys-core gem end ## # Searches the config directories for an object with the given relative # path, and returns an array of absolute paths to all objects found in # all config directories (i.e. {#config_home} and {#config_dirs}), in # order from most to least important. Returns the empty array if no # suitable objects are found. # # If multiple objects are found, the caller should implement its own # logic to resolve them. For example, it can select the first (most # important) object, or implement logic to combine the contents. # # @param path [String] Relative path of the object to search for # @param type [String,Symbol,Array] The type(s) of objects # to find. You can specify any of the types defined by # [File::Stat#ftype](https://ruby-doc.org/core/File/Stat.html#method-i-ftype), # such as `file` or `directory`, or the special type `any`. Types can # be specified as strings or the corresponding symbols. If this # argument is not provided, the default of `file` is used. # @return [Array] # def lookup_config(path, type: :file) # Source available in the toys-core gem end ## # Searches the state directory ({#state_home}) for an object with the # given relative path, and returns an array of zero or one absolute paths # to any found object. Because the XDG basedir spec does not provide for # a list of fallback directories for state files (i.e. there is no # `XDG_STATE_DIRS` variable or list of default paths), this will return a # maximum of one result. However, it returns an array for consistency # with the {#lookup_data} and {#lookup_config} methods. # # @param path [String] Relative path of the object to search for # @param type [String,Symbol,Array] The type(s) of objects # to find. You can specify any of the types defined by # [File::Stat#ftype](https://ruby-doc.org/core/File/Stat.html#method-i-ftype), # such as `file` or `directory`, or the special type `any`. Types can # be specified as strings or the corresponding symbols. If this # argument is not provided, the default of `file` is used. # @return [Array] # def lookup_state(path, type: :file) # Source available in the toys-core gem end ## # Searches the cache directory ({#cache_home}) for an object with the # given relative path, and returns an array of zero or one absolute paths # to any found object. Because the XDG basedir spec does not provide for # a list of fallback directories for cache files (i.e. there is no # `XDG_CACHE_DIRS` variable or list of default paths), this will return a # maximum of one result. However, it returns an array for consistency # with the {#lookup_data} and {#lookup_config} methods. # # @param path [String] Relative path of the object to search for # @param type [String,Symbol,Array] The type(s) of objects # to find. You can specify any of the types defined by # [File::Stat#ftype](https://ruby-doc.org/core/File/Stat.html#method-i-ftype), # such as `file` or `directory`, or the special type `any`. Types can # be specified as strings or the corresponding symbols. If this # argument is not provided, the default of `file` is used. # @return [Array] # def lookup_cache(path, type: :file) # Source available in the toys-core gem end ## # Returns the absolute path to a directory under {#data_home}, creating # it if it doesn't already exist. # # @param path [String] The relative path to the subdir within the base # data directory. # @return [String] The absolute path to the subdir. # @raise [SystemCallError] If a non-directory already exists there. It is # unspecified which specific error will be raised; it typically could # be `Errno::EEXIST` or `Errno::ENOTDIR`. # def ensure_data_subdir(path) # Source available in the toys-core gem end ## # Returns the absolute path to a directory under {#config_home}, creating # it if it doesn't already exist. # # @param path [String] The relative path to the subdir within the base # config directory. # @return [String] The absolute path to the subdir. # @raise [SystemCallError] If a non-directory already exists there. It is # unspecified which specific error will be raised; it typically could # be `Errno::EEXIST` or `Errno::ENOTDIR`. # def ensure_config_subdir(path) # Source available in the toys-core gem end ## # Returns the absolute path to a directory under {#state_home}, creating # it if it doesn't already exist. # # @param path [String] The relative path to the subdir within the base # state directory. # @return [String] The absolute path to the subdir. # @raise [SystemCallError] If a non-directory already exists there. It is # unspecified which specific error will be raised; it typically could # be `Errno::EEXIST` or `Errno::ENOTDIR`. # def ensure_state_subdir(path) # Source available in the toys-core gem end ## # Returns the absolute path to a directory under {#cache_home}, creating # it if it doesn't already exist. # # @param path [String] The relative path to the subdir within the base # cache directory. # @return [String] The absolute path to the subdir. # @raise [SystemCallError] If a non-directory already exists there. It is # unspecified which specific error will be raised; it typically could # be `Errno::EEXIST` or `Errno::ENOTDIR`. # def ensure_cache_subdir(path) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/utils/exec.rb0000644000004100000410000010611115164300675020114 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # A service that executes subprocesses. # # This service provides a convenient interface for controlling spawned # processes and their streams. It also provides shortcuts for common cases # such as invoking Ruby in a subprocess or capturing output in a string. # # This class is not loaded by default. Before using it directly, you should # `require "toys/utils/exec"` # # ### The exec service # # The main entrypoint class is this one, {Toys::Utils::Exec}. It's a # "service" object that provides functionality, primarily methods that # spawn processes. Create it like any object: # # require "toys/utils/exec" # exec_service = Toys::Utils::Exec.new # # There are two "primitive" functions: {#exec} and {#exec_proc}. The {#exec} # method spawns an operating system process specified by an executable and # a set of arguments. The {#exec_proc} method takes a `Proc` and forks a # Ruby process. Both of these can be heavily configured with stream # handling, result handling, and numerous other options described below. # The class also provides convenience methods for common cases such as # spawning a Ruby process, spawning a shell script, or capturing output. # # The exec service class also stores default configuration that it applies # to processes it spawns. You can set these defaults when constructing the # service class, or at any time by calling {#configure_defaults}. # # ### Stream handling # # By default, subprocess streams are connected to the corresponding streams # in the parent process. You can change this behavior, redirecting streams # or providing ways to control them, using the `:in`, `:out`, and `:err` # options. # # Three general strategies are available for custom stream handling. First, # you can redirect to other streams such as files, IO objects, or Ruby # strings. Some of these options map directly to options provided by the # `Process#spawn` method. Second, you can use a controller to manipulate # the streams programmatically. Third, you can capture output stream data # and make it available in the result. # # Following is a full list of the stream handling options, along with how # to specify them using the `:in`, `:out`, and `:err` options. # # * **Inherit parent stream:** You can inherit the corresponding stream # in the parent process by passing `:inherit` as the option value. This # is the default if the subprocess is run in the foreground. # # * **Redirect to null:** You can redirect to a null stream by passing # `:null` as the option value. This connects to a stream that is not # closed but contains no data, i.e. `/dev/null` on unix systems. This # is the default if the subprocess is run in the background. # # * **Close the stream:** You can close the stream by passing `:close` as # the option value. This is the same as passing `:close` to # `Process#spawn`. # # * **Redirect to a file:** You can redirect to a file. This reads from # an existing file when connected to `:in`, and creates or appends to a # file when connected to `:out` or `:err`. To specify a file, use the # setting `[:file, "/path/to/file"]`. You can also, when writing a # file, append an optional mode and permission code to the array. For # example, `[:file, "/path/to/file", "a", 0644]`. # # * **Redirect to an IO object:** You can redirect to an IO object in the # parent process, by passing the IO object as the option value. You can # use any IO object. For example, you could connect the child's output # to the parent's error using `out: $stderr`, or you could connect to # an existing File stream. Unlike `Process#spawn`, this works for IO # objects that do not have a corresponding file descriptor (such as # StringIO objects). In such a case, a thread will be spawned to pipe # the IO data through to the child process. Note that the IO object # will _not_ be closed on completion. # # * **Redirect to a pipe:** You can redirect to a pipe created using # `IO.pipe` (i.e. a two-element array of read and write IO objects) by # passing the array as the option value. This will connect the # appropriate IO (either read or write), and close it in the parent. # Thus, you can connect only one process to each end. If you want more # direct control over IO closing behavior, pass the IO object (i.e. the # element of the pipe array) directly. # # * **Combine with another child stream:** You can redirect one child # output stream to another, to combine them. To merge the child's error # stream into its output stream, use `err: [:child, :out]`. # # * **Read from a string:** You can pass a string to the input stream by # setting `[:string, "the string"]`. This works only for `:in`. # # * **Capture output stream:** You can capture a stream and make it # available on the {Toys::Utils::Exec::Result} object, using the # setting `:capture`. This works only for the `:out` and `:err` # streams. # # * **Use the controller:** You can hook a stream to the controller using # the setting `:controller`. You can then manipulate the stream via the # controller. If you pass a block to {Toys::Utils::Exec#exec}, it # yields the {Toys::Utils::Exec::Controller}, giving you access to # streams. See the section below on controlling processes. # # * **Make copies of an output stream:** You can "tee," or duplicate the # `:out` or `:err` stream and redirect those copies to various # destinations. To specify a tee, use the setting `[:tee, ...]` where # the additional array elements include two or more of the following. # See the corresponding documentation above for more detail. # * `:inherit` to direct to the parent process's stream. # * `:capture` to capture the stream and store it in the result. # * `:controller` to direct the stream to the controller. # * `[:file, "/path/to/file"]` to write to a file. # * An `IO` or `StringIO` object. # * An array of two `IO` objects representing a pipe # # Additionally, the last element of the array can be a hash of options. # Supported options include: # * `:buffer_size` The size of the memory buffer for each element of # the tee. Larger buffers may allow higher throughput. The default # is 65536. # # ### Controlling processes # # A process can be started in the *foreground* or the *background*. If you # start a foreground process, it will inherit your standard input and # output streams by default, and it will keep control until it completes. # If you start a background process, its streams will be redirected to null # by default, and control will be returned to you immediately. # # While a process is running, you can control it using a # {Toys::Utils::Exec::Controller} object. Use a controller to interact with # the process's input and output streams, send it signals, or wait for it # to complete. # # When running a process in the foreground, the controller will be yielded # to an optional block. For example, the following code starts a process in # the foreground and passes its output stream to a controller. # # exec_service.exec(["git", "init"], out: :controller) do |controller| # loop do # line = controller.out.gets # break if line.nil? # puts "Got line: #{line}" # end # end # # At the end of the block, if the controller is handling the process's # input stream, that stream will automatically be closed. The following # example programmatically sends data to the `wc` unix program, and # captures its output. Because the controller is handling the input stream, # it automatically closes the stream at the end of the block, which causes # `wc` to end. # # result = exec_service.exec(["wc"], # in: :controller, # out: :capture) do |controller| # controller.in.puts "Hello, world!" # end # puts "Results: #{result.captured_out}" # # Otherwise, depending on the process's behavior, it may continue to run # after the end of the block. Control will not be returned to the caller # until the process actually terminates. Conversely, it is also possible # the process could terminate by itself while the block is still executing. # You can call controller methods to obtain the process's actual current # state. # # When running a process in the background, the controller is returned # immediately from the method that starts the process. In the following # example, git init is kicked off in the background and the output is # thrown away to /dev/null. # # controller = exec_service.exec(["git", "init"], background: true) # # In this mode, use the returned controller to query the process's state # and interact with it. Streams directed to the controller are not # automatically closed, so you will need to do so yourself. Following is an # example of running `wc` in the background: # # controller = exec_service.exec(["wc"], background: true, # in: :controller, out: :controller) # controller.in.puts "Hello, world!" # controller.in.close # Do this explicitly to cause wc to finish # puts "Results: #{controller.out.read}" # Read the entire stream # # ### Result handling # # A subprocess result is represented by a {Toys::Utils::Exec::Result} # object, which includes the exit code, the content of any captured output # streams, and any exception raised when attempting to run the process. # When you run a process in the foreground, the method will return a result # object. When you run a process in the background, you can obtain the # result from the controller once the process completes. # # The following example demonstrates running a process in the foreground # and getting the exit code: # # result = exec_service.exec(["git", "init"]) # puts "exit code: #{result.exit_code}" # # The following example demonstrates starting a process in the background, # waiting for it to complete, and getting its exit code: # # controller = exec_service.exec(["git", "init"], background: true) # result = controller.result(timeout: 1.0) # if result # puts "exit code: #{result.exit_code}" # else # puts "timed out" # end # # You can also provide a callback that is executed once a process # completes. For example: # # my_callback = proc do |result| # puts "exit code: #{result.exit_code}" # end # exec_service.exec(["git", "init"], result_callback: my_callback) # # In foreground mode, the callback is executed in the calling thread, after # the process terminates (and after any controller block has completed) but # before control is returned to the caller. In background mode, the # callback is executed asynchronously in a separate thread after the # process terminates. # # ### Configuration options # # A variety of options can be used to control subprocesses. These can be # provided to any method that starts a subprocess. You can also set # defaults by calling {Toys::Utils::Exec#configure_defaults}. # # Options that affect the behavior of subprocesses: # # * `:env` (Hash) Environment variables to pass to the subprocess. # Keys represent variable names and should be strings. Values should be # either strings or `nil`, which unsets the variable. # # * `:background` (boolean) Runs the process in the background if `true`. # # * `:result_callback` (Proc) Called and passed the result object when # the subprocess exits. If the process was run in the background, this # callback is executed in a separate thread. If the process was run in # the foreground, this callback is executed in the calling thread. # # * `:unbundle` (boolean) Disables any existing bundle when running the # subprocess. Has no effect if Bundler isn't active at the call point. # Cannot be used when executing in a fork, e.g. via {#exec_proc}. # # Options for connecting input and output streams. See the section above on # stream handling for info on the values that can be passed. # # * `:in` Connects the input stream of the subprocess. See the section on # stream handling. # # * `:out` Connects the standard output stream of the subprocess. See the # section on stream handling. # # * `:err` Connects the standard error stream of the subprocess. See the # section on stream handling. # # Options related to logging and reporting: # # * `:logger` (Logger) Logger to use for logging the actual command. If # not present, the command is not logged. # # * `:log_level` (Integer,false) Level for logging the actual command. # Defaults to Logger::INFO if not present. You can also pass `false` to # disable logging of the command. # # * `:log_cmd` (String) The string logged for the actual command. # Defaults to the `inspect` representation of the command. # # * `:name` (Object) An optional object that can be used to identify this # subprocess. It is available in the controller and result objects. # # In addition, the following options recognized by # [`Process#spawn`](https://ruby-doc.org/core/Process.html#method-c-spawn) # are supported. # # * `:chdir` (String) Set the working directory for the command. # # * `:close_others` (boolean) Whether to close non-redirected # non-standard file descriptors. # # * `:new_pgroup` (boolean) Create new process group (Windows only). # # * `:pgroup` (Integer,true,nil) The process group setting. # # * `:umask` (Integer) Umask setting for the new process. # # * `:unsetenv_others` (boolean) Clear environment variables except those # explicitly set. # # Any other option key will result in an `ArgumentError`. # class Exec ## # Create an exec service. # # @param block [Proc] A block that is called if a key is not found. It is # passed the unknown key, and expected to return a default value # (which can be nil). # @param opts [keywords] Initial default options. See {Toys::Utils::Exec} # for a description of the options. # def initialize(**opts, &block) # Source available in the toys-core gem end ## # Set default options. See {Toys::Utils::Exec} for a description of the # options. # # @param opts [keywords] New default options to set # @return [self] # def configure_defaults(**opts) # Source available in the toys-core gem end ## # Execute a command. The command can be given as a single string to pass # to a shell, or an array of strings indicating a posix command. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # @param cmd [String,Array] The command to execute. # @param opts [keywords] The command options. See the section on # configuration options in the {Toys::Utils::Exec} class docs. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec(cmd, **opts, &block) # Source available in the toys-core gem end ## # Spawn a ruby process and pass the given arguments to it. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # @param args [String,Array] The arguments to ruby. # @param opts [keywords] The command options. See the section on # configuration options in the {Toys::Utils::Exec} class docs. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec_ruby(args, **opts, &block) # Source available in the toys-core gem end alias ruby exec_ruby ## # Execute a proc in a fork. # # If the process is not set to run in the background, and a block is # provided, a {Toys::Utils::Exec::Controller} will be yielded to it. # # @param func [Proc] The proc to call. # @param opts [keywords] The command options. See the section on # configuration options in the {Toys::Utils::Exec} class docs. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # # @return [Toys::Utils::Exec::Controller] The subprocess controller, if # the process is running in the background. # @return [Toys::Utils::Exec::Result] The result, if the process ran in # the foreground. # def exec_proc(func, **opts, &block) # Source available in the toys-core gem end ## # Execute a command. The command can be given as a single string to pass # to a shell, or an array of strings indicating a posix command. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # @param cmd [String,Array] The command to execute. # @param opts [keywords] The command options. See the section on # configuration options in the {Toys::Utils::Exec} class docs. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # # @return [String] What was written to standard out. # def capture(cmd, **opts, &block) # Source available in the toys-core gem end ## # Spawn a ruby process and pass the given arguments to it. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # @param args [String,Array] The arguments to ruby. # @param opts [keywords] The command options. See the section on # configuration options in the {Toys::Utils::Exec} class docs. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # # @return [String] What was written to standard out. # def capture_ruby(args, **opts, &block) # Source available in the toys-core gem end ## # Execute a proc in a fork. # # Captures standard out and returns it as a string. # Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # @param func [Proc] The proc to call. # @param opts [keywords] The command options. See the section on # configuration options in the {Toys::Utils::Exec} class docs. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # # @return [String] What was written to standard out. # def capture_proc(func, **opts, &block) # Source available in the toys-core gem end ## # Execute the given string in a shell. Returns an effective exit code # that is always an integer. Cannot be run in the background. # # If a block is provided, a {Toys::Utils::Exec::Controller} will be # yielded to it. # # @param cmd [String] The shell command to execute. # @param opts [keywords] The command options. See the section on # configuration options in the {Toys::Utils::Exec} class docs. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # # @return [Integer] An effective exit code. See # {Toys::Utils::Exec::Result#effective_code}. # def sh(cmd, **opts, &block) # Source available in the toys-core gem end ## # **_Defined in the toys-core gem_** # # An object that controls a subprocess. This object is returned from an # execution running in the background, or is yielded to a control block # for an execution running in the foreground. # You can use this object to interact with the subcommand's streams, # send signals to the process, and get its result. # class Controller ## # The subcommand's name. # @return [Object] # attr_reader :name ## # The subcommand's standard input stream (which can be written to). # # @return [IO] if the command was configured with `in: :controller` # @return [nil] if the command was not configured with # `in: :controller` # attr_reader :in ## # The subcommand's standard output stream (which can be read from). # # @return [IO] if the command was configured with `out: :controller` # @return [nil] if the command was not configured with # `out: :controller` # attr_reader :out ## # The subcommand's standard error stream (which can be read from). # # @return [IO] if the command was configured with `err: :controller` # @return [nil] if the command was not configured with # `err: :controller` # attr_reader :err ## # The process ID. # # Exactly one of {#exception} and {#pid} will be non-nil. # # @return [Integer] if the process start was successful # @return [nil] if the process could not be started. # attr_reader :pid ## # The exception raised when the process failed to start. # # Exactly one of {#exception} and {#pid} will be non-nil. # # @return [Exception] if the process failed to start. # @return [nil] if the process start was successful. # attr_reader :exception ## # Captures the remaining data in the given stream. # After calling this, do not read directly from the stream. # # @param which [:out,:err] Which stream to capture # # @return [self] if the stream was captured # @return [nil] if the stream was not captured because the process has # completed or did not start successfully # def capture(which) # Source available in the toys-core gem end ## # Captures the remaining data in the standard output stream. # After calling this, do not read directly from the stream. # # @return [self] # def capture_out # Source available in the toys-core gem end ## # Captures the remaining data in the standard error stream. # After calling this, do not read directly from the stream. # # @return [self] # def capture_err # Source available in the toys-core gem end ## # Redirects the remainder of the given stream. # # You can specify the stream as an IO or IO-like object, or as a file # specified by its path. If specifying a file, you can optionally # provide the mode and permissions for the call to `File#open`. You can # also specify the value `:null` to indicate the null file. # # If the stream is redirected to an IO-like object, it is _not_ closed # when the process is completed. (If it is redirected to a file # specified by path, the file is closed on completion.) # # After calling this, do not interact directly with the stream. # # @param which [:in,:out,:err] Which stream to redirect # @param io [IO,StringIO,String,:null] Where to redirect the stream # @param io_args [Object...] The mode and permissions for opening the # file, if redirecting to/from a file. # # @return [self] if the stream was redirected # @return [nil] if the stream was not redirected because the process # has completed or did not start successfully # def redirect(which, io, *io_args) # Source available in the toys-core gem end ## # Redirects the remainder of the standard input stream. # # You can specify the stream as an IO or IO-like object, or as a file # specified by its path. If specifying a file, you can optionally # provide the mode and permissions for the call to `File#open`. You can # also specify the value `:null` to indicate the null file. # # After calling this, do not interact directly with the stream. # # @param io [IO,StringIO,String,:null] Where to redirect the stream # @param io_args [Object...] The mode and permissions for opening the # file, if redirecting from a file. # # @return [self] if the stream was redirected # @return [nil] if the stream was not redirected because the process # has completed or did not start successfully # def redirect_in(io, *io_args) # Source available in the toys-core gem end ## # Redirects the remainder of the standard output stream. # # You can specify the stream as an IO or IO-like object, or as a file # specified by its path. If specifying a file, you can optionally # provide the mode and permissions for the call to `File#open`. You can # also specify the value `:null` to indicate the null file. # # After calling this, do not interact directly with the stream. # # @param io [IO,StringIO,String,:null] Where to redirect the stream # @param io_args [Object...] The mode and permissions for opening the # file, if redirecting to a file. # # @return [self] if the stream was redirected # @return [nil] if the stream was not redirected because the process # has completed or did not start successfully # def redirect_out(io, *io_args) # Source available in the toys-core gem end ## # Redirects the remainder of the standard error stream. # # You can specify the stream as an IO or IO-like object, or as a file # specified by its path. If specifying a file, you can optionally # provide the mode and permissions for the call to `File#open`. You can # also specify the value `:null` to indicate the null file. # # After calling this, do not interact directly with the stream. # # @param io [IO,StringIO,String,:null] Where to redirect the stream # @param io_args [Object...] The mode and permissions for opening the # file, if redirecting to a file. # # @return [self] if the stream was redirected # @return [nil] if the stream was not redirected because the process # has completed or did not start successfully # def redirect_err(io, *io_args) # Source available in the toys-core gem end ## # Send the given signal to the process. The signal can be specified # by name or number. # # @param sig [Integer,String] The signal to send. # @return [self] # def kill(sig) # Source available in the toys-core gem end alias signal kill ## # Determine whether the subcommand is still executing # # @return [boolean] # def executing? # Source available in the toys-core gem end ## # Wait for the subcommand to complete, and return a result object. # # @param timeout [Numeric,nil] The timeout in seconds, or `nil` to # wait indefinitely. # @return [Toys::Utils::Exec::Result] The result object # @return [nil] if a timeout occurred. # def result(timeout: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # The result returned from a subcommand execution. This includes the # identifying name of the execution (if any), the result status of the # execution, and any captured stream output. # # Possible result statuses are: # # * The process failed to start. {Result#failed?} will return true, and # {Result#exception} will return an exception describing the failure # (often an errno). # * The process executed and exited with a normal exit code. Either # {Result#success?} or {Result#error?} will return true, and # {Result.exit_code} will return the numeric exit code. # * The process executed but was terminated by an uncaught signal. # {Result#signaled?} will return true, and {Result#signal_code} will # return the numeric signal code. # class Result ## # The subcommand's name. # # @return [Object] # attr_reader :name ## # The captured output string. # # @return [String] The string captured from stdout. # @return [nil] if the command was not configured to capture stdout. # attr_reader :captured_out ## # The captured error string. # # @return [String] The string captured from stderr. # @return [nil] if the command was not configured to capture stderr. # attr_reader :captured_err ## # The Ruby process status object, providing various information about # the ending state of the process. # # Exactly one of {#exception} and {#status} will be non-nil. # # @return [Process::Status] The status, if the process was successfully # spawned and terminated. # @return [nil] if the process could not be started. # attr_reader :status ## # The exception raised if a process couldn't be started. # # Exactly one of {#exception} and {#status} will be non-nil. # Exactly one of {#exception}, {#exit_code}, or {#signal_code} will be # non-nil. # # @return [Exception] The exception raised from process start. # @return [nil] if the process started successfully. # attr_reader :exception ## # The numeric status code for a process that exited normally, # # Exactly one of {#exception}, {#exit_code}, or {#signal_code} will be # non-nil. # # @return [Integer] the numeric status code, if the process started # successfully and exited normally. # @return [nil] if the process did not start successfully, or was # terminated by an uncaught signal. # def exit_code # Source available in the toys-core gem end ## # The numeric signal code that caused process termination. # # Exactly one of {#exception}, {#exit_code}, or {#signal_code} will be # non-nil. # # @return [Integer] The signal that caused the process to terminate. # @return [nil] if the process did not start successfully, or executed # and exited with a normal exit code. # def signal_code # Source available in the toys-core gem end alias term_signal signal_code ## # Returns true if the subprocess failed to start, or false if the # process was able to execute. # # @return [boolean] # def failed? # Source available in the toys-core gem end ## # Returns true if the subprocess terminated due to an unhandled signal, # or false if the process failed to start or exited normally. # # @return [boolean] # def signaled? # Source available in the toys-core gem end ## # Returns true if the subprocess terminated with a zero status, or # false if the process failed to start, terminated due to a signal, or # returned a nonzero status. # # @return [boolean] # def success? # Source available in the toys-core gem end ## # Returns true if the subprocess terminated with a nonzero status, or # false if the process failed to start, terminated due to a signal, or # returned a zero status. # # @return [boolean] # def error? # Source available in the toys-core gem end ## # Returns an "effective" exit code, which is always an integer if the # process has terminated for any reason. In general, this code will be: # # * The same as {#exit_code} if the process terminated normally with an # exit code, # * The convention of `128+signalnum` if the process terminated due to # a signal, # * The convention of 126 if the process could not start due to lack of # execution permissions, # * The convention of 127 if the process could not start because the # command was not recognized or could not be found, or # * An undefined value between 1 and 255 for other failures. # # Note that the normal exit code and signal number cases are stable, # but any other cases are subject to change on future releases. # # @return [Integer] # def effective_code # Source available in the toys-core gem end end end end end toys-0.21.0/core-docs/toys/utils/help_text.rb0000644000004100000410000001221115164300675021161 0ustar www-datawww-datamodule Toys module Utils ## # **_Defined in the toys-core gem_** # # A helper class that generates usage documentation for a tool. # # This class generates full usage documentation, including description, # flags, and arguments. It is used by middleware that implements help # and related options. # # This class is not loaded by default. Before using it directly, you should # `require "toys/utils/help_text"` # class HelpText ## # Default width of first column # @return [Integer] # DEFAULT_LEFT_COLUMN_WIDTH = 32 ## # Default indent # @return [Integer] # DEFAULT_INDENT = 4 ## # Create a usage helper given an execution context. # # @param context [Toys::Context] The current context. # @return [Toys::Utils::HelpText] # def self.from_context(context) # Source available in the toys-core gem end ## # Create a usage helper. # # @param tool [Toys::ToolDefinition] The tool to document. # @param loader [Toys::Loader] A loader that can provide subcommands. # @param executable_name [String] The name of the executable. # e.g. `"toys"`. # @param delegates [Array] The delegation path to # the tool. # # @return [Toys::Utils::HelpText] # def initialize(tool, loader, executable_name, delegates: []) # Source available in the toys-core gem end ## # The ToolDefinition being documented. # @return [Toys::ToolDefinition] # attr_reader :tool ## # Generate a short usage string. # # @param recursive [Boolean] If true, and the tool is a namespace, # display all subtools recursively. Defaults to false. # @param include_hidden [Boolean] Include hidden subtools (i.e. whose # names begin with underscore.) Default is false. # @param separate_sources [Boolean] Split up tool list by source root. # Defaults to false. # @param left_column_width [Integer] Width of the first column. Default # is {DEFAULT_LEFT_COLUMN_WIDTH}. # @param indent [Integer] Indent width. Default is {DEFAULT_INDENT}. # @param wrap_width [Integer,nil] Overall width to wrap to. Default is # `nil` indicating no wrapping. # # @return [String] A usage string. # def usage_string(recursive: false, include_hidden: false, separate_sources: false, left_column_width: nil, indent: nil, wrap_width: nil) # Source available in the toys-core gem end ## # Generate a long help string. # # @param recursive [Boolean] If true, and the tool is a namespace, # display all subtools recursively. Defaults to false. # @param search [String,nil] An optional string to search for when # listing subtools. Defaults to `nil` which finds all subtools. # @param include_hidden [Boolean] Include hidden subtools (i.e. whose # names begin with underscore.) Default is false. # @param show_source_path [Boolean] If true, shows the source path # section. Defaults to false. # @param separate_sources [Boolean] Split up tool list by source root. # Defaults to false. # @param indent [Integer] Indent width. Default is {DEFAULT_INDENT}. # @param indent2 [Integer] Second indent width. Default is # {DEFAULT_INDENT}. # @param wrap_width [Integer,nil] Wrap width of the column, or `nil` to # disable wrap. Default is `nil`. # @param styled [Boolean] Output ansi styles. Default is `true`. # # @return [String] A usage string. # def help_string(recursive: false, search: nil, include_hidden: false, show_source_path: false, separate_sources: false, indent: nil, indent2: nil, wrap_width: nil, styled: true) # Source available in the toys-core gem end ## # Generate a subtool list string. # # @param recursive [Boolean] If true, and the tool is a namespace, # display all subtools recursively. Defaults to false. # @param search [String,nil] An optional string to search for when # listing subtools. Defaults to `nil` which finds all subtools. # @param include_hidden [Boolean] Include hidden subtools (i.e. whose # names begin with underscore.) Default is false. # @param separate_sources [Boolean] Split up tool list by source root. # Defaults to false. # @param indent [Integer] Indent width. Default is {DEFAULT_INDENT}. # @param wrap_width [Integer,nil] Wrap width of the column, or `nil` to # disable wrap. Default is `nil`. # @param styled [Boolean] Output ansi styles. Default is `true`. # # @return [String] A usage string. # def list_string(recursive: false, search: nil, include_hidden: false, separate_sources: false, indent: nil, wrap_width: nil, styled: true) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/acceptor.rb0000644000004100000410000004431415164300675017636 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # An Acceptor validates and converts arguments. It is designed to be # compatible with the OptionParser accept mechanism. # # First, an acceptor validates the argument via its # {Toys::Acceptor::Base#match} method. This method should determine whether # the argument is valid, and return information that will help with # conversion of the argument. # # Second, an acceptor converts the argument to its final form via the # {Toys::Acceptor::Base#convert} method. # # Finally, an acceptor has a name that may appear in help text for flags and # arguments that use it. # module Acceptor ## # A sentinel that may be returned from a function-based acceptor to # indicate invalid input. # @return [Object] # REJECT = ::Object.new.freeze ## # The default type description. # @return [String] # DEFAULT_TYPE_DESC = "string" ## # **_Defined in the toys-core gem_** # # A base class for acceptors. # # The base acceptor does not do any validation (i.e. it accepts all # arguments) or conversion (i.e. it returns the original string). You can # subclass this base class and override the {#match} and {#convert} methods # to implement an acceptor. # class Base ## # Create a base acceptor. # # @param type_desc [String] Type description string, shown in help. # Defaults to {Toys::Acceptor::DEFAULT_TYPE_DESC}. # @param well_known_spec [Object] The well-known acceptor spec associated # with this acceptor, or `nil` for none. # def initialize(type_desc: nil, well_known_spec: nil) # Source available in the toys-core gem end ## # Type description string, shown in help. # @return [String] # attr_reader :type_desc ## # The well-known acceptor spec associated with this acceptor, if any. # This generally identifies an OptionParser-compatible acceptor spec. For # example, the acceptor object that corresponds to `Integer` will return # `Integer` from this attribute. # # @return [Object] the well-known acceptor # @return [nil] if there is no corresponding well-known acceptor # attr_reader :well_known_spec ## # Type description string, shown in help. # @return [String] # def to_s # Source available in the toys-core gem end ## # Validate the given input. # # When given a valid input, return an array in which the first element is # the original input string, and the remaining elements (which may be # empty) comprise any additional information that may be useful during # conversion. If there is no additional information, you can return the # original input string by itself without wrapping in an array. # # When given an invalid input, return a falsy value such as `nil`. # # Note that a `MatchData` object is a legitimate return value because it # duck-types the appropriate array. # # This default implementation simply returns the original input string, # as the only array element, indicating all inputs are valid. You can # override this method to provide a different validation function. # # @param str [String] The input argument string. # @return [String,Array,nil] # def match(str) # Source available in the toys-core gem end ## # Convert the given input. # # This method is passed the results of a successful match, including the # original input string and any other values returned from {#match}. It # must return the final converted value to use. # # @param str [String] Original argument string. # @param extra [Object...] Zero or more additional arguments comprising # additional elements returned from the match function. # @return [Object] The converted argument as it should be stored in the # context data. # def convert(str, *extra) # Source available in the toys-core gem end ## # Return suggestions for a given non-matching string. # # This method may be called when a match fails. It should return a # (possibly empty) array of suggestions that could be displayed to the # user as "did you mean..." # # The default implementation returns the empty list. # # @param str [String] A string that failed matching. # @return [Array] A possibly empty array of alternative # suggestions that could be displayed with "did you mean..." # def suggestions(str) # Source available in the toys-core gem end end ## # The default acceptor. Corresponds to the well-known acceptor for # `Object`. # @return [Toys::Acceptor::Base] # DEFAULT = Base.new(type_desc: "string", well_known_spec: ::Object) ## # **_Defined in the toys-core gem_** # # An acceptor that uses a simple function to validate and convert input. # The function must take the input string as its argument, and either # return the converted object to indicate success, or raise an exception or # return the sentinel {Toys::Acceptor::REJECT} to indicate invalid input. # class Simple < Base ## # Create a simple acceptor. # # You should provide an acceptor function, either as a proc in the # `function` argument, or as a block. The function must take as its one # argument the input string. If the string is valid, the function must # return the value to store in the tool's data. If the string is invalid, # the function may either raise an exception (which must descend from # `StandardError`) or return {Toys::Acceptor::REJECT}. # # @param function [Proc] The acceptor function # @param type_desc [String] Type description string, shown in help. # Defaults to {Toys::Acceptor::DEFAULT_TYPE_DESC}. # @param well_known_spec [Object] The well-known acceptor spec associated # with this acceptor, or `nil` for none. # @param block [Proc] The acceptor function, if not provided as a normal # parameter. # def initialize(function = nil, type_desc: nil, well_known_spec: nil, &block) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # An acceptor that uses a regex to validate input. It also supports a # custom conversion function that generates the final value from the match # results. # class Pattern < Base ## # Create a pattern acceptor. # # You must provide a regular expression (or any object that duck-types # `Regexp#match`) as a validator. # # You may also optionally provide a converter, either as a proc or a # block. A converter must take as its arguments the values in the # `MatchData` returned from a successful regex match. That is, the first # argument is the original input string, and the remaining arguments are # the captures. The converter must return the final converted value. # If no converter is provided, no conversion is done and the input string # is returned. # # @param regex [Regexp] Regular expression defining value values. # @param converter [Proc] An optional converter function. May also be # given as a block. Note that the converter will be passed all # elements of the `MatchData`. # @param type_desc [String] Type description string, shown in help. # Defaults to {Toys::Acceptor::DEFAULT_TYPE_DESC}. # @param well_known_spec [Object] The well-known acceptor spec associated # with this acceptor, or `nil` for none. # @param block [Proc] A converter function, if not provided as a normal # parameter. # def initialize(regex, converter = nil, type_desc: nil, well_known_spec: nil, &block) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # An acceptor that recognizes a fixed set of values. # # You provide a list of valid values. The input argument string will be # matched against the string forms of these valid values. If it matches, # the converter will return the actual value from the valid list. # # For example, you could pass `[:one, :two, 3]` as the set of values. If # an argument of `"two"` is passed in, the converter will yield a final # value of the symbol `:two`. If an argument of "3" is passed in, the # converter will yield the integer `3`. If an argument of "three" is # passed in, the match will fail. # class Enum < Base ## # Create an acceptor. # # @param values [Array] Valid values. # @param type_desc [String] Type description string, shown in help. # Defaults to {Toys::Acceptor::DEFAULT_TYPE_DESC}. # @param well_known_spec [Object] The well-known acceptor spec associated # with this acceptor, or `nil` for none. # def initialize(values, type_desc: nil, well_known_spec: nil) # Source available in the toys-core gem end ## # The array of enum values. # @return [Array] # attr_reader :values end ## # **_Defined in the toys-core gem_** # # An acceptor that recognizes a range of values. # # The input argument is matched against the given range. For example, you # can match against the integers from 1 to 10 by passing the range # `(1..10)`. # # You can also provide a conversion function that takes the input string # and converts it an object that can be compared by the range. If you do # not provide a converter, a default converter will be provided depending # on the types of objects serving as the range limits. Specifically: # # * If the range beginning and end are both `Integer`, then input strings # are likewise converted to `Integer` when matched against the range. # Accepted values are returned as integers. # * If the range beginning and end are both `Float`, then input strings # are likewise converted to `Float`. # * If the range beginning and end are both `Rational`, then input # strings are likewise converted to `Rational`. # * If the range beginning and end are both `Numeric` types but different # subtypes (e.g. an `Integer` and a `Float`), then any type of numeric # input (integer, float, rational) is accepted and matched against the # range. # * If the range beginning and/or end are not numeric types, then no # conversion is done by default. # class Range < Simple ## # Create an acceptor. # # @param range [Range] The range of acceptable values # @param converter [Proc] A converter proc that takes an input string and # attempts to convert it to a type comparable by the range. For # numeric ranges, this can be omitted because one is provided by # default. You should provide a converter for other types of ranges. # You can also pass the converter as a block. # @param type_desc [String] Type description string, shown in help. # Defaults to {Toys::Acceptor::DEFAULT_TYPE_DESC}. # @param well_known_spec [Object] The well-known acceptor spec associated # with this acceptor, or `nil` for none. # @param block [Proc] Converter function, if not provided as a normal # parameter. # def initialize(range, converter = nil, type_desc: nil, well_known_spec: nil, &block) # Source available in the toys-core gem end ## # The range being checked. # @return [Range] # attr_reader :range end ## # A converter proc that handles integers. Useful in Simple and Range # acceptors. # @return [Proc] # INTEGER_CONVERTER = proc { |s| s.nil? ? nil : Integer(s) } ## # A converter proc that handles floats. Useful in Simple and Range # acceptors. # @return [Proc] # FLOAT_CONVERTER = proc { |s| s.nil? ? nil : Float(s) } ## # A converter proc that handles rationals. Useful in Simple and Range # acceptors. # @return [Proc] # RATIONAL_CONVERTER = proc { |s| s.nil? ? nil : Rational(s) } ## # A converter proc that handles any numeric value. Useful in Simple and # Range acceptors. # @return [Proc] # NUMERIC_CONVERTER = proc do |s| if s.include?("/") Rational(s) elsif s.include?(".") || (s.include?("e") && s !~ /\A-?0x/) Float(s) else Integer(s) end end ## # A set of strings that are considered true for boolean acceptors. # Currently set to `["+", "true", "yes"]`. # @return [Array] # TRUE_STRINGS = ["+", "true", "yes"].freeze ## # A set of strings that are considered false for boolean acceptors. # Currently set to `["-", "false", "no", "nil"]`. # @return [Array] # FALSE_STRINGS = ["-", "false", "no", "nil"].freeze ## # A converter proc that handles boolean strings. Recognizes {TRUE_STRINGS} # and {FALSE_STRINGS}. Useful for Simple acceptors. # @return [Proc] # BOOLEAN_CONVERTER = proc do |s| if s.empty? REJECT else s = s.downcase if TRUE_STRINGS.any? { |t| t.start_with?(s) } true elsif FALSE_STRINGS.any? { |f| f.start_with?(s) } false else REJECT end end end class << self ## # Lookup a standard acceptor name recognized by OptionParser. # # @param spec [Object] A well-known acceptor specification, such as # `String`, `Integer`, `Array`, `OptionParser::DecimalInteger`, etc. # @return [Toys::Acceptor::Base] The corresponding Acceptor object # @return [nil] if the given standard acceptor was not recognized. # def lookup_well_known(spec) # Source available in the toys-core gem end ## # Create an acceptor from a variety of specification formats. The # acceptor is constructed from the given specification object and/or the # given block. Additionally, some acceptors can take an optional type # description string used to describe the type of data in online help. # # Recognized specs include: # # * Any well-known acceptor recognized by OptionParser, such as # `Integer`, `Array`, or `OptionParser::DecimalInteger`. Any block # and type description you provide are ignored. # # * Any **regular expression**. The returned acceptor validates only if # the regex matches the *entire string parameter*. # # You can also provide an optional conversion function as a block. If # provided, the block must take a variable number of arguments, the # first being the matched string and the remainder being the captures # from the regular expression. It should return the converted object # that will be stored in the context data. If you do not provide a # block, no conversion takes place, and the original string is used. # # * An **array** of possible values. The acceptor validates if the # string parameter matches the *string form* of one of the array # elements (i.e. the results of calling `to_s` on the element.) # # An array acceptor automatically converts the string parameter to # the actual array element that it matched. For example, if the # symbol `:foo` is in the array, it will match the string `"foo"`, # and then store the symbol `:foo` in the tool data. You may not # further customize the conversion function; any block is ignored. # # * A **range** of possible values. The acceptor validates if the # string parameter, after conversion to the range type, lies within # the range. The final value stored in context data is the converted # value. For numeric ranges, conversion is provided, but if the range # has a different type, you must provide the conversion function as # a block. # # * A **function** as a Proc (where the block is ignored) or a block # (if the spec is nil). This function performs *both* validation and # conversion. It should take the string parameter as its argument, # and it must either return the object that should be stored in the # tool data, or raise an exception (descended from `StandardError`) # to indicate that the string parameter is invalid. You may also # return the sentinel value {Toys::Acceptor::REJECT} to indicate that # the string is invalid. # # * The value `nil` or `:default` with no block, to indicate the # default pass-through acceptor {Toys::Acceptor::DEFAULT}. Any type # description you provide is ignored. # # Additional options: # # * `:type_desc` (String) The type description for interpolating into # help text. Ignored if the spec indicates the default acceptor or a # well-known acceptor. # # @param spec [Object] See the description for recognized values. # @param options [Hash] Additional options to pass to the acceptor. # @param block [Proc] See the description for recognized forms. # @return [Toys::Acceptor::Base,Proc] # def create(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Take the various ways to express an acceptor spec, and convert them to # a canonical form expressed as a single object. This is called from the # DSL to generate a spec object that can be stored. # # @private This interface is internal and subject to change without warning. # def scalarize_spec(spec, options, block) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/compat.rb0000644000004100000410000000002015164300675017303 0ustar www-datawww-datamodule Toys end toys-0.21.0/core-docs/toys/module_lookup.rb0000644000004100000410000000413315164300675020707 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A helper module that provides methods to do module lookups. This is # used to obtain named helpers, middleware, and templates from the # respective modules. # class ModuleLookup class << self ## # Convert the given string to a path element. Specifically, converts # to `lower_snake_case`. # # @param str [String,Symbol] String to convert. # @return [String] Converted string # def to_path_name(str) # Source available in the toys-core gem end ## # Convert the given string to a module name. Specifically, converts # to `UpperCamelCase`, and then to a symbol. # # @param str [String,Symbol] String to convert. # @return [Symbol] Converted name # def to_module_name(str) # Source available in the toys-core gem end ## # Given a require path, return the module expected to be defined. # # @param path [String] File path, delimited by forward slash # @return [Module] The module loaded from that path # def path_to_module(path) # Source available in the toys-core gem end end ## # Create an empty ModuleLookup # def initialize # Source available in the toys-core gem end ## # Add a lookup path for modules. # # @param path_base [String] The base require path # @param module_base [Module] The base module, or `nil` (the default) to # infer a default from the path base. # @param high_priority [Boolean] If true, add to the head of the lookup # path, otherwise add to the end. # @return [self] # def add_path(path_base, module_base: nil, high_priority: false) # Source available in the toys-core gem end ## # Obtain a named module. Returns `nil` if the name is not present. # # @param name [String,Symbol] The name of the module to return. # @return [Module] The specified module # def lookup(name) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/loader.rb0000644000004100000410000003777115164300675017315 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # The Loader service loads tools from configuration files, and finds the # appropriate tool given a set of command line arguments. # class Loader ## # Create a Loader # # @param index_file_name [String,nil] A file with this name that appears # in any configuration directory (not just a toplevel directory) is # loaded first as a standalone configuration file. If not provided, # standalone configuration files are disabled. # @param preload_file_name [String,nil] A file with this name that appears # in any configuration directory is preloaded before any tools in that # configuration directory are defined. # @param preload_dir_name [String,nil] A directory with this name that # appears in any configuration directory is searched for Ruby files, # which are preloaded before any tools in that configuration directory # are defined. # @param data_dir_name [String,nil] A directory with this name that appears # in any configuration directory is added to the data directory search # path for any tool file in that directory. # @param lib_dir_name [String,nil] A directory with this name that appears # in any configuration directory is added to the Ruby load path for any # tool file in that directory. # @param middleware_stack [Array] An array of # middleware that will be used by default for all tools loaded by this # loader. # @param extra_delimiters [String] A string containing characters that can # function as delimiters in a tool name. Defaults to empty. Allowed # characters are period, colon, and slash. # @param mixin_lookup [Toys::ModuleLookup] A lookup for well-known # mixin modules. Defaults to an empty lookup. # @param middleware_lookup [Toys::ModuleLookup] A lookup for # well-known middleware classes. Defaults to an empty lookup. # @param template_lookup [Toys::ModuleLookup] A lookup for # well-known template classes. Defaults to an empty lookup. # def initialize(index_file_name: nil, preload_dir_name: nil, preload_file_name: nil, data_dir_name: nil, lib_dir_name: nil, middleware_stack: [], extra_delimiters: "", mixin_lookup: nil, middleware_lookup: nil, template_lookup: nil, git_cache: nil, gems_util: nil) # Source available in the toys-core gem end ## # Add a configuration file/directory to the loader. # # @param path [String] A single path to add. # @param high_priority [Boolean] If true, add this path at the top of the # priority list. Defaults to false, indicating the new path should be # at the bottom of the priority list. # @param source_name [String] A custom name for the root source. Optional. # @param context_directory [String,nil,:path,:parent] The context directory # for tools loaded from this path. You can pass a directory path as a # string, `:path` to denote the given path, `:parent` to denote the # given path's parent directory, or `nil` to denote no context. # Defaults to `:parent`. # @return [self] # def add_path(path, high_priority: false, source_name: nil, context_directory: :parent) # Source available in the toys-core gem end ## # Add a set of configuration files/directories from a common directory to # the loader. The set of paths will be added at the same priority level and # will share a root. # # @param root_path [String] A root path to be seen as the root source. This # should generally be a directory containing the paths to add. # @param relative_paths [String,Array] One or more paths to add, as # relative paths from the common root. # @param high_priority [Boolean] If true, add the paths at the top of the # priority list. Defaults to false, indicating the new paths should be # at the bottom of the priority list. # @param context_directory [String,nil,:path,:parent] The context directory # for tools loaded from this path. You can pass a directory path as a # string, `:path` to denote the given root path, `:parent` to denote # the given root path's parent directory, or `nil` to denote no context. # Defaults to `:path`. # @return [self] # def add_path_set(root_path, relative_paths, high_priority: false, context_directory: :path) # Source available in the toys-core gem end ## # Add a configuration block to the loader. # # @param high_priority [Boolean] If true, add this block at the top of the # priority list. Defaults to false, indicating the block should be at # the bottom of the priority list. # @param source_name [String] The source name that will be shown in # documentation for tools defined in this block. If omitted, a default # unique string will be generated. # @param block [Proc] The block of configuration, executed in the context # of the tool DSL {Toys::DSL::Tool}. # @param context_directory [String,nil] The context directory for tools # loaded from this block. You can pass a directory path as a string, or # `nil` to denote no context. Defaults to `nil`. # @return [self] # def add_block(high_priority: false, source_name: nil, context_directory: nil, &block) # Source available in the toys-core gem end ## # Add a configuration git source to the loader. # # @param git_remote [String] The git repo URL # @param git_path [String] The path to the relevant file or directory in # the repo. Specify the empty string to use the entire repo. # @param git_commit [String] The git ref (i.e. SHA, tag, or branch name) # @param high_priority [Boolean] If true, add this path at the top of the # priority list. Defaults to false, indicating the new path should be # at the bottom of the priority list. # @param update [Boolean] If the commit is not a SHA, pulls any updates # from the remote. Defaults to false, which uses a local cache and does # not update if the commit has been fetched previously. # @param context_directory [String,nil] The context directory for tools # loaded from this source. You can pass a directory path as a string, # or `nil` to denote no context. Defaults to `nil`. # @return [self] # def add_git(git_remote, git_path, git_commit, high_priority: false, update: false, context_directory: nil) # Source available in the toys-core gem end ## # Add a configuration gem source to the loader. # # @param gem_name [String] The name of the gem # @param gem_version [String,Array] The version requirements # @param gem_path [String] The path from the gem's toys directory to the # relevant file or directory. Specify the empty string to use the # entire toys directory. # @param high_priority [Boolean] If true, add this path at the top of the # priority list. Defaults to false, indicating the new path should be # at the bottom of the priority list. # @param gem_toys_dir [String] The name of the toys directory. Optional. # Defaults to the directory specified in the gem's metadata, or the # value "toys". # @param context_directory [String,nil] The context directory for tools # loaded from this source. You can pass a directory path as a string, # or `nil` to denote no context. Defaults to `nil`. # @return [self] # def add_gem(gem_name, gem_version, gem_path, high_priority: false, gem_toys_dir: nil, context_directory: nil) # Source available in the toys-core gem end ## # Given a list of command line arguments, find the appropriate tool to # handle the command, loading it from the configuration if necessary. # This always returns a tool. If the specific tool path is not defined and # cannot be found in any configuration, it finds the nearest namespace that # *would* contain that tool, up to the root tool. # # Returns a tuple of the found tool, and the array of remaining arguments # that are not part of the tool name and should be passed as tool args. # # @param args [Array] Command line arguments # @return [Array(Toys::ToolDefinition,Array)] # def lookup(args) # Source available in the toys-core gem end ## # Given a tool name, looks up the specific tool, loading it from the # configuration if necessary. # # If there is an active tool, returns it; otherwise, returns the highest # priority tool that has been defined. If no tool has been defined with # the given name, returns `nil`. # # @param words [Array] The tool name # @return [Toys::ToolDefinition] if the tool was found # @return [nil] if no such tool exists # def lookup_specific(words) # Source available in the toys-core gem end ## # Returns a list of subtools for the given path, loading from the # configuration if necessary. The list will be sorted by name. # # @param words [Array] The name of the parent tool # @param recursive [Boolean] If true, return all subtools recursively # rather than just the immediate children (the default) # @param include_hidden [Boolean] If true, include hidden subtools, # i.e. names beginning with underscores. Defaults to false. # @param include_namespaces [Boolean] If true, include namespaces, # i.e. tools that are not runnable but have descendents that would have # been listed by the current filters. Defaults to false. # @param include_non_runnable [Boolean] If true, include tools that have # no children and are not runnable. Defaults to false. # @return [Array] An array of subtools. # def list_subtools(words, recursive: false, include_hidden: false, include_namespaces: false, include_non_runnable: false) # Source available in the toys-core gem end ## # Returns true if the given path has at least one subtool, even if they are # hidden or non-runnable. Loads from the configuration if necessary. # # @param words [Array] The name of the parent tool # @return [Boolean] # def has_subtools?(words) # Source available in the toys-core gem end ## # Splits the given path using the delimiters configured in this Loader. # You may pass in either an array of strings, or a single string possibly # delimited by path separators. Always returns an array of strings. # # @param str [String,Symbol,Array] The path to split. # @return [Array] # def split_path(str) # Source available in the toys-core gem end #### INTERNAL METHODS #### ## # Get or create the tool definition for the given name and priority. # # @private This interface is internal and subject to change without warning. # def get_tool(words, priority, tool_class = nil) # Source available in the toys-core gem end ## # Returns the active tool specified by the given words, with the given # priority, without doing any loading. If the given priority matches the # currently active tool, returns it. If the given priority is lower than # the active priority, returns `nil`. If the given priority is higher than # the active priority, returns and activates a new tool. # # @private This interface is internal and subject to change without warning. # def activate_tool(words, priority) # Source available in the toys-core gem end ## # Returns true if the given tool name currently exists in the loader. # Does not load the tool if not found. # # @private This interface is internal and subject to change without warning. # def tool_defined?(words) # Source available in the toys-core gem end ## # Build a new tool. # Called only from ToolData. # # @private This interface is internal and subject to change without warning. # def build_tool(words, priority, tool_class = nil) # Source available in the toys-core gem end ## # Stop search at the given priority. Returns true if successful. # Called only from the DSL. # # @private This interface is internal and subject to change without warning. # def stop_loading_at_priority(priority) # Source available in the toys-core gem end ## # Loads the subtree under the given prefix. # # @private This interface is internal and subject to change without warning. # def load_for_prefix(prefix) # Source available in the toys-core gem end ## # Attempt to get a well-known mixin module for the given symbolic name. # # @private This interface is internal and subject to change without warning. # def resolve_standard_mixin(name) # Source available in the toys-core gem end ## # Attempt to get a well-known template class for the given symbolic name. # # @private This interface is internal and subject to change without warning. # def resolve_standard_template(name) # Source available in the toys-core gem end ## # Load configuration from the given path. This is called from the `load` # directive in the DSL. # # @private This interface is internal and subject to change without warning. # def load_path(parent_source, path, words, remaining_words, priority) # Source available in the toys-core gem end ## # Load configuration from the given git remote. This is called from the # `load_git` directive in the DSL. # # @private This interface is internal and subject to change without warning. # def load_git(parent_source, git_remote, git_path, git_commit, update, words, remaining_words, priority) # Source available in the toys-core gem end ## # Load configuration from the given gem. This is called from the `load_gem` # directive in the DSL. # # @private This interface is internal and subject to change without warning. # def load_gem(parent_source, gem_name, gem_version, gem_toys_dir, gem_path, words, remaining_words, priority) # Source available in the toys-core gem end ## # Load a subtool block. Called from the `tool` directive in the DSL. # # @private This interface is internal and subject to change without warning. # def load_block(parent_source, block, words, remaining_words, priority) # Source available in the toys-core gem end @git_cache_mutex = ::Mutex.new @default_git_cache = nil @default_gems_util = nil ## # Get a global default GitCache. # # @private This interface is internal and subject to change without warning. # def self.default_git_cache # Source available in the toys-core gem end ## # Get a global default Gems utility. # # @private This interface is internal and subject to change without warning. # def self.default_gems_util # Source available in the toys-core gem end ## # Determine the next setting for remaining_words, given a word. # # @private This interface is internal and subject to change without warning. # def self.next_remaining_words(remaining_words, word) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/flag.rb0000644000004100000410000004101715164300675016744 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # Representation of a formal set of flags that set a particular context # key. The flags within a single Flag definition are synonyms. # class Flag ## # **_Defined in the toys-core gem_** # # Representation of a single flag. # class Syntax # rubocop:disable Style/PerlBackrefs ## # Parse flag syntax # @param str [String] syntax. # def initialize(str) # Source available in the toys-core gem end # rubocop:enable Style/PerlBackrefs ## # The original string that was parsed to produce this syntax. # @return [String] # attr_reader :original_str ## # The flags (without values) corresponding to this syntax. # @return [Array] # attr_reader :flags ## # The flag (without values) corresponding to the normal "positive" form # of this flag. # @return [String] # attr_reader :positive_flag ## # The flag (without values) corresponding to the "negative" form of this # flag, if any. i.e. if the original string was `"--[no-]abc"`, the # negative flag is `"--no-abc"`. # @return [String] The negative form. # @return [nil] if the flag has no negative form. # attr_reader :negative_flag ## # The original string with the value (if any) stripped, but retaining # the `[no-]` prefix if present. # @return [String] # attr_reader :str_without_value ## # A string used to sort this flag compared with others. # @return [String] # attr_reader :sort_str ## # The style of flag (`:long` or `:short`). # @return [:long] if this is a long flag (i.e. double hyphen) # @return [:short] if this is a short flag (i.e. single hyphen with one # character). # attr_reader :flag_style ## # The type of flag (`:boolean` or `:value`) # @return [:boolean] if this is a boolean flag (i.e. no value) # @return [:value] if this flag takes a value (even if optional) # @return [nil] if this flag is indeterminate # attr_reader :flag_type ## # The type of value (`:required` or `:optional`) # @return [:required] if this flag takes a required value # @return [:optional] if this flag takes an optional value # @return [nil] if this flag is a boolean flag # attr_reader :value_type ## # The default delimiter used for the value of this flag. This could be # `""` or `" "` for a short flag, or `" "` or `"="` for a long flag. # @return [String] delimiter # @return [nil] if this flag is a boolean flag # attr_reader :value_delim ## # The default "label" for the value. e.g. in `--abc=VAL` the label is # `"VAL"`. # @return [String] the label # @return [nil] if this flag is a boolean flag # attr_reader :value_label ## # A canonical string representing this flag's syntax, normalized to match # the type, delimiters, etc. settings of other flag syntaxes. This is # generally used in help strings to represent this flag. # @return [String] # attr_reader :canonical_str ## # This method is accessible for testing only. # # @private This interface is internal and subject to change without warning. # def configure_canonical(canonical_flag_type, canonical_value_type, canonical_value_label, canonical_value_delim) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # The result of looking up a flag by name. # class Resolution ## # The flag string that was looked up # @return [String] # attr_reader :string ## # Whether an exact match of the string was found # @return [Boolean] # def found_exact? # Source available in the toys-core gem end ## # The number of matches that were found. # @return [Integer] # def count # Source available in the toys-core gem end ## # Whether a single unique match was found. # @return [Boolean] # def found_unique? # Source available in the toys-core gem end ## # Whether no matches were found. # @return [Boolean] # def not_found? # Source available in the toys-core gem end ## # Whether multiple matches were found (i.e. ambiguous input). # @return [Boolean] # def found_multiple? # Source available in the toys-core gem end ## # Return the unique {Toys::Flag}, or `nil` if not found or # not unique. # @return [Toys::Flag,nil] # def unique_flag # Source available in the toys-core gem end ## # Return the unique {Toys::Flag::Syntax}, or `nil` if not found # or not unique. # @return [Toys::Flag::Syntax,nil] # def unique_flag_syntax # Source available in the toys-core gem end ## # Return whether the unique match was a hit on the negative (`--no-*`) # case, or `nil` if not found or not unique. # @return [Boolean,nil] # def unique_flag_negative? # Source available in the toys-core gem end ## # Returns an array of the matching full flag strings. # @return [Array] # def matching_flag_strings # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A Completion that returns all possible flags associated with a # {Toys::Flag}. # class DefaultCompletion < Completion::Base ## # Create a completion given configuration options. # # @param flag [Toys::Flag] The flag definition. # @param include_short [Boolean] Whether to include short flags. # @param include_long [Boolean] Whether to include long flags. # @param include_negative [Boolean] Whether to include `--no-*` forms. # def initialize(flag:, include_short: true, include_long: true, include_negative: true) # Source available in the toys-core gem end ## # Whether to include short flags # @return [Boolean] # def include_short? # Source available in the toys-core gem end ## # Whether to include long flags # @return [Boolean] # def include_long? # Source available in the toys-core gem end ## # Whether to include negative long flags # @return [Boolean] # def include_negative? # Source available in the toys-core gem end ## # Returns candidates for the current completion. # # @param context [Toys::Completion::Context] the current completion # context including the string fragment. # @return [Array] an array of candidates # def call(context) # Source available in the toys-core gem end end ## # The set handler replaces the previous value. # @return [Proc] # SET_HANDLER = proc { |val| val } ## # The push handler pushes the given value using the `<<` operator. # @return [Proc] # PUSH_HANDLER = proc { |val, prev| prev.nil? ? [val] : prev << val } ## # The default handler is the set handler, which replaces the previous value. # @return [Proc] # DEFAULT_HANDLER = SET_HANDLER ## # Create a flag definition. # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param flags [Array] The flags in OptionParser format. If empty, # a flag will be inferred from the key. # @param accept [Object] An acceptor that validates and/or converts the # value. See {Toys::Acceptor.create} for recognized formats. Optional. # If not specified, defaults to {Toys::Acceptor::DEFAULT}. # @param default [Object] 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`. # @param handler [Proc,nil,:set,:push] An optional handler that customizes # how a value is set or updated when the flag is parsed. # A handler is a proc that takes up to three arguments: the given # value, the previous value, and a hash containing all the data # collected so far during argument parsing. The proc must return the # new value for the flag. # You may also specify a predefined named handler. The `:set` handler # (the default) replaces the previous value (effectively # `-> (val) { val }`). The `:push` handler expects the previous value # to be an array and pushes the given value onto it; it should be # combined with setting `default: []` and is intended for # "multi-valued" flags. # @param complete_flags [Object] A specifier for shell tab completion for # flag names associated with this flag. By default, a # {Toys::Flag::DefaultCompletion} is used, which provides the flag's # names as completion candidates. To customize completion, set this to # a hash of options to pass to the constructor for # {Toys::Flag::DefaultCompletion}, or pass any other spec recognized # by {Toys::Completion.create}. # @param complete_values [Object] A specifier for shell tab completion for # flag values associated with this flag. Pass any spec recognized by # {Toys::Completion.create}. # @param report_collisions [Boolean] Raise an exception if a flag is # requested that is already in use or marked as disabled. Default is # true. # @param group [Toys::FlagGroup] Group containing this flag. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::ToolDefinition#desc} for a # description of allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::ToolDefinition#long_desc} # for a description of allowed formats. Defaults to the empty array. # @param display_name [String] A display name for this flag, used in help # text and error messages. # @param used_flags [Array] An array of flags already in use. # def self.create(key, flags = [], used_flags: nil, report_collisions: true, accept: nil, handler: nil, default: nil, complete_flags: nil, complete_values: nil, display_name: nil, desc: nil, long_desc: nil, group: nil) # Source available in the toys-core gem end ## # Returns the flag group containing this flag # @return [Toys::FlagGroup] # attr_reader :group ## # Returns the key. # @return [Symbol] # attr_reader :key ## # Returns an array of Flag::Syntax for the flags. # @return [Array] # attr_reader :flag_syntax ## # Returns the effective acceptor. # @return [Toys::Acceptor::Base] # attr_reader :acceptor ## # Returns the default value, which may be `nil`. # @return [Object] # attr_reader :default ## # The short description string. # # When reading, this is always returned as a {Toys::WrappableString}. # # When setting, the description may be provided as any of the following: # * A {Toys::WrappableString}. # * A normal String, which will be transformed into a # {Toys::WrappableString} using spaces as word delimiters. # * An Array of String, which will be transformed into a # {Toys::WrappableString} where each array element represents an # individual word for wrapping. # # @return [Toys::WrappableString] # attr_reader :desc ## # The long description strings. # # When reading, this is returned as an Array of {Toys::WrappableString} # representing the lines in the description. # # When setting, the description must be provided as an Array where *each # element* may be any of the following: # * A {Toys::WrappableString} representing one line. # * A normal String representing a line. This will be transformed into a # {Toys::WrappableString} using spaces as word delimiters. # * An Array of String representing a line. This will be transformed into # a {Toys::WrappableString} where each array element represents an # individual word for wrapping. # # @return [Array] # attr_reader :long_desc ## # The handler for setting/updating the value. # @return [Proc] # attr_reader :handler ## # The proc that determines shell completions for the flag. # @return [Proc,Toys::Completion::Base] # attr_reader :flag_completion ## # The proc that determines shell completions for the value. # @return [Proc,Toys::Completion::Base] # attr_reader :value_completion ## # The type of flag. # # @return [:boolean] if the flag is a simple boolean switch # @return [:value] if the flag sets a value # attr_reader :flag_type ## # The type of value. # # @return [:required] if the flag type is `:value` and the value is # required. # @return [:optional] if the flag type is `:value` and the value is # optional. # @return [nil] if the flag type is not `:value`. # attr_reader :value_type ## # The string label for the value as it should display in help. # @return [String] The label # @return [nil] if the flag type is not `:value`. # attr_reader :value_label ## # The value delimiter, which may be `""`, `" "`, or `"="`. # # @return [String] The delimiter # @return [nil] if the flag type is not `:value`. # attr_reader :value_delim ## # The display name of this flag. # @return [String] # attr_reader :display_name ## # A string that can be used to sort this flag # @return [String] # attr_reader :sort_str ## # An array of Flag::Syntax including only short (single dash) flags. # @return [Array] # def short_flag_syntax # Source available in the toys-core gem end ## # An array of Flag::Syntax including only long (double-dash) flags. # @return [Array] # def long_flag_syntax # Source available in the toys-core gem end ## # The list of all effective flags used. # @return [Array] # def effective_flags # Source available in the toys-core gem end ## # Look up the flag by string. Returns an object that indicates whether # the given string matched this flag, whether the match was unique, and # other pertinent information. # # @param str [String] Flag string to look up # @return [Toys::Flag::Resolution] Information about the match. # def resolve(str) # Source available in the toys-core gem end ## # A list of canonical flag syntax strings. # # @return [Array] # def canonical_syntax_strings # Source available in the toys-core gem end ## # Whether this flag is active--that is, it has a nonempty flags list. # # @return [Boolean] # def active? # Source available in the toys-core gem end ## # Set the short description string. # # See {#desc} for details. # # @param desc [Toys::WrappableString,String,Array] # def desc=(desc) # Source available in the toys-core gem end ## # Set the long description strings. # # See {#long_desc} for details. # # @param long_desc [Array>] # def long_desc=(long_desc) # Source available in the toys-core gem end ## # Append long description strings. # # You must pass an array of lines in the long description. See {#long_desc} # for details on how each line may be represented. # # @param long_desc [Array>] # @return [self] # def append_long_desc(long_desc) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/flag_group.rb0000644000004100000410000001320415164300675020155 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A FlagGroup is a group of flags with the same requirement settings. # module FlagGroup ## # Create a flag group object of the given type. # # The type should be one of the following symbols: # * `:optional` All flags in the group are optional # * `:required` All flags in the group are required # * `:exactly_one` Exactly one flag in the group must be provided # * `:at_least_one` At least one flag in the group must be provided # * `:at_most_one` At most one flag in the group must be provided # # @param type [Symbol] The type of group. Default is `:optional`. # @param desc [String,Array,Toys::WrappableString] Short # description for the group. See {Toys::ToolDefinition#desc} for a # description of allowed formats. Defaults to `"Flags"`. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag group. See # {Toys::ToolDefinition#long_desc} for a description of allowed # formats. Defaults to the empty array. # @param name [String,Symbol,nil] The name of the group, or nil for no # name. # @return [Toys::FlagGroup::Base] A flag group of the correct subclass. # def self.create(type: nil, name: nil, desc: nil, long_desc: nil) # Source available in the toys-core gem end ## # **_Defined in the toys-core gem_** # # The base class of a FlagGroup, implementing everything except validation. # The base class effectively behaves as an Optional group. And the default # group that contains flags not otherwise assigned to a group, is of this # type. However, you should use {Toys::FlagGroup::Optional} when creating # an explicit optional group. # class Base ## # The symbolic name for this group # @return [String,Symbol,nil] # attr_reader :name ## # The short description string. # # When reading, this is always returned as a {Toys::WrappableString}. # # When setting, the description may be provided as any of the following: # * A {Toys::WrappableString}. # * A normal String, which will be transformed into a # {Toys::WrappableString} using spaces as word delimiters. # * An Array of String, which will be transformed into a # {Toys::WrappableString} where each array element represents an # individual word for wrapping. # # @return [Toys::WrappableString] # attr_reader :desc ## # The long description strings. # # When reading, this is returned as an Array of {Toys::WrappableString} # representing the lines in the description. # # When setting, the description must be provided as an Array where *each # element* may be any of the following: # * A {Toys::WrappableString} representing one line. # * A normal String representing a line. This will be transformed into # a {Toys::WrappableString} using spaces as word delimiters. # * An Array of String representing a line. This will be transformed # into a {Toys::WrappableString} where each array element represents # an individual word for wrapping. # # @return [Array] # attr_reader :long_desc ## # An array of flags that are in this group. # Do not modify the returned array. # @return [Array] # attr_reader :flags ## # Returns true if this group is empty # @return [Boolean] # def empty? # Source available in the toys-core gem end ## # Returns a string summarizing this group. This is generally either the # short description or a representation of all the flags included. # @return [String] # def summary # Source available in the toys-core gem end ## # Set the short description string. # # See {#desc} for details. # # @param desc [Toys::WrappableString,String,Array] # def desc=(desc) # Source available in the toys-core gem end ## # Set the long description strings. # # See {#long_desc} for details. # # @param long_desc [Array>] # def long_desc=(long_desc) # Source available in the toys-core gem end ## # Append long description strings. # # You must pass an array of lines in the long description. See {#long_desc} # for details on how each line may be represented. # # @param long_desc [Array>] # @return [self] # def append_long_desc(long_desc) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A FlagGroup containing all required flags # class Required < Base end ## # **_Defined in the toys-core gem_** # # A FlagGroup containing all optional flags # class Optional < Base end ## # **_Defined in the toys-core gem_** # # A FlagGroup in which exactly one flag must be set # class ExactlyOne < Base end ## # **_Defined in the toys-core gem_** # # A FlagGroup in which at most one flag must be set # class AtMostOne < Base end ## # **_Defined in the toys-core gem_** # # A FlagGroup in which at least one flag must be set # class AtLeastOne < Base end end end toys-0.21.0/core-docs/toys/cli.rb0000644000004100000410000004732515164300675016612 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A Toys-based CLI. # # This is the entry point for command line execution. It includes the set of # tool definitions (and/or information on how to load them from the file # system), configuration parameters such as logging and error handling, and a # method to call to invoke a command. # # This is the class to instantiate to create a Toys-based command line # executable. For example: # # #!/usr/bin/env ruby # require "toys-core" # cli = Toys::CLI.new # cli.add_config_block do # def run # puts "Hello, world!" # end # end # exit(cli.run(*ARGV)) # # The currently running CLI is also available at runtime, and can be used by # tools that want to invoke other tools. For example: # # # My .toys.rb # tool "foo" do # def run # puts "in foo" # end # end # tool "bar" do # def run # puts "in bar" # cli.run "foo" # end # end # class CLI ## # Create a CLI. # # Most configuration parameters (besides tool definitions and tool lookup # paths) are set as options passed to the constructor. These options fall # roughly into four categories: # # * Options affecting output behavior: # * `logger`: A global logger for all tools to use # * `logger_factory`: A proc that returns a logger to use # * `base_level`: The default log level # * `error_handler`: Callback for handling exceptions # * `executable_name`: The name of the executable # * Options affecting tool specification # * `extra_delimibers`: Tool name delimiters besides space # * `completion`: Tab completion handler # * Options affecting tool definition # * `middleware_stack`: The middleware applied to all tools # * `mixin_lookup`: Where to find well-known mixins # * `middleware_lookup`: Where to find well-known middleware # * `template_lookup`: Where to find well-known templates # * Options affecting tool files and directories # * `config_dir_name`: Directory name containing tool files # * `config_file_name`: File name for tools # * `index_file_name`: Name of index files in tool directories # * `preload_file_name`: Name of preload files in tool directories # * `preload_dir_name`: Name of preload directories in tool directories # * `data_dir_name`: Name of data directories in tool directories # # @param logger [Logger] A global logger to use for all tools. This can be # set if the CLI will call at most one tool at a time. However, it will # behave incorrectly if CLI might run multiple tools at the same time # with different verbosity settings (since the logger cannot have # multiple level settings simultaneously). In that case, do not set a # global logger, but use the `logger_factory` parameter instead. # @param logger_factory [Proc] A proc that takes a {Toys::ToolDefinition} # as an argument, and returns a `Logger` to use when running that tool. # Optional. If not provided (and no global logger is set), # {Toys::CLI.default_logger_factory} is called to get a basic default. # @param base_level [Integer] The logger level that should correspond # to zero verbosity. # Optional. If not provided, defaults to the current level of the # logger (which is often `Logger::WARN`). # @param error_handler [Proc,nil] A proc that is called when an unhandled # exception (a normal exception subclassing `StandardError`, an error # loading a toys config file subclassing `SyntaxError`, or an unhandled # signal subclassing `SignalException`) is detected. The proc should # take a {Toys::ContextualError}, whose cause is the unhandled # exception, as the sole argument, and report the error. It should # return an exit code (normally nonzero) appropriate to the error. # Optional. If not provided, {Toys::CLI.default_error_handler} is # called to get a basic default handler. # @param executable_name [String] The executable name displayed in help # text. Optional. Defaults to the ruby program name. # # @param extra_delimiters [String] A string containing characters that can # function as delimiters in a tool name. Defaults to empty. Allowed # characters are period, colon, and slash. # @param completion [Toys::Completion::Base] A specifier for shell tab # completion for the CLI as a whole. # Optional. If not provided, {Toys::CLI.default_completion} is called # to get a default completion that delegates to the tool. # # @param middleware_stack [Array] An array of # middleware that will be used by default for all tools. # Optional. If not provided, uses a default set of middleware defined # in {Toys::CLI.default_middleware_stack}. To include no middleware, # pass the empty array explicitly. # @param mixin_lookup [Toys::ModuleLookup] A lookup for well-known mixin # modules (i.e. with symbol names). # Optional. If not provided, defaults to the set of standard mixins # provided by toys-core, as defined by # {Toys::CLI.default_mixin_lookup}. If you explicitly want no standard # mixins, pass an empty instance of {Toys::ModuleLookup}. # @param middleware_lookup [Toys::ModuleLookup] A lookup for well-known # middleware classes. # Optional. If not provided, defaults to the set of standard middleware # classes provided by toys-core, as defined by # {Toys::CLI.default_middleware_lookup}. If you explicitly want no # standard middleware, pass an empty instance of # {Toys::ModuleLookup}. # @param template_lookup [Toys::ModuleLookup] A lookup for well-known # template classes. # Optional. If not provided, defaults to the set of standard template # classes provided by toys core, as defined by # {Toys::CLI.default_template_lookup}. If you explicitly want no # standard templates, pass an empty instance of {Toys::ModuleLookup}. # # @param config_dir_name [String] A directory with this name that appears # in the loader path, is treated as a configuration directory whose # contents are loaded into the toys configuration. # Optional. If not provided, toplevel configuration directories are # disabled. # Note: the standard toys executable sets this to `".toys"`. # @param config_file_name [String] A file with this name that appears in # the loader path, is treated as a toplevel configuration file whose # contents are loaded into the toys configuration. This does not # include "index" configuration files located within a configuration # directory. # Optional. If not provided, toplevel configuration files are disabled. # Note: the standard toys executable sets this to `".toys.rb"`. # @param index_file_name [String] A file with this name that appears in any # configuration directory is loaded first as a standalone configuration # file. This does not include "toplevel" configuration files outside # configuration directories. # Optional. If not provided, index configuration files are disabled. # Note: the standard toys executable sets this to `".toys.rb"`. # @param preload_file_name [String] A file with this name that appears # in any configuration directory is preloaded using `require` before # any tools in that configuration directory are defined. A preload file # includes normal Ruby code, rather than Toys DSL definitions. The # preload file is loaded before any files in a preload directory. # Optional. If not provided, preload files are disabled. # Note: the standard toys executable sets this to `".preload.rb"`. # @param preload_dir_name [String] A directory with this name that appears # in any configuration directory is searched for Ruby files, which are # preloaded using `require` before any tools in that configuration # directory are defined. Files in a preload directory include normal # Ruby code, rather than Toys DSL definitions. Files in a preload # directory are loaded after any standalone preload file. # Optional. If not provided, preload directories are disabled. # Note: the standard toys executable sets this to `".preload"`. # @param data_dir_name [String] A directory with this name that appears in # any configuration directory is added to the data directory search # path for any tool file in that directory. # Optional. If not provided, data directories are disabled. # Note: the standard toys executable sets this to `".data"`. # @param lib_dir_name [String] A directory with this name that appears in # any configuration directory is added to the Ruby load path when # executing any tool file in that directory. # Optional. If not provided, lib directories are disabled. # Note: the standard toys executable sets this to `".lib"`. # def initialize(executable_name: nil, # rubocop:disable Metrics/MethodLength middleware_stack: nil, extra_delimiters: "", config_dir_name: nil, config_file_name: nil, index_file_name: nil, preload_file_name: nil, preload_dir_name: nil, data_dir_name: nil, lib_dir_name: nil, mixin_lookup: nil, middleware_lookup: nil, template_lookup: nil, logger_factory: nil, logger: nil, base_level: nil, error_handler: nil, completion: nil) # Source available in the toys-core gem end ## # Make a clone with the same settings but no config blocks and no paths in # the loader. This is sometimes useful for calling another tool that has to # be loaded from a different configuration. # # @param opts [keywords] Any configuration arguments that should be # modified from the original. See {#initialize} for a list of # recognized keywords. # @return [Toys::CLI] # @yieldparam cli [Toys::CLI] If you pass a block, the new CLI is yielded # to it so you can add paths and make other modifications. # def child(**opts) # Source available in the toys-core gem end ## # The current loader for this CLI. # @return [Toys::Loader] # attr_reader :loader ## # The effective executable name used for usage text in this CLI. # @return [String] # attr_reader :executable_name ## # The string of tool name delimiter characters (besides space). # @return [String] # attr_reader :extra_delimiters ## # The global logger, if any. # @return [Logger,nil] # attr_reader :logger ## # The logger factory. # @return [Proc] # attr_reader :logger_factory ## # The initial logger level in this CLI, used as the level for verbosity 0. # May be `nil`, indicating it will use the initial logger setting. # @return [Integer,nil] # attr_reader :base_level ## # The overall completion strategy for this CLI. # @return [Toys::Completion::Base,Proc] # attr_reader :completion ## # Add a specific configuration file or directory to the loader. # # This is generally used to load a static or "built-in" set of tools, # either for a standalone command line executable based on Toys, or to # provide a "default" set of tools for a dynamic executable. For example, # the main Toys executable uses this to load the builtin tools from its # "builtins" directory. # # @param path [String] A path to add. May reference a single Toys file or # a Toys directory. # @param high_priority [Boolean] Add the config at the head of the priority # list rather than the tail. # @param source_name [String] A custom name for the root source. Optional. # @param context_directory [String,nil,:path,:parent] The context directory # for tools loaded from this path. You can pass a directory path as a # string, `:path` to denote the given path, `:parent` to denote the # given path's parent directory, or `nil` to denote no context. # Defaults to `:parent`. # @return [self] # def add_config_path(path, high_priority: false, source_name: nil, context_directory: :parent) # Source available in the toys-core gem end ## # Add a configuration block to the loader. # # This is used to create tools "inline", and is useful for simple command # line executables based on Toys. # # @param high_priority [Boolean] Add the config at the head of the priority # list rather than the tail. # @param source_name [String] The source name that will be shown in # documentation for tools defined in this block. If omitted, a default # unique string will be generated. # @param block [Proc] The block of configuration, executed in the context # of the tool DSL {Toys::DSL::Tool}. # @param context_directory [String,nil] The context directory for tools # loaded from this block. You can pass a directory path as a string, or # `nil` to denote no context. Defaults to `nil`. # @return [self] # def add_config_block(high_priority: false, source_name: nil, context_directory: nil, &block) # Source available in the toys-core gem end ## # Checks the given directory path. If it contains a config file and/or # config directory, those are added to the loader. # # The main Toys executable uses this method to load tools from directories # in the `TOYS_PATH`. # # @param search_path [String] A path to search for configs. # @param high_priority [Boolean] Add the configs at the head of the # priority list rather than the tail. # @param context_directory [String,nil,:path,:parent] The context directory # for tools loaded from this path. You can pass a directory path as a # string, `:path` to denote the given path, `:parent` to denote the # given path's parent directory, or `nil` to denote no context. # Defaults to `:path`. # @return [self] # def add_search_path(search_path, high_priority: false, context_directory: :path) # Source available in the toys-core gem end ## # Walk up the directory hierarchy from the given start location, and add to # the loader any config files and directories found. # # The main Toys executable uses this method to load tools from the current # directory and its ancestors. # # @param start [String] The first directory to add. Defaults to the current # working directory. # @param terminate [Array] Optional list of directories that should # terminate the search. If the walk up the directory tree encounters # one of these directories, the search is halted without checking the # terminating directory. # @param high_priority [Boolean] Add the configs at the head of the # priority list rather than the tail. # @return [self] # def add_search_path_hierarchy(start: nil, terminate: [], high_priority: false) # Source available in the toys-core gem end ## # Run the CLI with the given command line arguments. # Handles exceptions using the error handler. # # @param args [String...] Command line arguments specifying which tool to # run and what arguments to pass to it. You may pass either a single # array of strings, or a series of string arguments. # @param verbosity [Integer] Initial verbosity. Default is 0. # @param delegated_from [Toys::Context] The context from which this # execution is delegated. Optional. Should be set only if this is a # delegated execution. # # @return [Integer] The resulting process status code (i.e. 0 for success). # def run(*args, verbosity: 0, delegated_from: nil) # Source available in the toys-core gem end ## # Prepare a tool to be run, but just execute the given block rather than # performing a full run of the tool. This is intended for testing tools. # Unlike {#run}, this does not catch errors and perform error handling. # # @param args [String...] Command line arguments specifying which tool to # run and what arguments to pass to it. You may pass either a single # array of strings, or a series of string arguments. # @yieldparam context [Toys::Context] Yields the tool context. # # @return [Object] The value returned from the block. # def load_tool(*args) # Source available in the toys-core gem end class << self ## # Returns a default set of middleware that may be used as a starting # point for a typical CLI. This set includes the following in order: # # * {Toys::StandardMiddleware::SetDefaultDescriptions} providing # defaults for description fields. # * {Toys::StandardMiddleware::ShowHelp} adding the `--help` flag and # providing default behavior for namespaces. # * {Toys::StandardMiddleware::HandleUsageErrors} # * {Toys::StandardMiddleware::AddVerbosityFlags} adding the `--verbose` # and `--quiet` flags for managing the logger level. # # @return [Array] # def default_middleware_stack # Source available in the toys-core gem end ## # Returns a default ModuleLookup for mixins that points at the # StandardMixins module. # # @return [Toys::ModuleLookup] # def default_mixin_lookup # Source available in the toys-core gem end ## # Returns a default ModuleLookup for middleware that points at the # StandardMiddleware module. # # @return [Toys::ModuleLookup] # def default_middleware_lookup # Source available in the toys-core gem end ## # Returns a default empty ModuleLookup for templates. # # @return [Toys::ModuleLookup] # def default_template_lookup # Source available in the toys-core gem end ## # Returns a bare-bones error handler that takes simply reraises the # error. If the original error (the cause of the {Toys::ContextualError}) # was a `SignalException` (or a subclass such as `Interrupted`), that # `SignalException` itself is reraised so that the Ruby VM has a chance # to handle it. Otherwise, for any other error, the # {Toys::ContextualError} is reraised. # # @return [Proc] # def default_error_handler # Source available in the toys-core gem end ## # Returns a default logger factory that generates simple loggers that # write to STDERR. # # @return [Proc] # def default_logger_factory # Source available in the toys-core gem end ## # Returns a default Completion that simply uses the tool's completion. # def default_completion # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/arg_parser.rb0000644000004100000410000002761615164300675020171 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # An internal class that parses command line arguments for a tool. # # Generally, you should not need to use this class directly. It is called # from {Toys::CLI}. # class ArgParser ## # **_Defined in the toys-core gem_** # # Base representation of a usage error reported by the ArgParser. # # This functions similarly to an exception, but is not raised. Rather, it # is returned in the {Toys::ArgParser#errors} array. # class UsageError ## # Create a UsageError given a message and common data # # @param message [String] The basic error message. # @param name [String,nil] The name of the element (normally flag or # positional argument) that reported the error, or nil if there is # no definite element. # @param value [String,nil] The value that was rejected, or nil if not # applicable. # @param suggestions [Array,nil] An array of suggestions from # DidYouMean, or nil if not applicable. # def initialize(message, name: nil, value: nil, suggestions: nil) # Source available in the toys-core gem end ## # The basic error message. Does not include suggestions, if any. # # @return [String] # attr_reader :message ## # The name of the element (normally a flag or positional argument) that # reported the error. # # @return [String] The element name. # @return [nil] if there is no definite element source. # attr_reader :name ## # The value that was rejected. # # @return [String] the value string # @return [nil] if a value is not applicable to this error. # attr_reader :value ## # An array of suggestions from DidYouMean. # # @return [Array] array of suggestions. # @return [nil] if suggestions are not applicable to this error. # attr_reader :suggestions ## # A fully formatted error message including suggestions. # # @return [String] # def full_message # Source available in the toys-core gem end alias to_s full_message end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a value was provided for a flag that does not # take a value. # class FlagValueNotAllowedError < UsageError ## # Create a FlagValueNotAllowedError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param name [String] The name of the flag. Normally required. # def initialize(message = nil, name: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a value was not provided for a flag that requires # a value. # class FlagValueMissingError < UsageError ## # Create a FlagValueMissingError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param name [String] The name of the flag. Normally required. # def initialize(message = nil, name: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a flag name was not recognized. # class FlagUnrecognizedError < UsageError ## # Create a FlagUnrecognizedError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param value [String] The requested flag name. Normally required. # @param suggestions [Array] An array of suggestions to present # to the user. Optional. # def initialize(message = nil, value: nil, suggestions: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a flag name prefix was given that matched # multiple flags. # class FlagAmbiguousError < UsageError ## # Create a FlagAmbiguousError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param value [String] The requested flag name. Normally required. # @param suggestions [Array] An array of suggestions to present # to the user. Optional. # def initialize(message = nil, value: nil, suggestions: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a flag did not accept the value given it. # class FlagValueUnacceptableError < UsageError ## # Create a FlagValueUnacceptableError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param name [String] The name of the flag. Normally required. # @param value [String] The value given. Normally required. # @param suggestions [Array] An array of suggestions to present # to the user. Optional. # def initialize(message = nil, name: nil, value: nil, suggestions: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a positional argument did not accept the value # given it. # class ArgValueUnacceptableError < UsageError ## # Create an ArgValueUnacceptableError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param name [String] The name of the argument. Normally required. # @param value [String] The value given. Normally required. # @param suggestions [Array] An array of suggestions to present # to the user. Optional. # def initialize(message = nil, name: nil, value: nil, suggestions: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a required positional argument was not fulfilled. # class ArgMissingError < UsageError ## # Create an ArgMissingError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param name [String] The name of the argument. Normally required. # def initialize(message = nil, name: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating extra arguments were supplied. # class ExtraArgumentsError < UsageError ## # Create an ExtraArgumentsError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param value [String] The first extra argument. Normally required. # @param values [Array] All extra arguments. Normally required. # def initialize(message = nil, value: nil, values: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating the given subtool name does not exist. # class ToolUnrecognizedError < UsageError ## # Create a ToolUnrecognizedError. # # @param message [String,nil] A custom message. Normally omitted, in # which case an appropriate default is supplied. # @param value [String] The requested subtool. Normally required. # @param values [Array] The full path of the requested tool. # Normally required. # @param suggestions [Array] An array of suggestions to present # to the user. Optional. # def initialize(message = nil, value: nil, values: nil, suggestions: nil) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A UsageError indicating a flag group constraint was not fulfilled. # class FlagGroupConstraintError < UsageError ## # Create a FlagGroupConstraintError. # # @param message [String] The message. Required. # def initialize(message) # Source available in the toys-core gem end end ## # Create an argument parser for a particular tool. # # @param cli [Toys::CLI] The CLI in effect. # @param tool [Toys::ToolDefinition] The tool defining the argument format. # @param default_data [Hash] Additional initial data (such as verbosity). # @param require_exact_flag_match [Boolean] Whether to require flag matches # be exact (not partial). Default is false. # def initialize(cli, tool, default_data: {}, require_exact_flag_match: false) # Source available in the toys-core gem end ## # The tool definition governing this parser. # @return [Toys::ToolDefinition] # attr_reader :tool ## # All command line arguments that have been parsed. # @return [Array] # attr_reader :parsed_args ## # Extra positional args that were not matched. # @return [Array] # attr_reader :unmatched_positional ## # Flags that were not matched. # @return [Array] # attr_reader :unmatched_flags ## # All args that were not matched. # @return [Array] # attr_reader :unmatched_args ## # The collected tool data from parsed arguments. # @return [Hash] # attr_reader :data ## # An array of parse error messages. # @return [Array] # attr_reader :errors ## # The current flag definition whose value is still pending # # @return [Toys::Flag] The pending flag definition # @return [nil] if there is no pending flag # attr_reader :active_flag_def ## # Whether flags are currently allowed. Returns false after `--` is received. # @return [Boolean] # def flags_allowed? # Source available in the toys-core gem end ## # Determine if this parser is finished # @return [Boolean] # def finished? # Source available in the toys-core gem end ## # The argument definition that will be applied to the next argument. # # @return [Toys::PositionalArg] The next argument definition. # @return [nil] if all arguments have been filled. # def next_arg_def # Source available in the toys-core gem end ## # Incrementally parse a single string or an array of strings # # @param args [String,Array] # @return [self] # def parse(args) # Source available in the toys-core gem end ## # Complete parsing. This should be called after all arguments have been # processed. It does a final check for any errors, including: # # * The arguments ended with a flag that was expecting a value but wasn't # provided. # * One or more required arguments were never given a value. # * One or more extra arguments were provided. # * Restrictions defined in one or more flag groups were not fulfilled. # # Any errors are added to the errors array. It also fills in final values # for `Context::Key::USAGE_ERRORS` and `Context::Key::ARGS`. # # After this method is called, this object is locked down, and no # additional arguments may be parsed. # # @return [self] # def finish # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/input_file.rb0000644000004100000410000000126615164300675020173 0ustar www-datawww-data## # **_Defined in the toys-core gem_** # # This module is the root namespace for tool definitions loaded from files. # Whenever a toys configuration file is parsed, a module is created under this # parent for that file's contents. Tool classes defined in that file, along # with mixins and templates, and any other classes, modules, and constants # defined, are located within that file's module. # module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren ## # @private This interface is internal and subject to change without warning. # def self.evaluate(tool_class, words, priority, remaining_words, source, loader) # Source available in the toys-core gem end end toys-0.21.0/core-docs/toys/core.rb0000644000004100000410000000032215164300675016755 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # The core Toys classes. # module Core ## # Current version of Toys core. # @return [String] # VERSION = "0.21.0" end end toys-0.21.0/core-docs/toys/standard_middleware/0000755000004100000410000000000015164300675021500 5ustar www-datawww-datatoys-0.21.0/core-docs/toys/standard_middleware/apply_config.rb0000644000004100000410000000142515164300675024501 0ustar www-datawww-datamodule Toys module StandardMiddleware ## # **_Defined in the toys-core gem_** # # A middleware that applies the given block to all tool configurations. # class ApplyConfig ## # Create an ApplyConfig middleware # # @param parent_source [Toys::SourceInfo] The SourceInfo corresponding to # the source where this block is provided, or `nil` (the default) if # the block does not come from a Toys file. # @param source_name [String] A user-visible name for the source, or # `nil` to use the default. # @param block [Proc] The configuration to apply. # def initialize(parent_source: nil, source_name: nil, &block) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_middleware/handle_usage_errors.rb0000644000004100000410000000224615164300675026044 0ustar www-datawww-datamodule Toys module StandardMiddleware ## # **_Defined in the toys-core gem_** # # This middleware handles the case of a usage error. If a usage error, such # as an unrecognized flag or an unfulfilled required argument, is detected, # this middleware intercepts execution and displays the error along with # the short help string, and terminates execution with an error code. # class HandleUsageErrors ## # Exit code for usage error. (2 by convention) # @return [Integer] # USAGE_ERROR_EXIT_CODE = 2 ## # Create a HandleUsageErrors middleware. # # @param exit_code [Integer] The exit code to return if a usage error # occurs. Default is {USAGE_ERROR_EXIT_CODE}. # @param stream [IO] Output stream to write to. Default is stderr. # @param styled_output [Boolean,nil] Cause the tool to display help text # with ansi styles. If `nil`, display styles if the output stream is # a tty. Default is `nil`. # def initialize(exit_code: nil, stream: $stderr, styled_output: nil) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_middleware/set_default_descriptions.rb0000644000004100000410000002332015164300675027112 0ustar www-datawww-datamodule Toys module StandardMiddleware ## # **_Defined in the toys-core gem_** # # This middleware sets default description fields for tools and command # line arguments and flags that do not have them set otherwise. # # You can modify the static descriptions for tools, namespaces, and the # root tool by passing parameters to this middleware. For finer control, # you can override methods to modify the description generation logic. # class SetDefaultDescriptions ## # The default description for tools. # @return [String] # DEFAULT_TOOL_DESC = "(No tool description available)" ## # The default description for delegating tools. # @return [String] # DEFAULT_DELEGATE_DESC = '(Delegates to "%s")' ## # The default description for namespaces. # @return [String] # DEFAULT_NAMESPACE_DESC = "(A namespace of tools)" ## # The default description for the root tool. # @return [String] # DEFAULT_ROOT_DESC = "Command line tool built using the toys-core gem." ## # The default long description for the root tool. # @return [String] # DEFAULT_ROOT_LONG_DESC = [ "This command line tool was built using the toys-core gem. See" \ " https://dazuma.github.io/toys/gems/toys-core for more info.", "To replace this message, set the description and long description" \ " of the root tool, or configure the SetDefaultDescriptions" \ " middleware.", ].freeze ## # Create a SetDefaultDescriptions middleware given default descriptions. # # @param default_tool_desc [String,nil] The default short description for # runnable tools, or `nil` not to set one. Defaults to # {DEFAULT_TOOL_DESC}. # @param default_tool_long_desc [Array,nil] The default long # description for runnable tools, or `nil` not to set one. Defaults # to `nil`. # @param default_namespace_desc [String,nil] The default short # description for non-runnable tools, or `nil` not to set one. # Defaults to {DEFAULT_TOOL_DESC}. # @param default_namespace_long_desc [Array,nil] The default long # description for non-runnable tools, or `nil` not to set one. # Defaults to `nil`. # @param default_root_desc [String,nil] The default short description for # the root tool, or `nil` not to set one. Defaults to # {DEFAULT_ROOT_DESC}. # @param default_root_long_desc [Array,nil] The default long # description for the root tool, or `nil` not to set one. Defaults to # {DEFAULT_ROOT_LONG_DESC}. # @param default_delegate_desc [String,nil] The default short description # for delegate tools, or `nil` not to set one. May include an sprintf # field for the `target` name. Defaults to {DEFAULT_DELEGATE_DESC}. # def initialize(default_tool_desc: DEFAULT_TOOL_DESC, default_tool_long_desc: nil, default_namespace_desc: DEFAULT_NAMESPACE_DESC, default_namespace_long_desc: nil, default_root_desc: DEFAULT_ROOT_DESC, default_root_long_desc: DEFAULT_ROOT_LONG_DESC, default_delegate_desc: DEFAULT_DELEGATE_DESC) # Source available in the toys-core gem end protected ## # This method implements the logic for generating a tool description. # By default, it uses the parameters given to the middleware object. # Override this method to provide different logic. # # @param tool [Toys::ToolDefinition] The tool to document. # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::Loader} is passed with key `:loader`. Future versions # of Toys may provide additional information. # @return [String,Array,Toys::WrappableString] The default # description. See {Toys::DSL::Tool#desc} for info on the format. # @return [nil] if this middleware should not set the description. # def generate_tool_desc(tool, data) # Source available in the toys-core gem end ## # This method implements logic for generating a tool long description. # By default, it uses the parameters given to the middleware object. # Override this method to provide different logic. # # @param tool [Toys::ToolDefinition] The tool to document # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::Loader} is passed with key `:loader`. Future versions of # Toys may provide additional information. # @return [Array>] The default # long description. See {Toys::DSL::Tool#long_desc} for info on the # format. # @return [nil] if this middleware should not set the long description. # def generate_tool_long_desc(tool, data) # Source available in the toys-core gem end ## # This method implements the logic for generating a flag description. # Override this method to provide different logic. # # @param flag [Toys::Flag] The flag to document # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::ToolDefinition} is passed with key `:tool`. Future # versions of Toys may provide additional information. # @return [String,Array,Toys::WrappableString] The default # description. See {Toys::DSL::Tool#desc} for info on the format. # @return [nil] if this middleware should not set the description. # def generate_flag_desc(flag, data) # Source available in the toys-core gem end ## # This method implements logic for generating a flag long description. # Override this method to provide different logic. # # @param flag [Toys::Flag] The flag to document # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::ToolDefinition} is passed with key `:tool`. Future # versions of Toys may provide additional information. # @return [Array>] The default # long description. See {Toys::DSL::Tool#long_desc} for info on the # format. # @return [nil] if this middleware should not set the long description. # def generate_flag_long_desc(flag, data) # Source available in the toys-core gem end ## # This method implements the logic for generating an arg description. # Override this method to provide different logic. # # @param arg [Toys::PositionalArg] The arg to document # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::ToolDefinition} is passed with key `:tool`. Future # versions of Toys may provide additional information. # @return [String,Array,Toys::WrappableString] The default # description. See {Toys::DSL::Tool#desc} for info on the format. # @return [nil] if this middleware should not set the description. # def generate_arg_desc(arg, data) # Source available in the toys-core gem end ## # This method implements logic for generating an arg long description. # Override this method to provide different logic. # # @param arg [Toys::PositionalArg] The arg to document # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::ToolDefinition} is passed with key `:tool`. Future # versions of Toys may provide additional information. # @return [Array>] The default # long description. See {Toys::DSL::Tool#long_desc} for info on the # format. # @return [nil] if this middleware should not set the long description. # def generate_arg_long_desc(arg, data) # Source available in the toys-core gem end ## # This method implements the logic for generating a flag group # description. Override this method to provide different logic. # # @param group [Toys::FlagGroup] The flag group to document # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::ToolDefinition} is passed with key `:tool`. Future # versions of Toys may provide additional information. # @return [String,Array,Toys::WrappableString] The default # description. See {Toys::DSL::Tool#desc} for info on the format. # @return [nil] if this middleware should not set the description. # def generate_flag_group_desc(group, data) # Source available in the toys-core gem end ## # This method implements the logic for generating a flag group long # description. Override this method to provide different logic. # # @param group [Toys::FlagGroup] The flag group to document # @param data [Hash] Additional data that might be useful. Currently, # the {Toys::ToolDefinition} is passed with key `:tool`. Future # versions of Toys may provide additional information. # @return [Array>] The default # long description. See {Toys::DSL::Tool#long_desc} for info on the # format. # @return [nil] if this middleware should not set the long description. # def generate_flag_group_long_desc(group, data) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_middleware/show_help.rb0000644000004100000410000001551615164300675024025 0ustar www-datawww-datamodule Toys module StandardMiddleware ## # **_Defined in the toys-core gem_** # # A middleware that shows help text for the tool when a flag (typically # `--help`) is provided. It can also be configured to show help by # default if the tool is a namespace that is not runnable. # # If a tool is not runnable, this middleware can also add a # `--[no-]recursive` flag, which, when set to `true` (the default), shows # all subtools recursively rather than only immediate subtools. This # middleware can also search for keywords in its subtools. # class ShowHelp ## # Default help flags # @return [Array] # DEFAULT_HELP_FLAGS = ["-?", "--help"].freeze ## # Default usage flags # @return [Array] # DEFAULT_USAGE_FLAGS = ["--usage"].freeze ## # Default list subtools flags # @return [Array] # DEFAULT_LIST_FLAGS = ["--tools"].freeze ## # Default recursive flags # @return [Array] # DEFAULT_RECURSIVE_FLAGS = ["-r", "--[no-]recursive"].freeze ## # Default search flags # @return [Array] # DEFAULT_SEARCH_FLAGS = ["-s WORD", "--search=WORD"].freeze ## # Default show-all-subtools flags # @return [Array] # DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS = ["--all"].freeze ## # Key set when the show help flag is present # @return [Object] # SHOW_HELP_KEY = ::Object.new.freeze ## # Key set when the show usage flag is present # @return [Object] # SHOW_USAGE_KEY = ::Object.new.freeze ## # Key set when the show subtool list flag is present # @return [Object] # SHOW_LIST_KEY = ::Object.new.freeze ## # Key for the recursive setting # @return [Object] # RECURSIVE_SUBTOOLS_KEY = ::Object.new.freeze ## # Key for the search string # @return [Object] # SEARCH_STRING_KEY = ::Object.new.freeze ## # Key for the show-all-subtools setting # @return [Object] # SHOW_ALL_SUBTOOLS_KEY = ::Object.new.freeze ## # Key for the tool name # @return [Object] # TOOL_NAME_KEY = ::Object.new.freeze ## # Create a ShowHelp middleware. # # @param help_flags [Boolean,Array,Proc] Specify flags to # display help. The value may be any of the following: # # * An array of flags. # * The `true` value to use {DEFAULT_HELP_FLAGS}. # * The `false` value for no flags. (Default) # * A proc that takes a tool and returns any of the above. # # @param usage_flags [Boolean,Array,Proc] Specify flags to # display usage. The value may be any of the following: # # * An array of flags. # * The `true` value to use {DEFAULT_USAGE_FLAGS}. # * The `false` value for no flags. (Default) # * A proc that takes a tool and returns any of the above. # # @param list_flags [Boolean,Array,Proc] Specify flags to # display subtool list. The value may be any of the following: # # * An array of flags. # * The `true` value to use {DEFAULT_LIST_FLAGS}. # * The `false` value for no flags. (Default) # * A proc that takes a tool and returns any of the above. # # @param recursive_flags [Boolean,Array,Proc] Specify flags # to control recursive subtool search. The value may be any of the # following: # # * An array of flags. # * The `true` value to use {DEFAULT_RECURSIVE_FLAGS}. # * The `false` value for no flags. (Default) # * A proc that takes a tool and returns any of the above. # # @param search_flags [Boolean,Array,Proc] Specify flags # to search subtools for a search term. The value may be any of # the following: # # * An array of flags. # * The `true` value to use {DEFAULT_SEARCH_FLAGS}. # * The `false` value for no flags. (Default) # * A proc that takes a tool and returns any of the above. # # @param show_all_subtools_flags [Boolean,Array,Proc] Specify # flags to show all subtools, including hidden tools and non-runnable # namespaces. The value may be any of the following: # # * An array of flags. # * The `true` value to use {DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS}. # * The `false` value for no flags. (Default) # * A proc that takes a tool and returns any of the above. # # @param default_recursive [Boolean] Whether to search recursively for # subtools by default. Default is `false`. # @param default_show_all_subtools [Boolean] Whether to show all subtools # by default. Default is `false`. # @param fallback_execution [Boolean] Cause the tool to display its own # help text if it is not otherwise runnable. This is mostly useful # for namespaces, which have children are not runnable. Default is # `false`. # @param allow_root_args [Boolean] If the root tool includes flags for # help or usage, and doesn't otherwise use positional arguments, # then a tool name can be passed as arguments to display help for # that tool. # @param show_source_path [Boolean] Show the source path section. Default # is `false`. # @param separate_sources [Boolean] Split up tool list by source root. # Defaults to false. # @param use_less [Boolean] If the `less` tool is available, and the # output stream is a tty, then use `less` to display help text. # @param stream [IO] Output stream to write to. Default is stdout. # @param styled_output [Boolean,nil] Cause the tool to display help text # with ansi styles. If `nil`, display styles if the output stream is # a tty. Default is `nil`. # def initialize(help_flags: false, usage_flags: false, list_flags: false, recursive_flags: false, search_flags: false, show_all_subtools_flags: false, default_recursive: false, default_show_all_subtools: false, fallback_execution: false, allow_root_args: false, show_source_path: false, separate_sources: false, use_less: false, stream: $stdout, styled_output: nil) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_middleware/show_root_version.rb0000644000004100000410000000244015164300675025615 0ustar www-datawww-datamodule Toys module StandardMiddleware ## # **_Defined in the toys-core gem_** # # A middleware that displays a version string for the root tool if the # `--version` flag is given. # class ShowRootVersion ## # Default version flags # @return [Array] # DEFAULT_VERSION_FLAGS = ["--version"].freeze ## # Default description for the version flags # @return [String] # DEFAULT_VERSION_FLAG_DESC = "Display the version" ## # Key set when the version flag is present # @return [Object] # SHOW_VERSION_KEY = ::Object.new.freeze ## # Create a ShowVersion middleware # # @param version_string [String] The string that should be displayed. # @param version_flags [Array] A list of flags that should # trigger displaying the version. Default is # {DEFAULT_VERSION_FLAGS}. # @param stream [IO] Output stream to write to. Default is stdout. # def initialize(version_string: nil, version_flags: DEFAULT_VERSION_FLAGS, version_flag_desc: DEFAULT_VERSION_FLAG_DESC, stream: $stdout) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/standard_middleware/add_verbosity_flags.rb0000644000004100000410000000327615164300675026047 0ustar www-datawww-datamodule Toys module StandardMiddleware ## # **_Defined in the toys-core gem_** # # A middleware that provides flags for editing the verbosity. # # This middleware adds `-v`, `--verbose`, `-q`, and `--quiet` flags, if # not already defined by the tool. These flags affect the setting of # {Toys::Context::Key::VERBOSITY}, and, thus, the logger level. # class AddVerbosityFlags ## # Default verbose flags # @return [Array] # DEFAULT_VERBOSE_FLAGS = ["-v", "--verbose"].freeze ## # Default quiet flags # @return [Array] # DEFAULT_QUIET_FLAGS = ["-q", "--quiet"].freeze ## # Create a AddVerbosityFlags middleware. # # @param verbose_flags [Boolean,Array,Proc] Specify flags # to increase verbosity. The value may be any of the following: # # * An array of flags that increase verbosity. # * The `true` value to use {DEFAULT_VERBOSE_FLAGS}. (Default) # * The `false` value to disable verbose flags. # * A proc that takes a tool and returns any of the above. # # @param quiet_flags [Boolean,Array,Proc] Specify flags # to decrease verbosity. The value may be any of the following: # # * An array of flags that decrease verbosity. # * The `true` value to use {DEFAULT_QUIET_FLAGS}. (Default) # * The `false` value to disable quiet flags. # * A proc that takes a tool and returns any of the above. # def initialize(verbose_flags: true, quiet_flags: true) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/wrappable_string.rb0000644000004100000410000000675315164300675021406 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A string intended for word-wrapped display. # # A WrappableString is an array of string "fragments" representing the atomic # units that should not be split when word-wrapping. It should be possible to # reconstruct the original string by joining these fragments with whitespace. # class WrappableString ## # Create a wrapped string. # # You can pass either: # # * A single String, which will be split into fragments by whitespace. # * An array of Strings representing the fragments explicitly. # # @param string [String,Array] The string or array of string # fragments # def initialize(string = "") # Source available in the toys-core gem end ## # Returns the string fragments, i.e. the individual "words" for wrapping. # # @return [Array] # attr_reader :fragments ## # Returns a new WrappaableString whose content is the concatenation of this # WrappableString with another WrappableString. # # @param other [WrappableString] # @return [WrappableString] # def +(other) # Source available in the toys-core gem end ## # Returns true if the string is empty (i.e. has no fragments) # # @return [Boolean] # def empty? # Source available in the toys-core gem end ## # Returns the string without any wrapping # # @return [String] # def string # Source available in the toys-core gem end alias to_s string ## # Tests two wrappable strings for equality # @param other [Object] # @return [Boolean] # def ==(other) # Source available in the toys-core gem end alias eql? == ## # Returns a hash code for this object # @return [Integer] # def hash # Source available in the toys-core gem end ## # Wraps the string to the given width. # # @param width [Integer,nil] Width in characters, or `nil` for infinite. # @param width2 [Integer,nil] Width in characters for the second and # subsequent lines, or `nil` to use the same as width. # # @return [Array] Wrapped lines # def wrap(width, width2 = nil) # Source available in the toys-core gem end ## # Wraps an array of lines to the given width. # # @param strs [Array] Array of strings to wrap. # @param width [Integer,nil] Width in characters, or `nil` for infinite. # @param width2 [Integer,nil] Width in characters for the second and # subsequent lines, or `nil` to use the same as width. # # @return [Array] Wrapped lines # def self.wrap_lines(strs, width, width2 = nil) # Source available in the toys-core gem end ## # Make the given object a WrappableString. # If the object is already a WrappableString, return it. Otherwise, # treat it as a string or an array of strings and wrap it in a # WrappableString. # # @param obj [Toys::WrappableString,String,Array] # @return [Toys::WrappableString] # def self.make(obj) # Source available in the toys-core gem end ## # Make the given object an array of WrappableString. # # @param objs [Array>] # @return [Array] # def self.make_array(objs) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/context.rb0000644000004100000410000002664215164300675017526 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # This is the base class for tool execution. It represents `self` when your # tool's methods (such as `run`) are called, and it defines the methods that # can be called by your tool (such as {#logger} and {#exit}.) # # This class also manages the "data" available to your tool when it runs. # This data is a hash of key-value pairs. It consists of values set by flags # and arguments defined by the tool, plus some "well-known" values such as # the logger and verbosity level. # # You can obtain a value from the data using the {Toys::Context#get} method. # Additionally, convenience methods are provided for many of the well-known # keys. For instance, you can call {Toys::Context#verbosity} to obtain the # value for the key {Toys::Context::Key::VERBOSITY}. Finally, flags and # positional arguments that store their data here will also typically # generate convenience methods. For example, an argument with key `:abc` will # add a method called `abc` that you can call to get the value. # # By convention, flags and arguments defined by your tool should use strings # or symbols as keys. Keys that are not strings or symbols should either be # well-known keys such as {Toys::Context::Key::VERBOSITY}, or should be used # for internal private information needed by middleware and mixins. The # module {Toys::Context::Key} defines a number of well-known keys as # constants. # class Context ## # **_Defined in the toys-core gem_** # # Well-known context keys. # # This module is mixed into the runtime context. This means you can # reference any of these constants directly from your run method. # # ### Example # # tool "my-name" do # def run # # TOOL_NAME is available here. # puts "My name is #{get(TOOL_NAME)}" # end # end # module Key ## # Context key for the argument list passed to the current tool. Value is # an array of strings. # @return [Object] # ARGS = ::Object.new.freeze ## # Context key for the currently running {Toys::CLI}. You can use the # value to run other tools from your tool by calling {Toys::CLI#run}. # @return [Object] # CLI = ::Object.new.freeze ## # Context key for the context directory path. The value is a string # @return [Object] # CONTEXT_DIRECTORY = ::Object.new.freeze ## # Context key for the context from which the current call was delegated. # The value is either another context object, or `nil` if the current # call is not delegated. # @return [Object] # DELEGATED_FROM = ::Object.new.freeze ## # Context key for the active `Logger` object. # @return [Object] # LOGGER = ::Object.new.freeze ## # Context key for the {Toys::ToolDefinition} object being executed. # @return [Object] # TOOL = ::Object.new.freeze ## # Context key for the full name of the tool being executed. Value is an # array of strings. # @return [Object] # TOOL_NAME = ::Object.new.freeze ## # Context key for the {Toys::SourceInfo} describing the source of this # tool. # @return [Object] # TOOL_SOURCE = ::Object.new.freeze ## # Context key for all unmatched args in order. The value is an array of # strings. # @return [Object] # UNMATCHED_ARGS = ::Object.new.freeze ## # Context key for unmatched flags. The value is an array of strings. # @return [Object] # UNMATCHED_FLAGS = ::Object.new.freeze ## # Context key for unmatched positional args. The value is an array of # strings. # @return [Object] # UNMATCHED_POSITIONAL = ::Object.new.freeze ## # Context key for the list of usage errors raised. The value is an array # of {Toys::ArgParser::UsageError}. # @return [Object] # USAGE_ERRORS = ::Object.new.freeze ## # Context key for the verbosity value. The value is an integer defaulting # to 0, with higher values meaning more verbose and lower meaning more # quiet. # @return [Object] # VERBOSITY = ::Object.new.freeze end ## # The raw arguments passed to the tool, as an array of strings. # This does not include the tool name itself. # # This is a convenience getter for {Toys::Context::Key::ARGS}. # # If the `args` method is overridden by the tool, you can still access it # using the name `__args`. # # @return [Array] # def args # Source available in the toys-core gem end alias __args args ## # The currently running CLI. # # This is a convenience getter for {Toys::Context::Key::CLI}. # # If the `cli` method is overridden by the tool, you can still access it # using the name `__cli`. # # @return [Toys::CLI] # def cli # Source available in the toys-core gem end alias __cli cli ## # Return the context directory for this tool. Generally, this defaults # to the directory containing the toys config directory structure being # read, but it may be changed by setting a different context directory # for the tool. # # This is a convenience getter for {Toys::Context::Key::CONTEXT_DIRECTORY}. # # If the `context_directory` method is overridden by the tool, you can # still access it using the name `__context_directory`. # # @return [String] Context directory path # @return [nil] if there is no context. # def context_directory # Source available in the toys-core gem end alias __context_directory context_directory ## # The logger for this execution. # # This is a convenience getter for {Toys::Context::Key::LOGGER}. # # If the `logger` method is overridden by the tool, you can still access it # using the name `__logger`. # # @return [Logger] # def logger # Source available in the toys-core gem end alias __logger logger ## # The full name of the tool being executed, as an array of strings. # # This is a convenience getter for {Toys::Context::Key::TOOL_NAME}. # # If the `tool_name` method is overridden by the tool, you can still access # it using the name `__tool_name`. # # @return [Array] # def tool_name # Source available in the toys-core gem end alias __tool_name tool_name ## # The source of the tool being executed. # # This is a convenience getter for {Toys::Context::Key::TOOL_SOURCE}. # # If the `tool_source` method is overridden by the tool, you can still # access it using the name `__tool_source`. # # @return [Toys::SourceInfo] # def tool_source # Source available in the toys-core gem end alias __tool_source tool_source ## # The (possibly empty) array of errors detected during argument parsing. # # This is a convenience getter for {Toys::Context::Key::USAGE_ERRORS}. # # If the `usage_errors` method is overridden by the tool, you can still # access it using the name `__usage_errors`. # # @return [Array] # def usage_errors # Source available in the toys-core gem end alias __usage_errors usage_errors ## # The current verbosity setting as an integer. # # This is a convenience getter for {Toys::Context::Key::VERBOSITY}. # # If the `verbosity` method is overridden by the tool, you can still access # it using the name `__verbosity`. # # @return [Integer] # def verbosity # Source available in the toys-core gem end alias __verbosity verbosity ## # Fetch an option or other piece of data by key. # # If the `get` method is overridden by the tool, you can still access it # using the name `__get` or the `[]` operator. # # @param key [Symbol] # @return [Object] # def [](key) # Source available in the toys-core gem end alias get [] alias __get [] ## # Set an option or other piece of context data by key. # # @param key [Symbol] # @param value [Object] # def []=(key, value) # Source available in the toys-core gem end ## # Set one or more options or other context data by key. # # If the `set` method is overridden by the tool, you can still access it # using the name `__set`. # # @return [self] # # @overload set(key, value) # Set an option or other piece of context data by key. # @param key [Symbol] # @param value [Object] # @return [self] # # @overload set(hash) # Set multiple content data keys and values # @param hash [Hash] The keys and values to set # @return [self] # def set(key, value = nil) # Source available in the toys-core gem end alias __set set ## # The subset of the context that uses string or symbol keys. By convention, # this includes keys that are set by tool flags and arguments, but does not # include well-known context values such as verbosity or private context # values used by middleware or mixins. # # If the `options` method is overridden by the tool, you can still access # it using the name `__options`. # # @return [Hash] # def options # Source available in the toys-core gem end alias __options options ## # Find the given data file or directory in this tool's search path. # # If the `find_data` method is overridden by the tool, you can still access # it using the name `__find_data`. # # @param path [String] The path to find # @param type [nil,:file,:directory] Type of file system object to find, # or nil to return any type. # # @return [String] Absolute path of the result # @return [nil] if the data was not found. # def find_data(path, type: nil) # Source available in the toys-core gem end alias __find_data find_data ## # Exit immediately with the given status code. # # If the `exit` method is overridden by the tool, you can still access it # using the name `__exit` or by calling {Context.exit}. # # @param code [Integer] The status code, which should be 0 for no error, # or nonzero for an error condition. Default is 0. # @return [void] # def exit(code = 0) # Source available in the toys-core gem end alias __exit exit ## # Exit immediately with the given status code. This class method can be # called if the instance method is or could be replaced by the tool. # # @param code [Integer] The status code, which should be 0 for no error, # or nonzero for an error condition. Default is 0. # @return [void] # def self.exit(code = 0) # Source available in the toys-core gem end ## # Create a Context object. Applications generally will not need to create # these objects directly; they are created by the tool when it is preparing # for execution. # # @param data [Hash] # # @private This interface is internal and subject to change without warning. # def initialize(data) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/completion.rb0000644000004100000410000002447415164300675020214 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A Completion is a callable Proc that determines candidates for shell tab # completion. You pass a {Toys::Completion::Context} object (which includes # the current string fragment and other information) and it returns an array # of candidates, represented by {Toys::Completion::Candidate} objects, for # completing the fragment. # # A useful method here is the class method {Toys::Completion.create} which # takes a variety of inputs and returns a suitable completion Proc. # module Completion ## # **_Defined in the toys-core gem_** # # The context in which to determine completion candidates. # class Context ## # Create a completion context # # @param cli [Toys::CLI] The CLI being run. Required. # @param previous_words [Array] Array of complete strings that # appeared prior to the fragment to complete. # @param fragment_prefix [String] A prefix in the fragment that does not # participate in completion. (e.g. "key=") # @param fragment [String] The string fragment to complete. # @param params [Hash] Miscellaneous context data # def initialize(cli:, previous_words: [], fragment_prefix: "", fragment: "", **params) # Source available in the toys-core gem end ## # Create a new completion context with the given modifications. # # @param delta_params [Hash] Replace context data. # @return [Toys::Completion::Context] # def with(**delta_params) # Source available in the toys-core gem end ## # The CLI being run. # @return [Toys::CLI] # attr_reader :cli ## # All previous words. # @return [Array] # attr_reader :previous_words ## # A non-completed prefix for the current fragment. # @return [String] # attr_reader :fragment_prefix ## # The current string fragment to complete # @return [String] # attr_reader :fragment ## # Get data for arbitrary key. # @param [Symbol] key # @return [Object] # def [](key) # Source available in the toys-core gem end alias get [] ## # The tool being invoked, which should control the completion. # @return [Toys::ToolDefinition] # def tool # Source available in the toys-core gem end ## # An array of complete arguments passed to the tool, prior to the # fragment to complete. # @return [Array] # def args # Source available in the toys-core gem end ## # Current ArgParser indicating the status of argument parsing up to # this point. # # @return [Toys::ArgParser] # def arg_parser # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A candidate for completing a string fragment. # # A candidate includes a string representing the potential completed # word, as well as a flag indicating whether it is a *partial* completion # (i.e. a prefix that could still be added to) versus a *final* word. # Generally, tab completion systems should add a trailing space after a # final completion but not after a partial completion. # class Candidate include ::Comparable ## # Create a new candidate # @param string [String] The candidate string # @param partial [Boolean] Whether the candidate is partial. Defaults # to `false`. # def initialize(string, partial: false) # Source available in the toys-core gem end ## # Get the candidate string. # @return [String] # attr_reader :string alias to_s string ## # Determine whether the candidate is partial completion. # @return [Boolean] # def partial? # Source available in the toys-core gem end ## # Determine whether the candidate is a final completion. # @return [Boolean] # def final? # Source available in the toys-core gem end ## # Create an array of candidates given an array of strings. # # @param array [Array] # @return [Array] An array of candidates # def call(context) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A Completion that returns candidates from the local file system. # class FileSystem < Base ## # Create a completion that gets candidates from names in the local file # system. # # @param cwd [String] Working directory (defaults to the current dir). # @param omit_files [Boolean] Omit files from candidates # @param omit_directories [Boolean] Omit directories from candidates # @param prefix_constraint [String,Regexp] Constraint on the fragment # prefix. Defaults to requiring the prefix be empty. # def initialize(cwd: nil, omit_files: false, omit_directories: false, prefix_constraint: "") # Source available in the toys-core gem end ## # Whether files are included in the completion candidates. # @return [Boolean] # attr_reader :include_files ## # Whether directories are included in the completion candidates. # @return [Boolean] # attr_reader :include_directories ## # Constraint on the fragment prefix. # @return [String,Regexp] # attr_reader :prefix_constraint ## # Path to the starting directory. # @return [String] # attr_reader :cwd ## # Returns candidates for the current completion. # # @param context [Toys::Completion::Context] the current completion # context including the string fragment. # @return [Array] an array of candidates # def call(context) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A Completion whose candidates come from a static list of strings. # class Enum < Base ## # Create a completion from a list of values. # # @param values [Array] # @param prefix_constraint [String,Regexp] Constraint on the fragment # prefix. Defaults to requiring the prefix be empty. # def initialize(values, prefix_constraint: "") # Source available in the toys-core gem end ## # The array of completion candidates. # @return [Array] # attr_reader :values ## # Constraint on the fragment prefix. # @return [String,Regexp] # attr_reader :prefix_constraint ## # Returns candidates for the current completion. # # @param context [Toys::Completion::Context] the current completion # context including the string fragment. # @return [Array] an array of candidates # def call(context) # Source available in the toys-core gem end end ## # An instance of the empty completion that returns no candidates. # @return [Toys:::Completion::Base] # EMPTY = Base.new ## # Create a completion Proc from a variety of specification formats. The # completion is constructed from the given specification object and/or the # given block. Additionally, some completions can take a hash of options. # # Recognized specs include: # # * `:empty`: Returns the empty completion. Any block or options are # ignored. # # * `:file_system`: Returns a completion that searches the current # directory for file and directory names. You may also pass any of the # options recognized by {Toys::Completion::FileSystem#initialize}. The # block is ignored. # # * An **Array** of strings. Returns a completion that uses those values # as candidates. You may also pass any of the options recognized by # {Toys::Completion::Enum#initialize}. The block is ignored. # # * A **function**, either passed as a Proc (where the block is ignored) # or as a block (if the spec is nil). The function must behave as a # completion object, taking {Toys::Completion::Context} as the sole # argument, and returning an array of {Toys::Completion::Candidate}. # # * `:default` and `nil` indicate the **default completion**. For this # method, the default is the empty completion (i.e. these are synonyms # for `:empty`). However, other completion resolution methods might # have a different default. # # @param spec [Object] See the description for recognized values. # @param options [Hash] Additional options to pass to the completion. # @param block [Proc] See the description for recognized forms. # @return [Toys::Completion::Base,Proc] # def self.create(spec = nil, **options, &block) # Source available in the toys-core gem end ## # Take the various ways to express a completion spec, and convert them to a # canonical form expressed as a single object. This is called from the DSL # DSL to generate a spec object that can be stored. # # @private This interface is internal and subject to change without warning. # def self.scalarize_spec(spec, options, block) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/middleware.rb0000644000004100000410000002637015164300675020155 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A middleware is an object that has the opportunity to alter the # configuration and runtime behavior of each tool in a Toys CLI. A CLI # contains an ordered list of middleware, known as the *middleware stack*, # that together define the CLI's default behavior. # # Specifically, a middleware can perform two functions. # # First, it can modify the configuration of a tool. After tools are defined # from configuration, the middleware stack can make modifications to each # tool. A middleware can add flags and arguments to the tool, modify the # description, or make any other changes to how the tool is set up. # # Second, a middleware can intercept and change tool execution. Like a Rack # middleware, a Toys middleware can wrap execution with its own code, # replace it outright, or leave it unmodified. # # Generally, a middleware is a class that implements one or more of the # methods defined in this module: {Toys::Middleware#config}, and # {Toys::Middleware#run}. This module provides default implementations that # do nothing, but it is not required to include this module, or even to # define both methods. Middleware objects need respond only to methods they # care about. # module Middleware ## # This method is called *after* a tool has been defined, and gives this # middleware the opportunity to modify the tool definition. It is passed # the tool definition object and the loader, and can make any changes to # the tool definition. In most cases, this method should also call # `yield`, which passes control to the next middleware in the stack. A # middleware can disable modifications done by subsequent middleware by # omitting the `yield` call, but this is uncommon. # # This basic implementation does nothing and simply yields to the next # middleware. # # @param tool [Toys::ToolDefinition] The tool definition to modify. # @param loader [Toys::Loader] The loader that loaded this tool. # @return [void] # def config(tool, loader) # Source available in the toys-core gem end ## # This method is called when the tool is run. It gives the middleware an # opportunity to modify the runtime behavior of the tool. It is passed # the tool instance (i.e. the object that hosts a tool's `run` method), # and you can use this object to access the tool's options and other # context data. In most cases, this method should also call `yield`, # which passes control to the next middleware in the stack. A middleware # can "wrap" normal execution by calling `yield` somewhere in its # implementation of this method, or it can completely replace the # execution behavior by not calling `yield` at all. # # Like a tool's `run` method, this method's return value is unused. If # you want to output from a tool, write to stdout or stderr. If you want # to set the exit status code, call {Toys::Context#exit} on the context. # # This basic implementation does nothing and simply yields to the next # middleware. # # @param context [Toys::Context] The tool execution context. # @return [void] # def run(context) # Source available in the toys-core gem end class << self ## # Create a middleware spec. # # @overload spec(name, *args, **kwargs, &block) # Create a spec indicating a given middleware name should be # instantiated with the given arguments. # # @param name [String,Symbol,Class] The middleware name or class # @param args [Array] The arguments to pass to the constructor # @param kwargs [Hash] The keyword arguments to pass to the constructor # @param block [Proc,nil] The block to pass to the constructor # @return [Toys::Middleware::Spec] A spec # # @overload spec(array) # Create a middleware spec from an array specification. # # The array must be 1-4 elements long. The first element must be the # middleware name or class. The other three arguments may include any # or all of the following optional elements, in any order: # * An array for the positional arguments to pass to the constructor # * A hash for the keyword arguments to pass to the constructor # * A proc for the block to pass to the constructor # # @param array [Array] The array input # @return [Toys::Middleware::Spec] A spec # # @overload spec(middleware_object) # Create a spec wrapping an existing middleware object # # @param middleware_object [Toys::Middleware] The middleware object # @return [Toys::Middleware::Spec] A spec # def spec(middleware, *args, **kwargs, &block) # Source available in the toys-core gem end ## # Create a {Toys::Middleware::Stack} from an array of middleware specs. # Each element may be one of the following: # # * A {Toys::Middleware} object # * A {Toys::Middleware::Spec} # * An array whose first element is a middleware name or class, and the # subsequent elements are params that define what to pass to the class # constructor (see {Toys::Middleware.spec}) # # @param input [Array] # @return [Toys::Middleware::Stack] # def stack(input) # Source available in the toys-core gem end ## # Create a spec from an array specification. # # @private This interface is internal and subject to change without warning. # def spec_from_array(array) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A base class that provides default no-op implementation of the middleware # interface. This base class may optionally be subclassed by a middleware # implementation. # class Base include Middleware end ## # **_Defined in the toys-core gem_** # # A middleware specification, including the middleware class and the # arguments to pass to the constructor. # # Use {Toys::Middleware.spec} to create a middleware spec. # class Spec ## # Builds a middleware for this spec, given a ModuleLookup for middleware. # # If this spec wraps an existing middleware object, returns that object. # Otherwise, constructs a middleware object from the spec. # # @param lookup [Toys::ModuleLookup] A module lookup to resolve # middleware names # @return [Toys::Middleware] The middleware # def build(lookup) # Source available in the toys-core gem end ## # @return [Toys::Middleware] if this spec wraps a middleware object # @return [nil] if this spec represents a class to instantiate # attr_reader :object ## # @return [String,Symbol] if this spec represents a middleware name # @return [Class] if this spec represents a middleware class # @return [nil] if this spec wraps a middleware object # attr_reader :name ## # @return [Array] the positional arguments to be passed to a middleware # class constructor, or the empty array if there are no positional # arguments # @return [nil] if this spec wraps a middleware object # attr_reader :args ## # @return [Hash] the keyword arguments to be passed to a middleware class # constructor, or the empty hash if there are no keyword arguments # @return [nil] if this spec wraps a middleware object # attr_reader :kwargs ## # @return [Proc] if there is a block argument to be passed to a # middleware class constructor # @return [nil] if there is no block argument, or this spec wraps a # middleware object # attr_reader :block ## # Equality check # # @param other [Object] # @return [Boolean] # def ==(other) # Source available in the toys-core gem end alias eql? == ## # Return the hash code # # @return [Integer] # def hash # Source available in the toys-core gem end ## # Internal constructor. Use {Toys::Middleware.spec} instead. # # @private This interface is internal and subject to change without warning. # def initialize(object, name, args, kwargs, block) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A stack of middleware specs, which can be applied in order to a tool. # # A middleware stack is separated into three groups: # # * {#pre_specs}, which are applied first. # * {#default_specs}, which are applied next. The default specs are set # when the stack is created and are generally not modified. # * {#post_specs}, which are applied third. # # When adding middleware to a stack, you should normally add them to the # pre or post specs. By default, {Stack#add} appends to the pre specs, # inserting new middleware just before the defaults. # # Use {Toys::Middleware.stack} to create a middleware stack. # class Stack ## # The middleware specs that precede the default set. # @return [Array] # attr_reader :pre_specs ## # The default set of middleware specs. # @return [Array] # attr_reader :default_specs ## # The middleware specs that follow the default set. # @return [Array] # attr_reader :post_specs ## # Add a middleware spec to the stack, in the default location, which is # at the end of pre_specs). See {Toys::Middleware.spec} for a description # of the arguments you can pass. # # @overload add(name, *args, **kwargs, &block) # @overload add(array) # @overload add(middleware_object) # def add(middleware, *args, **kwargs, &block) # Source available in the toys-core gem end ## # Duplicate this stack. # # @return [Toys::Middleware::Stack] # def dup # Source available in the toys-core gem end ## # Build the middleware in this stack. # # @return [Array] # def build(middleware_lookup) # Source available in the toys-core gem end ## # Equality check # # @param other [Object] # @return [Boolean] # def ==(other) # Source available in the toys-core gem end alias eql? == ## # Return the hash code # # @return [Integer] # def hash # Source available in the toys-core gem end ## # Internal constructor. Use {Toys::Middleware.stack} instead. # # @private This interface is internal and subject to change without warning. # def initialize(default_specs: nil, pre_specs: nil, post_specs: nil) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/errors.rb0000644000004100000410000000630315164300675017346 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # An exception indicating an error in a tool definition. # class ToolDefinitionError < ::StandardError end ## # **_Defined in the toys-core gem_** # # An exception indicating that a tool has no run method. # class NotRunnableError < ::StandardError end ## # **_Defined in the toys-core gem_** # # An exception indicating problems parsing arguments. # class ArgParsingError < ::StandardError ## # Create an ArgParsingError given a set of error messages # @param errors [Array] # def initialize(errors) # Source available in the toys-core gem end ## # The individual usage error messages. # @return [Array] # attr_reader :usage_errors end ## # **_Defined in the toys-core gem_** # # An exception indicating a problem during tool lookup # class LoaderError < ::StandardError end ## # **_Defined in the toys-core gem_** # # A wrapper exception used to provide user-oriented context for an error # thrown during tool execution. # class ContextualError < ::StandardError ## # Construct a ContextualError. This exception type is thrown from # {ContextualError.capture} and {ContextualError.capture_path} and should # not be constructed directly. # # @private This interface is internal and subject to change without warning. # def initialize(underlying_error, banner, config_path: nil, config_line: nil, tool_name: nil, tool_args: nil) # Source available in the toys-core gem end ## # The underlying exception. # Generally the same as `Exception#cause`. # @return [::StandardError] # attr_reader :underlying_error ## # An overall banner message # @return [String] # attr_reader :banner ## # The path to the toys config file in which the error was detected # @return [String] # attr_reader :config_path ## # The line number in the toys config file in which the error was detected # @return [Integer] # attr_reader :config_line ## # The full name of the tool that was running when the error occurred # @return [Array] # attr_reader :tool_name ## # The arguments passed to the tool that was running when the error occurred # @return [Array] # attr_reader :tool_args class << self ## # Execute the given block, and wrap any exceptions thrown with a # ContextualError. This is intended for loading a config file from the # given path, and wraps any Ruby parsing errors. # # @private This interface is internal and subject to change without warning. # def capture_path(banner, path, **opts) # Source available in the toys-core gem end ## # Execute the given block, and wrap any exceptions thrown with a # ContextualError. # # @private This interface is internal and subject to change without warning. # def capture(banner, **opts) # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys/tool_definition.rb0000644000004100000410000011047715164300675021227 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A ToolDefinition describes a single command that can be invoked using Toys. # It has a name, a series of one or more words that you use to identify # the tool on the command line. It also has a set of formal flags and # command line arguments supported, and a block that gets run when the # tool is executed. # class ToolDefinition ## # **_Defined in the toys-core gem_** # # A Completion that implements the default algorithm for a tool. # class DefaultCompletion < Completion::Base ## # Create a completion given configuration options. # # @param complete_subtools [true,false] Whether to complete subtool names # @param include_hidden_subtools [true,false] Whether to include hidden # subtools (i.e. those beginning with an underscore) # @param complete_args [true,false] Whether to complete positional args # @param complete_flags [true,false] Whether to complete flag names # @param complete_flag_values [true,false] Whether to complete flag values # @param delegation_target [Array,nil] Delegation target, or # `nil` if none. # def initialize(complete_subtools: true, include_hidden_subtools: false, complete_args: true, complete_flags: true, complete_flag_values: true, delegation_target: nil) # Source available in the toys-core gem end ## # Whether to complete subtool names # @return [true,false] # def complete_subtools? # Source available in the toys-core gem end ## # Whether to include hidden subtools # @return [true,false] # def include_hidden_subtools? # Source available in the toys-core gem end ## # Whether to complete flags # @return [true,false] # def complete_flags? # Source available in the toys-core gem end ## # Whether to complete positional args # @return [true,false] # def complete_args? # Source available in the toys-core gem end ## # Whether to complete flag values # @return [true,false] # def complete_flag_values? # Source available in the toys-core gem end ## # Delegation target, or nil for none. # @return [Array] if there is a delegation target # @return [nil] if there is no delegation target # attr_accessor :delegation_target ## # Returns candidates for the current completion. # # @param context [Toys::Completion::Context] the current completion # context including the string fragment. # @return [Array] an array of candidates # def call(context) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # Tool-based settings class. # # The following settings are supported: # # * `propagate_helper_methods` (_boolean_) - Whether subtools should # inherit methods defined by parent tools. Defaults to `false`. # class Settings < ::Toys::Settings settings_attr :propagate_helper_methods, default: false end ## # Create a new tool. # Should be created only from the DSL via the Loader. # # @private This interface is internal and subject to change without warning. # def initialize(parent, full_name, priority, source_root, middleware_stack, middleware_lookup, tool_class = nil) # Source available in the toys-core gem end ## # Reset the definition of this tool, deleting all definition data but # leaving named acceptors, mixins, and templates intact. # Should be called only from the DSL. # # @private This interface is internal and subject to change without warning. # def reset_definition # Source available in the toys-core gem end ## # Settings for this tool # # @return [Toys::ToolDefinition::Settings] # attr_reader :settings ## # The name of the tool as an array of strings. # This array may not be modified. # # @return [Array] # attr_reader :full_name ## # The priority of this tool definition. # # @return [Integer] # attr_reader :priority ## # The root source info defining this tool, or nil if there is no source. # # @return [Toys::SourceInfo,nil] # attr_reader :source_root ## # The tool class. # # @return [Class] # attr_reader :tool_class ## # The short description string. # # When reading, this is always returned as a {Toys::WrappableString}. # # When setting, the description may be provided as any of the following: # * A {Toys::WrappableString}. # * A normal String, which will be transformed into a # {Toys::WrappableString} using spaces as word delimiters. # * An Array of String, which will be transformed into a # {Toys::WrappableString} where each array element represents an # individual word for wrapping. # # @return [Toys::WrappableString] # attr_reader :desc ## # The long description strings. # # When reading, this is returned as an Array of {Toys::WrappableString} # representing the lines in the description. # # When setting, the description must be provided as an Array where *each # element* may be any of the following: # * A {Toys::WrappableString} representing one line. # * A normal String representing a line. This will be transformed into a # {Toys::WrappableString} using spaces as word delimiters. # * An Array of String representing a line. This will be transformed into # a {Toys::WrappableString} where each array element represents an # individual word for wrapping. # # @return [Array] # attr_reader :long_desc ## # A list of all defined flag groups, in order. # # @return [Array] # attr_reader :flag_groups ## # A list of all defined flags. # # @return [Array] # attr_reader :flags ## # A list of all defined required positional arguments. # # @return [Array] # attr_reader :required_args ## # A list of all defined optional positional arguments. # # @return [Array] # attr_reader :optional_args ## # The remaining arguments specification. # # @return [Toys::PositionalArg] The argument definition # @return [nil] if remaining arguments are not supported by this tool. # attr_reader :remaining_arg ## # A list of flags that have been used in the flag definitions. # # @return [Array] # attr_reader :used_flags ## # The default context data set by arguments. # # @return [Hash] # attr_reader :default_data ## # The stack of middleware specs used for subtools. # # This array may be modified in place. # # @return [Array] # attr_reader :subtool_middleware_stack ## # The stack of built middleware specs for this tool. # # @return [Array] # attr_reader :built_middleware ## # Info on the source of this tool. # # @return [Toys::SourceInfo] The source info # @return [nil] if the source is not defined. # attr_reader :source_info ## # The custom context directory set for this tool. # # @return [String] The directory path # @return [nil] if no custom context directory is set. # attr_reader :custom_context_directory ## # The completion strategy for this tool. # # When reading, this may return an instance of one of the subclasses of # {Toys::Completion::Base}, or a Proc that duck-types it. Generally, this # defaults to a {Toys::ToolDefinition::DefaultCompletion}, providing a # standard algorithm that finds appropriate completions from flags, # positional arguments, and subtools. # # When setting, you may pass any of the following: # * `nil` or `:default` which sets the value to a default instance. # * A Hash of options to pass to the # {Toys::ToolDefinition::DefaultCompletion} constructor. # * Any other form recognized by {Toys::Completion.create}. # # @return [Toys::Completion::Base,Proc] # attr_reader :completion ## # The run handler. # # This handler is called to run the tool. Normally it is a method name, # represented by a symbol. (The default is `:run`.) It can be set to a # different method name, or to a proc that will be called with `self` set # to the tool context. Either way, it takes no arguments. The run handler # can also be explicitly set to `nil` indicating a non-runnable tool; # however, typically a tool is made non-runnable simply by leaving the run # handler set to `:run` and not defining the method. # # @return [Proc] if the run handler is defined as a Proc # @return [Symbol] if the run handler is defined as a method # @return [nil] if the tool is explicitly made non-runnable # attr_reader :run_handler ## # The usage error handler. # # This handler is called when at least one usage error is detected during # argument parsing, and is called instead of the `run` method. It can be # specified as a Proc, or a Symbol indicating a method to call. It # optionally takes an array of {Toys::ArgParser::UsageError} as the sole # argument. # # @return [Proc] if the usage error handler is defined as a Proc # @return [Symbol] if the user error handler is defined as a method # @return [nil] if there is no usage error handler # attr_reader :usage_error_handler ## # The full name of the delegate target, if any. # # @return [Array] if this tool delegates # @return [nil] if this tool does not delegate # attr_reader :delegate_target ## # The local name of this tool, i.e. the last element of the full name. # # @return [String] # def simple_name # Source available in the toys-core gem end ## # A displayable name of this tool, generally the full name delimited by # spaces. # # @return [String] # def display_name # Source available in the toys-core gem end ## # Return the signal handler for the given signal. # # This handler is called when the given signal is received, immediately # taking over the execution as if it were the new run handler. The signal # handler can be specified as a Proc, or a Symbol indicating a method to # call. It optionally takes the `SignalException` as the sole argument. # # @param signal [Integer,String,Symbol] The signal number or name # @return [Proc] if the signal handler is defined as a Proc # @return [Symbol] if the signal handler is defined as a method # @return [nil] if there is no handler for the given signal # def signal_handler(signal) # Source available in the toys-core gem end ## # Return the interrupt handler. This is equivalent to `signal_handler(2)`. # # @return [Proc] if the interrupt signal handler is defined as a Proc # @return [Symbol] if the interrupt signal handler is defined as a method # @return [nil] if there is no handler for the interrupt signals # def interrupt_handler # Source available in the toys-core gem end ## # Returns true if this tool is a root tool. # @return [true,false] # def root? # Source available in the toys-core gem end ## # Returns true if this tool is marked as runnable. # @return [true,false] # def runnable? # Source available in the toys-core gem end ## # Returns true if this tool handles interrupts. This is equivalent to # `handles_signal?(2)`. # # @return [true,false] # def handles_interrupts? # Source available in the toys-core gem end ## # Returns true if this tool handles the given signal. # # @param signal [Integer,String,Symbol] The signal number or name # @return [true,false] # def handles_signal?(signal) # Source available in the toys-core gem end ## # Returns true if this tool handles usage errors. # @return [true,false] # def handles_usage_errors? # Source available in the toys-core gem end ## # Returns true if this tool has at least one included module. # @return [true,false] # def includes_modules? # Source available in the toys-core gem end ## # Returns true if there is a specific description set for this tool. # @return [true,false] # def includes_description? # Source available in the toys-core gem end ## # Returns true if at least one flag or positional argument is defined # for this tool. # @return [true,false] # def includes_arguments? # Source available in the toys-core gem end ## # Returns true if this tool has any definition information. # @return [true,false] # def includes_definition? # Source available in the toys-core gem end ## # Returns true if this tool's definition has been finished and is locked. # @return [true,false] # def definition_finished? # Source available in the toys-core gem end ## # Returns true if this tool has disabled argument parsing. # @return [true,false] # def argument_parsing_disabled? # Source available in the toys-core gem end ## # Returns true if this tool enforces flags before args. # @return [true,false] # def flags_before_args_enforced? # Source available in the toys-core gem end ## # Returns true if this tool requires exact flag matches. # @return [true,false] # def exact_flag_match_required? # Source available in the toys-core gem end ## # All arg definitions in order: required, optional, remaining. # # @return [Array] # def positional_args # Source available in the toys-core gem end ## # Resolve the given flag given the flag string. Returns an object that # describes the resolution result, including whether the resolution # matched a unique flag, the specific flag syntax that was matched, and # additional information. # # @param str [String] Flag string # @return [Toys::Flag::Resolution] # def resolve_flag(str) # Source available in the toys-core gem end ## # Get the named acceptor from this tool or its ancestors. # # @param name [String] The acceptor name. # @return [Toys::Acceptor::Base] The acceptor. # @return [nil] if no acceptor of the given name is found. # def lookup_acceptor(name) # Source available in the toys-core gem end ## # Get the named template from this tool or its ancestors. # # @param name [String] The template name. # @return [Class,nil] The template class. # @return [nil] if no template of the given name is found. # def lookup_template(name) # Source available in the toys-core gem end ## # Get the named mixin from this tool or its ancestors. # # @param name [String] The mixin name. # @return [Module] The mixin module. # @return [nil] if no mixin of the given name is found. # def lookup_mixin(name) # Source available in the toys-core gem end ## # Get the named completion from this tool or its ancestors. # # @param name [String] The completion name # @return [Toys::Completion::Base,Proc] The completion proc. # @return [nil] if no completion of the given name is found. # def lookup_completion(name) # Source available in the toys-core gem end ## # Include the given mixin in the tool class. # # The mixin must be given as a module. You can use {#lookup_mixin} to # resolve named mixins. # # @param mod [Module] The mixin module # @return [self] # def include_mixin(mod, *args, **kwargs) # Source available in the toys-core gem end ## # Sets the path to the file that defines this tool. # A tool may be defined from at most one path. If a different path is # already set, it is left unchanged. # # @param source [Toys::SourceInfo] Source info # @return [self] # def lock_source(source) # Source available in the toys-core gem end ## # Set the short description string. # # See {#desc} for details. # # @param desc [Toys::WrappableString,String,Array] # def desc=(desc) # Source available in the toys-core gem end ## # Set the long description strings. # # See {#long_desc} for details. # # @param long_desc [Array>] # def long_desc=(long_desc) # Source available in the toys-core gem end ## # Append long description strings. # # You must pass an array of lines in the long description. See {#long_desc} # for details on how each line may be represented. # # @param long_desc [Array>] # @return [self] # def append_long_desc(long_desc) # Source available in the toys-core gem end ## # Add a named acceptor to the tool. This acceptor may be refereneced by # name when adding a flag or an arg. See {Toys::Acceptor.create} for # detailed information on how to specify an acceptor. # # @param name [String] The name of the acceptor. # @param acceptor [Toys::Acceptor::Base,Object] The acceptor to add. You # can provide either an acceptor object, or a spec understood by # {Toys::Acceptor.create}. # @param type_desc [String] Type description string, shown in help. # Defaults to the acceptor name. # @param block [Proc] Optional block used to create an acceptor. See # {Toys::Acceptor.create}. # @return [self] # def add_acceptor(name, acceptor = nil, type_desc: nil, &block) # Source available in the toys-core gem end ## # Add a named mixin module to this tool. # You may provide a mixin module or a block that configures one. # # @param name [String] The name of the mixin. # @param mixin_module [Module] The mixin module. # @param block [Proc] Define the mixin module here if a `mixin_module` is # not provided directly. # @return [self] # def add_mixin(name, mixin_module = nil, &block) # Source available in the toys-core gem end ## # Add a named completion proc to this tool. The completion may be # referenced by name when adding a flag or an arg. See # {Toys::Completion.create} for detailed information on how to specify a # completion. # # @param name [String] The name of the completion. # @param completion [Proc,Toys::Completion::Base,Object] The completion to # add. You can provide either a completion object, or a spec understood # by {Toys::Completion.create}. # @param options [Hash] Additional options to pass to the completion. # @param block [Proc] Optional block used to create a completion. See # {Toys::Completion.create}. # @return [self] # def add_completion(name, completion = nil, **options, &block) # Source available in the toys-core gem end ## # Add a named template class to this tool. # You may provide a template class or a block that configures one. # # @param name [String] The name of the template. # @param template_class [Class] The template class. # @param block [Proc] Define the template class here if a `template_class` # is not provided directly. # @return [self] # def add_template(name, template_class = nil, &block) # Source available in the toys-core gem end ## # Disable argument parsing for this tool. # # @return [self] # def disable_argument_parsing # Source available in the toys-core gem end ## # Enforce that flags must come before args for this tool. # You may disable enforcement by passoing `false` for the state. # # @param state [true,false] # @return [self] # def enforce_flags_before_args(state = true) # Source available in the toys-core gem end ## # Require that flags must match exactly. (If false, flags can match an # unambiguous substring.) # # @param state [true,false] # @return [self] # def require_exact_flag_match(state = true) # Source available in the toys-core gem end ## # Add a flag group to the group list. # # The type should be one of the following symbols: # * `:optional` All flags in the group are optional # * `:required` All flags in the group are required # * `:exactly_one` Exactly one flag in the group must be provided # * `:at_least_one` At least one flag in the group must be provided # * `:at_most_one` At most one flag in the group must be provided # # @param type [Symbol] The type of group. Default is `:optional`. # @param desc [String,Array,Toys::WrappableString] Short # description for the group. See {Toys::ToolDefinition#desc} for a # description of allowed formats. Defaults to `"Flags"`. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag group. See # {Toys::ToolDefinition#long_desc} for a description of allowed # formats. Defaults to the empty array. # @param name [String,Symbol,nil] The name of the group, or nil for no # name. # @param report_collisions [true,false] If `true`, raise an exception if a # the given name is already taken. If `false`, ignore. Default is # `true`. # @param prepend [true,false] If `true`, prepend rather than append the # group to the list. Default is `false`. # @return [self] # def add_flag_group(type: :optional, desc: nil, long_desc: nil, name: nil, report_collisions: true, prepend: false) # Source available in the toys-core gem end ## # 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. # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param flags [Array] The flags in OptionParser format. If empty, # a flag will be inferred from the key. # @param accept [Object] 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. # @param default [Object] 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`. # @param handler [Proc,nil,:set,:push] An optional handler that customizes # how a value is set or updated. A handler is a proc that takes up to # three arguments: the given value, the previous value, and a hash # containing all the data collected so far during argument parsing. It # must return the new value that should be set. You may also specify a # predefined named handler. The `:set` handler (the default) replaces # the previous value (effectively `-> (val) { val }`). The `:push` # handler expects the previous value to be an array and pushes the # given value onto it; it should be combined with setting `default: []` # and is intended for "multi-valued" flags. # @param complete_flags [Object] A specifier for shell tab completion # for flag names associated with this flag. By default, a # {Toys::Flag::DefaultCompletion} is used, which provides the flag's # names as completion candidates. To customize completion, set this to # a hash of options to pass to the constructor for # {Toys::Flag::DefaultCompletion}, or pass any other spec recognized # by {Toys::Completion.create}. # @param complete_values [Object] A specifier for shell tab completion # for flag values associated with this flag. Pass any spec # recognized by {Toys::Completion.create}. # @param report_collisions [true,false] Raise an exception if a flag is # requested that is already in use or marked as disabled. Default is # true. # @param group [Toys::FlagGroup,String,Symbol,nil] Group for # this flag. You may provide a group name, a FlagGroup object, or # `nil` which denotes the default group. # @param desc [String,Array,Toys::WrappableString] Short # description for the flag. See {Toys::ToolDefinition#desc} for a # description of allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the flag. See {Toys::ToolDefinition#long_desc} # for a description of allowed formats. Defaults to the empty array. # @param display_name [String] A display name for this flag, used in help # text and error messages. # @return [self] # def add_flag(key, flags = [], accept: nil, default: nil, handler: nil, complete_flags: nil, complete_values: nil, report_collisions: true, group: nil, desc: nil, long_desc: nil, display_name: nil) # Source available in the toys-core gem end ## # Mark one or more flags as disabled, preventing their use by any # subsequent flag definition. This may be used to prevent middleware from # defining a particular flag. # # @param flags [String...] The flags to disable # @return [self] # def disable_flag(*flags) # Source available in the toys-core gem end ## # 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. # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param accept [Object] 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. # @param complete [Object] A specifier for shell tab completion. See # {Toys::Completion.create} for recognized formats. # @param display_name [String] A name to use for display (in help text and # error reports). Defaults to the key in upper case. # @param desc [String,Array,Toys::WrappableString] Short # description for the arg. See {Toys::ToolDefinition#desc} for a # description of allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the arg. See {Toys::ToolDefinition#long_desc} # for a description of allowed formats. Defaults to the empty array. # @return [self] # def add_required_arg(key, accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil) # Source available in the toys-core gem end ## # 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. # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param default [Object] 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`. # @param accept [Object] 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. # @param complete [Object] A specifier for shell tab completion. See # {Toys::Completion.create} for recognized formats. # @param display_name [String] A name to use for display (in help text and # error reports). Defaults to the key in upper case. # @param desc [String,Array,Toys::WrappableString] Short # description for the arg. See {Toys::ToolDefinition#desc} for a # description of allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the arg. See {Toys::ToolDefinition#long_desc} # for a description of allowed formats. Defaults to the empty array. # @return [self] # def add_optional_arg(key, default: nil, accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil) # Source available in the toys-core gem end ## # 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. # # @param key [String,Symbol] The key to use to retrieve the value from # the execution context. # @param default [Object] 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 `[]`. # @param accept [Object] 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. # @param complete [Object] A specifier for shell tab completion. See # {Toys::Completion.create} for recognized formats. # @param display_name [String] A name to use for display (in help text and # error reports). Defaults to the key in upper case. # @param desc [String,Array,Toys::WrappableString] Short # description for the arg. See {Toys::ToolDefinition#desc} for a # description of allowed formats. Defaults to the empty string. # @param long_desc [Array,Toys::WrappableString>] # Long description for the arg. See {Toys::ToolDefinition#long_desc} # for a description of allowed formats. Defaults to the empty array. # @return [self] # def set_remaining_args(key, default: [], accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil) # Source available in the toys-core gem end ## # Set the run handler. # # This handler is called to run the tool. Normally it is a method name, # represented by a symbol. (The default is `:run`.) It can be set to a # different method name, or to a proc that will be called with `self` set # to the tool context. Either way, it takes no arguments. The run handler # can also be explicitly set to `nil` indicating a non-runnable tool; # however, typically a tool is made non-runnable simply by leaving the run # handler set to `:run` and not defining the method. # # @param handler [Proc,Symbol,nil] the run handler # def run_handler=(handler) # Source available in the toys-core gem end ## # Set the interrupt handler. This is equivalent to calling # {#set_signal_handler} for the `SIGINT` signal. # # @param handler [Proc,Symbol] The interrupt signal handler # def interrupt_handler=(handler) # Source available in the toys-core gem end ## # Set the handler for the given signal. # # This handler is called when the given signal is received, immediately # taking over the execution as if it were the new `run` method. The signal # handler can be specified as a Proc, or a Symbol indicating a method to # call. It optionally takes the `SignalException` as the sole argument. # # @param signal [Integer,String,Symbol] The signal number or name # @param handler [Proc,Symbol] The signal handler # def set_signal_handler(signal, handler) # Source available in the toys-core gem end ## # Set the usage error handler. # # This handler is called when at least one usage error is detected during # argument parsing, and is called instead of the `run` method. It can be # specified as a Proc, or a Symbol indicating a method to call. It # optionally takes an array of {Toys::ArgParser::UsageError} as the sole # argument. # # @param handler [Proc,Symbol] The usage error handler # def usage_error_handler=(handler) # Source available in the toys-core gem end ## # Add an initializer. # # @param proc [Proc] The initializer block # @param args [Object...] Arguments to pass to the initializer # @param kwargs [keywords] Keyword arguments to pass to the initializer # @return [self] # def add_initializer(proc, *args, **kwargs) # Source available in the toys-core gem end ## # Set the custom context directory. # # See {#custom_context_directory} for details. # # @param dir [String] # def custom_context_directory=(dir) # Source available in the toys-core gem end ## # Set the completion strategy for this ToolDefinition. # # See {#completion} for details. # # @param spec [Object] # def completion=(spec) # Source available in the toys-core gem end ## # Return the effective context directory. # If there is a custom context directory, uses that. Otherwise, looks for # a custom context directory up the tool ancestor chain. If none is # found, uses the default context directory from the source info. It is # possible for there to be no context directory at all, in which case, # returns nil. # # @return [String] The effective context directory path. # @return [nil] if there is no effective context directory. # def context_directory # Source available in the toys-core gem end ## # Causes this tool to delegate to another tool. # # @param target [Array] The full path to the delegate tool. # @return [self] # def delegate_to(target) # Source available in the toys-core gem end ## # Lookup the custom context directory in this tool and its ancestors. # # @private This interface is internal and subject to change without warning. # def lookup_custom_context_directory # Source available in the toys-core gem end ## # Mark this tool as having at least one module included. # # @private This interface is internal and subject to change without warning. # def mark_includes_modules # Source available in the toys-core gem end ## # Complete definition and run middleware configs. Should be called from # the Loader only. # # @private This interface is internal and subject to change without warning. # def finish_definition(loader) # Source available in the toys-core gem end ## # Run all initializers against a context. Called from the Runner. # # @private This interface is internal and subject to change without warning. # def run_initializers(context) # Source available in the toys-core gem end ## # Check that the tool can still be defined. Should be called internally # or from the DSL only. # # @private This interface is internal and subject to change without warning. # def check_definition_state(is_arg: false, is_method: false) # Source available in the toys-core gem end end end toys-0.21.0/core-docs/toys/template.rb0000644000004100000410000000755015164300675017652 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A template definition. Template classes should include this module. # # A template is a configurable set of DSL code that can be run in a toys # configuration to automate tool defintion. For example, toys provides a # "minitest" template that generates a "test" tool that invokes minitest. # Templates will often support configuration; for example the minitest # template lets you configure the paths to the test files. # # ### Usage # # To create a template, define a class and include this module. # The class defines the "configuration" of the template. If your template # has options/parameters, you should provide a constructor, and methods # appropriate to edit those options. The arguments given to the # {Toys::DSL::Tool#expand} method are passed to your constructor, and your # template object is passed to any block given to {Toys::DSL::Tool#expand}. # # Next, in your template class, call the `on_expand` method, which is defined # in {Toys::Template::ClassMethods#on_expand}. Pass this a block which # defines the implementation of the template. Effectively, the contents of # this block are "inserted" into the user's configuration. The template # object is passed to the block so you have access to the template options. # # ### Example # # This is a simple template that generates a "hello" tool. The tool simply # prints a `"Hello, #{name}!"` greeting. The name is set as a template # option; it is defined when the template is expanded in a toys # configuration. # # # Define a template by creating a class that includes Toys::Template. # class MyHelloTemplate # include Toys::Template # # # A user of the template may pass an optional name as a parameter to # # `expand`, or leave it as the default of "world". # def initialize(name: "world") # @name = name # end # # # The template is passed to the expand block, so a user of the # # template may also call this method to set the name. # attr_accessor :name # # # The following block is inserted when the template is expanded. # on_expand do |template| # desc "Prints a greeting to #{template.name}" # tool "templated-greeting" do # to_run do # puts "Hello, #{template.name}!" # end # end # end # end # # Now you can use the template in your `.toys.rb` file like this: # # expand(MyHelloTemplate, name: "rubyists") # # or alternately: # # expand(MyHelloTemplate) do |template| # template.name = "rubyists" # end # # And it will create a tool called "templated-greeting". # module Template ## # Create a template class with the given block. # # @param block [Proc] Defines the template class. # @return [Class] # def self.create(&block) # Source available in the toys-core gem end ## # **_Defined in the toys-core gem_** # # Class methods that will be added to a template class. # module ClassMethods ## # Define how to expand this template. The given block is passed the # template object, and is evaluated in the tool class. It should invoke # directives to create tools and other objects. # # @param block [Proc] The expansion of this template. # @return [self] # def on_expand(&block) # Source available in the toys-core gem end alias to_expand on_expand ## # The template expansion proc. This proc is passed the template object, # and is evaluted in the tool class. It should invoke directives to # create tools and other objects. # # @return [Proc] The expansion of this template. # attr_accessor :expansion end end end toys-0.21.0/core-docs/toys/settings.rb0000644000004100000410000005023315164300675017673 0ustar www-datawww-datamodule Toys ## # **_Defined in the toys-core gem_** # # A settings class defines the structure of application settings, i.e. the # various fields that can be set, and their types. You can define a settings # structure by subclassing this base class, and using the provided methods. # # ### Attributes # # To define an attribute, use the {Settings.settings_attr} declaration. # # Example: # # class ServiceSettings < Toys::Settings # settings_attr :endpoint, default: "api.example.com" # end # # my_settings = ServiceSettings.new # my_settings.endpoint_set? # => false # my_settings.endpoint # => "api.example.com" # my_settings.endpoint = "rest.example.com" # my_settings.endpoint_set? # => true # my_settings.endpoint # => "rest.example.com" # my_settings.endpoint_unset! # my_settings.endpoint_set? # => false # my_settings.endpoint # => "api.example.com" # # An attribute has a name, a default value, and a type specification. The # name is used to define methods for getting and setting the attribute. The # default is returned if no value is set. (See the section below on parents # and defaults for more information.) The type specification governs what # values are allowed. (See the section below on type specifications.) # # Attribute names must start with an ascii letter, and may contain only ascii # letters, digits, and underscores. Unlike method names, they may not include # non-ascii unicode characters, nor may they end with `!` or `?`. # Additionally, some names are reserved because they would shadow critical # Ruby methods or interfere with Settings' internal behavior (see # {Toys::Settings::RESERVED_FIELD_NAMES} for the complete list, which # currently includes names such as `class`, `clone`, `dup`, `freeze`, `hash`, # `initialize`, `method_missing`, `object_id`, `public_send`, `raise`, # `require`, and `send`). # # Each attribute defines four methods: a getter, a setter, an unsetter, and a # set detector. In the above example, the attribute named `:endpoint` creates # the following four methods: # # * `endpoint` - retrieves the attribute value, or a default if not set. # * `endpoint=(value)` - sets a new attribute value. # * `endpoint_unset!` - unsets the attribute, reverting to a default. # * `endpoint_set?` - returns a boolean, whether the attribute is set. # # ### Groups # # A group is a settings field that itself is a Settings object. You can use # it to group settings fields in a hierarchy. # # Example: # # class ServiceSettings < Toys::Settings # settings_attr :endpoint, default: "api.example.com" # settings_group :service_flags do # settings_attr :verbose, default: false # settings_attr :use_proxy, default: false # end # end # # my_settings = ServiceSettings.new # my_settings.service_flags.verbose # => false # my_settings.service_flags.verbose = true # my_settings.service_flags.verbose # => true # my_settings.endpoint # => "api.example.com" # # You can define a group inline, as in the example above, or create an # explicit settings class and use it for the group. For example: # # class Flags < Toys::Settings # settings_attr :verbose, default: false # settings_attr :use_proxy, default: false # end # class ServiceSettings < Toys::Settings # settings_attr :endpoint, default: "api.example.com" # settings_group :service_flags, Flags # end # # my_settings = ServiceSettings.new # my_settings.service_flags.verbose = true # # If the module enclosing a subclass of `Settings` is itself a subclass of # `Settings`, then the class is automatically added to its enclosing class as # a group. For example: # # class ServiceSettings < Toys::Settings # settings_attr :endpoint, default: "api.example.com" # # Automatically adds this as the group service_flags. # # The name is inferred (snake_cased) from the class name. # class ServiceFlags < Toys::Settings # settings_attr :verbose, default: false # settings_attr :use_proxy, default: false # end # end # # my_settings = ServiceSettings.new # my_settings.service_flags.verbose = true # # ### Type specifications # # A type specification is a restriction on the types of values allowed for a # settings field. Every attribute has a type specification. You can set it # explicitly by providing a `:type` argument or a block. If a type # specification is not provided explicitly, it is inferred from the default # value of the attribute. # # Type specifications can be any of the following: # # * A Module, restricting values to those that include the module. # # For example, a type specification of `Enumerable` would accept `[123]` # but not `123`. # # * A Class, restricting values to that class or any subclass. # # For example, a type specification of `Time` would accept `Time.now` but # not `DateTime.now`. # # Note that some classes will convert (i.e. parse) strings. For example, # a type specification of `Integer` will accept the string `"-123"`` and # convert it to the value `-123`. Classes that support parsing include: # # * `Date` # * `DateTime` # * `Float` # * `Integer` # * `Regexp` # * `Symbol` # * `Time` # # * A Regexp, restricting values to strings matching the regexp. # # For example, a type specification of `/^\w+$/` would match `"abc"` but # not `"abc!"`. # # * A Range, restricting values to objects that fall in the range and are # of the same class (or a subclass) as the endpoints. String values are # accepted if they can be converted to the endpoint class as specified by # a class type specification. # # For example, a type specification of `(1..5)` would match `5` but not # `6`. It would also match `"5"` because the String can be parsed into an # Integer in the range. # # * A specific value, any Symbol, String, Numeric, or the values `nil`, # `true`, or `false`, restricting the value to only that given value. # # For example, a type specification of `:foo` would match `:foo` but not # `:bar`. # # (It might not seem terribly useful to have an attribute that can take # only one value, but this type is generally used as part of a union # type, described below, to implement an enumeration.) # # * An Array representing a union type, each of whose elements is one of # the above types. Values are accepted if they match any of the elements. # # For example, a type specification of `[:a, :b :c]` would match `:a` but # not `"a"`. Similarly, a type specification of `[String, Integer, nil]` # would match `"hello"`, `123`, or `nil`, but not `123.4`. # # * A Proc that takes the proposed value and returns either the value if it # is legal, the converted value if it can be converted to a legal value, # or the constant {Toys::Settings::ILLEGAL_VALUE} if it cannot be # converted to a legal value. You may also pass a block to # `settings_attr` to set a Proc type specification. # # * A {Toys::Settings::Type} that checks and converts values. # # If you do not explicitly provide a type specification, one is inferred from # the attribute's default value. The rules are: # # * If the default value is `true` or `false`, then the type specification # inferred is `[true, false]`. # # * If the default value is `nil` or not provided, then the type # specification allows any object (i.e. is equivalent to `Object`). # # * Otherwise, the type specification allows any value of the same class as # the default value. For example, if the default value is `""`, the # effective type specification is `String`. # # Examples: # # class ServiceSettings < Toys::Settings # # Allows only strings because the default is a string. # settings_attr :endpoint, default: "example.com" # end # # class ServiceSettings < Toys::Settings # # Allows strings or nil. # settings_attr :endpoint, default: "example.com", type: [String, nil] # end # # class ServiceSettings < Toys::Settings # # Raises ArgumentError because the default is nil, which does not # # match the type specification. (You should either allow nil # # explicitly with `type: [String, nil]` or set the default to a # # suitable string such as the empty string "".) # settings_attr :endpoint, type: String # end # # ### Settings parents # # A settings object can have a "parent" which provides the values if they are # not set in the settings object. This lets you organize settings as # "defaults" and "overrides". A parent settings object provides the defaults, # and a child can selectively override certain values. # # To set the parent for a settings object, pass it as the argument to the # Settings constructor. When a field in a settings object is queried, it # looks up the value as follows: # # * If a field value is explicitly set in the settings object, that value # is returned. # * If the field is not set in the settings object, but the settings object # has a parent, the parent is queried. If that parent also does not have # a value for the field, it may query its parent in turn, and so forth. # * If we encounter a root settings with no parent, and still no value is # set for the field, the default for the *original* setting is returned. # # Example: # # class MySettings < Toys::Settings # settings_attr :str, default: "default" # end # # root_settings = MySettings.new # child_settings = MySettings.new(root_settings) # child_settings.str # => "default" # root_settings.str = "value_from_root" # child_settings.str # => "value_from_root" # child_settings.str = "value_from_child" # child_settings.str # => "value_from_child" # child_settings.str_unset! # child_settings.str # => "value_from_root" # root_settings.str_unset! # child_settings.str # => "default" # # Parents are honored through groups as well. For example: # # class MySettings < Toys::Settings # settings_group :flags do # settings_attr :verbose, default: false # settings_attr :force, default: false # end # end # # root_settings = MySettings.new # child_settings = MySettings.new(root_settings) # child_settings.flags.verbose # => false # root_settings.flags.verbose = true # child_settings.flags.verbose # => true # # Usually, a settings and its parent (and its parent, and so forth) should # have the same class. This guarantees that they define the same fields with # the same type specifications. However, this is not required. If a parent # does not define a particular field, it is treated as if that field is # unset, and lookup proceeds to its parent. To illustrate: # # class Settings1 < Toys::Settings # settings_attr :str, default: "default" # end # class Settings2 < Toys::Settings # end # # root_settings = Settings1.new # child_settings = Settings2.new(root_settings) # does not have str # grandchild_settings = Settings1.new(child_settings) # # grandchild_settings.str # => "default" # root_settings.str = "value_from_root" # grandchild_settings.str # => "value_from_root" # # Type specifications are enforced when falling back to parent values. If a # parent provides a value that is not allowed, it is treated as if the field # is unset, and lookup proceeds to its parent. # # class Settings1 < Toys::Settings # settings_attr :str, default: "default" # type spec is String # end # class Settings2 < Toys::Settings # settings_attr :str, default: 0 # type spec is Integer # end # # root_settings = Settings1.new # child_settings = Settings2.new(root_settings) # grandchild_settings = Settings1.new(child_settings) # # grandchild_settings.str # => "default" # child_settings.str = 123 # does not match grandchild's type # root_settings.str = "value_from_root" # grandchild_settings.str # => "value_from_root" # class Settings ## # A special value indicating a type check failure. # ILLEGAL_VALUE = ::Object.new.freeze ## # A special type specification indicating infer from the default value. # DEFAULT_TYPE = ::Object.new.freeze ## # Field names that are not allowed because they would shadow critical Ruby # methods or break Settings' own internal method calls. # # @return [Array] # RESERVED_FIELD_NAMES = [ "class", "clone", "dup", "freeze", "hash", "initialize", "method_missing", "object_id", "public_send", "raise", "require", "send" ].freeze ## # **_Defined in the toys-core gem_** # # Error raised when a value does not match the type constraint. # class FieldError < ::StandardError ## # The value that did not match # @return [Object] # attr_reader :value ## # The settings class that rejected the value # @return [Class] # attr_reader :settings_class ## # The field that rejected the value # @return [Symbol] # attr_reader :field_name ## # A description of the type constraint, or nil if the field didn't exist. # @return [String, nil] # attr_reader :type_description ## # @private This interface is internal and subject to change without warning. # def initialize(value, settings_class, field_name, type_description) # Source available in the toys-core gem end end ## # **_Defined in the toys-core gem_** # # A type object that checks values. # # A Type includes a description string and a testing function. The testing # function takes a proposed value and returns either the value itself if it # is valid, a converted value if the value can be converted to a valid # value, or {ILLEGAL_VALUE} if the type check failed. # class Type ## # Create a new Type. # # @param description [String] Name of the type. # @param block [Proc] A testing function. # def initialize(description, &block) # Source available in the toys-core gem end ## # The name of the type. # @return [String] # attr_reader :description ## # Test a value, possibly converting to a legal value. # # @param val [Object] The value to be tested. # @return [Object] The validated value, the value converted to a legal # value, or {ILLEGAL_VALUE} if the type check is unsuccessful. # def call(val) # Source available in the toys-core gem end class << self ## # Create and return a Type given a type specification. See the # {Settings} class documentation for valid type specifications. # # @param type_spec [Object] # @return [Type] # @raise [ArgumentError] if the type specification is invalid. # def for_type_spec(type_spec) # Source available in the toys-core gem end ## # Create and return a Type given a default value. See the {Settings} # class documentation for the rules. # # @param value [Object] # @return [Type] # def for_default_value(value) # Source available in the toys-core gem end end end ## # Create a settings instance. # # @param parent [Settings,nil] Optional parent settings. # def initialize(parent: nil) # Source available in the toys-core gem end ## # Load the given hash of data into this settings object. # # @param data [Hash] The data as a hash of key-value pairs. # @param raise_on_failure [boolean] If `true`, raises an exception on the # first error encountered. If `false`, continues parsing and returns an # array of the errors raised. # @return [Array] An array of errors. # def load_data!(data, raise_on_failure: false) # Source available in the toys-core gem end ## # Parse the given YAML string and load the data into this settings object. # # @param str [String] The YAML-formatted string. # @param raise_on_failure [boolean] If `true`, raises an exception on the # first error encountered. If `false`, continues parsing and returns an # array of the errors raised. # @return [Array] An array of errors. # def load_yaml!(str, raise_on_failure: false) # Source available in the toys-core gem end ## # Parse the given YAML file and load the data into this settings object. # # @param filename [String] The path to the YAML-formatted file. # @param raise_on_failure [boolean] If `true`, raises an exception on the # first error encountered. If `false`, continues parsing and returns an # array of the errors raised. # @return [Array] An array of errors. # def load_yaml_file!(filename, raise_on_failure: false) # Source available in the toys-core gem end ## # Parse the given JSON string and load the data into this settings object. # # @param str [String] The JSON-formatted string. # @param raise_on_failure [boolean] If `true`, raises an exception on the # first error encountered. If `false`, continues parsing and returns an # array of the errors raised. # @return [Array] An array of errors. # def load_json!(str, raise_on_failure: false, **json_opts) # Source available in the toys-core gem end ## # Parse the given JSON file and load the data into this settings object. # # @param filename [String] The path to the JSON-formatted file. # @param raise_on_failure [boolean] If `true`, raises an exception on the # first error encountered. If `false`, continues parsing and returns an # array of the errors raised. # @return [Array] An array of errors. # def load_json_file!(filename, raise_on_failure: false, **json_opts) # Source available in the toys-core gem end class << self ## # Add an attribute field. # # @param name [Symbol,String] The name of the attribute. # @param default [Object] Optional. The final default value if the field # is not set in this settings object or any of its ancestors. If not # provided, `nil` is used. # @param type [Object] Optional. The type specification. If not provided, # one is inferred from the default value. # def settings_attr(name, default: nil, type: DEFAULT_TYPE, &block) # Source available in the toys-core gem end ## # Add a group field. # # Specify the group's structure by passing either a class (which must # subclass Settings) or a block (which will be called on the group's # class.) # # @param name [Symbol, String] The name of the group. # @param klass [Class] Optional. The class of the group (which must # subclass Settings). If not present, an anonymous subclass will be # created, and you must provide a block to configure it. # def settings_group(name, klass = nil, &block) # Source available in the toys-core gem end ## # @private This interface is internal and subject to change without warning. # # Returns the fields hash. This is shared between the settings class and # all its instances. # def fields # Source available in the toys-core gem end end end end toys-0.21.0/core-docs/toys-core.rb0000644000004100000410000000766215164300675016771 0ustar www-datawww-data## # **_Defined in the toys-core gem_** # # Toys is a configurable command line tool. Write commands in config files # using a simple DSL, and Toys will provide the command line executable and # take care of all the details such as argument parsing, online help, and error # reporting. Toys is designed for software developers, IT professionals, and # other power users who want to write and organize scripts to automate their # workflows. It can also be used as a Rake replacement, providing a more # natural command line interface for your project's build tasks. # # This module contains the command line framework underlying Toys. It can be # used to create command line executables using the Toys DSL and classes. # # ## Common starting points # # Some of the most commonly needed class documentation is listed below: # # * For information on the DSL used to write tools, start with # {Toys::DSL::Tool}. # * The base class for tool runtime (i.e. that defines the basic methods # available to a tool's implementation) is {Toys::Context}. # * For information on writing mixins, see {Toys::Mixin}. # * For information on writing templates, see {Toys::Template}. # * For information on writing acceptors, see {Toys::Acceptor}. # * For information on writing custom shell completions, see {Toys::Completion}. # * Standard mixins are defined under the {Toys::StandardMixins} module. # * Various utilities are defined under {Toys::Utils}. Some of these serve as # the implementations of corresponding mixins. # * The main entrypoint for the command line framework is {Toys::CLI}. # # Other important internal classes are listed below. # # * The definition of a tool is represented by {Toys::ToolDefinition} along # the helpers {Toys::Flag}, {Toys::PositionalArg}, and {Toys::FlagGroup}. # * Argument parsing is implemented by {Toys::ArgParser}. # * The process of finding and loading a tool definition given a tool name, is # implemented by {Toys::Loader}. # * Text wrapping is handled by {Toys::WrappableString}. # * The settings system is implemented by {Toys::Settings}. # module Toys ## # **_Defined in the toys-core gem_** # # Namespace for DSL classes. These classes provide the directives that can be # used in configuration files. # # DSL directives that can appear at the top level of Toys files and tool # blocks are defined by the {Toys::DSL::Tool} module. # # Directives that can appear within a block passed to {Toys::DSL::Tool#flag} # are defined by the {Toys::DSL::Flag} class. # # Directives that can appear within a {Toys::DSL::Tool#flag_group} block or # any of its related directives, are defined by the {Toys::DSL::FlagGroup} # class. # # Directives that can appear within a {Toys::DSL::Tool#required_arg}, # {Toys::DSL::Tool#optional_arg}, or {Toys::DSL::Tool#remaining_args} block, # are defined by the {Toys::DSL::PositionalArg} class. # module DSL end ## # **_Defined in the toys-core gem_** # # Namespace for standard middleware classes. # # These middleware are provided by Toys-Core and can be referenced by name # when creating a {Toys::CLI}. # module StandardMiddleware end ## # **_Defined in the toys-core gem_** # # Namespace for standard mixin classes. # # These mixins are provided by Toys-Core and can be included by name by # passing a symbol to {Toys::DSL::Tool#include}. # module StandardMixins end ## # **_Defined in the toys-core gem_** # # Namespace for common utility classes. # # These classes are not loaded by default, and must be required explicitly. # For example, before using {Toys::Utils::Exec}, you must: # # require "toys/utils/exec" # module Utils end class << self ## # Path to the executable. This can, for example, be invoked to run a subtool # in a clean environment. # # @return [String] if there is an executable # @return [nil] if there is no such executable # attr_accessor :executable_path end end toys-0.21.0/lib/0000755000004100000410000000000015164300675013375 5ustar www-datawww-datatoys-0.21.0/lib/toys/0000755000004100000410000000000015164300675014373 5ustar www-datawww-datatoys-0.21.0/lib/toys/templates/0000755000004100000410000000000015164300675016371 5ustar www-datawww-datatoys-0.21.0/lib/toys/templates/rdoc.rb0000644000004100000410000002251615164300675017653 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template that generates rdoc tools. # class Rdoc include Template ## # Default version requirements for the rdoc gem. # @return [Array] # DEFAULT_GEM_VERSION_REQUIREMENTS = [">= 6.1.0"].freeze ## # Default tool name # @return [String] # DEFAULT_TOOL_NAME = "rdoc" ## # Default output directory # @return [String] # DEFAULT_OUTPUT_DIR = "html" ## # Default file globs # @return [Array] # DEFAULT_FILES = ["lib/**/*.rb"].freeze ## # Create the template settings for the Rdoc template. # # @param name [String] Name of the tool to create. Defaults to # {DEFAULT_TOOL_NAME}. # @param gem_version [String,Array] Version requirements for # the rdoc gem. Defaults to {DEFAULT_GEM_VERSION_REQUIREMENTS}. # @param files [Array] An array of globs indicating the files # to document. Defaults to {DEFAULT_FILES}. # @param output_dir [String] Name of directory to receive html output # files. Defaults to {DEFAULT_OUTPUT_DIR}. # @param markup [String] Markup format. Allowed values include "rdoc", # "rd", and "tomdoc". If not specified, RDoc will use its default # markup, which is "rdoc". # @param title [String] Title of RDoc documentation. If not specified, # RDoc will use a default title. # @param main [String] Name of the file to use as the main top level # document. Default is none. # @param template [String] Name of the template to use. If not specified, # RDoc will use its default template. # @param generator [String] Name of the format generator. If not # specified, RDoc will use its default generator. # @param options [Array] Additional options to pass to RDoc. # @param bundler [Boolean,Hash] If `false` (the default), bundler is not # enabled for this tool. If `true` or a Hash of options, bundler is # enabled. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on available options. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(name: nil, gem_version: nil, files: nil, output_dir: nil, markup: nil, title: nil, main: nil, template: nil, generator: nil, options: [], bundler: false, context_directory: nil) @name = name @gem_version = gem_version @files = files @output_dir = output_dir @markup = markup @title = title @main = main @template = template @generator = generator @options = options @bundler = bundler @context_directory = context_directory end ## # Name of the tool to create. # # @param value [String] # @return [String] # attr_writer :name ## # Version requirements for the rdoc gem. # If set to `nil`, uses the bundled version if bundler is enabled, or # defaults to {DEFAULT_GEM_VERSION_REQUIREMENTS} if bundler is not # enabled. # # @param value [String,Array,nil] # @return [String,Array,nil] # attr_writer :gem_version ## # An array of globs indicating which files to document. # # @param value [Array] # @return [Array] # attr_writer :files ## # Name of directory to receive html output files. # If set to `nil`, defaults to {DEFAULT_OUTPUT_DIR}. # # @param value [String,nil] # @return [String,nil] # attr_writer :output_dir ## # Markup format. Allowed values include "rdoc", "rd", and "tomdoc". # If set to `nil`, RDoc will use its default markup, which is "rdoc". # # @param value [String,nil] # @return [String,nil] # attr_writer :markup ## # Title of RDoc documentation pages. # If set to `nil`, RDoc will use a default title. # # @param value [String,nil] # @return [String,nil] # attr_writer :title ## # Name of the file to use as the main top level document, or `nil` for # no top level document. # # @param value [String,nil] # @return [String,nil] # attr_writer :main ## # Name of the template to use. # If set to `nil`, RDoc will choose a default template. # # @param value [String,nil] # @return [String,nil] # attr_writer :template ## # Name of the format generator. # If set to `nil`, RDoc will use its default generator. # # @param value [String,nil] # @return [String,nil] # attr_writer :generator ## # Additional options to pass to RDoc # # @param value [Array] # @return [Array] # attr_writer :options ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # Set the bundler state and options for this tool. # # Pass `false` to disable bundler. Pass `true` or a hash of options to # enable bundler. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param value [Boolean,Hash] # @return [Boolean,Hash] # attr_writer :bundler ## # Use bundler for this tool. # # See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param opts [keywords] Options for bundler # @return [self] # def use_bundler(**opts) @bundler = opts self end ## # @private # attr_reader :markup ## # @private # attr_reader :title ## # @private # attr_reader :main ## # @private # attr_reader :template ## # @private # attr_reader :generator ## # @private # attr_reader :context_directory ## # @private # def name @name || DEFAULT_TOOL_NAME end ## # @private # def gem_version return Array(@gem_version) if @gem_version @bundler ? [] : DEFAULT_GEM_VERSION_REQUIREMENTS end ## # @private # def files @files ? Array(@files) : DEFAULT_FILES end ## # @private # def output_dir @output_dir || DEFAULT_OUTPUT_DIR end ## # @private # def options Array(@options) end ## # @private # def bundler_settings if @bundler && !@bundler.is_a?(::Hash) {} else @bundler end end on_expand do |template| tool(template.name) do desc "Run rdoc on the current project." set_context_directory template.context_directory if template.context_directory static :gem_version, template.gem_version static :template_files, template.files static :rdoc_options, template.options static :output_dir, template.output_dir static :rdoc_markup, template.markup static :rdoc_main, template.main static :rdoc_title, template.title static :rdoc_template, template.template static :rdoc_generator, template.generator include :exec, exit_on_nonzero_status: true include :gems bundler_settings = template.bundler_settings include :bundler, **bundler_settings if bundler_settings # @private def run # rubocop:disable all gem "rdoc", *gem_version ::Dir.chdir(context_directory || ::Dir.getwd) do files = [] template_files.each do |pattern| files.concat(::Dir.glob(pattern)) end files.uniq! args = rdoc_options.dup args << "-o" << output_dir args << "--markup" << rdoc_markup if rdoc_markup args << "--main" << rdoc_main if rdoc_main args << "--title" << rdoc_title if rdoc_title args << "-T" << rdoc_template if rdoc_template args << "-f" << rdoc_generator if rdoc_generator code = <<~CODE gem 'rdoc', *#{gem_version.inspect} require 'rdoc' ::RDoc::RDoc.new.document(#{(args + files).inspect}) CODE exec_ruby(["-e", code]) end end end end end end end toys-0.21.0/lib/toys/templates/rubocop.rb0000644000004100000410000001323215164300675020370 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template for tools that run rubocop # class Rubocop include Template ## # Default version requirements for the rubocop gem. # @return [Array] # DEFAULT_GEM_VERSION_REQUIREMENTS = [].freeze ## # Default tool name # @return [String] # DEFAULT_TOOL_NAME = "rubocop" ## # Create the template settings for the Rubocop template. # # @param name [String] Name of the tool to create. Defaults to # {DEFAULT_TOOL_NAME}. # @param gem_version [String,Array] Version requirements for # the rubocop gem. Defaults to {DEFAULT_GEM_VERSION_REQUIREMENTS}. # @param fail_on_error [Boolean] If true, exits with a nonzero code if # Rubocop fails. Defaults to true. # @param options [Array] Additional options passed to the Rubocop # CLI. # @param bundler [Boolean,Hash] If `false` (the default), bundler is not # enabled for this tool. If `true` or a Hash of options, bundler is # enabled. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on available options. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(name: DEFAULT_TOOL_NAME, gem_version: nil, fail_on_error: true, options: [], bundler: false, context_directory: nil) @name = name @gem_version = gem_version @fail_on_error = fail_on_error @options = options @bundler = bundler @context_directory = context_directory end ## # Name of the tool to create. # # @param value [String] # @return [String] # attr_writer :name ## # Version requirements for the rdoc gem. # If set to `nil`, uses the bundled version if bundler is enabled, or # defaults to {DEFAULT_GEM_VERSION_REQUIREMENTS} if bundler is not # enabled. # # @param value [String,Array,nil] # @return [String,Array,nil] # attr_writer :gem_version ## # Whether to exit with a nonzero code if Rubocop fails. # # @param value [Boolean] # @return [Boolean] # attr_writer :fail_on_error ## # Additional options to pass to Rubocop # # @param value [Array] # @return [Array] # attr_writer :options ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # Set the bundler state and options for this tool. # # Pass `false` to disable bundler. Pass `true` or a hash of options to # enable bundler. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param value [Boolean,Hash] # @return [Boolean,Hash] # attr_writer :bundler ## # Activate bundler for this tool. # # See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param opts [keywords] Options for bundler # @return [self] # def use_bundler(**opts) @bundler = opts self end ## # @private # attr_reader :fail_on_error ## # @private # attr_reader :context_directory ## # @private # def name @name || DEFAULT_TOOL_NAME end ## # @private # def gem_version return Array(@gem_version) if @gem_version @bundler ? [] : DEFAULT_GEM_VERSION_REQUIREMENTS end ## # @private # def options Array(@options) end ## # @private # def bundler_settings if @bundler && !@bundler.is_a?(::Hash) {} else @bundler end end on_expand do |template| tool(template.name) do desc "Run rubocop on the current project." set_context_directory template.context_directory if template.context_directory static :gem_version, template.gem_version static :rubocop_options, template.options static :fail_on_error, template.fail_on_error include :gems include :exec bundler_settings = template.bundler_settings include :bundler, **bundler_settings if bundler_settings disable_argument_parsing # @private def run gem "rubocop", *gem_version ::Dir.chdir(context_directory || ::Dir.getwd) do logger.info "Running RuboCop..." rubocop_args = rubocop_options + args code = <<~CODE gem 'rubocop', *#{gem_version.inspect} require 'rubocop' exit(::RuboCop::CLI.new.run(#{rubocop_args.inspect})) CODE result = exec_ruby(["-e", code]) if result.error? logger.error "RuboCop failed!" exit(1) if fail_on_error end end end end end end end end toys-0.21.0/lib/toys/templates/rspec.rb0000644000004100000410000004721415164300675020042 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template for tools that run rspec # class Rspec include Template ## # Default version requirements for gem names. # @return [Hash{String=>Array}] # DEFAULT_GEM_VERSION_REQUIREMENTS = { "rspec" => ["~> 3.1"].freeze, }.freeze ## # Default tool name # @return [String] # DEFAULT_TOOL_NAME = "spec" ## # Default set of library paths # @return [Array] # DEFAULT_LIBS = ["lib"].freeze ## # Default order type # @return [String] # DEFAULT_ORDER = "defined" ## # Default format code # @return [String] # DEFAULT_FORMAT = "p" ## # Default spec file glob # @return [String] # DEFAULT_PATTERN = "spec/**/*_spec.rb" ## # Create the template settings for the RSpec template. # # Note that arguments related to gem and bundler settings are defaults # that can be overridden by command line arguments. # # @param name [String] Name of the tool to create. Defaults to # {DEFAULT_TOOL_NAME}. # @param rspec [String,Array] Version requirements for # the "rspec" gem, used if bundler is not enabled. # Optional. If not provided, defaults to the value given in # {DEFAULT_GEM_VERSION_REQUIREMENTS}. # @param gem_version [String,Array] Deprecated alias for the # `rspec` argument. # @param gems [Hash{String=>String|Array|true}] Include the given # gems with the given version requirements. Used if bundler is not # enabled. If the version requirement is set to `true`, then the # default in {DEFAULT_GEM_VERSION_REQUIREMENTS} is used. If there # is no default available, then no particular version requirements # are imposed. # @param libs [Array] An array of library paths to add to the # ruby require path. Defaults to {DEFAULT_LIBS}. # @param options [String] The path to a custom options file, if any. # @param order [String] The order in which to run examples. Default is # {DEFAULT_ORDER}. # @param format [String] The formatter code. Default is {DEFAULT_FORMAT}. # @param out [String] Write output to a file instead of stdout. # @param backtrace [Boolean] Enable full backtrace (default is false). # @param pattern [String] A glob indicating the spec files to load. # Defaults to {DEFAULT_PATTERN}. # @param warnings [Boolean] If true, runs specs with Ruby warnings. # Defaults to true. # @param bundler [Boolean,Hash] If `false` (the default), bundler is not # used unless enabled via command line argument. If `true` or a Hash # of options, bundler is enabled by default unless disabled via a # command line argument. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on available options. Note that any `:setup` option # is ignored; the bundle, if enabled, is always installed and set up # at the start of the tool execution. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(name: nil, rspec: nil, gem_version: nil, gems: nil, libs: nil, options: nil, order: nil, format: nil, out: nil, backtrace: false, pattern: nil, warnings: true, bundler: false, context_directory: nil) @name = name @libs = libs @options = options @order = order @format = format @out = out @backtrace = backtrace @pattern = pattern @warnings = warnings @bundler = bundler @context_directory = context_directory @gem_dependencies = {} update_version_spec("rspec", rspec || gem_version) update_gems(gems) if gems end ## # Name of the tool to create. # # @param value [String] # @return [String] # attr_writer :name ## # Update the gems and version requirements that are used if bundler is # not enabled. # # @param gems [Hash{String=>String|Array|true|false|nil}] # A mapping from gem name to either the version requirements, # `true` to use a default, or `false` or `nil` to remove the gem from # the list. (Note that it is not possible to remove the `rspec` # gem, and setting it to `nil` will simply restore the default # version requirements.) # @return [self] # def update_gems(gems) gems.each do |gem_name, version_requirements| update_version_spec(gem_name, version_requirements) end self end ## # Version requirements for the rspec gem. Used if bundler is not used. # If set to `true` or `nil`, a default version requirement is used. # # @param value [String,Array,true,nil] # def rspec=(value) update_version_spec("rspec", value) end alias gem_version= rspec= ## # An array of directories to add to the Ruby require path. # If set to `nil`, defaults to {DEFAULT_LIBS}. # # @param value [Array,nil] # @return [Array,nil] # attr_writer :libs ## # Path to the custom options file, or `nil` for none. # # @param value [String,nil] # @return [String,nil] # attr_writer :options ## # The order in which to run examples. # If set to `nil`, defaults to {DEFAULT_ORDER}. # # @param value [String,nil] # @return [String,nil] # attr_writer :order ## # The formatter code. # If set to `nil`, defaults to {DEFAULT_FORMAT}. # # @param value [String,nil] # @return [String,nil] # attr_writer :format ## # Path to a file to write output to. # If set to `nil`, writes output to standard out. # # @param value [String,nil] # @return [String,nil] # attr_writer :out ## # Whether to enable full backtraces. # # @param value [Boolean] # @return [Boolean] # attr_writer :backtrace ## # A glob indicating the spec files to load. # If set to `nil`, defaults to {DEFAULT_PATTERN}. # # @param value [String,nil] # @return [String,nil] # attr_writer :pattern ## # Whether to run with Ruby warnings. # # @param value [Boolean] # @return [Boolean] # attr_writer :warnings ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # Set the bundler state and options for this tool. # # Pass `false` to disable bundler. Pass `true` or a hash of options to # enable bundler. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param value [Boolean,Hash] # @return [Boolean,Hash] # attr_writer :bundler ## # Activate bundler for this tool. # # See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param opts [keywords] Options for bundler # @return [self] # def use_bundler(**opts) @bundler = opts self end ## # @private # attr_reader :options ## # @private # attr_reader :out ## # @private # attr_reader :backtrace ## # @private # attr_reader :warnings ## # @private # attr_reader :context_directory ## # @private # attr_reader :gem_dependencies ## # @private # def name @name || DEFAULT_TOOL_NAME end ## # @private # def libs @libs ? Array(@libs) : DEFAULT_LIBS end ## # @private # def order @order || DEFAULT_ORDER end ## # @private # def format @format || DEFAULT_FORMAT end ## # @private # def pattern @pattern || DEFAULT_PATTERN end ## # @private # def bundler_settings if @bundler.is_a?(::Hash) @bundler.merge({setup: :manual}) else {setup: :manual} end end ## # @private # def default_to_bundler? @bundler ? true : false end on_expand do |template| tool(template.name) do desc "Run rspec on the current project." long_desc( "Run rspec specs for the current project.", "", "By default, executes specs matching the following pattern:" ) long_desc([" - `#{template.pattern}`"]) long_desc( "To override this, pass the paths to the spec files to run as command line arguments." \ " You can also filter by example name using the --example argument.", "" ) if template.default_to_bundler? long_desc( "By default, uses the project bundle to load gems needed for testing, including the rspec gem.", "You can specify a particular Gemfile using the --gemfile argument." \ " Alternatively, you can disable the bundle and manually specify the gem list using the --use-gem" \ " and --omit-gem arguments." ) else long_desc( "By default, loads the following gems with version constraints:" ) template.gem_dependencies.each do |name, versions| spec = ([name] + versions).join(", ") long_desc([" - `#{spec}`"]) end long_desc( "To alter this list, use the --use-gem and --omit-gem arguments." \ " Alternatively, you can use a bundle instead by passing the --gemfile argument." ) end set_context_directory template.context_directory if template.context_directory include :exec include :gems include :bundler, **template.bundler_settings flag(:order, "--order TYPE") do default(template.order) desc("Run examples by the specified order type (default: #{template.order})") end flag(:format, "-f", "--format FORMATTER") do default(template.format) desc("Choose a formatter (default: #{template.format})") end flag(:out, "-o", "--out FILE") do default(template.out) desc("Write output to a file (default: #{template.out.inspect})") end flag(:backtrace, "-b", "--[no-]backtrace") do default(template.backtrace) desc("Enable full backtrace (default: #{template.backtrace})") end flag(:warnings, "-w", "--[no-]warnings") do default(template.warnings) desc("Turn on Ruby warnings (default: #{template.warnings})") end flag(:pattern, "-P", "--pattern PATTERN") do default(template.pattern) desc("Load files matching pattern (default: #{template.pattern.inspect})") end flag(:exclude_pattern, "--exclude-pattern PATTERN") do desc("Load files except those matching pattern.") end flag(:example, "-e", "--example STRING") do default([]) handler(:push) desc("Run examples whose full nested names include STRING (may be used more than once).") end flag(:example_matches, "-E", "--example-matches REGEX") do default([]) handler(:push) desc("Run examples whose full nested names match REGEX (may be used more than once).") end flag(:tag, "-t", "--tag TAG") do default([]) handler(:push) desc("Run examples with the specified tag, or exclude examples by adding ~ before the tag.") end flag(:gemfile_path, "--gemfile PATH", "--gemfile-path PATH") do desc("Bundle with the given gemfile, overriding any static setting.") long_desc( "Bundle with the given gemfile, overriding any static setting.", "", "You must provide the path to the Gemfile to use. This may override any default project Gemfile.", "This flag is mutually exclusive with --use-gem and --omit-gem." ) end flag(:override_use_gems, "--use-gem SPEC") do default([]) handler(:push) desc("Install the given gem with version requirements, overriding any static setting.") long_desc( "Install the given gem with version requirements, overriding any static setting.", "", "The format is the gem name followed by zero or more version requirements, separated by commas.", "For example:", [" --use-gem rspec,~>3.1"], "", "Each --use-gem flag specifies a single gem." \ " You can provide any number of --use-gem flags to specify any number of gems.", "", "This flag can be used in conjunction with --omit-gem, but is mutually exclusive with --gemfile." ) end flag(:override_omit_gems, "--omit-gem NAME") do default([]) handler(:push) desc("Do not install the given gem, overriding any static setting.") long_desc( "Do not install the given gem, overriding any static setting.", "", "Each --omit-gem flag omits a single gem." \ " You can provide any number of --omit-gem flags to specifically omit any number of gems.", "", "This flag can be used in conjunction with --use-gem, but is mutually exclusive with --gemfile." ) end remaining_args :files, complete: :file_system, desc: "Paths to the specs to run (defaults to all specs)" static :libs, template.libs static :gem_dependencies, template.gem_dependencies static :default_to_bundler, template.default_to_bundler? static :rspec_options, template.options # @private def run require "tempfile" ::Dir.chdir(context_directory || ::Dir.getwd) do loaded_gem_versions = init_bundle_or_gems ::Tempfile.create(["toys-rspec-script-", ".rb"]) do |script_file| script_file.write(ruby_script(loaded_gem_versions)) script_file.close result = exec_ruby(ruby_args(script_file.path)) if result.error? logger.error("RSpec failed!") exit(result.exit_code) end end end end # @private def ruby_args(script_path) # rubocop:disable Metrics/AbcSize args = [] args << "-I#{libs.join(::File::PATH_SEPARATOR)}" unless libs.empty? args << "-w" if warnings args << script_path args << "--options" << rspec_options if rspec_options args << "--order" << order if order args << "--format" << format if format args << "--out" << out if out args << "--backtrace" if backtrace args << "--pattern" << pattern args << "--exclude-pattern" << exclude_pattern if exclude_pattern example.each { |val| args << "--example" << val } example_matches.each { |val| args << "--example-matches" << val } tag.each { |val| args << "--tag" << val } args.concat(files) args end # @private def init_bundle_or_gems if gemfile_path && (!override_use_gems.empty? || !override_omit_gems.empty?) logger.error("--gemfile is mutually exclusive with --use-gem and --omit-gem") exit(1) end if gemfile_path || (default_to_bundler && override_use_gems.empty? && override_omit_gems.empty?) bundler_setup(gemfile_path: gemfile_path) nil else load_gems end end # @private def load_gems updated_dependencies = updated_gem_dependencies updated_dependencies.each do |gem_name, version_requirements| next if gem_name == "rspec" gem gem_name, *version_requirements end gem "rspec", *updated_dependencies["rspec"] loaded_versions = {} ::Gem.loaded_specs.each_value do |spec| loaded_versions[spec.name] = spec.version.to_s if updated_dependencies.key?(spec.name) end loaded_versions end # @private def updated_gem_dependencies dependencies = gem_dependencies.dup override_use_gems.each do |spec| name, *versions = spec.strip.split(/\s*,\s*/) if name.to_s.empty? logger.error("Bad format for --use-gem: #{spec.inspect}") exit(1) end versions.delete_if(&:empty?) versions = ::Toys::Templates::Rspec::DEFAULT_GEM_VERSION_REQUIREMENTS[name] || [] if versions.empty? dependencies[name] = versions end override_omit_gems.each do |name| name = name.strip if name == "rspec" logger.warn("You cannot omit the rspec gem. Ignoring --omit-gem=rspec.") else dependencies.delete(name) end end dependencies end # @private def ruby_script(loaded_gem_versions) lines = [] if loaded_gem_versions loaded_gem_versions.each do |gem_name, gem_version| lines << "gem #{gem_name.inspect}, '= #{gem_version}'" end else lines << "require 'bundler/setup'" end lines << "require 'rspec/core'" lines << "::RSpec::Core::Runner.invoke" lines << "" lines.join("\n") end end end private def update_version_spec(gem_name, version_requirements) version_requirements ||= true if gem_name == "rspec" if version_requirements @gem_dependencies[gem_name] = if version_requirements == true DEFAULT_GEM_VERSION_REQUIREMENTS[gem_name] || [] else Array(version_requirements).map(&:to_s) end else @gem_dependencies.delete(gem_name) nil end end end end end toys-0.21.0/lib/toys/templates/rake.rb0000644000004100000410000001676415164300675017656 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template that generates tools matching a rakefile. # class Rake include Template ## # Default path to the Rakefile. # @return [String] # DEFAULT_RAKEFILE_PATH = "Rakefile" ## # Create the template settings for the rake template. # # @param gem_version [String,Array,nil] Version requirements for # the rake gem. Defaults to nil, indicating no version requirement. # @param rakefile_path [String] Path to the Rakefile. Defaults to # {DEFAULT_RAKEFILE_PATH}. # @param only_described [Boolean] If true, tools are generated only for # rake tasks with descriptions. Default is false. # @param use_flags [Boolean] Generated tools use flags instead of # positional arguments to pass arguments to rake tasks. Default is # false. # @param bundler [Boolean,Hash] If `false` (the default), bundler is not # enabled for Rake tools. If `true` or a Hash of options, bundler is # enabled. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on available options. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(gem_version: nil, rakefile_path: nil, only_described: false, use_flags: false, bundler: false, context_directory: nil) @gem_version = gem_version @rakefile_path = rakefile_path @only_described = only_described @use_flags = use_flags @bundler = bundler @context_directory = context_directory end ## # Version requirements for the minitest gem. # If set to `nil`, has no version requirement (unless one is specified in # the bundle.) # # @param value [String,Array,nil] # @return [String,Array,nil] # attr_writer :gem_version ## # Path to the Rakefile. # If set to `nil`, defaults to {DEFAULT_RAKEFILE_PATH}. # # @param value [String,nil] # @return [String,nil] # attr_writer :rakefile_path ## # Whether to generate tools only for rake tasks with descriptions. # # @param value [Boolean] # @return [Boolean] # attr_writer :only_described ## # Whether generated tools should use flags instead of positional # arguments to pass arguments to rake tasks. # # @param value [Boolean] # @return [Boolean] # attr_writer :use_flags ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # Set the bundler state and options for all Rake tools. # # Pass `false` to disable bundler. Pass `true` or a hash of options to # enable bundler. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param value [Boolean,Hash] # @return [Boolean,Hash] # attr_writer :bundler ## # Use bundler for all Rake tools. # # See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param opts [keywords] Options for bundler # @return [self] # def use_bundler(**opts) @bundler = opts self end ## # @private # attr_reader :only_described ## # @private # attr_reader :use_flags ## # @private # attr_reader :context_directory ## # @private # def gem_version Array(@gem_version) end ## # @private # def rakefile_path @rakefile_path || DEFAULT_RAKEFILE_PATH end ## # @private # def bundler_settings if @bundler && !@bundler.is_a?(::Hash) {} else @bundler end end on_expand do |template| gem "rake", *template.gem_version require "rake" rakefile_path = ::Toys::Templates::Rake.find_rakefile( template.rakefile_path, template.context_directory || context_directory ) raise "Cannot find #{template.rakefile_path}" unless rakefile_path rake_context_dir = ::File.dirname(rakefile_path) rake = ::Toys::Templates::Rake.prepare_rake(rakefile_path, rake_context_dir) rake.tasks.each do |task| comments = task.full_comment.to_s.split("\n") next if comments.empty? && template.only_described tool(task.name.split(":"), if_defined: :ignore) do static :task, task static :rake_context_dir, rake_context_dir bundler_settings = template.bundler_settings include :bundler, **bundler_settings if bundler_settings unless comments.empty? desc(comments.first) comments << "" << "Defined as a Rake task in #{rakefile_path}" long_desc(*comments) end if template.use_flags task.arg_names.each do |arg| specs = ::Toys::Templates::Rake.flag_specs(arg) flag(arg, *specs) unless specs.empty? end # @private def run args = task.arg_names.map { |arg| self[arg] } ::Dir.chdir(rake_context_dir) do task.invoke(*args) end end else task.arg_names.each do |arg| optional_arg(arg) end # @private def run ::Dir.chdir(rake_context_dir) do task.invoke(*args) end end end end end end ## # @private # def self.flag_specs(arg) name = arg.to_s.gsub(/\W/, "").downcase specs = [] unless name.empty? specs << "--#{name}=VALUE" name2 = name.tr("_", "-") specs << "--#{name2}=VALUE" unless name2 == name end specs end ## # @private # def self.find_rakefile(path, context_dir) if path == ::File.absolute_path(path) return ::File.file?(path) && ::File.readable?(path) ? path : nil end dir = ::Dir.getwd 50.times do rakefile_path = ::File.expand_path(path, dir) return rakefile_path if ::File.file?(rakefile_path) && ::File.readable?(rakefile_path) break if dir == context_dir next_dir = ::File.dirname(dir) break if dir == next_dir dir = next_dir end nil end ## # @private # def self.prepare_rake(rakefile_path, context_dir) ::Rake::TaskManager.record_task_metadata = true rake = ::Rake::Application.new ::Rake.application = rake ::Dir.chdir(context_dir) do ::Rake.load_rakefile(rakefile_path) end rake end end end end toys-0.21.0/lib/toys/templates/clean.rb0000644000004100000410000001627415164300675020012 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template for tools that clean build artifacts # class Clean include Template ## # Default tool name # @return [String] # DEFAULT_TOOL_NAME = "clean" ## # Create the template settings for the Clean template. # # @param name [String] Name of the tool to create. Defaults to # {DEFAULT_TOOL_NAME}. # @param paths [Array] An array of glob patterns # indicating what to clean. You can also include the symbol # `:gitignore` which will clean all items covered by `.gitignore` # files, if contained in a git working tree. # @param preserve [Array] An array of glob patterns indicating # what to preserve. Matching paths will be skipped even if they # match `paths`. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(name: nil, paths: [], preserve: [], context_directory: nil) @name = name @paths = paths @preserve = preserve @context_directory = context_directory end ## # Name of the tool to create. # # @param value [String] # @return [String] # attr_writer :name ## # An array of glob patterns indicating what to clean. May also include # the symbol `:gitignore` which indicates all items covered by # `.gitignore` files, if contained in a git working tree. # # @param value [Array] # @return [Array] # attr_writer :paths ## # An array of glob patterns indicating what to preserve. # # @param value [Array] # @return [Array] # attr_writer :preserve ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # @private # attr_reader :context_directory ## # @private # def paths Array(@paths) end ## # @private # def preserve Array(@preserve) end ## # @private # def name @name || DEFAULT_TOOL_NAME end on_expand do |template| tool(template.name) do desc "Clean built files and directories." flag(:dry_run, "--dry-run", "-n") do desc "Dry run that outputs files that would be cleaned but doesn't actually delete them" end set_context_directory template.context_directory if template.context_directory paths = template.paths.dup static :template_gitignore, paths.delete(:gitignore) paths.each do |elem| unless elem.is_a?(::String) raise Toys::ToolDefinitionError, "Unexpected element in paths: #{elem.inspect} (expected a glob or :gitignore)" end end static :template_paths, paths template.preserve.each do |elem| unless elem.is_a?(::String) raise Toys::ToolDefinitionError, "Unexpected element in preserve: #{elem.inspect} (expected a glob)" end end static :template_preserve, template.preserve include :fileutils include :exec ## # @private # def run cd(context_directory || ::Dir.getwd) do preserve_set = make_preserve_set(template_preserve) clean_globs(template_paths, preserve_set) clean_gitignore(preserve_set) if template_gitignore end end ## # @private # Takes a possibly empty array of globs. For each, adds matching # paths to the set of paths to preserve. Returns the set. All parent # and ancestor directories are also included in the set. This # prevents the cleaner from deleting those directories recursively # since there are preserved contents. # def make_preserve_set(globs) require "set" preserve_set = ::Set.new << "." process_globs(globs) do |path| until preserve_set.include?(path) preserve_set << path path = ::File.dirname(path) end end preserve_set end ## # @private # Takes a possibly empty array of globs. For each, attempts to clean # matching paths that are not included in the given preserve set. # def clean_globs(globs, preserve_set) process_globs(globs) do |path| unless preserve_set.include?(path) rm_rf(path) unless dry_run puts "Cleaned: #{path}" end end end ## # @private # Iterates through the entire directory structure recursively. Cleans # anything that is not covered in the given preserve set AND is # covered by gitignore. # def clean_gitignore(preserve_set) result = exec(["git", "rev-parse", "--is-inside-work-tree"], out: :null, err: :null) unless result.success? logger.error("Skipping :gitignore because we don't seem to be in a git directory") return end exec(["git", "check-ignore", "--stdin"], in: :controller, out: :controller) do |controller| ::Thread.new do ::Dir.children(".").sort.each do |child| process_path(child) do |path| controller.in.puts(path) unless preserve_set.include?(path) end end ensure controller.in.close end controller.out.each_line do |path| path = path.chomp rm_rf(path) unless dry_run puts "Cleaned: #{path}" end end end ## # @private # Iterates through a list of globs. For each glob, iterates over all # matching paths and calls the given block. # def process_globs(globs, &block) globs.each do |glob| ::Dir.glob(glob) do |path| process_path(path.sub(%r{/$}, ""), &block) end end end ## # @private # For the given path and all its recursive descendents, calls the # given block. Calls the block on children before parents. # def process_path(path, &block) begin children = ::File.lstat(path).directory? ? ::Dir.children(path).sort : [] rescue ::Errno::ENOENT, ::Errno::ENOTDIR # Something happened to the path. Just skip it. return end children.each do |child| process_path(::File.join(path, child), &block) end yield path end end end end end end toys-0.21.0/lib/toys/templates/minitest.rb0000644000004100000410000006043215164300675020557 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template for tools that run minitest # class Minitest include Template ## # Default version requirements for gem names. # @return [Hash{String=>Array}] # DEFAULT_GEM_VERSION_REQUIREMENTS = { "minitest" => [">= 5.0", "< 7"].freeze, "minitest-mock" => ["~> 5.27"].freeze, "minitest-focus" => ["~> 1.4", ">= 1.4.1"].freeze, "minitest-rg" => ["~> 5.4"].freeze, }.freeze ## # Default tool name # @return [String] # DEFAULT_TOOL_NAME = "test" ## # Default set of library paths # @return [Array] # DEFAULT_LIBS = ["lib"].freeze ## # Default set of test file globs # @return [Array] # DEFAULT_FILES = ["test/**/test_*.rb", "test/**/*_test.rb"].freeze ## # Create the template settings for the Minitest template. # # Note that arguments related to gem and bundler settings are defaults # that can be overridden by command line arguments. # # @param name [String] Name of the tool to create. Defaults to # {DEFAULT_TOOL_NAME}. # @param minitest [String,Array] Version requirements for # the "minitest" gem, used if bundler is not enabled. # Optional. If not provided, defaults to the value given in # {DEFAULT_GEM_VERSION_REQUIREMENTS}. # @param gem_version [String,Array] Deprecated alias for the # `minitest` argument. # @param minitest_mock [String,Array,true] Include the # "minitest-mock" gem with the given version requirements. Used if # bundler is not enabled. If true is passed, the value in # {DEFAULT_GEM_VERSION_REQUIREMENTS} is used. # @param minitest_focus [String,Array,true] Include the # "minitest-focus" gem with the given version requirements. Used if # bundler is not enabled. If true is passed, the value in # {DEFAULT_GEM_VERSION_REQUIREMENTS} is used. # @param minitest_rg [String,Array,true] Include the # "minitest-rg" gem with the given version requirements. Used if # bundler is not enabled. If true is passed, the value in # {DEFAULT_GEM_VERSION_REQUIREMENTS} is used. # @param gems [Hash{String=>String|Array|true}] Include the given # gems with the given version requirements. Used if bundler is not # enabled. If the version requirement is set to `true`, then a # the default in {DEFAULT_GEM_VERSION_REQUIREMENTS} is used. If there # is no default available, then no particular version requirements # are imposed. # @param libs [Array] An array of library paths to add to the # ruby require path. Defaults to {DEFAULT_LIBS}. # @param files [Array] An array of globs indicating the test # files to load. Defaults to {DEFAULT_FILES}. # @param seed [Integer] The random seed, if any. Optional. # @param verbose [Boolean] Whether to produce verbose output. Defaults to # false. # @param warnings [Boolean] If true, runs tests with Ruby warnings. # Defaults to true. # @param bundler [Boolean,Hash] If `false` (the default), bundler is not # used unless enabled via command line argument. If `true` or a Hash # of options, bundler is enabled by default unless disabled via a # command line argument. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on available options. Note that any `:setup` option # is ignored; the bundle, if enabled, is always installed and set up # at the start of the tool execution. # @param mt_compat [boolean] If set to `true` or `false`, sets the # `MT_COMPAT` environment variable accordingly. This may be required # for certain older Minitest plugins. Optional. If not present, keeps # any current setting. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(name: nil, minitest: nil, minitest_mock: nil, minitest_focus: nil, minitest_rg: nil, gems: nil, gem_version: nil, libs: nil, files: nil, seed: nil, verbose: false, warnings: true, bundler: false, mt_compat: nil, context_directory: nil) @name = name @libs = libs @files = files @seed = seed @verbose = verbose @warnings = warnings @bundler = bundler @mt_compat = mt_compat @context_directory = context_directory @gem_dependencies = {} update_version_spec("minitest", minitest || gem_version) update_version_spec("minitest-mock", minitest_mock) update_version_spec("minitest-focus", minitest_focus) update_version_spec("minitest-rg", minitest_rg) update_gems(gems) if gems end ## # Name of the tool to create. # # @param value [String] # @return [String] # attr_writer :name ## # Update the gems and version requirements that are used if bundler is # not enabled. # # @param gems [Hash{String=>String|Array|true|false|nil}] # A mapping from gem name to either the version requirements, # `true` to use a default, or `false` or `nil` to remove the gem from # the list. (Note that it is not possible to remove the `minitest` # gem, and setting it to `nil` will simply restore the default # version requirements.) # @return [self] # def update_gems(gems) gems.each do |gem_name, version_requirements| update_version_spec(gem_name, version_requirements) end self end ## # Version requirements for the minitest gem. Used if bundler is not used. # If set to `true` or `nil`, a default version requirement is used. # # @param value [String,Array,true,nil] # def minitest=(value) update_version_spec("minitest", value) end alias gem_version= minitest= ## # Version requirements for the minitest-mock gem. Used if bundler is not # used. # If set to `true`, a default version requirement is used. # If set to `nil`, minitest-mock is removed from the gem dependencies. # # @param value [String,Array,true,nil] # def minitest_mock=(value) update_version_spec("minitest-mock", value) end ## # Version requirements for the minitest-focus gem. Used if bundler is not # used. # If set to `true`, a default version requirement is used. # If set to `nil`, minitest-focus is removed from the gem dependencies. # # @param value [String,Array,true,nil] # def minitest_focus=(value) update_version_spec("minitest-focus", value) end ## # Version requirements for the minitest-rg gem. Used if bundler is not # used. # If set to `true`, a default version requirement is used. # If set to `nil`, minitest-rg is removed from the gem dependencies. # # @param value [String,Array,true,nil] # def minitest_rg=(value) update_version_spec("minitest-rg", value) end ## # An array of library paths to add to the ruby require path. # If set to `nil`, defaults to {DEFAULT_LIBS}. # # @param value [String,Array,nil] # @return [String,Array,nil] # attr_writer :libs ## # An array of globs indicating the test files to load. # If set to `nil`, defaults to {DEFAULT_FILES}. # # @param value [String,Array,nil] # @return [String,Array,nil] # attr_writer :files ## # The random seed, or `nil` if not specified. # # @param value [Integer,nil] # @return [Integer,nil] # attr_writer :seed ## # Whether to produce verbose output. # # @param value [Boolean] # @return [Boolean] # attr_writer :verbose ## # Whether to run tests with Ruby warnings. # # @param value [Boolean] # @return [Boolean] # attr_writer :warnings ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # Set the bundler state and options for this tool. # # Pass `false` to disable bundler. Pass `true` or a hash of options to # enable bundler. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param value [Boolean,Hash] # @return [Boolean,Hash] # attr_writer :bundler ## # Adjust the `MT_COMPAT` environment variable when running tests. This # setting may be necessary for certain older Minitest plugins. # # Pass `true` to enable compat mode, `false` to disable it, or `nil` to # use any ambient setting from the current environment. # # @param value [true,false,nil] # @return [true,false,nil] # attr_writer :mt_compat ## # Use bundler for this tool. # # See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param opts [keywords] Options for bundler # @return [self] # def use_bundler(**opts) @bundler = opts self end ## # @private # attr_reader :seed ## # @private # attr_reader :verbose ## # @private # attr_reader :warnings ## # @private # attr_reader :context_directory ## # @private # attr_reader :mt_compat ## # @private # attr_reader :gem_dependencies ## # @private # def name @name || DEFAULT_TOOL_NAME end ## # @private # def libs @libs ? Array(@libs) : DEFAULT_LIBS end ## # @private # def files @files ? Array(@files) : DEFAULT_FILES end ## # @private # def bundler_settings if @bundler.is_a?(::Hash) @bundler.merge({setup: :manual}) else {setup: :manual} end end ## # @private # def default_to_bundler? @bundler ? true : false end on_expand do |template| tool(template.name) do desc("Run minitest-based tests for the current project.") long_desc( "Run minitest-based tests for the current project.", "", "By default, executes tests in the files that match the following patterns:" ) template.files.each do |pattern| long_desc([" - `#{pattern}`"]) end long_desc( "To override this list, pass the paths to the test files to run as command line arguments." \ " You can also filter the test names to run using the --include and --exclude arguments.", "" ) if template.default_to_bundler? long_desc( "By default, uses the project bundle to load gems needed for testing, including the minitest gem.", "You can specify a particular Gemfile using the --gemfile argument." \ " Alternatively, you can disable the bundle and manually specify the gem list using the --use-gem" \ " and --omit-gem arguments." ) else long_desc( "By default, loads the following gems with version constraints:" ) template.gem_dependencies.each do |name, versions| spec = ([name] + versions).join(", ") long_desc([" - `#{spec}`"]) end long_desc( "To alter this list, use the --use-gem and --omit-gem arguments." \ " Alternatively, you can use a bundle instead by passing the --gemfile argument." ) end set_context_directory template.context_directory if template.context_directory include :exec include :gems include :bundler, **template.bundler_settings flag(:seed, "-s", "--seed SEED") do default(template.seed) desc("Sets random seed.") end flag(:warnings, "-w", "--[no-]warnings") do default(template.warnings) desc("Turn on Ruby warnings (defaults to #{template.warnings})") end flag(:include_name, "-i", "-n", "--include PATTERN", "--name PATTERN") do desc("Include /regexp/ or string for run.") long_desc( "Include /regexp/ or string for run.", "", "If the argument begins and ends with slashes, it is treated as a regular expression that must" \ " match test names in order to run them. Otherwise, the argument is treated as the name of the" \ " single test to run.", "This can be combined with --exclude." ) end flag(:exclude_name, "-e", "-x", "--exclude PATTERN") do desc("Exclude /regexp/ or string from run.") long_desc( "Exclude /regexp/ or string for run.", "", "If the argument begins and ends with slashes, it is treated as a regular expression that will" \ " filter out any matching test names. Otherwise, the argument is treated as the name of the" \ " single test to omit.", "This can be combined with --include." ) end flag(:expand_globs, "--globs", "--expand-globs") do desc("Expand any literal globs in the test file arguments.") end flag(:preload_code, "--preload-code CODE") do desc("Ruby code to execute before loading tests.") end flag(:override_libs, "--libs PATH") do # The logic below requires this to default to nil instead of the empty array. handler(:push) desc("Override the test library paths.") long_desc( "Specifies require paths to use when running tests." \ " Pass this flag multiple times to include multiple paths.", "", "If no --libs flags are present, defaults to the following list of paths:" ) template.libs.each do |path| long_desc([" - #{path}"]) end end flag(:gemfile_path, "--gemfile PATH", "--gemfile-path PATH") do desc("Bundle with the given gemfile, overriding any static setting.") long_desc( "Bundle with the given gemfile, overriding any static setting.", "", "You must provide the path to the Gemfile to use. This may override any default project Gemfile.", "This flag is mutually exclusive with --use-gem and --omit-gem." ) end flag(:override_use_gems, "--use-gem SPEC") do default([]) handler(:push) desc("Install the given gem with version requirements, overriding any static setting.") long_desc( "Install the given gem with version requirements, overriding any static setting.", "", "The format is the gem name followed by zero or more version requirements, separated by commas.", "For example:", [" --use-gem minitest-focus,~>1.4"], "", "Each --use-gem flag specifies a single gem." \ " You can provide any number of --use-gem flags to specify any number of gems.", "", "This flag can be used in conjunction with --omit-gem, but is mutually exclusive with --gemfile." ) end flag(:override_omit_gems, "--omit-gem NAME") do default([]) handler(:push) desc("Do not install the given gem, overriding any static setting.") long_desc( "Do not install the given gem, overriding any static setting.", "", "Each --omit-gem flag omits a single gem." \ " You can provide any number of --omit-gem flags to specifically omit any number of gems.", "", "This flag can be used in conjunction with --use-gem, but is mutually exclusive with --gemfile." ) end remaining_args(:tests) do complete(:file_system) desc("Paths to the test files to load (defaults to all tests)") long_desc( "Paths to the test files to load.", "", "Defaults to all files matching the following patterns:" ) template.files.each do |pattern| long_desc([" - `#{pattern}`"]) end end static :default_to_bundler, template.default_to_bundler? static :gem_dependencies, template.gem_dependencies static :libs, template.libs static :files, template.files static :template_verbose, template.verbose static :mt_compat, template.mt_compat # @private def run require "tempfile" ::Dir.chdir(context_directory || ::Dir.getwd) do loaded_gem_versions = init_bundle_or_gems found_tests = expand_tests validate_tests(found_tests) ::Tempfile.create(["toys-minitest-script-", ".rb"]) do |script_file| script_file.write(ruby_script(loaded_gem_versions, found_tests)) script_file.close result = exec_ruby(ruby_args(script_file.path), env: ruby_env) if result.error? logger.error("Minitest failed!") exit(result.exit_code) end end end end # @private def init_bundle_or_gems if gemfile_path && (!override_use_gems.empty? || !override_omit_gems.empty?) logger.error("--gemfile is mutually exclusive with --use-gem and --omit-gem") exit(1) end if gemfile_path || (default_to_bundler && override_use_gems.empty? && override_omit_gems.empty?) bundler_setup(gemfile_path: gemfile_path) nil else load_gems end end # @private def load_gems updated_dependencies = updated_gem_dependencies updated_dependencies.each do |gem_name, version_requirements| next if gem_name == "minitest" gem gem_name, *version_requirements end gem "minitest", *updated_dependencies["minitest"] loaded_versions = {} ::Gem.loaded_specs.each_value do |spec| loaded_versions[spec.name] = spec.version.to_s if updated_dependencies.key?(spec.name) end loaded_versions end # @private def updated_gem_dependencies dependencies = gem_dependencies.dup override_use_gems.each do |spec| name, *versions = spec.strip.split(/\s*,\s*/) if name.to_s.empty? logger.error("Bad format for --use-gem: #{spec.inspect}") exit(1) end versions.delete_if(&:empty?) versions = ::Toys::Templates::Minitest::DEFAULT_GEM_VERSION_REQUIREMENTS[name] || [] if versions.empty? dependencies[name] = versions end override_omit_gems.each do |name| name = name.strip if name == "minitest" logger.warn("You cannot omit the minitest gem. Ignoring --omit-gem=minitest.") else dependencies.delete(name) end end dependencies end # @private def ruby_env case mt_compat when true { "MT_COMPAT" => "true" } when false { "MT_COMPAT" => nil } else {} end end # @private def ruby_script(loaded_gem_versions, found_tests) lines = [] if loaded_gem_versions loaded_gem_versions.each do |gem_name, gem_version| lines << "gem #{gem_name.inspect}, '= #{gem_version}'" end else lines << "require 'bundler/setup'" end loaded_gems = ::Gem.loaded_specs ["minitest", "minitest-mock", "minitest-focus", "minitest-rg"].each do |gem_name| lines << "require #{gem_name.tr('-', '/').inspect}" if loaded_gems.key?(gem_name) end lines << "require 'minitest/autorun'" lines.append(preload_code) if preload_code lines.concat(found_tests.map { |path| "load #{path.inspect}" }) lines << "" lines.join("\n") end # @private def expand_tests if !tests.empty? && !expand_globs tests.dup else all_matches = [] (tests.empty? ? files : tests).each do |pattern| matches = ::Dir.glob(pattern) # Warn per-pattern only from globs supplied on the command line. # It's generally not actionable if ndividual globs from the # static configuration don't match. That said, in this case we # warn if no files at all match, and thus no tests are run. logger.warn("Glob #{pattern.inspect} did not match anything") if matches.empty? && !tests.empty? all_matches.concat(matches) end logger.warn("No test files matched any of the configured patterns") if all_matches.empty? && tests.empty? all_matches.uniq end end # @private def validate_tests(found_tests) ok = true found_tests.each do |path| if ::File.file?(path) && ::File.readable?(path) logger.info("Reading test: #{path}") else logger.error("Unable to load test: #{path}") ok = false end end exit(1) unless ok end # @private def ruby_args(script_path) args = [] effective_libs = override_libs || libs args << "-I#{effective_libs.join(::File::PATH_SEPARATOR)}" unless effective_libs.empty? args << "-w" if warnings args << script_path args << "--seed" << seed if seed vv = verbosity vv += 1 if template_verbose args << "--verbose" if vv.positive? args << "--name" << include_name if include_name args << "--exclude" << exclude_name if exclude_name args end end end private def update_version_spec(gem_name, version_requirements) version_requirements ||= true if gem_name == "minitest" if version_requirements @gem_dependencies[gem_name] = if version_requirements == true DEFAULT_GEM_VERSION_REQUIREMENTS[gem_name] || [] else Array(version_requirements).map(&:to_s) end else @gem_dependencies.delete(gem_name) nil end end end end end toys-0.21.0/lib/toys/templates/gem_build.rb0000644000004100000410000002071315164300675020650 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template for tools that build, install, and release gems # class GemBuild include Template ## # Default tool name. # @return [String] # DEFAULT_TOOL_NAME = "build" ## # Default output flags. If `output_flags` is set to `true`, this is the # value used. # @return [Array] # DEFAULT_OUTPUT_FLAGS = ["-o", "--output"].freeze ## # Default remote for pushing tags. # @return [String] # DEFAULT_PUSH_REMOTE = "origin" ## # Create the template settings for the GemBuild template. # # @param name [String] Name of the tool to create. Defaults to # {DEFAULT_TOOL_NAME}. # @param gem_name [String] Name of the gem to build. If not provided, # searches the context and current directories and uses the first # gemspec file it finds. # @param output [String] Path to the gem package to generate. Optional. # If not provided, defaults to a file name based on the gem name and # version, under "pkg" in the current directory. # @param output_flags [Array,true] Provide flags on the tool that # set the output path. Optional. If not provided, no flags are # created. You may set this to an array of flags (e.g. `["-o"]`) or # set to `true` to choose {DEFAULT_OUTPUT_FLAGS}. # @param push_gem [Boolean] If true, pushes the built gem to rubygems. # @param install_gem [Boolean] If true, installs the built gem locally. # @param tag [Boolean] If true, tags the git repo with the gem version. # @param push_tag [Boolean,String] If truthy, pushes the new tag to # a git remote. You may specify which remote by setting the value to # a string. Otherwise, if the value is simply `true`, the "origin" # remote is used by default. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(name: nil, gem_name: nil, output: nil, output_flags: nil, push_gem: false, install_gem: false, tag: false, push_tag: false, context_directory: nil) @name = name @gem_name = gem_name @output = output @output_flags = output_flags @push_gem = push_gem @install_gem = install_gem @tag = tag @push_tag = push_tag @context_directory = context_directory end ## # Name of the tool to create. # # @param value [String] # @return [String] # attr_writer :name ## # Name of the gem to build. If `nil`, searches the context and current # directories and uses the first gemspec file it finds. # # @param value [String,nil] # @return [String,nil] # attr_writer :gem_name ## # Path to the gem package to generate. If `nil`, defaults to a file name # based on the gem name and version, under "pkg" in the current directory. # # @param value [String,nil] # @return [String,nil] # attr_writer :output ## # Flags that set the output path on the generated tool. If `nil`, no # flags are generated. If set to `true`, {DEFAULT_OUTPUT_FLAGS} is used. # # @param value [Array,true,nil] # @return [Array,true,nil] # attr_writer :output_flags ## # Whether the tool should push the gem to Rubygems. # # @param value [Boolean] # @return [Boolean] # attr_writer :push_gem ## # Whether the tool should install the built gen locally. # # @param value [Boolean] # @return [Boolean] # attr_writer :install_gem ## # Whether to tag the git repo with the gem version. # # @param value [Boolean] # @return [Boolean] # attr_writer :tag ## # Whether to push the new tag to a git remote. This may be set to the # name of the remote as a string, to `true` to use {DEFAULT_PUSH_REMOTE} # by default, or to `false` to disable pushing. # # @param value [Boolean,String] # @return [Boolean,String] # attr_writer :push_tag ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # @private # attr_reader :output ## # @private # attr_reader :push_gem ## # @private # attr_reader :install_gem ## # @private # attr_reader :tag ## # @private # attr_reader :context_directory ## # @private # def name @name || DEFAULT_TOOL_NAME end ## # @private # def gem_name(context_dir = nil) return @gem_name if @gem_name candidates = if context_dir ::Dir.glob("*.gemspec", base: context_dir) else ::Dir.glob("*.gemspec") end if candidates.empty? raise ToolDefinitionError, "Could not find a gemspec" end ::File.basename(candidates.first, ".gemspec") end ## # @private # def output_flags @output_flags == true ? DEFAULT_OUTPUT_FLAGS : Array(@output_flags) end ## # @private # def push_tag @push_tag == true ? DEFAULT_PUSH_REMOTE : @push_tag end ## # @private # def task_names names = [] names << "Install" if @install_gem names << "Release" if @push_gem names.empty? ? "Build" : names.join(" and ") end on_expand do |template| tool(template.name) do set_context_directory template.context_directory if template.context_directory desc "#{template.task_names} the gem: #{template.gem_name(context_directory)}" flag :yes, "-y", "--yes", desc: "Do not ask for interactive confirmation" if template.output_flags.empty? static :output, template.output else flag :output do flags(template.output_flags.map { |f| "#{f} VAL" }) desc "output gem with the given filename" default template.output complete_values :file_system end end static :gem_name, template.gem_name(context_directory || ::Dir.getwd) static :install_gem, template.install_gem static :push_gem, template.push_gem static :tag, template.tag static :push_tag, template.push_tag include :exec, exit_on_nonzero_status: true include :fileutils include :terminal # @private def run # rubocop:disable all require "rubygems" require "rubygems/package" ::Dir.chdir(context_directory || ::Dir.getwd) do gemspec = ::Gem::Specification.load("#{gem_name}.gemspec") ::Gem::Package.build(gemspec) version = gemspec.version archive_name = "#{gem_name}-#{version}.gem" archive_path = output || "pkg/#{archive_name}" if archive_name != archive_path mkdir_p(::File.dirname(archive_path)) mv(archive_name, archive_path) end if install_gem exit(1) unless yes || confirm("Install #{gem_name} #{version}? ", default: true) exec ["gem", "install", archive_path] end if push_gem if ::File.directory?(".git") && capture("git status -s").strip != "" logger.error "Cannot push the gem when there are uncommited changes" exit(1) end exit(1) unless yes || confirm("Release #{gem_name} #{version}? ", default: true) exec(["gem", "push", archive_path]) if tag exec(["git", "tag", "v#{version}"]) if push_tag exec(["git", "push", push_tag, "v#{version}"]) end end end end end end end end end end toys-0.21.0/lib/toys/templates/yardoc.rb0000644000004100000410000003765615164300675020220 0ustar www-datawww-data# frozen_string_literal: true module Toys module Templates ## # A template that generates yardoc tools. # class Yardoc include Template ## # Default version requirements for the yard gem. # @return [String] # DEFAULT_GEM_VERSION_REQUIREMENTS = ["~> 0.9"].freeze ## # Default tool name # @return [String] # DEFAULT_TOOL_NAME = "yardoc" ## # Default file globs # @return [Array] # DEFAULT_FILES = ["lib/**/*.rb"].freeze ## # Default output directory # @return [String] # DEFAULT_OUTPUT_DIR = "doc" ## # Create the template settings for the Yardoc template. # # @param name [String] Name of the tool to create. Defaults to # {DEFAULT_TOOL_NAME}. # @param gem_version [String,Array] Version requirements for # the yard gem. Defaults to {DEFAULT_GEM_VERSION_REQUIREMENTS}. # @param files [Array] An array of globs indicating the files # to document. Defaults to {DEFAULT_FILES}. # @param generate_output [Boolean] Whether to generate output. Setting to # false causes yardoc to emit warnings/errors but not generate html. # Defaults to true. # @param generate_output_flag [Boolean] Whether to create a flag # `--[no-]output` that can control whether output is generated. # Defaults to false. # @param output_dir [String,nil] Output directory. Defaults to # {DEFAULT_OUTPUT_DIR}. # @param fail_on_warning [Boolean] Whether the tool should return a # nonzero error code if any warnings happen. Defaults to false. # @param fail_on_undocumented_objects [Boolean] Whether the tool should # return a nonzero error code if any objects remain undocumented. # Defaults to false. # @param show_public [Boolean] Show public methods. Defaults to true. # @param show_protected [Boolean] Show protected methods. Defaults to # false. # @param show_private [Boolean] Show private methods. Defaults to false. # @param hide_private_tag [Boolean] Hide methods with the `@private` tag. # Defaults to false. # @param readme [String,nil] Name of the readme file used as the title # page. If not provided, YARD will choose a default. # @param markup [String,nil] Markup style used in documentation. If not # provided, YARD will choose a default, likely "rdoc". # @param template [String,nil] Template to use. If not provided, YARD # will choose a default. # @param template_path [String,nil] The optional template path to look # for templates in. # @param format [String,nil] The output format for the template. If not # provided, YARD will choose a default, likely "html". # @param options [Array] Additional options passed to YARD # @param stats_options [Array] Additional stats options passed to # YARD # @param bundler [Boolean,Hash] If `false` (the default), bundler is not # enabled for this tool. If `true` or a Hash of options, bundler is # enabled. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on available options. # @param context_directory [String] A custom context directory to use # when executing this tool. # def initialize(name: nil, gem_version: nil, files: nil, generate_output: true, generate_output_flag: false, output_dir: nil, fail_on_warning: false, fail_on_undocumented_objects: false, show_public: true, show_protected: false, show_private: false, hide_private_tag: false, readme: nil, markup: nil, template: nil, template_path: nil, format: nil, options: [], stats_options: [], bundler: false, context_directory: nil) @name = name @gem_version = gem_version @files = files @generate_output = generate_output @generate_output_flag = generate_output_flag @output_dir = output_dir @fail_on_warning = fail_on_warning @fail_on_undocumented_objects = fail_on_undocumented_objects @show_public = show_public @show_protected = show_protected @show_private = show_private @hide_private_tag = hide_private_tag @readme = readme @markup = markup @template = template @template_path = template_path @format = format @options = options @stats_options = stats_options @bundler = bundler @context_directory = context_directory end ## # Name of the tool to create. # # @param value [String] # @return [String] # attr_writer :name ## # Version requirements for the rdoc gem. # If set to `nil`, uses the bundled version if bundler is enabled, or # defaults to {DEFAULT_GEM_VERSION_REQUIREMENTS} if bundler is not # enabled. # # @param value [String,Array,nil] # @return [String,Array,nil] # attr_writer :gem_version ## # An array of globs indicating which files to document. # # @param value [Array] # @return [Array] # attr_writer :files ## # Whether to generate output. Setting to false causes yardoc to emit # warnings/errors but not generate html. # # @param value [Boolean] # @return [Boolean] # attr_writer :generate_output ## # Whether to create a flag `--[no-]output` that can control whether # output is generated. # # @param value [Boolean] # @return [Boolean] # attr_writer :generate_output_flag ## # Name of directory to receive html output files. # If set to `nil`, defaults to {DEFAULT_OUTPUT_DIR}. # # @param value [String,nil] # @return [String,nil] # attr_writer :output_dir ## # Whether the tool should return a nonzero error code if any warnings # happen. # # @param value [Boolean] # @return [Boolean] # attr_writer :fail_on_warning ## # Whether the tool should return a nonzero error code if any objects # remain undocumented. # # @param value [Boolean] # @return [Boolean] # attr_writer :fail_on_undocumented_objects ## # Whether to document public methods. # # @param value [Boolean] # @return [Boolean] # attr_writer :show_public ## # Whether to document protected methods. # # @param value [Boolean] # @return [Boolean] # attr_writer :show_protected ## # Whether to document private methods. # # @param value [Boolean] # @return [Boolean] # attr_writer :show_private ## # Whether to hide methods with the `@private` tag. # # @param value [Boolean] # @return [Boolean] # attr_writer :hide_private_tag ## # Name of the readme file used as the title page. # If set to `nil`, YARD will choose a default. # # @param value [String,nil] # @return [String,nil] # attr_writer :readme ## # Markup style used in documentation. # If set to `nil`, YARD will choose a default, likely "rdoc". # # @param value [String,nil] # @return [String,nil] # attr_writer :markup ## # Template to use. # If set to `nil`, YARD will choose a default. # # @param value [String,nil] # @return [String,nil] # attr_writer :template ## # Directory path to look for templates in. # If set to `nil`, no additional template lookup paths will be used. # # @param value [String,nil] # @return [String,nil] # attr_writer :template_path ## # Output format for the template. # If set to `nil`, YARD will choose a default, likely "html". # # @param value [String,nil] # @return [String,nil] # attr_writer :format ## # Additional options to pass to YARD # # @param value [Array] # @return [Array] # attr_writer :options ## # Additional stats options to pass to YARD # # @param value [Array] # @return [Array] # attr_writer :stats_options ## # Custom context directory for this tool. # # @param value [String] # @return [String] # attr_writer :context_directory ## # Set the bundler state and options for this tool. # # Pass `false` to disable bundler. Pass `true` or a hash of options to # enable bundler. See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param value [Boolean,Hash] # @return [Boolean,Hash] # attr_writer :bundler ## # Activate bundler for this tool. # # See the documentation for the # [bundler mixin](https://dazuma.github.io/toys/gems/toys-core/latest/Toys/StandardMixins/Bundler) # for information on the options that can be passed. # # @param opts [keywords] Options for bundler # @return [self] # def use_bundler(**opts) @bundler = opts self end ## # @private # attr_reader :generate_output ## # @private # attr_reader :generate_output_flag ## # @private # attr_reader :fail_on_warning ## # @private # attr_reader :fail_on_undocumented_objects ## # @private # attr_reader :show_public ## # @private # attr_reader :show_protected ## # @private # attr_reader :show_private ## # @private # attr_reader :hide_private_tag ## # @private # attr_reader :readme ## # @private # attr_reader :markup ## # @private # attr_reader :template ## # @private # attr_reader :template_path ## # @private # attr_reader :format ## # @private # attr_reader :context_directory ## # @private # def name @name || DEFAULT_TOOL_NAME end ## # @private # def gem_version return Array(@gem_version) if @gem_version @bundler ? [] : DEFAULT_GEM_VERSION_REQUIREMENTS end ## # @private # def files @files ? Array(@files) : DEFAULT_FILES end ## # @private # def output_dir @output_dir || DEFAULT_OUTPUT_DIR end ## # @private # def options Array(@options) end ## # @private # def stats_options Array(@stats_options) end ## # @private # def bundler_settings if @bundler && !@bundler.is_a?(::Hash) {} else @bundler end end on_expand do |template| tool(template.name) do desc "Run yardoc on the current project." set_context_directory template.context_directory if template.context_directory if template.generate_output_flag flag :generate_output, "--[no-]output", default: template.generate_output, desc: "Whether to generate output" else static :generate_output, template.generate_output end static :gem_version, template.gem_version static :template_files, template.files static :run_options, template.options.dup static :stats_options, template.stats_options.dup static :fail_on_undocumented_objects, template.fail_on_undocumented_objects static :fail_on_warning, template.fail_on_warning static :output_dir, template.output_dir static :show_public, template.show_public static :show_protected, template.show_protected static :show_private, template.show_private static :hide_private_tag, template.hide_private_tag static :readme, template.readme static :markup, template.markup static :yard_template, template.template static :template_path, template.template_path static :yard_format, template.format include :exec include :terminal include :gems bundler_settings = template.bundler_settings include :bundler, **bundler_settings if bundler_settings # @private def run # rubocop:disable all gem "yard", *gem_version ::Dir.chdir(context_directory || ::Dir.getwd) do files = [] template_files.each do |pattern| files.concat(::Dir.glob(pattern)) end files.uniq! stats_options << "--list-undoc" if fail_on_undocumented_objects run_options << "--fail-on-warning" if fail_on_warning run_options << "--no-output" unless generate_output run_options << "--output-dir" << output_dir if output_dir run_options << "--no-public" unless show_public run_options << "--protected" if show_protected run_options << "--private" if show_private run_options << "--no-private" if hide_private_tag run_options << "-r" << readme if readme run_options << "-m" << markup if markup run_options << "-t" << yard_template if yard_template run_options << "-p" << template_path if template_path run_options << "-f" << yard_format if yard_format unless stats_options.empty? run_options << "--no-stats" stats_options << "--use-cache" end run_options.concat(files) code = <<~CODE gem 'yard', *#{gem_version.inspect} require 'yard' ::YARD::CLI::Yardoc.run(*#{run_options.inspect}) CODE result = exec_ruby(["-e", code]) if result.error? puts("Yardoc encountered errors", :red, :bold) unless verbosity.negative? exit(1) end unless stats_options.empty? code = <<~CODE gem 'yard', *#{gem_version.inspect} require 'yard' ::YARD::CLI::Stats.run(*#{stats_options.inspect}) CODE result = exec_ruby(["-e", code], out: :capture) puts result.captured_out if result.error? puts("Yardoc encountered errors", :red, :bold) unless verbosity.negative? exit(1) end exit_on_nonzero_status(result) if fail_on_undocumented_objects && result.captured_out =~ /Undocumented\sObjects:/ unless verbosity.negative? puts("Yardoc encountered undocumented objects", :red, :bold) end exit(1) end end end end end end end end end toys-0.21.0/lib/toys/testing.rb0000644000004100000410000002272715164300675016407 0ustar www-datawww-data# frozen_string_literal: true require "shellwords" module Toys ## # Helpers for writing tool tests. # # EXPERIMENTAL: Interfaces are subject to change. # module Testing ## # Returns the Toys CLI for this test class. By default, a single CLI and # Loader are shared by all tests in a given class (or _describe_ block). # # @return [Toys::CLI] # def toys_cli self.class.toys_cli end ## # Prepares the tool corresponding to the given command line, but instead of # running it, yields the execution context to the given block. This can be # used to test individual methods in a tool. # # By default, a single CLI is shared among the tests in each test class or # _describe_ block. Thus, tools are loaded only once, and the loader is # shared across the tests. If you need to isolate loading for a test, # create a separate CLI and pass it in using the `:cli` keyword argument. # # Note: this method runs the given block in-process. This means you can # test assertions within the block, but any input or output performed by # the tool's methods that you call, will manifest during your test. If this # is a problem, you might consider redirecting the standard streams when # calling this method, for example by using # [capture_subprocess_io](https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-capture_subprocess_io). # # @param cmd [String,Array] The command to execute. # @yieldparam tool [Toys::Context] The tool context. # @return [Object] The value returned from the block. # # @example # # Given the following tool: # # tool "hello" do # flag :shout # def run # puts message # end # def message # shout ? "HELLO" : "hello" # end # end # # # You can test the `message` method in isolation as follows: # # class MyTest < Minitest::Test # include Toys::Testing # def test_message_without_shout # toys_load_tool(["hello"]) do |tool| # assert_equal("hello", tool.message) # end # end # def test_message_with_shout # toys_load_tool(["hello", "--shout"]) do |tool| # assert_equal("HELLO", tool.message) # end # end # end # def toys_load_tool(cmd, cli: nil, &block) cli ||= toys_cli cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String) cli.load_tool(*cmd, &block) end ## # Runs the tool corresponding to the given command line, in-process, and # returns the result code. # # By default, a single CLI is shared among the tests in each test class or # _describe_ block. Thus, tools are loaded only once, and the loader is # shared across the tests. If you need to isolate loading for a test, # create a separate CLI and pass it in using the `:cli` keyword argument. # # Note: This method runs the tool in-process. This is often faster than # running it in a separate process with {#toys_exec_tool}, but it also # means any input or output performed by the tool, will manifest during # your test. If this is a problem, you might consider redirecting the # standard streams when calling this method, for example by using # [capture_subprocess_io](https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-capture_subprocess_io). # # @param cmd [String,Array] The command to execute. # @return [Integer] The integer result code (i.e. 0 for success). # # @example # # Given the following tool: # # tool "hello" do # flag :shout # def run # puts message # end # def message # shout ? "HELLO" : "hello" # end # end # # # You can test the tool's output as follows: # # class MyTest < Minitest::Test # include Toys::Testing # def test_output_without_shout # assert_output("hello\n") do # result = toys_run_tool(["hello"]) # assert_equal(0, result) # end # end # def test_with_shout # assert_output("HELLO\n") do # result = toys_run_tool(["hello", "--shout"]) # assert_equal(0, result) # end # end # end # def toys_run_tool(cmd, cli: nil) cli ||= toys_cli cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String) cli.run(*cmd) end ## # Runs the tool corresponding to the given command line, in a separate # forked process, and returns a {Toys::Exec::Result}. You can either # provide a block to control the process, or simply let it run and capture # its output. # # By default, a single CLI is shared among the tests in each test class or # _describe_ block. Thus, tools are loaded only once, and the loader is # shared across the tests. If you need to isolate loading for a test, # create a separate CLI and pass it in using the `:cli` keyword argument. # # All other keyword arguments are the same as those defined by the # {Toys::Utils::Exec} class. If a block is given, all streams are directed # to a {Toys::Utils::Exec::Controller} which is yielded to the block. If no # block is given, the output and error streams are captured and the input # stream is closed. # # This method uses "fork" to isolate the run of the tool. It will not work # on environments such as JRuby or Ruby on Windows that do not support # process forking. # # @param cmd [String,Array] The command to execute. # @param opts [keywords] The command options. # @yieldparam controller [Toys::Utils::Exec::Controller] A controller # for the subprocess streams. # @return [Toys::Utils::Exec::Result] The process result. # # @example # # Given the following tool: # # tool "hello" do # flag :shout # def run # puts message # end # def message # shout ? "HELLO" : "hello" # end # end # # # You can test the tool's output as follows: # # class MyTest < Minitest::Test # include Toys::Testing # def test_output_without_shout # result = toys_exec_tool(["hello"]) # assert_equal("hello\n", result.captured_out) # end # def test_with_shout # result = toys_exec_tool(["hello", "--shout"]) # assert_equal("HELLO\n", result.captured_out) # end # end # def toys_exec_tool(cmd, cli: nil, **opts, &block) cli ||= toys_cli cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String) opts = if block { out: :controller, err: :controller, in: :controller, }.merge(opts) else { out: :capture, err: :capture, in: :close, }.merge(opts) end cli.loader.lookup(cmd) tool_caller = proc { ::Kernel.exit(cli.run(*cmd)) } self.class.toys_exec.exec_proc(tool_caller, **opts, &block) end alias exec_tool toys_exec_tool @toys_mutex = ::Mutex.new ## # @private # def self.included(klass) klass.extend(ClassMethods) end ## # @private # def self.toys_mutex @toys_mutex end ## # @private # def self.toys_custom_paths(paths = :read) @toys_custom_paths = paths unless paths == :read @toys_custom_paths end ## # @private # def self.toys_include_builtins(value = :read) @toys_include_builtins = value unless value == :read @toys_include_builtins end @toys_custom_paths = nil @toys_include_builtins = true ## # Class methods added to a test class or describe block when # {Toys::Testing} is included. Generally, these are methods that configure # the load path for the CLI in scope for the block. # module ClassMethods ## # Configure the Toys CLI to load tools from the given paths, and ignore # the current directory and global paths. # # @param paths [String,Array] The paths to load from. # def toys_custom_paths(paths = :read) @toys_custom_paths = paths unless paths == :read return @toys_custom_paths if defined?(@toys_custom_paths) begin super rescue ::NoMethodError Testing.toys_custom_paths end end ## # Configure the Toys CLI to include or exclude builtins. Normally # builtins are included unless false is passed to this method. # # @param value [boolean] Whether to include builtins. # def toys_include_builtins(value = :read) @toys_include_builtins = value unless value == :read return @toys_include_builtins if defined?(@toys_include_builtins) begin super rescue ::NoMethodError Testing.toys_include_builtins end end ## # @private # def toys_cli Testing.toys_mutex.synchronize do @toys_cli ||= StandardCLI.new(custom_paths: toys_custom_paths, include_builtins: toys_include_builtins) end end ## # @private # def toys_exec Testing.toys_mutex.synchronize do require "toys/utils/exec" @toys_exec ||= Utils::Exec.new end end end end end toys-0.21.0/lib/toys/version.rb0000644000004100000410000000023315164300675016403 0ustar www-datawww-data# frozen_string_literal: true module Toys ## # Current version of the Toys command line executable. # @return [String] # VERSION = "0.21.0" end toys-0.21.0/lib/toys/standard_cli.rb0000644000004100000410000001656315164300675017362 0ustar www-datawww-data# frozen_string_literal: true module Toys ## # Subclass of `Toys::CLI` configured for the behavior of the standard Toys # executable. Specifically, this subclass: # # * Configures the standard names of files and directories, such as the # `.toys.rb` file for an "index" tool, and the `.data` and `.lib` directory # names. # * Configures default descriptions for the root tool. # * Configures a default error handler and logger that provide ANSI-colored # formatted output. # * Configures a set of middleware that implement online help, verbosity # flags, and other features. # * Provides a set of standard templates for typical project build and # maintenance scripts (suh as clean, test, and rubocop). # * Finds tool definitions in the standard Toys search path. # class StandardCLI < CLI ## # Standard toys configuration directory name. # @return [String] # CONFIG_DIR_NAME = ".toys" ## # Standard toys configuration file name. # @return [String] # CONFIG_FILE_NAME = ".toys.rb" ## # Standard index file name in a toys configuration. # @return [String] # INDEX_FILE_NAME = ".toys.rb" ## # Standard preload directory name in a toys configuration. # @return [String] # PRELOAD_DIR_NAME = ".preload" ## # Standard preload file name in a toys configuration. # @return [String] # PRELOAD_FILE_NAME = ".preload.rb" ## # Standard data directory name in a toys configuration. # @return [String] # DATA_DIR_NAME = ".data" ## # Standard lib directory name in a toys configuration. # @return [String] # LIB_DIR_NAME = ".lib" ## # Name of the standard toys executable. # @return [String] # EXECUTABLE_NAME = "toys" ## # Delimiter characters recognized. # @return [String] # EXTRA_DELIMITERS = ":." ## # Short description for the standard root tool. # @return [String] # DEFAULT_ROOT_DESC = "Your personal command line tool" ## # Help text for the standard root tool. # @return [String] # DEFAULT_ROOT_LONG_DESC = "Toys is your personal command line tool. You can write commands using a simple Ruby DSL," \ " and Toys will automatically organize them, parse arguments, and provide documentation." \ " Tools can be global or scoped to specific directories. You can also use Toys instead of" \ " Rake to provide build and maintenance scripts for your projects." \ " For detailed information, see https://dazuma.github.io/toys" ## # Short description for the version flag. # @return [String] # DEFAULT_VERSION_FLAG_DESC = "Show the version of Toys." ## # Name of the toys path environment variable. # @return [String] # TOYS_PATH_ENV = "TOYS_PATH" ## # Create a standard CLI, configured with the appropriate paths and # middleware. # # @param custom_paths [String,Array] Custom paths to use. If set, # the CLI uses only the given paths. If not, the CLI will search for # paths from the current directory and global paths. # @param include_builtins [boolean] Add the builtin tools. Default is true. # @param cur_dir [String,nil] Starting search directory for configs. # Defaults to the current working directory. # def initialize(custom_paths: nil, include_builtins: true, cur_dir: nil) require "toys/utils/standard_ui" ui = Toys::Utils::StandardUI.new super( executable_name: EXECUTABLE_NAME, config_dir_name: CONFIG_DIR_NAME, config_file_name: CONFIG_FILE_NAME, index_file_name: INDEX_FILE_NAME, preload_file_name: PRELOAD_FILE_NAME, preload_dir_name: PRELOAD_DIR_NAME, data_dir_name: DATA_DIR_NAME, lib_dir_name: LIB_DIR_NAME, extra_delimiters: EXTRA_DELIMITERS, middleware_stack: default_middleware_stack, template_lookup: default_template_lookup, **ui.cli_args ) if custom_paths Array(custom_paths).each { |path| add_config_path(path) } else add_current_directory_paths(cur_dir) end add_builtins if include_builtins end private ## # Add paths for builtin tools # def add_builtins builtins_path = ::File.join(::File.dirname(::File.dirname(__dir__)), "builtins") add_config_path(builtins_path, source_name: "(builtin tools)", context_directory: nil) self end ## # Add paths for the given current directory and its ancestors, plus the # global paths. # # @param cur_dir [String] The starting directory path, or nil to use the # current directory # @return [self] # def add_current_directory_paths(cur_dir) cur_dir = skip_toys_dir(cur_dir || ::Dir.pwd, CONFIG_DIR_NAME) global_dirs = default_global_dirs add_search_path_hierarchy(start: cur_dir, terminate: global_dirs) global_dirs.each { |path| add_search_path(path) } self end ## # Step out of any toys dir. # # @param dir [String] The starting path # @param toys_dir_name [String] The name of the toys directory to look for # @return [String] The final directory path # def skip_toys_dir(dir, toys_dir_name) cur_dir = dir loop do parent = ::File.dirname(dir) return cur_dir if parent == dir if ::File.basename(dir) == toys_dir_name cur_dir = dir = parent else dir = parent end end end ## # Returns the default set of global config directories. # # @return [Array] # def default_global_dirs paths = ::ENV[TOYS_PATH_ENV].to_s.split(::File::PATH_SEPARATOR) paths = [::Dir.home, "/etc"] if paths.empty? paths .compact .uniq .select { |path| ::File.directory?(path) && ::File.readable?(path) } .map { |path| ::File.realpath(::File.expand_path(path)) } end ## # Returns the middleware for the standard Toys CLI. # # @return [Array] # def default_middleware_stack [ Middleware.spec(:set_default_descriptions, default_root_desc: DEFAULT_ROOT_DESC, default_root_long_desc: DEFAULT_ROOT_LONG_DESC), Middleware.spec(:show_help, help_flags: true, usage_flags: true, list_flags: true, recursive_flags: true, search_flags: true, show_all_subtools_flags: true, default_recursive: true, allow_root_args: true, show_source_path: true, separate_sources: true, use_less: true, fallback_execution: true), Middleware.spec(:show_root_version, version_string: ::Toys::VERSION, version_flag_desc: DEFAULT_VERSION_FLAG_DESC), Middleware.spec(:handle_usage_errors), Middleware.spec(:add_verbosity_flags), ] end ## # Returns a ModuleLookup for the default templates. # # @return [Toys::ModuleLookup] # def default_template_lookup ModuleLookup.new.add_path("toys/templates") end end end toys-0.21.0/lib/toys.rb0000644000004100000410000000567515164300675014735 0ustar www-datawww-data# frozen_string_literal: true require "toys/version" # Add toys-core to the load path. The Toys debug scripts will set this # environment variable explicitly, but in production, we get it from rubygems. # We prepend to $LOAD_PATH directly rather than calling Kernel.gem, so that we # don't get clobbered in case someone sets up bundler later. unless ::ENV.key?("TOYS_CORE_LIB_PATH") path = ::File.expand_path("../../toys-core-#{::Toys::VERSION}/lib", __dir__) path = ::File.expand_path("../../toys-core/lib", __dir__) unless path && ::File.directory?(path) unless path && ::File.directory?(path) require "rubygems" dep = ::Gem::Dependency.new("toys-core", "= #{::Toys::VERSION}") path = dep.to_spec.full_require_paths.first end abort "Unable to find toys-core gem!" unless path && ::File.directory?(path) ::ENV.store("TOYS_CORE_LIB_PATH", path) end $LOAD_PATH.delete(::ENV["TOYS_CORE_LIB_PATH"]) $LOAD_PATH.unshift(::ENV["TOYS_CORE_LIB_PATH"]) require "toys-core" ## # Toys is a configurable command line tool. Write commands in config files # using a simple DSL, and Toys will provide the command line executable and # take care of all the details such as argument parsing, online help, and error # reporting. Toys is designed for software developers, IT professionals, and # other power users who want to write and organize scripts to automate their # workflows. It can also be used as a Rake replacement, providing a more # natural command line interface for your project's build tasks. # # This set of documentation includes classes from both Toys-Core, the # underlying command line framework, and the Toys executable itself. Most of # the actual classes you will likely need to look up are from Toys-Core. # # ## Common starting points # # * For information on the DSL used to write tools, start with # {Toys::DSL::Tool}. # * The base class for tool runtime (i.e. that defines the basic methods # available to a tool's implementation) is {Toys::Context}. # * For information on writing mixins, see {Toys::Mixin}. # * For information on writing templates, see {Toys::Template}. # * For information on writing acceptors, see {Toys::Acceptor}. # * For information on writing custom shell completions, see {Toys::Completion}. # * Standard mixins are defined under the {Toys::StandardMixins} module. # * Various utilities are defined under {Toys::Utils}. Some of these serve as # the implementations of corresponding mixins. # module Toys ## # Path to the Toys executable. # # @return [String] Absolute path to the executable # @return [nil] if the Toys executable is not running. # EXECUTABLE_PATH = ::ENV["TOYS_BIN_PATH"] ## # @private # LIB_PATH = __dir__ ## # Namespace for standard template classes. # # These templates are provided by Toys and can be expanded by name by passing # a symbol to {Toys::DSL::Tool#expand}. # module Templates; end end ::Toys.executable_path = ::Toys::EXECUTABLE_PATH require "toys/standard_cli" toys-0.21.0/share/0000755000004100000410000000000015164300675013731 5ustar www-datawww-datatoys-0.21.0/share/bash-completion-remove.sh0000644000004100000410000000013115164300675020637 0ustar www-datawww-dataif [[ $# -eq 0 ]]; then set -- "toys" fi for arg in "$@"; do complete -r "$arg" done toys-0.21.0/share/zsh-completion.sh0000644000004100000410000000115415164300675017241 0ustar www-datawww-data_toys_completion() { local -a finals=() partials=() local line in_partials=0 while IFS= read -r line; do if [[ -z "$line" ]]; then in_partials=1 elif (( in_partials )); then [[ -n "$line" ]] && partials+=("$line") else finals+=("$line") fi done < <(COMP_LINE="$BUFFER" COMP_POINT="$CURSOR" \ toys system zsh-completion eval 2>/dev/null) (( ${#finals[@]} )) && compadd -U -- "${finals[@]}" (( ${#partials[@]} )) && compadd -U -S '' -- "${partials[@]}" } if [[ $# -eq 0 ]]; then set -- "toys" fi for arg in "$@"; do compdef _toys_completion "$arg" done toys-0.21.0/share/zsh-completion-remove.sh0000644000004100000410000000013015164300675020525 0ustar www-datawww-dataif [[ $# -eq 0 ]]; then set -- "toys" fi for arg in "$@"; do compdef -d "$arg" done toys-0.21.0/share/bash-completion.sh0000644000004100000410000000020715164300675017350 0ustar www-datawww-dataif [[ $# -eq 0 ]]; then set -- "toys" fi for arg in "$@"; do complete -C "toys system bash-completion eval" -o nospace "$arg" done toys-0.21.0/.yardopts0000644000004100000410000000033615164300675014477 0ustar www-datawww-data--no-private --title=Toys --markup=markdown --markup-provider redcarpet --main=README.md ./core-docs/toys/**/*.rb ./core-docs/toys-core.rb ./lib/toys/**/*.rb ./lib/toys.rb - README.md LICENSE.md CHANGELOG.md docs/guide.md toys-0.21.0/builtins/0000755000004100000410000000000015164300675014460 5ustar www-datawww-datatoys-0.21.0/builtins/do.rb0000644000004100000410000000322015164300675015404 0ustar www-datawww-data# frozen_string_literal: true desc "Run multiple tools in order" long_desc \ "The \"toys do\" builtin provides a convenient interface for running multiple tools in" \ " sequence. Provide the tools to run as arguments, separated by a delimiter (which is" \ " the string \",\" by default). Toys will run them in order, stopping if any tool" \ " returns a nonzero exit code.", "", "Example: Suppose you have a \"rails build\" tool and a \"deploy\" tool. You could run them" \ " in order like this:", [" toys do rails build --staging , deploy --migrate"], "", "You may change the delimiter using the --delim flag. For example:", [" toys do --delim=/ rails build --staging / deploy --migrate"], "The --delim flag must appear first before the tools to run. Any flags that appear later in" \ " the command line will be passed to the tools themselves." flag :delim do flags "-d", "--delim=VALUE" default "," desc "Set the delimiter" long_desc "Sets the delimiter that separates tool invocations. The default value is \",\"." end remaining_args :commands do complete do |context| commands = context.arg_parser.data[:commands] last_command = commands.inject([]) { |acc, arg| arg == "," ? [] : (acc << arg) } new_context = context.with(previous_words: last_command, disable_flags: commands.empty?) new_context.tool.completion.call(new_context) end desc "A series of tools to run, separated by the delimiter" end enforce_flags_before_args def run commands .chunk { |arg| arg == delim ? :_separator : true } .each do |_, action| code = cli.run(action) exit(code) unless code.zero? end end toys-0.21.0/builtins/system/0000755000004100000410000000000015164300675016004 5ustar www-datawww-datatoys-0.21.0/builtins/system/zsh-completion.rb0000644000004100000410000000711715164300675021312 0ustar www-datawww-data# frozen_string_literal: true desc "Zsh tab completion for Toys" long_desc \ "Tools that manage tab completion for Toys in the zsh shell.", "", "To install tab completion for Toys, execute the following line in a zsh shell, or" \ " include it in an init file such as your .zshrc (after the line that calls compinit):", [" $(toys system zsh-completion install)"], "", "To remove tab completion, execute:", [" $(toys system zsh-completion remove)"], "", "It is also possible to install completions for different executable names if you have" \ " aliases for Toys. See the help for the \"install\" and \"remove\" tools for details.", "", "The \"eval\" tool is the actual completion command invoked by zsh when it needs to" \ " complete a toys command line. You shouldn't need to invoke it directly." tool "install" do desc "Install zsh tab completion" long_desc \ "Outputs a command to set up Toys tab completion in the current zsh shell.", "", "To use, execute the following line in a zsh shell, or include it in an init file" \ " such as your .zshrc (after the line that calls compinit):", [" $(toys system zsh-completion install)"], "", "This will associate the toys tab completion logic with the `toys` executable by default." \ " If you have aliases for the toys executable, pass them as arguments. e.g.", [" $(toys system zsh-completion install my-toys-alias another-alias)"] remaining_args :executable_names, desc: "Names of executables for which to set up tab completion" \ " (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})" def run require "shellwords" path = ::File.join(::File.dirname(::File.dirname(__dir__)), "share", "zsh-completion.sh") exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names puts ::Shellwords.join(["source", path] + exes) end end tool "remove" do desc "Remove zsh tab completion" long_desc \ "Outputs a command to remove Toys tab completion from the current zsh shell.", "", "To use, execute the following line in a zsh shell:", [" $(toys system zsh-completion remove)"], "", "If you have other names or aliases for the toys executable, pass them as arguments. e.g.", [" $(toys system zsh-completion remove my-toys-alias another-alias)"] remaining_args :executable_names, desc: "Names of executables for which to remove tab completion" \ " (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})" def run require "shellwords" path = ::File.join(::File.dirname(::File.dirname(__dir__)), "share", "zsh-completion-remove.sh") exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names puts ::Shellwords.join(["source", path] + exes) end end tool "eval" do desc "Tab completion command (executed by zsh)" long_desc \ "Completion command invoked by zsh to complete a toys command line. Generally you do not" \ " need to invoke this directly. It reads the command line context from the COMP_LINE" \ " and COMP_POINT environment variables, and outputs completion candidates to stdout in" \ " two sections separated by a blank line: final completions first, then partial" \ " completions (such as directory paths)." disable_argument_parsing def run require "toys/utils/completion_engine" result = ::Toys::Utils::CompletionEngine::Zsh.new(cli).run if result > 1 logger.fatal("This tool must be invoked as a zsh completion command.") end exit(result) end end toys-0.21.0/builtins/system/bash-completion.rb0000644000004100000410000000661715164300675021427 0ustar www-datawww-data# frozen_string_literal: true desc "Bash tab completion for Toys" long_desc \ "Tools that manage tab completion for Toys in the bash shell.", "", "To install tab completion for Toys, execute the following line in a bash shell, or" \ " include it in an init file such as your .bashrc:", [" $(toys system bash-completion install)"], "", "To remove tab completion, execute:", [" $(toys system bash-completion remove)"], "", "It is also possible to install completions for different executable names if you have" \ " aliases for Toys. See the help for the \"install\" and \"remove\" tools for details.", "", "The \"eval\" tool is the actual completion command invoked by bash when it needs to" \ " complete a toys command line. You shouldn't need to invoke it directly." tool "eval" do desc "Tab completion command (executed by bash)" long_desc \ "Completion command invoked by bash to compete a toys command line. Generally you do not" \ " need to invoke this directly. It reads the command line context from the COMP_LINE" \ " and COMP_POINT environment variables, and outputs completion candidates to stdout." disable_argument_parsing def run require "toys/utils/completion_engine" result = ::Toys::Utils::CompletionEngine::Bash.new(cli).run if result > 1 logger.fatal("This tool must be invoked as a bash completion command.") end exit(result) end end tool "install" do desc "Install bash tab completion" long_desc \ "Outputs a command to set up Toys tab completion in the current bash shell.", "", "To use, execute the following line in a bash shell, or include it in an init file" \ " such as your .bashrc:", [" $(toys system bash-completion install)"], "", "This will associate the toys tab completion logic with the `toys` executable by default." \ " If you have aliases for the toys executable, pass them as arguments. e.g.", [" $(toys system bash-completion install my-toys-alias another-alias)"] remaining_args :executable_names, desc: "Names of executables for which to set up tab completion" \ " (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})" def run require "shellwords" path = ::File.join(::File.dirname(::File.dirname(__dir__)), "share", "bash-completion.sh") exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names puts ::Shellwords.join(["source", path] + exes) end end tool "remove" do desc "Remove bash tab completion" long_desc \ "Outputs a command to remove Toys tab completion from the current bash shell.", "", "To use, execute the following line in a bash shell:", [" $(toys system bash-completion remove)"], "", "If you have other names or aliases for the toys executable, pass them as arguments. e.g.", [" $(toys system bash-completion remove my-toys-alias another-alias)"] remaining_args :executable_names, desc: "Names of executables for which to set up tab completion" \ " (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})" def run require "shellwords" path = ::File.join(::File.dirname(::File.dirname(__dir__)), "share", "bash-completion-remove.sh") exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names puts ::Shellwords.join(["source", path] + exes) end end toys-0.21.0/builtins/system/git-cache.rb0000644000004100000410000001734615164300675020170 0ustar www-datawww-data# frozen_string_literal: true desc "Git-cache management tools" long_desc \ "Tools that manage the git cache.", "", "The Toys::Utils::GitCache class manages a cache of files from remote git repoistories." \ " It is used when loading tools from git, and can also be used directly by tools to access" \ " files from a remote git repository such as from GitHub.", "", "The tools under the `system git-cache` namespace manage the contents of the git-cache," \ " including querying the cache, getting information about cache status, and removing old data." tool "list" do desc "Output a list of the git repositories in the cache." long_desc \ "Outputs a list of the git remotes for the repositories in the cache, in YAML format, to" \ " the standard output stream." flag :cache_dir, "--cache-dir=PATH" do desc "The base directory for the cache. Optional. Defaults to the standard cache directory." end include "output-tools" def run require "toys/utils/git_cache" git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir) output = { "cache_dir" => git_cache.cache_dir, "remotes" => git_cache.remotes, } puts(generate_output(output)) end end tool "show" do desc "Output information about the specified git repo in the cache." long_desc \ "Outputs information about the git repo specified by the given remote, in YAML format, to" \ " the standard output stream." required_arg :remote, desc: "The git remote identifying the repo. Required." flag :cache_dir, "--cache-dir=PATH" do desc "The base directory for the cache. Optional. Defaults to the standard cache directory." end include "output-tools" def run require "toys/utils/git_cache" git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir) info = git_cache.repo_info(remote) if info.nil? logger.fatal("Unknown remote: #{remote}") exit(1) end puts(generate_output(info.to_h)) end end tool "get" do desc "Get files from the git cache, loading from the repo if necessary." long_desc \ "Get files from the git cache, loading from the repo if necessary, and output the path to" \ " the files to the standard output stream.", "", "The resulting files are either served from a shared source directory, or copied into aa" \ " directory specified by the `--into=` flag. If you use the shared directory, do not" \ " modify the files or directory structure, or other callers will see your modifications." required_arg :remote, desc: "The git remote identifying the repo. Required." flag :cache_dir, "--cache-dir=PATH" do desc "The base directory for the cache. Optional. Defaults to the standard cache directory." end flag :path, "--path=PATH" do desc "A path to a specific file or directory in the repository. Optional. Defaults to the" \ " entire repository." end flag :commit, "--commit=REF", "--ref=REF" do desc "The commit, which may be a SHA, branch, tag, or HEAD (the default). Optional." end flag :update do desc "Update refs, such as branches, from the remote repo." end flag :into, "--into=DIR" do desc "Copy files into the given directory rather than returning a shared directory." end def run require "toys/utils/git_cache" git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir) out = git_cache.get(remote, path: path, commit: commit, into: into, update: update) puts(out) rescue ::Toys::Utils::GitCache::Error => e logger.fatal(e.message) exit(1) end end tool "remove" do desc "Remove the given repositories from the cache." long_desc \ "Remove the given repositories, including local clones and all shared source directories," \ " from the cache. The next time any of these repositories is requested, it will be" \ " reloaded from the remote repository from scratch.", "", "You can remove specific repositories by providing their remotes as arguments, or remove all" \ " repositories from the cache by specifying the `--all` flag.", "", "Be careful not to clear repos that are currently in use by other processes. This command" \ " may delete files that are in use by other git-cache clients." remaining_args :remotes, desc: "The git remote(s) identifying the repo(s) to remove." flag :cache_dir, "--cache-dir=PATH" do desc "The base directory for the cache. Optional. Defaults to the standard cache directory." end flag :all do desc "Remove all repositories. Required unless specific remotes are provided." end include "output-tools" def run require "toys/utils/git_cache" if remotes.empty? == !all logger.fatal("You must specify at least one remote to clear, or --all to clear all remotes.") exit(2) end git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir) removed = git_cache.remove_repos(all ? :all : remotes) output = { "removed" => removed, } puts(generate_output(output)) end end tool "remove-refs" do desc "Removes records of the given refs from the cache." long_desc \ "Removes records of the given refs (i.e. branches, tags, or HEAD) from the cache for the" \ " given repo. The next time any of these refs are requested, they will be pulled fresh" \ " from the remote repo.", "", "You must provide either the `--all` flag to remove all refs, or at least one `--ref=` flag" \ " to remove specific refs.", "", "Outputs a list of the refs actually removed, in YAML format, to the standard output stream." required_arg :remote, desc: "The git remote identifying the repo. Required." exactly_one_required do flag :refs, "--ref=REF", handler: :push do desc "Remove a specific ref." end flag :all do desc "Remove all refs." end end flag :cache_dir, "--cache-dir=PATH" do desc "The base directory for the cache. Optional. Defaults to the standard cache directory." end include "output-tools" def run require "toys/utils/git_cache" git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir) removed = git_cache.remove_refs(remote, refs: refs) if removed.nil? logger.fatal("Unknown remote: #{remote}") exit(1) end output = { "remote" => remote, "removed_refs" => removed.map(&:to_h), } puts(generate_output(output)) end end tool "remove-sources" do desc "Removes shared sources from the cache." long_desc \ "Removes the specified shared sources from the cache for the given repository. The next time" \ " these files are retrieved, they will be recopied from the repository.", "", "You must provide either the `--all` flag to remove all sources associated with the repo," \ " or at least one `--commit=` flag to remove sources for specific commits.", "", "Outputs a list of the sources actually removed, in YAML format, to the standard output stream." required_arg :remote, desc: "The git remote identifying the repo. Required." exactly_one_required do flag :commits, "--commit=REF", handler: :push do desc "Remove sources associated with a specific commit." end flag :all do desc "Remove all sources." end end flag :cache_dir, "--cache-dir=PATH" do desc "The base directory for the cache. Optional. Defaults to the standard cache directory." end include "output-tools" def run require "toys/utils/git_cache" git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir) removed = git_cache.remove_sources(remote, commits: commits) if removed.nil? logger.fatal("Unknown remote: #{remote}") exit(1) end output = { "remote" => remote, "removed_sources" => removed.map(&:to_h), } puts(generate_output(output)) end end toys-0.21.0/builtins/system/update.rb0000644000004100000410000000347015164300675017617 0ustar www-datawww-data# frozen_string_literal: true desc "Update Toys if a newer version is available" long_desc "Checks rubygems for a newer version of Toys. If one is available, downloads" \ " and installs it." flag :yes, "-y", "--yes", desc: "Do not ask for interactive confirmation" include :exec include :terminal def run require "rubygems" configure_exec(exit_on_nonzero_status: true) version_info = spinner(leading_text: "Checking rubygems for the latest release... ", final_text: "Done.\n") do capture(["gem", "list", "-q", "-r", "-e", "toys"]) end if version_info =~ /toys\s\((.+)\)/ latest_version = ::Gem::Version.new(::Regexp.last_match(1)) cur_version = ::Gem::Version.new(::Toys::VERSION) if latest_version > cur_version prompt = "Update Toys from #{cur_version} to #{latest_version}? " exit(1) unless yes || confirm(prompt, default: true) result = spinner(leading_text: "Installing Toys version #{latest_version}... ", final_text: "Done.\n") do exec(["gem", "install", "toys", "--version", latest_version.to_s], out: :capture, err: :capture) end if result.error? puts(result.captured_out + result.captured_err) puts("Toys failed to install version #{latest_version}", :red, :bold) exit(1) end puts("Toys successfully installed version #{latest_version}", :green, :bold) elsif latest_version < cur_version puts("Toys is already at experimental version #{cur_version}, which is later than" \ " the latest released version #{latest_version}", :yellow, :bold) else puts("Toys is already at the latest version: #{latest_version}", :green, :bold) end else puts("Could not get latest Toys version", :red, :bold) exit(1) end end toys-0.21.0/builtins/system/test.rb0000644000004100000410000001607615164300675017322 0ustar www-datawww-data# frozen_string_literal: true desc "Run tool tests" flag :directory, "-d", "--directory PATH", desc: "Run tests from the given directory" flag :seed, "-s", "--seed SEED", desc: "Sets random seed." flag :warnings, "-w", "--[no-]warnings", default: true, desc: "Turn on Ruby warnings (defaults to true)" flag :include_name, "-n", "-i", "--name PATTERN", "--include PATTERN", desc: "Filter run on /regexp/ or string." flag :exclude_name, "-e", "-x", "--exclude PATTERN", desc: "Exclude /regexp/ or string from run." flag :recursive, "--[no-]recursive", default: true, desc: "Recursively test subtools (default is true)" flag :tool, "-t TOOL", "--tool TOOL", default: "", desc: "Run tests only for tools under the given path" flag :minitest_version, "--minitest-version=VERSION", "--minitest=VERSION", desc: "Set the minitest version requirement during runs where no Gemfile is present" flag :minitest_focus, "--minitest-focus[=VERSION]", desc: "Make minitest-focus available during runs where no Gemfile is present" flag :minitest_mock, "--minitest-mock[=VERSION]", desc: "Make minitest-mock available during runs where no Gemfile is present" flag :minitest_rg, "--minitest-rg[=VERSION]", desc: "Make minitest-rg available during runs where no Gemfile is present" flag :use_gems, "--use-gem=SPEC", default: [], handler: :push, desc: "Install the given gem with version requirements during runs where no Gemfile is present" flag :minitest_compat, "--[no-]minitest-compat", "--[no-]mt-compat", desc: "Set MT_COMPAT to retain compatibility with certain old plugins" flag :expand_globs, "--globs", "--expand-globs", desc: "Expand globs in the test file arguments." remaining_args :tests, complete: :file_system, desc: "Paths to the tests to run (defaults to all tests)" include :exec include :gems include :terminal def run setup_mt_compat final_code = 0 jobs = determine_jobs if jobs.empty? puts "WARNING: No test files found", :yellow, :bold exit end jobs.each do |job| puts "Running #{job.name}", :bold result = run_job(job) if result.success? puts "Succeeded: #{job.name}", :green, :bold else puts "Failed: #{job.name} (code=#{result.effective_code})", :red, :bold final_code = 1 end end exit(final_code) end def setup_mt_compat case minitest_compat when true ENV["MT_COMPAT"] = "true" when false ENV.delete("MT_COMPAT") end end Job = ::Struct.new(:name, :globs, :tests, :gemfile) def run_job(job) args = ["system", "test", "_internal"] args.concat(verbosity_flags) args << "--seed" << seed if seed args << "--no-warnings" unless warnings args << "--name" << include_name if include_name args << "--exclude" << exclude_name if exclude_name if job.gemfile args << "--gemfile-path" << job.gemfile else add_gem_args(args) end args << "--globs" if job.globs args << "--preload-code" << preload_code args.concat(Array(job.globs || job.tests)) exec_separate_tool(args) end def preload_code <<~RUBY require "toys" require "toys/testing" Toys::Testing.toys_custom_paths(#{base_dir.inspect}) Toys::Testing.toys_include_builtins(false) RUBY end def add_gem_args(args) use_gems_hash = use_gems.to_h do |spec| name, version = spec.strip.split(/\s*,\s*/, 2) [name, version] end use_gems_hash["minitest"] = minitest_version use_gems_hash["minitest-mock"] = minitest_mock if minitest_mock use_gems_hash["minitest-focus"] = minitest_focus if minitest_focus use_gems_hash["minitest-rg"] = minitest_rg if minitest_rg if ::ENV["TOYS_DEV"] == "true" args << "--libs" << ::Toys::CORE_LIB_PATH unless use_gems_hash["toys-core"] args << "--libs" << ::Toys::LIB_PATH unless use_gems_hash["toys"] else use_gems_hash["toys"] ||= ::Toys::VERSION end use_gems_hash.each do |name, versions| versions = nil if versions == true args << "--use-gem" << [name, versions].compact.join(",") end end def determine_jobs return determine_jobs_from_tests unless tests.empty? jobs = [] job = build_job_under(::File.join(tool_dir, ".test")) jobs << job if job if recursive ::Dir.glob("#{tool_dir}/*/**/.test").sort.each do |test_dir| job = build_job_under(test_dir) jobs << job if job end end jobs end def determine_jobs_from_tests jobs = [] paths_by_test_dir = {} paths_without_gemfile = [] preprocess_tests.each do |path| test_dir = find_test_dir(path) if test_dir (paths_by_test_dir[test_dir] ||= []) << path else paths_without_gemfile << path end end paths_by_test_dir.each do |test_dir, paths| gemfile_path = ::File.join(test_dir, "Gemfile") if ::File.file?(gemfile_path) jobs << Job.new("specified tests under #{test_dir}", nil, paths, gemfile_path) else paths_without_gemfile.concat(paths) end end unless paths_without_gemfile.empty? name_prefix = jobs.empty? ? "" : "remaining " jobs << Job.new("#{name_prefix}specified tests", nil, paths_without_gemfile, nil) end jobs end def preprocess_tests results = [] tests.each do |elem| if expand_globs glob_results = ::Dir.glob(elem) if glob_results.empty? logger.warn("Pattern did not match any test files: #{elem}") else glob_results.each do |path| results << ::File.realpath(path) end end else begin results << ::File.realpath(elem) rescue ::Errno::ENOENT logger.error("Unable to find file: #{elem}") exit(1) end end end results end def find_test_dir(path) dir = ::File.dirname(path) while dir != path return dir if ::File.basename(dir) == ".test" path = dir dir = ::File.dirname(dir) end nil end def build_job_under(test_path) return nil unless ::File.directory?(test_path) globs = ["#{test_path}/**/test_*.rb", "#{test_path}/**/*_test.rb"] globs.delete_if { |glob| ::Dir.glob(glob).empty? } return nil if globs.empty? gemfile_path = ::File.join(test_path, "Gemfile") gemfile_path = nil unless ::File.file?(gemfile_path) Job.new("tests under #{test_path}", globs, nil, gemfile_path) end def tool_dir @tool_dir ||= begin words = cli.loader.split_path(tool) dir = base_dir unless words.empty? dir = ::File.join(dir, *words) unless ::File.directory?(dir) logger.warn("No such directory: #{dir}") exit end end dir end end def base_dir @base_dir ||= if directory unless ::File.directory?(directory) logger.error("Directory not found: #{directory}") exit(1) end ::File.realpath(directory) else dir = ::File.realpath(::Dir.getwd) loop do candidate = ::File.join(dir, ::Toys::StandardCLI::CONFIG_DIR_NAME) break candidate if ::File.directory?(candidate) parent = ::File.dirname(dir) if parent == dir logger.error("Unable to find a Toys directory") exit(1) end dir = parent end end end expand :minitest do |mt| mt.name = "_internal" mt.files = [] end toys-0.21.0/builtins/system/tools.rb0000644000004100000410000000757715164300675017511 0ustar www-datawww-data# frozen_string_literal: true mixin "tool-methods" do def format_tool(tool, namespace, detailed: false) output = { "name" => tool.full_name[namespace.length..].join(" "), "desc" => tool.desc.to_s, "runnable" => tool.runnable?, } if detailed output["exists"] = true output["long_desc"] = tool.long_desc.map(&:to_s) end output end def choose_loader(local, from_dir) return cli.loader unless local || from_dir if from_dir unless ::File.directory?(from_dir) logger.fatal("Not a directory: #{from_dir}") exit(1) end if ::File.basename(from_dir) == ".toys" from_dir = ::File.dirname(from_dir) elsif from_dir =~ %r{(^|/)\.toys/} logger.fatal("Directory is inside a toys directory: #{from_dir}") exit(1) end end special_cli = if local cli.child.add_search_path(from_dir || ::Dir.getwd) else ::Toys::StandardCLI.new(cur_dir: from_dir) end special_cli.loader end end desc "Tools that introspect available tools" long_desc \ "Tools that introspect the available tools." tool "list" do desc "Output a list of the tools under the given namespace." long_desc \ "Outputs a list of the tools under the given namespace, in YAML format, to the standard " \ "output stream." remaining_args :namespace flag :local do desc "List only tools defined locally in the current directory." end flag :recursive, "--[no-]recursive" do desc "Recursively list subtools" end flag :flatten, "--[no-]flatten" do desc "Display a flattened list of tools" end flag :from_dir, "--dir=PATH" do desc "List tools from the given directory." end flag :show_all, "--all" do desc "Show all tools, including hidden tools and non-runnable namespaces" end include "tool-methods" include "output-tools" def run loader = choose_loader(local, from_dir) words = namespace words = loader.split_path(words.first) if words.size == 1 tool_list = loader.list_subtools(words, recursive: recursive, include_hidden: show_all, include_namespaces: show_all || !flatten, include_non_runnable: show_all) output = { "namespace" => words.join(" "), "tools" => [], } if flatten output["tools"] = tool_list.map { |tool| format_tool(tool, words) } else format_tool_list(tool_list, output, words) end puts(generate_output(output)) end def format_tool_list(tool_list, toplevel, cur_ns) stack = [toplevel] tool_list.each do |tool| tool_name_size = tool.full_name.size while cur_ns.size >= tool_name_size stack.pop cur_ns.pop end formatted = format_tool(tool, cur_ns) (stack.last["tools"] ||= []) << formatted stack.push(formatted) cur_ns.push(tool.full_name.last) end end end tool "show" do desc "Show detailed information about a single tool" long_desc \ "Outputs details about the given tool, in YAML format, to the standard output stream." remaining_args :name flag :local do desc "Show only tools defined locally in the current directory." end flag :from_dir, "--dir=PATH" do desc "Show a tool accessible from the given directory." end include "tool-methods" include "output-tools" def run loader = choose_loader(local, from_dir) words = name words = loader.split_path(words.first) if words.size == 1 tool = loader.lookup_specific(words) output = if tool.nil? { "name" => words.join(" "), "exists" => false, } else format_tool(tool, [], detailed: true) end puts(generate_output(output)) exit(tool.nil? ? 1 : 0) end end toys-0.21.0/builtins/system/.toys.rb0000644000004100000410000000157215164300675017412 0ustar www-datawww-data# frozen_string_literal: true desc "A set of system commands for Toys" long_desc "Contains tools that inspect, configure, and update Toys itself." tool "version" do desc "Print the current Toys version" def run puts ::Toys::VERSION end end mixin "output-tools" do on_include do flag :output_format, "--format=FORMAT" do accept ["json", "json-compact", "yaml"] desc 'The output format. Recognized values are "yaml" (the default), "json", and ' \ '"json-compact".' end end def generate_output(object) case output_format when "json" require "json" ::JSON.pretty_generate(object) when "json-compact" require "json" ::JSON.generate(object) when nil, "yaml" require "psych" ::Psych.dump(object) else logger.error("Unknown output format: #{format}") exit(1) end end end toys-0.21.0/docs/0000755000004100000410000000000015164300675013557 5ustar www-datawww-datatoys-0.21.0/docs/guide.md0000644000004100000410000051217715164300675015213 0ustar www-datawww-data # Toys User Guide Toys is a configurable command line tool. Write commands in Ruby using a simple DSL, and Toys will provide the command line executable and take care of all the details such as argument parsing, online help, and error reporting. Toys is designed for software developers, IT professionals, and other power users who want to write and organize scripts to automate their workflows. It can also be used as a replacement for Rake, providing a more natural command line interface for your project's build tasks. Unlike most command line frameworks, Toys is *not primarily* designed for building and shipping a custom command line executable written in Ruby. Rather, it provides a single executable called `toys` whose functionality you can define by writing configuration files. (You can, however, build your own custom command line executable using the related **toys-core** library.) If this is your first time using Toys, we recommend starting with the [README](https://dazuma.github.io/toys/gems/toys/latest), which includes a tutorial that introduces how to install Toys, write and execute tools, and even use Toys to replace Rake. The README tutorial will likely give you enough information to start using Toys effectively. This user's guide is much longer and covers all the features of Toys in much more depth. Read it when you're ready to unlock all the capabilities of Toys to create sophisticated command line tools. ## Conceptual overview Toys is a command line framework. It provides an executable called `toys` with basic functions such as argument parsing and online help. You provide the actual behavior of the Toys executable by writing **Toys files**. Toys is a multi-command executable. You can define any number of commands, called **tools**, which can be invoked by passing the tool name as an argument to the `toys` executable. Tools are arranged in a hierarchy; you can define **namespaces** that have **subtools**. Tools can recognize command line arguments in the form of **flags** and **positional arguments**. Flags can optionally take values, while positional arguments can be required or optional. Flags can be organized into **flag groups** that support marking flags or flag combinations as required. The configuration of a tool can include descriptions, for the tool itself, and for each command line argument. These descriptions are displayed in the tool's online help screen. Descriptions come in long and short forms, which appear in different styles of help. Toys searches for tools in specifically-named Toys files and directories. It searches for these in the current directory, in parent directories, and in the Toys **search path**. You can also distribute tools in gems or via git. Toys provides various features to help you write tools. This includes providing a logger for each tool, **mixins** that provide common functions a tool can call (such as to control subprocesses and style output), and **templates** which are prefabricated tools that you can configure for your needs. It also integrates with testing frameworks, making it easy to write and run tests. Finally, Toys provides useful built-in behavior, including automatically providing flags to display help screens and set verbosity. It also includes a built-in set of **system tools** that let you inspect and configure the Toys system itself. ## The Toys command line In this section, you will learn how Toys parses its command line, identifies a tool to run, and interprets flags and other command line arguments. The general form of the `toys` command line is: toys [TOOL...] [FLAGS...] [ARGS...] ### Tools The **tool name** consists of all the command line arguments until the first argument that begins with a hyphen (which is interpreted as a **flag**), until no tool with that name exists (in which case the argument is treated as the first **positional argument**), or until there are no more arguments. For example, in the following command: |----TOOL----| $ toys system version The tool name is `system version`. Notice that the tool name can have multiple words. Tools are arranged hierarchically. In this case, `system` is a **namespace** for tools related to the Toys system, and `version` is one of its **subtools**. It prints the current Toys version. The words in a tool name can be delimited with spaces as shown above, or alternately periods or colons. The following commands also invoke the tool `system version`: $ toys system.version $ toys system:version In the following command: |TOOL| |ARG| $ toys system frodo There is no subtool `frodo` under the `system` namespace, so Toys works backward until it finds an existing tool. In this case, the `system` namespace itself does exist, so Toys runs *it* as the tool, and passes it `frodo` as an argument. Namespaces such as `system` are themselves tools and can be executed like any other tool. In the above case, it takes the argument `frodo`, determines it has no subtool of that name, and prints an error message. More commonly, though, you might execute a namespace without arguments: $ toys system This displays the **online help screen** for the `system` namespace, which includes a list of all its subtools and what they do. It is also legitimate for the tool name to be empty. This invokes the **root tool**, the top level namespace: $ toys Like any namespace, invoking the root tool displays its help screen, including showing the list of all its subtools. One last example: $ toys frodo If there is no tool called `frodo` in the top level namespace, then once again, `frodo` is interpreted as an argument to the root tool. The root tool responds by printing an error message that the `frodo` tool does not exist. ### Flags **Flags** are generally arguments that begin with a hyphen, and are used to set options for a tool. Each tool recognizes a specific set of flags. If you pass an unknown flag to a tool, the tool will generally display an error message. Toys follows the typical Unix conventions for flags, specifically those covered by Ruby's OptionParser library. You can provide short (single-character) flags with a single hyphen, or long flags with a double hyphen. Some flags can also take **values**. Following are a few examples. Here we pass a single short flag (for verbose output). $ toys system -v Here we pass multiple long flags (for verbose output and recursive subtool search). $ toys system --verbose --recursive You can combine short flags. The following passes both the `-v` and `-r` flags (i.e. it has the same effect as the previous example.) $ toys system -vr Long flags can be abbreviated, as long as the abbreviation is not ambiguous. For example, there is only one flag (`--recursive`) beginning with the string `--rec`, so you can use the shortened form. $ toys --rec However, there are two flags (`--version` and `--verbose`) beginning with `--ver`, so it cannot be used as an abbreviation. This will cause an error: $ toys --ver Some flags take values. The root tool supports the `--search` flag to search for tools that have the given keyword. $ toys --search=build $ toys --search build The short form of the search flag `-s` also takes a value. $ toys -s build $ toys -sbuild If a double hyphen `--` appears by itself in the arguments, it disables flag parsing from that point. Any further arguments are treated as positional arguments, even if they begin with hyphens. For example: |--FLAG--| |---ARG---| $ toys --verbose -- --recursive That will cause `--recursive` to be treated as a positional argument. (In this case, as we saw earlier, the root tool will respond by printing an error message that no tool named `--recursive` exists.) Note that a single hyphen by itself `-` is not considered a flag, nor does it disable flag parsing. It is treated as a normal positional argument. #### Standard flags For the most part, each tool specifies which flags and arguments it recognizes. However, Toys adds a few standard flags globally to every tool. (It is possible for individual tools to override these flags, but most tools should support them.) These standard flags include: * `--help` (also `-?`) which displays the full help screen for the tool. * `--usage` which displays a shorter usage screen for the tool. * `--verbose` (also `-v`) which increases the verbosity. This affects the tool's logging display, increasing the number of log levels shown. This flag can be issued multiple times. * `--quiet` (also `-q`) which decreases the verbosity. This affects the tool's logging display, decreasing the number of log levels shown. This flag can also be issued multiple times. Namespace tools (tools that have subtools but no explicit functionality of their own) always behave as though `--help` is invoked. (They do recognize the flag, but it has no additional effect.) Namespaces also support the following additional flags: * `--all` which displays all subtools, including [hidden subtools](#hidden-tools) and namespaces. * `--no-recursive` which displays only immediate subtools, instead of the default behavior of showing all subtools recursively. * `--search=TERM` which displays only subtools whose name or description contain the specified search term. * `--tools` which displays just the list of subtools rather than the entire help screen. Finally, the root tool also supports: * `--version` which displays the current Toys version. ### Positional arguments Any arguments not recognized as flags or flag arguments, are interpreted as **positional arguments**. Positional arguments are recognized in order and can be required or optional. Each tool recognizes a specific set of positional arguments. If you do not pass a value for a required argument, or you pass too many arguments, the tool will generally display an error message. For example, the built-in `do` tool runs multiple tools in sequence. It recognizes any number of positional arguments. Those arguments specify which tools to run and what arguments to pass to them. If, for example, you had a `build` tool and a `test` tool, you could run them in sequence with: |---ARGS---| $ toys do build , test The three arguments `build` and `,` and `test` are positional arguments to the `do` tool. (The `do` tool uses `,` to delimit the tools that it should run.) Most tools allow flags and positional arguments to be interspersed. A flag will be recognized even if it appears after some of the positional arguments. However, this approach would not work for the `do` tool because its common case is to pass flags down to the steps it runs. (That is, `do` wants most arguments to be treated as positional even if they look like flags.) So `do` stops recognizing flags once it encounters its first positional argument. That is, you could do this: |------------ARGS-----------| $ toys do build --staging , test --help Each tool can choose which behavior it will support, whether or not to enforce [flags before positional args](#enforcing-flags-before-args). You can also, of course, stop recognizing flags on the command line by passing `--` as an argument. ### Tab completion If you are using the Bash or Zsh shell, Toys provides custom tab completion. See [this section](#installing-tab-completion) for instructions on installing tab completion. Toys will complete tool and subtool names, flags, values passed to flags, and positional argument values, and it will respect the current context. For example, if you type: $ toys The tab completion will show you a list of reasonable things that could appear next, including the defined tool names (such as `system` and `do`) as well as all the flags supported by the root tool (such as `--help` and `-v`). And of course, if you start typing something, tab completion will limit the display to matching completions. The following displays only flags, i.e. completions that begin with a hyphen: $ toys - And if you type the following: $ toys sys It is likely only one tool name starts with `sys`, so completion will automatically type the rest of `system` for you. The tab completion for Toys also supports values passed to flags and positional args. As we shall see later, when you define a flag or a positional argument, you can specify how completions are computed. **Note:** Because of the highly dynamic nature of Toys in which tools, flags, and arguments can be highly customized, the completion implementation actually requires *executing Toys* so it can analyze your tool configurations. This unfortunately means paying some upfront latency as the Ruby interpreter starts up. So you can expect a slight pause when evaluating tab completion for Toys, at least in comparison with most other tab completions. ## Defining tools So far we've been experimenting only with the built-in tools provided by Toys. In this section, you will learn how to define tools by writing a **Toys file**. We will cover how to write tools, including specifying the functionality of the tool, the flags and arguments it takes, and how its description appears in the help screen. ### Basic Toys syntax A file named `.toys.rb` (note the leading period) in the current working directory is called a **Toys file**. It defines tools available in that directory and its subdirectories. The format of a Toys file is a Ruby DSL that includes directives, methods, and nested blocks. The actual DSL is specified in the {Toys::DSL::Tool} class. To create a tool, write a `tool` block, giving the tool a name. Within the block, use directives to set the properties of the tool, including descriptions and the flags and arguments recognized by the tool. The actual functionality of the tool is set by defining a `run` method. Let's start with an example: tool "greet" do desc "Print a friendly greeting." long_desc "Prints a friendly greeting. You can customize whom to" \ " greet, and how friendly it should be.", "", "Example:", [" toys greet --shout ruby"] optional_arg :whom, default: "world", desc: "Whom to greet." flag :shout, "-s", "--shout", desc: "Greet loudly." def run greeting = "Hello, #{whom}!" greeting = greeting.upcase if shout puts greeting end end Its results should be mostly self-evident. But let's unpack a few details. ### Tool descriptions Each tool can have a **short description** and/or a **long description**. The short description is a generally a single string that is displayed with the tool name, at the top of its help page or in a subtool list. The long description generally includes multiple strings, which are displayed in multiple lines in the "description" section of the tool's help page. Long descriptions can include blank lines to separate paragraphs visually. By default, each description string/line is word-wrapped when displayed. In the long description example above, the first line is a bit longer than 80 characters, and may be word-wrapped if displayed on an 80-character terminal. If you need to control the wrapping behavior, pass an array of strings for that line. Each array element will be considered a unit for wrapping purposes, and will not be split. The example command in the long description above illustrates how to prevent a line from being word-wrapped. This is also a useful technique for preserving spaces and indentation. You can see an example of this technique in the long description for the `greet` tool above, where the last line consists of a one-element array, preserving the indent and preventing the example command line from being word-wrapped. For more details, see the reference documentation for {Toys::DSL::Tool#desc} and {Toys::DSL::Tool#long_desc}. ### Tool arguments When you define a tool, you declare the arguments that it can take on the command line. Toys will parse the arguments and make the results available to your tool. Toys distinguishes two types of arguments: **positional arguments** and **flags**. Flags are generally arguments beginning with a single or double hyphen, as are common in Unix/POSIX commands. They are typically (but not always) optional, and can be referenced by name. Positional arguments generally do not begin with hyphens and are identified by their order on the command line. Positional arguments can be required or optional. Each argument, whether positional or flag, must have a name, which is a key that the tool can use to obtain the argument's value at execution time. The above example uses the directive {Toys::DSL::Tool#optional_arg} to declare an optional positional argument named `:whom`. If the argument is provided on the command line, the option `:whom` is set to the argument value, e.g.: $ toys greet ruby Hello, ruby! The above example also uses the directive {Toys::DSL::Tool#flag} to declare a flag named `:shout`. If this flag is included on the command line, the option `:shout` is set to `true`, with this result: $ toys greet --shout HELLO, WORLD! Arguments also have various properties controlling how values are validated and expressed. For all the details on configuring arguments, see the section on [defining command line arguments](#defining-command-line-arguments). ### Tool execution basics When you run a tool from the command line, Toys will build the tool based on its definition in a Toys file, and then it will attempt to execute it by calling the `run` method. Normally, you should define this method in each of your tools. Note: If you do not define the `run` method for a tool, Toys provides a default implementation that displays the tool's help screen. This is typically used for namespaces, as we shall see [below](#namespaces-and-subtools). Most tools, however, should define `run`. In our greeting example, note how the `run` method can access values that were assigned by flags or positional arguments by just calling a method with that flag or argument name. When you declare a flag or argument, if the option name is a symbol that is a valid Ruby method name, Toys will provide a method that you can call to get the value. In the example, `whom` and `shout` are such methods. Most tools produce output or interact with the console using the normal Ruby `$stdout`, `$stderr`, and `$stdin` streams. If a tool's `run` method finishes normally, Toys will exit with a result code of 0, indicating success. You can exit immediately and/or provide a nonzero result by calling {Toys::Context#exit}: def run puts "Exiting with an error..." exit(1) puts "Will never get here." end If your `run` method raises an exception, Toys will display the exception and exit with a nonzero code. Finally, you can also define additional methods within the tool. These are available to be called by your `run` method, and can be used to decompose your tool implementation. Indeed, a tool is actually a class under the hood, and you can define methods as with any other class. Here's a contrived example: tool "greet-many" do # Support any number of arguments on the command line remaining_args :whom, default: ["world"] flag :shout, "-s", "--shout" # You can define helper methods like this. def greet(name) greeting = "Hello, #{name}!" greeting = greeting.upcase if shout puts greeting end def run whom.each do |name| greet(name) end end end This should be enough to get you started implementing tools. A variety of additional features are available for your tool implementation and will be discussed further below. But first we will cover one final important topic. ### Namespaces and subtools Like many command line frameworks, Toys supports **subtools**. You can, for example create a tool called "test" that runs your tests for a particular project, but you might also want "test unit" and "test integration" tools to run specific subsets of the test suite. One way to do this, of course, is for the "test" tool to parse "unit" or "integration" as arguments. However, it's often easier to define them as separate tools, subtools of "test". To define a subtool, create nested `tool` directives. Here's a simple example: tool "test" do tool "unit" do def run puts "run unit tests here..." end end tool "integration" do def run puts "run integration tests here..." end end end You can now invoke them like this: $ toys test unit run unit tests here... $ toys test integration run integration tests here... Notice in this case, the parent "test" tool itself has no `run` method. This is a common pattern: "test" is just a "container" for tools, a way of organizing your tools. In Toys terminology, it is called a **namespace**. But it is still a tool, and it can still be run: $ toys test As discussed earlier, Toys provides a default implementation that displays the help screen, which includes a list of the subtools and their descriptions. As another example, the "root" tool is also normally a namespace. If you just run Toys with no arguments: $ toys The root tool will display the overall help screen for Toys. Although it is a less common pattern, it is possible for a tool to have its own `run` method even if it has subtools: tool "test" do def run puts "run all tests here..." end tool "unit" do def run puts "run only unit tests here..." end end tool "integration" do def run puts "run only integration tests here..." end end end Now running `toys test` will run its own implementation. (Yes, it is even possible to write a `run` method for the root tool. I don't recommend doing so, because you would lose the root tool's useful default implementation that lists all your tools.) Toys allows subtools to be nested arbitrarily deep. In practice, however, more than two or three levels of hierarchy can be confusing to use. ## Defining command line arguments In this section, we will take a closer look at command line arguments, including all the different ways to control and validate them. We'll first look at [positional arguments](#defining-positional-arguments) and [flags](#defining-flags) and the features that make them distinct. Afterward, we'll look at features that are common across both types of arguments, including [descriptions](#argument-descriptions), [acceptors](#argument-acceptors), and [completions](#argument-completions). ### Defining positional arguments Tools can recognize any number of positional arguments. Each argument must have a name, a key into the tool's context that can be used to obtain the argument's value at execution time. Arguments can also have various properties controlling how values are validated and expressed. Positional arguments can be **required**, declared using the directive {Toys::DSL::Tool#required_arg}, or **optional**, declared using the directive {Toys::DSL::Tool#optional_arg}. Required arguments *must* be provided on the command line, otherwise the tool will report a usage error. Optional arguments can be omitted; if they are, the value will be set to a default. #### Parsing required and optional arguments When command line arguments are parsed, the required arguments are matched first, in order, followed by the optional arguments. For example: tool "args-demo" do optional_arg :arg2 required_arg :arg1 def run puts "Options data is #{options.inspect}" end end If a user runs $ toys args-demo foo Options data is {arg1: "foo", arg2: nil} Then the required argument `:arg1` will be set to `"foo"`, and the optional argument `:arg2` will not be set (i.e. it will remain `nil`). If the user runs: $ toys args-demo foo bar Options data is {arg1: "foo", arg2: "bar"} Then `:arg1` is set to `"foo"`, and `:arg2` is set to `"bar"`. Running the following: $ toys args-demo Will produce a usage error, because no value is set for the required argument `:arg1`. Similarly, running: $ toys args-demo foo bar baz Will also produce an error, since the tool does not define an argument to match `"baz"`. Optional arguments can declare a default value to be used if the argument is not provided on the command line. For example: tool "args-demo" do required_arg :arg1 optional_arg :arg2, default: "the-default" def run puts "Options data is #{options.inspect}" end end Now running the following: $ toys args-demo foo Options data is {arg1: "foo", arg2: "the-default"} Will set the required argument to `"foo"` as usual, and the optional argument, because it is not provided, will default to `"the-default"` instead of `nil`. #### Remaining arguments Normally, unmatched arguments will result in an error message. However, you can provide an "argument" to match all **remaining** unmatched arguments at the end, using the directive {Toys::DSL::Tool#remaining_args}. For example: tool "args-demo" do required_arg :arg1 optional_arg :arg2 remaining_args :arg3 def run puts "Options data is #{options.inspect}" end end Now, we can see how the remaining arguments (if any) are collected by `:arg3`: $ toys args-demo foo bar baz qux Options data is {arg1: "foo", arg2: "bar", arg3: ["baz", "qux"]} $ toys args-demo foo Options data is {arg1: "foo", arg2: nil, arg3: []} Tools can include any number of `required_arg` and `optional_arg` directives, declaring any number of required and optional arguments. But tools can have at most only one `remaining_args` directive. ### Defining flags Tools can also recognize any number of flags. Each flag must have a name that can be used to obtain the flag's state at execution time. Flags also support a variety of properties controlling how the flag is parsed and handled. They are declared using the directive {Toys::DSL::Tool#flag}. Flags can support a number of "spellings" that are effectively synonyms for the same flag. These can include both short form (e.g. `-s`) and long form (e.g. `--shout`). Flags can optionally take values (e.g. `--output=file.txt`), or they can simply be present or absent. Let's take a closer look at the different types of flags. #### Flag types Toys recognizes the same syntax used by the standard OptionParser library. This means you can also declare a flag that can be set either to true or false: flag :shout, "--[no-]shout" You can declare that a short or long flag takes a value: flag :whom, "--whom=VALUE" flag :whom, "--whom VALUE" flag :whom, "-wVALUE" flag :whom, "-w VALUE" You can also declare the value to be optional: flag :whom, "--whom[=VALUE]" flag :whom, "--whom [VALUE]" flag :whom, "-w[VALUE]" flag :whom, "-w [VALUE]" Note that if you define multiple flags together, they will all be coerced to the same "type". That is, if one takes a value, they all will implicitly take a value. (This is the same behavior as OptionParser.) In this example: flag :whom, "-w", "--whom=VALUE" The `-w` flag will also implicitly take a value, because it is defined as a synonym of another flag that takes a value. Note also that Toys will raise an error if those flags are incompatible. For example: flag :whom, "-w[VALUE]", "--whom=VALUE" Raises an error because one flag's value is optional while the other is required. (Again, this is consistent with OptionParser's behavior.) #### Inferred flags If you do not provide any actual flags, Toys will attempt to infer one from the name of the option. A one-character name will yield a short flag, and a longer name a long flag. Hence, the following two definitions are equivalent: flag :shout flag :shout, "--shout" And the following two are equivalent: flag :S flag :S, "-S" Inferred flags will convert underscores to hyphens. So the following two definitions are also equivalent: flag :call_out flag :call_out, "--call-out" #### Handling optional values There are some subtleties in how the Ruby OptionParser library treats flags with optional values. Although Toys does not use OptionParser internally, it does, for the most part, replicate OptionParser's behavior. It is thus important to understand that behavior if you use optional values. First, if a flag has an optional value that is not provided on the command line, then the option is set to `true`, as if it were a normal boolean flag that didn't take a value. Consider this example: tool "flags-demo" do flag :output, "--output [DIRECTORY]", default: "." def run puts "output is #{output.inspect}" end end If a user executes this without passing the `--output` flag, the default will be printed as we expect. $ toys flags-demo output is "." If a user executes this and provides a value for `--output`, it will show up: $ toys flags-demo --output /etc output is "/etc" If a user provides `--output` but omits the value, it displays `true`: $ toys flags-demo --output output is true Second, if the following argument looks like a flag (i.e. it begins with a hyphen), it is not treated as an optional value. In this example, the argument `--verbose` is not treated as the value of `--output` but as a separate flag. (If `--output` had a *required* value, then `--verbose` would have been treated as the value.) $ toys flags-demo --output --verbose output is true Finally, there is an important difference between the syntax `"--output [DIRECTORY]"` and `"--output=[DIRECTORY]"`. In the former case, the following argument (as long as it doesn't look like a flag) will be treated as the value. In the latter case, however, the following argument is *never* treated as the value. In that latter case, you *must* use the equals sign syntax to provide a value. To illustrate, consider two flags with optional values, one using space and the other using equals. tool "flags-demo-space" do flag :output, "--output [DIRECTORY]", default: "." remaining_args :remaining def run puts "output is #{output.inspect}" puts "remaining is #{remaining.inspect}" end end tool "flags-demo-equals" do flag :output, "--output=[DIRECTORY]", default: "." remaining_args :remaining def run puts "output is #{output.inspect}" puts "remaining is #{remaining.inspect}" end end Here is the behavior: $ toys flags-demo-space --output=/etc output is "/etc" remaining is [] $ toys flags-demo-space --output /etc output is "/etc" remaining is [] $ toys flags-demo-equals --output=/etc output is "/etc" remaining is [] $ toys flags-demo-equals --output /etc output is true remaining is ["/etc"] #### Defaults and handlers Flags are usually optional; a flag can appear in a command line zero, one, or any number of times. If a flag is not passed in the command line arguments for a tool, by default its corresponding option value will be `nil`. You can change this by providing a default value for a flag: flag :age, accept: Integer, default: 21 If you pass a flag multiple times on the command line, by default the *last* appearance of the flag will take effect. That is, suppose you define this flag: flag :shout, "--[no-]shout" Now if you pass `--shout --no-shout`, then the value of the `:shout` option will be `false`, i.e. the last value set on the command line. This is because a flag normally *sets* its option value, replacing any previously set value. You can, however, change this behavior by providing a **handler**. A handler is a Ruby Proc that defines what a flag does to its option value. It takes up to three arguments: 1. The new value given 2. The previously set value (which might be the default value if this is the first appearance of the flag) 3. A hash containing all the options set so far The proc must then return the new value for the option. If the proc takes three arguments, it can use the third argument to read and/or modify any other option. This enables powerful abilities such as flags that affect the behavior of other flags. Effectively, the default behavior (setting the value and ignoring the previous value) is equivalent to the following handler that takes only the first argument: flag :shout, "--[no-]shout", handler: proc { |val| val } Toys gives the default handler the special name `:set`. So the above is also equivalent to: flag :shout, "--[no-]shout", handler: :set The `--verbose` flag, provided automatically by Toys for most tools, shows an example of an alternate handler. Verbosity is represented by an integer value, defaulting to 0. The `--verbose` flag can appear any number of times, and *each* appearance increases the verbosity. Its implementation is internal to Toys, but looks something like this: flag Toys::Context::Key::VERBOSITY, "-v", "--verbose", default: 0, handler: proc { |_val, prev| prev + 1 } Similarly, the "--quiet" flag, which decreases the verbosity, is implemented like this: flag Toys::Context::Key::VERBOSITY, "-q", "--quiet", default: 0, handler: proc { |_val, prev| prev - 1 } Note that both flags affect the same option name, {Toys::Context::Key::VERBOSITY}. The first increments it each time it appears, and the second decrements it. A tool can query this option and get an integer telling the requested verbosity level, as you will see [below](#logging-and-verbosity). Toys provides a few built-in handlers that can be specified by name. We already discussed the default handler that can be specified by its name `:set` or by simply omitting the `handler:` option. Another named handler is `:push`. This handler is intended for flags that take values and can be provided more than once. The final value is then an array of values. In the following example, an invocation can provide any number of `--include` flags, and the `:include` option will be set to an array of the given paths. flag :include, "-I", "--include PATH", default: [], handler: :push The `:push` handler is equivalent to `proc { |val, array| array.nil? ? [val] : array << val }`. #### Flag groups Flags can be organized into groups. This serves two functions: * Grouping related flags in the help and usage screens * Implementing required flags and other constraints To create a simple flag group, use the `flag_group` directive, and provide a block that defines the group's flags. You can also provide a group description that appears in the help screen. flag_group desc: "Debug flags" do flag :debug, "-D", desc: "Enable debugger" flag :warnings, "-W[VAL]", desc: "Enable warnings" end Flag groups can have a "type" that specifies constraints on the flags contained in the group. Flags in a simple group like the above are ordinary optional flags. However, you can specify that flags in the group are required using the `all_required` directive: all_required desc: "Login flags (all required)" do flag :username, "--username=VAL", desc: "Set the username (required)" flag :password, "--password=VAL", desc: "Set the password (required)" end If the tool is invoked without providing each of these required flags, it will display an option parsing error. The `all_required` directive is actually just shorthand for passing `type: :required` to the `flag_group` directive. So the above is the same as: flag_group type: :required, desc: "Login flags (all required)" do flag :username, "--username=VAL", desc: "Set the username (required)" flag :password, "--password=VAL", desc: "Set the password (required)" end The following are the supported types of flag groups: * The `:required` type, which you can create using the directive `all_required`. All flags from the group are required and must be provided on the command line to avoid an error. * The `:exactly_one` type, which you can create using the directive `exactly_one_required`. Exactly one, and no more than one, flag from the group must be provided on the command line to avoid an error. * The `:at_most_one` type, which you can create using the directive `at_most_one_required`. At most one flag from the group must be provided on the command line to avoid an error. * The `:at_least_one` type, which you can create using the directive `at_least_one_required`. At least one flag from the group must be provided on the command line to avoid an error. * The `:optional` type is the default created using the directive `flag_group` when no type is specified. Flags in the group are ordinary optional flags. Flag group types are useful for a variety of tools. For example, suppose you are writing a tool that deploys an app to one of several different kinds of targets---say, a server, a VM, or a container. You could provide this configuration for your tool with a flag group: tool "deploy" do exactly_one_required desc: "Deployment targets" do flag :server, "--server=IP_ADDR" flag :vm, "--vm=VM_ID" flag :container, "--container=CONTAINER_ID" end def run # Now exactly one of server, vm, or container will be set. The other # two options will be their default value, nil. end end ### Argument descriptions Both positional arguments and flags can have short and long descriptions, which are displayed in online help. Set descriptions via the `desc:` and `long_desc:` arguments to the argument directive. The `desc:` argument takes a single string description, while the `long_desc:` argument takes an array of strings. Here is an example of a positional argument with a description: required_arg :arg, desc: "This is a short description for the arg", long_desc: ["Long descriptions can have multiple lines.", "This is the second line."] Here is an example of a flag with a description: flag :my_flag, "--my-flag", desc: "This is a short description for the arg", long_desc: ["Long descriptions can have multiple lines.", "This is the second line."] See the [above section on Descriptions](#tool-descriptions) for more information on how descriptions are rendered and word wrapped. #### Argument acceptors Positional arguments and flags can use **acceptors** to define how to validate values and convert them to Ruby objects for your tool to consume. By default Toys will accept any argument string, and expose it to your tool as a raw string. However, you can provide an acceptor to change this behavior. Acceptors are part of the OptionParser interface, and are described under the [argument converters](https://docs.ruby-lang.org/en/4.0/optparse/argument_converters_rdoc.html) section. For example, you can provide the `Integer` class as an acceptor, which will validate that the argument is a well-formed integer, and convert it to an integer during parsing: tool "acceptor-demo" do required_arg :sibling, accept: ["Pollux", "Castor"] flag :age, accept: Integer def run puts "Next year #{sibling} will be #{age + 1}" # Age is an integer end end If you pass a non-integer for the `:age` argument, or a name other than the two allowed `:sibling` names, Toys will report a usage error. You can use any of the ready-to-use coercion types provided by OptionParser, including the special types such as [OptionParser::DecimalInteger](https://docs.ruby-lang.org/en/4.0/optparse/argument_converters_rdoc.html#label-DecimalInteger) and [OptionParser::OctalInteger](https://docs.ruby-lang.org/en/4.0/optparse/argument_converters_rdoc.html#label-OctalInteger). You can also create **custom acceptors**. See the [section below on Custom Acceptors](#custom-acceptors) for more information. #### Argument completions Shell tab completion supports positional arguments and flags, and both can be configured to present a set of completion candidates for themselves. By default, an argument does not provide any completions for itself. To change that, set the `completion` option. Currently there are three ways to set the completion: * Provide a static set of possible values, as an array of strings. * Specify that values should be paths in the file system by setting the symbol `:file_system`. * Provide a `Proc` that returns possible values. The following are two example arguments, one that supports a static set of completions and the other that supports file paths. flag :language, "--lang=VALUE", complete_values: ["ruby", "elixir", "rust"] required_arg :path, complete: :file_system Completions are somewhat related to acceptors, and it is a common pattern to set both in concert. But they perform distinct functions. Acceptors affect argument parsing, whereas completions affect tab completion in the shell. ### Argument block syntax Complex flags and positional arguments can sometimes be unwieldy to configure using keyword arguments, especially if they contain long descriptions. Thus, Toys provides an alternate syntax for defining arguments using a block. Here is an example of using a block to define a positional argument. Within the block, directives such as `long_desc` are defined as methods of the {Toys::DSL::PositionalArg} class. required_arg :arg do desc "This is a short description for the arg" long_desc "Long desc can be set as multiple lines together,", "like this second line." long_desc "Or you can call long_desc again to add more lines." end Here is an example using a block to define a flag. Within the block, directives such as `default` are defined as methods of the {Toys::DSL::Flag} class. flag :my_flag do flags "--my-flag VALUE" default "hello" desc "This is a short description for the flag" long_desc "Long desc can be set as multiple lines together,", "like this second line." long_desc "Or you can call long_desc again to add more lines." end ## The execution environment This section describes the context and resources available to your tool when it is running; that is, what you can call from your tool's `run` method. Each tool is defined as a class that subclasses {Toys::Context}. The base class defines various helper methods, and provides access to a variety of data, resources, and capabilities relevant to your tool. We have already seen earlier how to use the {Toys::Context#exit} method to exit immediately and return an exit code. Now we will cover other resources available to your tool. ### The options hash The core of the execution context is the options hash. This is a hash that contains, among other things, the values provided by a tool's command line arguments and flags. You've seen already that you can access the state of these arguments by calling methods defined by the argument name. You can also access these values by calling {Toys::Context#get} (or its alias {Toys::Context#[]}) and passing the argument name. This also lets you access arguments if the name is not a valid method name. For example: tool "greet" do # The name "whom-to-greet" is not a valid method name. optional_arg "whom-to-greet", default: "world" flag :shout, "-s", "--shout" def run # We can access the "whom-to-greet" option using the "get" method. greeting = "Hello, #{get('whom-to-greet')}!" greeting = greeting.upcase if shout puts greeting end end In addition to reading values, you can also write to the options hash, by calling {Toys::Context#set}. This can be used to postprocess inputs. For example, suppose you are writing a command line tool that needs to work with absolute file paths. You can still allow a user to pass relative paths on the command line, and postprocess them at execution time: tool "mytool" do remaining_args :paths def run set(:paths, paths.map { |path| File.absolute_path(path) }) # Now you know the `paths` are all absolute... end end You can also define new entries in the options hash, along with corresponding helper methods, using the {Toys::DSL::Tool#static} directive. This directive sets a value in the options hash, and also defines a helper method for accessing it if the key is a symbol that is a legal method name. This is often a good alternative to constants, which can be [problematic](#using-constants) in a tool definition. tool "greet" do static :greeting, "Hello" optional_arg :whom, default: "world" def run puts "#{greeting}, #{whom}!" end end Finally, you can access the options as a hash, and read and modify it accordingly, by calling {Toys::Context#options}. ### Built-in context In addition to the options set by your tool's flags and command line arguments, a variety of other data and objects are also accessible in the options hash via {Toys::Context#get}. For example, you can get the full name of the tool being executed like this: def run puts "Current tool is #{get(TOOL_NAME)}" end The `TOOL_NAME` constant above is a well-known key that corresponds to the full name (as an array of strings) of the running tool. A variety of well-known keys are defined in the {Toys::Context::Key} module. They include information about the current execution, such as the tool name and the original command line arguments passed to it (before they were parsed). They also include some internal Toys objects, which can be used to do things like write to the logger or look up and call other tools. Most of the important context also can be accessed from convenience methods. For example, the `TOOL_NAME` is also available from {Toys::Context#tool_name}: def run puts "Current tool is #{tool_name}" end Let's take a look at a few things your tool can do with the objects you can access from built-in context. ### Working directory and context directory When a tool runs, the working directory is initially set to what was the current working directory when toys was invoked. In many cases, this is what you'll want. However, for some tools, it might make more sense for the working directory to be set to the so-called **context directory**. This is the directory *containing* the top level `.toys.rb` file or `.toys` directory where the tool is defined. For example, suppose you have a Ruby project directory: my-project/ | +- .toys.rb <-- project tools defined here | +- lib/ | +- test/ | etc... Now suppose you defined a tool that lists the tests: tool "list-tests" do def run puts Dir.glob("test/**/*.rb").join("\n") end end This tool assumes it will be run from the main project directory (`my-project` in the above case). However, Toys lets you invoke tools even if you are in a subdirectory: $ cd lib $ toys list-tests # Does not work Rake handles this by always automatically changing the current working directory to the directory containing the active Rakefile. Toys, however, does not change the working directory unless you tell it to. You can make the `list-tests` tool work correctly by changing the directory to the context directory, which in this example is the `my-project` directory. The context directory is part of the built-in context, and is available via the `CONTEXT_DIRECTORY` context key or the {Toys::Context#context_directory} method. tool "list-tests" do def run Dir.chdir(context_directory) do puts Dir.glob("test/**/*.rb").join("\n") end end end Note that the context directory is different from `__dir__`. It is not necessarily the directory containing the tool file being executed, but the directory containing the entire toys directory structure. So if your tool definition is inside a `.toys` directory, it will still work: my-project/ <-- context_directory still points here | +- .toys/ | | | +- list-tests.rb <-- tool defined here | +- lib/ | +- test/ | etc... This technique is particularly useful for build tools. Indeed, all the build tools described in the section on [Toys as a Rake Replacement](#toys-as-a-rake-replacement) automatically move into the context directory when they execute. ### Logging and verbosity Toys provides a Logger (a simple instance of the Ruby standard library logger that writes to standard error) for your tool to use to report status information. You can access this logger via the `LOGGER` context key, or {Toys::Context#logger}. For example: def run logger.warn "Danger Will Robinson!" end The current logger level is controlled by the verbosity. Verbosity is an integer context value that you can retrieve using the `VERBOSITY` context key or {Toys::Context#verbosity}. The verbosity is set to 0 by default. This corresponds to a logger level of `WARN`. That is, warnings, errors, and fatals are displayed, while infos and debugs are not. However, [as we saw earlier](#standard-flags), most tools automatically respond to the `--verbose` and `--quiet` flags, (or `-v` and `-q`), which increment and decrement the verbosity value, respectively. If you pass `-v` to a tool, the verbosity is incremented to 1, and the logger level is set to `INFO`. If you pass `-q`, the verbosity is decremented to -1, and the logger level is set to `ERROR`. So by using the provided logger, a tool can easily provide command line based control of the output verbosity. ### Running tools from tools A common operation a tool might want to do is "call" another tool. This can be done via the CLI object, which you can retrieve using the `CLI` key or {Toys::Context#cli}. These return the current instance of {Toys::CLI}, which is the "main" interface to Toys. In particular, it provides the {Toys::CLI#run} method, which can be used to call another tool: def run status = cli.run("greet", "rubyists", "-v") exit(status) unless status.zero? end Pass the tool name and arguments as arguments to the run method. It will execute, and return a process status code (i.e. 0 for success, and nonzero for error). Make sure you handle the exit status. For example, in most cases, you should probably exit if the tool you are calling returns a nonzero code. You can also use the `exec` mixin [described below](#executing-subprocesses) to run a tool in a separate process. This is particularly useful if you need to capture or manipulate that tool's input or output stream. ### Helper methods and mixins The methods of {Toys::Context} are not the only methods available for your tool to call. We [saw earlier](#tool-execution-basics) that a tool can define additional methods that you can use as helpers. You can also include **mixins**, which are modules that bring in a whole set of helper methods. Include a mixin using the `include` directive: tool "greet" do include :terminal def run puts "This is a bold line.", :bold end end A mixin can be specified by providing a module itself, or by providing a **mixin name**. In the above example, we used `:terminal`, which is the name of a built-in mixin that Toys provides. Among other things, it defines a special `puts` method that lets you include style information such as `:bold`, which affects the display on ANSI-capable terminals. For details on the built-in mixins provided by Toys, see the modules under {Toys::StandardMixins}. We will look at a few examples of the use of these mixins below. Built-in mixins have names that are symbols. You can also define your own mixins, as we will see in the [section on defining mixins](#defining-mixins). ### Executing subprocesses Another common operation you might do in a tool is to execute other binaries. For example, you might write a tool that shells out to `scp` to copy files to a remote server. Ruby itself provides a few convenient methods for simple execution, such as the [Kernel#system](https://docs.ruby-lang.org/en/4.0/Kernel.html#method-i-system) method. However, these typically provide limited ability to control or interact with subprocess streams, and you also need to remember to handle the exit status yourself. If you do want to exert more control over subprocesses, you can use [Process.spawn](https://docs.ruby-lang.org/en/4.0/Process.html#method-c-spawn), or a higher-level wrapper such as the [open3 library](https://docs.ruby-lang.org/en/4.0/Open3.html). Another alternative is to use the `:exec` built-in mixin. This mixin provides convenient methods for the common cases of executing subprocesses and capturing their output, and a powerful block-based interface for controlling streams. The exec mixin also lets you set a useful default option that causes the tool to exit automatically if one of its subprocesses exits abnormally. The exec mixin provides methods for running several different kinds of subprocesses: * Normal processes started by the operating system. * Another Ruby process. * A shell script. * Another tool run in a separate (forked) process. * A block run in a separate (forked) process. Following is a simple example that installs the bundle and starts a rails app: tool "start-rails" do include :exec, exit_on_nonzero_status: true def run exec(["bundle", "install"]) exec(["bin/rails", "serve"], env: {"RAILS_ENV" => "production"}) end end For more information, see the {Toys::StandardMixins::Exec} mixin module and the underlying library {Toys::Utils::Exec}. ### Formatting output Interacting with the user is a very common function of a command line tool, and many modern tools include intricately designed and styled output, and terminal effects such as progress bars and spinners. Toys provides several mixins that can help create nicer interfaces. First, there is `:terminal`, which provides some basic terminal features such as styled output and simple spinners. For information, see the {Toys::StandardMixins::Terminal} mixin module and the underlying library {Toys::Utils::Terminal}. If you prefer the venerable Highline library interface, Toys provides a mixin called `:highline` that automatically installs the highline gem (version 2.x) if it is not already available, and makes a highline object available to the tool. For more information, see the {Toys::StandardMixins::Highline} mixin module. The `:pager` mixin provides a convenient interface to pager utilities such as `less`, which let your users interactively page through long output. For more information, see the {Toys::StandardMixins::Pager} mixin module and the underlying library {Toys::Utils::Pager}. You can also use other third-party gems such as [tty](https://github.com/piotrmurach/tty). The section below on [useful gems](#useful-gems) provides some examples. ## Understanding Toys files Toys commands are defined in Toys files. We covered the basic syntax for these files in the [above section on defining tools](#defining-tools). In this section, we will take a deeper look at what you can do with Toys files. ### Toys directories So far we have been defining tools by writing a Toys file named `.toys.rb` located in the current working directory. This works great if you have a small number of fairly simple tools, but if you are defining many tools or tools with long or complex implementations, you may find it better to split your tools in separate files. You can have Toys load tools from multiple files by creating a **Toys directory**. A Toys directory is a directory called `.toys` located in the current working directory. (Again, note the leading period.) Ruby files inside a Toys directory (or any of its subdirectories) are loaded when Toys looks for tool definitions. Furthermore, the name of the Ruby file (and indeed its path relative to the Toys directory) determines which tool it defines. For example, one way to create a "greet" tool, as we saw before, is to write a `.toys.rb` file in the current directory, and populate it like this: tool "greet" do optional_arg :whom, default: "world" def run puts "Hello, #{whom}" end end You could also create the same tool by creating a `.toys` directory, and then creating a file `greet.rb` inside that directory. (current directory) | +- .toys/ | +- greet.rb The contents of `greet.rb` would be: optional_arg :whom, default: "world" def run puts "Hello, #{whom}" end Notice that we did not use a `tool "greet"` block here. That is because the name of the file `greet.rb` already provides a naming context: Toys already knows that we are defining a "greet" tool. If you do include a `tool` block inside the `greet.rb` file, it will create a *subtool* of `greet`. In other words, the path to the Ruby file defines a "starting point" for the names of tools defined in that file. If you create subdirectories inside a Toys directory, their names also contribute to the namespace of created tools. For example, if you create a directory `.toys/test` and a file `unit.rb` under that directory, it will create the tool `test unit`. (current directory) | +- .toys/ | +- greet.rb <-- defines "greet" (and subtools) | +- test/ | +- unit.rb <-- defines "test unit" (and its subtools) Once again, `test unit` is the "starting point" for tools defined in the `.toys/test/unit.rb` file. Declarations and methods at the top level of that file will define the `test unit` tool. Any `tool` blocks you add to that file will define subtools of `test unit`. #### Index files The file name `.toys.rb` can also be used inside Toys directories and subdirectories. Such files are called **index files**, and they create tools with the *directory* as the "starting point" namespace. For example, if you create an index file directly underneath a `.toys` directory, it will define top level tools (just like a `.toys.rb` file in the current directory.) An index file located inside `.toys/test` will define tools with `test` as the "starting point" namespace. (current directory) | +- .toys/ | +- .toys.rb <-- index file, defines tools at the top level | +- greet.rb <-- defines "greet" (and subtools) | +- test/ | +- .toys.rb <-- index file, defines "test" (and its subtools) | +- unit.rb <-- defines "test unit" (and its subtools) Index files give you some flexibility for organizing your tools. For example, if you have a number of subtools of `test`, including a lot of small tools and one big complex subtool called `unit`, you might define all the simple tools in the index file `.toys/test/.toys.rb`, while defining the large `test unit` tool in the separate file `.toys/test/unit.rb`. Toys also loads the index file in a directory *before* any other files in that directory. This means they are convenient places to define shared code that can be used by all the subtools defined in that directory, as we shall see later in the [section on sharing code](#sharing-code). ### The Toys search path So far we have seen how to define tools by writing a `.toys.rb` file in the current directory, or by writing files inside a `.toys` directory in the current directory. These tools are "scoped" to the current directory. If you move to a different directory, they may not be available. When Toys runs, it looks for tools in a **search path**. Specifically: 1. It looks for a `.toys.rb` file and/or a `.toys` directory in the *current working directory*. 2. It does the same in the *parent directory* of the current directory, and then its parent, and so on until it hits either the root of the file system or one of the global directories described in (3). 3. It looks in a list of *global directories*, specified in the environment variable `TOYS_PATH`. This variable can contain a colon-delimited list of directory paths. If the variable is not set, the current user's *home directory*, and the system configuration directory (`/etc` on Unix systems) are used by default. Toys does *not* search parents of global directories. It uses the *first* implementation that it finds for the requested tool. For example, if the tool `greet` is defined in the `.toys.rb` file in the current working directory, and also in the `.toys/greet.rb` file of the parent directory, it will use the version in the current directory. This means you could write a default implementation for a tool in your home directory, and override it in the current directory. For example, you could define a tool `get-credentials` in your home directory that gets credentials you need for *most* of your projects. But maybe on particular project requires different credentials, so you could define a different `get-credentials` tool in that project's directory. While a tool can be overridden when it is defined at different points in the search path, it is *not* allowed to provide multiple definitions of a tool at the *same* point in the search path. For example, if you define the `greet` tool twice in the same `.toys.rb` file, Toys will report an error. Perhaps less obviously, if you define `greet` in the `.toys.rb` file in the current directory, and you also define it in the `.toys/greet.rb` file in the same current directory, Toys will also report an error, since both are defined at the same point (the current directory) in the search path. Note that in the search path above, steps (1) and (2) are *context-dependent*. That is, they may be different depending on what directory you are in. However, step (3) is *not* context-dependent, and is searched regardless of where you are located. Tools defined here are *global*, available everywhere. #### Stopping search Though it is uncommon practice, it is possible to stop the search process and prevent Toys from loading tools further down in the search path (e.g. prevent tools from being defined from parent directories or global directories). To do so, use the directive {Toys::DSL::Tool#truncate_load_path!}. This directive removes all directories further down the search path. It can be used, for example, to disable global tools when you run Toys from the current directory. It can also be useful if you are using [Bundler integration](#using-bundler-with-a-tool) to prevent bundle conflicts with parent directories, by disabling tools from parent directories. The `truncate_load_path!` directive works only if no tools from further down the search path have been loaded yet. It will raise {Toys::ToolDefinitionError} if it fails to truncate the load path. In most cases, Toys is very smart about loading tools only when needed, but there are exceptions. To minimize the chance of problems, if you need to use `truncate_load_path!`, locate it as early as possible in your Toys files, typically at the top of the [index file](#index-files). ### Sharing tool definitions It is a common practice to "share" tools across projects, that is, to implement tools in a central place and reference them from potentially multiple toys files. You can do this by "loading" remote tools from a toys file. You can load tools from a file path on your local file system, from a git repository, or even from a Ruby gem. #### Loading from the file system To load a tool from the file system, use the `load` directive, providing a path to a file or directory. This will behave as though the file or directory's contents were inserted in the current location. As an example, if you had a shared file `/var/share/mytools.rb` as follows: # /var/share/mytools.rb tool "foo" do def run puts "Running foo" end end tool "bar" do def run puts "Running bar" end end You could instantiate it in your toys file as follows: # .toys.rb load "/var/share/mytools.rb" This has the effect of copying the _contents_ of `mytools.rb` into where it is being referenced. In this case, it defines the two tools "foo" and "bar" at the top level of your toys file. Note that when you load a file, the name of the file does not define a tool or a tool namespace. In the above case, the tools "foo" and "bar" are defined, _not_ "mytools foo" or "mytools bar". Avoid the common mistake of referencing a file that defines at the top level and not loading it inside a tool. For example, consider a shared file `/var/share/single-tool.rb`: # /var/share/single-tool.rb desc "This file defines a tool at the top level" def run puts "Running the top level of single-tool.rb" end If you load it like follows: # .toys.rb load "/var/share/single-tool.rb" You will define the run method of the _root tool_. Instead, what you probably want is: # .toys.rb tool "my-single-tool" do load "/var/share/single-tool.rb" end Equivalently, you can say: # .toys.rb load "/var/share/single-tool.rb", as: "my-single-tool" You can also use the `load` directive to load an entire directory. Again, this will behave as though the directory's contents are inserted at the load point. The contents of any `.toys.rb` index file will be defined at the load point itself, and other files within the directory and subdirectories will be namespaced accordingly. As an example, consider this shared directory: /var/share/mytools | +- .toys.rb | +- greet.rb Here's the contents of `.toys.rb` above: # /var/share/mytools/.toys.rb tool "foo" do def run puts "Running foo" end end Here's the contents of `greet.rb`: # /var/share/mytools/greet.rb optional_arg :whom, default: "world" def run puts "Hello, #{whom}" end You can then define the tools "foo" and "greet" by loading this directory: # .toys.rb load "/var/share/mytools" #### Loading from a git repository You can also load tools over a network using git, by defining the tools in a git repository (e.g. on GitHub) and referencing it via the `load_git` directive. This is a simple way to distribute tools across an organization or over the internet. The `load_git` directive takes a repository URL, and optionally a path within the repository and a commit. If you do not specify the path, the entire repository is loaded as if you were loading a directory. (See the discussion above on loading from the file system.) Otherwise, if you specify a path, it should be a file or a directory within the repository. A commit can be a SHA, a branch, or a tag. If it is omitted, the default HEAD of the repository is used. As an example, suppose you have a repository `https://github.com/dazuma/example` (not a real repo...), with the file `greet.rb` at the top level. You can then reference that tool as follows: # .toys.rb load_git remote: "https://github.com/dazuma/example", path: "greet.rb", as: "greet" As discussed earlier, if you are loading a file that includes definitions at the top level, make sure you load it inside a tool, or use the `as:` parameter. The `load_git` directive uses the Git Cache to download and reference files in git repositories. This means if you omit the commit or set it to a reference that can float such as a branch or HEAD, it is possible you might get a stale commit, if the repository was previously pulled into the cache, and then additional commits were subsequently pushed to the remote repo. To ensure you get the latest data from `load_git`, you can set the `update:` parameter. If set to true, this forces the cache to refresh (i.e. git pull). You can also set it to an integer, representing a cache lifetime in seconds; the cache will refresh only if it has been at least that long since the last refresh. # .toys.rb load_git remote: "https://github.com/dazuma/example", path: "greet.rb", as: "greet", update: 3600 # Pull the latest HEAD if the cache is an hour old #### Loading from a Ruby gem Finally, you can define and distribute tools in a Ruby gem, and load them from the gem. This can be more involved as it requires creating and publishing a gem, but it is useful for versioning and public distribution of tools. A Ruby gem is basically just a zip file with some metadata. By convention, it contains Ruby classes (usually in a top level `lib` directory), and sometimes executables (often in a top level `bin` directory), and sometimes a few documentation files. However, it's just a zip file and you can put anything in it, including toys files. By convention, toys tools live in the top level `toys` directory in a gem. For example, you could publish a gem called "my-tools" and include `toys/greet.rb`. Then, to reference the tool: # .toys.rb load_gem "my-tools" This will define the tool "greet". In the process, it will ensure the gem is installed and loaded (prompting you to confirm the install from rubygems.org if necessary). You can specify gem version requirements by providing the `version:` parameter. For example: # .toys.rb load_gem "my-tools", version: ["~> 1.5", ">= 1.5.2"] It takes version requirements in the format used by Rubygems and Bundler. You can pass a single requirement, multiple requirements as an array, or none. You can also specify a subpath within the gem to load. The subpath is relative to the "toys" root directory in the gem. For example: # .toys.rb load_gem "my-tools", path: "greet.rb", as: "greet" As before, if you are loading a file that includes definitions at the top level, make sure you load it inside a tool, or use the `as:` parameter. By default, `load_gem` looks inside the `toys` top level directory of a gem. Although this is not recommended, if you need to change this default, you can set the gem's `"toys_dir"` metadata to a different directory name. If this metadata is set, `load_gem` will use it as the toys top level directory for that gem. You can also specify a different toys top level directory in the `load_gem` directive. This can be useful if a gem has multiple toys directories: # .toys.rb load_gem "my-tools", toys_dir: "uncommon-tools" #### Publishing a tools Ruby gem Publishing a Ruby gem that includes tools is as simple as publishing a gem with the tool files in an extra `toys` directory. Another benefit of distributing tools via Ruby gem is that the gem's library files can be used in the tools' implementations. When `load_gem` is run, the gem is activated so its library directory is made available in the require path. This may be simpler than using `.lib` directories as described later. By default, the `load_gem` mechanism expects toys files to live inside the `toys` top level directory of the gem. Although this is not recommended, if you need to change this default, you can set the gem's `"toys_dir"` metadata to a different directory name. If this metadata is set, `load_gem` will use it as the toys top level directory for that gem. In either case, the caller of the `load_gem` directive can still override it by passing the `:toys_dir` argument. ## Sharing code As you accumulate additional and more complex tools, you may find that some of your tools need to share some common configuration, data, or logic. You might, for example, have a set of admin scripts that need to do some common authentication. This section describes several techniques for sharing code between tools, and describes the scope of Ruby structures, such as methods, classes, and constants, that you might define in your tools. ### Defining mixins We [saw earlier](#helper-methods-and-mixins) that you can mix a module (with all its methods) into your tool using the `include` directive. You can specify a module itself, or the name of a built-in mixin such as `:exec` or `:terminal`. But you can also define your own mixin using the `mixin` directive. A mixin defined in a tool can be `include`d in that tool or any of its subtools or their subtools, recursively, so it's a useful way to share code. Here's how that works. Define a mixin using the `mixin` directive, and give it a name and a block. The mixin name must be a string. (Symbols are reserved for built-in mixins.) In the block, you can define methods that will be made available to any tool that includes the mixin, in the same way that you can include a Ruby module. (Note that, unlike full modules, mixins allow only methods to be shared. Mixins do not support constants. See the next section on [using constants](#using-constants) to learn how Toys handles constants.) Here's an example. Suppose you had common setup code that you wanted to share among your testing tools. tool "test" do # Define a mixin, which is just a collection of methods. mixin "common_test_code" do def setup # Do setup here end end tool "unit" do # Include the mixin by name include "common_test_code" def run setup # Mixin methods are made available puts "run only unit tests here..." end end tool "integration" do include "common_test_code" def run setup puts "run only integration tests here..." end end end A mixin is available to the tool in which it is defined, and any subtools and descendants defined at the same point in the Toys search path, but not from tools defined in a different point in the search path. For example, if you define a mixin in a file located in a `.toys` directory, it will be visible to descendant tools defined in that same directory, but not in a different `.toys` directory. A common technique, for example, would be to define a mixin in the [index file](#index-files) in a Toys directory. You can then include it from any subtools defined in other files in that same directory. #### Mixin initializers Sometimes a mixin will want to initialize some state before the tool executes. For example, the `:highline` mixin creates an instance of Highline during tool initialization. To do so, provide an `on_initialize` block in the mixin block. The initializer block is called within the context of the tool after arguments are parsed, so it has access to the tool's built-in context and options. If you provide extra arguments when you `include` a mixin, those are passed to the initializer block. For example, suppose the `"common_test_code"` mixin needs to behave differently depending on the type of tests (unit vs integration). Let's have the subtools pass a value to the mixin's initializer: tool "test" do mixin "common_test_code" do # Initialize the mixin, and receive the argument passed to the # include directive on_initialize do |type| # Initializers are called in the context of the tool, and so can # affect the tool's state. set(:test_type, type) end def setup puts "Setting up #{get(:test_type)}..." end end tool "unit" do # Pass an extra argument to include include "common_test_code", "unit" def run setup puts "run only unit tests here..." end end tool "integration" do include "common_test_code", "integration" def run setup puts "run only integration tests here..." end end end #### Mixin inclusion hooks A mixin can also optionally provide directives to run when the mixin is included, by defining an `on_include` block. (This is functionally similar to defining an `included` method on a Ruby module.) The `on_include` block is called within the context of the tool DSL, so it can invoke any DSL directives. If you provide extra arguments when you `include` a mixin, those are passed to the inclusion block. ### Using constants You can define and use Ruby constants, i.e. names beginning with a capital letter, in a Toys file. However, they are subject to Ruby's rules regarding constant scope and lookup, which can be confusing, especially in a DSL. Toys tries to simplify those rules and make constant behavior somewhat tractable, but if you do use constants (which includes modules and classes defined in a Toys file), it is important to understand how they work. Constants in Toys are visible only within the Toys file in which they are defined. They normally behave as though they are defined at the "top level" of the file. Even if you define a constant lexically "inside" a tool or a mixin, the constant does *not* end up connected to that tool or mixin; it is defined at the file level. tool "test" do tool "unit" do # This constant is now usable for the rest of the file API_KEY_FOR_TESTING = "12345" def run # It is visible here puts API_KEY_FOR_TESTING end end tool "integration" do def run # And it is still visible here puts API_KEY_FOR_TESTING end end end (Note it is still possible to attach constants to a tool or mixin by defining them with `self::`. However, this is uncommon Ruby practice and is mildly discouraged.) Because of this, it is highly recommended that you define constants only at the top level of a Toys file, so it doesn't "look" like it is scoped to something smaller. In particular, do not attempt to define constants in a mixin, unless you scope them with `self::`. Often a better alternative to a constant is to use the {Toys::DSL::Tool#static} directive to set a static value and create a method that returns it, similar to the `let` construct used in some test frameworks. See the section on [the options hash](#the-options-hash) for details on this technique. Modules and classes defined using the `module` or `class` keyword, are also constants, and thus follow the same rules. So you could, for example, define a "mixin" module like this: module CommonTestCode include Toys::Mixin def setup # Do setup here end end tool "test" do tool "unit" do # Include the modules as a mixin include CommonTestCode def run setup # Module methods are made available puts "run only unit tests here..." end end tool "integration" do include CommonTestCode def run setup puts "run only integration tests here..." end end end The difference between this technique and using the `mixin` directive we saw earlier, is the scope. The module here is accessed via a constant, and so, like any constant, it is visible only in the same file it is defined in. The `mixin` directive creates mixins that are visible from *all* files at the same point in the search path. Note also, when you define a mixin in this way, you should include `Toys::Mixin` in the module, as illustrated above. This makes `on_initialize` available in the module. ### Templates Another way to share code is to expand a **template**. A template is a class that inserts a bunch of lines into a Toys file. It is often used to "instantiate" prefabricated tools. For instance, Toys comes with a template called "minitest" that can generate a test tool for you. You instantiate it using the `expand` directive in your Toys file, like this: expand :minitest And it will generate a tool called "test" that runs your test suite. Most templates generate one or more complete tools. However, it is possible for a template to generate just part of a tool, such as one or more description directives. In general, expanding a template simply adds directives to your Toys file. Many templates can be configured with options such as the name of the tool to generate, or details of the tool's behavior. This is done by passing additional arguments to the `expand` directive, such as: expand :minitest, name: "unit-test", warnings: true Alternatively, you can provide a block to `expand`. It will yield the template to your block, letting you modify its properties: expand :minitest do |t| t.name = "unit-test" t.warnings = true end Toys provides several built-in templates that are useful for project and gem development, including templates that generate build, test, and documentation tools. The `:minitest` template illustrated above is one of these built-in templates. Like built-in mixins, built-in template names are always symbols. You can read more about them in the next section on using [Toys as a Rake replacement](#toys-as-a-rake-replacement). You can also write your own templates. Here's how... #### Defining templates One way to define a template is to use the `template` directive. Like the `mixin` directive, this creates a named template that you can access inside the current tool and any of its subtools. Also, like mixins, your template name must be a string. Following is a simple template example: template "greet" do def initialize(name: "greet", whom: "world") @name = name @whom = whom end attr_accessor :name attr_accessor :whom on_expand do |template| tool template.name do desc "A greeting tool generated from a template" static :whom, template.whom def run puts "Hello, #{whom}!" end end end end expand "greet" expand "greet", name: "greet-ruby", whom: "ruby" Above we created a template called "greet". A template is simply a class. It will typically have a constructor, and methods to access configuration properties. When the template is expanded, the class gets instantiated, and you can set those properties. Next, a template has an `on_expand` block. This block contains the Toys file directives that should be generated by the template. The template object is passed to the block, so it can access the template configuration when generating directives. The "greet" template in the above example generates a tool whose name is set by the template's `name` property. Notice in the above example, we used the {Toys::DSL::Tool#static} directive to create a "static" option for `template.whom`. A "static" option is an option whose value is set directly in code rather than via a flag or argument. We use it here because we can't access the `template` local variable from within the `run` method. Static options are a common technique for accessing template configuration properties from inside tool methods. By convention, it is a good idea for configuration options for your template to be settable *both* as arguments to the constructor, *and* as `attr_accessor` properties. In this way, when you expand the template, options can be provided either as arguments to the `expand` directive, or in a block passed to the directive by setting properties on the template object. #### Template classes Finally, templates are classes, and you can create a template directly as a class by including the {Toys::Template} module in your class definition. class GreetTemplate include Toys::Template def initialize(name: "greet", whom: "world") @name = name @whom = whom end attr_accessor :name attr_accessor :whom on_expand do |template| tool template.name do desc "A greeting tool generated from a template" static :whom, template.whom def run puts "Hello, #{whom}!" end end end end expand GreetTemplate, name: "greet-ruby", whom: "ruby" Remember that classes created this way are constants, and so the name `GreetTemplate` is available only inside the Toys file where it was defined. You must `include Toys::Template` if you define a template directly as a class, but you can omit it if you use the `template` directive to define the template in a block. Defining templates as classes is also a useful way for third-party gems to provide Toys integration. For example, suppose you are writing a code analysis gem, and you want to make it easy for your users to create a Toys tool that invokes your analysis. Just write a template class in your gem, maybe named `MyAnalysis::ToysTemplate`. Now, just instruct your users to include the following in their Toys file: require "my_analysis" expand MyAnalysis::ToysTemplate ### Loading from a lib directory For more complicated tools, you might want to write normal Ruby modules and classes as helpers. Toys provides a way to write Ruby code outside of its DSL and `require` the code from your tool, using `.lib` directories. Note: If you are writing tools for distribution in a Ruby gem as described [earlier](#publishing-a-tools-ruby-gem), consider simply defining your Ruby modules and classes in the gem's lib directory instead of using this mechanism. To use `.lib` directories, you must define your tools inside a [Toys directory](#toys-directories). When a tool is executed, it looks for directories called `.lib` in the Toys directory, and adds them to the Ruby load path. Your tool can thus call `require` to load helpers from any Ruby files in a `.lib` directory. For example, take the following directory structure: (current directory) | +- .toys/ | +- .lib/ <-- available when a tool is executed | | | +- greeting_helper.rb | +- greet.rb The `greeting_helper.rb` file can contain any Ruby code. # .toys/.lib/greeting_helper.rb module GreetingHelper def self.make_greeting(whom) "Hello, #{whom}!" end end Now you can `require "greeting_helper"` in your `greet` tool. # .toys/greet.rb tool "greet" do optional_arg :whom, default: "world", desc: "Whom to greet." def run require "greeting_helper" puts GreetingHelper.make_greeting(whom) end end Note that `.lib` directories are available only when your tool is being *run*, not when it is being defined. So any `require` statements should be located *inside* your `run` method. tool "greet" do # Do not try to require the file here. Toys will not find it because # the tool is not yet being run. # require "greeting_helper" # ERRORS! optional_arg :whom, default: "world", desc: "Whom to greet." def run # Require a helper file here, so it is loaded during tool execution. require "greeting_helper" # Now you can use classes defined in the helper puts GreetingHelper.make_greeting(whom) end end If your Toys directory has subdirectories, lib directories will be prioritized by how close they are to the tool being executed. For example: (current directory) | +- .toys/ | +- .lib/ <-- available when any tool defined in this directory | | is executed | | | +- helper.rb <-- visible to "greet" but overridden for "test unit" | | | +- helper2.rb <-- visible to both "greet" and "test unit" | +- greet.rb | +- test/ | +- .lib/ <-- available only when tools under "test" are executed | | | +- helper.rb <-- overrides the other helper.rb when | "test unit" is executed | +- unit.rb ### Preloading Ruby files You can also provide Ruby files that are "preloaded" before tools are defined. This is useful if those Ruby files are required by the tool definitions themselves. Like files in the `.lib` directory, preloaded files can define Ruby classes, modules, and other code. But preloaded files *automatically* loaded (i.e. you do not `require` them explicitly) *before* your tools are defined. To use preloaded files, you must define your tools inside a [Toys directory](#toys-directories). Before any tools inside a directory are loaded, any file named `.preload.rb` in the directory is automatically required. Next, any Ruby files inside a subdirectory called `.preload` are also automatically required. For example, take the following directory structure: (current directory) | +- .toys/ | +- .preload.rb <-- required first | +- greet.rb <-- defines "greet" (and subtools) | +- test/ | +- .preload/ | | | +- my_classes.rb <-- required before unit.rb | | | +- my_modules.rb <-- also required before unit.rb | +- unit.rb <-- defines "test unit" (and its subtools) Toys will execute require ".toys/.preload.rb" first before loading any of the tools in the `.toys` directory (or any of its subdirectories). Thus, you can define classes used by both the `greet` and the `test unit` tool in this file. Furthermore, Toys will also execute require ".toys/test/.preload/my_classes.rb" require ".toys/test/.preload/my_modules.rb" first before loading any of the tools in the `test` subdirectory. Thus, any additional classes needed by `test unit` can be defined in these files. Either a single `.preload.rb` file or a `.preload` directory, or both, can be used. If both are present in the same directory, the `.preload.rb` file is loaded first before the `.preload` directory contents. ### Applying directives to multiple tools Sometimes a group of tools are set up similarly or share a set of flags, mixins, or other directives. You can apply a set of directives to all subtools (recursively) of the current tool, using the {Toys::DSL::Tool#subtool_apply} directive. For example, it is common for tools to use the `:exec` built-in mixin to invoke external programs. You can use `subtool_apply` to ensure that the mixin is included in all subtools, so that you do not need to repeat the `include` directive in every tool. subtool_apply do # Include the mixin only if the tool hasn't already done so unless include?(:exec) include :exec, exit_on_nonzero_status: true end end tool "my-tool" do def run # This tool has access to methods defined by the :exec mixin # because the above block is applied to the tool sh "echo hello" end end Importantly, `subtool_apply` blocks are "applied" at the *end* of a tool's definition. Therefore, when using `subtool_apply`, you have the ability to look at the current definition of the tool to decide whether to apply further changes. The `subtool_apply` block in the above example uses this technique; it checks whether the `:exec` mixin has already been included before attempting to include it. Thus, it is possible for a tool to "override" the inclusion, say, to use a different configuration: tool "another-tool" do # Use a different configuration for the :exec mixin. # This "overrides" the subtool_apply block above. include :exec, exit_on_nonzero_status: false def run # This is run with exit_on_nonzero_status: false sh "echo hello" end end ## Testing your tools Tests play a critical part in the maintainability of any Ruby app. However, it can be difficult to set up tests for command line tools, Rake tasks, and similar code. Toys embraces testability by providing a dedicated place for tests, and a set of helpers that make it easy to test tools and parts of tools. ### Writing and running tests Toys integrates with [Minitest](https://github.com/seattlerb/minitest) to provide a testing framework for your tools. To write tests, you must define your tools inside a [Toys directory](#toys-directories). Create a directory called `.test` inside that directory. You can then write Minitest tests in files within that directory with names matching `test_*.rb`. You can also provide other files that do not match that name pattern, as fixture data or common test tools to load using `require_relative`. For example: (current directory) | +- .toys/ | +- greet.rb <-- defines "greet" (and subtools) | +- .test/ | +- test_greet.rb <-- includes tests | +- helper.rb <-- includes helper code but not tests Test files must be written against Minitest, and can use either unit test or spec syntax. (RSpec support is not currently available.) The framework will require `minitest/autorun` for you. Here is trivial example content for the `test_greet.rb` file: # test_greet.rb require_relative "helper" class MyTest < Minitest::Test def test_hello assert_equal "hello", MyHelper.hello end end And the `helper.rb` file: # helper.rb module MyHelper def self.hello "hello" end end You can run tests using: $ toys system test The built-in `system test` tool searches for the closest `.toys` directory, and runs any tests it finds. In the example above, `toys system test` will find the "hello" test in the file `.toys/.test/test_greet.rb`, run it, and display the results. Now, the example above defines a file called `test_greet.rb` but it doesn't actually test our `greet` tool. So let's cover how to invoke tools from tests so you can test their functionality. ### Testing tool functionality Included in the Toys gem is the helper module {Toys::Testing}, which provides methods that load and execute tools for testing purposes. You can access these methods by including this module in your test class or `describe` block: class MyTest < Minitest::Test include Toys::Testing # Write your tests.. end The simplest way to test a tool's functionality is by running it and observing the results. To run a tool, invoke the {Toys::Testing#toys_run_tool} method, passing in the name of the tool and its arguments as if you were invoking the tool from the command line. For example, recall our original `greet` tool that takes an optional argument and a `--shout` flag: # greet.rb desc "Print a friendly greeting." long_desc "Prints a friendly greeting. You can customize whom to" \ " greet, and how friendly it should be.", "", "Example:", [" toys greet --shout ruby"] optional_arg :whom, default: "world", desc: "Whom to greet." flag :shout, "-s", "--shout", desc: "Greet loudly." def run greeting = "Hello, #{whom}!" greeting = greeting.upcase if shout puts greeting end To execute the tool from a test, call `toys_run_tool` and pass it `["greet"]` as the command: class GreetTest < Minitest::Test include Toys::Testing def test_greet_with_no_arguments exit_code = toys_run_tool(["greet"]) assert_equal(0, exit_code) end end The `toys_run_tool` method executes the tool and returns the numeric exit code, which we can assert is 0 for a successful run. However, when we run this test, notice that "Hello, world!" is printed to the console in the middle of the test run. That is, of course, what our "greet" tool does. But normally, if you are testing a tool that writes to standard out, you'll want to assert against that output rather than have it pollute your test output. So typically, you'll wrap a call to `toys_run_tool` in a [`capture_subprocess_io`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-capture_subprocess_io) block, to capture the output. So here's a complete test for our `greet` tool: class GreetTest < Minitest::Test include Toys::Testing def test_greet_with_no_arguments out, err = capture_subprocess_io do exit_code = toys_run_tool(["greet"]) assert_equal(0, exit_code) end assert_equal("Hello, world!\n", out) assert_empty(err) end end Generally, you should test various use cases of your tool, including the effect of passing different arguments and flags. For example, here's a test of the `--shout` flag: class GreetTest < Minitest::Test include Toys::Testing def test_greet_with_shout_flag out, err = capture_subprocess_io do exit_code = toys_run_tool(["greet", "--shout"]) assert_equal(0, exit_code) end assert_equal("HELLO, WORLD!\n", out) assert_empty(err) end end It is important to note that `toys_run_tool` executes the tool *in-process* in your tests. This may be appropriate for simple tools, but may not work for other cases such as tools that replace the current process using [Kernel#exec](https://docs.ruby-lang.org/en/4.0/Kernel.html#method-i-exec), or tools that require user interaction. For these cases, you can run a tool in a separate forked process using the {Toys::Testing#toys_exec_tool} method, which we shall look at next. ### Running tools out of process The {Toys::Testing#toys_exec_tool} method, like {Toys::Testing#toys_run_tool}, executes a tool and lets you assert on its behavior. However, while `toys_run_tool` executes the tool *within the test process*, `toys_exec_tool` *forks a separate process* to run the tool. This insulates your tests from anything disruptive the tool might do (such as replacing the current process). It also provides access to the powerful subprocess control functionality provided by Toys, letting you redirect, capture, or interact with the tool's streams while it is running. There are two ways to run `toys_exec_tool`: in "capture" or "controller" mode. If you *do not* pass a block to `toys_exec_tool`, you are running in "capture" mode. It will close the input stream, and capture the output and error streams and return an object that provides the exit code and the stream contents. This object is actually an instance of {Toys::Utils::Exec::Result}, the same class returned by methods from the `:exec` mixin. Thus, we could use "capture" mode to rewrite our greeting test as follows: class GreetTest < Minitest::Test include Toys::Testing def test_greet_with_no_arguments result = toys_exec_tool(["greet"]) assert_equal(0, result.exit_code) assert_equal("Hello, world!\n", result.captured_out) assert_empty(result.captured_err) end end If you pass a block to `toys_exec_tool`, you will be running in "controller" mode. In this mode, all the tool's streams are redirected to a controller object that is passed to your block. See the controller's interface at {Toys::Utils::Exec::Controller} for more information. In either mode, you can still override many aspects of the subprocess's behavior. For example, "capture" closes the input stream by default, but you can override this and pass data into the tool's input stream by providing the `:in` keyword argument. The various arguments available are documented in the {Toys::Utils::Exec} class. Here is an example, passing a string into our tool's input stream: class GreetTest < Minitest::Test include Toys::Testing def test_greet_ignores_the_input_stream result = toys_exec_tool(["greet"], in: [:string, "I am ignored"]) assert_equal(0, result.exit_code) assert_equal("Hello, world!\n", result.captured_out) assert_empty(result.captured_err) end end It is important to note that `toys_exec_tool` requires the Ruby runtime and underlying operating system to support `fork`. Standard "MRI" Ruby running on a Mac or Linux system will work; but JRuby, TruffleRuby, and any Ruby running on Windows, will not support `fork` and will not be able to run tests that use `toys_exec_tool`. ### Testing helper methods The {Toys::Testing} module also provides a way to load tools and test individual methods without running the entire tool. This can be useful for writing unit tests for tools that are large, complex, or otherwise should not be executed in their entirety during testing. To test individual methods, pass a block to {Toys::Testing#toys_load_tool}. This method loads the tool associated with the given command line, parses the command line arguments, and prepares the tool for execution, but does not invoke the `run` method. The tool's context is passed to the block, and you can then execute any of the tool's helper methods and check assertions. Let's consider an example tool with a helper method: tool "hello" do flag :shout # The normal entrypoint "run" method def run puts message end # A helper method def message shout ? "HELLO" : "hello" end end Normally, testing this tool would involve capturing and asserting against the output printed by the `run` method. However, we can also test the `message` method in isolation without capturing any streams by loading the tool and calling the method directly. class MyTest < Minitest::Test include Toys::Testing # A test of the tool when passed no command-line arguments def test_message_without_shout # Load the tool toys_load_tool(["hello"]) do |tool| # The tool's context is passed to the block, and you can use it to # call the tool's methods. assert_equal("hello", tool.message) end end # Another test that includes a command-line flag def test_message_with_shout toys_load_tool(["hello", "--shout"]) do |tool| assert_equal("HELLO", tool.message) end end end Like `toys_run_tool`, `toys_load_tool` loads the tool *in-process* in your tests. This means it would be difficult to use it to test methods that replace the current process or depend on user interaction. It is not currently possible to fork `toys_load_tool` into a separate process because Minitest would not be able to track assertions in that separate process. It is therefore appropriate mainly for true unit tests of isolated methods. ### Organizing and selecting tests So far we learned how to define tests in a `.test` directory located directly within a `.toys` directory. However, it is also possible to locate `.test` directories within subdirectories. Just like tools and tool namespaces can be defined within a directory structure, so corresponding tests can be associated with those tools by putting them in the same subdirectories. Consider the following `.toys` directory that includes `build` and `deploy` tools: (current directory) | +- .toys/ | +- build/ | | | +- .toys.rb <-- defines "build" (and subtools) | | | +- .test/ | | | +- test_build.rb <-- defines tests for "build" | +- deploy/ | +- .toys.rb <-- defines "deploy" (and subtools) | +- .test/ | +- test_staging.rb <-- defines tests for "deploy" | +- test_prod.rb <-- defines more tests for "deploy" We've now created separate sets of tests under the `build/` and `deploy/` subdirectories. These are associated with those tools, and we can now run those sets of tests independently. To run just the tests under `build/`: $ toys system test --tool build To run just the tests under `deploy/`: $ toys system test --tool deploy Now, if you do not provide a specific tool: $ toys system test ...Toys will recursively search for `.test` directories, and run *all* the tests, including both the build and deploy tests. To disable this recursive search, pass `--no-recursive`. In the above case, that will result in no tests running because there are no tests defined at the top level. It is also possible to focus on specific tests using the [minitest-focus](https://github.com/seattlerb/minitest-focus) gem. Pass the `--minitest-focus` argument to `toys system test` to tell it to load the gem, and you will be able to use the `focus` keyword in your tests to temporarily focus on a few tests. Finally, you can limit your test run to specific test files by passing their paths as command line arguments to `toys system test`. For example, to run only the staging deployment tests: $ toys system test .toys/deploy/.test/test_staging.rb ### Gem dependencies in tests Running tests always requires using gems, at the very least the [minitest](https://rubygems.org/gems/minitest) gem. But they might also require auxiliary test framework gems such as [minitest-mock](https://rubygems.org/gems/minitest-mock), or gems used by the tools themselves. So you'll need a way to tell `toys system test` which gems it needs to load when running tests. The easiest way is to provide a Gemfile *for the tests*. This is a Gemfile located in the `.test` directory; if the file exists, `toys system test` will install and use it when running the tests. This Gemfile *must* include `minitest` itself, any other test framework gems needed by the tests, and any gems used by the tool itself. You can also pass arguments to `toys system test` to specify which gems to load *for tests that do not have a Gemfile*. Specifically, pass `--use-gem=[,]` for each gem needed beyond `minitest` itself. The versions are optional and can be any number of comma-delimited requirements using the Rubygems syntax. For example, you can use the `minitest-focus` and `minitest-mock` gems as follows: $ toys system test --use-gem=minitest-mock --use-gem=minitest-focus,~>1.4 The `minitest-mock`, `minitest-focus`, and `minitest-rg` gems are common enough that `toys system test` provides convenience flags for them: $ toys system test --minitest-mock --minitest-focus=~>1.4 Note that specifying gems via flag takes effect only for tests for which there is no Gemfile. If there is a Gemfile in the `.test` directory, it always overrides any flag-specified gems. Also note that such a Gemfile is distinct from a Gemfile that might be used by the tool itself, as described on the section on [using bundler](#using-bundler-with-a-tool). A testing gemfile must be located in the `.test` directory, whereas a tool Gemfile should be located at the top of the project directory or the `.toys` directory. If those Gemfiles are closely related, you could have one load the other or even symlink them together. ## Using third-party gems The toys executable itself uses only two gems: **toys** and **toys-core**. It has no other gem dependencies besides standard gems bundled with Ruby. However, the Ruby community has developed many resources for building command line tools, including a variety of gems that provide alternate command line parsing, control of the ANSI terminal, formatted output such as trees and tables, and effects such as hidden input, progress bars, various ways to spawn and control subprocesses, and so forth. You may find some of these gems useful when writing your tools. Additionally, if you are using Toys for your project's build scripts, it might be necessary to install your bundle when running some tools. This section describes how to manage and use external gems with Toys. Note that running Toys with `bundle exec` is generally *not* recommended. We'll discuss the reasons for this, and what you can do instead. ### Why not "bundle exec toys" [Bundler](https://bundler.io) is often used when a command-line program depends on external gems. You specify the gem dependencies in a `Gemfile`, use bundler to resolve and install those dependencies, and then run the program prefixed by `bundle exec` to ensure those dependencies are in the Ruby load path. When running a Rake task, for example, it is almost automatic for many Ruby developers to run `bundle exec rake my-task`. So why not simply run `bundle exec toys my-tool`? In simple cases, this will work just fine. However, Toys is a much more flexible tool than Rake, and it covers two cases that are not well served by `bundle exec`. First, Toys lets you define *global tools* that are defined in your home directory or system config directory. (See the previous section on [the Toys search path](#the-toys-search-path).) These tools are global, and can be called from anywhere. But if they have gem dependencies, it might not be feasible for their Gemfiles to be present in every directory from which you might want to run them. Second, it's possible for a variety of tools to be available together, including both locally and globally defined tools, with potentially different sets of dependencies. With `bundle exec`, you must choose beforehand which bundle to use. Although traditional `bundle exec` doesn't always work, Toys provides ways for individual tools to manage their own gem dependencies. ### Using bundler with a tool The recommended way for a Toys tool to depend on third-party gems is for the tool to set up Bundler when it runs. The tool can load a bundle from an appropriate `Gemfile` at runtime, by including the `:bundler` mixin. Here's an example. Suppose you are writing a tool in a Rails app. It might, for example, load the Rails environment and populate some data into the database. Hence, it needs to run with your app's bundle, represented by your app's `Gemfile`. Simply `include :bundler` in your tool definition: tool "populate-data" do include :bundler def run # The bundle will be set up before the tool is run, # so you can now run code that depends on rails: require "./config/environment.rb" # ... etc. end end When the `:bundler` mixin is included in a tool, it installs a [mixin initializer](#mixin-initializers) that calls `Bundler.setup` when the tool is *executed*. This assumes the bundle is already installed, and brings the appropriate gems into the Ruby load path. That is, it's basically the same as `bundle exec`, but it applies only to the running tool. #### Applying bundler to all subtools In many cases, you might find that bundler is needed for many or most of the tools you write for a particular project. In this case, you might find it convenient to use {Toys::DSL::Tool#subtool_apply} to include the bundle in all your tools. For example: # Include bundler in every tool under this one subtool_apply do include :bundler end tool "one-tool" do # This tool will run with the bundle # ... end tool "another-tool" do # So will this tool # ... end See the section on [applying directives to multiple tools](#applying-directives-to-multiple-tools) for more information on `subtool_apply`. #### Bundler options By default, the `:bundler` mixin will look for a `Gemfile` within the `.toys` directory (if your tool is defined in one), and if one is not found there, within the [context directory](#working-directory-and-context-directory) (the directory containing your `.toys` directory or `.toys.rb` file), and if one still is not found, in the current working directory. You can change this behavior by passing an option to the `:bundler` mixin. For example, you can search only the current working directory by passing `search_dirs: :current` as such: tool "populate-data" do include :bundler, search_dirs: :current # etc... end The `:search_dirs` option takes a either directory path (as a string) or a symbol indicating a "semantic" directory. You can also pass an array of directories that will be searched in order. For each directory, Toys will look for a file called `.gems.rb`, `gems.rb`, or `Gemfile` (in that order) and use the first one that it finds. The supported "semantic directory" symbols are `:current` indicating the current working directory, `:context` indicating the context directory, and `:toys` indicating the Toys directory in which the tool is defined. Furthermore, the semantic directory `:toys` is treated specially in that it looks up the `.toys` directory hierarchy. So if your tool is defined in `.toys/foo/bar.rb`, it will look for a Gemfile first in `.toys/foo/` and then in `.toys/`. Additionally, when looking for a Gemfile in `:toys`, it searches only for `.gems.rb` and `Gemfile`. A file called `gems.rb` is not treated as a Gemfile under the `:toys` directory, because it could be a tool. The default gemfile search path, if you do not provide the `search_dirs:` option, is equivalent to `[:toys, :context, :current]`. If the bundle is not installed, or is out of date, Toys will ask you whether you want it to install the bundle first before running the tool. A tool can also choose to install the bundle without prompting, or simply to raise an error, by passing another option to the `:bundler` mixin. For example, to simply install the bundle without asking for confirmation: tool "populate-data" do include :bundler, on_missing: :install # etc... end See the documentation for {Toys::StandardMixins::Bundler} for more information about bundler options. #### Solving bundle conflicts It is important to understand that the `:bundler` mixin installs the bundle when the tool *executes*, rather than when the tool is defined. Gems in the bundle will not be available during tool definition, so for example you *cannot* reference bundled gems when you are setting up the tool's flags, description, or other directives. This is so that Toys can define tools with competing bundles. Your Rails app's tools can use that app's bundle, while your global tools can use a different bundle. They will not conflict because Toys will not actually load a bundle until one or the other tool is executed. (This is of course different from using `bundle exec`, which chooses and loads a bundle before even starting Toys.) If a *different* bundle (i.e. a different `Gemfile`) is already in effect when a tool is run, then the `:bundler` mixin will raise an error. Ruby will not let you set up two different bundles at the same time. This might happen, for example, if you use `bundle exec` to run Toys, but the tool you are running asks for a different bundle---one more reason not to use `bundle exec` with Toys. It might also happen if one tool that uses one bundle, *calls* a tool that uses a different bundle. If you need to do this, use the {Toys::StandardMixins::Exec#exec_separate_tool} method from the `:exec` mixin, to call the tool. This method spawns a separate process with a clean Bundler setup for running the tool. #### Manual bundle setup By default, the `:bundler` mixin sets up the bundle (i.e. ensures gems are installed, and activates the resolved versions) at the beginning of tool execution. If you need greater control over the timing, or need to decide at runtime whether to use a bundle, pass the `setup: :manual` option when including the `:bundler` mixin. This will prevent the mixin from automatically setting up the bundle, but will define a `bundler_setup` method in your tool that you can call to set it up explicitly. For example: tool "my-tool" do include :bundler, setup: :manual flag :bundle, desc: "Use the bundle" def run # Set up the bundle only if the --bundle flag was invoked bundler_setup if bundle # Do other stuff... end end #### When a bundle is needed to define a tool Usually, the `:bundler` mixin sets up your bundle when the tool is *executed*. However, occasionally, you need the gems in the bundle to *define* a tool. This might happen, for instance, if your bundle includes gems that define mixins or templates used by your tool. If you need the bundle set up immediately because its gems are needed by the tool definition, pass the `setup: :static` option when including the `:bundler` mixin. For example, if you are using the [flame_server_toys](https://github.com/AlexWayfer/flame_server_toys) gem, which provides a template that generates tools for the [Flame](https://github.com/AlexWayfer/flame) web framework, you could include the `flame_server_toys` gem in your Gemfile, and make it available for defining tools: # Set up the bundle immediately. include :bundler, setup: :static # Now you can use the gems in the bundle when defining tools. require "flame_server_toys" expand FlameServerToys::Template There is a big caveat to using `setup: :static`, which is that you are setting up a bundle immediately, and as a result any subsequent attempt to set up or use a different bundle will fail. (See the section on [bundle conflicts](#solving-bundle-conflicts) for a discussion of other reasons this can happen.) As a result, it's best not to use `setup: :static` unless you *really* need it to define tools. If you do run into this problem, here are two things you could try: 1. "Scope" the bundle to the tool or the namespace where you need it. Toys makes an effort not to define a tool unless you actually need to execute it or one of its subtools, so if you can locate `include :bundler` inside just the tool or namespace that needs it, you might be able to avoid conflicts. 2. Failing that, if you need a particular gem in order to define a tool, you could consider activating the gem directly rather than as part of a bundle. See the following section on [Activating gems directly](#activating-gems-directly) for details on this technique. ### Activating gems directly Although we recommend the `:bundler` mixin for most cases, it is also possible for a tool to install individual gems, using the `:gems` mixin. This mixin provides a way for a tool to install individual gems without using Bundler. Here's an example tool that just runs `rake`. Because it requires rake to be installed in order to run the tool, we call the {Toys::StandardMixins::Gems#gem} method (which is provided by the `:gems` mixin) at the beginning of the `run` method: tool "rake" do include :gems remaining_args :rake_args def run gem "rake", "~> 12.0" Kernel.exec(["rake"] + rake_args) end end The `gem` method takes the name of the gem, and an optional set of version requirements. If a gem matching the given version requirements is installed, it is activated. If not, the gem is installed (which the user can confirm or abort). Or, if Toys is being run in a bundle, a message is printed informing the user that they need to add the gem to their Gemfile. If a gem satisfying the given version constraints is already activated, it remains active. If a gem with a conflicting version is already activated, an exception is raised. The `:gems` mixin also provides a `gem` *directive* that ensures a gem is installed while the tool is being defined. In general, we recommend avoiding doing this, because it could make your tool incompatible with another tool that might need a competing gem during its definition. Toys would not be able to define both tools together. However, occasionally it might be useful. Here's an example tool with flags for each of the HighLine styles. Because highline is needed to decide what flags to define, we use the `gem` directive to ensure highline is installed while the tool is being defined. tool "highline-styles-demo" do include :gems gem "highline", "~> 2.0" require "highline" HighLine::BuiltinStyles::STYLES.each do |style| style = style.downcase flag style.to_sym, "--#{style}", "Apply #{style} to the text" end def run # ... end end Note these methods are a bit different from the [gem method](https://docs.ruby-lang.org/en/4.0/Kernel.html#method-i-gem) provided by Rubygems. The Toys version attempts to install a missing gem for you, whereas Rubygems will just throw an exception. ### Activating gems outside the DSL The above techniques for installing a bundle or activating a gem directly, are all part of the tool definition DSL. However, the functionality is also available outside the DSL---for example, from a class-based mixin. To set up a bundle, call {Toys::Utils::Gems#bundle}. (Note that you must `require "toys/utils/gems"` explicitly before invoking the {Toys::Utils::Gems} class because, like all classes under `Toys::Utils`, Toys does not load it automatically.) For example: require "toys/utils/gems" gem_utils = Toys::Utils::Gems.new gem_utils.bundle(search_dirs: Dir.getwd) To activate single gems explicitly, call {Toys::Utils::Gems#activate}. For example: require "toys/utils/gems" gem_utils = Toys::Utils::Gems.new gem_utils.activate("highline", "~> 2.0") ### Useful gems Now that you know how to ensure a gem is installed, either individually or as part of a bundle, let's look at some third-party gems that you might find useful when writing tools. We already saw how to use the **highline** gem. Highline generally provides two features: terminal styling, and prompts. For these capabilities and many more, you might also consider [TTY](https://github.com/piotrmurach/tty). It comprises a suite of gems that you can use separately or in tandem. Here are a few examples. To produce styled output, consider [Pastel](https://github.com/piotrmurach/pastel). tool "fancy-output" do def run require "pastel" pastel = Pastel.new puts pastel.red("Rubies!") end end To create rich user prompts, consider [tty-prompt](https://github.com/piotrmurach/tty-prompt). tool "favorite-language" do def run require "tty-prompt" prompt = TTY::Prompt.new lang = prompt.select("What is your favorite language?", %w[Elixir Java Python Ruby Rust Other]) prompt.say("#{lang} is awesome!") end end To create tabular output, consider [tty-table](https://github.com/piotrmurach/tty-table). tool "matrix" do def run require "tty-table" table = TTY::Table.new(["Language", "Creator"], [["Ruby", "Matz"], ["Python", "Guido"], ["Elixir", "Jose"]]) puts table.render(:ascii) end end To show progress, consider [tty-progressbar](https://github.com/piotrmurach/tty-progressbar) for deterministic processes, or [tty-spinner](https://github.com/piotrmurach/tty-spinner) for non-deterministic. tool "waiting" do def run require "tty-progressbar" bar = TTY::ProgressBar.new("Waiting [:bar]", total: 30) 30.times do sleep(0.1) bar.advance(1) end end end Another interesting terminal UI system that just appeared recently (as of this writing in 2026) is [RatatuiRuby](https://www.ratatui-ruby.dev/), a Ruby wrapper around the Rust library Ratatui. RatatuiRuby pairs very well with Toys to build highly sophisticated terminal-based UIs. ## Toys as a Rake replacement Toys was designed to organize scripts that can be "scoped" to a project or directory. Rake is also commonly used for this purpose: you can write a "Rakefile" that defines rake tasks scoped to a directory. In many cases, Toys can be used as a replacement for Rake. Indeed, the Toys repository itself contains a `.toys.rb` file instead of a Rakefile, for running tests, builds, and so forth. This section will explore the differences between Toys and Rake, and describe how to use Toys for some of the things traditionally done with Rake. ### Comparing Toys and Rake Although Toys and Rake serve many of the same use cases, they have very different design goals, and it is useful to understand the differences. Rake's design is based on the classic "make" tool often provided in Unix development environments. This design focuses on *targets* and *dependencies*, and is meant for a world where you invoke an external compiler tool whenever changes are made to an individual source file or any of its dependencies. This "declarative" approach expresses very well the build process for programs written in C and similar compiled languages. Ruby, however, does not have an external compiler, and certainly not one that requires separate invocation for each source file as does the C compiler. So although Rake does support file dependencies, they are much less commonly used than in their Makefile cousins. Instead, in practice, most Rake tasks are not connected to a dependency at all; they are simply standalone scripts, what would be called "phony" targets in a Makefile. Such tasks are more imperative than declarative. The Toys approach to build tools simply embraces the fact that our build processes already tend to be imperative. So unlike Rake, Toys does not provide syntax for describing targets and dependencies, since we generally don't have them in Ruby programs. Instead, it is optimized for writing imperative tools. For example, Rake provides a primitive mechanism for passing arguments to a task, but it is clumsy and quite different from most Unix programs. However, to do otherwise would clash with Rake's design goal of treating tasks as targets and dependencies. Toys does not have those design goals, so it is able to embrace the familiar Unix conventions for command line arguments. Toys actually borrows some of its design from the "mix" build tool used for Elixir and Erlang programs. Unlike C, the Erlang and Elixir compilers do their own dependency management, so mix does not require those capabilities. Instead, it focuses on making it easy to define imperative tasks. All told, this boils down to the principle of using the best tool for the job. There will be times when you need to express file-based dependencies in some of your build tasks. Rake will continue to be your friend in those cases. However, for imperative tasks such as "run my tests", "build my YARD documentation", or "release my gem", you may find Toys easier to use. ### Using Toys to invoke Rake tasks If you've already written a Rakefile for your project, Toys provides a convenient way to invoke your existing Rake tasks using Toys. The built-in `:rake` template reads a Rakefile and automatically generates corresponding tools. In the same directory as your Rakefile, create a `.toys.rb` file with the following contents: # In .toys.rb expand :rake Now within that directory, if you had a Rake task called `test`, you can invoke it with: $ toys test Similarly, a Rake task named `test:integration` can be invoked with either of the following: $ toys test integration $ toys test:integration Rake tasks with arguments are mapped to tool arguments, making it easier to invoke those tasks using Toys. For example, consider a Rake task with two arguments, defined as follows: # In Rakefile task :my_task, [:first, :second] do |task, args| do_something_with args[:first] do_something_else_with args[:second] end would have to be invoked as follows using rake: $ rake my_task[value1,value2] You may even need to escape the brackets if you are using a shell that treats them specially. Toys will let you pass them as normal command line arguments: $ toys my_task value1 value2 The `:rake` template provides several options. If your Rakefile is named something other than `Rakefile` or isn't in the current directory, you can pass an explicit path to it when expanding the template: # In .toys.rb expand :rake, rakefile_path: "path/to/my_rakefile" You can also choose to pass arguments as named flags rather than command line arguments. Set `:use_flags` when expanding the template: # In .toys.rb expand :rake, use_flags: true Now with this option, to pass arguments to the tool, use the argument names as flags: $ toys my_task --first=value1 --second=value2 ### From Rakefiles to Toys files Invoking Rake tasks using Toys is an easy first step, but eventually you will likely want to migrate some of your project's build tasks from Rake to Toys. The remainder of this section describes the common patterns and features Toys provides for writing build tasks that are traditionally done with Rake. Many common Rake tasks can be generated using code provided by either Rake or the third party library. Different libraries provide different mechanisms for task generation. For example, a test task might be defined like this: require "rake/testtask" Rake::TestTask.new do |t| t.test_files = FileList["test/test*.rb"] end In Toys, templates are the standard mechanism for generating tools. expand :minitest do |t| t.files = ["test/test*.rb"] end The following sections will describe some of the built-in templates provided by Toys to generate common build tools. Note that Rakefiles and Toys files can coexist in the same directory, so you can use either or both tools, depending on your needs. It's also quite easy to invoke Rake from Toys and vice versa. ### Running tests Toys provides a built-in template called `:minitest` for running unit tests with [minitest](https://github.com/seattlerb/minitest). The following example directive uses the Minitest template to create a tool called `test` that runs Minitest-based tests using the current Gemfile: expand :minitest, bundler: true Now to run the tests, you can simply: $ toys test The `test` tool created by the `:minitest` template supports a variety of command line arguments that you can use to control which tests are run and how results are output. For example, the following command runs only the tests in a particular test file whose description does not contain "integration", and shows verbose results: $ toys test --verbose --exclude=integration test/test_foo.rb See the {Toys::Templates::Minitest} documentation for details on options you can pass to customize the `:minitest` template, and run `toys test --help` on the resulting tool for a detailed description of the command line arguments. Toys also provides a built-in template called `:rspec` for running BDD examples using [RSpec](http://rspec.info). The following example directive uses this template to create a tool called `spec`: expand :rspec, bundler: true See the {Toys::Templates::Rspec} documentation for details on the available options, and try `toys spec --help` for command line arguments. If you want to enforce code style using the [rubocop gem](https://rubygems.org/gems/rubocop), you can use the built-in `:rubocop` template. The following directive uses this template to create a tool called `rubocop`: expand :rubocop See the {Toys::Templates::Rubocop} documentation for details on the available options. ### Building and releasing gems The `:gem_build` built-in template can generate a variety of build and release tools for gems, and is a useful alternative to the Rake tasks provided by bundler. It is implemented by {Toys::Templates::GemBuild}. The following directive uses this template to create a tool called `build`: expand :gem_build The `:gem_build` template by default looks for a gemspec file in the current directory, and builds that gem into a `pkg` directory. You can also build a specific gem if you have multiple gemspec files. You can also configure the template so it also releases the gem to Rubygems (using your stored Rubygems credentials), by setting the `push_gem` option. For example, here is how to generate a "release" tool that builds and releases your gem: expand :gem_build, name: "release", push_gem: true See the {Toys::Templates::GemBuild} documentation for details on the various options for build tools. To create a "clean" tool, you can use the `:clean` built-in template. For example: expand :clean, paths: ["pkg", "doc", "tmp"] See the {Toys::Templates::Clean} documentation for details on the various options for clean. ### Building documentation Toys provides an `:rdoc` template for creating tools that generate RDoc documentation, and a `:yardoc` template for creating tools that generate YARD. Both templates provide a variety of options for controlling documentation generation. See {Toys::Templates::Rdoc} and {Toys::Templates::Yardoc} for detailed information. Here's an example for YARD, creating a tool called `yardoc`: expand :yardoc, protected: true, markup: "markdown" ### Gem example Let's look at a complete example that combines the techniques above to provide all the basic tools for a Ruby gem. It includes: * A testing tool that can be invoked using `toys test` * Code style checking using Rubocop, invoked using `toys rubocop` * Documentation building using Yardoc, invoked using `toys yardoc` * Gem building, invoked using `toys build` * Gem build and release to Rubygems.org, invoked using `toys release` * A full CI tool, invoked using `toys ci`, that can be run from your favorite CI system. It runs the tests and style checks, and checks (but does not actually build) the documentation for warnings and completeness. Below is the full annotated `.toys.rb` file. For many gems, you could drop this into the gem source repo with minimal or no modifications. Indeed, it is very similar to the Toys files provided for the **toys** and **toys-core** gems themselves. # This file is .toys.rb # A "clean" tool that cleans out anything matched by the gitignore. You can # also configure it to clean specific files and directories. expand :clean, paths: :gitignore # This is the "test" tool. expand :minitest, bundler: true, libs: ["lib", "test"] # This is the "rubocop" tool. expand :rubocop, bundler: true # This is the "yardoc" tool. We cause it to fail on warnings and if there # are any undocumented objects, which is useful for CI. We also configure # the tool so it recognizes the "--no-output" flag. The CI tool will use # this flag to invoke yardoc but suppress output, because it just wants to # check for warnings. expand :yardoc do |t| t.bundler = true t.generate_output_flag = true t.fail_on_warning = true t.fail_on_undocumented_objects = true end # The normal "build" tool that just builds a gem into the pkg directory. expand :gem_build # An "install" tool that builds the gem and installs it locally. expand :gem_build, name: "install", install_gem: true # A full gem "release" tool that builds the gem, and pushes it to rubygems. # This assumes your local rubygems configuration is set up with the proper # credentials. expand :gem_build, name: "release", push_gem: true # Now we create a full CI tool. It runs the test, rubocop, and yardoc tools # and checks for errors. This tool could be invoked from a CI system. tool "ci" do # The :exec mixin provides the exec_tool() method that we will use to run # other tools and check their exit status. include :exec # The :terminal mixin provides an enhanced "puts" method that lets you # write styled text to the terminal. include :terminal # A helper method, that runs a tool and outputs the result. It also # terminates if the tool reported an error. def run_stage(name, tool) if exec_tool(tool).success? puts("** #{name} passed", :green, :bold) puts else puts("** CI terminated: #{name} failed!", :red, :bold) exit(1) end end # The main run method. It just calls the above helper method for the # three tools we want to run for CI def run run_stage("Tests", ["test"]) run_stage("Style checker", ["rubocop"]) run_stage("Docs generation", ["yardoc", "--no-output"]) end end ## Additional tool definition features This section covers some additional features that are often useful for writing tools. Each of these features is very useful for certain types of tools, and it is good at least to know that you *can* do these things, even if you don't use them regularly. ### Standard base directories Command line tools often need access to common user-specific files such as caches and state data. The [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) specifies standard locations for these files and standard environment variables that can be used to override them. Toys provides easy access to these paths via the `:xdg` mixin. Here's a simple example: tool "my-tool" do include :xdg def run # Get config file paths, in order from most to least important config_files = xdg.lookup_config("my-tool/my-config.toml") config_files.each do |absolute_path| # Read the file... end end end The `lookup_config` method and many others are documented under the {Toys::Utils::XDG} class. ### Delegating tools A tool can **delegate** to another tool, which means it uses the other tool's flags, arguments, and execution. Effectively, it becomes an "alias"---that is, an alternate name---for the target tool. For example, suppose you have a tool called `test` that can be invoked with `toys test`. You could define a tool `t` that delegates to `test`. Then, running `toys t` will have the same effect as `toys test`. To delegate a tool, pass the `:delegate_to` keyword argument to the `tool` directive. For example: tool "test" do # Define test tool here... end tool "t", delegate_to: "test" Tools can delegate to tools or namespaces. For example, you can delegate `sys` to the built-in namespace `system`: tool "sys", delegate_to: "system" That will let you run `toys sys version` (which will be the equivalent of `toys system version`). To delegate to a subtool, pass an array, or a string delimited by `":"` or `"."` characters, as the target: tool "gem" do tool "test" do # Define the tool here end end tool "test", delegate_to: ["gem", "test"] In most cases, if a tool delegates to another tool, you should not do anything else with it. For example, it should not have its own implementation or contain any subtools. However, there are a few exceptions. You might, for example, want a namespace to delegate to one of its subtools: tool "test", delegate_to: ["test", "unit"] do tool "unit" do # Run unit tests end tool "integration" do # Run integration tests end end Now `toys test` delegates to, and thus has the same effect as `toys test unit`. It is also possible to specify the delegate via _relative_ name, using the `:delegate_relative` keyword argument. This can be helpful if you have a set of tools and delegates that are meant to be reused under different namespaces. ### Custom acceptors We saw earlier that flags and positional arguments can have acceptors, which control the allowed format, and can also convert the string argument to a Ruby object. By default, Toys supports the same acceptors recognized by Ruby's OptionParser library. And like OptionParser, Toys also lets you define your own acceptors. Define an acceptor using the `acceptor` directive. You provide a name for the acceptor, and specify how to validate input strings and how to convert input strings to Ruby objects. You can then reference the acceptor in that tool or any of its subtools or their subtools, recursively. There are several ways to define an acceptor. You can validate input strings against a regular expression, by passing the regex to the `acceptor` directive. You can also optionally provide a block to convert input strings to objects (or omit the block to use the original string as the option value.) For example, a simple hexadecimal input acceptor might look like this: acceptor("hex", /^[0-9a-fA-F]+$/) { |input| input.to_i(16) } You can also accept enum values by passing an array of valid values to the `acceptor` directive. Inputs will be matched against the `to_s` form of the given values, and will be converted to the value itself. For example, one way to accept integers from 1 to 5 is: acceptor("1to5", [1, 2, 3, 4, 5]) There are various other options. See the reference documentation for {Toys::DSL::Tool#acceptor}. An acceptor is available to the tool in which it is defined, and any subtools and descendants defined at the same point in the Toys search path, but not from tools defined in a different point in the search path. For example, if you define an acceptor in a file located in a `.toys` directory, it will be visible to descendant tools defined in that same directory, but not in a different `.toys` directory. A common technique, for example, would be to define an acceptor in the index file in a Toys directory. You can then include it from any subtools defined in other files in that same directory. ### Controlling built-in flags Earlier we saw that certain flags are added automatically to every tool: `--verbose`, `--quiet`, `--help`, and so forth. You may occasionally want to disable some of these "built-in" flags. There are two ways to do so: If you want to use one of the built-in flags for another purpose, simply define the flag as you choose. Flags explicitly defined by your tool take precedence over the built-ins. For example, normally two built-in flags are provided to decrease the verbosity level: `-q` and `--quiet`. If you define `-q` yourself (for example to activate a "quick" mode) then `-q` will be repurposed for your flag, but `--quiet` will still be present to decrease verbosity. # Repurposes -q to set the "quick" option instead of "quiet" flag :quick, "-q" You can also completely disable a flag, and *not* repurpose it, using the `disable_flag` directive. It lets you mark one or more flags as "never use". For example, if you disable the `-q` flag, then `-q` will no longer be a built-in flag that decreases the verbosity, but `--quiet` will remain. To completely disable decreasing the verbosity, disable both `-q` and `--quiet`. # Disables -q but leaves --quiet disable_flag "-q" # Completely disables decreasing verbosity disable_flag "-q", "--quiet" ### Enforcing flags before args By default, tools allow flags and positional arguments to be interspersed when command line arguments are parsed. This matches the behavior of most common command line binaries. However, some tools prefer to follow the convention that all flags must appear first, followed by positional arguments. In such a tool, once a non-flag argument appears on the command line, all remaining arguments are treated as positional, even if they look like a flag and start with a hyphen. You can configure a tool to follow this alternate parsing strategy using the `enforce_flags_before_args` directive. The built-in tool `toys do` is an example of a tool that does this. It recognizes its own flags (such as `--help` and `--delim`) but once positional arguments start appearing, it wants further flags to be treated as positional so it can pass them down to the different steps it is executing. Here is a simplified excerpt from the implementation that tool: tool "do" do flag :delim, default: "," remaining_args :commands # the commands to execute enforce_flags_before_args def run # Now commands includes both the commands to run and # the "flags" to pass to them. commands.each do # ... end end end ### Requiring exact flag matches By default, tools will recognize "shortened" forms of long flags. For example, suppose you are defining a tool with long flags: tool "my-tool" do flag :long_flag_name, "--long-flag-name" flag :another_long_flag, "--another-long-flag" def run # ... end end When you invoke this tool, you do not need to type the entire flag names. Abbreviations will also work: $ toys my-tool --long --an As long as the abbreviation is unambiguous (i.e. there is no other flag that begins with the same string), the Toys argument parser will recognize the flag. This is consistent with the behavior of most command line tools (and is also the behavior of Ruby's OptionParser library.) However, it is possible to disable this behavior and require that flags be presented in their entirety, using the `require_exact_flag_match` directive. tool "my-tool" do require_exact_flag_match flag :long_flag_name, "--long-flag-name" flag :another_long_flag, "--another-long-flag" def run # ... end end Now, all flags for this tool must be presented in their entirety. Abbreviations are not allowed. $ toys my-tool --long-flag-name --another-long-flag Currently you can require exact flag matches only at the tool level, applied to all flags for that tool. You cannot set this option for individual flags. ### Disabling argument parsing Normally Toys handles parsing command line arguments for you. This makes writing tools easier, and also allows Toys to generate documentation automatically for flags and arguments. However, occasionally you'll want Toys not to perform any parsing, but just to give you the command line arguments raw. One common case is if your tool turns around and passes its arguments verbatim to a subprocess. To disable argument parsing, use the `disable_argument_parsing` directive. This directive disables parsing and validation of flags and positional arguments. (Thus, it is incompatible with specifying any flags or arguments for the tool.) Instead, you can retrieve the raw arguments using the {Toys::Context#args} method. Here is an example that wraps calls to git: tool "my-git" do desc "Prints a message, and then calls git normally" disable_argument_parsing def run puts "Calling my-git!" Kernel.exec(["git"] + args) end end ### Handling signals If you interrupt a running tool by hitting `CTRL`-`C` or sending it a signal, Toys will normally terminate execution and display the message `INTERRUPTED` on the standard error stream. If your tool needs to handle signals or interrupts itself, you have several options. You can rescue the `SignalException` or call `Signal.trap`. Or you can provide a *signal handler* in your tool using the `on_signal` or `on_interrupt` directive. These directives either provide a block or designate a named method to handle a given signal received by the process. A separate handler must be provided for each signal type. (The `on_interrupt` directive is simply shorthand for registering a handler for `SIGINT`.) If a signal or interrupt is received and is not caught via `Signal.trap`, the following takes place: 1. Ruby will terminate the tool's `run` method by raising a `SignalException` Any `ensure` blocks in the tool will be called. 2. Toys will rescue the `SignalException` and call the signal handler, either a method or a block. If the handler takes an argument, Toys will pass it the `SignalException` object. 3. The signal handler is then responsible for tool execution from that point. It can terminate execution by returning or calling `exit`, or it can restart or resume processing (perhaps by calling the `run` method again). Or it can invoke the normal Toys signal handling (i.e. terminating execution and displaying the message `INTERRUPTED` or `SIGNAL RECEIVED`) by re-raising *the same* `SignalException` object. 4. If another signal is received or interrupt takes place during the execution of the handler, Toys will terminate the handler by raising a *second* `SignalException` (again calling any `ensure` blocks). Then, any matching signal handler will be called *again* for the new signal and passed the new exception. Any further signals will be handled similarly. It is possible for a signal handler itself to receive signals. For example, if you have a long-running `CTRL`-`C` interrupt handler, it itself could get interrupted. You can tell how many signals have taken place by looking at the `Exception#cause` property of the `SignalException`. The first signal will have a cause of `nil`. The second signal (i.e. the first time a signal handler itself receives a signal) will have a cause pointing to the first `SignalException` (which in turn has a `nil` cause). The third signal's cause points at the second, and so forth. Hence, you can determine the signal "depth" by counting the length of the cause chain, which could be important to prevent "infinite" signals. Here is an example that performs a long-running task. The first two times the task is interrupted, it is restarted. The third time, it is terminated. tool "long-running" do def long_task(is_restart) puts "task #{is_restart ? 're' : ''}starting..." sleep 10 puts "task finished!" end def run long_task(false) end on_interrupt do |ex| # The third interrupt will have a non-nil ex.cause.cause. # At that time, just give up and re-raise the exception, which causes # it to propagate out and invoke the standard Toys interrupt handler. raise ex if ex.cause&.cause # Otherwise, restart the long task. long_task(true) end end ### Handling usage errors Normally, if Toys detects a usage error (such as an unrecognized flag) while parsing arguments, it will respond by aborting the tool and displaying the usage error. It is possible to override this behavior by providing your own usage error handler using the `on_usage_error` directive. This directive either provides a block to handle usage errors, or designates a named method as the handler. If your handler block or method takes a parameter, Toys will pass it the array of usage errors. Otherwise, you can get the array by calling {Toys::Context#usage_errors}. This array will provide you with a list of the usage errors encountered. You can also get information about the arguments that could not be parsed from the context. For example, the list of unrecognized flags is available from the context key `UNMATCHED_FLAGS`. One common technique is to redirect usage errors back to the `run` method. In this way, `run` is called regardless of whether argument parsing succeeded or failed. tool "lenient-parser" do flag :abc on_usage_error :run def run if usage_errors.empty? puts "Usage was correct" else puts "Usage was not correct" end end end ### Changing the entrypoint The normal entrypoint for a tool is the `run` method. However, you can change it using the {Toys::DSL::Tool#to_run} directive, providing either a different method name as a symbol, or a block that will be called instead of a method to run the tool. One reason to change which method is called, is if you need to modify a tool that was already defined by some other means (like a template expansion). For example, the tests for the Toys gems include a number of longer-running "integration" tests that run only when the `TOYS_TEST_INTEGRATION` environment variable is set. The Toys gems provide standard `test` tools using the `:minitest` template, but then modify those tools to add an `--integration` flag. When this flag is set, the `TOYS_TEST_INTEGRATION` environment variable gets set, causing the integration tests to run. Here's what that looks like: # Use the minitest template to generate the normal test tool with the # normal "run" method as the entrypoint expand :minitest, libs: ["lib", "test"], bundler: true # Reopen the "test" tool generated above tool "test" do # Add a flag to the tool flag :integration # Create a new run method that "wraps" the existing method def run_with_integration # First set the environment variable to activate integration tests # if requested ENV["TOYS_TEST_INTEGRATION"] = "true" if integration # Invoke the original run method that was defined in the template run end # Now set the entrypoint to our new method to_run :run_with_integration end Passing a block to {Toys::DSL::Tool#to_run} is less common. One reason you might do it is if you need to access variables from an outer scope. (Normally, defining a method isolates your variables from any enclosing scopes.) Here is an example that prints out the time that the tool was *defined* (rather than executed). tool "def-time" do # Grab the time during tool definition time = Time.now # Define the entrypoint using a block rather than a method. # The block is still called in the same object context, but now has # access to variables from enclosing scopes. to_run do puts "Defined at #{time}" end end The downside of this technique is that the entrypoint is not a method, and can't be called like one. Which means, among other things, you can't "wrap" it like we did with the test tool above. If you need to access information from enclosing scopes, consider instead setting options using the {Toys::DSL::Tool#static} directive, as illustrated earlier when we discussed [defining templates](#defining-templates). tool "def-time" do # Grab the time during tool definition and set it as a static option. static :time, Time.now # Now you can access it from a run method like any other option def run puts "Defined at #{time}" end end ### Data files If your tools require images, archives, keys, or other such static data, Toys provides a convenient place to put data files that can be looked up by tools either during definition or runtime. To use data files, you must define your tools inside a [Toys directory](#toys-directories). Within the Toys directory, create a directory named `.data` and copy your data files there. You may then "find" a data file by providing the relative path to the file from the `.data` directory. When defining a tool, use the {Toys::DSL::Tool#find_data} directive in a Toys file. Or, at tool execution time, call {Toys::Context#find_data} (which is a convenience method for getting the tool source object using the `TOOL_SOURCE` key, and calling {Toys::SourceInfo#find_data} on it). In either case, `find_data` locates a matching file (or directory) among the data files, and returns the full path to that file system object. You can then read the file or perform any other operation on it. For example, take the following directory structure: (current directory) | +- .toys/ | +- .data/ | | | +- greeting.txt | | | +- desc/ | | | +- short.txt | +- greet.rb <-- defines "greet" (and subtools) The data files in `.toys/.data` are available to any tool in the `.toys` directory or any of its subdirectories. For example, suppose we want our "greet" tool to use the contents of `greeting.txt`. We can call `find_data` to read those contents when the tool is executed: # greet.rb desc "Print a friendly greeting." optional_arg :whom, default: "world", desc: "Whom to greet." def run greeting = IO.read(find_data("greeting.txt")).strip puts "#{greeting}, #{whom}!" end You can include directories in the argument to `find_data`. For example, here is how to use the `find_data` directive to read the short description from the file "desc/short.txt": # greet.rb desc IO.read(find_data("desc/short.txt")).strip optional_arg :whom, default: "world", desc: "Whom to greet." def run greeting = IO.read(find_data("greeting.txt")).strip puts "#{greeting}, #{whom}!" end The `find_data` mechanism will return the "closest" file or directory found. In the example below, there is a `desc/short.txt` file in the `.data` directory at the top level, but there is also a `desc/short.txt` file in the `.data` directory under `test`. Tools under the `test` directory will find the more specific data file, while other tools will find the more general file. (current directory) | +- .toys/ | +- .data/ | | | +- greeting.txt | | | +- desc/ | | | +- short.txt <-- default description for all tools | +- greet.rb <-- defines "greet" (and subtools) | +- test/ | +- .data/ | | | +- desc/ | | | +- short.txt <-- override description for test tools | +- unit.rb <-- defines "test unit" (and its subtools) If, however, you find `greeting.txt` from a tool under `test`, it will still find the more general `.toys/.data/greeting.txt` file because there is no overriding file under `.toys/test/.data`. ### Changing the context directory It is possible to modify the [context directory](#working-directory-and-context-directory), causing tools that use the context directory (such as the standard build tools) to run in a different directory. Here is an example: Suppose you have a repository with multiple gems, each in its own directory: my-repo/ | +- .toys.rb <-- all project tools defined here | +- gem1/ | | | +- lib/ | | | +- test/ | +- gem2/ | | | +- lib/ | | | +- test/ | etc... Assuming all the gems use the same set of build tools, it is possible to define those tools once in a single `.toys.rb` file and have it run in a particular gem directory depending on your current location. For example, you can cd into `gem1` or even `gem1/lib` to have the tools run on `gem1`. Because the standard build tools execute within the context directory, you can accomplish this by setting the context directory to the gem directory corresponding to the current location. That is, if the working directory is `my-repo/gem1/lib`, set the context directory to `my-repo/gem1`. Here's what that could look like: # .toys.rb content require "pathname" base_dir = Pathname.new(context_directory) cur_dir = Pathname.new(Dir.getwd) # The gem name is the first segment of the relative path from the context # directory to the current directory. relative_path = cur_dir.relative_path_from(base_dir).to_s gem_name = relative_path.split("/").first # Only proceed if we're truly in a subdirectory if gem_name && gem_name != "." && gem_name != ".." # Now set the context directory to the gem directory. set_context_directory(base_dir.join(gem_name).to_s) # Define the build tools. Each of these uses the custom context directory # set above, and thus runs for the selected gem. expand :minitest expand :gem_build # etc. end ### Hidden tools Tools whose name begins with an underscore (e.g. `_foo`) are called "hidden" tools. They can be executed the same as any other tool, but are normally omitted from the subtool list displayed in help and usage screens. You can use hidden tools as "internal" tools that are meant to be called only as part of the implementation of other tools. If you pass the `--all` flag when displaying help, the help screen will include hidden tools in the subtools list. ### Defining subtools using classes It is also possible to define subtools by subclassing `Toys::Tool`. The class will support the same DSL as a `tool` block would. For example, this is what our greet tool would look like as a class: class Greet < Toys::Tool optional_arg :whom, default: "world" flag :shout, "-s", "--shout" def run greeting = "Hello, #{whom}!" greeting = greeting.upcase if shout puts greeting end end Defining tools as classes is useful if you want your Toys files to look a bit more like normal Ruby or you want to avoid long blocks. Additionally, they can be useful to scope constants, which otherwise would be visible throughout the entire file, as noted in the [section on using constants](#using-constants). When you define a tool as a class, Toys infers the tool name by converting the class name to "kebab-case". For example, the `Greet` class above would define a tool called `greet`. If the class were called `GreetMany`, the tool would be called `greet-many`. If you need to override this behavior or set a tool name that is not possible from legal class names, pass the desired tool name to the `Toys.Tool` method like this: class Greet < Toys.Tool("_my_greet") optional_arg :whom, default: "world" flag :shout, "-s", "--shout" def run greeting = "Hello, #{whom}!" greeting = greeting.upcase if shout puts greeting end end You can create subtools by nesting classes: class Test < Toys::Tool class Unit < Toys::Tool def run puts "run only unit tests here..." end end class Integration < Toys::Tool def run puts "run only integration tests here..." end end end The above defines `test unit` and `test integration` tools as expected. Finally, you may not nest a class-based tool inside a block-based tool. (This is because Ruby's constant definition rules would put such a class in an unexpected namespace, leading to potential clashes that would be difficult to diagnose.) It is, however, permissible to nest a block-based tool inside a class-based tool. Hence, this is not allowed, and will result in an exception: tool "test" do # This will raise Toys::ToolDefinitionError... class Unit < Toys::Tool def run puts "run unit tests..." end end end But this is legal: class Test < Toys::Tool tool "unit" do def run puts "run unit tests..." end end end In general, though, as a best practice, I recommend against mixing the two styles. Define tools either as classes or as blocks, not both. ## Toys administration using the system tools Toys comes with a few built-in tools, including some that let you administer Toys itself. These tools live in the `system` namespace. ### Getting the Toys version You can get the current version of Toys by running: $ toys system version Note that the same output can be obtained by passing the `--version` flag to the root tool: $ toys --version ### Upgrading Toys To update Toys to the latest released version, run: $ toys system update This will determine the latest version from Rubygems, and update your Toys installation if it is not already current. Normally it asks you for confirmation before downloading. To disable interactive confirmation, pass the `--yes` flag. A similar effect can of course be obtained by running `gem install toys`. ### Installing tab completion Toys provides tab completion for Bash and Zsh, and lets tools customize the completions for their arguments. However, you need to install the Toys completion mechanism into your shell. #### Installing tab completion for Bash The following command sets up tab completion for the current Bash shell: $(toys system bash-completion install) Typically, you will want to include the above in your `.bashrc` or other Bash initialization file. By default, this associates the Toys tab completion logic with the `toys` executable. If you have other names or aliases for the executable, pass them as arguments. For example, if you use `t` as an alias for `toys`, you can install Toys's completion logic for `t`: $(toys system bash-completion install t) You can also remove the completion logic from the current shell: $(toys system bash-completion remove) $(toys system bash-completion remove t) #### Installing tab completion for Zsh The following command sets up tab completion for the current Zsh shell: $(toys system zsh-completion install) Typically, you will want to include the above in your `.zshrc` or other Zsh initialization file. You should ensure that `compinit` has been run before this line (for example, by calling `autoload -Uz compinit && compinit` earlier in your `.zshrc`). See `toys system zsh-completion --help` for more details. By default, this associates the Toys tab completion logic with the `toys` executable. If you have other names or aliases for the executable, pass them as arguments. For example, if you use `t` as an alias for `toys`, you can install Toys's completion logic for `t`: $(toys system zsh-completion install t) You can also remove the completion logic from the current shell: $(toys system zsh-completion remove) $(toys system zsh-completion remove t) ### Getting information about tools Sometimes you need to introspect the list of defined tools. For example, you might have a CI job that runs a Toys tool if available, but falls back to a Rake task if no Toys tools are defined. To determine whether a tool exists, you could use the status code returned from `system tools show`. Two operations are available under the `system tools` namespace, one to search and list tools, and another to get information about a specific tool. In both cases, you can view results as either YAML or JSON. For more information, see the help screens: $ toys system tools list --help $ toys system tools show --help ### Managing the Git cache Toys manages a cache of files downloaded from remote Git repositories. This is used, for example, to share tools via Git. You can manage the contents of the Toys Git Cache using subtools under the `system git-cache` namespace. This includes querying the cache and using it to download files from git, gathering information about cache status, and deleting old data. To display general information and a list of tools, run: $ toys system git-cache --help By default, files in the Git Cache are read-only. This is to prevent code that uses the cache from modifying those files in place and thus causing problems for other users of the cache. However, some environments that auto-delete cache directories might have a problem with this behavior, so the Git Cache supports an environment variable `TOYS_GIT_CACHE_WRITABLE` which, if set to a nonempty value, causes cache files to be writable. ## Writing your own CLI using Toys Although Toys is not primarily designed to help you write a custom command-line executable, you can use it in that way. Toys is factored into two gems: **toys-core**, which includes all the underlying machinery for creating command-line executables, and **toys**, which is really just a wrapper that provides the `toys` executable itself and its built-in commands and behavior. To write your own command line executable based on the Toys system, just require the **toys-core** gem and configure your executable the way you want. Toys-Core is modular and lets you customize much of the behavior of a command line executable, simply by setting options or adding plugins. For example: * Toys itself automatically adds a number of flags, such as `--verbose` and `--help`, to each tool. Toys-Core lets you customize what flags (if any) are automatically added for your own command line executable. * Toys itself provides a default way to run tools that have no `run` method. It assumes such tools are namespaces, and displays the online help screen. Toys-Core lets you provide an alternate default run method for your own command line executable. * Toys itself provides several built-in tools, such as `do`, and `system`. Toys-Core lets you write your own command line executable with its own built-in tools. * Toys itself implements a particular search path for user-provided Toys files, and looks for specific file and directory names such as `.toys.rb`. Toys-Core lets you change the search path, the file/directory names, or disable user-provided Toys files altogether for your own command line executable. Indeed, most command line executables do not need user-customizable tools, and can ship with only built-in tools. * Toys itself has a particular way of displaying online help and reporting errors. Toys-Core lets your own command line executable customize these and many other features. For more information, see the [Toys-Core documentation](https://dazuma.github.io/toys/gems/toys-core/latest/). toys-0.21.0/LICENSE.md0000644000004100000410000000210615164300675014232 0ustar www-datawww-data# License Copyright 2019-2023 Daniel Azuma and the Toys contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. toys-0.21.0/README.md0000644000004100000410000003623615164300675014120 0ustar www-datawww-data# Toys Toys is a configurable command line tool. Write commands in Ruby using a simple DSL, and Toys will provide the command line executable and take care of all the details such as argument parsing, online help, and error reporting. Toys is designed for software developers, IT professionals, and other power users who want to write and organize scripts to automate their workflows. It can also be used as a replacement for Rake, providing a more natural command line interface for your project's build tasks. Unlike most command line frameworks, Toys is *not primarily* designed for building and shipping a custom command line executable written in Ruby. However, you *can* use it in that way with the "toys-core" API, available as a separate gem. For more info on using toys-core, see [its documentation](https://dazuma.github.io/toys/gems/toys-core/latest). ## Introductory tutorial Here's a tutorial to help you get a feel of what Toys can do. ### Install and try out Toys Install the **toys** gem using: $ gem install toys This installs the `toys` executable, along with some built-in tools and libraries. You can run the executable immediately: $ toys This displays overall help for Toys. If you have `less` installed, Toys will use it to display the help screen. Press `q` to exit. You may notice that the help screen lists some tools that are pre-installed. Let's run one of them: $ toys system version The `system version` tool displays the current version of the toys gem. Toys also provides optional tab completion for bash. To install it, execute the following command in your shell, or add it to your bash configuration file (e.g. `~/.bashrc`). $(toys system bash-completion install) Toys does not yet specially implement tab completion for zsh or other shells. However, if you are using zsh, installing bash completion using `bashcompinit` *mostly* works. ### Write your first tool You can define tools by creating a *Toys file*. Go into any directory, and, using your favorite editor, create a new file called `.toys.rb` (note the leading period). Copy the following text into the file, and save it: tool "greet" do desc "My first tool!" flag :whom, default: "world" def run puts "Hello, #{whom}!" end end This defines a tool named "greet". Try running it: $ toys greet The tool also recognizes a flag on the command line. Try this: $ toys greet --whom=ruby Toys provides a rich set of features for defining command line arguments and flags. It can also validate arguments. Try this: $ toys greet --bye Notice that Toys automatically generated a usage summary for your tool. It also automatically generates a full help screen, which you can view using the `--help` flag: $ toys greet --help Toys searches up the directory hierarchy for Toys files. So it will find this `.toys.rb` if you are located in this directory or any subdirectory. It will also read multiple files if it finds them, so you can "scope" your tools more specifically or generally by locating them in your directory hierarchy. If you want to define "global" tools that apply anywhere, write a Toys file either in your home directory, or in the system configuration directory (usually `/etc`). Toys always searches these locations. ### A more sophisticated example Let's take a look at another example that exercises some of the features you're likely to see in real-world usage. Add the following to your `.toys.rb` file. (You don't need to replace the greet tool you just wrote; just add this new tool to the end of the file.) tool "new-repo" do desc "Create a new git repo" optional_arg :name, desc: "Name of the directory to create" include :exec, exit_on_nonzero_status: true include :fileutils include :terminal def run if name.nil? response = ask "Please enter a directory name: " set :name, response end if File.exist? name puts "Aborting because #{name} already exists", :red, :bold exit 1 end logger.info "Creating new repo in directory #{name}..." mkdir name cd name do create_repo end puts "Created repo in #{name}", :green, :bold end def create_repo exec "git init" File.write ".gitignore", <<~CONTENT tmp .DS_Store CONTENT # You can add additional files here. exec "git add ." exec "git commit -m 'Initial commit'" end end Now you should have an additional tool called `new-repo` available. Type: $ toys The help screen lists both the `greet` tool we started with, and the new `new-repo` tool. This new tool creates a directory containing a newly created git repo. (It assumes you have `git` available on your path.) Try running it: $ toys new-repo foo That should create a directory `foo`, initialize a git repository within it, and make a commit. Notice that this tool accepts a positional command line argument. Toys supports any combination of flags and required and optional arguments. This tool's argument is declared with a description string, which you can see if you view the tool's help: $ toys new-repo --help The argument is marked as "optional" which means you can omit it. Notice that the tool's code detects that it has been omitted and responds by prompting you interactively for a directory name. You can also mark a positional argument as "required", which causes Toys to report a usage error if it is omitted. Next, notice this tool includes two methods, `create_repo` as well as `run`. The "entrypoint" for a tool is always the `run` method, but each tool is actually a class under the hood, and you can add any helper methods you want. You can even define and include modules if you want to share code across tools. For our tool, notice that the three "include" lines are taking symbols rather than modules. These symbols are the names of some of Toys's built-in helper *mixins*, which are configurable modules that enhance your tool. They may provide methods your tool can call, or invoke other behavior. In our example: * The `:exec` mixin provides a variety of methods for running external commands. In this example, we use the `exec` method to run shell commands, but you can also signal and control these commands, capture and redirect streams, and so forth. Note that we pass the `:exit_on_nonzero_status` option, which configures the `:exec` mixin to abort the tool automatically if any of the external commands fails (similar to `set -e` in bash). This is a common pattern when writing tools that invoke external commands. (If you want more control, the `:exec` mixin also provides ways to respond to result codes individually.) * The `:fileutils` mixin provides the methods of the Ruby `FileUtils` library, such as `mkdir` and `cd` used in this example. It's effectively shorthand for `require "fileutils"; include ::FileUtils`. * The `:terminal` mixin provides styled output, as you can see with the style codes being passed to `puts`. It also provides some user interaction commands such as `ask`, as well as spinners and other controls. You can see operation of the `:terminal` mixin in the tool's output, which is styled either green (for success) or red (on error) when running on a supported tty. Now try running this: $ toys new-repo bar --verbose You'll notice some diagnostic log output. Toys provides a standard Ruby Logger for each tool, and you can use it to emit diagnostic logs directly as demonstrated in the example. Some other Toys features might also emit log entries: the `:exec` mixin, for example, by default logs every external command it runs (although this can be customized). By default, only warnings and higher severity logs are displayed, but you can change that by applying the `--verbose` or `--quiet` flags as we have done here. These flags, like `--help`, are provided automatically to every tool. ### A better Rake? Let's look at one more example. Traditionally, Ruby developers often use Rakefiles to write scripts for tasks such as build, test, and deploy. And Toys is similar to Rake in how it uses directory-scoped files to define tools. But Rake is really designed for dependency management, not for writing scripts. As a result, some features, such as passing arguments to a task, are very clumsy with Rake. If you have a project with a Rakefile, move into that directory and create a new file called `.toys.rb` in that same directory (next to the Rakefile). Add the following line to your `.toys.rb` file: expand :rake This syntax is called a "template expansion." It's a way to generate tools programmatically. In this case, Toys provides the `:rake` template, which reads your Rakefile and generates Toys tools corresponding to all your Rake tasks! Now if you run: $ toys You'll see that you now have tools associated with each of your Rake tasks. So if you have a `rake test` task, you can run it using `toys test`. Note that if you normally run Rake with Bundler (e.g. `bundle exec rake test`), you may need to add Toys to your Gemfile and use Bundler to invoke Toys (i.e. `bundle exec toys test`). This is because Toys is just calling the Rake API to run your task, and the Rake task might require the bundle. However, when Toys is not wrapping Rake, typical practice is actually *not* to use `bundle exec`. Toys provides its own mechanisms to manage bundles or install gems for you. So far, we've made Toys a front-end for your Rake tasks. This may be useful by itself. Toys lets you pass command line arguments "normally" to tools, whereas Rake requires a weird square bracket syntax (which may also require escaping depending on your shell.) Toys also provides more sophisticated online help than Rake does. But you also might find Toys a more natural way to *write* tasks, and indeed you can often rewrite an entire Rakefile as a Toys file and get quite a bit of benefit in readability and maintainability. For an example, see the [Toys file for the Toys gem itself](https://github.com/dazuma/toys/blob/main/toys/.toys/.toys.rb). It contains Toys scripts that I use to develop, test, and release Toys itself. Yes, Toys is self-hosted. You'll notice much of this Toys file consists of template expansions. Toys provides templates for a lot of common build, test, and release tasks for Ruby projects. If you're feeling adventurous, try translating some of your Rake tasks into native Toys tools. You can do so in your existing `.toys.rb` file. Keep the `expand :rake` line at the *end* of the file, and locate your tools (whether simple tools or template expansions) before it. That way, your Toys-native tools will take precedence, and `expand :rake` will proxy out to Rake only for the remaining tasks that haven't been ported explicitly. ### Learning more This introduction should be enough to get you started. However, Toys is a deep tool with many more features, all explained in detail in the [User Guide](https://dazuma.github.io/toys/gems/toys/latest/file.guide.html). For example, Toys lets you create tool namespaces and "subtools", and search for tools by name and description. There are various ways to validate and interpret command line arguments. You can create your own mixins and templates, and take advantage of a variety of third-party libraries such as Highline and TTY. Finally, if your `.toys.rb` files are growing too large or complicated, you can replace them with `.toys` directories that contain tool definitions in separate files. Such directories are versatile, letting you organize your tool definitions, along with shared code, normal Ruby classes, tests, and even data files for use by tools. You can find detailed usage information, including the entire DSL, in the [class reference documentation](https://dazuma.github.io/toys/gems/toys/latest/Toys.html) Unlike most command line frameworks, Toys is *not primarily* designed to help you build and ship a custom command line executable written in Ruby. However, you *can* use it in that way with the "toys-core" API, available as a separate gem. You would effectively write your command line executable using the same Toys DSL that you use to write `.toys.rb` files. For more info on using toys-core, see [its documentation](https://dazuma.github.io/toys/gems/toys-core/latest). ## Why Toys? I originally wrote Toys because I was accumulating dozens of *ad hoc* Ruby scripts I had written to automate various tasks in my workflow, everything from refreshing credentials, to displaying git history in my favorite format, to running builds and tests of complex multi-component projects. It was becoming difficult to remember which scripts did what, and what arguments each required, and I was constantly digging back into their source just to remember how to use them. Furthermore, when writing new scripts, I was repeating the same OptionParser boilerplate and common functionality. Toys was designed to address those problems by providing a framework for writing and organizing your own command line scripts. You provide the actual functionality by writing Toys files, and Toys takes care of all the other details expected from a good command line tool. It provides a streamlined interface for defining and handling command line flags and positional arguments, and sensible ways to organize shared code. It automatically generates help text so you can see usage information at a glance, provides a search feature to help you find the script you need, and generates tab completion for your shell. Toys can also be used to share scripts. For example, it can be used instead of Rake to provide build and test scripts for a project. Unlike Rake tasks, scripts written for Toys can be invoked and passed arguments and flags using familiar Unix command line conventions. The Toys GitHub repo itself comes with Toys scripts instead of Rakefiles. ## System requirements Toys requires Ruby 2.7 or later. Most parts of Toys work on JRuby. However, JRuby is not recommended because of JVM boot latency, lack of support for Kernel#fork, and other issues. Most parts of Toys work on TruffleRuby. However, TruffleRuby is not recommended because it has a few known bugs that affect Toys. ## License Copyright 2019-2025 Daniel Azuma and the Toys contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. toys-0.21.0/CHANGELOG.md0000644000004100000410000011140015164300675014435 0ustar www-datawww-data# Release History ### v0.21.0 / 2026-03-23 This release includes a variety of small fixes and updates toward improving product polish in preparation for a 1.0 release. It focuses on the following areas: * Updates to the help system * FIXED / BREAKING CHANGE: The help middleware omits the subtool filter flags on runnable tools that have subtools * Updates to bundler integration * FIXED: Fixed some cleanup issues when bundler setup fails * Updates to the exec utility * ADDED: Support for the `:unbundle` option which removes any existing bundle for a subprocess * FIXED: Fixed several thread-safety issues with the controller * FIXED: The result callback is now called in background mode if the result is never explicitly obtained from the controller * Updates to the git_cache utility * ADDED: Support for filtering lookups of ref and source info * ADDED: Support for SHA-256 repos * FIXED: Fixed failure to get a parent directory after previously getting a descendant object * FIXED: GitCache no longer unnecessarily sets the write bit on files copied into a specified directory * FIXED: GitCache no longer cleans out existing files when writing to a shared source, which should reduce issues with concurrent clients * Updates to the XDG utility * FIXED / BREAKING CHANGE: The XDG utility returns an empty array (instead of the system defaults) from the corresponding methods if `XDG_DATA_DIRS` or `XDG_CONFIG_DIRS` is nonempty but contains only relative paths * ADDED: Provided `lookup_state` and `lookup_cache` methods on the XDG utility * Updates to the settings system: * FIXED: Block certain reserved names from being used as Settings field names * FIXED: Fixed `Settings#load_data!` when subclassing another settings class * FIXED: Reject non-String values in Settings regexp type spec * FIXED: Settings guards against unknown classes when loading YAML on older Ruby versions * FIXED: Settings guards against `ILLEGAL_VALUE` being passed to `range.member?` * FIXED: Settings does a better job choosing exact-match values when using a union type * FIXED: Settings reject non-numeric strings in Integer and Float converters * FIXED: Settings propagates nested group errors in `Settings#load_data!` ### v0.20.0 / 2026-03-09 Toys 0.20 is a major release with several new features and a number of fixes, including a few minor breaking changes. Changes in the `:minitest` template: * BREAKING CHANGE: Tools produced by the `:minitest` template default to looking for test files of the forms `*_test.rb` and `test_*.rb` instead of `test*.rb`. * NEW: The `:minitest` template supports installing specified gems without using bundler. * NEW: Tools produced by the `:minitest` template provide command line arguments for overriding the gem installation or bundler settings. * NEW: Tools produced by the `:minitest` template recognize `--include` as an alias for `--name`. This matches recent versions of minitest. * NEW: The `:minitest` template generates more comprehensive documentation. * NEW: The minitest tool template now requires "minitest/autorun" before loading tests, so tests don't have to do so themselves. * BREAKING API CHANGE: Toys::Templates::Minitest::DEFAULT_GEM_VERSION_REQUIREMENTS is now a hash that covers multiple gems rather than just the minitest gem * FIXED: The minitest template no longer exceeds command line length limits if the list of test files is extremely long. New functionality in the `:rspec` template: * NEW: The `:rspec` template supports installing specified gems without using bundler. * NEW: Tools produced by the `:rspec` template provide command line arguments for overriding the gem installation or bundler settings. * NEW: Tools produced by the `:rspec` template recognize the `--example-matches` flag, and can handle multiple `--example` and `--tag` flags. * NEW: The `:rspec` template generates more comprehensive documentation. * BREAKING API CHANGE: Toys::Templates::Rspec::DEFAULT_GEM_VERSION_REQUIREMENTS is now a hash that covers potentially multiple gems New functionality in the `:clean` template: * The `:clean` template supports specifying certain gitignored files to preserve. * The `:clean` template is more robust against concurrent modification and works better with large git repos. New functionality in the `system test` builtin tool: * BREAKING CHANGE: The `system test` builtin looks for test files of the form `*_test.rb` in addition to `test_*.rb`. * The `system test` builtin uses bundler to install gems if a Gemfile is present in the `.test` directory. * The `system test` builtin supports flags that can specify arbitrary gems to load. Updates to the Exec mixin and library: * NEW: The new `Toys::Utils::Exec::Result#effective_result` method provides a reasonable integer result code even when a process terminates via signal or fails to start at all. * BREAKING API CHANGE: `:cli` is no longer a legal config option. * BREAKING API CHANGE: An options hash is no longer passed to the proc when executing a proc using a fork. * BREAKING API CHANGE: Passing an IO object as an input or output stream no longer closes it afterward. * BREAKING API CHANGE: `Toys::Utils::Exec::Controller#result` no longer preemptively (and prematurely) closes the controller input stream * FIXED: The `:unsetenv_others` option now works properly when executing a proc using a fork. * FIXED: Environment variable values specified as nil are now correctly unset when executing a proc using a fork. * FIXED: Fixed a rare concurrency issue if multiple threads concurrently get the result from a controller. Other changes: * NEW: Native tab completion for zsh. * NEW: The `:bundler` mixin supports "manual" bundle setup, allowing bundler decisions to be deferred to execution time * FIXED: The `:bundler` mixin will not attempt to add the `pathname` gem to generated Gemfiles when running on TruffleRuby. This caused issues because TruffleRuby includes a special version of the gem and cannot install the one from Rubygems. * FIXED: `ContextualError` no longer overrides `Exception#cause`, which could confuse TruffleRuby. * DOCUMENTATION: Updated user guide to cover zsh completion and manual bundler setup * DOCUMENTATION: Some reorganization and cleanup in the user guide ### v0.19.1 / 2026-01-06 * FIXED: The minitest template and the "system test" builtin now support minitest 6 ### v0.19.0 / 2025-12-22 Compatibility update for Ruby 4.0, including: * The logger gem is now an explicit dependency * Calling a tool via exec no longer disables rubygems * Bundler integration does a better job of cleaning up temporary lockfiles under bundler 4 Additionally, this release includes updates to readmes and users guides ### v0.18.0 / 2025-12-05 * ADDED: The load_gem directive can now take version requirements as positional arguments * ### v0.17.2 / 2025-11-30 * DOCS: Fixed minor typos in readme files ### v0.17.1 / 2025-11-07 * FIXED: Rolled back dependency on the logger gem because it is causing some issues with bundler integration ### v0.17.0 / 2025-11-07 Toys 0.17 includes several significant new pieces of functionality: * Support for loading tools from Rubygems. The load_gem directive loads tools from the "toys" directory in a gem, installing the gem if necessary. This makes it easy to distribute tools, securely and versioned, as gems. * Flag handlers can now take an optional third argument, the entire options hash. This enables significantly more powerful behavior during flag parsing, such as letting flags affect the behavior of other flags. Additional new features: * When using the :gems mixin, you can now specify installation options such as on_missing not only when you include the mixin, but also when you declare the gem. * Added support for an environment variable `TOYS_GIT_CACHE_WRITABLE` to disable the read-only behavior of git cache sources. This improves compatibility with environments that want to delete caches. Other fixes and documentation: * Added the standard logger gem to the toys-core dependencies to silence Ruby 3.5 warnings. * Updated the user guide to cover new features and fix some internal links ### v0.16.0 / 2025-10-31 * ADDED: Updated minimum Ruby version to 2.7 * ADDED: Rubocop template now creates tools that respond to Rubocop command line flags and arguments ### v0.15.6 / 2024-05-15 * FIXED: Fixed argument parsing to allow a flag values delimited by "=" to contain newlines * FIXED: Fixed minitest version failures in the system test builtin tool * FIXED: Fixed crash in the system test builtin tool's minitest-rg integration with minitest-rg 5.3 ### v0.15.5 / 2024-01-31 * FIXED: Fix for uri version mismatch error in certain bundler integration cases ### v0.15.4 / 2024-01-04 * FIXED: Fix error message when failing assertion of the toys version * DOCS: Various documentation improvements ### v0.15.3 / 2023-10-31 * FIXED: Minitest template defers calling autorun until after tests are loaded, which should eliminate stringio warnings when running Rails tests without bundler integration ### v0.15.2 / 2023-10-17 * FIXED: The gem_build template tried to use a badly formatted directory on Windows. ### v0.15.1 / 2023-10-15 * FIXED: Clean up some internal requires, which may improve performance with built-in gems. ### v0.15.0 / 2023-10-12 Toys 0.15.0 is a major release that adds arbitrary signal handling, cleans up some warts around entrypoint and method definition, and fixes a few long-standing issues. Breaking changes: * If a missing delegate or a delegation loop is detected, ToolDefinitionError is raised instead of RuntimeError. * Passing a block to the to_run directive no longer defines the "run" method, but simply uses the block as the run entrypoint. * The default algorithm for determining whether flags and arguments add methods now allows overriding of methods of Toys::Context and any other included modules, but prevents collisions with private methods defined in the tool. (It continues to prevent overriding of public methods of Object and BasicObject.) New functionality: * New DSL directive on_signal lets tools provide signal handlers. * You can pass a symbol to the to_run directive to set the entrypoint to a method other than "run". * Flags and arguments can be configured explicitly to add methods or not add methods, overriding the default behavior. * The minitest template has a configuration argument that defines MT_COMPAT. Fixes and documentation: * The Bundler integration prevents Bundler from attempting to self-update to the version specified in a lockfile (which would often cause problems when Bundler is called from Toys). * Tools generated by the minitest and rspec standard templates can now interact properly with stdin (e.g. so you can invoke pry temporarily from a test.) * Some cleanup of various mixins to prevent issues if their methods ever get overridden. * Various improvements and clarifications in the reference documentation and user guide. ### v0.14.7 / 2023-07-19 * FIXED: Fixed an exception when passing a non-string to puts in the terminal mixin ### v0.14.6 / 2023-06-29 * FIXED: Fixed a GitCache exception when loading a repository containing a broken symlink ### v0.14.5 / 2023-03-20 * FIXED: Rescue broken pipe errors by default when running a pager ### v0.14.4 / 2023-01-23 * (No significant changes) ### v0.14.3 / 2022-12-29 * FIXED: Exit with a code -1 if a non-integer exit code is thrown * FIXED: The sh command in the Exec utility returns -1 if the exit code cannot be determined * FIXED: Update Bundler integration to support Bundler 2.4 and Ruby 3.2 * FIXED: Fix for installing bundler on older Rubies * FIXED: Fixed XDG defaults on JRuby 9.4 ### v0.14.2 / 2022-10-09 * ADDED: The tool directive supports the delegate_relative argument, as a preferred alternative over alias_tool. * FIXED: The toys file reference now properly appears in error messages on Ruby 3.1. * FIXED: Error messages show the correct toys file line number on TruffleRuby. * FIXED: Inspect strings for tool classes are less opaque and include the tool name. * FIXED: The presence of an acceptor forces an ambiguous flag to take a value rather than erroring. ### v0.14.1 / 2022-10-03 * FIXED: Fixed a crash due to a missing file in the gem ### v0.14.0 / 2022-10-03 Toys 0.14.0 is a major release with new system tools for introspecting defined tools, pager support, support for tees and pipes in the Exec utility, some cleanup of the behavior of Acceptors, and other improvements. Fixes that are potentially breaking: * Disallowed acceptors on flags that are explicitly boolean. * Acceptors no longer sometimes apply to the boolean setting of a flag with an optional value. New functionality: * Implemented new builtins for obtaining information about defined tools: `system tools show` and `system tools list`. * Implemented a utility class and mixin for output pagers. * Builtin commands that display data can format as either YAML or JSON. * The Exec utility and mixin can tee (i.e. duplicate and split) output streams. * The Exec utility and mixin can take pipes as input and output streams. * The Exec mixin provides a `verbosity_flags` convenience method. Fixes: * The `system test` builtin no longer requires toys to be installed as a gem. * Fixed a failure when passing a relative path to `system test --directory`. * Contents of preload directories are loaded in sorted order. * Various clarifications, fixes, and updates to the users guide and documentation. ### v0.13.1 / 2022-03-01 * FIXED: Bundler integration no longer fails if a bundle was locked to a different version of a builtin gem ### v0.13.0 / 2022-02-08 Toys 0.13.0 is a major release with significant improvements to the testing framework and git cache, along with compatibility improvements and bug fixes. Breaking changes: * Passing `--directory=` to `toys system test` now restricts the search to *only* that directory, excluding global and builtin tools. * Renamed `Toys::Testing#exec_tool` to `Toys::Testing#toys_exec_tool`. * Removed the methods of `Toys::Testing` that were specific to "capture", "control", or "separate" spawning (i.e. `#exec_separate_tool`, `#capture_tool`, `#capture_separate_tool`, `#control_tool`, and `#control_separate_tool`). The general `Toys::Testing#toys_exec_tool` now either controls or captures depending on whether a block is present. Spawn-based execution is no longer available. New functionality: * Provided several builtin system tools for viewing and managing the git cache. * The `load_git` directive and the underlying `Toys::Utils::GitCache` class now support updating from git based on cache age. * The `Toys::Utils::GitCache` class supports supports copying git content into a provided directory, querying repo information, and deleting cache data. * The `Toys::Utils::GitCache` class makes files read-only, to help prevent clients from interfering with one another. * The `:terminal` mixin and the underlying `Toys::Utils::Terminal` class now honor the `NO_COLOR` environment variable. * `toys system test` now understands `--minitest-focus` and `--minitest-rg` to activate those plugins. * `toys system test` now understands `--minitest-version=` to set the minitest version requirement. * Added `Toys::Testing#toys_run_tool` to run a tool in-process for testing. * Added `Toys::Testing#toys_load_tool` to load a tool in-process and provide a way to test individual methods. * Added `:custom_paths` and `:include_builtins` arguments to the `Toys::StandardCLI` constructor. Fixes and compatibility: * Bundler install/updates are now spawned in subprocesses for compatibility with bundler 2.3. The bundler integration also now requires bundler 2.2 or later. * The `exec_tool` and `exec_proc` methods in the `:exec` mixin now log their execution in the same way as other exec functions. * Minor compatibility fixes to provide partial support for TruffleRuby. Other notes: * The internal GitCache representation has changed significantly to support additional features and improve robustness and performance. This will force existing caches to update, but should not break existing usage. * Significant updates to the readme and user guide, including a section in the user guide on the test framework. * Toys-core docs are now embedded into the toys gem, so that all the links will work in rubydoc.info. ### v0.12.2 / 2021-08-30 * FIXED: Tool context inspect string is no longer overwhelmingly long * FIXED: Fixed an exception in GitCache and load_git when updating a changed ref ### v0.12.1 / 2021-08-17 * FIXED: Fixed a regression in 0.12.0 where bundler could use the wrong Gemfile if you set a custom context directory ### v0.12.0 / 2021-08-05 Toys 0.12.0 is a major release with significant new features and bug fixes, and a few minor breaking changes. Additionally, this release now requires Ruby 2.4 or later. Breaking changes: * Defining a tool with whitespace, control characters, or certain punctuation in the name, now raises ToolDefinitionError. * The Toys::Tool class (for the object returned by the Toys::Context::Key::TOOL attribute) has been renamed to Toys::ToolDefinition so that the old name can be used for class-based tool definition. New functionality: * The DSL now supports a class-based tool definition syntax (in addition to the existing block-based syntax). Some users may prefer this new class-based style as more Ruby-like. * The subtool list on help screens is now split into sections by source directory * You can now load tools from a remote git repository using the load_git directive. * Whitespace is now automatically considered a name delimiter when defining tools. * There is experimental support for providing tests for tools. * There is now an extensible settings mechanism to activate less-common tool behavior. Currently there is one setting, which causes subtools to inherit their parent's methods by default. * The load directive can load into a new tool. * You can now set the context directory individually for the standard build tools. * Added a new standard mixin that provides XDG Base Directory information. * Added a new standard mixin that provides cached access to remote git repos. Fixes: * Fixed some bundler integration issues that occurred when the bundle is being installed in a separate path such as a vendor directory. * Exceptions raised from internal classes now include the full backtrace. ### v0.11.5 / 2021-03-28 * BREAKING CHANGE: The exit_on_nonzero_status option to exec now exits on signals and failures to spawn, in addition to error codes. * ADDED: Support retries in the bundler integration. * FIXED: Fix a bundler 2.2 integration issue that fails install in certain cases when an update is needed. * FIXED: Eliminate Rubygems warning on toys system update. ### v0.11.4 / 2020-10-11 * FIXED: Doesn't modify bundler lockfiles when adding Toys to a bundle * FIXED: Rdoc template now works on Ruby 3 ### v0.11.3 / 2020-09-13 * FIXED: The Exec mixin recognizes the argv0 option, and logs it appropriately ### v0.11.2 / 2020-09-06 * FIXED: Don't get confused when running toys from within a toys directory * FIXED: Fix a JRuby-specific race condition when capturing exec streams ### v0.11.1 / 2020-08-24 * FIXED: The `.lib` directory actually works as advertised. ### v0.11.0 / 2020-08-21 * ADDED: The toys search path can be truncated using the `truncate_load_path!` directive. * ADDED: The `:clean` template recognizes `:gitignore` as a path indicating all gitignored files. * IMPROVED: Generated help for delegates now includes the information for the target tool, plus subtools of the delegate. * IMPROVED: The `:bundler` mixin searches for `gems.rb` and `.gems.rb` in addition to `Gemfile`. * IMPROVED: The `:budnler` mixin can load a specific Gemfile path. * FIXED: The loader can now find `.data` and `.lib` directories at the root level of a `.toys` directory. * FIXED: Exec::Result correctly reports processes that terminated due to signals. * FIXED: Fixed a rare Exec capture failure that resulted from a race condition when closing streams. * DOCS: The Toys user guide now covers static bundle loading and `truncate_load_path!`. ### v0.10.5 / 2020-07-18 * IMPROVED: The bundler mixin silences bundler output during bundle setup. * IMPROVED: The bundler mixin allows toys and toys-core to be in the Gemfile. It checks their version requirements against the running Toys version, and either adds the corret version to the bundle or raises IncompatibleToysError. * IMPROVED: The bundler mixin automatically updates the bundle if install fails (typically because a transitive dependency has been explicitly updated.) * FIXED: Some cases of transitive dependency handling by the bundler mixin. * FIXED: Fixed a crash when computing suggestions, when running with a bundle on Ruby 2.6 or earlier. ### v0.10.4 / 2020-07-11 * IMPROVED: Bundler integration can now handle Toys itself being in the bundle, as long as the version requirements cover the running Toys version. * IMPROVED: Passing `static: true` to the `:bundler` mixin installs the bundle at definition rather than execution time. ### v0.10.3 / 2020-07-04 * FIXED: The `exec_separate_tool` method in the `:exec` mixin no longer throws ENOEXEC on Windows. ### v0.10.2 / 2020-07-03 * FIXED: The load path no longer loses the toys and toys-core directories after a bundle install. ### v0.10.1 / 2020-03-07 * FIXED: Setting `:exit_on_nonzero_status` explicitly to false now works as expected. ### v0.10.0 / 2020-02-24 * ADDED: `:bundler` mixin that installs and sets up a bundle for the tool * ADDED: `bundler` options in the standard templates, to run those tools in a bundle * ADDED: `subtool_apply` directive which applies a block to all subtools. * ADDED: Add `.lib` directories to the Ruby load path when executing a tool. * ADDED: `toys_version?` and `toys_version!` directives that check against version requirements. * ADDED: `exec_separate_tool` and `capture_separate_tool` methods in the `:exec` mixin, to support executing tools in a separate process without forking * IMPROVED: `long_desc` directive can now read the description from a text file. * IMPROVED: The `tool` directive can take delimited strings as tool names. * IMPROVED: Subtool blocks aren't actually executed unless the tool is needed. * CHANGED: Added `on_missing` and `on_conflict` arguments to `Toys::Utils::Gems` constructor (which also affects the `:gems` mixin), and deprecated `suppress_confirm` and `default_confirm`. * CHANGED: Tightened `rdoc` template's default gem version to `~> 6.1.0`. * FIXED: `rdoc` template crashed if any nonstandard options were given. * FIXED: `rubocop` template would abort prematurely if standard streams were redirected. ### v0.9.4 / 2020-01-26 * FIXED: Crash in the loader when a non-ruby file appears in a toys directory ### v0.9.3 / 2020-01-05 * FIXED: `delegate_to` directive could crash if an overriding tool has already been defined. * FIXED: A Ruby 2.7 warning when reporting a Toys file syntax error. ### v0.9.2 / 2020-01-03 * IMPROVED: Mixins can now take real keyword arguments, and will pass them on properly to `on_initialize` and `on_include` blocks. * CHANGED: `Toys::Utils::Exec` and the `:exec` mixin methods now take real keyword arguments rather than an `opts` hash. This means you should use keywords (or the double-splat operator) to avoid a deprecation warning on Ruby 2.7. ### v0.9.1 / 2019-12-22 * IMPROVED: `delegate_to` and `alias_tool` can take symbols as well as strings. * DOCS: Fixed user guide internal links on rubydoc.info. ### v0.9.0 / 2019-12-02 * ADDED: The `delegate_to` directive causes the tool to delegate execution to another tool. This means it takes the same arguments and has the same execution behavior. * ADDED: The `delegate_to` argument to the `tool` directive causes the tool to delegate to another tool. (Note: the `alias_tool` directive is now just shorthand for creating a tool with a delegate, and as such is mildly deprecated.) * ADDED: The `current_tool` function can be called from the DSL to get the current `Toys::Tool` object. * ADDED: The `:e` option is now an alias for `:exit_on_nonzero_status`. * IMPROVED: `alias_tool` is now just shorthand for delegating. This means, aliases can now point to namespaces and will resolve subtools of their targets, and they now support tab completion and online help. * IMPROVED: This release of Toys is now compatible with Ruby 2.7.0-preview3. It fixes some Ruby 2.7 specific bugs, and sanitizes keyword argument usage to eliminate Ruby 2.7 warnings. * IMPROVED: JRuby is now supported for most operations. However, JRuby is generally not recommended because of JVM boot latency, lack of Kernel#fork support, and other issues. * FIXED: The the `tool` directive no longer crashes if not passed a block. ### v0.8.1 / 2019-11-19 * FIXED: Listing subtools would crash if a broken alias was present. * DOCUMENTATION: Switched from redcarpet to kramdown, and tried to make some structural fixes. ### v0.8.0 / 2019-06-20 This is a major update with significant new features and a bunch of fixes. It does include a few minor backward-incompatible changes. All signifiant features planned for beta are now implemented. Highlights: * Tab completion is available for Bash! See the README for instructions on installing it. Tab completion covers tool names, flags, flag values, and positional arguments. Tools can also customize the completion for their own flag and argument values. * Toys now integrates with `did_you_mean` to provide suggestions for misspelled tools, flags, and arguments (when run on Ruby 2.4 or later.) * Tools can now provide their own interrupt handler to respond to user `CTRL-C`. And the default handler no longer displays an unsightly stack trace. Tools can also provide their own handler for usage errors. * A new argument parsing engine, supporting additional features such as optional enforcing that flags appear before positional arguments, as well as a bunch of fixes, especially around acceptors and optional flag values. * Changed the license from BSD to MIT to better match how most libraries in the Ruby community are licensed. Details: * CHANGED: Relicensed under the MIT License. * CHANGED: Requires Ruby 2.3 or later. * ADDED: Tab completion for bash. Args and flags can provide their own completion information. * ADDED: The usage error screen displays alternative suggestions when an argument is misspelled. (Requires Ruby 2.4 or later.) * ADDED: Tools can provide an interrupt handler. * ADDED: Tools can enforce that flags must be given before positional args. In particular, `toys do` now uses this feature, which eliminates most of the need to use `--` to get flags to work for subtools. * ADDED: Tools can control whether their flags can be invoked by partial matches. * ADDED: Function and range based acceptors. * ADDED: Flag handlers can accept the symbolic names `:set` and `:push` for common cases. * ADDED: The `:gem_build` template includes an `:install_gem` option. It also allows customization of gem output path. * ADDED: The `acceptor` directive takes an optional `type_desc` argument. * ADDED: The `accept` directives under flag and positional arg blocks in the DSL can now take blocks and `type_desc` values. * ADDED: Context keys `UNMATCHED_ARGS`, `UNMATCHED_POSITIONAL`, and `UNMATCHED_FLAGS` that provide arguments that were not handled during arg parsing. * ADDED: The Exec util and mixin support specifying a callback for process results. * ADDED: The Exec util and mixin provide a way to identify processes by name. * CHANGED: Toys now implements its own argument parsing and standard acceptors rather than relying on OptionParser. For the most part, OptionParser behavior is preserved, except in cases where there is clearly a bug. * CHANGED: Flags create a short form flag by default if the name has one character. * CHANGED: Flags with explicit value-less syntax are no longer given a value even if they specify a default or an acceptor. * CHANGED: Renamed the `TOOL_DEFINITION` context key to `TOOL`, and removed the `tool_definition` convenience method. * CHANGED: Removed the `BINARY_NAME` and `LOADER` context keys, and removed and the corresponding convenience methods. Get these values from the CLI if needed. * CHANGED: Renamed the `USAGE_ERROR` context key to `USAGE_ERRORS`, and the corresponding convenience method to `usage_errors`. The value is now a (possibly empty) array of `Toys::ArgParser::UsageError` objects rather than a string that isn't machine-parseable. * CHANGED: The root tool no longer defines remaining_args. * CHANGED: Renamed `to_expand` to `on_expand` in template definitions. * CHANGED: Renamed `to_initialize` to `on_initialize`, and `to_include` to `on_include` in mixin definitions. * CHANGED: Default descriptions for flag groups is now handled by the `set_default_descriptions` middleware rather than hard-coded in FlagGroup. * CHANGED: Exec reports failure to start processes in the result object rather than, e.g. raising ENOENT. * IMPROVED: Toys no longer displays a stack trace if a tool is interrupted. * IMPROVED: Error messages for flag groups are more complete. * IMPROVED: All context data, including well-known data, is available to be modified by flags and args. * FIXED: Flags with optional values are properly set to `true` (rather than left at `nil`) if no value is provided. * FIXED: Prevented toys-core from being ousted from the load path if a toys file invoked bundler setup. * FIXED: Acceptors no longer raise errors when run on missing optional values. * FIXED: When reporting errors in toys files, the line number was off by 2. * FIXED: The `--usage` help flag now honors `--all` and `--no-recursive`. * FIXED: The terminal now handles nil streams, as advertised. Additionally, a significant amount of internal reorganization and cleanup happened in the toys-core gem. See the changelog for toys-core for more details. ### v0.7.0 / 2019-01-23 * ADDED: A template for creating tools that invoke RSpec. * ADDED: Flag groups, which enforce policies around which flags are required. * CHANGED: Flags within a group are sorted in the help screens. * IMPROVED: The minitest template now honors all standard minitest flags. ### v0.6.1 / 2019-01-07 * FIXED: The presence of aliases caused subtool listing to crash. ### v0.6.0 / 2018-10-22 * FIXED: Build tools cd into the context directory when running. * FIXED: Rakefiles are evaluated and tasks are run in the Rakefile's directory. * ADDED: Context directory is available in the DSL and the tool runtime. * IMPROVED: Rake template searches parent directories for Rakefile. * IMPROVED: Rake tasks show the Rakefile path in the long description. * IMPROVED: Subtools whose names begin with underscore are no longer listed in help screens unless the `--all` flag is given. * IMPROVED: Non-runnable namespaces are no longer displayed in recursive subtool lists if their children are already displayed. ### v0.5.0 / 2018-10-07 * ADDED: Period and colon are recognized as tool path delimiters. * ADDED: New rake template that supports loading rake tasks as tools. * ADDED: Files named ".preload.rb" and files in a ".preload" directory are loaded before tools are defined. * ADDED: Directories named ".data" can contain data files accessible from tools. * ADDED: Passing "--tools" displays just the list of subtools of a tool * IMPROVED: The tool directive can now take an array as the tool name. * IMPROVED: The tool directive can now take an `if_defined` argument. * FIXED: Template instantiation was failing if the hosting tool was priority-masked. ### v0.4.5 / 2018-08-05 * CHANGED: Dropped preload file feature ### v0.4.4 / 2018-07-21 * FIXED: Utils::Exec wasn't closing streams after copying. * IMPROVED: Utils::Exec::Controller can capture or redirect the remainder of a controlled stream. * ADDED: Terminal#ask ### v0.4.3 / 2018-07-13 * IMPROVED: Exec mixin methods can now spawn subprocesses in the background * IMPROVED: Exec mixin capture methods can now yield a controller ### v0.4.2 / 2018-07-08 * FIXED: Raise an error rather than cause unexpected behavior if a mixin is included twice. * IMPROVED: The `include?` method extended to support mixin names in a tool dsl. ### v0.4.1 / 2018-07-03 * FIXED: Terminal#confirm uppercased "N" for the wrong default. ### v0.4.0 / 2018-07-03 Now declaring this alpha quality. Backward-incompatible changes are still possible from this point, but I'll try to avoid them. * CHANGED: Utils::Terminal#confirm default is now unset by default * CHANGED: Moved gem install/activation methods into a mixin * IMPROVED: Toys::Utils::Gems can suppress the confirmation prompt * IMPROVED: Magic comments are now honored in toys files. ### v0.3.11 / 2018-07-02 * CHANGED: Require Ruby 2.3 or later * CHANGED: Renamed "set" directive to "static" to reduce confusion with Tool#set. * ADDED: Convenience methods for getting option values ### v0.3.10 / 2018-06-30 * CHANGED: Dropped Tool#option. Use Tool#get instead. * CHANGED: "run" directive renamed to "to_run" * CHANGED: Highline mixin now uses Highline 2.0 * ADDED: Mixins can provide initializers ### v0.3.9.1 / 2018-06-24 * FIXED: Built-in flags were interfering with disable_argument_parsing ### v0.3.9 / 2018-06-24 * CHANGED: Removed alias_as directive since it's incompatible with selective loading. * ADDED: Ability to define named templates in Toys files * ADDED: Ability to disable argument parsing * ADDED: Rdoc template * ADDED: Exec#exec_proc and Exec#exec_tool that supports all the stream redirects * IMPROVED: Acceptors can be looked up recursively in the same way as mixins and templates * FIXED: Templates were not activating needed gems ### v0.3.8 / 2018-06-10 * CHANGED: Renamed helpers to mixins. * CHANGED: Renamed :in_from, :out_to, and :err_to exec options to :in, :out, :err * IMPROVED: Exec raises an error if passed an unknown option. * IMPROVED: Exec now accepts nearly all the same stream specifications as Process#spawn. ### v0.3.7.1 / 2018-05-30 * FIXED: Fix crash in system update. ### v0.3.7 / 2018-05-30 * CHANGED: Execution runs in the same scope as the DSL, which lets us use normal methods instead of helper-blocks. * CHANGED: Renamed "script" to "run", and allow setting of runnable by defining a "run" method * CHANGED: Set up a constant scope for each config file, to make constant lookup make sense. * CHANGED: Removed run_toys and dropped EXIT_ON_NONZERO_STATUS key in favor of using cli directly. * CHANGED: Removed spinner helper and added terminal helper. * ADDED: Helper modules scoped to the tool hierarchy ### v0.3.6 / 2018-05-21 * CHANGED: Removed Context#new_cli and exposed Context#cli instead. * CHANGED: Raises ToolDefinitionError if you declare a duplicate flag. * IMPROVED: Provide more details in default descriptions. * IMPROVED: Optional parameters are now supported for flags. * IMPROVED: Support custom acceptors. * IMPROVED: Highline helper automatically sets use_color based on the type of stdout. ### v0.3.5 / 2018-05-15 * CHANGED: Flag and arg blocks in the DSL have an interface more similar to the rest of the DSL. * CHANGED: Renamed `execute do` to `script do`. * IMPROVED: Help display uses `less` if available. ### v0.3.4 / 2018-05-14 * CHANGED: Renamed switch to flag * CHANGED: Renamed docs: parameter again, to desc: and long_desc: to match tool desc. * CHANGED: desc is now a single string rather than an array. * CHANGED: accept: parameter now controls whether a switch takes a value by default * IMPROVED: Nicer help page format * IMPROVED: gem_build template can suppress interactive confirmation. * IMPROVED: system update builtin can optionally ask for confirmation. * IMPROVED: Error reporting is significantly improved. * IMPROVED: Logger colors the header when possible. * IMPROVED: Style support for spinner helper * IMPROVED: Set default descriptions for flags and args * ADDED: Alias DSL methods `required`, `optional`, and `remaining`. * FIXED: Subtools with no desc now properly pick up the default * FIXED: Usage errors and show-help now interact in the right way ### v0.3.3 / 2018-05-09 * CHANGED: Renamed file_utils helper to fileutils. * CHANGED: Renamed doc: parameter to docs: * FIXED: Documentation strings that begin with "-" no longer cause problems. * ADDED: Highline helper * ADDED: Spinner helper * ADDED: WrappableString for descriptions and docs * IMPROVED: Descriptions can have multiple lines ### v0.3.2 / 2018-05-07 * CHANGED: Split core engine out into separate "toys-core" gem. See the toys-core changelog for additional changes in core. * CHANGED: Tools can no longer be alias_of. However, alias_as still works, and it is now possible to create an alias using alias_tool. * IMPROVED: The root tool now responds to the "--version" switch. * IMPROVED: Group help can now "--search" for subcommands. * IMPROVED: Help shows the config file path on "--verbose". * IMPROVED: You can now run a sub-instance of toys from an executor. ### v0.3.1 / 2018-05-02 * CHANGED: Subcommand display is now recursive by default. * IMPROVED: Improved error messaging for bad switch syntax. * FIXED: toys system update now reports experimental versions correctly. * FIXED: Subtools of an overridden group are now properly deleted. * DOCS: Completed a first pass on class and method documentation. * INTERNAL: Adjusted naming of switch-related methods. ### v0.3.0 / 2018-04-30 * Initial generally usable release toys-0.21.0/toys.gemspec0000644000004100000410000001204615164300675015175 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: toys 0.21.0 ruby lib Gem::Specification.new do |s| s.name = "toys".freeze s.version = "0.21.0".freeze s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/dazuma/toys/issues", "changelog_uri" => "https://dazuma.github.io/toys/gems/toys/v0.21.0/file.CHANGELOG.html", "documentation_uri" => "https://dazuma.github.io/toys/gems/toys/v0.21.0", "source_code_uri" => "https://github.com/dazuma/toys/tree/toys/v0.21.0/toys" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Daniel Azuma".freeze] s.date = "1980-01-02" s.description = "Toys is a configurable command line tool. Write commands in Ruby using a simple DSL, and Toys will provide the command line executable and take care of all the details such as argument parsing, online help, and error reporting. Toys is designed for software developers, IT professionals, and other power users who want to write and organize scripts to automate their workflows. It can also be used as a replacement for Rake, providing a more natural command line interface for your project's build tasks.".freeze s.email = ["dazuma@gmail.com".freeze] s.executables = ["toys".freeze] s.files = [".yardopts".freeze, "CHANGELOG.md".freeze, "LICENSE.md".freeze, "README.md".freeze, "bin/toys".freeze, "builtins/do.rb".freeze, "builtins/system/.toys.rb".freeze, "builtins/system/bash-completion.rb".freeze, "builtins/system/git-cache.rb".freeze, "builtins/system/test.rb".freeze, "builtins/system/tools.rb".freeze, "builtins/system/update.rb".freeze, "builtins/system/zsh-completion.rb".freeze, "core-docs/toys-core.rb".freeze, "core-docs/toys/acceptor.rb".freeze, "core-docs/toys/arg_parser.rb".freeze, "core-docs/toys/cli.rb".freeze, "core-docs/toys/compat.rb".freeze, "core-docs/toys/completion.rb".freeze, "core-docs/toys/context.rb".freeze, "core-docs/toys/core.rb".freeze, "core-docs/toys/dsl/base.rb".freeze, "core-docs/toys/dsl/flag.rb".freeze, "core-docs/toys/dsl/flag_group.rb".freeze, "core-docs/toys/dsl/internal.rb".freeze, "core-docs/toys/dsl/positional_arg.rb".freeze, "core-docs/toys/dsl/tool.rb".freeze, "core-docs/toys/errors.rb".freeze, "core-docs/toys/flag.rb".freeze, "core-docs/toys/flag_group.rb".freeze, "core-docs/toys/input_file.rb".freeze, "core-docs/toys/loader.rb".freeze, "core-docs/toys/middleware.rb".freeze, "core-docs/toys/mixin.rb".freeze, "core-docs/toys/module_lookup.rb".freeze, "core-docs/toys/positional_arg.rb".freeze, "core-docs/toys/settings.rb".freeze, "core-docs/toys/source_info.rb".freeze, "core-docs/toys/standard_middleware/add_verbosity_flags.rb".freeze, "core-docs/toys/standard_middleware/apply_config.rb".freeze, "core-docs/toys/standard_middleware/handle_usage_errors.rb".freeze, "core-docs/toys/standard_middleware/set_default_descriptions.rb".freeze, "core-docs/toys/standard_middleware/show_help.rb".freeze, "core-docs/toys/standard_middleware/show_root_version.rb".freeze, "core-docs/toys/standard_mixins/bundler.rb".freeze, "core-docs/toys/standard_mixins/exec.rb".freeze, "core-docs/toys/standard_mixins/fileutils.rb".freeze, "core-docs/toys/standard_mixins/gems.rb".freeze, "core-docs/toys/standard_mixins/git_cache.rb".freeze, "core-docs/toys/standard_mixins/highline.rb".freeze, "core-docs/toys/standard_mixins/pager.rb".freeze, "core-docs/toys/standard_mixins/terminal.rb".freeze, "core-docs/toys/standard_mixins/xdg.rb".freeze, "core-docs/toys/template.rb".freeze, "core-docs/toys/tool_definition.rb".freeze, "core-docs/toys/utils/completion_engine.rb".freeze, "core-docs/toys/utils/exec.rb".freeze, "core-docs/toys/utils/gems.rb".freeze, "core-docs/toys/utils/git_cache.rb".freeze, "core-docs/toys/utils/help_text.rb".freeze, "core-docs/toys/utils/pager.rb".freeze, "core-docs/toys/utils/standard_ui.rb".freeze, "core-docs/toys/utils/terminal.rb".freeze, "core-docs/toys/utils/xdg.rb".freeze, "core-docs/toys/wrappable_string.rb".freeze, "docs/guide.md".freeze, "lib/toys.rb".freeze, "lib/toys/standard_cli.rb".freeze, "lib/toys/templates/clean.rb".freeze, "lib/toys/templates/gem_build.rb".freeze, "lib/toys/templates/minitest.rb".freeze, "lib/toys/templates/rake.rb".freeze, "lib/toys/templates/rdoc.rb".freeze, "lib/toys/templates/rspec.rb".freeze, "lib/toys/templates/rubocop.rb".freeze, "lib/toys/templates/yardoc.rb".freeze, "lib/toys/testing.rb".freeze, "lib/toys/version.rb".freeze, "share/bash-completion-remove.sh".freeze, "share/bash-completion.sh".freeze, "share/zsh-completion-remove.sh".freeze, "share/zsh-completion.sh".freeze] s.homepage = "https://github.com/dazuma/toys".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.7.0".freeze) s.rubygems_version = "4.0.6".freeze s.summary = "A configurable command line tool".freeze s.specification_version = 4 s.add_runtime_dependency(%q.freeze, ["= 0.21.0".freeze]) end