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,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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTY
4
+ class Prompt
5
+ VERSION = "0.23.1"
6
+ end # Prompt
7
+ end # TTY