console-glitter 0.2.1 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90788325694ec068cfc935f33d158bc21a49d61b
4
- data.tar.gz: 7103fb578db1f056c5806109a6a363ad9e9e2a28
3
+ metadata.gz: bca0b708e7d721160c7371babc90ae03901a0715
4
+ data.tar.gz: 37d30153c76f6f2118aa9419beeeb46b605b10eb
5
5
  SHA512:
6
- metadata.gz: ffe819e0b0f0cb4dfebf32a67a727af2628da21c77572f86885315119e88a4bd7567d55998ee56a4880a3a01ffdb267a1ae77e17d9f3dd0c31d47f0355cd9671
7
- data.tar.gz: 8ea90df7f43d7574121591c52a8ff9404449d98d201f3ed5f1980716d2c1b4dbae28032cb48a5ff493dfbb748bcb89f6453682854e31c31aeb416359699d2316
6
+ metadata.gz: 16e22ce68dfda6b492be65e4b26b7147a3bbf241c29454870d09c8f6b8b2cc86dd1f162716385eddfad9cd506adb0ae670a09ff3ccf89036d103ae9bdb2d819a
7
+ data.tar.gz: 5ae03963d116bfe502a8f54584c6dadba29e8f8a557afb2fdfd8a9417319dc0b616586c5ea4b7c266df6e3344fc5059860d3a1e86c749542b8ada5ae832752cb
@@ -1,264 +1,20 @@
1
- require 'console-glitter/version'
2
- require 'console-glitter/ansi'
3
- require 'readline'
4
- require 'io/console'
5
-
6
1
  module ConsoleGlitter
7
- module UI extend self
8
- extend ANSI
9
- # Public: Prompt user for input, allowing for a default answer and a list
10
- # of valid responses to be provided.
11
- #
12
- # question - Query to be presented to the user.
13
- # options - Hash containing arguments defining acceptable responses.
14
- # (default: {}):
15
- # :default_answer - String containing the default answer. If
16
- # this is nil, a non-empty answer MUST be
17
- # given.
18
- # :allow_empty - Whether or not to allow empty responses.
19
- # Unless explicitly allowed, empty answers
20
- # will be rejected.
21
- # :valid_answers - An Array containing all valid responses. If
22
- # this is empty, any answer will be accepted
23
- # (unless empty answers are disallowed as
24
- # specified above). Valid responses may be
25
- # any class with a match method such as
26
- # Strings or Regexps.
27
- # wordlist - Array of words to be used for input auto-completion.
28
- # (default: [])
29
- # block - Lambda which will override the default autocompletion lambda
30
- # as defined in autocomplete_lambda if present. (default: nil)
31
- #
32
- # Returns a String containing the answer provided by the user.
33
- def prompt(question, options = {}, wordlist = [], block = nil)
34
- default = options[:default_answer].to_s
35
- allow_empty = options[:allow_empty]
36
- valid = regexify_answers(options[:valid_answers])
37
-
38
- default_display = " [#{default.strip}]" unless default.empty?
39
- question = "#{question.strip}#{default_display}> "
40
-
41
- answer = nil
42
- while answer.nil?
43
- answer = user_prompt(question, wordlist, block)
44
- answer = default if answer.empty?
45
-
46
- if answer.empty?
47
- answer = nil unless allow_empty
48
- elsif valid.any?
49
- answer = nil unless valid.map { |valid| answer.match(valid) }.any?
50
- end
51
- end
52
-
53
- answer
54
- end
55
-
56
- # Public: Wrap Console#prompt but accept only a Y/N response.
57
- #
58
- # question - String containing the question to present to the user.
59
- # args - Hash containing arguments to control acceptable responses.
60
- # (default: {}):
61
- # :default_answer - String containing the default answer.
62
- #
63
- # Returns true or false corresponding to Y or N answer respectively.
64
- def prompt_yn(question, args = {})
65
- args[:valid_answers] = [/^[yn]/i]
66
- /^n/i.match(prompt(question, args)).nil?
67
- end
2
+ VERSION = '0.3.0'
68
3
 
