rfix 1.4.0.pre.201 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 +67 -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 +117 -163
- data/lib/rfix/types.rb +52 -0
- data/lib/rfix/version.rb +1 -1
- data/rfix.gemspec +28 -49
- 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 +276 -190
- data/.github/workflows/main.yml +0 -24
- data/.gitignore +0 -43
- data/.rspec +0 -2
- data/.rubocop.yml +0 -87
- data/.travis.yml +0 -34
- 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 -92
- data/Rakefile +0 -27
- 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
|