toys-core 0.10.5 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +62 -36
- data/docs/guide.md +2 -0
- data/lib/toys/acceptor.rb +1 -1
- data/lib/toys/core.rb +1 -1
- data/lib/toys/dsl/flag.rb +2 -2
- data/lib/toys/dsl/flag_group.rb +2 -2
- data/lib/toys/dsl/tool.rb +27 -6
- data/lib/toys/loader.rb +71 -31
- data/lib/toys/source_info.rb +22 -23
- data/lib/toys/standard_mixins/bundler.rb +113 -56
- data/lib/toys/utils/exec.rb +126 -34
- data/lib/toys/utils/gems.rb +71 -20
- data/lib/toys/utils/help_text.rb +54 -59
- metadata +5 -5
    
        data/lib/toys/utils/gems.rb
    CHANGED
    
    | @@ -71,6 +71,12 @@ module Toys | |
| 71 71 | 
             
                  class IncompatibleToysError < BundlerFailedError
         | 
| 72 72 | 
             
                  end
         | 
| 73 73 |  | 
| 74 | 
            +
                  ##
         | 
| 75 | 
            +
                  # The gemfile names that are searched by default.
         | 
| 76 | 
            +
                  # @return [Array<String>]
         | 
| 77 | 
            +
                  #
         | 
| 78 | 
            +
                  DEFAULT_GEMFILE_NAMES = [".gems.rb", "gems.rb", "Gemfile"].freeze
         | 
| 79 | 
            +
             | 
| 74 80 | 
             
                  ##
         | 
| 75 81 | 
             
                  # Activate the given gem. If it is not present, attempt to install it (or
         | 
| 76 82 | 
             
                  # inform the user to update the bundle).
         | 
| @@ -149,27 +155,55 @@ module Toys | |
| 149 155 | 
             
                  end
         | 
| 150 156 |  | 
| 151 157 | 
             
                  ##
         | 
| 152 | 
            -
                  #  | 
| 158 | 
            +
                  # Search for an appropriate Gemfile, and set up the bundle.
         | 
| 159 | 
            +
                  #
         | 
| 160 | 
            +
                  # @param groups [Array<String>] The groups to include in setup.
         | 
| 161 | 
            +
                  #
         | 
| 162 | 
            +
                  # @param gemfile_path [String] The path to the Gemfile to use. If `nil`
         | 
| 163 | 
            +
                  #     or not given, the `:search_dirs` will be searched for a Gemfile.
         | 
| 164 | 
            +
                  #
         | 
| 165 | 
            +
                  # @param search_dirs [String,Array<String>] Directories in which to
         | 
| 166 | 
            +
                  #     search for a Gemfile, if gemfile_path is not given. You can provide
         | 
| 167 | 
            +
                  #     a single directory or an array of directories.
         | 
| 168 | 
            +
                  #
         | 
| 169 | 
            +
                  # @param gemfile_names [String,Array<String>] File names that are
         | 
| 170 | 
            +
                  #     recognized as Gemfiles, when searching because gemfile_path is not
         | 
| 171 | 
            +
                  #     given. Defaults to {DEFAULT_GEMFILE_NAMES}.
         | 
| 153 172 | 
             
                  #
         | 
| 154 | 
            -
                  # @param groups [Array<String>] The groups to include in setup
         | 
| 155 | 
            -
                  # @param search_dirs [Array<String>] Directories to search for a Gemfile
         | 
| 156 173 | 
             
                  # @return [void]
         | 
| 157 174 | 
             
                  #
         | 
| 158 175 | 
             
                  def bundle(groups: nil,
         | 
| 159 | 
            -
                              | 
| 176 | 
            +
                             gemfile_path: nil,
         | 
| 177 | 
            +
                             search_dirs: nil,
         | 
| 178 | 
            +
                             gemfile_names: nil)
         | 
| 179 | 
            +
                    Array(search_dirs).each do |dir|
         | 
| 180 | 
            +
                      break if gemfile_path
         | 
| 181 | 
            +
                      gemfile_path = Gems.find_gemfile(dir, gemfile_names: gemfile_names)
         | 
| 182 | 
            +
                    end
         | 
| 183 | 
            +
                    raise GemfileNotFoundError, "Gemfile not found" unless gemfile_path
         | 
| 160 184 | 
             
                    Gems.synchronize do
         | 
