rfix 1.4.1 → 2.0.0
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/exe/rfix +78 -34
- data/lib/rfix.rb +17 -28
- data/lib/rfix/branch.rb +3 -25
- data/lib/rfix/branch/base.rb +27 -0
- data/lib/rfix/branch/head.rb +15 -0
- data/lib/rfix/branch/main.rb +33 -0
- data/lib/rfix/branch/name.rb +21 -0
- data/lib/rfix/branch/reference.rb +15 -0
- data/lib/rfix/branch/upstream.rb +17 -0
- data/lib/rfix/cli/command.rb +19 -0
- data/lib/rfix/cli/command/base.rb +61 -0
- data/lib/rfix/cli/command/branch.rb +13 -0
- data/lib/rfix/cli/command/config.rb +22 -0
- data/lib/rfix/cli/command/extension.rb +25 -0
- data/lib/rfix/cli/command/help.rb +11 -0
- data/lib/rfix/cli/command/info.rb +11 -0
- data/lib/rfix/cli/command/lint.rb +17 -0
- data/lib/rfix/cli/command/local.rb +11 -0
- data/lib/rfix/cli/command/origin.rb +11 -0
- data/lib/rfix/cli/command/setup.rb +11 -0
- data/lib/rfix/error.rb +5 -1
- data/lib/rfix/extension/offense.rb +79 -0
- data/lib/rfix/extension/pastel.rb +11 -0
- data/lib/rfix/extension/string.rb +12 -0
- data/lib/rfix/extension/strings.rb +9 -0
- data/lib/rfix/file.rb +6 -41
- data/lib/rfix/file/base.rb +73 -0
- data/lib/rfix/file/deleted.rb +17 -0
- data/lib/rfix/file/ignored.rb +17 -0
- data/lib/rfix/file/tracked.rb +42 -0
- data/lib/rfix/file/untracked.rb +20 -0
- data/lib/rfix/formatter.rb +125 -86
- data/lib/rfix/highlighter.rb +118 -0
- data/lib/rfix/indicator.rb +19 -0
- data/lib/rfix/log.rb +12 -121
- data/lib/rfix/rake/gemfile.rb +111 -0
- data/lib/rfix/rake/paths.rb +25 -23
- data/lib/rfix/rake/support.rb +72 -57
- data/lib/rfix/repository.rb +114 -164
- data/lib/rfix/types.rb +52 -0
- data/lib/rfix/version.rb +1 -1
- data/rfix.gemspec +28 -38
- data/vendor/dry-cli/CHANGELOG.md +191 -0
- data/vendor/dry-cli/CODEOWNERS +1 -0
- data/vendor/dry-cli/CODE_OF_CONDUCT.md +13 -0
- data/vendor/dry-cli/CONTRIBUTING.md +29 -0
- data/vendor/dry-cli/Gemfile +14 -0
- data/vendor/dry-cli/Gemfile.devtools +18 -0
- data/vendor/dry-cli/LICENSE +20 -0
- data/vendor/dry-cli/README.md +29 -0
- data/vendor/dry-cli/Rakefile +13 -0
- data/vendor/dry-cli/bin/console +15 -0
- data/vendor/dry-cli/bin/setup +8 -0
- data/vendor/dry-cli/changelog.yml +97 -0
- data/vendor/dry-cli/docsite/source/arguments.html.md +57 -0
- data/vendor/dry-cli/docsite/source/callbacks.html.md +51 -0
- data/vendor/dry-cli/docsite/source/commands-with-subcommands-and-params.md +86 -0
- data/vendor/dry-cli/docsite/source/commands.html.md +41 -0
- data/vendor/dry-cli/docsite/source/index.html.md +302 -0
- data/vendor/dry-cli/docsite/source/options.html.md +51 -0
- data/vendor/dry-cli/docsite/source/subcommands.html.md +38 -0
- data/vendor/dry-cli/docsite/source/variadic-arguments.html.md +45 -0
- data/vendor/dry-cli/dry-cli.gemspec +36 -0
- data/vendor/dry-cli/lib/dry/cli.rb +224 -0
- data/vendor/dry-cli/lib/dry/cli/banner.rb +135 -0
- data/vendor/dry-cli/lib/dry/cli/command.rb +387 -0
- data/vendor/dry-cli/lib/dry/cli/command_registry.rb +253 -0
- data/vendor/dry-cli/lib/dry/cli/errors.rb +37 -0
- data/vendor/dry-cli/lib/dry/cli/inflector.rb +17 -0
- data/vendor/dry-cli/lib/dry/cli/inline.rb +75 -0
- data/vendor/dry-cli/lib/dry/cli/option.rb +131 -0
- data/vendor/dry-cli/lib/dry/cli/parser.rb +138 -0
- data/vendor/dry-cli/lib/dry/cli/program_name.rb +21 -0
- data/vendor/dry-cli/lib/dry/cli/registry.rb +338 -0
- data/vendor/dry-cli/lib/dry/cli/usage.rb +94 -0
- data/vendor/dry-cli/lib/dry/cli/version.rb +8 -0
- data/vendor/dry-cli/project.yml +13 -0
- data/vendor/dry-cli/spec/integration/commands_spec.rb +14 -0
- data/vendor/dry-cli/spec/integration/inherited_commands_spec.rb +24 -0
- data/vendor/dry-cli/spec/integration/inline_spec.rb +43 -0
- data/vendor/dry-cli/spec/integration/processes_errors_spec.rb +29 -0
- data/vendor/dry-cli/spec/integration/rendering_spec.rb +31 -0
- data/vendor/dry-cli/spec/integration/single_command_spec.rb +81 -0
- data/vendor/dry-cli/spec/integration/subcommands_spec.rb +60 -0
- data/vendor/dry-cli/spec/integration/third_party_gems_spec.rb +18 -0
- data/vendor/dry-cli/spec/spec_helper.rb +15 -0
- data/vendor/dry-cli/spec/support/coverage.rb +15 -0
- data/vendor/dry-cli/spec/support/files.rb +13 -0
- data/vendor/dry-cli/spec/support/fixtures/based +65 -0
- data/vendor/dry-cli/spec/support/fixtures/baz +9 -0
- data/vendor/dry-cli/spec/support/fixtures/baz_command.rb +19 -0
- data/vendor/dry-cli/spec/support/fixtures/foo +588 -0
- data/vendor/dry-cli/spec/support/fixtures/infinites +31 -0
- data/vendor/dry-cli/spec/support/fixtures/inline +20 -0
- data/vendor/dry-cli/spec/support/fixtures/registry.rb +15 -0
- data/vendor/dry-cli/spec/support/fixtures/shared_commands.rb +596 -0
- data/vendor/dry-cli/spec/support/fixtures/with_block.rb +86 -0
- data/vendor/dry-cli/spec/support/fixtures/with_registry.rb +90 -0
- data/vendor/dry-cli/spec/support/fixtures/with_zero_arity_block.rb +87 -0
- data/vendor/dry-cli/spec/support/helpers.rb +37 -0
- data/vendor/dry-cli/spec/support/path.rb +24 -0
- data/vendor/dry-cli/spec/support/rspec.rb +26 -0
- data/vendor/dry-cli/spec/support/rspec_options.rb +16 -0
- data/vendor/dry-cli/spec/support/shared_examples/commands.rb +300 -0
- data/vendor/dry-cli/spec/support/shared_examples/inherited_commands.rb +197 -0
- data/vendor/dry-cli/spec/support/shared_examples/rendering.rb +181 -0
- data/vendor/dry-cli/spec/support/shared_examples/subcommands.rb +226 -0
- data/vendor/dry-cli/spec/support/shared_examples/third_party_gems.rb +49 -0
- data/vendor/dry-cli/spec/support/warnings.rb +10 -0
- data/vendor/dry-cli/spec/unit/dry/cli/cli_spec.rb +123 -0
- data/vendor/dry-cli/spec/unit/dry/cli/inflector_spec.rb +26 -0
- data/vendor/dry-cli/spec/unit/dry/cli/registry_spec.rb +78 -0
- data/vendor/dry-cli/spec/unit/dry/cli/version_spec.rb +7 -0
- data/vendor/strings-ansi/CHANGELOG.md +24 -0
- data/vendor/strings-ansi/CODE_OF_CONDUCT.md +74 -0
- data/vendor/strings-ansi/Gemfile +11 -0
- data/{LICENSE.txt → vendor/strings-ansi/LICENSE.txt} +1 -1
- data/vendor/strings-ansi/README.md +155 -0
- data/vendor/strings-ansi/Rakefile +8 -0
- data/vendor/strings-ansi/appveyor.yml +32 -0
- data/vendor/strings-ansi/bin/console +14 -0
- data/vendor/strings-ansi/bin/setup +8 -0
- data/vendor/strings-ansi/lib/strings-ansi.rb +1 -0
- data/vendor/strings-ansi/lib/strings/ansi.rb +84 -0
- data/vendor/strings-ansi/lib/strings/ansi/extensions.rb +23 -0
- data/vendor/strings-ansi/lib/strings/ansi/version.rb +7 -0
- data/vendor/strings-ansi/spec/fixtures/ansi_codes.yaml +194 -0
- data/vendor/strings-ansi/spec/spec_helper.rb +51 -0
- data/vendor/strings-ansi/spec/unit/ansi_spec.rb +15 -0
- data/vendor/strings-ansi/spec/unit/extensions_spec.rb +19 -0
- data/vendor/strings-ansi/spec/unit/only_ansi_spec.rb +36 -0
- data/vendor/strings-ansi/spec/unit/sanitize_spec.rb +53 -0
- data/vendor/strings-ansi/strings-ansi.gemspec +34 -0
- data/vendor/strings-ansi/tasks/console.rake +11 -0
- data/vendor/strings-ansi/tasks/coverage.rake +11 -0
- data/vendor/strings-ansi/tasks/spec.rake +29 -0
- metadata +274 -188
- data/.github/workflows/main.yml +0 -26
- data/.gitignore +0 -43
- data/.rspec +0 -2
- data/.rubocop.yml +0 -87
- data/.travis.yml +0 -35
- data/Gemfile +0 -2
- data/Gemfile.base +0 -14
- data/Gemfile.base.lock +0 -172
- data/Gemfile.lock +0 -188
- data/Guardfile +0 -16
- data/Makefile +0 -12
- data/README.md +0 -85
- data/Rakefile +0 -31
- data/bin/bundle +0 -114
- data/bin/console +0 -29
- data/bin/guard +0 -29
- data/bin/rake +0 -29
- data/bin/rfix +0 -29
- data/bin/rspec +0 -29
- data/bin/setup +0 -29
- data/ci/Gemfile.rubocop-0.80 +0 -2
- data/ci/Gemfile.rubocop-0.80.lock +0 -170
- data/ci/Gemfile.rubocop-0.81 +0 -2
- data/ci/Gemfile.rubocop-0.81.lock +0 -170
- data/ci/Gemfile.rubocop-0.82 +0 -2
- data/ci/Gemfile.rubocop-0.82.lock +0 -170
- data/ci/Gemfile.rubocop-0.83 +0 -2
- data/ci/Gemfile.rubocop-0.83.lock +0 -168
- data/ci/Gemfile.rubocop-0.84 +0 -2
- data/ci/Gemfile.rubocop-0.84.lock +0 -171
- data/ci/Gemfile.rubocop-0.85 +0 -2
- data/ci/Gemfile.rubocop-0.85.1 +0 -2
- data/ci/Gemfile.rubocop-0.85.1.lock +0 -173
- data/ci/Gemfile.rubocop-0.85.lock +0 -173
- data/lib/rfix/box.rb +0 -112
- data/lib/rfix/branches/base.rb +0 -15
- data/lib/rfix/branches/head.rb +0 -13
- data/lib/rfix/branches/main.rb +0 -28
- data/lib/rfix/branches/name.rb +0 -23
- data/lib/rfix/branches/reference.rb +0 -21
- data/lib/rfix/branches/upstream.rb +0 -13
- data/lib/rfix/cmd.rb +0 -39
- data/lib/rfix/commands/branch.rb +0 -15
- data/lib/rfix/commands/extensions/options.rb +0 -8
- data/lib/rfix/commands/help.rb +0 -7
- data/lib/rfix/commands/helper/args.rb +0 -141
- data/lib/rfix/commands/helper/help.rb +0 -6
- data/lib/rfix/commands/helper/loader.rb +0 -6
- data/lib/rfix/commands/helper/option.rb +0 -0
- data/lib/rfix/commands/helper/params.rb +0 -0
- data/lib/rfix/commands/helper/rubocop.rb +0 -17
- data/lib/rfix/commands/info.rb +0 -30
- data/lib/rfix/commands/lint.rb +0 -22
- data/lib/rfix/commands/local.rb +0 -12
- data/lib/rfix/commands/origin.rb +0 -19
- data/lib/rfix/commands/setup.rb +0 -29
- data/lib/rfix/commands/welcome.rb +0 -24
- data/lib/rfix/deleted.rb +0 -13
- data/lib/rfix/extensions/extensions.rb +0 -18
- data/lib/rfix/extensions/offense.rb +0 -78
- data/lib/rfix/extensions/string.rb +0 -8
- data/lib/rfix/file_cache.rb +0 -59
- data/lib/rfix/git_helper.rb +0 -59
- data/lib/rfix/indentation.rb +0 -39
- data/lib/rfix/loader/bundler.rb +0 -37
- data/lib/rfix/loader/env.rb +0 -33
- data/lib/rfix/loader/spec.rb +0 -41
- data/lib/rfix/no_file.rb +0 -13
- data/lib/rfix/rfix.rb +0 -34
- data/lib/rfix/tracked.rb +0 -72
- data/lib/rfix/tracked_file.rb +0 -16
- data/lib/rfix/untracked.rb +0 -13
- data/resources/ps.png +0 -0
- data/tasks/bump.rake +0 -11
- data/tasks/bundle.rake +0 -17
- data/tasks/complex.rake +0 -54
- data/tasks/execute.rake +0 -38
- data/tasks/libgit2.rake +0 -33
- data/tasks/simple.rake +0 -62
- data/tasks/travis.rake +0 -74
- data/tasks/vendor.rake +0 -34
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
# General purpose Command Line Interface (CLI) framework for Ruby
|
|
5
|
+
#
|
|
6
|
+
# @since 0.1.0
|
|
7
|
+
class CLI
|
|
8
|
+
# @since 0.2.0
|
|
9
|
+
class Error < StandardError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @since 0.2.1
|
|
13
|
+
class UnknownCommandError < Error
|
|
14
|
+
# @since 0.2.1
|
|
15
|
+
# @api private
|
|
16
|
+
def initialize(command_name)
|
|
17
|
+
super("unknown command: `#{command_name}'")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @since 0.2.0
|
|
22
|
+
class InvalidCallbackError < Error
|
|
23
|
+
# @since 0.2.0
|
|
24
|
+
# @api private
|
|
25
|
+
def initialize(callback)
|
|
26
|
+
message = case callback
|
|
27
|
+
when Class
|
|
28
|
+
"expected `#{callback.inspect}' to respond to `#initialize' with arity 0"
|
|
29
|
+
else
|
|
30
|
+
"expected `#{callback.inspect}' to respond to `#call'"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
super(message)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
class CLI
|
|
5
|
+
# @api private
|
|
6
|
+
# @since 0.5.0
|
|
7
|
+
module Inflector
|
|
8
|
+
# @api private
|
|
9
|
+
# @since 0.5.0
|
|
10
|
+
def self.dasherize(input)
|
|
11
|
+
return nil unless input
|
|
12
|
+
|
|
13
|
+
input.to_s.downcase.gsub(/[[[:space:]]_]/, "-")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "backports/2.5.0/module/define_method" if RUBY_VERSION < "2.5"
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
class CLI
|
|
7
|
+
require "dry/cli"
|
|
8
|
+
# Inline Syntax (aka DSL) to implement one-file applications
|
|
9
|
+
#
|
|
10
|
+
# `dry/cli/inline` is not required by default
|
|
11
|
+
# and explicit requirement of this file means that
|
|
12
|
+
# it is expected of abusing global namespace with
|
|
13
|
+
# methods below
|
|
14
|
+
#
|
|
15
|
+
# DSL consists of 5 methods:
|
|
16
|
+
# `desc`, `example`, `argument`, `option`
|
|
17
|
+
# — are similar to methods from Command class
|
|
18
|
+
#
|
|
19
|
+
# `run` accepts a block to execute
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# require 'bundler/inline'
|
|
23
|
+
# gemfile { gem 'dry/cli', require: 'dry/cli/inline' }
|
|
24
|
+
#
|
|
25
|
+
# desc 'List files in a directory'
|
|
26
|
+
# argument :path, required: false, desc: '[DIR]'
|
|
27
|
+
# option :all, aliases: ['a'], type: :boolean
|
|
28
|
+
#
|
|
29
|
+
# run do |path: '.', **options|
|
|
30
|
+
# puts options.key?(:all) ? Dir.entries(path) : Dir.children(path)
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# # $ ls -a
|
|
34
|
+
# # $ ls somepath
|
|
35
|
+
# # $ ls somepath --all
|
|
36
|
+
# @since 0.6.0
|
|
37
|
+
module Inline
|
|
38
|
+
extend Forwardable
|
|
39
|
+
|
|
40
|
+
# AnonymousCommand
|
|
41
|
+
#
|
|
42
|
+
# @since 0.6.0
|
|
43
|
+
AnonymousCommand = Class.new(Dry::CLI::Command)
|
|
44
|
+
|
|
45
|
+
# @since 0.6.0
|
|
46
|
+
delegate %i[desc example argument option] => AnonymousCommand
|
|
47
|
+
|
|
48
|
+
# The rule of thumb for implementation of run block
|
|
49
|
+
# is that for every argument from your CLI
|
|
50
|
+
# you need to specify that as an mandatory argument for a block.
|
|
51
|
+
# Optional arguments have to have default value as ruby syntax expect them
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# argument :one
|
|
55
|
+
# argument :two, required: false
|
|
56
|
+
# option :three
|
|
57
|
+
#
|
|
58
|
+
# run do |one:, two: 'default', **options|
|
|
59
|
+
# puts one, two, options.inspect
|
|
60
|
+
# end
|
|
61
|
+
#
|
|
62
|
+
# @since 0.6.0
|
|
63
|
+
def run(arguments: ARGV, out: $stdout)
|
|
64
|
+
command = AnonymousCommand
|
|
65
|
+
command.define_method(:call) do |**args|
|
|
66
|
+
yield(**args)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
Dry.CLI(command).call(arguments: arguments, out: out)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
include Dry::CLI::Inline # rubocop:disable Style/MixinUsage
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
class CLI
|
|
5
|
+
# Command line option
|
|
6
|
+
#
|
|
7
|
+
# @since 0.1.0
|
|
8
|
+
# @api private
|
|
9
|
+
class Option
|
|
10
|
+
# @since 0.1.0
|
|
11
|
+
# @api private
|
|
12
|
+
attr_reader :name
|
|
13
|
+
|
|
14
|
+
# @since 0.1.0
|
|
15
|
+
# @api private
|
|
16
|
+
attr_reader :options
|
|
17
|
+
|
|
18
|
+
# @since 0.1.0
|
|
19
|
+
# @api private
|
|
20
|
+
def initialize(name, options = {})
|
|
21
|
+
@name = name
|
|
22
|
+
@options = options
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @since 0.1.0
|
|
26
|
+
# @api private
|
|
27
|
+
def aliases
|
|
28
|
+
options[:aliases] || []
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @since 0.1.0
|
|
32
|
+
# @api private
|
|
33
|
+
def desc
|
|
34
|
+
desc = options[:desc]
|
|
35
|
+
values ? "#{desc}: (#{values.join("/")})" : desc
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @since 0.1.0
|
|
39
|
+
# @api private
|
|
40
|
+
def required?
|
|
41
|
+
options[:required]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @since 0.1.0
|
|
45
|
+
# @api private
|
|
46
|
+
def type
|
|
47
|
+
options[:type]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @since 0.1.0
|
|
51
|
+
# @api private
|
|
52
|
+
def values
|
|
53
|
+
options[:values]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @since 0.1.0
|
|
57
|
+
# @api private
|
|
58
|
+
def boolean?
|
|
59
|
+
type == :boolean
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @since 0.3.0
|
|
63
|
+
# @api private
|
|
64
|
+
def array?
|
|
65
|
+
type == :array
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @since 0.1.0
|
|
69
|
+
# @api private
|
|
70
|
+
def default
|
|
71
|
+
options[:default]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @since 0.1.0
|
|
75
|
+
# @api private
|
|
76
|
+
def description_name
|
|
77
|
+
options[:label] || name.upcase
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @since 0.1.0
|
|
81
|
+
# @api private
|
|
82
|
+
def argument?
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @since 0.1.0
|
|
87
|
+
# @api private
|
|
88
|
+
#
|
|
89
|
+
def parser_options
|
|
90
|
+
dasherized_name = Inflector.dasherize(name)
|
|
91
|
+
parser_options = []
|
|
92
|
+
|
|
93
|
+
if boolean?
|
|
94
|
+
parser_options << "--[no-]#{dasherized_name}"
|
|
95
|
+
else
|
|
96
|
+
parser_options << "--#{dasherized_name}=#{name}"
|
|
97
|
+
parser_options << "--#{dasherized_name} #{name}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
parser_options << Array if array?
|
|
101
|
+
parser_options << values if values
|
|
102
|
+
parser_options.unshift(*alias_names) if aliases.any?
|
|
103
|
+
parser_options << desc if desc
|
|
104
|
+
parser_options
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @since 0.1.0
|
|
108
|
+
# @api private
|
|
109
|
+
def alias_names
|
|
110
|
+
aliases
|
|
111
|
+
.map { |name| name.gsub(/^-{1,2}/, "") }
|
|
112
|
+
.compact
|
|
113
|
+
.uniq
|
|
114
|
+
.map { |name| name.size == 1 ? "-#{name}" : "--#{name}" }
|
|
115
|
+
.map { |name| boolean? ? name : "#{name} VALUE" }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Command line argument
|
|
120
|
+
#
|
|
121
|
+
# @since 0.1.0
|
|
122
|
+
# @api private
|
|
123
|
+
class Argument < Option
|
|
124
|
+
# @since 0.1.0
|
|
125
|
+
# @api private
|
|
126
|
+
def argument?
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "dry/cli/program_name"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
class CLI
|
|
8
|
+
# Parse command line arguments and options
|
|
9
|
+
#
|
|
10
|
+
# @since 0.1.0
|
|
11
|
+
# @api private
|
|
12
|
+
module Parser
|
|
13
|
+
# @since 0.1.0
|
|
14
|
+
# @api private
|
|
15
|
+
#
|
|
16
|
+
def self.call(command, arguments, prog_name)
|
|
17
|
+
original_arguments = arguments.dup
|
|
18
|
+
parsed_options = {}
|
|
19
|
+
|
|
20
|
+
OptionParser.new do |opts|
|
|
21
|
+
command.options.each do |option|
|
|
22
|
+
opts.on(*option.parser_options) do |value|
|
|
23
|
+
parsed_options[option.name.to_sym] = value
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
opts.on_tail("-h", "--help") do
|
|
28
|
+
return Result.help
|
|
29
|
+
end
|
|
30
|
+
end.parse!(arguments)
|
|
31
|
+
|
|
32
|
+
parsed_options = command.default_params.merge(parsed_options)
|
|
33
|
+
parse_required_params(command, arguments, prog_name, parsed_options)
|
|
34
|
+
rescue ::OptionParser::ParseError
|
|
35
|
+
Result.failure("ERROR: \"#{prog_name}\" was called with arguments \"#{original_arguments.join(" ")}\"") # rubocop:disable Metrics/LineLength
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @since 0.1.0
|
|
39
|
+
# @api private
|
|
40
|
+
#
|
|
41
|
+
# rubocop:disable Metrics/AbcSize
|
|
42
|
+
def self.parse_required_params(command, arguments, prog_name, parsed_options)
|
|
43
|
+
parsed_params = match_arguments(command.arguments, arguments)
|
|
44
|
+
parsed_required_params = match_arguments(command.required_arguments, arguments)
|
|
45
|
+
all_required_params_satisfied = command.required_arguments.all? { |param| !parsed_required_params[param.name].nil? } # rubocop:disable Metrics/LineLength
|
|
46
|
+
|
|
47
|
+
unused_arguments = arguments.drop(command.required_arguments.length)
|
|
48
|
+
|
|
49
|
+
unless all_required_params_satisfied
|
|
50
|
+
parsed_required_params_values = parsed_required_params.values.compact
|
|
51
|
+
|
|
52
|
+
usage = "\nUsage: \"#{prog_name} #{command.required_arguments.map(&:description_name).join(" ")}" # rubocop:disable Metrics/LineLength
|
|
53
|
+
|
|
54
|
+
usage += " | #{prog_name} SUBCOMMAND" if command.subcommands.any?
|
|
55
|
+
|
|
56
|
+
usage += '"'
|
|
57
|
+
|
|
58
|
+
if parsed_required_params_values.empty?
|
|
59
|
+
return Result.failure("ERROR: \"#{prog_name}\" was called with no arguments#{usage}")
|
|
60
|
+
else
|
|
61
|
+
return Result.failure("ERROR: \"#{prog_name}\" was called with arguments #{parsed_required_params_values}#{usage}") # rubocop:disable Metrics/LineLength
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
parsed_params.reject! { |_key, value| value.nil? }
|
|
66
|
+
parsed_options = parsed_options.merge(parsed_params)
|
|
67
|
+
parsed_options = parsed_options.merge(args: unused_arguments) if unused_arguments.any?
|
|
68
|
+
Result.success(parsed_options)
|
|
69
|
+
end
|
|
70
|
+
# rubocop:enable Metrics/AbcSize
|
|
71
|
+
|
|
72
|
+
def self.match_arguments(command_arguments, arguments)
|
|
73
|
+
result = {}
|
|
74
|
+
|
|
75
|
+
command_arguments.each_with_index do |cmd_arg, index|
|
|
76
|
+
if cmd_arg.array?
|
|
77
|
+
result[cmd_arg.name] = arguments[index..-1]
|
|
78
|
+
break
|
|
79
|
+
else
|
|
80
|
+
result[cmd_arg.name] = arguments.at(index)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
result
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @since 0.1.0
|
|
88
|
+
# @api private
|
|
89
|
+
class Result
|
|
90
|
+
# @since 0.1.0
|
|
91
|
+
# @api private
|
|
92
|
+
def self.help
|
|
93
|
+
new(help: true)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @since 0.1.0
|
|
97
|
+
# @api private
|
|
98
|
+
def self.success(arguments = {})
|
|
99
|
+
new(arguments: arguments)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @since 0.1.0
|
|
103
|
+
# @api private
|
|
104
|
+
def self.failure(error = "Error: Invalid param provided")
|
|
105
|
+
new(error: error)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @since 0.1.0
|
|
109
|
+
# @api private
|
|
110
|
+
attr_reader :arguments
|
|
111
|
+
|
|
112
|
+
# @since 0.1.0
|
|
113
|
+
# @api private
|
|
114
|
+
attr_reader :error
|
|
115
|
+
|
|
116
|
+
# @since 0.1.0
|
|
117
|
+
# @api private
|
|
118
|
+
def initialize(arguments: {}, error: nil, help: false)
|
|
119
|
+
@arguments = arguments
|
|
120
|
+
@error = error
|
|
121
|
+
@help = help
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @since 0.1.0
|
|
125
|
+
# @api private
|
|
126
|
+
def error?
|
|
127
|
+
!error.nil?
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @since 0.1.0
|
|
131
|
+
# @api private
|
|
132
|
+
def help?
|
|
133
|
+
@help
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
class CLI
|
|
5
|
+
# Program name
|
|
6
|
+
#
|
|
7
|
+
# @since 0.1.0
|
|
8
|
+
# @api private
|
|
9
|
+
module ProgramName
|
|
10
|
+
# @since 0.1.0
|
|
11
|
+
# @api private
|
|
12
|
+
SEPARATOR = " "
|
|
13
|
+
|
|
14
|
+
# @since 0.1.0
|
|
15
|
+
# @api private
|
|
16
|
+
def self.call(names = [], program_name: $PROGRAM_NAME)
|
|
17
|
+
[File.basename(program_name), names].flatten.join(SEPARATOR)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/cli/command_registry"
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
class CLI
|
|
7
|
+
# Registry mixin
|
|
8
|
+
#
|
|
9
|
+
# @since 0.1.0
|
|
10
|
+
module Registry
|
|
11
|
+
# @since 0.1.0
|
|
12
|
+
# @api private
|
|
13
|
+
def self.extended(base)
|
|
14
|
+
base.class_eval do
|
|
15
|
+
@_mutex = Mutex.new
|
|
16
|
+
@commands = CommandRegistry.new
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Register a command
|
|
21
|
+
#
|
|
22
|
+
# @param name [String] the command name
|
|
23
|
+
# @param command [NilClass,Dry::CLI::Command] the optional command
|
|
24
|
+
# @param aliases [Array<String>] an optional list of aliases
|
|
25
|
+
# @param options [Hash] a set of options
|
|
26
|
+
#
|
|
27
|
+
# @since 0.1.0
|
|
28
|
+
#
|
|
29
|
+
# @example Register a command
|
|
30
|
+
# require "dry/cli"
|
|
31
|
+
#
|
|
32
|
+
# module Foo
|
|
33
|
+
# module Commands
|
|
34
|
+
# extend Dry::CLI::Registry
|
|
35
|
+
#
|
|
36
|
+
# class Hello < Dry::CLI::Command
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# register "hi", Hello
|
|
40
|
+
# end
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# @example Register a command with aliases
|
|
44
|
+
# require "dry/cli"
|
|
45
|
+
#
|
|
46
|
+
# module Foo
|
|
47
|
+
# module Commands
|
|
48
|
+
# extend Dry::CLI::Registry
|
|
49
|
+
#
|
|
50
|
+
# class Hello < Dry::CLI::Command
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# register "hello", Hello, aliases: ["hi", "ciao"]
|
|
54
|
+
# end
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# @example Register a group of commands
|
|
58
|
+
# require "dry/cli"
|
|
59
|
+
#
|
|
60
|
+
# module Foo
|
|
61
|
+
# module Commands
|
|
62
|
+
# extend Dry::CLI::Registry
|
|
63
|
+
#
|
|
64
|
+
# module Generate
|
|
65
|
+
# class App < Dry::CLI::Command
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# class Action < Dry::CLI::Command
|
|
69
|
+
# end
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# register "generate", aliases: ["g"] do |prefix|
|
|
73
|
+
# prefix.register "app", Generate::App
|
|
74
|
+
# prefix.register "action", Generate::Action
|
|
75
|
+
# end
|
|
76
|
+
# end
|
|
77
|
+
# end
|
|
78
|
+
def register(name, command = nil, aliases: [], &block)
|
|
79
|
+
@commands.set(name, command, aliases)
|
|
80
|
+
|
|
81
|
+
if block_given?
|
|
82
|
+
prefix = Prefix.new(@commands, name, aliases)
|
|
83
|
+
if block.arity.zero?
|
|
84
|
+
prefix.instance_eval(&block)
|
|
85
|
+
else
|
|
86
|
+
yield(prefix)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Register a before callback.
|
|
92
|
+
#
|
|
93
|
+
# @param command_name [String] the name used for command registration
|
|
94
|
+
# @param callback [Class, #call] the callback object. If a class is given,
|
|
95
|
+
# it MUST respond to `#call`.
|
|
96
|
+
# @param blk [Proc] the callback espressed as a block
|
|
97
|
+
#
|
|
98
|
+
# @raise [Dry::CLI::UnknownCommandError] if the command isn't registered
|
|
99
|
+
# @raise [Dry::CLI::InvalidCallbackError] if the given callback doesn't
|
|
100
|
+
# implement the required interface
|
|
101
|
+
#
|
|
102
|
+
# @since 0.2.0
|
|
103
|
+
#
|
|
104
|
+
# @example
|
|
105
|
+
# require "dry/cli"
|
|
106
|
+
#
|
|
107
|
+
# module Foo
|
|
108
|
+
# module Commands
|
|
109
|
+
# extend Dry::CLI::Registry
|
|
110
|
+
#
|
|
111
|
+
# class Hello < Dry::CLI::Command
|
|
112
|
+
# def call(*)
|
|
113
|
+
# puts "hello"
|
|
114
|
+
# end
|
|
115
|
+
# end
|
|
116
|
+
#
|
|
117
|
+
# register "hello", Hello
|
|
118
|
+
# before "hello", -> { puts "I'm about to say.." }
|
|
119
|
+
# end
|
|
120
|
+
# end
|
|
121
|
+
#
|
|
122
|
+
# @example Register an object as callback
|
|
123
|
+
# require "dry/cli"
|
|
124
|
+
#
|
|
125
|
+
# module Callbacks
|
|
126
|
+
# class Hello
|
|
127
|
+
# def call(*)
|
|
128
|
+
# puts "world"
|
|
129
|
+
# end
|
|
130
|
+
# end
|
|
131
|
+
# end
|
|
132
|
+
#
|
|
133
|
+
# module Foo
|
|
134
|
+
# module Commands
|
|
135
|
+
# extend Dry::CLI::Registry
|
|
136
|
+
#
|
|
137
|
+
# class Hello < Dry::CLI::Command
|
|
138
|
+
# def call(*)
|
|
139
|
+
# puts "I'm about to say.."
|
|
140
|
+
# end
|
|
141
|
+
# end
|
|
142
|
+
#
|
|
143
|
+
# register "hello", Hello
|
|
144
|
+
# before "hello", Callbacks::Hello.new
|
|
145
|
+
# end
|
|
146
|
+
# end
|
|
147
|
+
#
|
|
148
|
+
# @example Register a class as callback
|
|
149
|
+
# require "dry/cli"
|
|
150
|
+
#
|
|
151
|
+
# module Callbacks
|
|
152
|
+
# class Hello
|
|
153
|
+
# def call(*)
|
|
154
|
+
# puts "world"
|
|
155
|
+
# end
|
|
156
|
+
# end
|
|
157
|
+
# end
|
|
158
|
+
#
|
|
159
|
+
# module Foo
|
|
160
|
+
# module Commands
|
|
161
|
+
# extend Dry::CLI::Registry
|
|
162
|
+
#
|
|
163
|
+
# class Hello < Dry::CLI::Command
|
|
164
|
+
# def call(*)
|
|
165
|
+
# puts "I'm about to say.."
|
|
166
|
+
# end
|
|
167
|
+
# end
|
|
168
|
+
#
|
|
169
|
+
# register "hello", Hello
|
|
170
|
+
# before "hello", Callbacks::Hello
|
|
171
|
+
# end
|
|
172
|
+
# end
|
|
173
|
+
def before(command_name, callback = nil, &blk)
|
|
174
|
+
@_mutex.synchronize do
|
|
175
|
+
command(command_name).before_callbacks.append(&_callback(callback, blk))
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Register an after callback.
|
|
180
|
+
#
|
|
181
|
+
# @param command_name [String] the name used for command registration
|
|
182
|
+
# @param callback [Class, #call] the callback object. If a class is given,
|
|
183
|
+
# it MUST respond to `#call`.
|
|
184
|
+
# @param blk [Proc] the callback espressed as a block
|
|
185
|
+
#
|
|
186
|
+
# @raise [Dry::CLI::UnknownCommandError] if the command isn't registered
|
|
187
|
+
# @raise [Dry::CLI::InvalidCallbackError] if the given callback doesn't
|
|
188
|
+
# implement the required interface
|
|
189
|
+
#
|
|
190
|
+
# @since 0.2.0
|
|
191
|
+
#
|
|
192
|
+
# @example
|
|
193
|
+
# require "dry/cli"
|
|
194
|
+
#
|
|
195
|
+
# module Foo
|
|
196
|
+
# module Commands
|
|
197
|
+
# extend Dry::CLI::Registry
|
|
198
|
+
#
|
|
199
|
+
# class Hello < Dry::CLI::Command
|
|
200
|
+
# def call(*)
|
|
201
|
+
# puts "hello"
|
|
202
|
+
# end
|
|
203
|
+
# end
|
|
204
|
+
#
|
|
205
|
+
# register "hello", Hello
|
|
206
|
+
# after "hello", -> { puts "world" }
|
|
207
|
+
# end
|
|
208
|
+
# end
|
|
209
|
+
#
|
|
210
|
+
# @example Register an object as callback
|
|
211
|
+
# require "dry/cli"
|
|
212
|
+
#
|
|
213
|
+
# module Callbacks
|
|
214
|
+
# class World
|
|
215
|
+
# def call(*)
|
|
216
|
+
# puts "world"
|
|
217
|
+
# end
|
|
218
|
+
# end
|
|
219
|
+
# end
|
|
220
|
+
#
|
|
221
|
+
# module Foo
|
|
222
|
+
# module Commands
|
|
223
|
+
# extend Dry::CLI::Registry
|
|
224
|
+
#
|
|
225
|
+
# class Hello < Dry::CLI::Command
|
|
226
|
+
# def call(*)
|
|
227
|
+
# puts "hello"
|
|
228
|
+
# end
|
|
229
|
+
# end
|
|
230
|
+
#
|
|
231
|
+
# register "hello", Hello
|
|
232
|
+
# after "hello", Callbacks::World.new
|
|
233
|
+
# end
|
|
234
|
+
# end
|
|
235
|
+
#
|
|
236
|
+
# @example Register a class as callback
|
|
237
|
+
# require "dry/cli"
|
|
238
|
+
#
|
|
239
|
+
# module Callbacks
|
|
240
|
+
# class World
|
|
241
|
+
# def call(*)
|
|
242
|
+
# puts "world"
|
|
243
|
+
# end
|
|
244
|
+
# end
|
|
245
|
+
# end
|
|
246
|
+
#
|
|
247
|
+
# module Foo
|
|
248
|
+
# module Commands
|
|
249
|
+
# extend Dry::CLI::Registry
|
|
250
|
+
#
|
|
251
|
+
# class Hello < Dry::CLI::Command
|
|
252
|
+
# def call(*)
|
|
253
|
+
# puts "hello"
|
|
254
|
+
# end
|
|
255
|
+
# end
|
|
256
|
+
#
|
|
257
|
+
# register "hello", Hello
|
|
258
|
+
# after "hello", Callbacks::World
|
|
259
|
+
# end
|
|
260
|
+
# end
|
|
261
|
+
def after(command_name, callback = nil, &blk)
|
|
262
|
+
@_mutex.synchronize do
|
|
263
|
+
command(command_name).after_callbacks.append(&_callback(callback, blk))
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# @since 0.1.0
|
|
268
|
+
# @api private
|
|
269
|
+
def get(arguments)
|
|
270
|
+
@commands.get(arguments)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
|
|
275
|
+
COMMAND_NAME_SEPARATOR = " "
|
|
276
|
+
|
|
277
|
+
# @since 0.2.0
|
|
278
|
+
# @api private
|
|
279
|
+
def command(command_name)
|
|
280
|
+
get(command_name.split(COMMAND_NAME_SEPARATOR)).tap do |result|
|
|
281
|
+
raise UnknownCommandError, command_name unless result.found?
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# @since 0.2.0
|
|
286
|
+
# @api private
|
|
287
|
+
#
|
|
288
|
+
def _callback(callback, blk)
|
|
289
|
+
return blk if blk.respond_to?(:to_proc)
|
|
290
|
+
|
|
291
|
+
case callback
|
|
292
|
+
when ->(c) { c.respond_to?(:call) }
|
|
293
|
+
callback.method(:call)
|
|
294
|
+
when Class
|
|
295
|
+
begin
|
|
296
|
+
_callback(callback.new, blk)
|
|
297
|
+
rescue ArgumentError
|
|
298
|
+
raise InvalidCallbackError, callback
|
|
299
|
+
end
|
|
300
|
+
else
|
|
301
|
+
raise InvalidCallbackError, callback
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Command name prefix
|
|
306
|
+
#
|
|
307
|
+
# @since 0.1.0
|
|
308
|
+
class Prefix
|
|
309
|
+
# @since 0.1.0
|
|
310
|
+
# @api private
|
|
311
|
+
def initialize(registry, prefix, aliases)
|
|
312
|
+
@registry = registry
|
|
313
|
+
@prefix = prefix
|
|
314
|
+
|
|
315
|
+
registry.set(prefix, nil, aliases)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# @since 0.1.0
|
|
319
|
+
#
|
|
320
|
+
# @see Dry::CLI::Registry#register
|
|
321
|
+
def register(name, command, aliases: [])
|
|
322
|
+
command_name = "#{prefix} #{name}"
|
|
323
|
+
registry.set(command_name, command, aliases)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
private
|
|
327
|
+
|
|
328
|
+
# @since 0.1.0
|
|
329
|
+
# @api private
|
|
330
|
+
attr_reader :registry
|
|
331
|
+
|
|
332
|
+
# @since 0.1.0
|
|
333
|
+
# @api private
|
|
334
|
+
attr_reader :prefix
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
end
|