console-glitter 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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