| 161 | 
            -
                      gemfile_path = find_gemfile(Array(search_dirs))
         | 
| 162 185 | 
             
                      if configure_gemfile(gemfile_path)
         | 
| 163 186 | 
             
                        activate("bundler", "~> 2.1")
         | 
| 164 187 | 
             
                        require "bundler"
         | 
| 165 | 
            -
                         | 
| 188 | 
            +
                        lockfile_path = find_lockfile_path(gemfile_path)
         | 
| 189 | 
            +
                        setup_bundle(gemfile_path, lockfile_path, groups || [])
         | 
| 166 190 | 
             
                      end
         | 
| 167 191 | 
             
                    end
         | 
| 168 192 | 
             
                  end
         | 
| 169 193 |  | 
| 194 | 
            +
                  # @private
         | 
| 195 | 
            +
                  def self.find_gemfile(search_dir, gemfile_names: nil)
         | 
| 196 | 
            +
                    gemfile_names ||= DEFAULT_GEMFILE_NAMES
         | 
| 197 | 
            +
                    Array(gemfile_names).each do |file|
         | 
| 198 | 
            +
                      gemfile_path = ::File.join(search_dir, file)
         | 
| 199 | 
            +
                      return gemfile_path if ::File.readable?(gemfile_path)
         | 
| 200 | 
            +
                    end
         | 
| 201 | 
            +
                    nil
         | 
| 202 | 
            +
                  end
         | 
| 203 | 
            +
             | 
| 170 204 | 
             
                  @global_mutex = ::Monitor.new
         | 
| 171 205 |  | 
| 172 | 
            -
                   | 
| 206 | 
            +
                  # @private
         | 
| 173 207 | 
             
                  def self.synchronize(&block)
         | 
| 174 208 | 
             
                    @global_mutex.synchronize(&block)
         | 
| 175 209 | 
             
                  end
         | 
| @@ -237,14 +271,6 @@ module Toys | |
| 237 271 | 
             
                    raise ActivationFailedError, err.message
         | 
| 238 272 | 
             
                  end
         | 
| 239 273 |  | 
| 240 | 
            -
                  def find_gemfile(search_dirs)
         | 
| 241 | 
            -
                    search_dirs.each do |dir|
         | 
| 242 | 
            -
                      gemfile_path = ::File.join(dir, "Gemfile")
         | 
| 243 | 
            -
                      return gemfile_path if ::File.readable?(gemfile_path)
         | 
| 244 | 
            -
                    end
         | 
| 245 | 
            -
                    raise GemfileNotFoundError, "Gemfile not found"
         | 
| 246 | 
            -
                  end
         | 
| 247 | 
            -
             | 
| 248 274 | 
             
                  def configure_gemfile(gemfile_path)
         | 
| 249 275 | 
             
                    old_path = ::ENV["BUNDLE_GEMFILE"]
         | 
| 250 276 | 
             
                    if old_path
         | 
| @@ -262,21 +288,46 @@ module Toys | |
| 262 288 | 
             
                    true
         | 
| 263 289 | 
             
                  end
         | 
| 264 290 |  | 
| 265 | 
            -
                  def  | 
| 291 | 
            +
                  def find_lockfile_path(gemfile_path)
         | 
| 292 | 
            +
                    if ::File.basename(gemfile_path) == "gems.rb"
         | 
| 293 | 
            +
                      ::File.join(::File.dirname(gemfile_path), "gems.locked")
         | 
| 294 | 
            +
                    else
         | 
| 295 | 
            +
                      gemfile_path + ".lock"
         | 
| 296 | 
            +
                    end
         | 
| 297 | 
            +
                  end
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                  def setup_bundle(gemfile_path, lockfile_path, groups)
         | 
| 300 | 
            +
                    old_lockfile_contents = save_old_lockfile(lockfile_path)
         | 
| 266 301 | 
             
                    begin
         | 
| 267 | 
            -
                      modify_bundle_definition(gemfile_path)
         | 
| 302 | 
            +
                      modify_bundle_definition(gemfile_path, lockfile_path)
         | 
| 268 303 | 
             
                      ::Bundler.ui.silence { ::Bundler.setup(*groups) }
         | 
| 269 304 | 
             
                    rescue ::Bundler::GemNotFound, ::Bundler::VersionConflict
         | 
