gorails 0.1.0 → 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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/Gemfile +3 -1
  4. data/Gemfile.lock +65 -0
  5. data/README.md +41 -12
  6. data/bin/update-deps +95 -0
  7. data/exe/gorails +18 -0
  8. data/gorails.gemspec +4 -3
  9. data/lib/gorails/commands/episodes.rb +25 -0
  10. data/lib/gorails/commands/example.rb +19 -0
  11. data/lib/gorails/commands/help.rb +21 -0
  12. data/lib/gorails/commands/jobs.rb +25 -0
  13. data/lib/gorails/commands/jumpstart.rb +29 -0
  14. data/lib/gorails/commands/railsbytes.rb +67 -0
  15. data/lib/gorails/commands.rb +19 -0
  16. data/lib/gorails/entry_point.rb +10 -0
  17. data/lib/gorails/version.rb +1 -1
  18. data/lib/gorails.rb +22 -1
  19. data/vendor/deps/cli-kit/REVISION +1 -0
  20. data/vendor/deps/cli-kit/lib/cli/kit/args/definition.rb +301 -0
  21. data/vendor/deps/cli-kit/lib/cli/kit/args/evaluation.rb +237 -0
  22. data/vendor/deps/cli-kit/lib/cli/kit/args/parser/node.rb +131 -0
  23. data/vendor/deps/cli-kit/lib/cli/kit/args/parser.rb +128 -0
  24. data/vendor/deps/cli-kit/lib/cli/kit/args/tokenizer.rb +132 -0
  25. data/vendor/deps/cli-kit/lib/cli/kit/args.rb +15 -0
  26. data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +29 -0
  27. data/vendor/deps/cli-kit/lib/cli/kit/command_help.rb +256 -0
  28. data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +141 -0
  29. data/vendor/deps/cli-kit/lib/cli/kit/config.rb +137 -0
  30. data/vendor/deps/cli-kit/lib/cli/kit/core_ext.rb +30 -0
  31. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +165 -0
  32. data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +99 -0
  33. data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +94 -0
  34. data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +89 -0
  35. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +95 -0
  36. data/vendor/deps/cli-kit/lib/cli/kit/opts.rb +284 -0
  37. data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +67 -0
  38. data/vendor/deps/cli-kit/lib/cli/kit/sorbet_runtime_stub.rb +142 -0
  39. data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +253 -0
  40. data/vendor/deps/cli-kit/lib/cli/kit/support.rb +10 -0
  41. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +350 -0
  42. data/vendor/deps/cli-kit/lib/cli/kit/util.rb +133 -0
  43. data/vendor/deps/cli-kit/lib/cli/kit/version.rb +7 -0
  44. data/vendor/deps/cli-kit/lib/cli/kit.rb +151 -0
  45. data/vendor/deps/cli-ui/REVISION +1 -0
  46. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +180 -0
  47. data/vendor/deps/cli-ui/lib/cli/ui/color.rb +98 -0
  48. data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +216 -0
  49. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +116 -0
  50. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +176 -0
  51. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +149 -0
  52. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +112 -0
  53. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +300 -0
  54. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +92 -0
  55. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +58 -0
  56. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +72 -0
  57. data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +102 -0
  58. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +534 -0
  59. data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +36 -0
  60. data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +354 -0
  61. data/vendor/deps/cli-ui/lib/cli/ui/sorbet_runtime_stub.rb +143 -0
  62. data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +46 -0
  63. data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +292 -0
  64. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +82 -0
  65. data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +264 -0
  66. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +53 -0
  67. data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +107 -0
  68. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +6 -0
  69. data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +37 -0
  70. data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +75 -0
  71. data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +91 -0
  72. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +63 -0
  73. data/vendor/deps/cli-ui/lib/cli/ui.rb +356 -0
  74. metadata +114 -5