69
- # Public: Wrap Console#prompt, disabling local echo of user input.
70
- #
71
- # question - Query to be presented to the user.
72
- # options - Hash containing arguments defining acceptable responses.
73
- # (default: {}):
74
- # :default_answer - String containing the default answer. If
75
- # this is nil, a non-empty answer MUST be
76
- # given.
77
- # :allow_empty - Whether or not to allow empty responses.
78
- # Unless explicitly allowed, empty answers
79
- # will be rejected.
80
- # :valid_answers - An Array containing all valid responses. If
81
- # this is empty, any answer will be accepted
82
- # (unless empty answers are disallowed as
83
- # specified above). Valid responses may be
84
- # any class with a match method such as
85
- # Strings or Regexps.
86
- #
87
- # Returns a String containing the answer provided by the user.
88
- def secure_prompt(question, args = {})
89
- IO.console.echo = false
90
- prompt(question, args)
91
- ensure
92
- IO.console.echo = true
93
- puts
94
- end
4
+ extend self
95
5
 
96
- # Public: Wrap Console#prompt, specifically targeting filesystem paths.
6
+ # Public: Return an appropriate escape sequence depending upon the current
7
+ # platform.
97
8
  #
98
- # question - Query to be presented to the user.
99
- # options - Hash containing arguments defining acceptable responses.
100
- # (default: {}):
101
- # :default_answer - String containing the default answer. If
102
- # this is nil, a non-empty answer MUST be
103
- # given.
104
- # :allow_empty - Whether or not to allow empty responses.
105
- # Unless explicitly allowed, empty answers
106
- # will be rejected.
107
- # :valid_answers - An Array containing all valid responses. If
108
- # this is empty, any answer will be accepted
109
- # (unless empty answers are disallowed as
110
- # specified above). Valid responses may be
111
- # any class with a match method such as
112
- # Strings or Regexps.
9
+ # sequence - String containing control code(s) to be escaped.
113
10
  #