| 270 305 | 
             
                      restore_toys_libs
         | 
| 271 306 | 
             
                      install_bundle(gemfile_path)
         | 
| 307 | 
            +
                      old_lockfile_contents = save_old_lockfile(lockfile_path)
         | 
| 272 308 | 
             
                      ::Bundler.reset!
         | 
| 273 | 
            -
                      modify_bundle_definition(gemfile_path)
         | 
| 309 | 
            +
                      modify_bundle_definition(gemfile_path, lockfile_path)
         | 
| 274 310 | 
             
                      ::Bundler.ui.silence { ::Bundler.setup(*groups) }
         | 
| 275 311 | 
             
                    end
         | 
| 276 312 | 
             
                    restore_toys_libs
         | 
| 313 | 
            +
                  ensure
         | 
| 314 | 
            +
                    restore_old_lockfile(lockfile_path, old_lockfile_contents)
         | 
| 315 | 
            +
                  end
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                  def save_old_lockfile(lockfile_path)
         | 
| 318 | 
            +
                    return nil unless ::File.readable?(lockfile_path) && ::File.writable?(lockfile_path)
         | 
| 319 | 
            +
                    ::File.read(lockfile_path)
         | 
| 320 | 
            +
                  end
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                  def restore_old_lockfile(lockfile_path, contents)
         | 
| 323 | 
            +
                    if contents
         | 
| 324 | 
            +
                      ::File.open(lockfile_path, "w") do |file|
         | 
| 325 | 
            +
                        file.write(contents)
         | 
| 326 | 
            +
                      end
         | 
| 327 | 
            +
                    end
         | 
| 277 328 | 
             
                  end
         | 
| 278 329 |  | 
| 279 | 
            -
                  def modify_bundle_definition(gemfile_path)
         | 
| 330 | 
            +
                  def modify_bundle_definition(gemfile_path, lockfile_path)
         | 
| 280 331 | 
             
                    builder = ::Bundler::Dsl.new
         | 
| 281 332 | 
             
                    builder.eval_gemfile(gemfile_path)
         | 
| 282 333 | 
             
                    toys_gems = ["toys-core"]
         | 
| @@ -287,7 +338,7 @@ module Toys | |
| 287 338 | 
             
                      add_gem_to_definition(builder, "toys")
         | 
| 288 339 | 
             
                      toys_gems << "toys"
         | 
| 289 340 | 
             
                    end
         | 