@@ -0,0 +1,107 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'cli/ui'
5
+
6
+ module CLI
7
+ module UI
8
+ # Truncater truncates a string to a provided printable width.
9
+ module Truncater
10
+ PARSE_ROOT = :root
11
+ PARSE_ANSI = :ansi
12
+ PARSE_ESC = :esc
13
+ PARSE_ZWJ = :zwj
14
+
15
+ ESC = 0x1b
16
+ LEFT_SQUARE_BRACKET = 0x5b
17
+ ZWJ = 0x200d # emojipedia.org/emoji-zwj-sequences
18
+ SEMICOLON = 0x3b
19
+
20
+ # EMOJI_RANGE in particular is super inaccurate. This is best-effort.
21
+ # If you need this to be more accurate, we'll almost certainly accept a
22
+ # PR improving it.
23
+ EMOJI_RANGE = 0x1f300..0x1f5ff
24
+ NUMERIC_RANGE = 0x30..0x39
25
+ LC_ALPHA_RANGE = 0x40..0x5a
26
+ UC_ALPHA_RANGE = 0x60..0x71
27
+
28
+ TRUNCATED = "\x1b[0m…"
29
+
30
+ class << self
31
+ extend T::Sig
32
+
33
+ sig { params(text: String, printing_width: Integer).returns(String) }
34
+ def call(text, printing_width)
35
+ return text if text.size <= printing_width
36
+
37
+ width = 0
38
+ mode = PARSE_ROOT
39
+ truncation_index = T.let(nil, T.nilable(Integer))
40
+
41
+ codepoints = text.codepoints
42
+ codepoints.each.with_index do |cp, index|
43
+ case mode
44
+ when PARSE_ROOT
45
+ case cp
46
+ when ESC # non-printable, followed by some more non-printables.
47
+ mode = PARSE_ESC
48
+ when ZWJ # non-printable, followed by another non-printable.
49
+ mode = PARSE_ZWJ
50
+ else
51
+ width += width(cp)
52
+ if width >= printing_width
53
+ truncation_index ||= index
54
+ # it looks like we could break here but we still want the
55
+ # width calculation for the rest of the characters.
56
+ end
57
+ end
58
+ when PARSE_ESC
59
+ mode = case cp
60
+ when LEFT_SQUARE_BRACKET
61
+ PARSE_ANSI
62
+ else
63
+ PARSE_ROOT
64
+ end
65
+ when PARSE_ANSI
66
+ # ANSI escape codes preeeetty much have the format of:
67
+ # \x1b[0-9;]+[A-Za-z]
68
+ case cp
69
+ when NUMERIC_RANGE, SEMICOLON
70
+ when LC_ALPHA_RANGE, UC_ALPHA_RANGE
71
+ mode = PARSE_ROOT
72
+ else
73
+ # unexpected. let's just go back to the root state I guess?
74
+ mode = PARSE_ROOT
75
+ end
76
+ when PARSE_ZWJ
77
+ # consume any character and consider it as having no width
78
+ # width(x+ZWJ+y) = width(x).
79
+ mode = PARSE_ROOT
80
+ end
81
+ end
82
+
83
+ # Without the `width <= printing_width` check, we truncate
84
+ # "foo\x1b[0m" for a width of 3, but it should not be truncated.
85
+ # It's specifically for the case where we decided "Yes, this is the
86
+ # point at which we'd have to add a truncation!" but it's actually
87
+ # the end of the string.
88
+ return text if !truncation_index || width <= printing_width
89
+
90
+ T.must(codepoints[0...truncation_index]).pack('U*') + TRUNCATED
91
+ end
92
+
93
+ private
94
+
95
+ sig { params(printable_codepoint: Integer).returns(Integer) }
96
+ def width(printable_codepoint)
97
+ case printable_codepoint
98
+ when EMOJI_RANGE
99
+ 2
100
+ else
101
+ 1
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,6 @@
1
+ # typed: true
2
+ module CLI
3
+ module UI
4
+ VERSION = '1.5.1'
5
+ end
6
+ end
@@ -0,0 +1,37 @@
1
+ # typed: true
2
+ require('cli/ui')
3
+
4
+ module CLI
5
+ module UI
6
+ module Widgets
7
+ class Base
8
+ extend T::Sig
9
+ extend T::Helpers
10
+ abstract!
11
+
12
+ sig { params(argstring: String).returns(String) }
13
+ def self.call(argstring)
14
+ new(argstring).render
15
+ end
16
+
17
+ sig { params(argstring: String).void }
18
+ def initialize(argstring)
19
+ pat = self.class.argparse_pattern
20
+ unless (@match_data = pat.match(argstring))
21
+ raise(Widgets::InvalidWidgetArguments.new(argstring, pat))
22
+ end
23
+
24
+ @match_data.names.each do |name|
25
+ instance_variable_set(:"@#{name}", @match_data[name])
26
+ end
27
+ end
28
+
29
+ sig { abstract.returns(Regexp) }
30
+ def self.argparse_pattern; end
31
+
32
+ sig { abstract.returns(String) }
33
+ def render; end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,75 @@
1
+ # typed: true
2
+ # frozen-string-literal: true
3
+
4
+ require('cli/ui')
5
+
6
+ module CLI
7
+ module UI
8
+ module Widgets
9
+ class Status < Widgets::Base
10
+ ARGPARSE_PATTERN = %r{
11
+ \A (?<succeeded> \d+)
12
+ : (?<failed> \d+)
13
+ : (?<working> \d+)
14
+ : (?<pending> \d+) \z
15
+ }x # e.g. "1:23:3:404"
16
+ OPEN = Color::RESET.code + Color::BOLD.code + '[' + Color::RESET.code
17
+ CLOSE = Color::RESET.code + Color::BOLD.code + ']' + Color::RESET.code
18
+ ARROW = Color::RESET.code + Color::GRAY.code + '◂' + Color::RESET.code
19
+ COMMA = Color::RESET.code + Color::GRAY.code + ',' + Color::RESET.code
20
+
21
+ SPINNER_STOPPED = '⠿'
22
+ EMPTY_SET = '∅'
23
+
24
+ sig { override.returns(Regexp) }
25
+ def self.argparse_pattern
26
+ ARGPARSE_PATTERN
27
+ end
28
+
29
+ sig { override.returns(String) }
30
+ def render
31
+ if zero?(@succeeded) && zero?(@failed) && zero?(@working) && zero?(@pending)
32
+ Color::RESET.code + Color::BOLD.code + EMPTY_SET + Color::RESET.code
33
+ else
34
+ # [ 0✓ , 2✗ ◂ 3⠼ ◂ 4⌛︎ ]
35
+ "#{OPEN}#{succeeded_part}#{COMMA}#{failed_part}#{ARROW}#{working_part}#{ARROW}#{pending_part}#{CLOSE}"
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ sig { params(num_str: String).returns(T::Boolean) }
42
+ def zero?(num_str)
43
+ num_str == '0'
44
+ end
45
+
46
+ sig { params(num_str: String, rune: String, color: Color).returns(String) }
47
+ def colorize_if_nonzero(num_str, rune, color)
48
+ color = Color::GRAY if zero?(num_str)
49
+ color.code + num_str + rune
50
+ end
51
+
52
+ sig { returns(String) }
53
+ def succeeded_part
54
+ colorize_if_nonzero(@succeeded, Glyph::CHECK.char, Color::GREEN)
55
+ end
56
+
57
+ sig { returns(String) }
58
+ def failed_part
59
+ colorize_if_nonzero(@failed, Glyph::X.char, Color::RED)
60
+ end
61
+
62
+ sig { returns(String) }
63
+ def working_part
64
+ rune = zero?(@working) ? SPINNER_STOPPED : Spinner.current_rune
65
+ colorize_if_nonzero(@working, rune, Color::BLUE)
66
+ end
67
+
68
+ sig { returns(String) }
69
+ def pending_part
70
+ colorize_if_nonzero(@pending, Glyph::HOURGLASS.char, Color::WHITE)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,91 @@
1
+ # typed: true
2
+ require('cli/ui')
3
+
4
+ module CLI
5
+ module UI
6
+ # Widgets are formatter objects with more custom implementations than the
7
+ # other features, which all center around formatting text with colours,
8
+ # etc.
9
+ #
10
+ # If you want to extend CLI::UI with your own widgets, you may want to do
11
+ # something like this:
12
+ #
13
+ # require('cli/ui')
14
+ # class MyWidget < CLI::UI::Widgets::Base
15
+ # # ...
16
+ # end
17
+ # CLI::UI::Widgets.register('my-widget') { MyWidget }
18
+ # puts(CLI::UI.fmt("{{@widget/my-widget:args}}"))
19
+ module Widgets
20
+ extend T::Sig
21
+
22
+ MAP = {}
23
+
24
+ autoload(:Base, 'cli/ui/widgets/base')
25
+
26
+ sig { params(name: String, cb: T.proc.returns(T.class_of(Widgets::Base))).void }
27
+ def self.register(name, &cb)
28
+ MAP[name] = cb
29
+ end
30
+
31
+ autoload(:Status, 'cli/ui/widgets/status')
32
+ register('status') { Widgets::Status }
33
+
34
+ # Looks up a widget by handle
35
+ #
36
+ # ==== Raises
37
+ # Raises InvalidWidgetHandle if the widget is not available.
38
+ #
39
+ # ==== Returns
40
+ # A callable widget, to be invoked like `.call(argstring)`
41
+ #
42
+ sig { params(handle: String).returns(T.class_of(Widgets::Base)) }
43
+ def self.lookup(handle)
44
+ MAP.fetch(handle).call
45
+ rescue KeyError, NameError
46
+ raise(InvalidWidgetHandle, handle)
47
+ end
48
+
49
+ # All available widgets by name
50
+ #
51
+ sig { returns(T::Array[String]) }
52
+ def self.available
53
+ MAP.keys
54
+ end
55
+
56
+ class InvalidWidgetHandle < ArgumentError
57
+ extend T::Sig
58
+
59
+ sig { params(handle: String).void }
60
+ def initialize(handle)
61
+ super
62
+ @handle = handle
63
+ end
64
+
65
+ sig { returns(String) }
66
+ def message
67
+ keys = Widgets.available.join(',')
68
+ "invalid widget handle: #{@handle} " \
69
+ "-- must be one of CLI::UI::Widgets.available (#{keys})"
70
+ end
71
+ end
72
+
73
+ class InvalidWidgetArguments < ArgumentError
74
+ extend T::Sig
75
+
76
+ sig { params(argstring: String, pattern: Regexp).void }
77
+ def initialize(argstring, pattern)
78
+ super
79
+ @argstring = argstring
80
+ @pattern = pattern
81
+ end
82
+
83
+ sig { returns(String) }
84
+ def message
85
+ "invalid widget arguments: #{@argstring} " \
86
+ "-- must match pattern: #{@pattern.inspect}"
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,63 @@
1
+ # coding: utf-8
2
+
3
+ # typed: true
4
+
5
+ require 'cli/ui'
6
+ require 'cli/ui/frame/frame_stack'
7
+ require 'cli/ui/frame/frame_style'
8
+
9
+ module CLI
10
+ module UI
11
+ class Wrap
12
+ extend T::Sig
13
+
14
+ sig { params(input: String).void }
15
+ def initialize(input)
16
+ @input = input
17
+ end
18
+
19
+ sig { returns(String) }
20
+ def wrap
21
+ max_width = Terminal.width - Frame.prefix_width
22
+ width = T.let(0, Integer)
23
+ final = []
24
+ # Create an alternation of format codes of parameter lengths 1-20, since + and {1,n} not allowed in lookbehind
25
+ format_codes = (1..20).map { |n| /\x1b\[[\d;]{#{n}}m/ }.join('|')
26
+ codes = ''
27
+ @input.split(/(?=\s|\x1b\[[\d;]+m|\r)|(?<=\s|#{format_codes})/).each do |token|
28
+ case token
29
+ when '\x1B[0?m'
30
+ codes = ''
31
+ final << token
32
+ when /\x1b\[[\d;]+m/
33
+ codes += token # Track in use format codes so that they are resent after frame coloring
34
+ final << token
35
+ when "\n"
36
+ final << "\n#{codes}"
37
+ width = 0
38
+ when /\s/
39
+ token_width = ANSI.printing_width(token)
40
+ if width + token_width <= max_width
41
+ final << token
42
+ width += token_width
43
+ else
44
+ final << "\n#{codes}"
45
+ width = 0
46
+ end
47
+ else
48
+ token_width = ANSI.printing_width(token)
49
+ if width + token_width <= max_width
50
+ final << token
51
+ width += token_width
52
+ else
53
+ final << "\n#{codes}"
54
+ final << token
55
+ width = token_width
56
+ end
57
+ end
58
+ end
59
+ final.join
60
+ end
61
+ end
62
+ end
63
+ end