gorails 0.1.2 → 0.1.3
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 +14 -1
- data/Gemfile.lock +1 -1
- data/bin/update-deps +95 -0
- data/exe/gorails +3 -2
- data/lib/gorails/commands/railsbytes.rb +10 -10
- data/lib/gorails/commands.rb +1 -4
- data/lib/gorails/version.rb +1 -1
- data/lib/gorails.rb +11 -20
- data/vendor/deps/cli-kit/REVISION +1 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/definition.rb +301 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/evaluation.rb +237 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/parser/node.rb +131 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/parser.rb +128 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/tokenizer.rb +132 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args.rb +15 -0
- data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +29 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_help.rb +256 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +141 -0
- data/vendor/deps/cli-kit/lib/cli/kit/config.rb +137 -0
- data/vendor/deps/cli-kit/lib/cli/kit/core_ext.rb +30 -0
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +165 -0
- data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +99 -0
- data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +94 -0
- data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +89 -0
- data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +95 -0
- data/vendor/deps/cli-kit/lib/cli/kit/opts.rb +284 -0
- data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +67 -0
- data/vendor/deps/cli-kit/lib/cli/kit/sorbet_runtime_stub.rb +142 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +253 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support.rb +10 -0
- data/vendor/deps/cli-kit/lib/cli/kit/system.rb +350 -0
- data/vendor/deps/cli-kit/lib/cli/kit/util.rb +133 -0
- data/vendor/deps/cli-kit/lib/cli/kit/version.rb +7 -0
- data/vendor/deps/cli-kit/lib/cli/kit.rb +151 -0
- data/vendor/deps/cli-ui/REVISION +1 -0
- data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +180 -0
- data/vendor/deps/cli-ui/lib/cli/ui/color.rb +98 -0
- data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +216 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +116 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +176 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +149 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +112 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +300 -0
- data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +92 -0
- data/vendor/deps/cli-ui/lib/cli/ui/os.rb +58 -0
- data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +72 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +102 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +534 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +36 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +354 -0
- data/vendor/deps/cli-ui/lib/cli/ui/sorbet_runtime_stub.rb +143 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +46 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +292 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +82 -0
- data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +264 -0
- data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +53 -0
- data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +107 -0
- data/vendor/deps/cli-ui/lib/cli/ui/version.rb +6 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +37 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +75 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +91 -0
- data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +63 -0
- data/vendor/deps/cli-ui/lib/cli/ui.rb +356 -0
- metadata +57 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e70b979a5284596091b39be5e2372914fc9712839b6e1d5a666bc7ac142e61a2
|
4
|
+
data.tar.gz: 838162e13a911e26c5654778513229b1411441d968f593bf467168b57d24b985
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b108dc53a37aa0bed0faae758d157c742de83e9f6b30258c42dc36e0a1b1440f9112bde7d5db71bc1ddeb23b7995151b82309eb5e921e05004e8c0cde1e180f
|
7
|
+
data.tar.gz: 585422f5c6221e3d3fb23c0aa7a460dc2725e702cfe58ded81566b21b94432087312cd4a29f20b8d58a02f7c5c94984eb79e0ee247c1a70b490c2776499b6bbd
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
## [0.1.
|
3
|
+
## [0.1.3] - 2022-03-13
|
4
|
+
|
5
|
+
- Vendor cli-kit and cli-ui for speed
|
6
|
+
This also skips loading bundler/setup which looks for a Gemfile
|
7
|
+
|
8
|
+
## [0.1.2] - 2022-03-13
|
9
|
+
|
10
|
+
- Add ability to view and apply individual Railsbytes
|
11
|
+
|
12
|
+
## [0.1.1] - 2022-03-13
|
4
13
|
|
5
14
|
- Initial release
|
15
|
+
|
16
|
+
## [0.1.0] - 2021-12-16
|
17
|
+
|
18
|
+
- Empty release
|
data/Gemfile.lock
CHANGED
data/bin/update-deps
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../../vendor/deps/cli-ui/lib", __FILE__))
|
4
|
+
require 'open3'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
def fmt(tag, msg)
|
8
|
+
# Not really CLI::UI.fmt compatible: no nesting support, for example.
|
9
|
+
# While we could pull in CLI::UI here, it makes it more difficult to
|
10
|
+
# bootstrap a new project and to fix a broken vendor.
|
11
|
+
fmt_msg = msg
|
12
|
+
.gsub(/{{yellow:(.*?)}}/, "\x1b[33m\\1\x1b[31m")
|
13
|
+
.gsub(/{{green:(.*?)}}/, "\x1b[32m\\1\x1b[31m")
|
14
|
+
.gsub(/{{blue:(.*?)}}/, "\x1b[34m\\1\x1b[31m")
|
15
|
+
.gsub(/{{bold_blue:(.*?)}}/, "\x1b[1;34m\\1\x1b[0;31m")
|
16
|
+
.gsub(/{{bold_green:(.*?)}}/, "\x1b[1;32m\\1\x1b[0;31m")
|
17
|
+
"\x1b[1;31m[#{tag}] \x1b[0;31m#{fmt_msg}\x1b[0m"
|
18
|
+
end
|
19
|
+
|
20
|
+
def bail(msg)
|
21
|
+
STDERR.puts(fmt("ERROR", msg))
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def warn(msg)
|
26
|
+
STDERR.puts(fmt("WARNING", msg))
|
27
|
+
end
|
28
|
+
|
29
|
+
def source_path
|
30
|
+
File.expand_path(ENV.fetch('SOURCE_ROOT', File.expand_path('../../..', __FILE__)))
|
31
|
+
end
|
32
|
+
|
33
|
+
deps = %w(cli-ui cli-kit)
|
34
|
+
|
35
|
+
deps.each do |dep|
|
36
|
+
path = File.expand_path(dep, source_path)
|
37
|
+
|
38
|
+
unless Dir.exist?(path)
|
39
|
+
bail(
|
40
|
+
"dependency is not checked out: {{yellow:#{dep}}}.\n" \
|
41
|
+
" This repo {{bold_blue:(github.com/shopify/#{dep})}} must be cloned at {{bold_blue:#{path}}} for this script to succeed.\n" \
|
42
|
+
" Currently, SOURCE_ROOT is set to {{bold_blue:#{source_path}}}.\n" \
|
43
|
+
" Alternatively, you can set {{bold_blue:SOURCE_ROOT}} to a directory containing {{yellow:#{dep}}}.\n" \
|
44
|
+
" {{bold_blue:SOURCE_ROOT}} defaults to {{bold_blue:../}}."
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
head_sha = nil
|
49
|
+
dirty = false
|
50
|
+
|
51
|
+
Dir.chdir(path) do
|
52
|
+
_, _, stat = Open3.capture3('git fetch origin main')
|
53
|
+
bail("couldn't git fetch in dependency: {{yellow:#{dep}}}") unless stat.success?
|
54
|
+
|
55
|
+
head_sha, stat = Open3.capture2('git rev-parse HEAD')
|
56
|
+
bail("couldn't determine HEAD: {{yellow:#{dep}}}") unless stat.success?
|
57
|
+
head_sha.chomp!
|
58
|
+
|
59
|
+
fetch_head_sha, stat = Open3.capture2('git rev-parse FETCH_HEAD')
|
60
|
+
bail("couldn't determine FETCH_HEAD: {{yellow:#{dep}}}") unless stat.success?
|
61
|
+
fetch_head_sha.chomp!
|
62
|
+
|
63
|
+
git_status, stat = Open3.capture2('git status --porcelain')
|
64
|
+
bail("couldn't determine git status: {{yellow:#{dep}}}") unless stat.success?
|
65
|
+
|
66
|
+
if head_sha != fetch_head_sha
|
67
|
+
warn(
|
68
|
+
"Copying files from {{yellow:#{path}}} to satisfy dependency {{yellow:#{dep}}}.\n" \
|
69
|
+
" However, the repo at {{yellow:#{path}}} isn't up to date.\n" \
|
70
|
+
" The checked-out revision is {{yellow:#{head_sha[0..8]}}}, and "\
|
71
|
+
"{{yellow:origin/master}} is {{yellow:#{fetch_head_sha[0..8]}}}.\n" \
|
72
|
+
" Unless you know what you're doing, you should {{green:cd}} to that repo and {{green:git pull}}, then run this again."
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
unless git_status.chomp.empty?
|
77
|
+
dirty = true
|
78
|
+
warn("importing uncommitted changes from dependency: {{yellow:#{dep}}}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
depdir = File.expand_path("../../vendor/deps/#{dep}", __FILE__)
|
83
|
+
FileUtils.rm_rf(depdir)
|
84
|
+
FileUtils.mkdir_p(depdir)
|
85
|
+
dstlib = File.expand_path('lib', depdir)
|
86
|
+
srclib = File.expand_path('lib', path)
|
87
|
+
|
88
|
+
FileUtils.cp_r(srclib, dstlib)
|
89
|
+
|
90
|
+
rev = head_sha
|
91
|
+
rev << " (dirty)" if dirty
|
92
|
+
rev << "\n"
|
93
|
+
|
94
|
+
File.write("#{depdir}/REVISION", rev)
|
95
|
+
end
|
data/exe/gorails
CHANGED
@@ -7,9 +7,10 @@ unshift_path = ->(path) {
|
|
7
7
|
p = File.expand_path("../../#{path}", __FILE__)
|
8
8
|
$LOAD_PATH.unshift(p) unless $LOAD_PATH.include?(p)
|
9
9
|
}
|
10
|
-
unshift_path.call(
|
10
|
+
unshift_path.call('vendor/deps/cli-ui/lib')
|
11
|
+
unshift_path.call('vendor/deps/cli-kit/lib')
|
12
|
+
unshift_path.call('lib')
|
11
13
|
|
12
|
-
require "bundler/setup"
|
13
14
|
require "gorails"
|
14
15
|
|
15
16
|
exit(Gorails::ErrorHandler.call do
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require "gorails"
|
2
|
-
require "
|
2
|
+
require "bundler"
|
3
3
|
require "json"
|
4
|
+
require "net/http"
|
4
5
|
|
5
6
|
module Gorails
|
6
7
|
module Commands
|
@@ -44,21 +45,20 @@ module Gorails
|
|
44
45
|
puts
|
45
46
|
|
46
47
|
CLI::UI::Prompt.ask("What would you like to do?") do |handler|
|
47
|
-
handler.option("View source") do |selection|
|
48
|
-
puts
|
49
|
-
puts byte["script"]
|
50
|
-
end
|
51
|
-
|
52
48
|
handler.option("Apply Railsbyte") do |selection|
|
53
49
|
puts
|
54
50
|
puts "Running Railsbyte..."
|
51
|
+
puts
|
52
|
+
|
53
|
+
system "rails app:template LOCATION=\"https://railsbytes.com/script/#{id}\""
|
54
|
+
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
handler.option("View source") do |selection|
|
57
|
+
puts
|
58
|
+
puts byte["script"]
|
59
59
|
end
|
60
60
|
|
61
|
-
handler.option("
|
61
|
+
handler.option("Exit") { |selection| exit 0 }
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
data/lib/gorails/commands.rb
CHANGED
@@ -2,10 +2,7 @@ require "gorails"
|
|
2
2
|
|
3
3
|
module Gorails
|
4
4
|
module Commands
|
5
|
-
Registry = CLI::Kit::CommandRegistry.new(
|
6
|
-
default: "help",
|
7
|
-
contextual_resolver: nil
|
8
|
-
)
|
5
|
+
Registry = CLI::Kit::CommandRegistry.new(default: "help")
|
9
6
|
|
10
7
|
def self.register(const, cmd, path)
|
11
8
|
autoload(const, path)
|
data/lib/gorails/version.rb
CHANGED
data/lib/gorails.rb
CHANGED
@@ -9,30 +9,21 @@ CLI::UI::StdoutRouter.enable
|
|
9
9
|
module Gorails
|
10
10
|
class Error < StandardError; end
|
11
11
|
|
12
|
-
extend CLI::Kit::Autocall
|
13
|
-
|
14
12
|
TOOL_NAME = "gorails"
|
15
13
|
ROOT = File.expand_path("../..", __FILE__)
|
16
|
-
LOG_FILE = "/tmp/
|
14
|
+
LOG_FILE = "/tmp/gorails.log"
|
17
15
|
|
18
16
|
autoload(:EntryPoint, "gorails/entry_point")
|
19
17
|
autoload(:Commands, "gorails/commands")
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
autocall(:ErrorHandler) do
|
33
|
-
CLI::Kit::ErrorHandler.new(
|
34
|
-
log_file: LOG_FILE,
|
35
|
-
exception_reporter: nil
|
36
|
-
)
|
37
|
-
end
|
19
|
+
Config = CLI::Kit::Config.new(tool_name: TOOL_NAME)
|
20
|
+
Command = CLI::Kit::BaseCommand
|
21
|
+
|
22
|
+
Executor = CLI::Kit::Executor.new(log_file: LOG_FILE)
|
23
|
+
Resolver = CLI::Kit::Resolver.new(
|
24
|
+
tool_name: TOOL_NAME,
|
25
|
+
command_registry: Gorails::Commands::Registry
|
26
|
+
)
|
27
|
+
|
28
|
+
ErrorHandler = CLI::Kit::ErrorHandler.new(log_file: LOG_FILE)
|
38
29
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
635eab627526f34c7ce15d2c894633246493b1a4
|
@@ -0,0 +1,301 @@
|
|
1
|
+
# typed: true
|
2
|
+
require 'cli/kit'
|
3
|
+
|
4
|
+
module CLI
|
5
|
+
module Kit
|
6
|
+
module Args
|
7
|
+
class Definition
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
Error = Class.new(Args::Error)
|
11
|
+
ConflictingFlag = Class.new(Error)
|
12
|
+
InvalidFlag = Class.new(Error)
|
13
|
+
InvalidLookup = Class.new(Error)
|
14
|
+
InvalidPosition = Class.new(Error)
|
15
|
+
|
16
|
+
sig { returns(T::Array[Flag]) }
|
17
|
+
attr_reader :flags
|
18
|
+
|
19
|
+
sig { returns(T::Array[Option]) }
|
20
|
+
attr_reader :options
|
21
|
+
|
22
|
+
sig { returns(T::Array[Position]) }
|
23
|
+
attr_reader :positions
|
24
|
+
|
25
|
+
sig { params(name: Symbol, short: T.nilable(String), long: T.nilable(String), desc: T.nilable(String)).void }
|
26
|
+
def add_flag(name, short: nil, long: nil, desc: nil)
|
27
|
+
short, long = strip_prefixes_and_validate(short, long)
|
28
|
+
flag = Flag.new(name: name, short: short, long: long, desc: desc)
|
29
|
+
add_resolution(flag)
|
30
|
+
@flags << flag
|
31
|
+
end
|
32
|
+
|
33
|
+
sig do
|
34
|
+
params(
|
35
|
+
name: Symbol, short: T.nilable(String), long: T.nilable(String),
|
36
|
+
desc: T.nilable(String), default: T.any(NilClass, String, T.proc.returns(String)),
|
37
|
+
required: T::Boolean, multi: T::Boolean,
|
38
|
+
).void
|
39
|
+
end
|
40
|
+
def add_option(name, short: nil, long: nil, desc: nil, default: nil, required: false, multi: false)
|
41
|
+
short, long = strip_prefixes_and_validate(short, long)
|
42
|
+
option = Option.new(
|
43
|
+
name: name, short: short, long: long, desc: desc, default: default,
|
44
|
+
required: required, multi: multi,
|
45
|
+
)
|
46
|
+
add_resolution(option)
|
47
|
+
@options << option
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { params(name: Symbol, required: T::Boolean, multiple: T::Boolean, desc: T.nilable(String)).void }
|
51
|
+
def add_position(name, required:, multiple:, desc: nil)
|
52
|
+
index = @positions.size
|
53
|
+
position = Position.new(name: name, desc: desc, required: required, multiple: multiple, index: index)
|
54
|
+
validate_order(position)
|
55
|
+
add_name_resolution(position)
|
56
|
+
@positions << position
|
57
|
+
end
|
58
|
+
|
59
|
+
sig { void }
|
60
|
+
def initialize
|
61
|
+
@flags = []
|
62
|
+
@options = []
|
63
|
+
@by_short = {}
|
64
|
+
@by_long = {}
|
65
|
+
@by_name = {}
|
66
|
+
@positions = []
|
67
|
+
end
|
68
|
+
|
69
|
+
class Flag
|
70
|
+
extend T::Sig
|
71
|
+
|
72
|
+
sig { returns(Symbol) }
|
73
|
+
attr_reader :name
|
74
|
+
|
75
|
+
sig { returns(T.nilable(String)) }
|
76
|
+
attr_reader :short
|
77
|
+
|
78
|
+
sig { returns(T.nilable(String)) }
|
79
|
+
attr_reader :long
|
80
|
+
|
81
|
+
sig { returns(T.nilable(String)) }
|
82
|
+
attr_reader :desc
|
83
|
+
|
84
|
+
sig { returns(String) }
|
85
|
+
def as_written_by_user
|
86
|
+
long ? "--#{long}" : "-#{short}"
|
87
|
+
end
|
88
|
+
|
89
|
+
sig { params(name: Symbol, short: T.nilable(String), long: T.nilable(String), desc: T.nilable(String)).void }
|
90
|
+
def initialize(name:, short: nil, long: nil, desc: nil)
|
91
|
+
if long&.start_with?('-') || short&.start_with?('-')
|
92
|
+
raise(ArgumentError, 'invalid - prefix')
|
93
|
+
end
|
94
|
+
|
95
|
+
@name = name
|
96
|
+
@short = short
|
97
|
+
@long = long
|
98
|
+
@desc = desc
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Position
|
103
|
+
extend T::Sig
|
104
|
+
|
105
|
+
sig { returns(Symbol) }
|
106
|
+
attr_reader :name
|
107
|
+
|
108
|
+
sig { returns(T.nilable(String)) }
|
109
|
+
attr_reader :desc
|
110
|
+
|
111
|
+
sig { returns(Integer) }
|
112
|
+
attr_reader :index
|
113
|
+
|
114
|
+
sig do
|
115
|
+
params(name: Symbol, desc: T.nilable(String), required: T::Boolean, multiple: T::Boolean, index: Integer)
|
116
|
+
.void
|
117
|
+
end
|
118
|
+
def initialize(name:, desc:, required:, multiple:, index:)
|
119
|
+
raise(ArgumentError, 'Cannot be required and multiple') if required && multiple
|
120
|
+
|
121
|
+
@name = name
|
122
|
+
@desc = desc
|
123
|
+
@required = required
|
124
|
+
@multiple = multiple
|
125
|
+
@index = index
|
126
|
+
end
|
127
|
+
|
128
|
+
sig { returns(T::Boolean) }
|
129
|
+
def required?
|
130
|
+
@required
|
131
|
+
end
|
132
|
+
|
133
|
+
sig { returns(T::Boolean) }
|
134
|
+
def multiple?
|
135
|
+
@multiple
|
136
|
+
end
|
137
|
+
|
138
|
+
sig { returns(T::Boolean) }
|
139
|
+
def optional?
|
140
|
+
!required?
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Option < Flag
|
145
|
+
extend T::Sig
|
146
|
+
|
147
|
+
sig { returns(T.nilable(String)) }
|
148
|
+
def default
|
149
|
+
if @default.is_a?(Proc)
|
150
|
+
@default.call
|
151
|
+
else
|
152
|
+
@default
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
sig { returns(T::Boolean) }
|
157
|
+
def dynamic_default?
|
158
|
+
@default.is_a?(Proc)
|
159
|
+
end
|
160
|
+
|
161
|
+
sig { returns(T::Boolean) }
|
162
|
+
attr_reader :required
|
163
|
+
|
164
|
+
sig { returns(T::Boolean) }
|
165
|
+
attr_reader :multi
|
166
|
+
|
167
|
+
sig do
|
168
|
+
params(
|
169
|
+
name: Symbol, short: T.nilable(String), long: T.nilable(String),
|
170
|
+
desc: T.nilable(String), default: T.any(NilClass, String, T.proc.returns(String)),
|
171
|
+
required: T::Boolean, multi: T::Boolean,
|
172
|
+
).void
|
173
|
+
end
|
174
|
+
def initialize(name:, short: nil, long: nil, desc: nil, default: nil, required: false, multi: false)
|
175
|
+
if multi && (default || required)
|
176
|
+
raise(ArgumentError, 'multi-valued options cannot have a default or required value')
|
177
|
+
end
|
178
|
+
|
179
|
+
super(name: name, short: short, long: long, desc: desc)
|
180
|
+
@default = default
|
181
|
+
@required = required
|
182
|
+
@multi = multi
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
sig { params(name: Symbol).returns(T.nilable(Flag)) }
|
187
|
+
def lookup_flag(name)
|
188
|
+
flagopt = @by_name[name]
|
189
|
+
if flagopt.class == Flag
|
190
|
+
flagopt
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
sig { params(name: Symbol).returns(T.nilable(Option)) }
|
195
|
+
def lookup_option(name)
|
196
|
+
flagopt = @by_name[name]
|
197
|
+
if flagopt.class == Option
|
198
|
+
flagopt
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
sig { params(name: String).returns(T.any(Flag, Option, NilClass)) }
|
203
|
+
def lookup_short(name)
|
204
|
+
raise(InvalidLookup, "invalid '-' prefix") if name.start_with?('-')
|
205
|
+
|
206
|
+
@by_short[name]
|
207
|
+
end
|
208
|
+
|
209
|
+
sig { params(name: String).returns(T.any(Flag, Option, NilClass)) }
|
210
|
+
def lookup_long(name)
|
211
|
+
raise(InvalidLookup, "invalid '-' prefix") if name.start_with?('-')
|
212
|
+
|
213
|
+
@by_long[name]
|
214
|
+
end
|
215
|
+
|
216
|
+
sig { params(name: Symbol).returns(T.nilable(Position)) }
|
217
|
+
def lookup_position(name)
|
218
|
+
position = @by_name[name]
|
219
|
+
if position.class == Position
|
220
|
+
position
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
sig { params(position: Position).void }
|
227
|
+
def validate_order(position)
|
228
|
+
if @positions.last&.multiple?
|
229
|
+
raise(InvalidPosition, 'Cannot have any more positional arguments after multiple')
|
230
|
+
elsif @positions.last&.optional? && !position.optional?
|
231
|
+
raise(InvalidPosition, 'Cannot have any more required positional arguments after optional ones')
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
sig { params(short: String).returns(String) }
|
236
|
+
def strip_short_prefix(short)
|
237
|
+
unless short.match?(/^-[^-]/)
|
238
|
+
raise(InvalidFlag, "Short flag '#{short}' does not start with '-'")
|
239
|
+
end
|
240
|
+
if short.size != 2
|
241
|
+
raise(InvalidFlag, 'Short flag must be a single character')
|
242
|
+
end
|
243
|
+
|
244
|
+
short.sub(/^-/, '')
|
245
|
+
end
|
246
|
+
|
247
|
+
sig { params(long: String).returns(String) }
|
248
|
+
def strip_long_prefix(long)
|
249
|
+
unless long.match?(/^--[^-]/)
|
250
|
+
raise(InvalidFlag, "Long flag '#{long}' does not start with '--'")
|
251
|
+
end
|
252
|
+
|
253
|
+
long.sub(/^--/, '')
|
254
|
+
end
|
255
|
+
|
256
|
+
sig do
|
257
|
+
params(short: T.nilable(String), long: T.nilable(String))
|
258
|
+
.returns([T.nilable(String), T.nilable(String)])
|
259
|
+
end
|
260
|
+
def strip_prefixes_and_validate(short, long)
|
261
|
+
if short.nil? && long.nil?
|
262
|
+
raise(Error, 'One or more of short and long must be specified')
|
263
|
+
end
|
264
|
+
|
265
|
+
short = strip_short_prefix(short) if short
|
266
|
+
long = strip_long_prefix(long) if long
|
267
|
+
|
268
|
+
[short, long]
|
269
|
+
end
|
270
|
+
|
271
|
+
sig { params(flagopt: Flag).void }
|
272
|
+
def add_resolution(flagopt)
|
273
|
+
if flagopt.short
|
274
|
+
if (existing = @by_short[flagopt.short])
|
275
|
+
raise(ConflictingFlag, "Short flag '#{flagopt.short}' already defined by #{existing.name}")
|
276
|
+
end
|
277
|
+
|
278
|
+
@by_short[flagopt.short] = flagopt
|
279
|
+
end
|
280
|
+
if flagopt.long
|
281
|
+
if (existing = @by_long[flagopt.long])
|
282
|
+
raise(ConflictingFlag, "Long flag '#{flagopt.long}' already defined by #{existing.name}")
|
283
|
+
end
|
284
|
+
|
285
|
+
@by_long[flagopt.long] = flagopt
|
286
|
+
end
|
287
|
+
add_name_resolution(flagopt)
|
288
|
+
end
|
289
|
+
|
290
|
+
sig { params(arg: T.any(Flag, Position)).void }
|
291
|
+
def add_name_resolution(arg)
|
292
|
+
if (existing = @by_name[arg.name])
|
293
|
+
raise(ConflictingFlag, "Flag '#{arg.name}' already defined by #{existing.name}")
|
294
|
+
end
|
295
|
+
|
296
|
+
@by_name[arg.name] = arg
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|