| 290 | 
            -
                    definition = builder.to_definition( | 
| 341 | 
            +
                    definition = builder.to_definition(lockfile_path, { gems: toys_gems })
         | 
| 291 342 | 
             
                    ::Bundler.instance_variable_set(:@definition, definition)
         | 
| 292 343 | 
             
                  end
         | 
| 293 344 |  | 
    
        data/lib/toys/utils/help_text.rb
    CHANGED
    
    | @@ -32,14 +32,13 @@ module Toys | |
| 32 32 | 
             
                  # @return [Toys::Utils::HelpText]
         | 
| 33 33 | 
             
                  #
         | 
| 34 34 | 
             
                  def self.from_context(context)
         | 
| 35 | 
            -
                     | 
| 36 | 
            -
                     | 
| 37 | 
            -
             | 
| 35 | 
            +
                    delegates = []
         | 
| 36 | 
            +
                    cur = context
         | 
| 37 | 
            +
                    while (cur = cur[Context::Key::DELEGATED_FROM])
         | 
| 38 | 
            +
                      delegates << cur[Context::Key::TOOL]
         | 
| 38 39 | 
             
                    end
         | 
| 39 | 
            -
                    delegate_target = orig_context == context ? nil : orig_context[Context::Key::TOOL_NAME]
         | 
| 40 40 | 
             
                    cli = context[Context::Key::CLI]
         | 
| 41 | 
            -
                    new(context[Context::Key::TOOL], cli.loader, cli.executable_name,
         | 
| 42 | 
            -
                        delegate_target: delegate_target)
         | 
| 41 | 
            +
                    new(context[Context::Key::TOOL], cli.loader, cli.executable_name, delegates: delegates)
         | 
| 43 42 | 
             
                  end
         | 
| 44 43 |  | 
| 45 44 | 
             
                  ##
         | 
| @@ -49,16 +48,15 @@ module Toys | |
| 49 48 | 
             
                  # @param loader [Toys::Loader] A loader that can provide subcommands.
         | 
| 50 49 | 
             
                  # @param executable_name [String] The name of the executable.
         | 
| 51 50 | 
             
                  #     e.g. `"toys"`.
         | 
| 52 | 
            -
                  # @param  | 
| 53 | 
            -
                  #     tool will delegate to. Default is `nil` for no delegation.
         | 
| 51 | 
            +
                  # @param delegates [Array<Toys::Tool>] The delegation path to the tool.
         | 
| 54 52 | 
             
                  #
         | 
| 55 53 | 
             
                  # @return [Toys::Utils::HelpText]
         | 
| 56 54 | 
             
                  #
         | 
| 57 | 
            -
                  def initialize(tool, loader, executable_name,  | 
| 55 | 
            +
                  def initialize(tool, loader, executable_name, delegates: [])
         | 
| 58 56 | 
             
                    @tool = tool
         | 
| 59 57 | 
             
                    @loader = loader
         | 
| 60 58 | 
             
                    @executable_name = executable_name
         | 
| 61 | 
            -
                    @ | 
| 59 | 
            +
                    @delegates = delegates
         | 
| 62 60 | 
             
                  end
         | 
| 63 61 |  | 
| 64 62 | 
             
                  ##
         | 
| @@ -88,8 +86,7 @@ module Toys | |
| 88 86 | 
             
                    indent ||= DEFAULT_INDENT
         | 
| 89 87 | 
             
                    subtools = find_subtools(recursive, nil, include_hidden)
         | 
| 90 88 | 
             
                    assembler = UsageStringAssembler.new(
         | 
| 91 | 
            -
                      @tool, @executable_name,  | 
| 92 | 
            -
                      indent, left_column_width, wrap_width
         | 
| 89 | 
            +
                      @tool, @executable_name, subtools, indent, left_column_width, wrap_width
         | 
| 93 90 | 
             
                    )
         | 
| 94 91 | 
             
                    assembler.result
         | 
| 95 92 | 
             
                  end
         | 
| @@ -121,7 +118,7 @@ module Toys | |
| 121 118 | 
             
                    indent2 ||= DEFAULT_INDENT
         | 
| 122 119 | 
             
                    subtools = find_subtools(recursive, search, include_hidden)
         | 
| 123 120 | 
             
                    assembler = HelpStringAssembler.new(
         | 
| 124 | 
            -
                      @tool, @executable_name, @ | 
| 121 | 
            +
                      @tool, @executable_name, @delegates, subtools, search, show_source_path,
         | 
| 125 122 | 
             
                      indent, indent2, wrap_width, styled
         | 
| 126 123 | 
             
                    )
         | 
| 127 124 | 
             
                    assembler.result
         | 
| @@ -155,22 +152,29 @@ module Toys | |
| 155 152 | 
             
                  private
         | 
| 156 153 |  | 
| 157 154 | 
             
                  def find_subtools(recursive, search, include_hidden)
         | 
| 158 | 
            -
                     | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 155 | 
            +
                    subtools_by_name = {}
         | 
| 156 | 
            +
                    ([@tool] + @delegates).each do |tool|
         | 
| 157 | 
            +
                      name_len = tool.full_name.length
         | 
| 158 | 
            +
                      subtools = @loader.list_subtools(tool.full_name,
         | 
| 159 | 
            +
                                                       recursive: recursive, include_hidden: include_hidden)
         | 
| 160 | 
            +
                      subtools.each do |subtool|
         | 
| 161 | 
            +
                        local_name = subtool.full_name.slice(name_len..-1).join(" ")
         | 
| 162 | 
            +
                        subtools_by_name[local_name] = subtool
         | 
| 163 | 
            +
                      end
         | 
| 164 | 
            +
                    end
         | 
| 165 | 
            +
                    subtool_list = subtools_by_name.sort_by { |(local_name, _tool)| local_name }
         | 
| 166 | 
            +
                    return subtool_list if search.nil? || search.empty?
         | 
| 161 167 | 
             
                    regex = ::Regexp.new(search, ::Regexp::IGNORECASE)
         | 
| 162 | 
            -
                     | 
| 163 | 
            -
                      regex =~  | 
| 168 | 
            +
                    subtool_list.find_all do |local_name, tool|
         | 
| 169 | 
            +
                      regex =~ local_name || regex =~ tool.desc.to_s
         | 
| 164 170 | 
             
                    end
         | 
| 165 171 | 
             
                  end
         | 
| 166 172 |  | 
| 167 173 | 
             
                  ## @private
         | 
| 168 174 | 
             
                  class UsageStringAssembler
         | 
| 169 | 
            -
                    def initialize(tool, executable_name,  | 
| 170 | 
            -
                                   indent, left_column_width, wrap_width)
         | 
| 175 | 
            +
                    def initialize(tool, executable_name, subtools, indent, left_column_width, wrap_width)
         | 
| 171 176 | 
             
                      @tool = tool
         | 
| 172 177 | 
             
                      @executable_name = executable_name
         | 
| 173 | 
            -
                      @delegate_target = delegate_target
         | 
| 174 178 | 
             
                      @subtools = subtools
         | 
| 175 179 | 
             
                      @indent = indent
         | 
| 176 180 | 
             
                      @left_column_width = left_column_width
         | 
| @@ -195,7 +199,7 @@ module Toys | |
| 195 199 | 
             
                    def add_synopsis_section
         | 
| 196 200 | 
             
                      synopses = []
         | 
| 197 201 | 
             
                      synopses << namespace_synopsis unless @subtools.empty?
         | 
| 198 | 
            -
                      synopses <<  | 
| 202 | 
            +
                      synopses << tool_synopsis
         | 
| 199 203 | 
             
                      first = true
         | 
| 200 204 | 
             
                      synopses.each do |synopsis|
         | 
| 201 205 | 
             
                        @lines << (first ? "Usage:  #{synopsis}" : "        #{synopsis}")
         | 
| @@ -212,11 +216,6 @@ module Toys | |
| 212 216 | 
             
                      synopsis.join(" ")
         | 
| 213 217 | 
             
                    end
         | 
| 214 218 |  | 
| 215 | 
            -
                    def delegate_synopsis
         | 
| 216 | 
            -
                      target = @delegate_target.join(" ")
         | 
| 217 | 
            -
                      "#{@executable_name} #{@tool.display_name} [ARGUMENTS FOR \"#{target}\"...]"
         | 
| 218 | 
            -
                    end
         | 
| 219 | 
            -
             | 
| 220 219 | 
             
                    def namespace_synopsis
         | 
| 221 220 | 
             
                      "#{@executable_name} #{@tool.display_name} TOOL [ARGUMENTS...]"
         | 
| 222 221 | 
             
                    end
         | 
| @@ -256,12 +255,10 @@ module Toys | |
| 256 255 |  | 
| 257 256 | 
             
                    def add_subtool_list_section
         | 
| 258 257 | 
             
                      return if @subtools.empty?
         | 
| 259 | 
            -
                      name_len = @tool.full_name.length
         | 
| 260 258 | 
             
                      @lines << ""
         | 
| 261 259 | 
             
                      @lines << "Tools:"
         | 
| 262 | 
            -
                      @subtools.each do |subtool|
         | 
| 263 | 
            -
                         | 
| 264 | 
            -
                        add_right_column_desc(tool_name, wrap_desc(subtool.desc))
         | 
| 260 | 
            +
                      @subtools.each do |local_name, subtool|
         | 
| 261 | 
            +
                        add_right_column_desc(local_name, wrap_desc(subtool.desc))
         | 
| 265 262 | 
             
                      end
         | 
| 266 263 | 
             
                    end
         | 
| 267 264 |  | 
| @@ -301,12 +298,12 @@ module Toys | |
| 301 298 |  | 
| 302 299 | 
             
                  ## @private
         | 
| 303 300 | 
             
                  class HelpStringAssembler
         | 
| 304 | 
            -
                    def initialize(tool, executable_name,  | 
| 301 | 
            +
                    def initialize(tool, executable_name, delegates, subtools, search_term,
         | 
| 305 302 | 
             
                                   show_source_path, indent, indent2, wrap_width, styled)
         | 
| 306 303 | 
             
                      require "toys/utils/terminal"
         | 
| 307 304 | 
             
                      @tool = tool
         | 
| 308 305 | 
             
                      @executable_name = executable_name
         | 
| 309 | 
            -
                      @ | 
| 306 | 
            +
                      @delegates = delegates
         | 
| 310 307 | 
             
                      @subtools = subtools
         | 
| 311 308 | 
             
                      @search_term = search_term
         | 
| 312 309 | 
             
                      @show_source_path = show_source_path
         | 
| @@ -356,7 +353,7 @@ module Toys | |
| 356 353 | 
             
                      @lines << ""
         | 
| 357 354 | 
             
                      @lines << bold("SYNOPSIS")
         | 
| 358 355 | 
             
                      add_synopsis_clause(namespace_synopsis) unless @subtools.empty?
         | 
| 359 | 
            -
                      add_synopsis_clause(@ | 
| 356 | 
            +
                      add_synopsis_clause(tool_synopsis(@tool))
         | 
| 360 357 | 
             
                    end
         | 
| 361 358 |  | 
| 362 359 | 
             
                    def add_synopsis_clause(synopsis)
         | 
| @@ -367,8 +364,8 @@ module Toys | |
| 367 364 | 
             
                      end
         | 
| 368 365 | 
             
                    end
         | 
| 369 366 |  | 
| 370 | 
            -
                    def tool_synopsis
         | 
| 371 | 
            -
                      synopsis = [full_executable_name]
         | 
| 367 | 
            +
                    def tool_synopsis(tool_for_name)
         | 
| 368 | 
            +
                      synopsis = [full_executable_name(tool_for_name)]
         | 
| 372 369 | 
             
                      @tool.flag_groups.each do |flag_group|
         | 
| 373 370 | 
             
                        case flag_group
         | 
| 374 371 | 
             
                        when FlagGroup::Required
         | 
| @@ -441,19 +438,14 @@ module Toys | |
| 441 438 | 
             
                    end
         | 
| 442 439 |  | 
| 443 440 | 
             
                    def namespace_synopsis
         | 
| 444 | 
            -
                      synopsis = [full_executable_name | 
| 441 | 
            +
                      synopsis = [full_executable_name(@tool),
         | 
| 442 | 
            +
                                  underline("TOOL"),
         | 
| 443 | 
            +
                                  "[#{underline('ARGUMENTS')}...]"]
         | 
| 445 444 | 
             
                      wrap_indent_indent2(WrappableString.new(synopsis))
         | 
| 446 445 | 
             
                    end
         | 
| 447 446 |  | 
| 448 | 
            -
                    def  | 
| 449 | 
            -
                       | 
| 450 | 
            -
                      args_clause = underline("ARGUMENTS FOR \"#{target}\"")
         | 
| 451 | 
            -
                      synopsis = [full_executable_name, "[#{args_clause}...]"]
         | 
| 452 | 
            -
                      wrap_indent_indent2(WrappableString.new(synopsis))
         | 
| 453 | 
            -
                    end
         | 
| 454 | 
            -
             | 
| 455 | 
            -
                    def full_executable_name
         | 
| 456 | 
            -
                      bold(([@executable_name] + @tool.full_name).join(" "))
         | 
| 447 | 
            +
                    def full_executable_name(tool_for_name)
         | 
| 448 | 
            +
                      bold(([@executable_name] + tool_for_name.full_name).join(" "))
         | 
| 457 449 | 
             
                    end
         | 
| 458 450 |  | 
| 459 451 | 
             
                    def add_source_section
         | 
| @@ -461,15 +453,22 @@ module Toys | |
| 461 453 | 
             
                      @lines << ""
         | 
| 462 454 | 
             
                      @lines << bold("SOURCE")
         | 
| 463 455 | 
             
                      @lines << indent_str("Defined in #{@tool.source_info.source_name}")
         | 
| 456 | 
            +
                      @delegates.each do |delegate|
         | 
| 457 | 
            +
                        @lines << indent_str("Delegated from \"#{delegate.display_name}\"" \
         | 
| 458 | 
            +
                                             " defined in #{delegate.source_info.source_name}")
         | 
| 459 | 
            +
                      end
         | 
| 464 460 | 
             
                    end
         | 
| 465 461 |  | 
| 466 462 | 
             
                    def add_description_section
         | 
| 467 | 
            -
                      desc = @tool.long_desc
         | 
| 468 | 
            -
                       | 
| 469 | 
            -
                         | 
| 470 | 
            -
             | 
| 471 | 
            -
             | 
| 463 | 
            +
                      desc = @tool.long_desc.dup
         | 
| 464 | 
            +
                      @delegates.each do |delegate|
         | 
| 465 | 
            +
                        desc << "" << "Delegated from \"#{delegate.display_name}\""
         | 
| 466 | 
            +
                        unless delegate.long_desc.empty?
         | 
| 467 | 
            +
                          desc << ""
         | 
| 468 | 
            +
                          desc += delegate.long_desc
         | 
| 469 | 
            +
                        end
         | 
| 472 470 | 
             
                      end
         | 
| 471 | 
            +
                      desc = desc[1..-1] if desc.first == ""
         | 
| 473 472 | 
             
                      desc = wrap_indent(desc)
         | 
| 474 473 | 
             
                      return if desc.empty?
         | 
| 475 474 | 
             
                      @lines << ""
         | 
| @@ -533,10 +532,8 @@ module Toys | |
| 533 532 | 
             
                        @lines << indent_str("Showing search results for \"#{@search_term}\"")
         | 
| 534 533 | 
             
                        @lines << ""
         | 
| 535 534 | 
             
                      end
         | 
| 536 | 
            -
                       | 
| 537 | 
            -
             | 
| 538 | 
            -
                        tool_name = subtool.full_name.slice(name_len..-1).join(" ")
         | 
| 539 | 
            -
                        add_prefix_with_desc(bold(tool_name), subtool.desc)
         | 
| 535 | 
            +
                      @subtools.each do |local_name, subtool|
         | 
| 536 | 
            +
                        add_prefix_with_desc(bold(local_name), subtool.desc)
         | 
| 540 537 | 
             
                      end
         | 
| 541 538 | 
             
                    end
         | 
| 542 539 |  | 
| @@ -637,10 +634,8 @@ module Toys | |
| 637 634 | 
             
                    end
         | 
| 638 635 |  | 
| 639 636 | 
             
                    def add_list
         | 
| 640 | 
            -
                       | 
| 641 | 
            -
             | 
| 642 | 
            -
                        tool_name = subtool.full_name.slice(name_len..-1).join(" ")
         | 
| 643 | 
            -
                        add_prefix_with_desc(bold(tool_name), subtool.desc)
         | 
| 637 | 
            +
                      @subtools.each do |local_name, subtool|
         | 
| 638 | 
            +
                        add_prefix_with_desc(bold(local_name), subtool.desc)
         | 
| 644 639 | 
             
                      end
         | 
| 645 640 | 
             
                    end
         | 
| 646 641 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: toys-core
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.11.4
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Daniel Azuma
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2020- | 
| 11 | 
            +
            date: 2020-10-11 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies: []
         | 
| 13 13 | 
             
            description: Toys-Core is the command line tool framework underlying Toys. It can
         | 
| 14 14 | 
             
              be used to create command line executables using the Toys DSL and classes.
         | 
| @@ -69,10 +69,10 @@ homepage: https://github.com/dazuma/toys | |
| 69 69 | 
             
            licenses:
         | 
| 70 70 | 
             
            - MIT
         | 
| 71 71 | 
             
            metadata:
         | 
| 72 | 
            -
              changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0. | 
| 72 | 
            +
              changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.11.4/file.CHANGELOG.html
         | 
| 73 73 | 
             
              source_code_uri: https://github.com/dazuma/toys
         | 
| 74 74 | 
             
              bug_tracker_uri: https://github.com/dazuma/toys/issues
         | 
| 75 | 
            -
              documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0. | 
| 75 | 
            +
              documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.11.4
         | 
| 76 76 | 
             
            post_install_message: 
         | 
| 77 77 | 
             
            rdoc_options: []
         | 
| 78 78 | 
             
            require_paths:
         | 
| @@ -88,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 88 88 | 
             
                - !ruby/object:Gem::Version
         | 
| 89 89 | 
             
                  version: '0'
         | 
| 90 90 | 
             
            requirements: []
         | 
| 91 | 
            -
            rubygems_version: 3.1. | 
| 91 | 
            +
            rubygems_version: 3.1.4
         | 
| 92 92 | 
             
            signing_key: 
         | 
| 93 93 | 
             
            specification_version: 4
         | 
| 94 94 | 
             
            summary: Framework for creating command line executables
         |