114
- # Returns a String containing the answer provided by the user.
115
- def prompt_filesystem(question, args = {})
116
- fs_lambda = lambda { |d| Dir[d + '*'].grep(/^#{Regexp.escape(d)}/) }
117
- old_append = Readline.completion_append_character
118
-
119
- Readline.completion_append_character = ''
120
- response = prompt(question, args, [], fs_lambda)
121
- Readline.completion_append_character = old_append
122
-
123
- response
124
- end
125
-
126
- # Public: Render a "spinner" on the command line and yield to a block,
127
- # reporting success if nothing is raised, or else reporting failure.
11
+ # Examples
128
12
  #
129
- # message - Message to be displayed describing the task being evaluated.
130
- # block - Block to be yielded to determine pass or fail.
131
- #
132
- # Returns the result of the yielded block if successful.
133
- # Raises whatever is raised inside the yielded block.
134
- def spinner(message, &block)
135
- success = nil
136
- result = nil
137
-
138
- pre = "\r#{bold}#{white} [#{reset}"
139
- post = "#{bold}#{white}] #{reset}#{message}"
140
- pre_ok = "\r#{bold}#{white} [#{green} ok "
141
- pre_fail = "\r#{bold}#{white} [#{red}fail"
142
-
143
- thread = Thread.new do
144
- step = 0
145
- spin = [" ", ". ", ".. ", "... ", "....", " ...", " ..", " ."]
146
- while success.nil?
147
- print "#{pre}#{spin[step % 8]}#{post}"
148
- step += 1
149
- sleep 0.5
150
- end
151
-
152
- if success
153
- print "#{pre_ok}#{post}\n"
154
- else
155
- print "#{pre_fail}#{post}\n"
156
- end
157
- end
158
-
159
- begin
160
- result = yield
161
- success = true
162
- thread.join
163
- return result
164
- rescue
165
- success = false
166
- thread.join
167
- raise
168
- end
169
- end
170
-
171
- # Public: Generate a formatted, printable table.
172
- #
173
- # rows - An Array containing Hashes which contain desired options to
174
- # display. (e.g. [{"col1" => "a", "col2" => "b"}])
175
- # labels - Hash containing key-value pairs to label each key in options.
176
- # (default: nil)
177
- #
178
- # Returns a String containing the grid.
179
- # Raises ArgumentError if anything but an Array is passed as rows.
180
- def build_grid(rows, labels = nil)
181
- if labels.nil?
182
- labels = rows[0].keys.reduce({}) { |c,e| c.merge({e => e}) }
183
- end
184
-
185
- keys = labels.keys
186
-
187
- max_width = labels.reduce({}) do |c,e|
188
- c.merge({e[0]=> ([labels] + rows).map { |r| r[e[0]].length }.max})
189
- end
190
-
191
- grid_rule = max_width.reduce('+') do |c,e|
192
- c + ('-' * (e[1] + 2)) + '+'
193
- end
194
- grid_rule << "\n"
195
-
196
- grid = grid_rule.dup
197
- grid << keys.reduce('|') do |c,e|
198
- c + " #{bold}% #{max_width[e]}s#{reset} |" % labels[e]
199
- end
200
- grid << "\n"
201
-
202
- grid << rows.reduce(grid_rule) do |c,e|
203
- content = keys.reduce('') do |s,k|
204
- s + " % #{max_width[k]}s |" % e[k]
205
- end
206
- c + "|#{content}\n"
207
- end
208
- grid << grid_rule
209
- end
210
-
211
- private
212
-
213
- # Internal: Promp user for input via Readline, handling automatic setting
214
- # and restoring of Readline's autocompletion proc, avoiding trampling over
215
- # other uses.
216
- #
217
- # question - String containing a question to be displayed to the user.
218
- # wordlist - Array containing Strings which are considered valid answers
219
- # for autocompletion.
220
- # block - Lambda which will override the default autocompletion lambda
221
- # as defined in autocomplete_lambda if present. (default: nil)
13
+ # ConsoleGlitter.escape 'A'
14
+ # # => "\033[A"
222
15
  #
223
16
  # Returns a String.
224
- def user_prompt(question, wordlist, block = nil)
225
- block = autocomplete_lambda(wordlist) if block.nil?
226
- old_completion_proc = Readline.completion_proc
227
-
228
- Readline.completion_proc = block
229
- response = Readline.readline(question)
230
- Readline.completion_proc = old_completion_proc
231
-
232
- response.to_s
233
- end
234
-
235
- # Internal: Generate a lambda which retuns an array of autocompletion
236
- # candidates.
237
- #
238
- # wordlist - Array containing Strings which are considered valid answers
239
- # for autocompletion.
240
- #
241
- # Returns a lambda which returns an Array.
242
- def autocomplete_lambda(wordlist)
243
- lambda { |s| wordlist.grep(/^#{Regexp.escape(s)}/) }
244
- end
245
-
246
- # Internal: Generate Regexps for any String in an array to be used in order
247
- # to match against user input. Strings are expected to indicate exact
248
- # matches desired.
249
- #
250
- # valid_answers - Array containing Objects against which user input may be
251
- # matched.
252
- #
253
- # Returns an Array of Regexps.
254
- def regexify_answers(valid_answers)
255
- valid_answers.to_a.map do |answer|
256
- if answer.is_a?(String)
257
- Regexp.new("^#{Regexp.escape(answer)}$")
258
- else
259
- answer
260
- end
261
- end
17
+ def escape(sequence)
18
+ RUBY_PLATFORM =~ /(^win)|mingw/i ? '' : "\033[#{sequence}"
262
19
  end
263
- end
264
20
  end
@@ -1,21 +1,7 @@
1
+ require 'console-glitter'
2
+
1
3
  module ConsoleGlitter
2
4
  module ANSI extend self
3
- # Public: Return an appropriate escape sequence depending upon the current
4
- # platform.
5
- #
6
- # sequence - Control code(s) to be escaped. Multiple codes may be chained,
7
- # separated by ';'.
8
- #
9
- # Examples
10
- #
11
- # ConsoleGlitter::ANSI.escape 0
12
- # # => "\033[0m"
13
- #
14
- # Returns a String.
15
- def escape(sequence)
16
- RUBY_PLATFORM =~ /win|mingw/i ? '' : "\033[#{sequence}m"
17
- end
18
-
19
5
  # Public: Generate an escape sequence to set the foreground color to an
20
6
  # approximation of a 4 or 8 bit per channel hex RGB color a la CSS.
21
7
  #
@@ -56,6 +42,24 @@ module ConsoleGlitter
56
42
 
57
43
  private
58
44
 
45
+ # Internal: Wrap ConsoleGlitter#escape, appending the character 'm' to the
46
+ # control code (per SGR spec).
47
+ #
48
+ # sequence - String containing the control code(s) to be escaped. Multiple
49
+ # codes may be chained, separated by a ';'.
50
+ #
51
+ # Examples
52
+ #
53
+ # escape('0')
54
+ # # => "\033[0m"
55
+ # escape('1;31')
56
+ # # => "\033[1;31m"
57
+ #
58
+ # Returns a String.
59
+ def escape(sequence)
60
+ ConsoleGlitter.escape(sequence.to_s + 'm')
61
+ end
62
+
59
63
  # Internal: Allow on-the-fly definition of ANSI control sequences. Methods
60
64
  # are created for convenience which will either wrap the result of a block
61
65
  # in the tag specified and a reset tag, or else simply return the code in
@@ -0,0 +1,112 @@
1
+ require 'console-glitter'
2
+
3
+ module ConsoleGlitter
4
+ module Cursor extend self
5
+ # Public: Move the cursor up for a given distance.
6
+ #
7
+ # distance - Distance to move the cursor.
8
+ #
9
+ # Returns a String containing the VT control code.
10
+ def up(distance = 1)
11
+ ConsoleGlitter.escape("#{distance}A")
12
+ end
13
+
14
+ # Public: Move the cursor down for a given distance.
15
+ #
16
+ # distance - Distance to move the cursor.
17
+ #
18
+ # Returns a String containing the VT control code.
19
+ def down(distance = 1)
20
+ ConsoleGlitter.escape("#{distance}B")
21
+ end
22
+
23
+ # Public: Move the cursor forward for a given distance.
24
+ #
25
+ # distance - Distance to move the cursor.
26
+ #
27
+ # Returns a String containing the VT control code.
28
+ def forward(distance = 1)
29
+ ConsoleGlitter.escape("#{distance}C")
30
+ end
31
+ alias :right :forward
32
+
33
+ # Public: Move the cursor back for a given distance.
34
+ #
35
+ # distance - Distance to move the cursor.
36
+ #
37
+ # Returns a String containing the VT control code.
38
+ def back(distance = 1)
39
+ ConsoleGlitter.escape("#{distance}D")
40
+ end
41
+ alias :left :back
42
+
43
+ # Public: Move the cursor to the next line, returning the cursor to the
44
+ # beginning of the line.
45
+ #
46
+ # Returns a String containing the VT control code.
47
+ def nextline(distance = 1)
48
+ ConsoleGlitter.escape("#{distance}E")
49
+ end
50
+
51
+ # Public: Move the cursor to the previous line, returning the cursor to the
52
+ # beginning of the line.
53
+ #
54
+ # Returns a String containing the VT control code.
55
+ def prevline(distance = 1)
56
+ ConsoleGlitter.escape("#{distance}F")
57
+ end
58
+ alias :previousline :prevline
59
+
60
+ # Public: Move the absolute horizontal position specified.
61
+ #
62
+ # position - Number of the column to which the cursor should be moved.
63
+ #
64
+ # Returns a String containing the VT control code.
65
+ def column(position)
66
+ ConsoleGlitter.escape("#{position}G")
67
+ end
68
+
69
+ # Public: Move the cursor to the position specified. The top left position
70
+ # is 1,1.
71
+ #
72
+ # x - Column (x-position) to which the cursor should be moved.
73
+ # x - Row (y-position) to which the cursor should be moved.
74
+ #
75
+ # Returns a String containing the VT control code.
76
+ def move(x, y)
77
+ ConsoleGlitter.escape("#{y};#{x}H")
78
+ end
79
+
80
+ # Public: Save the cursor's current position, replacing any previously
81
+ # saved position.
82
+ #
83
+ # Returns a String containing the VT control code.
84
+ def save
85
+ ConsoleGlitter.escape("s")
86
+ end
87
+
88
+ # Public: Move the cursor to a previously saved position.
89
+ #
90
+ # Note: If the cursor's position was never previously saved, it will
91
+ # default to 1,1.
92
+ #
93
+ # Returns a String containing the VT control code.
94
+ def restore
95
+ ConsoleGlitter.escape("u")
96
+ end
97
+
98
+ # Public: Hide the cursor.
99
+ #
100
+ # Returns a String containing the VT control code.
101
+ def hide
102
+ ConsoleGlitter.escape("?25l")
103
+ end
104
+
105
+ # Public: Show the cursor.
106
+ #
107
+ # Returns a String containing the VT control code.
108
+ def show
109
+ ConsoleGlitter.escape("?25h")
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,106 @@
1
+ require 'console-glitter'
2
+
3
+ module ConsoleGlitter
4
+ module Screen extend self
5
+ # Public: From the cursor's position clear either the whole screen, or from
6
+ # the cursor's position to the beginning or end of the screen.
7
+ #
8
+ # direction - Symbol denoting the direction the screen should be cleared,
9
+ # between :both :end or :beginning. (default: :both)
10
+ #
11
+ # Returns a String containing the VT control code.
12
+ def clear(direction = :both)
13
+ options = [:both, :beginning, :end]
14
+ target = "clear_to_#{direction}"
15
+ return(self.send(target)) if options.grep(direction).any?
16
+
17
+ raise(ArgumentError,
18
+ "Expected :both, :end, or :beginning, got #{direction}.")
19
+ end
20
+ alias :clear_to :clear
21
+
22
+ # Public: Return the VT control code to clear from the cursor to the end of
23
+ # the screen.
24
+ #
25
+ # Returns a String containing the VT control code.
26
+ def clear_to_end
27
+ ConsoleGlitter.escape('0J')
28
+ end
29
+
30
+ # Public: Return the VT control code to clear from the cursor to the
31
+ # beginning of the screen.
32
+ #
33
+ # Returns a String containing the VT control code.
34
+ def clear_to_beginning
35
+ ConsoleGlitter.escape('1J')
36
+ end
37
+
38
+ # Public: Return the VT control code to clear the screen.
39
+ #
40
+ # Returns a String containing the VT control code.
41
+ def clear_to_both
42
+ ConsoleGlitter.escape('2J')
43
+ end
44
+ alias :clear_screen :clear_to_both
45
+
46
+ # Public: From the cursor's position clear either the whole line, or from
47
+ # the cursor's position to the beginning or end of the line.
48
+ #
49
+ # direction - Symbol denoting the direction the line should be cleared,
50
+ # between :both :end or :beginning. (default: :both)
51
+ #
52
+ # Returns a String containing the VT control code.
53
+ def erase_line(direction = :both)
54
+ options = [:both, :beginning, :end]
55
+ target = "erase_line_to_#{direction}"
56
+ return(self.send(target)) if options.grep(direction).any?
57
+
58
+ raise(ArgumentError,
59
+ "Expected :both, :end, or :beginning, got #{direction}.")
60
+ end
61
+ alias :erase_line_to :erase_line
62
+
63
+ # Public: Return the VT control code to clear from the cursor to the end of
64
+ # the line.
65
+ #
66
+ # Returns a String containing the VT control code.
67
+ def erase_line_to_end
68
+ ConsoleGlitter.escape('0K')
69
+ end
70
+
71
+ # Public: Return the VT control code to clear from the cursor to the
72
+ # beginning of the line.
73
+ #
74
+ # Returns a String containing the VT control code.
75
+ def erase_line_to_beginning
76
+ ConsoleGlitter.escape('1K')
77
+ end
78
+
79
+ # Public: Return the VT control code to clear the line where the cursor is.
80
+ #
81
+ # Returns a String containing the VT control code.
82
+ def erase_line_to_both
83
+ ConsoleGlitter.escape('2K')
84
+ end
85
+
86
+ # Public: Return the VT control code to scroll the screen up by a given
87
+ # amount.
88
+ #
89
+ # distance - Number of lines to scroll the screen. (default: 1)
90
+ #
91
+ # Returns a String containing the VT control code.
92
+ def scroll_up(distance = 1)
93
+ ConsoleGlitter.escape("#{distance}S")
94
+ end
95
+
96
+ # Public: Return the VT control code to scroll the screen down by a given
97
+ # amount.
98
+ #
99
+ # distance - Number of lines to scroll the screen. (default: 1)
100
+ #
101
+ # Returns a String containing the VT control code.
102
+ def scroll_down(distance = 1)
103
+ ConsoleGlitter.escape("#{distance}T")
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,264 @@
1
+ require 'console-glitter'
2
+ require 'console-glitter/ansi'
3
+ require 'readline'
4
+ require 'io/console'
5
+
6
+ module ConsoleGlitter
7
+ module UI extend self
8
+ extend ANSI
9
+ # Public: Prompt user for input, allowing for a default answer and a list
10
+ # of valid responses to be provided.
11
+ #
12
+ # question - Query to be presented to the user.
13
+ # options - Hash containing arguments defining acceptable responses.
14
+ # (default: {}):
15
+ # :default_answer - String containing the default answer. If
16
+ # this is nil, a non-empty answer MUST be
17
+ # given.
18
+ # :allow_empty - Whether or not to allow empty responses.
19
+ # Unless explicitly allowed, empty answers
20
+ # will be rejected.
21
+ # :valid_answers - An Array containing all valid responses. If
22
+ # this is empty, any answer will be accepted
23
+ # (unless empty answers are disallowed as
24
+ # specified above). Valid responses may be
25
+ # any class with a match method such as
26
+ # Strings or Regexps.
27
+ # wordlist - Array of words to be used for input auto-completion.
28
+ # (default: [])
29
+ # block - Lambda which will override the default autocompletion lambda
30
+ # as defined in autocomplete_lambda if present. (default: nil)
31
+ #
32
+ # Returns a String containing the answer provided by the user.
33
+ def prompt(question, options = {}, wordlist = [], block = nil)
34
+ default = options[:default_answer].to_s
35
+ allow_empty = options[:allow_empty]
36
+ valid = regexify_answers(options[:valid_answers])
37
+
38
+ default_display = " [#{default.strip}]" unless default.empty?
39
+ question = "#{question.strip}#{default_display}> "
40
+
41
+ answer = nil
42
+ while answer.nil?
43
+ answer = user_prompt(question, wordlist, block)
44
+ answer = default if answer.empty?
45
+
46
+ if answer.empty?
47
+ answer = nil unless allow_empty
48
+ elsif valid.any?
49
+ answer = nil unless valid.map { |valid| answer.match(valid) }.any?
50
+ end
51
+ end
52
+
53
+ answer
54
+ end
55
+
56
+ # Public: Wrap Console#prompt but accept only a Y/N response.
57
+ #
58
+ # question - String containing the question to present to the user.
59
+ # args - Hash containing arguments to control acceptable responses.
60
+ # (default: {}):
61
+ # :default_answer - String containing the default answer.
62
+ #
63
+ # Returns true or false corresponding to Y or N answer respectively.
64
+ def prompt_yn(question, args = {})
65
+ args[:valid_answers] = [/^[yn]/i]
66
+ /^n/i.match(prompt(question, args)).nil?
67
+ end
68
+
69
+ # Public: Wrap Console#prompt, disabling local echo of user input.
70
+ #
71
+ # question - Query to be presented to the user.
72
+ # options - Hash containing arguments defining acceptable responses.
73
+ # (default: {}):
74
+ # :default_answer - String containing the default answer. If
75
+ # this is nil, a non-empty answer MUST be
76
+ # given.
77
+ # :allow_empty - Whether or not to allow empty responses.
78
+ # Unless explicitly allowed, empty answers
79
+ # will be rejected.
80
+ # :valid_answers - An Array containing all valid responses. If
81
+ # this is empty, any answer will be accepted
82
+ # (unless empty answers are disallowed as
83
+ # specified above). Valid responses may be
84
+ # any class with a match method such as
85
+ # Strings or Regexps.
86
+ #
87
+ # Returns a String containing the answer provided by the user.
88
+ def secure_prompt(question, args = {})
89
+ IO.console.echo = false
90
+ prompt(question, args)
91
+ ensure
92
+ IO.console.echo = true
93
+ puts
94
+ end
95
+
96
+ # Public: Wrap Console#prompt, specifically targeting filesystem paths.
97
+ #
98
+ # question - Query to be presented to the user.
99
+ # options - Hash containing arguments defining acceptable responses.
100
+ # (default: {}):
101
+ # :default_answer - String containing the default answer. If
102
+ # this is nil, a non-empty answer MUST be
103
+ # given.
104
+ # :allow_empty - Whether or not to allow empty responses.
105
+ # Unless explicitly allowed, empty answers
106
+ # will be rejected.
107
+ # :valid_answers - An Array containing all valid responses. If
108
+ # this is empty, any answer will be accepted
109
+ # (unless empty answers are disallowed as
110
+ # specified above). Valid responses may be
111
+ # any class with a match method such as
112
+ # Strings or Regexps.
113
+ #
114
+ # Returns a String containing the answer provided by the user.
115
+ def prompt_filesystem(question, args = {})
116
+ fs_lambda = lambda { |d| Dir[d + '*'].grep(/^#{Regexp.escape(d)}/) }
117
+ old_append = Readline.completion_append_character
118
+
119
+ Readline.completion_append_character = ''
120
+ response = prompt(question, args, [], fs_lambda)
121
+ Readline.completion_append_character = old_append
122
+
123
+ response
124
+ end
125
+
126
+ # Public: Render a "spinner" on the command line and yield to a block,
127
+ # reporting success if nothing is raised, or else reporting failure.
128
+ #
129
+ # message - Message to be displayed describing the task being evaluated.
130
+ # block - Block to be yielded to determine pass or fail.
131
+ #
132
+ # Returns the result of the yielded block if successful.
133
+ # Raises whatever is raised inside the yielded block.
134
+ def spinner(message, &block)
135
+ success = nil
136
+ result = nil
137
+
138
+ pre = "\r#{bold}#{white} [#{reset}"
139
+ post = "#{bold}#{white}] #{reset}#{message}"
140
+ pre_ok = "\r#{bold}#{white} [#{green} ok "
141
+ pre_fail = "\r#{bold}#{white} [#{red}fail"
142
+
143
+ thread = Thread.new do
144
+ step = 0
145
+ spin = [" ", ". ", ".. ", "... ", "....", " ...", " ..", " ."]
146
+ while success.nil?
147
+ print "#{pre}#{spin[step % 8]}#{post}"
148
+ step += 1
149
+ sleep 0.5
150
+ end
151
+
152
+ if success
153
+ print "#{pre_ok}#{post}\n"
154
+ else
155
+ print "#{pre_fail}#{post}\n"
156
+ end
157
+ end
158
+
159
+ begin
160
+ result = yield
161
+ success = true
162
+ thread.join
163
+ return result
164
+ rescue
165
+ success = false
166
+ thread.join
167
+ raise
168
+ end
169
+ end
170
+
171
+ # Public: Generate a formatted, printable table.
172
+ #
173
+ # rows - An Array containing Hashes which contain desired options to
174
+ # display. (e.g. [{"col1" => "a", "col2" => "b"}])
175
+ # labels - Hash containing key-value pairs to label each key in options.
176
+ # (default: nil)
177
+ #
178
+ # Returns a String containing the grid.
179
+ # Raises ArgumentError if anything but an Array is passed as rows.
180
+ def build_grid(rows, labels = nil)
181
+ if labels.nil?
182
+ labels = rows[0].keys.reduce({}) { |c,e| c.merge({e => e}) }
183
+ end
184
+
185
+ keys = labels.keys
186
+
187
+ max_width = labels.reduce({}) do |c,e|
188
+ c.merge({e[0]=> ([labels] + rows).map { |r| r[e[0]].length }.max})
189
+ end
190
+
191
+ grid_rule = max_width.reduce('+') do |c,e|
192
+ c + ('-' * (e[1] + 2)) + '+'
193
+ end
194
+ grid_rule << "\n"
195
+
196
+ grid = grid_rule.dup
197
+ grid << keys.reduce('|') do |c,e|
198
+ c + " #{bold}% #{max_width[e]}s#{reset} |" % labels[e]
199
+ end
200
+ grid << "\n"
201
+
202
+ grid << rows.reduce(grid_rule) do |c,e|
203
+ content = keys.reduce('') do |s,k|
204
+ s + " % #{max_width[k]}s |" % e[k]
205
+ end
206
+ c + "|#{content}\n"
207
+ end
208
+ grid << grid_rule
209
+ end
210
+
211
+ private
212
+
213
+ # Internal: Promp user for input via Readline, handling automatic setting
214
+ # and restoring of Readline's autocompletion proc, avoiding trampling over
215
+ # other uses.
216
+ #
217
+ # question - String containing a question to be displayed to the user.
218
+ # wordlist - Array containing Strings which are considered valid answers
219
+ # for autocompletion.
220
+ # block - Lambda which will override the default autocompletion lambda
221
+ # as defined in autocomplete_lambda if present. (default: nil)
222
+ #
223
+ # Returns a String.
224
+ def user_prompt(question, wordlist, block = nil)
225
+ block = autocomplete_lambda(wordlist) if block.nil?
226
+ old_completion_proc = Readline.completion_proc
227
+
228
+ Readline.completion_proc = block
229
+ response = Readline.readline(question)
230
+ Readline.completion_proc = old_completion_proc
231
+
232
+ response.to_s
233
+ end
234
+
235
+ # Internal: Generate a lambda which retuns an array of autocompletion
236
+ # candidates.
237
+ #
238
+ # wordlist - Array containing Strings which are considered valid answers
239
+ # for autocompletion.
240
+ #
241
+ # Returns a lambda which returns an Array.
242
+ def autocomplete_lambda(wordlist)
243
+ lambda { |s| wordlist.grep(/^#{Regexp.escape(s)}/) }
244
+ end
245
+
246
+ # Internal: Generate Regexps for any String in an array to be used in order
247
+ # to match against user input. Strings are expected to indicate exact
248
+ # matches desired.
249
+ #
250
+ # valid_answers - Array containing Objects against which user input may be
251
+ # matched.
252
+ #
253
+ # Returns an Array of Regexps.
254
+ def regexify_answers(valid_answers)
255
+ valid_answers.to_a.map do |answer|
256
+ if answer.is_a?(String)
257
+ Regexp.new("^#{Regexp.escape(answer)}$")
258
+ else
259
+ answer
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
metadata CHANGED
@@ -1,24 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: console-glitter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
- - Chris Wuest
7
+ - Tina Wuest
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-05 00:00:00.000000000 Z
11
+ date: 2015-02-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Tools for building nice looking CLI applications
14
- email: chris@chriswuest.com
14
+ email: tina@wuest.me
15
15
  executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - lib/console-glitter.rb
20
20
  - lib/console-glitter/ansi.rb
21
- - lib/console-glitter/version.rb
21
+ - lib/console-glitter/cursor.rb
22
+ - lib/console-glitter/screen.rb
23
+ - lib/console-glitter/ui.rb
22
24
  homepage: https://gitlab.com/wuest/console-glitter
23
25
  licenses:
24
26
  - MIT
@@ -39,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
41
  version: '0'
40
42
  requirements: []
41
43
  rubyforge_project:
42
- rubygems_version: 2.2.2
44
+ rubygems_version: 2.4.5
43
45
  signing_key:
44
46
  specification_version: 4
45
47
  summary: Tools for prettier CLI apps
@@ -1,3 +0,0 @@
1
- module ConsoleGlitter
2
- VERSION = '0.2.1'
3
- end