Module: Kino::CLI

Defined in:
lib/kino/cli.rb

Overview

The kino executable (CLI.start) plus startup presentation shared with Server.run: the banner, ANSI styling, and the exit credit. Nothing here is part of the serving API. (The native layer has a twin of paint in style.rs for the few places Rust writes to the terminal.)

This file deliberately loads no native code: require "kino" happens inside the actions that need it, so --help and --version stay instant.

Constant Summary collapse

MOTD =

The plain banner art: "Kino" in TheDraw's Mindbenders font; motd adds the original three-tone shading.

<<~BANNER
  ggg    .o
  $$$_,o$$P aaa $$$eea,.   .,aaa,.
  %$$`4eP'  $$$ $$$``$$$% $$$```$$$
  $$$--`$$o ggg $$$---$$$ $$$---$$$
  $$$ ░ $$$ $$$ $$$ ░ $$$ $$$ ░ $$$
  $$$---$$$ $$$ $$$---$$$ $$$---$$$
  $$$   $$$ $$' $$$   $$$ ^$$aaaS$'
BANNER

Class Method Summary collapse

Class Method Details

.action!(server) ⇒ void

This method returns an undefined value.

The ready block: what this process is (Ruby build with its JIT and parser flags, environment, topology, pid) and where it listens.

Parameters:



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/kino/cli.rb', line 126

def action!(server)
  stats = server.stats
  puts dim("- ruby:      #{RUBY_DESCRIPTION}")
  puts dim("- env:       #{ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"}")
  puts dim("- mode:      #{server.mode}, #{count(stats[:workers], "worker")} × #{count(stats[:threads], "thread")}")
  puts dim("- pid:       #{Process.pid}")
  puts dim("- listening: #{server.url}")
  puts dim("- control:   #{server.control_url}") if server.control_url
  puts dim("- Ctrl-C to drain and stop")
  puts "\n#{bold("Action!")}\n\n"
end

.bold(text, io: $stdout) ⇒ String

Bold styling for headings and the Action!/Fin. bookends.

Returns:

  • (String)


72
73
74
# File 'lib/kino/cli.rb', line 72

def bold(text, io: $stdout)
  paint("1", text, io: io)
end

.color?(io = $stdout) ⇒ Boolean

True when output to io may use ANSI styling.

Parameters:

  • io (IO) (defaults to: $stdout)

Returns:

  • (Boolean)


49
50
51
# File 'lib/kino/cli.rb', line 49

def color?(io = $stdout)
  io.tty? && ENV["NO_COLOR"].nil? && ENV["TERM"] != "dumb"
end

.dim(text, io: $stdout) ⇒ String

Startup-output styling, same gray family as the banner.

Returns:

  • (String)


66
67
68
# File 'lib/kino/cli.rb', line 66

def dim(text, io: $stdout)
  paint("38;5;243", text, io: io)
end

.fin_at_exitvoid

This method returns an undefined value.

Roll credits when the process ends: normal exit or crash (at_exit also runs after an uncaught exception; only a force-exit skips it).



146
147
148
149
150
151
# File 'lib/kino/cli.rb', line 146

def fin_at_exit
  return if @fin_registered

  @fin_registered = true
  at_exit { $stdout.puts bold("\nFin.\n") }
end

.motd(color: color?) ) ⇒ String

The banner with its three-tone shading applied per character.

Parameters:

  • color (Boolean) (defaults to: color?) )

Returns:

  • (String)


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/kino/cli.rb', line 85

def motd(color: color?)
  return MOTD unless color

  MOTD.lines.zip(MOTD_TONES.lines).map do |art, tones|
    current = nil
    line = art.chomp.each_char.with_index.map { |char, i|
      sgr = TONE_SGR[tones.to_s[i]]
      if char != " " && sgr && sgr != current
        current = sgr
        "\e[#{sgr}m#{char}"
      else
        char
      end
    }.join
    "#{line}\e[0m\n"
  end.join
end

.opening_creditsvoid

This method returns an undefined value.

The two banner halves around Server#start: credits before, the ready block plus a bold "Action!" after, once mode and port are known. Server.run is the one caller; the kino CLI funnels into it.



117
118
119
120
# File 'lib/kino/cli.rb', line 117

def opening_credits
  puts motd
  puts dim("\nKino #{VERSION} presents:")
end

.paint(code, text, io: $stdout) ⇒ String

Wrap text in an SGR code ("1" bold, "31" red, "38;5;N" 256-color), resetting at the end; plain when io is not a color terminal.

Parameters:

  • code (String)

    an SGR code

  • text (String)
  • io (IO) (defaults to: $stdout)

    the stream the text is destined for (gates coloring)

Returns:

  • (String)


60
61
62
# File 'lib/kino/cli.rb', line 60

def paint(code, text, io: $stdout)
  color?(io) ? "\e[#{code}m#{text}\e[0m" : text
end

.red(text, io: $stderr) ⇒ String

Errors are red (gated on stderr unless another io is given).

Returns:

  • (String)


78
79
80
# File 'lib/kino/cli.rb', line 78

def red(text, io: $stderr)
  paint("91", text, io: io)
end

.start(argv) ⇒ Integer

The kino executable: parse flags, then init/check/serve. Returns the process exit status (exe/kino passes it to Kernel#exit), except for -v and -h, which print and exit in place per optparse convention.

Parameters:

  • argv (Array<String>)

    command-line arguments (consumed)

Returns:

  • (Integer)

    process exit status



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/kino/cli.rb', line 159

def start(argv)
  options = {overrides: {}}
  parser = option_parser(options)
  parser.parse!(argv)

  return write_sample(options[:init_path]) if options[:init_path]

  config = resolve_config(options)

  # Precedence for the rackup file: positional arg > `rackup` in config > config.ru
  rackup_file = argv.first || config[:rackup] || "config.ru"
  unless File.exist?(rackup_file)
    warn red("Kino: #{rackup_file} not found")
    puts
    print_help(parser)
    return 1
  end

  ENV["RACK_ENV"] ||= config[:environment] if config[:environment]

  app = Rack::Builder.parse_file(rackup_file)
  app = app.first if app.is_a?(Array) # rack < 3 compat

  return Check.print_report(app) ? 0 : 1 if options[:check]

  serve(app, config)
  0
end

.stats_line(stats) ⇒ String

One-line stats dump (what the SIGUSR1 handler logs). Excludes worker_status: it's an array with one entry per execution slot, and printing it inline would break the one-line contract (see /stats for per-worker detail).

Parameters:

Returns:

  • (String)


109
110
111
# File 'lib/kino/cli.rb', line 109

def stats_line(stats)
  "stats #{stats.except(:worker_status).map { |k, v| "#{k}=#{v.inspect}" }.join(" ")}"
end