wibrfake-brk 0.0.2
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 +7 -0
- data/.dockerignore +48 -0
- data/.gitattributes +19 -0
- data/.github/dependabot.yml +12 -0
- data/.github/workflows/ci.yml +90 -0
- data/.rubocop.yml +8 -0
- data/.ruby-version +1 -0
- data/Dockerfile +69 -0
- data/Gemfile +68 -0
- data/README.md +188 -0
- data/Rakefile +6 -0
- data/app/assets/config/manifest.js +5 -0
- data/app/assets/images/.keep +0 -0
- data/app/assets/images/2024-12-30-07:58:34-screenshot.png +0 -0
- data/app/assets/images/facebook/facebook-logo.svg +3 -0
- data/app/assets/images/google/googlelogo_color_272x92dp.png +0 -0
- data/app/assets/images/instagram/app-store.png +0 -0
- data/app/assets/images/instagram/google-play.png +0 -0
- data/app/assets/images/instagram/instagram-logo.png +0 -0
- data/app/assets/images/instagram/screenshot1.png +0 -0
- data/app/assets/images/instagram/screenshot2.png +0 -0
- data/app/assets/images/instagram/screenshot3.png +0 -0
- data/app/assets/images/instagram/screenshot4.png +0 -0
- data/app/assets/images/nauta_etecsa/CUBADEBATE.jpg +0 -0
- data/app/assets/images/nauta_etecsa/CUBAEDUCA.jpg +0 -0
- data/app/assets/images/nauta_etecsa/CUBARTE.jpg +0 -0
- data/app/assets/images/nauta_etecsa/CUBASI.jpg +0 -0
- data/app/assets/images/nauta_etecsa/ECURED.jpg +0 -0
- data/app/assets/images/nauta_etecsa/OFERTAS.jpg +0 -0
- data/app/assets/images/nauta_etecsa/PAPELETA.jpg +0 -0
- data/app/assets/images/nauta_etecsa/contenedor_bg.jpg +0 -0
- data/app/assets/images/nauta_etecsa/etecsa.jpg +0 -0
- data/app/assets/images/nauta_etecsa/nauta_wifi.jpg +0 -0
- data/app/assets/images/nauta_hogar/CUBADEBATE.jpg +0 -0
- data/app/assets/images/nauta_hogar/CUBAEDUCA.jpg +0 -0
- data/app/assets/images/nauta_hogar/CUBARTE.jpg +0 -0
- data/app/assets/images/nauta_hogar/CUBASI.jpg +0 -0
- data/app/assets/images/nauta_hogar/ECURED.jpg +0 -0
- data/app/assets/images/nauta_hogar/OFERTAS.jpg +0 -0
- data/app/assets/images/nauta_hogar/PAPELETA.jpg +0 -0
- data/app/assets/images/nauta_hogar/banner_hogar.png +0 -0
- data/app/assets/images/wibrfake/Breaker-Wallpaper1.jpg +0 -0
- data/app/assets/images/wibrfake/mac-list-parameter.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-100x100.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-32x32.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-48x48.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-apfake-on.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-apfake-set-basic.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-apfake-show.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-help.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-hostpot.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-init.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-init2.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-login-basic.jpg +0 -0
- data/app/assets/images/wibrfake/wibrfake-mac-list.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-mac-ramset-oui.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-mac-ramset.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-mac-set.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-mac-show.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-need-signin.jpg +0 -0
- data/app/assets/images/wibrfake/wibrfake-session-active.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-session-init.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-session-list.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-session-new.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-session-rename.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-session-save.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-web-server-apfake.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-web-server-on.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-web-server-show.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-wkdump-loaded.png +0 -0
- data/app/assets/images/wibrfake/wibrfake-wkdump.png +0 -0
- data/app/assets/images/wibrfake/wibrfake.png +0 -0
- data/app/assets/stylesheets/application.css +15 -0
- data/app/assets/stylesheets/basic/page_basic.css +0 -0
- data/app/assets/stylesheets/facebook/page_facebook.css +93 -0
- data/app/assets/stylesheets/google/page_google.css +137 -0
- data/app/assets/stylesheets/instagram/page_instagram.css +94 -0
- data/app/assets/stylesheets/nauta_etecsa/page_nauta.css +325 -0
- data/app/assets/stylesheets/nauta_hogar/page_nauta.css +469 -0
- data/app/assets/stylesheets/wibrfake/page_wibrfake.css +1181 -0
- data/app/assets/stylesheets/wibrfake/page_wibrfake_about.css +486 -0
- data/app/channels/application_cable/channel.rb +4 -0
- data/app/channels/application_cable/connection.rb +4 -0
- data/app/controllers/application_controller.rb +15 -0
- data/app/controllers/concerns/.keep +0 -0
- data/app/controllers/sessions_controller.rb +111 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/helpers/sessions_helper.rb +2 -0
- data/app/javascript/application.js +3 -0
- data/app/javascript/bootstrap.bundle.min.js +7 -0
- data/app/javascript/controllers/application.js +9 -0
- data/app/javascript/controllers/hello_controller.js +7 -0
- data/app/javascript/controllers/index.js +4 -0
- data/app/javascript/google/page_google.js +50 -0
- data/app/javascript/incorrect3.js +29 -0
- data/app/javascript/page_wibrfake.js +50 -0
- data/app/javascript/page_wibrfake_about.js +147 -0
- data/app/jobs/application_job.rb +7 -0
- data/app/mailers/application_mailer.rb +4 -0
- data/app/models/application_record.rb +3 -0
- data/app/models/concerns/.keep +0 -0
- data/app/models/user.rb +6 -0
- data/app/views/layouts/application.html.erb +33 -0
- data/app/views/layouts/mailer.html.erb +13 -0
- data/app/views/layouts/mailer.text.erb +1 -0
- data/app/views/pwa/manifest.json.erb +22 -0
- data/app/views/pwa/service-worker.js +26 -0
- data/app/views/sessions/basic/basic.html.erb +21 -0
- data/app/views/sessions/facebook/facebook.html.erb +132 -0
- data/app/views/sessions/google/google.html.erb +72 -0
- data/app/views/sessions/index.html.erb +11 -0
- data/app/views/sessions/instagram/instagram.html.erb +69 -0
- data/app/views/sessions/nauta_etecsa/nauta_etecsa.html.erb +97 -0
- data/app/views/sessions/nauta_hogar/nauta_hogar.html.erb +135 -0
- data/app/views/sessions/wibrfake/about/about.html.erb +164 -0
- data/app/views/sessions/wibrfake/wibrfake.html.erb +417 -0
- data/bin/.names.rb.swp +0 -0
- data/bin/brakeman +7 -0
- data/bin/bundle +114 -0
- data/bin/docker-entrypoint +13 -0
- data/bin/google.wkdump +3 -0
- data/bin/importmap +4 -0
- data/bin/rails +4 -0
- data/bin/rake +4 -0
- data/bin/rubocop +8 -0
- data/bin/setup +37 -0
- data/bin/wibrfake +20 -0
- data/config/application.rb +26 -0
- data/config/boot.rb +4 -0
- data/config/cable.yml +10 -0
- data/config/credentials.yml.enc +1 -0
- data/config/database.yml +16 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +85 -0
- data/config/environments/production.rb +105 -0
- data/config/environments/test.rb +67 -0
- data/config/icons/application-x-wkdump.xml +13 -0
- data/config/icons/hicolor/16x16/mimetypes/application-x-wkdump.png +0 -0
- data/config/icons/hicolor/32x32/mimetypes/application-x-wkdump.png +0 -0
- data/config/icons/hicolor/48x48/mimetypes/application-x-wkdump.png +0 -0
- data/config/icons/hicolor/icon-theme.cache +0 -0
- data/config/icons/hicolor/index.theme +20 -0
- data/config/icons/wkdump.desktop +5 -0
- data/config/importmap.rb +7 -0
- data/config/initializers/assets.rb +12 -0
- data/config/initializers/content_security_policy.rb +25 -0
- data/config/initializers/devise.rb +313 -0
- data/config/initializers/filter_parameter_logging.rb +8 -0
- data/config/initializers/inflections.rb +16 -0
- data/config/initializers/permissions_policy.rb +13 -0
- data/config/locales/devise.en.yml +65 -0
- data/config/locales/en.yml +31 -0
- data/config/puma.rb +35 -0
- data/config/routes.rb +12 -0
- data/config/storage.yml +34 -0
- data/config.ru +6 -0
- data/db/development.sqlite3 +0 -0
- data/db/schema.rb +14 -0
- data/ext/wibrfake/extconf.rb +3 -0
- data/ext/wibrfake/wibrfake.h +0 -0
- data/images/ouilist.png +0 -0
- data/images/wibrfakehelp.png +0 -0
- data/images/wibrfakeinit.png +0 -0
- data/lib/assets/.keep +0 -0
- data/lib/tasks/.keep +0 -0
- data/lib/wibrfake/Config/ipaddr.rb +71 -0
- data/lib/wibrfake/Config/options.rb +98 -0
- data/lib/wibrfake/Config/security_wpa.rb +43 -0
- data/lib/wibrfake/DataBase/howuis.db +0 -0
- data/lib/wibrfake/DataBase/ouis.db +0 -0
- data/lib/wibrfake/DataBase/ouis.rb +24 -0
- data/lib/wibrfake/DataBase/rename.rb +15 -0
- data/lib/wibrfake/DataBase/show.rb +18 -0
- data/lib/wibrfake/Dnsmasq/dhcp.rb +149 -0
- data/lib/wibrfake/Dnsmasq/dhcp_prueba.rb +135 -0
- data/lib/wibrfake/Dnsmasq/dns.rb +34 -0
- data/lib/wibrfake/Dnsmasq/dns_prueba.rb +36 -0
- data/lib/wibrfake/Dnsmasq/dnsmasq.rb +33 -0
- data/lib/wibrfake/Listing/apfake.rb +28 -0
- data/lib/wibrfake/Listing/clients.rb +76 -0
- data/lib/wibrfake/Listing/ouis.rb +53 -0
- data/lib/wibrfake/Listing/process.rb +14 -0
- data/lib/wibrfake/Listing/sessions.rb +47 -0
- data/lib/wibrfake/Listing/web_server.rb +17 -0
- data/lib/wibrfake/Listing/wireless.rb +30 -0
- data/lib/wibrfake/Logs/credentials.log +59 -0
- data/lib/wibrfake/NetworkInterface/ip-routing.rb +99 -0
- data/lib/wibrfake/NetworkInterface/mac.rb +114 -0
- data/lib/wibrfake/NetworkInterface/monitor.rb +44 -0
- data/lib/wibrfake/NetworkInterface/ouis.db +0 -0
- data/lib/wibrfake/Process/id.rb +11 -0
- data/lib/wibrfake/Process/pids.log +5 -0
- data/lib/wibrfake/Process/pkill.rb +124 -0
- data/lib/wibrfake/Process/processes.rb +76 -0
- data/lib/wibrfake/Rails/login.rb +20 -0
- data/lib/wibrfake/Rails/routes.rb +76 -0
- data/lib/wibrfake/Rails/service.rb +58 -0
- data/lib/wibrfake/Rails/service_usage.rb +46 -0
- data/lib/wibrfake/Sessions/session.rb +188 -0
- data/lib/wibrfake/String/string.rb +109 -0
- data/lib/wibrfake/Tmp/config.yml +1 -0
- data/lib/wibrfake/config.rb +24 -0
- data/lib/wibrfake/help.rb +107 -0
- data/lib/wibrfake/optionpr.rb +68 -0
- data/lib/wibrfake/run.rb +9 -0
- data/lib/wibrfake/ty/apld.rb +110 -0
- data/lib/wibrfake/ty/prompt/answers_collector.rb +78 -0
- data/lib/wibrfake/ty/prompt/block_paginator.rb +59 -0
- data/lib/wibrfake/ty/prompt/choice.rb +147 -0
- data/lib/wibrfake/ty/prompt/choices.rb +129 -0
- data/lib/wibrfake/ty/prompt/confirm_question.rb +158 -0
- data/lib/wibrfake/ty/prompt/const.rb +17 -0
- data/lib/wibrfake/ty/prompt/converter_dsl.rb +21 -0
- data/lib/wibrfake/ty/prompt/converter_registry.rb +69 -0
- data/lib/wibrfake/ty/prompt/converters.rb +182 -0
- data/lib/wibrfake/ty/prompt/distance.rb +49 -0
- data/lib/wibrfake/ty/prompt/enum_list.rb +433 -0
- data/lib/wibrfake/ty/prompt/errors.rb +31 -0
- data/lib/wibrfake/ty/prompt/evaluator.rb +29 -0
- data/lib/wibrfake/ty/prompt/expander.rb +321 -0
- data/lib/wibrfake/ty/prompt/keypress.rb +98 -0
- data/lib/wibrfake/ty/prompt/list.rb +589 -0
- data/lib/wibrfake/ty/prompt/mask_question.rb +96 -0
- data/lib/wibrfake/ty/prompt/multi_list.rb +224 -0
- data/lib/wibrfake/ty/prompt/multiline.rb +72 -0
- data/lib/wibrfake/ty/prompt/paginator.rb +111 -0
- data/lib/wibrfake/ty/prompt/question/checks.rb +105 -0
- data/lib/wibrfake/ty/prompt/question/modifier.rb +96 -0
- data/lib/wibrfake/ty/prompt/question/validation.rb +72 -0
- data/lib/wibrfake/ty/prompt/question.rb +391 -0
- data/lib/wibrfake/ty/prompt/result.rb +42 -0
- data/lib/wibrfake/ty/prompt/selected_choices.rb +77 -0
- data/lib/wibrfake/ty/prompt/slider.rb +286 -0
- data/lib/wibrfake/ty/prompt/statement.rb +55 -0
- data/lib/wibrfake/ty/prompt/suggestion.rb +113 -0
- data/lib/wibrfake/ty/prompt/symbols.rb +89 -0
- data/lib/wibrfake/ty/prompt/test.rb +36 -0
- data/lib/wibrfake/ty/prompt/timer.rb +75 -0
- data/lib/wibrfake/ty/prompt/utils.rb +42 -0
- data/lib/wibrfake/ty/prompt/version.rb +7 -0
- data/lib/wibrfake/ty/prompt.rb +589 -0
- data/lib/wibrfake/version.rb +5 -0
- data/lib/wibrfake/wibrfake_cli.rb +683 -0
- data/log/.keep +0 -0
- data/pruebas/daemon1.pid +1 -0
- data/pruebas/daemon2.pid +1 -0
- data/pruebas/daemons.rb +44 -0
- data/public/404.html +67 -0
- data/public/406-unsupported-browser.html +66 -0
- data/public/422.html +67 -0
- data/public/500.html +66 -0
- data/public/icon.png +0 -0
- data/public/icon.svg +3 -0
- data/public/robots.txt +1 -0
- data/storage/.keep +0 -0
- data/test/application_system_test_case.rb +5 -0
- data/test/channels/application_cable/connection_test.rb +13 -0
- data/test/controllers/.keep +0 -0
- data/test/controllers/sessions_controller_test.rb +7 -0
- data/test/fixtures/files/.keep +0 -0
- data/test/fixtures/users.yml +11 -0
- data/test/helpers/.keep +0 -0
- data/test/integration/.keep +0 -0
- data/test/mailers/.keep +0 -0
- data/test/models/.keep +0 -0
- data/test/models/user_test.rb +7 -0
- data/test/system/.keep +0 -0
- data/test/test_helper.rb +15 -0
- data/tmp/.keep +0 -0
- data/tmp/pids/.keep +0 -0
- data/tmp/storage/.keep +0 -0
- data/vendor/.keep +0 -0
- data/vendor/javascript/.keep +0 -0
- data/wibrfake.gemspec +49 -0
- metadata +743 -0
@@ -0,0 +1,286 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
# A class responsible for shell prompt interactions.
|
5
|
+
class Prompt
|
6
|
+
# A class responsible for gathering numeric input from range
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Slider
|
10
|
+
HELP = "(Use %s arrow keys, press Enter to select)"
|
11
|
+
|
12
|
+
FORMAT = ":slider %s"
|
13
|
+
|
14
|
+
# Initailize a Slider
|
15
|
+
#
|
16
|
+
# @param [Prompt] prompt
|
17
|
+
# the prompt
|
18
|
+
# @param [Hash] options
|
19
|
+
# the options to configure this slider
|
20
|
+
# @option options [Integer] :min The minimum value
|
21
|
+
# @option options [Integer] :max The maximum value
|
22
|
+
# @option options [Integer] :step The step value
|
23
|
+
# @option options [String] :format The display format
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def initialize(prompt, **options)
|
27
|
+
@prompt = prompt
|
28
|
+
@prefix = options.fetch(:prefix) { @prompt.prefix }
|
29
|
+
@choices = Choices.new
|
30
|
+
@min = options.fetch(:min, 0)
|
31
|
+
@max = options.fetch(:max, 10)
|
32
|
+
@step = options.fetch(:step, 1)
|
33
|
+
@default = options[:default]
|
34
|
+
@active_color = options.fetch(:active_color) { @prompt.active_color }
|
35
|
+
@help_color = options.fetch(:help_color) { @prompt.help_color }
|
36
|
+
@format = options.fetch(:format) { FORMAT }
|
37
|
+
@quiet = options.fetch(:quiet) { @prompt.quiet }
|
38
|
+
@help = options[:help]
|
39
|
+
@show_help = options.fetch(:show_help) { :start }
|
40
|
+
@symbols = @prompt.symbols.merge(options.fetch(:symbols, {}))
|
41
|
+
@first_render = true
|
42
|
+
@done = false
|
43
|
+
end
|
44
|
+
|
45
|
+
# Change symbols used by this prompt
|
46
|
+
#
|
47
|
+
# @param [Hash] new_symbols
|
48
|
+
# the new symbols to use
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def symbols(new_symbols = (not_set = true))
|
52
|
+
return @symbols if not_set
|
53
|
+
|
54
|
+
@symbols.merge!(new_symbols)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Setup initial active position
|
58
|
+
#
|
59
|
+
# @return [Integer]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def initial
|
63
|
+
if @default.nil?
|
64
|
+
# no default - choose the middle option
|
65
|
+
choices.size / 2
|
66
|
+
elsif default_choice = choices.find_by(:name, @default)
|
67
|
+
# found a Choice by name - use it
|
68
|
+
choices.index(default_choice)
|
69
|
+
else
|
70
|
+
# default is the index number
|
71
|
+
@default - 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Default help text
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
def default_help
|
79
|
+
arrows = @symbols[:arrow_left] + "/" + @symbols[:arrow_right]
|
80
|
+
sprintf(HELP, arrows)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Set help text
|
84
|
+
#
|
85
|
+
# @param [String] text
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def help(text = (not_set = true))
|
89
|
+
return @help if !@help.nil? && not_set
|
90
|
+
|
91
|
+
@help = (@help.nil? && not_set) ? default_help : text
|
92
|
+
end
|
93
|
+
|
94
|
+
# Change when help is displayed
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def show_help(value = (not_set = true))
|
98
|
+
return @show_ehlp if not_set
|
99
|
+
|
100
|
+
@show_help = value
|
101
|
+
end
|
102
|
+
|
103
|
+
# @api public
|
104
|
+
def default(value)
|
105
|
+
@default = value
|
106
|
+
end
|
107
|
+
|
108
|
+
# @api public
|
109
|
+
def min(value)
|
110
|
+
@min = value
|
111
|
+
end
|
112
|
+
|
113
|
+
# @api public
|
114
|
+
def max(value)
|
115
|
+
@max = value
|
116
|
+
end
|
117
|
+
|
118
|
+
# @api public
|
119
|
+
def step(value)
|
120
|
+
@step = value
|
121
|
+
end
|
122
|
+
|
123
|
+
# Add a single choice
|
124
|
+
#
|
125
|
+
# @api public
|
126
|
+
def choice(*value, &block)
|
127
|
+
if block
|
128
|
+
@choices << (value << block)
|
129
|
+
else
|
130
|
+
@choices << value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Add multiple choices
|
135
|
+
#
|
136
|
+
# @param [Array[Object]] values
|
137
|
+
# the values to add as choices
|
138
|
+
#
|
139
|
+
# @api public
|
140
|
+
def choices(values = (not_set = true))
|
141
|
+
if not_set
|
142
|
+
@choices
|
143
|
+
else
|
144
|
+
values.each { |val| @choices << val }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @api public
|
149
|
+
def format(value)
|
150
|
+
@format = value
|
151
|
+
end
|
152
|
+
|
153
|
+
# Set quiet mode.
|
154
|
+
#
|
155
|
+
# @api public
|
156
|
+
def quiet(value)
|
157
|
+
@quiet = value
|
158
|
+
end
|
159
|
+
|
160
|
+
# Call the slider by passing question
|
161
|
+
#
|
162
|
+
# @param [String] question
|
163
|
+
# the question to ask
|
164
|
+
#
|
165
|
+
# @apu public
|
166
|
+
def call(question, possibilities = nil, &block)
|
167
|
+
@question = question
|
168
|
+
choices(possibilities) if possibilities
|
169
|
+
block.call(self) if block
|
170
|
+
# set up a Choices collection for min, max, step
|
171
|
+
# if no possibilities were supplied
|
172
|
+
choices((@min..@max).step(@step).to_a) if @choices.empty?
|
173
|
+
|
174
|
+
@active = initial
|
175
|
+
@prompt.subscribe(self) do
|
176
|
+
render
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def keyleft(*)
|
181
|
+
@active -= 1 if @active > 0
|
182
|
+
end
|
183
|
+
alias keydown keyleft
|
184
|
+
|
185
|
+
def keyright(*)
|
186
|
+
@active += 1 if (@active + 1) < choices.size
|
187
|
+
end
|
188
|
+
alias keyup keyright
|
189
|
+
|
190
|
+
def keyreturn(*)
|
191
|
+
@done = true
|
192
|
+
end
|
193
|
+
alias keyspace keyreturn
|
194
|
+
alias keyenter keyreturn
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
# Check if help is shown only on start
|
199
|
+
#
|
200
|
+
# @api private
|
201
|
+
def help_start?
|
202
|
+
@show_help =~ /start/i
|
203
|
+
end
|
204
|
+
|
205
|
+
# Check if help is always displayed
|
206
|
+
#
|
207
|
+
# @api private
|
208
|
+
def help_always?
|
209
|
+
@show_help =~ /always/i
|
210
|
+
end
|
211
|
+
|
212
|
+
# Render an interactive range slider.
|
213
|
+
#
|
214
|
+
# @api private
|
215
|
+
def render
|
216
|
+
@prompt.print(@prompt.hide)
|
217
|
+
until @done
|
218
|
+
question = render_question
|
219
|
+
@prompt.print(question)
|
220
|
+
@prompt.read_keypress
|
221
|
+
refresh(question.lines.count)
|
222
|
+
end
|
223
|
+
@prompt.print(render_question) unless @quiet
|
224
|
+
answer
|
225
|
+
ensure
|
226
|
+
@prompt.print(@prompt.show)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Clear screen
|
230
|
+
#
|
231
|
+
# @param [Integer] lines
|
232
|
+
# the lines to clear
|
233
|
+
#
|
234
|
+
# @api private
|
235
|
+
def refresh(lines)
|
236
|
+
@prompt.print(@prompt.clear_lines(lines))
|
237
|
+
end
|
238
|
+
|
239
|
+
# @return [Integer, String]
|
240
|
+
#
|
241
|
+
# @api private
|
242
|
+
def answer
|
243
|
+
choices[@active].value
|
244
|
+
end
|
245
|
+
|
246
|
+
# Render question with the slider
|
247
|
+
#
|
248
|
+
# @return [String]
|
249
|
+
#
|
250
|
+
# @api private
|
251
|
+
def render_question
|
252
|
+
header = ["#{@prefix}#{@question} "]
|
253
|
+
if @done
|
254
|
+
header << @prompt.decorate(choices[@active].to_s, @active_color)
|
255
|
+
header << "\n"
|
256
|
+
else
|
257
|
+
header << render_slider
|
258
|
+
end
|
259
|
+
if @first_render && (help_start? || help_always?) ||
|
260
|
+
(help_always? && !@done)
|
261
|
+
header << "\n" + @prompt.decorate(help, @help_color)
|
262
|
+
@first_render = false
|
263
|
+
end
|
264
|
+
header.join
|
265
|
+
end
|
266
|
+
|
267
|
+
# Render slider representation
|
268
|
+
#
|
269
|
+
# @return [String]
|
270
|
+
#
|
271
|
+
# @api private
|
272
|
+
def render_slider
|
273
|
+
slider = (@symbols[:line] * @active) +
|
274
|
+
@prompt.decorate(@symbols[:bullet], @active_color) +
|
275
|
+
(@symbols[:line] * (choices.size - @active - 1))
|
276
|
+
value = choices[@active].name
|
277
|
+
case @format
|
278
|
+
when Proc
|
279
|
+
@format.call(slider, value)
|
280
|
+
else
|
281
|
+
@format.gsub(":slider", slider) % [value]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end # Slider
|
285
|
+
end # Prompt
|
286
|
+
end # TTY
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
# A class responsible for shell prompt interactions.
|
5
|
+
class Prompt
|
6
|
+
# A class representing a statement output to prompt.
|
7
|
+
class Statement
|
8
|
+
# Flag to display newline
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
attr_reader :newline
|
12
|
+
|
13
|
+
# Color used to display statement
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
attr_reader :color
|
17
|
+
|
18
|
+
# Initialize a Statement
|
19
|
+
#
|
20
|
+
# @param [TTY::Prompt] prompt
|
21
|
+
#
|
22
|
+
# @param [Hash] options
|
23
|
+
#
|
24
|
+
# @option options [Symbol] :newline
|
25
|
+
# force a newline break after the message
|
26
|
+
#
|
27
|
+
# @option options [Symbol] :color
|
28
|
+
# change the message display to color
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def initialize(prompt, newline: true, color: false)
|
32
|
+
@prompt = prompt
|
33
|
+
@newline = newline
|
34
|
+
@color = color
|
35
|
+
end
|
36
|
+
|
37
|
+
# Output the message to the prompt
|
38
|
+
#
|
39
|
+
# @param [String] message
|
40
|
+
# the message to be printed to stdout
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def call(message)
|
44
|
+
message = @prompt.decorate(message, *color) if color
|
45
|
+
|
46
|
+
if newline && /( |\t)(\e\[\d+(;\d+)*m)?\Z/ !~ message
|
47
|
+
@prompt.puts message
|
48
|
+
else
|
49
|
+
@prompt.print message
|
50
|
+
@prompt.flush
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end # Statement
|
54
|
+
end # Prompt
|
55
|
+
end # TTY
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "distance"
|
4
|
+
|
5
|
+
module TTY
|
6
|
+
# A class responsible for terminal prompt interactions.
|
7
|
+
class Prompt
|
8
|
+
# A class representing a suggestion out of possible choices
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class Suggestion
|
12
|
+
DEFAULT_INDENT = 8
|
13
|
+
|
14
|
+
SINGLE_TEXT = "Did you mean this?"
|
15
|
+
|
16
|
+
PLURAL_TEXT = "Did you mean one of these?"
|
17
|
+
|
18
|
+
# Number of spaces
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
attr_reader :indent
|
22
|
+
|
23
|
+
# Text for a single suggestion
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
attr_reader :single_text
|
27
|
+
|
28
|
+
# Text for multiple suggestions
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
attr_reader :plural_text
|
32
|
+
|
33
|
+
# Initialize a Suggestion
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def initialize(**options)
|
37
|
+
@indent = options.fetch(:indent) { DEFAULT_INDENT }
|
38
|
+
@single_text = options.fetch(:single_text) { SINGLE_TEXT }
|
39
|
+
@plural_text = options.fetch(:plural_text) { PLURAL_TEXT }
|
40
|
+
@suggestions = []
|
41
|
+
@comparator = Distance.new
|
42
|
+
end
|
43
|
+
|
44
|
+
# Suggest matches out of possibile strings
|
45
|
+
#
|
46
|
+
# @param [String] message
|
47
|
+
#
|
48
|
+
# @param [Array[String]] possibilities
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def suggest(message, possibilities)
|
52
|
+
distances = measure_distances(message, possibilities)
|
53
|
+
minimum_distance = distances.keys.min
|
54
|
+
max_distance = distances.keys.max
|
55
|
+
|
56
|
+
if minimum_distance < max_distance
|
57
|
+
@suggestions = distances[minimum_distance].sort
|
58
|
+
end
|
59
|
+
evaluate
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Measure distances between messag and possibilities
|
65
|
+
#
|
66
|
+
# @param [String] message
|
67
|
+
#
|
68
|
+
# @param [Array[String]] possibilities
|
69
|
+
#
|
70
|
+
# @return [Hash]
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
def measure_distances(message, possibilities)
|
74
|
+
distances = Hash.new { |hash, key| hash[key] = [] }
|
75
|
+
|
76
|
+
possibilities.each do |possibility|
|
77
|
+
distances[@comparator.distance(message, possibility)] << possibility
|
78
|
+
end
|
79
|
+
distances
|
80
|
+
end
|
81
|
+
|
82
|
+
# Build up a suggestion string
|
83
|
+
#
|
84
|
+
# @param [Array[String]] suggestions
|
85
|
+
#
|
86
|
+
# @return [String]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def evaluate
|
90
|
+
return @suggestions if @suggestions.empty?
|
91
|
+
|
92
|
+
if @suggestions.one?
|
93
|
+
build_single_suggestion
|
94
|
+
else
|
95
|
+
build_multiple_suggestions
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# @api private
|
100
|
+
def build_single_suggestion
|
101
|
+
single_text + "\n" + (" " * indent) + @suggestions.first
|
102
|
+
end
|
103
|
+
|
104
|
+
# @api private
|
105
|
+
def build_multiple_suggestions
|
106
|
+
plural_text + "\n" +
|
107
|
+
@suggestions.map do |sugest|
|
108
|
+
" " * indent + sugest
|
109
|
+
end.join("\n")
|
110
|
+
end
|
111
|
+
end # Suggestion
|
112
|
+
end # Prompt
|
113
|
+
end # TTY
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Prompt
|
5
|
+
# Cross platform common Unicode symbols.
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
module Symbols
|
9
|
+
KEYS = {
|
10
|
+
tick: "✓",
|
11
|
+
cross: "✘",
|
12
|
+
star: "★",
|
13
|
+
square: "◼",
|
14
|
+
square_empty: "◻",
|
15
|
+
dot: "•",
|
16
|
+
bullet: "●",
|
17
|
+
bullet_empty: "○",
|
18
|
+
marker: "‣",
|
19
|
+
line: "─",
|
20
|
+
pipe: "|",
|
21
|
+
ellipsis: "…",
|
22
|
+
radio_on: "⬢",
|
23
|
+
radio_off: "⬡",
|
24
|
+
checkbox_on: "☒",
|
25
|
+
checkbox_off: "☐",
|
26
|
+
circle: "◯",
|
27
|
+
circle_on: "ⓧ",
|
28
|
+
circle_off: "Ⓘ",
|
29
|
+
arrow_up: "↑",
|
30
|
+
arrow_down: "↓",
|
31
|
+
arrow_up_down: "↕",
|
32
|
+
arrow_left: "←",
|
33
|
+
arrow_right: "→",
|
34
|
+
arrow_left_right: "↔",
|
35
|
+
heart: "♥",
|
36
|
+
diamond: "♦",
|
37
|
+
club: "♣",
|
38
|
+
spade: "♠"
|
39
|
+
}.freeze
|
40
|
+
|
41
|
+
WIN_KEYS = {
|
42
|
+
tick: "√",
|
43
|
+
cross: "x",
|
44
|
+
star: "*",
|
45
|
+
square: "[█]",
|
46
|
+
square_empty: "[ ]",
|
47
|
+
dot: ".",
|
48
|
+
bullet: "O",
|
49
|
+
bullet_empty: "○",
|
50
|
+
marker: ">",
|
51
|
+
line: "-",
|
52
|
+
pipe: "|",
|
53
|
+
ellipsis: "...",
|
54
|
+
radio_on: "(*)",
|
55
|
+
radio_off: "( )",
|
56
|
+
checkbox_on: "[×]",
|
57
|
+
checkbox_off: "[ ]",
|
58
|
+
circle: "( )",
|
59
|
+
circle_on: "(x)",
|
60
|
+
circle_off: "( )",
|
61
|
+
arrow_up: "↑",
|
62
|
+
arrow_down: "↓",
|
63
|
+
arrow_up_down: "↕",
|
64
|
+
arrow_left: "←",
|
65
|
+
arrow_right: "→",
|
66
|
+
arrow_left_right: "↔",
|
67
|
+
heart: "♥",
|
68
|
+
diamond: "♦",
|
69
|
+
club: "♣",
|
70
|
+
spade: "♠"
|
71
|
+
}.freeze
|
72
|
+
|
73
|
+
def symbols
|
74
|
+
@symbols ||= windows? ? WIN_KEYS : KEYS
|
75
|
+
end
|
76
|
+
module_function :symbols
|
77
|
+
|
78
|
+
# Check if Windowz
|
79
|
+
#
|
80
|
+
# @return [Boolean]
|
81
|
+
#
|
82
|
+
# @api public
|
83
|
+
def windows?
|
84
|
+
::File::ALT_SEPARATOR == "\\"
|
85
|
+
end
|
86
|
+
module_function :windows?
|
87
|
+
end # Symbols
|
88
|
+
end # Prompt
|
89
|
+
end # TTY
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
|
5
|
+
require_relative "../prompt"
|
6
|
+
|
7
|
+
module TTY
|
8
|
+
# Used for initializing test cases
|
9
|
+
class Prompt
|
10
|
+
module StringIOExtensions
|
11
|
+
def wait_readable(*)
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def ioctl(*)
|
16
|
+
80
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Test < TTY::Prompt
|
21
|
+
def initialize(**options)
|
22
|
+
@input = StringIO.new
|
23
|
+
@input.extend(StringIOExtensions)
|
24
|
+
@output = StringIO.new
|
25
|
+
|
26
|
+
options.merge!({
|
27
|
+
input: @input,
|
28
|
+
output: @output,
|
29
|
+
env: { "TTY_TEST" => true },
|
30
|
+
enable_color: options.fetch(:enable_color, true)
|
31
|
+
})
|
32
|
+
super(**options)
|
33
|
+
end
|
34
|
+
end # Test
|
35
|
+
end # Prompt
|
36
|
+
end # TTY
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Prompt
|
5
|
+
class Timer
|
6
|
+
attr_reader :duration
|
7
|
+
|
8
|
+
attr_reader :total
|
9
|
+
|
10
|
+
attr_reader :interval
|
11
|
+
|
12
|
+
def initialize(duration, interval)
|
13
|
+
@duration = duration
|
14
|
+
@interval = interval
|
15
|
+
@total = 0.0
|
16
|
+
@current = nil
|
17
|
+
@events = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
return if @current
|
22
|
+
|
23
|
+
@current = time_now
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop
|
27
|
+
return unless @current
|
28
|
+
|
29
|
+
@current = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def runtime
|
33
|
+
time_now - @current
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_tick(&block)
|
37
|
+
@events << block
|
38
|
+
end
|
39
|
+
|
40
|
+
def while_remaining
|
41
|
+
start
|
42
|
+
remaining = duration
|
43
|
+
|
44
|
+
if @duration
|
45
|
+
while remaining >= 0.0
|
46
|
+
if runtime >= total
|
47
|
+
tick = duration - @total
|
48
|
+
@events.each { |block| block.(tick) }
|
49
|
+
@total += @interval
|
50
|
+
end
|
51
|
+
|
52
|
+
yield(remaining)
|
53
|
+
remaining = duration - runtime
|
54
|
+
end
|
55
|
+
else
|
56
|
+
loop { yield }
|
57
|
+
end
|
58
|
+
ensure
|
59
|
+
stop
|
60
|
+
end
|
61
|
+
|
62
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
63
|
+
# Object representing current time
|
64
|
+
def time_now
|
65
|
+
::Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
# Object represeting current time
|
69
|
+
def time_now
|
70
|
+
::Time.now
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end # Timer
|
74
|
+
end # Prompt
|
75
|
+
end # TTY
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
module Utils
|
5
|
+
module_function
|
6
|
+
|
7
|
+
BLANK_REGEX = /\A[[:space:]]*\z/o.freeze
|
8
|
+
|
9
|
+
# Extract options hash from array argument
|
10
|
+
#
|
11
|
+
# @param [Array[Object]] args
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
def extract_options(args)
|
15
|
+
options = args.last
|
16
|
+
options.respond_to?(:to_hash) ? options.to_hash.dup : {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def extract_options!(args)
|
20
|
+
args.last.respond_to?(:to_hash) ? args.pop : {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check if value is nil or an empty string
|
24
|
+
#
|
25
|
+
# @param [Object] value
|
26
|
+
# the value to check
|
27
|
+
#
|
28
|
+
# @return [Boolean]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def blank?(value)
|
32
|
+
value.nil? || BLANK_REGEX === value
|
33
|
+
end
|
34
|
+
|
35
|
+
# Deep copy object
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def deep_copy(object)
|
39
|
+
Marshal.load(Marshal.dump(object))
|
40
|
+
end
|
41
|
+
end # Utils
|
42
|
+
end # TTY
|