gorails 0.1.0 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +65 -0
- data/README.md +41 -12
- data/bin/update-deps +95 -0
- data/exe/gorails +18 -0
- data/gorails.gemspec +4 -3
- data/lib/gorails/commands/episodes.rb +25 -0
- data/lib/gorails/commands/example.rb +19 -0
- data/lib/gorails/commands/help.rb +21 -0
- data/lib/gorails/commands/jobs.rb +25 -0
- data/lib/gorails/commands/jumpstart.rb +29 -0
- data/lib/gorails/commands/railsbytes.rb +67 -0
- data/lib/gorails/commands.rb +19 -0
- data/lib/gorails/entry_point.rb +10 -0
- data/lib/gorails/version.rb +1 -1
- data/lib/gorails.rb +22 -1
- 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 +114 -5
@@ -0,0 +1,354 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# typed: true
|
4
|
+
|
5
|
+
require 'cli/ui'
|
6
|
+
require 'readline'
|
7
|
+
|
8
|
+
module Readline
|
9
|
+
unless const_defined?(:FILENAME_COMPLETION_PROC)
|
10
|
+
FILENAME_COMPLETION_PROC = proc do |input|
|
11
|
+
directory = input[-1] == '/' ? input : File.dirname(input)
|
12
|
+
filename = input[-1] == '/' ? '' : File.basename(input)
|
13
|
+
|
14
|
+
(Dir.entries(directory).select do |fp|
|
15
|
+
fp.start_with?(filename)
|
16
|
+
end - (input[-1] == '.' ? [] : ['.', '..'])).map do |fp|
|
17
|
+
File.join(directory, fp).gsub(/\A\.\//, '')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module CLI
|
24
|
+
module UI
|
25
|
+
module Prompt
|
26
|
+
autoload :InteractiveOptions, 'cli/ui/prompt/interactive_options'
|
27
|
+
autoload :OptionsHandler, 'cli/ui/prompt/options_handler'
|
28
|
+
|
29
|
+
class << self
|
30
|
+
extend T::Sig
|
31
|
+
|
32
|
+
# Ask a user a question with either free form answer or a set of answers (multiple choice)
|
33
|
+
# Can use arrows, y/n, numbers (1/2), and vim bindings to control multiple choice selection
|
34
|
+
# Do not use this method for yes/no questions. Use +confirm+
|
35
|
+
#
|
36
|
+
# * Handles free form answers (options are nil)
|
37
|
+
# * Handles default answers for free form text
|
38
|
+
# * Handles file auto completion for file input
|
39
|
+
# * Handles interactively choosing answers using +InteractiveOptions+
|
40
|
+
#
|
41
|
+
# https://user-images.githubusercontent.com/3074765/33799822-47f23302-dd01-11e7-82f3-9072a5a5f611.png
|
42
|
+
#
|
43
|
+
# ==== Attributes
|
44
|
+
#
|
45
|
+
# * +question+ - (required) The question to ask the user
|
46
|
+
#
|
47
|
+
# ==== Options
|
48
|
+
#
|
49
|
+
# * +:options+ - Options that the user may select from. Will use +InteractiveOptions+ to do so.
|
50
|
+
# * +:default+ - The default answer to the question (e.g. they just press enter and don't input anything)
|
51
|
+
# * +:is_file+ - Tells the input to use file auto-completion (tab completion)
|
52
|
+
# * +:allow_empty+ - Allows the answer to be empty
|
53
|
+
# * +:multiple+ - Allow multiple options to be selected
|
54
|
+
# * +:filter_ui+ - Enable option filtering (default: true)
|
55
|
+
# * +:select_ui+ - Enable long-form option selection (default: true)
|
56
|
+
#
|
57
|
+
# Note:
|
58
|
+
# * +:options+ or providing a +Block+ conflicts with +:default+ and +:is_file+,
|
59
|
+
# you cannot set options with either of these keywords
|
60
|
+
# * +:default+ conflicts with +:allow_empty:, you cannot set these together
|
61
|
+
# * +:options+ conflicts with providing a +Block+ , you may only set one
|
62
|
+
# * +:multiple+ can only be used with +:options+ or a +Block+; it is ignored, otherwise.
|
63
|
+
#
|
64
|
+
# ==== Block (optional)
|
65
|
+
#
|
66
|
+
# * A Proc that provides a +OptionsHandler+ and uses the public +:option+ method to add options and their
|
67
|
+
# respective handlers
|
68
|
+
#
|
69
|
+
# ==== Return Value
|
70
|
+
#
|
71
|
+
# * If a +Block+ was not provided, the selected option or response to the free form question will be returned
|
72
|
+
# * If a +Block+ was provided, the evaluated value of the +Block+ will be returned
|
73
|
+
#
|
74
|
+
# ==== Example Usage:
|
75
|
+
#
|
76
|
+
# Free form question
|
77
|
+
# CLI::UI::Prompt.ask('What color is the sky?')
|
78
|
+
#
|
79
|
+
# Free form question with a file answer
|
80
|
+
# CLI::UI::Prompt.ask('Where is your Gemfile located?', is_file: true)
|
81
|
+
#
|
82
|
+
# Free form question with a default answer
|
83
|
+
# CLI::UI::Prompt.ask('What color is the sky?', default: 'blue')
|
84
|
+
#
|
85
|
+
# Free form question when the answer can be empty
|
86
|
+
# CLI::UI::Prompt.ask('What is your opinion on this question?', allow_empty: true)
|
87
|
+
#
|
88
|
+
# Interactive (multiple choice) question
|
89
|
+
# CLI::UI::Prompt.ask('What kind of project is this?', options: %w(rails go ruby python))
|
90
|
+
#
|
91
|
+
# Interactive (multiple choice) question with defined handlers
|
92
|
+
# CLI::UI::Prompt.ask('What kind of project is this?') do |handler|
|
93
|
+
# handler.option('rails') { |selection| selection }
|
94
|
+
# handler.option('go') { |selection| selection }
|
95
|
+
# handler.option('ruby') { |selection| selection }
|
96
|
+
# handler.option('python') { |selection| selection }
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
sig do
|
100
|
+
params(
|
101
|
+
question: String,
|
102
|
+
options: T.nilable(T::Array[String]),
|
103
|
+
default: T.nilable(T.any(String, T::Array[String])),
|
104
|
+
is_file: T::Boolean,
|
105
|
+
allow_empty: T::Boolean,
|
106
|
+
multiple: T::Boolean,
|
107
|
+
filter_ui: T::Boolean,
|
108
|
+
select_ui: T::Boolean,
|
109
|
+
options_proc: T.nilable(T.proc.params(handler: OptionsHandler).void)
|
110
|
+
).returns(T.any(String, T::Array[String]))
|
111
|
+
end
|
112
|
+
def ask(
|
113
|
+
question,
|
114
|
+
options: nil,
|
115
|
+
default: nil,
|
116
|
+
is_file: false,
|
117
|
+
allow_empty: true,
|
118
|
+
multiple: false,
|
119
|
+
filter_ui: true,
|
120
|
+
select_ui: true,
|
121
|
+
&options_proc
|
122
|
+
)
|
123
|
+
has_options = !!(options || block_given?)
|
124
|
+
if has_options && default && !multiple
|
125
|
+
raise(ArgumentError, 'conflicting arguments: default may not be provided with options when not multiple')
|
126
|
+
end
|
127
|
+
|
128
|
+
if has_options && is_file
|
129
|
+
raise(ArgumentError, 'conflicting arguments: is_file is only useful when options are not provided')
|
130
|
+
end
|
131
|
+
|
132
|
+
if options && multiple && default && !(Array(default) - options).empty?
|
133
|
+
raise(ArgumentError, 'conflicting arguments: default should only include elements present in options')
|
134
|
+
end
|
135
|
+
|
136
|
+
if multiple && !has_options
|
137
|
+
raise(ArgumentError, 'conflicting arguments: options must be provided when multiple is true')
|
138
|
+
end
|
139
|
+
|
140
|
+
if !multiple && default.is_a?(Array)
|
141
|
+
raise(ArgumentError, 'conflicting arguments: multiple defaults may only be provided when multiple is true')
|
142
|
+
end
|
143
|
+
|
144
|
+
if has_options
|
145
|
+
ask_interactive(
|
146
|
+
question,
|
147
|
+
options,
|
148
|
+
multiple: multiple,
|
149
|
+
default: default,
|
150
|
+
filter_ui: filter_ui,
|
151
|
+
select_ui: select_ui,
|
152
|
+
&options_proc
|
153
|
+
)
|
154
|
+
else
|
155
|
+
ask_free_form(question, T.cast(default, T.nilable(String)), is_file, allow_empty)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Asks the user for a single-line answer, without displaying the characters while typing.
|
160
|
+
# Typically used for password prompts
|
161
|
+
#
|
162
|
+
# ==== Return Value
|
163
|
+
#
|
164
|
+
# The password, without a trailing newline.
|
165
|
+
# If the user simply presses "Enter" without typing any password, this will return an empty string.
|
166
|
+
sig { params(question: String).returns(String) }
|
167
|
+
def ask_password(question)
|
168
|
+
require 'io/console'
|
169
|
+
|
170
|
+
CLI::UI.with_frame_color(:blue) do
|
171
|
+
STDOUT.print(CLI::UI.fmt('{{?}} ' + question)) # Do not use puts_question to avoid the new line.
|
172
|
+
|
173
|
+
# noecho interacts poorly with Readline under system Ruby, so do a manual `gets` here.
|
174
|
+
# No fancy Readline integration (like echoing back) is required for a password prompt anyway.
|
175
|
+
password = STDIN.noecho do
|
176
|
+
# Chomp will remove the one new line character added by `gets`, without touching potential extra spaces:
|
177
|
+
# " 123 \n".chomp => " 123 "
|
178
|
+
T.must(STDIN.gets).chomp
|
179
|
+
end
|
180
|
+
|
181
|
+
STDOUT.puts # Complete the line
|
182
|
+
|
183
|
+
password
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Asks the user a yes/no question.
|
188
|
+
# Can use arrows, y/n, numbers (1/2), and vim bindings to control
|
189
|
+
#
|
190
|
+
# ==== Example Usage:
|
191
|
+
#
|
192
|
+
# Confirmation question
|
193
|
+
# CLI::UI::Prompt.confirm('Is the sky blue?')
|
194
|
+
#
|
195
|
+
# CLI::UI::Prompt.confirm('Do a dangerous thing?', default: false)
|
196
|
+
#
|
197
|
+
sig { params(question: String, default: T::Boolean).returns(T::Boolean) }
|
198
|
+
def confirm(question, default: true)
|
199
|
+
ask_interactive(question, default ? ['yes', 'no'] : ['no', 'yes'], filter_ui: false) == 'yes'
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
sig do
|
205
|
+
params(question: String, default: T.nilable(String), is_file: T::Boolean, allow_empty: T::Boolean)
|
206
|
+
.returns(String)
|
207
|
+
end
|
208
|
+
def ask_free_form(question, default, is_file, allow_empty)
|
209
|
+
if default && !allow_empty
|
210
|
+
raise(ArgumentError, 'conflicting arguments: default enabled but allow_empty is false')
|
211
|
+
end
|
212
|
+
|
213
|
+
if default
|
214
|
+
puts_question("#{question} (empty = #{default})")
|
215
|
+
else
|
216
|
+
puts_question(question)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Ask a free form question
|
220
|
+
loop do
|
221
|
+
line = readline(is_file: is_file)
|
222
|
+
|
223
|
+
if line.empty? && default
|
224
|
+
write_default_over_empty_input(default)
|
225
|
+
return default
|
226
|
+
end
|
227
|
+
|
228
|
+
if !line.empty? || allow_empty
|
229
|
+
return line
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
sig do
|
235
|
+
params(
|
236
|
+
question: String,
|
237
|
+
options: T.nilable(T::Array[String]),
|
238
|
+
multiple: T::Boolean,
|
239
|
+
default: T.nilable(T.any(String, T::Array[String])),
|
240
|
+
filter_ui: T::Boolean,
|
241
|
+
select_ui: T::Boolean
|
242
|
+
).returns(T.any(String, T::Array[String]))
|
243
|
+
end
|
244
|
+
def ask_interactive(question, options = nil, multiple: false, default: nil, filter_ui: true, select_ui: true)
|
245
|
+
raise(ArgumentError, 'conflicting arguments: options and block given') if options && block_given?
|
246
|
+
|
247
|
+
options ||= if block_given?
|
248
|
+
handler = OptionsHandler.new
|
249
|
+
yield handler
|
250
|
+
handler.options
|
251
|
+
end
|
252
|
+
|
253
|
+
raise(ArgumentError, 'insufficient options') if options.nil? || options.empty?
|
254
|
+
|
255
|
+
navigate_text = if CLI::UI::OS.current.suggest_arrow_keys?
|
256
|
+
'Choose with ↑ ↓ ⏎'
|
257
|
+
else
|
258
|
+
"Navigate up with 'k' and down with 'j', press Enter to select"
|
259
|
+
end
|
260
|
+
|
261
|
+
instructions = (multiple ? 'Toggle options. ' : '') + navigate_text
|
262
|
+
instructions += ", filter with 'f'" if filter_ui
|
263
|
+
instructions += ", enter option with 'e'" if select_ui && (options.size > 9)
|
264
|
+
puts_question("#{question} {{yellow:(#{instructions})}}")
|
265
|
+
resp = interactive_prompt(options, multiple: multiple, default: default)
|
266
|
+
|
267
|
+
# Clear the line
|
268
|
+
print(ANSI.previous_line + ANSI.clear_to_end_of_line)
|
269
|
+
# Force StdoutRouter to prefix
|
270
|
+
print(ANSI.previous_line + "\n")
|
271
|
+
|
272
|
+
# reset the question to include the answer
|
273
|
+
resp_text = case resp
|
274
|
+
when Array
|
275
|
+
case resp.size
|
276
|
+
when 0
|
277
|
+
'<nothing>'
|
278
|
+
when 1..2
|
279
|
+
resp.join(' and ')
|
280
|
+
else
|
281
|
+
"#{resp.size} items"
|
282
|
+
end
|
283
|
+
else
|
284
|
+
resp
|
285
|
+
end
|
286
|
+
puts_question("#{question} (You chose: {{italic:#{resp_text}}})")
|
287
|
+
|
288
|
+
return T.must(handler).call(resp) if block_given?
|
289
|
+
|
290
|
+
resp
|
291
|
+
end
|
292
|
+
|
293
|
+
# Useful for stubbing in tests
|
294
|
+
sig do
|
295
|
+
params(options: T::Array[String], multiple: T::Boolean, default: T.nilable(T.any(T::Array[String], String)))
|
296
|
+
.returns(T.any(T::Array[String], String))
|
297
|
+
end
|
298
|
+
def interactive_prompt(options, multiple: false, default: nil)
|
299
|
+
InteractiveOptions.call(options, multiple: multiple, default: default)
|
300
|
+
end
|
301
|
+
|
302
|
+
sig { params(default: String).void }
|
303
|
+
def write_default_over_empty_input(default)
|
304
|
+
CLI::UI.raw do
|
305
|
+
STDERR.puts(
|
306
|
+
CLI::UI::ANSI.cursor_up(1) +
|
307
|
+
"\r" +
|
308
|
+
CLI::UI::ANSI.cursor_forward(4) + # TODO: width
|
309
|
+
default +
|
310
|
+
CLI::UI::Color::RESET.code
|
311
|
+
)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
sig { params(str: String).void }
|
316
|
+
def puts_question(str)
|
317
|
+
CLI::UI.with_frame_color(:blue) do
|
318
|
+
STDOUT.puts(CLI::UI.fmt('{{?}} ' + str))
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
sig { params(is_file: T::Boolean).returns(String) }
|
323
|
+
def readline(is_file: false)
|
324
|
+
if is_file
|
325
|
+
Readline.completion_proc = Readline::FILENAME_COMPLETION_PROC
|
326
|
+
Readline.completion_append_character = ''
|
327
|
+
else
|
328
|
+
Readline.completion_proc = proc { |*| nil }
|
329
|
+
Readline.completion_append_character = ' '
|
330
|
+
end
|
331
|
+
|
332
|
+
# because Readline is a C library, CLI::UI's hooks into $stdout don't
|
333
|
+
# work. We could work around this by having CLI::UI use a pipe and a
|
334
|
+
# thread to manage output, but the current strategy feels like a
|
335
|
+
# better tradeoff.
|
336
|
+
prefix = CLI::UI.with_frame_color(:blue) { CLI::UI::Frame.prefix }
|
337
|
+
# If a prompt is interrupted on Windows it locks the colour of the terminal from that point on, so we should
|
338
|
+
# not change the colour here.
|
339
|
+
prompt = prefix + CLI::UI.fmt('{{blue:> }}')
|
340
|
+
prompt += CLI::UI::Color::YELLOW.code if CLI::UI::OS.current.use_color_prompt?
|
341
|
+
|
342
|
+
begin
|
343
|
+
line = Readline.readline(prompt, true)
|
344
|
+
print(CLI::UI::Color::RESET.code)
|
345
|
+
line.to_s.chomp
|
346
|
+
rescue Interrupt
|
347
|
+
CLI::UI.raw { STDERR.puts('^C' + CLI::UI::Color::RESET.code) }
|
348
|
+
raise
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# typed: ignore
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module T
|
5
|
+
class << self
|
6
|
+
def absurd(value); end
|
7
|
+
def all(type_a, type_b, *types); end
|
8
|
+
def any(type_a, type_b, *types); end
|
9
|
+
def attached_class; end
|
10
|
+
def class_of(klass); end
|
11
|
+
def enum(values); end
|
12
|
+
def nilable(type); end
|
13
|
+
def noreturn; end
|
14
|
+
def self_type; end
|
15
|
+
def type_alias(type = nil, &_blk); end
|
16
|
+
def type_parameter(name); end
|
17
|
+
def untyped; end
|
18
|
+
|
19
|
+
def assert_type!(value, _type, _checked: true)
|
20
|
+
value
|
21
|
+
end
|
22
|
+
|
23
|
+
def cast(value, _type, _checked: true)
|
24
|
+
value
|
25
|
+
end
|
26
|
+
|
27
|
+
def let(value, _type, _checked: true)
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
31
|
+
def must(arg, _msg = nil)
|
32
|
+
arg
|
33
|
+
end
|
34
|
+
|
35
|
+
def proc
|
36
|
+
T::Proc.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def reveal_type(value)
|
40
|
+
value
|
41
|
+
end
|
42
|
+
|
43
|
+
def unsafe(value)
|
44
|
+
value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Sig
|
49
|
+
def sig(arg0 = nil, &blk); end
|
50
|
+
end
|
51
|
+
|
52
|
+
module Helpers
|
53
|
+
def abstract!; end
|
54
|
+
def interface!; end
|
55
|
+
def final!; end
|
56
|
+
def sealed!; end
|
57
|
+
def mixes_in_class_methods(mod); end
|
58
|
+
end
|
59
|
+
|
60
|
+
module Generic
|
61
|
+
include(T::Helpers)
|
62
|
+
|
63
|
+
def type_parameters(*params); end
|
64
|
+
def type_member(variance = :invariant, fixed: nil, lower: nil, upper: BasicObject); end
|
65
|
+
def type_template(variance = :invariant, fixed: nil, lower: nil, upper: BasicObject); end
|
66
|
+
|
67
|
+
def [](*types)
|
68
|
+
self
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module Array
|
73
|
+
def self.[](type); end
|
74
|
+
end
|
75
|
+
|
76
|
+
Boolean = Object.new.freeze
|
77
|
+
|
78
|
+
module Configuration
|
79
|
+
def self.call_validation_error_handler(signature, opts); end
|
80
|
+
def self.call_validation_error_handler=(value); end
|
81
|
+
def self.default_checked_level=(default_checked_level); end
|
82
|
+
def self.enable_checking_for_sigs_marked_checked_tests; end
|
83
|
+
def self.enable_final_checks_on_hooks; end
|
84
|
+
def self.enable_legacy_t_enum_migration_mode; end
|
85
|
+
def self.reset_final_checks_on_hooks; end
|
86
|
+
def self.hard_assert_handler(str, extra); end
|
87
|
+
def self.hard_assert_handler=(value); end
|
88
|
+
def self.inline_type_error_handler(error); end
|
89
|
+
def self.inline_type_error_handler=(value); end
|
90
|
+
def self.log_info_handler(str, extra); end
|
91
|
+
def self.log_info_handler=(value); end
|
92
|
+
def self.scalar_types; end
|
93
|
+
def self.scalar_types=(values); end
|
94
|
+
# rubocop:disable Naming/InclusiveLanguage
|
95
|
+
def self.sealed_violation_whitelist; end
|
96
|
+
def self.sealed_violation_whitelist=(sealed_violation_whitelist); end
|
97
|
+
# rubocop:enable Naming/InclusiveLanguage
|
98
|
+
def self.sig_builder_error_handler(error, location); end
|
99
|
+
def self.sig_builder_error_handler=(value); end
|
100
|
+
def self.sig_validation_error_handler(error, opts); end
|
101
|
+
def self.sig_validation_error_handler=(value); end
|
102
|
+
def self.soft_assert_handler(str, extra); end
|
103
|
+
def self.soft_assert_handler=(value); end
|
104
|
+
end
|
105
|
+
|
106
|
+
module Enumerable
|
107
|
+
def self.[](type); end
|
108
|
+
end
|
109
|
+
|
110
|
+
module Enumerator
|
111
|
+
def self.[](type); end
|
112
|
+
end
|
113
|
+
|
114
|
+
module Hash
|
115
|
+
def self.[](keys, values); end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Proc
|
119
|
+
def bind(*_)
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
def params(*_param)
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
def void
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
def returns(_type)
|
132
|
+
self
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
module Range
|
137
|
+
def self.[](type); end
|
138
|
+
end
|
139
|
+
|
140
|
+
module Set
|
141
|
+
def self.[](type); end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# typed: true
|
2
|
+
module CLI
|
3
|
+
module UI
|
4
|
+
module Spinner
|
5
|
+
class Async
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
# Convenience method for +initialize+
|
9
|
+
#
|
10
|
+
sig { params(title: String).returns(Async) }
|
11
|
+
def self.start(title)
|
12
|
+
new(title)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Initializes a new asynchronous spinner with no specific end.
|
16
|
+
# Must call +.stop+ to end the spinner
|
17
|
+
#
|
18
|
+
# ==== Attributes
|
19
|
+
#
|
20
|
+
# * +title+ - Title of the spinner to use
|
21
|
+
#
|
22
|
+
# ==== Example Usage:
|
23
|
+
#
|
24
|
+
# CLI::UI::Spinner::Async.new('Title')
|
25
|
+
#
|
26
|
+
sig { params(title: String).void }
|
27
|
+
def initialize(title)
|
28
|
+
require 'thread'
|
29
|
+
sg = CLI::UI::Spinner::SpinGroup.new
|
30
|
+
@m = Mutex.new
|
31
|
+
@cv = ConditionVariable.new
|
32
|
+
sg.add(title) { @m.synchronize { @cv.wait(@m) } }
|
33
|
+
@t = Thread.new { sg.wait }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Stops an asynchronous spinner
|
37
|
+
#
|
38
|
+
sig { returns(T::Boolean) }
|
39
|
+
def stop
|
40
|
+
@m.synchronize { @cv.signal }
|
41
|
+
@t.value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|