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.
Files changed (275) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +48 -0
  3. data/.gitattributes +19 -0
  4. data/.github/dependabot.yml +12 -0
  5. data/.github/workflows/ci.yml +90 -0
  6. data/.rubocop.yml +8 -0
  7. data/.ruby-version +1 -0
  8. data/Dockerfile +69 -0
  9. data/Gemfile +68 -0
  10. data/README.md +188 -0
  11. data/Rakefile +6 -0
  12. data/app/assets/config/manifest.js +5 -0
  13. data/app/assets/images/.keep +0 -0
  14. data/app/assets/images/2024-12-30-07:58:34-screenshot.png +0 -0
  15. data/app/assets/images/facebook/facebook-logo.svg +3 -0
  16. data/app/assets/images/google/googlelogo_color_272x92dp.png +0 -0
  17. data/app/assets/images/instagram/app-store.png +0 -0
  18. data/app/assets/images/instagram/google-play.png +0 -0
  19. data/app/assets/images/instagram/instagram-logo.png +0 -0
  20. data/app/assets/images/instagram/screenshot1.png +0 -0
  21. data/app/assets/images/instagram/screenshot2.png +0 -0
  22. data/app/assets/images/instagram/screenshot3.png +0 -0
  23. data/app/assets/images/instagram/screenshot4.png +0 -0
  24. data/app/assets/images/nauta_etecsa/CUBADEBATE.jpg +0 -0
  25. data/app/assets/images/nauta_etecsa/CUBAEDUCA.jpg +0 -0
  26. data/app/assets/images/nauta_etecsa/CUBARTE.jpg +0 -0
  27. data/app/assets/images/nauta_etecsa/CUBASI.jpg +0 -0
  28. data/app/assets/images/nauta_etecsa/ECURED.jpg +0 -0
  29. data/app/assets/images/nauta_etecsa/OFERTAS.jpg +0 -0
  30. data/app/assets/images/nauta_etecsa/PAPELETA.jpg +0 -0
  31. data/app/assets/images/nauta_etecsa/contenedor_bg.jpg +0 -0
  32. data/app/assets/images/nauta_etecsa/etecsa.jpg +0 -0
  33. data/app/assets/images/nauta_etecsa/nauta_wifi.jpg +0 -0
  34. data/app/assets/images/nauta_hogar/CUBADEBATE.jpg +0 -0
  35. data/app/assets/images/nauta_hogar/CUBAEDUCA.jpg +0 -0
  36. data/app/assets/images/nauta_hogar/CUBARTE.jpg +0 -0
  37. data/app/assets/images/nauta_hogar/CUBASI.jpg +0 -0
  38. data/app/assets/images/nauta_hogar/ECURED.jpg +0 -0
  39. data/app/assets/images/nauta_hogar/OFERTAS.jpg +0 -0
  40. data/app/assets/images/nauta_hogar/PAPELETA.jpg +0 -0
  41. data/app/assets/images/nauta_hogar/banner_hogar.png +0 -0
  42. data/app/assets/images/wibrfake/Breaker-Wallpaper1.jpg +0 -0
  43. data/app/assets/images/wibrfake/mac-list-parameter.png +0 -0
  44. data/app/assets/images/wibrfake/wibrfake-100x100.png +0 -0
  45. data/app/assets/images/wibrfake/wibrfake-32x32.png +0 -0
  46. data/app/assets/images/wibrfake/wibrfake-48x48.png +0 -0
  47. data/app/assets/images/wibrfake/wibrfake-apfake-on.png +0 -0
  48. data/app/assets/images/wibrfake/wibrfake-apfake-set-basic.png +0 -0
  49. data/app/assets/images/wibrfake/wibrfake-apfake-show.png +0 -0
  50. data/app/assets/images/wibrfake/wibrfake-help.png +0 -0
  51. data/app/assets/images/wibrfake/wibrfake-hostpot.png +0 -0
  52. data/app/assets/images/wibrfake/wibrfake-init.png +0 -0
  53. data/app/assets/images/wibrfake/wibrfake-init2.png +0 -0
  54. data/app/assets/images/wibrfake/wibrfake-login-basic.jpg +0 -0
  55. data/app/assets/images/wibrfake/wibrfake-mac-list.png +0 -0
  56. data/app/assets/images/wibrfake/wibrfake-mac-ramset-oui.png +0 -0
  57. data/app/assets/images/wibrfake/wibrfake-mac-ramset.png +0 -0
  58. data/app/assets/images/wibrfake/wibrfake-mac-set.png +0 -0
  59. data/app/assets/images/wibrfake/wibrfake-mac-show.png +0 -0
  60. data/app/assets/images/wibrfake/wibrfake-need-signin.jpg +0 -0
  61. data/app/assets/images/wibrfake/wibrfake-session-active.png +0 -0
  62. data/app/assets/images/wibrfake/wibrfake-session-init.png +0 -0
  63. data/app/assets/images/wibrfake/wibrfake-session-list.png +0 -0
  64. data/app/assets/images/wibrfake/wibrfake-session-new.png +0 -0
  65. data/app/assets/images/wibrfake/wibrfake-session-rename.png +0 -0
  66. data/app/assets/images/wibrfake/wibrfake-session-save.png +0 -0
  67. data/app/assets/images/wibrfake/wibrfake-web-server-apfake.png +0 -0
  68. data/app/assets/images/wibrfake/wibrfake-web-server-on.png +0 -0
  69. data/app/assets/images/wibrfake/wibrfake-web-server-show.png +0 -0
  70. data/app/assets/images/wibrfake/wibrfake-wkdump-loaded.png +0 -0
  71. data/app/assets/images/wibrfake/wibrfake-wkdump.png +0 -0
  72. data/app/assets/images/wibrfake/wibrfake.png +0 -0
  73. data/app/assets/stylesheets/application.css +15 -0
  74. data/app/assets/stylesheets/basic/page_basic.css +0 -0
  75. data/app/assets/stylesheets/facebook/page_facebook.css +93 -0
  76. data/app/assets/stylesheets/google/page_google.css +137 -0
  77. data/app/assets/stylesheets/instagram/page_instagram.css +94 -0
  78. data/app/assets/stylesheets/nauta_etecsa/page_nauta.css +325 -0
  79. data/app/assets/stylesheets/nauta_hogar/page_nauta.css +469 -0
  80. data/app/assets/stylesheets/wibrfake/page_wibrfake.css +1181 -0
  81. data/app/assets/stylesheets/wibrfake/page_wibrfake_about.css +486 -0
  82. data/app/channels/application_cable/channel.rb +4 -0
  83. data/app/channels/application_cable/connection.rb +4 -0
  84. data/app/controllers/application_controller.rb +15 -0
  85. data/app/controllers/concerns/.keep +0 -0
  86. data/app/controllers/sessions_controller.rb +111 -0
  87. data/app/helpers/application_helper.rb +2 -0
  88. data/app/helpers/sessions_helper.rb +2 -0
  89. data/app/javascript/application.js +3 -0
  90. data/app/javascript/bootstrap.bundle.min.js +7 -0
  91. data/app/javascript/controllers/application.js +9 -0
  92. data/app/javascript/controllers/hello_controller.js +7 -0
  93. data/app/javascript/controllers/index.js +4 -0
  94. data/app/javascript/google/page_google.js +50 -0
  95. data/app/javascript/incorrect3.js +29 -0
  96. data/app/javascript/page_wibrfake.js +50 -0
  97. data/app/javascript/page_wibrfake_about.js +147 -0
  98. data/app/jobs/application_job.rb +7 -0
  99. data/app/mailers/application_mailer.rb +4 -0
  100. data/app/models/application_record.rb +3 -0
  101. data/app/models/concerns/.keep +0 -0
  102. data/app/models/user.rb +6 -0
  103. data/app/views/layouts/application.html.erb +33 -0
  104. data/app/views/layouts/mailer.html.erb +13 -0
  105. data/app/views/layouts/mailer.text.erb +1 -0
  106. data/app/views/pwa/manifest.json.erb +22 -0
  107. data/app/views/pwa/service-worker.js +26 -0
  108. data/app/views/sessions/basic/basic.html.erb +21 -0
  109. data/app/views/sessions/facebook/facebook.html.erb +132 -0
  110. data/app/views/sessions/google/google.html.erb +72 -0
  111. data/app/views/sessions/index.html.erb +11 -0
  112. data/app/views/sessions/instagram/instagram.html.erb +69 -0
  113. data/app/views/sessions/nauta_etecsa/nauta_etecsa.html.erb +97 -0
  114. data/app/views/sessions/nauta_hogar/nauta_hogar.html.erb +135 -0
  115. data/app/views/sessions/wibrfake/about/about.html.erb +164 -0
  116. data/app/views/sessions/wibrfake/wibrfake.html.erb +417 -0
  117. data/bin/.names.rb.swp +0 -0
  118. data/bin/brakeman +7 -0
  119. data/bin/bundle +114 -0
  120. data/bin/docker-entrypoint +13 -0
  121. data/bin/google.wkdump +3 -0
  122. data/bin/importmap +4 -0
  123. data/bin/rails +4 -0
  124. data/bin/rake +4 -0
  125. data/bin/rubocop +8 -0
  126. data/bin/setup +37 -0
  127. data/bin/wibrfake +20 -0
  128. data/config/application.rb +26 -0
  129. data/config/boot.rb +4 -0
  130. data/config/cable.yml +10 -0
  131. data/config/credentials.yml.enc +1 -0
  132. data/config/database.yml +16 -0
  133. data/config/environment.rb +5 -0
  134. data/config/environments/development.rb +85 -0
  135. data/config/environments/production.rb +105 -0
  136. data/config/environments/test.rb +67 -0
  137. data/config/icons/application-x-wkdump.xml +13 -0
  138. data/config/icons/hicolor/16x16/mimetypes/application-x-wkdump.png +0 -0
  139. data/config/icons/hicolor/32x32/mimetypes/application-x-wkdump.png +0 -0
  140. data/config/icons/hicolor/48x48/mimetypes/application-x-wkdump.png +0 -0
  141. data/config/icons/hicolor/icon-theme.cache +0 -0
  142. data/config/icons/hicolor/index.theme +20 -0
  143. data/config/icons/wkdump.desktop +5 -0
  144. data/config/importmap.rb +7 -0
  145. data/config/initializers/assets.rb +12 -0
  146. data/config/initializers/content_security_policy.rb +25 -0
  147. data/config/initializers/devise.rb +313 -0
  148. data/config/initializers/filter_parameter_logging.rb +8 -0
  149. data/config/initializers/inflections.rb +16 -0
  150. data/config/initializers/permissions_policy.rb +13 -0
  151. data/config/locales/devise.en.yml +65 -0
  152. data/config/locales/en.yml +31 -0
  153. data/config/puma.rb +35 -0
  154. data/config/routes.rb +12 -0
  155. data/config/storage.yml +34 -0
  156. data/config.ru +6 -0
  157. data/db/development.sqlite3 +0 -0
  158. data/db/schema.rb +14 -0
  159. data/ext/wibrfake/extconf.rb +3 -0
  160. data/ext/wibrfake/wibrfake.h +0 -0
  161. data/images/ouilist.png +0 -0
  162. data/images/wibrfakehelp.png +0 -0
  163. data/images/wibrfakeinit.png +0 -0
  164. data/lib/assets/.keep +0 -0
  165. data/lib/tasks/.keep +0 -0
  166. data/lib/wibrfake/Config/ipaddr.rb +71 -0
  167. data/lib/wibrfake/Config/options.rb +98 -0
  168. data/lib/wibrfake/Config/security_wpa.rb +43 -0
  169. data/lib/wibrfake/DataBase/howuis.db +0 -0
  170. data/lib/wibrfake/DataBase/ouis.db +0 -0
  171. data/lib/wibrfake/DataBase/ouis.rb +24 -0
  172. data/lib/wibrfake/DataBase/rename.rb +15 -0
  173. data/lib/wibrfake/DataBase/show.rb +18 -0
  174. data/lib/wibrfake/Dnsmasq/dhcp.rb +149 -0
  175. data/lib/wibrfake/Dnsmasq/dhcp_prueba.rb +135 -0
  176. data/lib/wibrfake/Dnsmasq/dns.rb +34 -0
  177. data/lib/wibrfake/Dnsmasq/dns_prueba.rb +36 -0
  178. data/lib/wibrfake/Dnsmasq/dnsmasq.rb +33 -0
  179. data/lib/wibrfake/Listing/apfake.rb +28 -0
  180. data/lib/wibrfake/Listing/clients.rb +76 -0
  181. data/lib/wibrfake/Listing/ouis.rb +53 -0
  182. data/lib/wibrfake/Listing/process.rb +14 -0
  183. data/lib/wibrfake/Listing/sessions.rb +47 -0
  184. data/lib/wibrfake/Listing/web_server.rb +17 -0
  185. data/lib/wibrfake/Listing/wireless.rb +30 -0
  186. data/lib/wibrfake/Logs/credentials.log +59 -0
  187. data/lib/wibrfake/NetworkInterface/ip-routing.rb +99 -0
  188. data/lib/wibrfake/NetworkInterface/mac.rb +114 -0
  189. data/lib/wibrfake/NetworkInterface/monitor.rb +44 -0
  190. data/lib/wibrfake/NetworkInterface/ouis.db +0 -0
  191. data/lib/wibrfake/Process/id.rb +11 -0
  192. data/lib/wibrfake/Process/pids.log +5 -0
  193. data/lib/wibrfake/Process/pkill.rb +124 -0
  194. data/lib/wibrfake/Process/processes.rb +76 -0
  195. data/lib/wibrfake/Rails/login.rb +20 -0
  196. data/lib/wibrfake/Rails/routes.rb +76 -0
  197. data/lib/wibrfake/Rails/service.rb +58 -0
  198. data/lib/wibrfake/Rails/service_usage.rb +46 -0
  199. data/lib/wibrfake/Sessions/session.rb +188 -0
  200. data/lib/wibrfake/String/string.rb +109 -0
  201. data/lib/wibrfake/Tmp/config.yml +1 -0
  202. data/lib/wibrfake/config.rb +24 -0
  203. data/lib/wibrfake/help.rb +107 -0
  204. data/lib/wibrfake/optionpr.rb +68 -0
  205. data/lib/wibrfake/run.rb +9 -0
  206. data/lib/wibrfake/ty/apld.rb +110 -0
  207. data/lib/wibrfake/ty/prompt/answers_collector.rb +78 -0
  208. data/lib/wibrfake/ty/prompt/block_paginator.rb +59 -0
  209. data/lib/wibrfake/ty/prompt/choice.rb +147 -0
  210. data/lib/wibrfake/ty/prompt/choices.rb +129 -0
  211. data/lib/wibrfake/ty/prompt/confirm_question.rb +158 -0
  212. data/lib/wibrfake/ty/prompt/const.rb +17 -0
  213. data/lib/wibrfake/ty/prompt/converter_dsl.rb +21 -0
  214. data/lib/wibrfake/ty/prompt/converter_registry.rb +69 -0
  215. data/lib/wibrfake/ty/prompt/converters.rb +182 -0
  216. data/lib/wibrfake/ty/prompt/distance.rb +49 -0
  217. data/lib/wibrfake/ty/prompt/enum_list.rb +433 -0
  218. data/lib/wibrfake/ty/prompt/errors.rb +31 -0
  219. data/lib/wibrfake/ty/prompt/evaluator.rb +29 -0
  220. data/lib/wibrfake/ty/prompt/expander.rb +321 -0
  221. data/lib/wibrfake/ty/prompt/keypress.rb +98 -0
  222. data/lib/wibrfake/ty/prompt/list.rb +589 -0
  223. data/lib/wibrfake/ty/prompt/mask_question.rb +96 -0
  224. data/lib/wibrfake/ty/prompt/multi_list.rb +224 -0
  225. data/lib/wibrfake/ty/prompt/multiline.rb +72 -0
  226. data/lib/wibrfake/ty/prompt/paginator.rb +111 -0
  227. data/lib/wibrfake/ty/prompt/question/checks.rb +105 -0
  228. data/lib/wibrfake/ty/prompt/question/modifier.rb +96 -0
  229. data/lib/wibrfake/ty/prompt/question/validation.rb +72 -0
  230. data/lib/wibrfake/ty/prompt/question.rb +391 -0
  231. data/lib/wibrfake/ty/prompt/result.rb +42 -0
  232. data/lib/wibrfake/ty/prompt/selected_choices.rb +77 -0
  233. data/lib/wibrfake/ty/prompt/slider.rb +286 -0
  234. data/lib/wibrfake/ty/prompt/statement.rb +55 -0
  235. data/lib/wibrfake/ty/prompt/suggestion.rb +113 -0
  236. data/lib/wibrfake/ty/prompt/symbols.rb +89 -0
  237. data/lib/wibrfake/ty/prompt/test.rb +36 -0
  238. data/lib/wibrfake/ty/prompt/timer.rb +75 -0
  239. data/lib/wibrfake/ty/prompt/utils.rb +42 -0
  240. data/lib/wibrfake/ty/prompt/version.rb +7 -0
  241. data/lib/wibrfake/ty/prompt.rb +589 -0
  242. data/lib/wibrfake/version.rb +5 -0
  243. data/lib/wibrfake/wibrfake_cli.rb +683 -0
  244. data/log/.keep +0 -0
  245. data/pruebas/daemon1.pid +1 -0
  246. data/pruebas/daemon2.pid +1 -0
  247. data/pruebas/daemons.rb +44 -0
  248. data/public/404.html +67 -0
  249. data/public/406-unsupported-browser.html +66 -0
  250. data/public/422.html +67 -0
  251. data/public/500.html +66 -0
  252. data/public/icon.png +0 -0
  253. data/public/icon.svg +3 -0
  254. data/public/robots.txt +1 -0
  255. data/storage/.keep +0 -0
  256. data/test/application_system_test_case.rb +5 -0
  257. data/test/channels/application_cable/connection_test.rb +13 -0
  258. data/test/controllers/.keep +0 -0
  259. data/test/controllers/sessions_controller_test.rb +7 -0
  260. data/test/fixtures/files/.keep +0 -0
  261. data/test/fixtures/users.yml +11 -0
  262. data/test/helpers/.keep +0 -0
  263. data/test/integration/.keep +0 -0
  264. data/test/mailers/.keep +0 -0
  265. data/test/models/.keep +0 -0
  266. data/test/models/user_test.rb +7 -0
  267. data/test/system/.keep +0 -0
  268. data/test/test_helper.rb +15 -0
  269. data/tmp/.keep +0 -0
  270. data/tmp/pids/.keep +0 -0
  271. data/tmp/storage/.keep +0 -0
  272. data/vendor/.keep +0 -0
  273. data/vendor/javascript/.keep +0 -0
  274. data/wibrfake.gemspec +49 -0
  275. metadata +743 -0
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "result"
4
+
5
+ module TTY
6
+ class Prompt
7
+ # Evaluates provided parameters and stops if any of them fails
8
+ # @api private
9
+ class Evaluator
10
+ attr_reader :results
11
+
12
+ def initialize(question, &block)
13
+ @question = question
14
+ @results = []
15
+ instance_eval(&block) if block
16
+ end
17
+
18
+ def call(initial)
19
+ seed = Result::Success.new(@question, initial)
20
+ results.reduce(seed, &:with)
21
+ end
22
+
23
+ def check(proc = nil, &block)
24
+ results << (proc || block)
25
+ end
26
+ alias << check
27
+ end # Evaluator
28
+ end # Prompt
29
+ end # TTY
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "choices"
4
+
5
+ module TTY
6
+ class Prompt
7
+ # A class responsible for rendering expanding options
8
+ # Used by {Prompt} to display key options question.
9
+ #
10
+ # @api private
11
+ class Expander
12
+ HELP_CHOICE = {
13
+ key: "h",
14
+ name: "print help",
15
+ value: :help
16
+ }.freeze
17
+
18
+ # Names for delete keys
19
+ DELETE_KEYS = %i[backspace delete].freeze
20
+
21
+ # Create instance of Expander
22
+ #
23
+ # @api public
24
+ def initialize(prompt, options = {})
25
+ @prompt = prompt
26
+ @prefix = options.fetch(:prefix) { @prompt.prefix }
27
+ @default = options.fetch(:default, 1)
28
+ @auto_hint = options.fetch(:auto_hint, false)
29
+ @active_color = options.fetch(:active_color) { @prompt.active_color }
30
+ @help_color = options.fetch(:help_color) { @prompt.help_color }
31
+ @quiet = options.fetch(:quiet) { @prompt.quiet }
32
+ @choices = Choices.new
33
+ @selected = nil
34
+ @done = false
35
+ @status = :collapsed
36
+ @hint = nil
37
+ @default_key = false
38
+ end
39
+
40
+ def expanded?
41
+ @status == :expanded
42
+ end
43
+
44
+ def collapsed?
45
+ @status == :collapsed
46
+ end
47
+
48
+ def expand
49
+ @status = :expanded
50
+ end
51
+
52
+ # Respond to submit event
53
+ #
54
+ # @api public
55
+ def keyenter(_)
56
+ if @input.nil? || @input.empty?
57
+ @input = @choices[@default - 1].key
58
+ @default_key = true
59
+ end
60
+
61
+ selected = select_choice(@input)
62
+
63
+ if selected && selected.key.to_s == "h"
64
+ expand
65
+ @selected = nil
66
+ @input = ""
67
+ elsif selected
68
+ @done = true
69
+ @selected = selected
70
+ @hint = nil
71
+ else
72
+ @input = ""
73
+ end
74
+ end
75
+ alias keyreturn keyenter
76
+
77
+ # Respond to key press event
78
+ #
79
+ # @api public
80
+ def keypress(event)
81
+ if DELETE_KEYS.include?(event.key.name)
82
+ @input.chop! unless @input.empty?
83
+ elsif event.value =~ /^[^\e\n\r]/
84
+ @input += event.value
85
+ end
86
+
87
+ @selected = select_choice(@input)
88
+ if @selected && !@default_key && collapsed?
89
+ @hint = @selected.name
90
+ end
91
+ end
92
+
93
+ # Select choice by given key
94
+ #
95
+ # @return [Choice]
96
+ #
97
+ # @api private
98
+ def select_choice(key)
99
+ @choices.find_by(:key, key)
100
+ end
101
+
102
+ # Set default value.
103
+ #
104
+ # @api public
105
+ def default(value = (not_set = true))
106
+ return @default if not_set
107
+
108
+ @default = value
109
+ end
110
+
111
+ # Set quiet mode.
112
+ #
113
+ # @api public
114
+ def quiet(value)
115
+ @quiet = value
116
+ end
117
+
118
+ # Add a single choice
119
+ #
120
+ # @api public
121
+ def choice(value, &block)
122
+ if block
123
+ @choices << value.update(value: block)
124
+ else
125
+ @choices << value
126
+ end
127
+ end
128
+
129
+ # Add multiple choices
130
+ #
131
+ # @param [Array[Object]] values
132
+ # the values to add as choices
133
+ #
134
+ # @api public
135
+ def choices(values)
136
+ values.each { |val| choice(val) }
137
+ end
138
+
139
+ # Execute this prompt
140
+ #
141
+ # @api public
142
+ def call(message, possibilities, &block)
143
+ choices(possibilities)
144
+ @message = message
145
+ block.call(self) if block
146
+ setup_defaults
147
+ choice(HELP_CHOICE)
148
+ @prompt.subscribe(self) do
149
+ render
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ # Create possible keys with current choice highlighted
156
+ #
157
+ # @return [String]
158
+ #
159
+ # @api private
160
+ def possible_keys
161
+ keys = @choices.pluck(:key)
162
+ default_key = keys[@default - 1]
163
+ if @selected
164
+ index = keys.index(@selected.key)
165
+ keys[index] = @prompt.decorate(keys[index], @active_color)
166
+ elsif @input.to_s.empty? && default_key
167
+ keys[@default - 1] = @prompt.decorate(default_key, @active_color)
168
+ end
169
+ keys.join(",")
170
+ end
171
+
172
+ # @api private
173
+ def render
174
+ @input = ""
175
+ until @done
176
+ question = render_question
177
+ @prompt.print(question)
178
+ read_input
179
+ @prompt.print(refresh(question.lines.count))
180
+ end
181
+ @prompt.print(render_question) unless @quiet
182
+ answer
183
+ end
184
+
185
+ # @api private
186
+ def answer
187
+ @selected.value
188
+ end
189
+
190
+ # Render message with options
191
+ #
192
+ # @return [String]
193
+ #
194
+ # @api private
195
+ def render_header
196
+ header = ["#{@prefix}#{@message} "]
197
+ if @done
198
+ selected_item = @selected.name.to_s
199
+ header << @prompt.decorate(selected_item, @active_color)
200
+ elsif collapsed?
201
+ header << %[(enter "h" for help) ]
202
+ header << "[#{possible_keys}] "
203
+ header << @input
204
+ end
205
+ header.join
206
+ end
207
+
208
+ # Show hint for selected option key
209
+ #
210
+ # return [String]
211
+ #
212
+ # @api private
213
+ def render_hint
214
+ "\n" + @prompt.decorate(">> ", @active_color) +
215
+ @hint +
216
+ @prompt.cursor.prev_line +
217
+ @prompt.cursor.forward(@prompt.strip(render_header).size)
218
+ end
219
+
220
+ # Render question with menu
221
+ #
222
+ # @return [String]
223
+ #
224
+ # @api private
225
+ def render_question
226
+ load_auto_hint if @auto_hint
227
+ header = render_header
228
+ header << render_hint if @hint
229
+ header << "\n" if @done
230
+
231
+ if !@done && expanded?
232
+ header << render_menu
233
+ header << render_footer
234
+ end
235
+ header
236
+ end
237
+
238
+ def load_auto_hint
239
+ if @hint.nil? && collapsed?
240
+ if @selected
241
+ @hint = @selected.name
242
+ else
243
+ if @input.empty?
244
+ @hint = @choices[@default - 1].name
245
+ else
246
+ @hint = "invalid option"
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ def render_footer
253
+ " Choice [#{@choices[@default - 1].key}]: #{@input}"
254
+ end
255
+
256
+ def read_input
257
+ @prompt.read_keypress
258
+ end
259
+
260
+ # Refresh the current input
261
+ #
262
+ # @param [Integer] lines
263
+ #
264
+ # @return [String]
265
+ #
266
+ # @api private
267
+ def refresh(lines)
268
+ if (@hint && (!@selected || @done)) || (@auto_hint && collapsed?)
269
+ @hint = nil
270
+ @prompt.clear_lines(lines, :down) +
271
+ @prompt.cursor.prev_line
272
+ elsif expanded?
273
+ @prompt.clear_lines(lines)
274
+ else
275
+ @prompt.clear_line
276
+ end
277
+ end
278
+
279
+ # Render help menu
280
+ #
281
+ # @api private
282
+ def render_menu
283
+ output = ["\n"]
284
+ @choices.each do |choice|
285
+ chosen = %(#{choice.key} - #{choice.name})
286
+ if @selected && @selected.key == choice.key
287
+ chosen = @prompt.decorate(chosen, @active_color)
288
+ end
289
+ output << " " + chosen + "\n"
290
+ end
291
+ output.join
292
+ end
293
+
294
+ def setup_defaults
295
+ validate_choices
296
+ end
297
+
298
+ def validate_choices
299
+ errors = []
300
+ keys = []
301
+ @choices.each do |choice|
302
+ if choice.key.nil?
303
+ errors << "Choice #{choice.name} is missing a :key attribute"
304
+ next
305
+ end
306
+ if choice.key.length != 1
307
+ errors << "Choice key `#{choice.key}` is more than one character long."
308
+ end
309
+ if choice.key.to_s == "h"
310
+ errors << "Choice key `#{choice.key}` is reserved for help menu."
311
+ end
312
+ if keys.include?(choice.key)
313
+ errors << "Choice key `#{choice.key}` is a duplicate."
314
+ end
315
+ keys << choice.key if choice.key
316
+ end
317
+ errors.each { |err| raise ConfigurationError, err }
318
+ end
319
+ end # Expander
320
+ end # Prompt
321
+ end # TTY
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "question"
4
+ require_relative "timer"
5
+
6
+ module TTY
7
+ class Prompt
8
+ class Keypress < Question
9
+ # Create keypress question
10
+ #
11
+ # @param [Prompt] prompt
12
+ # @param [Hash] options
13
+ #
14
+ # @api public
15
+ def initialize(prompt, **options)
16
+ super
17
+ @echo = options.fetch(:echo) { false }
18
+ @keys = options.fetch(:keys) { UndefinedSetting }
19
+ @timeout = options.fetch(:timeout) { UndefinedSetting }
20
+ @interval = options.fetch(:interval) {
21
+ (@timeout != UndefinedSetting && @timeout < 1) ? @timeout : 1
22
+ }
23
+ @decimals = (@interval.to_s.split(".")[1] || []).size
24
+ @countdown = @timeout
25
+ time = timeout? ? Float(@timeout) : nil
26
+ @timer = Timer.new(time, Float(@interval))
27
+
28
+ @prompt.subscribe(self)
29
+ end
30
+
31
+ def countdown(value = (not_set = true))
32
+ return @countdown if not_set
33
+
34
+ @countdown = value
35
+ end
36
+
37
+ # Check if any specific keys are set
38
+ def any_key?
39
+ @keys == UndefinedSetting
40
+ end
41
+
42
+ # Check if timeout is set
43
+ def timeout?
44
+ @timeout != UndefinedSetting
45
+ end
46
+
47
+ def keypress(event)
48
+ if any_key?
49
+ @done = true
50
+ elsif @keys.is_a?(Array) && @keys.include?(event.key.name)
51
+ @done = true
52
+ else
53
+ @done = false
54
+ end
55
+ end
56
+
57
+ def render_question
58
+ header = super
59
+ if timeout?
60
+ header.gsub!(/:countdown/, format("%.#{@decimals}f", countdown))
61
+ end
62
+ header
63
+ end
64
+
65
+ def interval_handler(time)
66
+ return if @done
67
+
68
+ question = render_question
69
+ line_size = question.size
70
+ total_lines = @prompt.count_screen_lines(line_size)
71
+ @prompt.print(refresh(question.lines.count, total_lines))
72
+ countdown(time)
73
+ @prompt.print(render_question)
74
+ end
75
+
76
+ def process_input(question)
77
+ @prompt.print(render_question)
78
+
79
+ @timer.on_tick do |time|
80
+ interval_handler(time)
81
+ end
82
+
83
+ @timer.while_remaining do |remaining|
84
+ break if @done
85
+
86
+ @input = @prompt.read_keypress(nonblock: true)
87
+ end
88
+ countdown(0) unless @done
89
+
90
+ @evaluator.(@input)
91
+ end
92
+
93
+ def refresh(lines, lines_to_clear)
94
+ @prompt.clear_lines(lines)
95
+ end
96
+ end # Keypress
97
+ end # Prompt
98
+ end # TTY