prompter 0.1.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.
- data/.gitignore +24 -0
- data/LICENSE +20 -0
- data/README.rdoc +54 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/examples/wizard.rb +43 -0
- data/lib/prompter.rb +314 -0
- data/prompter.gemspec +27 -0
- data/test/ask.irt +38 -0
- data/test/choose.irt +29 -0
- data/test/irt_helper.rb +2 -0
- data/test/say.irt +90 -0
- data/test/yes_no.irt +10 -0
- metadata +128 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Domizio Demichelis
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
= Prompter
|
2
|
+
|
3
|
+
Makes your prompts easier to build and prettier to look.
|
4
|
+
|
5
|
+
== Synopsis
|
6
|
+
|
7
|
+
require 'prompter'
|
8
|
+
|
9
|
+
Prompter.new(:prefix => '** ') do |p|
|
10
|
+
p.say_notice 'You can avoid to extend your class with Prompter::Methods'
|
11
|
+
end
|
12
|
+
|
13
|
+
# or
|
14
|
+
|
15
|
+
prompter = Prompter.new
|
16
|
+
|
17
|
+
# or you can also extend your modules with:
|
18
|
+
|
19
|
+
extend Prompter::Methods
|
20
|
+
self.prefix = ' '
|
21
|
+
say 'This part uses imported method from Prompter::Methods'
|
22
|
+
|
23
|
+
ask 'Type some input:' do |input|
|
24
|
+
yes_no? "Do you want it or not?" do |yes|
|
25
|
+
if yes
|
26
|
+
say("#{input} is ok")
|
27
|
+
else
|
28
|
+
say_warning("#{input} may be dangerous")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# or
|
34
|
+
|
35
|
+
name = ask('write your name:')
|
36
|
+
yes? "are you sure?" { do_something_if_yes }
|
37
|
+
|
38
|
+
(see also the file "examples/wizard.rb")
|
39
|
+
|
40
|
+
== General Notes
|
41
|
+
|
42
|
+
Methods are callable directly on the Prompter class, on a Prompter object, included in your class as instance methods or class methods.
|
43
|
+
|
44
|
+
Some methods yield the block with the returned value, so you can use them as an easy way to construct hierarchical wizards.
|
45
|
+
|
46
|
+
Colorer styles are passable as an option to the called method, and can be redefined entirely by calling Colorer.def_*_styles.
|
47
|
+
|
48
|
+
== Documentation and Example
|
49
|
+
|
50
|
+
All the methods are yard-documented, and there is and 'example/wizard.rb' that could be useful.
|
51
|
+
|
52
|
+
== Copyright
|
53
|
+
|
54
|
+
Copyright (c) 2010 Domizio Demichelis. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
name = 'prompter'
|
2
|
+
|
3
|
+
def ensure_clean(action, force=false)
|
4
|
+
if !force && ! `git status -s`.empty?
|
5
|
+
puts <<-EOS.gsub(/^ {6}/, '')
|
6
|
+
Rake task aborted: the working tree is dirty!
|
7
|
+
If you know what you are doing you can use \`rake #{action}[force]\`"
|
8
|
+
EOS
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Install the gem"
|
14
|
+
task :install, :force do |t, args|
|
15
|
+
ensure_clean(:install, args.force)
|
16
|
+
orig_version = version = File.read('VERSION').strip
|
17
|
+
begin
|
18
|
+
commit_id = `git log -1 --format="%h" HEAD`.strip
|
19
|
+
version = "#{orig_version}.#{commit_id}"
|
20
|
+
File.open('VERSION', 'w') {|f| f.puts version }
|
21
|
+
gem_name = "#{name}-#{version}.gem"
|
22
|
+
sh %(gem build #{name}.gemspec)
|
23
|
+
sh %(gem install #{gem_name} --local)
|
24
|
+
puts <<-EOS.gsub(/^ {6}/, '')
|
25
|
+
|
26
|
+
*******************************************************************************
|
27
|
+
* NOTICE *
|
28
|
+
*******************************************************************************
|
29
|
+
* The version id of locally installed gems is comparable to a --pre version: *
|
30
|
+
* i.e. it is alphabetically ordered (not numerically ordered), besides it *
|
31
|
+
* includes the sah1 commit id which is not aphabetically ordered, so be sure *
|
32
|
+
* your application picks the version you really intend to use *
|
33
|
+
*******************************************************************************
|
34
|
+
|
35
|
+
EOS
|
36
|
+
ensure
|
37
|
+
remove_entry_secure gem_name, true
|
38
|
+
File.open('VERSION', 'w') {|f| f.puts orig_version }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc %(Remove all the "#{name}" installed gems and executables and install this version)
|
43
|
+
task :clean_install, :force do |t, args|
|
44
|
+
ensure_clean(:install, args.force)
|
45
|
+
sh %(gem uninstall #{name} --all --ignore-dependencies --executables)
|
46
|
+
Rake::Task['install'].invoke(args.force)
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Push the gem to rubygems.org"
|
50
|
+
task :push, :force do |t, args|
|
51
|
+
begin
|
52
|
+
ensure_clean(:install, args.force)
|
53
|
+
version = File.read('VERSION').strip
|
54
|
+
gem_name = "#{name}-#{version}.gem"
|
55
|
+
sh %(gem build #{name}.gemspec)
|
56
|
+
sh %(gem push #{gem_name})
|
57
|
+
ensure
|
58
|
+
remove_entry_secure gem_name, true
|
59
|
+
end
|
60
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/examples/wizard.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'prompter'
|
4
|
+
|
5
|
+
|
6
|
+
Prompter.new(:prefix => '--- ') do |p|
|
7
|
+
p.say_notice 'You can avoid to extend your class with Prompter::Methods'
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
extend Prompter::Methods
|
13
|
+
|
14
|
+
self.prefix = '*** '
|
15
|
+
|
16
|
+
say 'This part uses imported method from Prompter::Methods'
|
17
|
+
|
18
|
+
yes_no?( 'Do you want to go on or not?') do |yes|
|
19
|
+
if yes
|
20
|
+
say 'Very good from you!'
|
21
|
+
choose('Do you want a, b or c?', /^a|b|c$/i, :hint => '[a|<enter>=b|c]', :default => 'b') do |choice|
|
22
|
+
case choice
|
23
|
+
when 'a'
|
24
|
+
say 'You have chosen A, but that is not good. END'
|
25
|
+
when 'b'
|
26
|
+
say 'You have chosen B. That is a good option!'
|
27
|
+
ask('Which name do you wanna have?', :hint => '[<enter>=user]', :default => 'user') do |name|
|
28
|
+
say "You have chosen '#{name}' and that's ok!"
|
29
|
+
end
|
30
|
+
when 'c'
|
31
|
+
say 'You have chosen C. Game over.'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
list = %w[alfa beta gamma delta]
|
36
|
+
choose_index('You should choose among this items:', list, :echo => false) do |index|
|
37
|
+
say "You have chosen '#{list[index]}', at index #{index}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
say '', :prefix => ''
|
42
|
+
say_notice 'The example is over'
|
43
|
+
|
data/lib/prompter.rb
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
require 'colorer'
|
2
|
+
|
3
|
+
# @author Domizio Demichelis
|
4
|
+
#
|
5
|
+
class Prompter
|
6
|
+
|
7
|
+
VERSION = File.read(File.expand_path('../../VERSION', __FILE__)).strip
|
8
|
+
|
9
|
+
Colorer.def_custom_styles :say_style => nil,
|
10
|
+
:say_echo_style => nil,
|
11
|
+
:say_notice_style => :yellow,
|
12
|
+
:say_warning_style => :red,
|
13
|
+
:ask_style => :magenta,
|
14
|
+
:hint_style => :green
|
15
|
+
|
16
|
+
# Standard constructor, also accepts a block
|
17
|
+
#
|
18
|
+
# @param [Hash] opts The options to create a Prompter object.
|
19
|
+
# @option opts [String] :prefix The prefix string
|
20
|
+
# @option opts [Boolean] :echo when true adds a line with the result
|
21
|
+
#
|
22
|
+
def initialize(opts={})
|
23
|
+
@prefix = opts[:prefix]
|
24
|
+
@echo = opts[:echo] || true
|
25
|
+
yield self if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
module Methods
|
29
|
+
|
30
|
+
attr_writer :echo, :prefix
|
31
|
+
|
32
|
+
# The echo instance option (true by default)
|
33
|
+
def echo
|
34
|
+
@echo ||= true
|
35
|
+
end
|
36
|
+
|
37
|
+
def prefix
|
38
|
+
@prefix ||= ''
|
39
|
+
end
|
40
|
+
|
41
|
+
# Shows a message
|
42
|
+
#
|
43
|
+
# @param [String] message Your message
|
44
|
+
#
|
45
|
+
# @param [Hash] opts the standard opts to pass along
|
46
|
+
# @option opts [String] :default The default choice when the user hits <enter> instead of any content
|
47
|
+
# @option opts [String] :prefix The prefix string
|
48
|
+
# @option opts [Boolean] :echo Adds a line showing the input (=> input)
|
49
|
+
# @option opts [String] :hint A string to show the available choices
|
50
|
+
# @option opts [Symbol] :style The Colorer style name to use for this prompt
|
51
|
+
# @option opts [Boolean] :force_new_line Forces the addition of a new line (otherwise determined by the end of the prompt string)
|
52
|
+
#
|
53
|
+
def say(message="", opts={})
|
54
|
+
message = message.to_s
|
55
|
+
opts = { :force_new_line => (message !~ /( |\t)$/),
|
56
|
+
:style => :say_style,
|
57
|
+
:prefix => prefix }.merge opts
|
58
|
+
message = message.send opts[:style] unless opts[:style].nil?
|
59
|
+
message = (opts[:prefix]||'') + message
|
60
|
+
$stdout.send((opts[:force_new_line] ? :puts : :print), message)
|
61
|
+
$stdout.flush
|
62
|
+
end
|
63
|
+
|
64
|
+
# Shows a colored message. It uses :say passing the :say_notice_style :style option
|
65
|
+
#
|
66
|
+
# @see #say
|
67
|
+
#
|
68
|
+
def say_notice(message="", opts={})
|
69
|
+
opts = { :style => :say_notice_style }.merge opts
|
70
|
+
say message, opts
|
71
|
+
end
|
72
|
+
|
73
|
+
# Shows a red colored message. It uses :say passing the :say_warning_style :style option,
|
74
|
+
# besides it adds an audible bell character to the message. Pass :mute => true to mute.
|
75
|
+
#
|
76
|
+
# @see #say
|
77
|
+
#
|
78
|
+
def say_warning(message="", opts={})
|
79
|
+
opts = { :style => :say_warning_style }.merge opts
|
80
|
+
message = "\a" + message unless opts[:mute]
|
81
|
+
say message, opts
|
82
|
+
end
|
83
|
+
|
84
|
+
# Asks for an input
|
85
|
+
#
|
86
|
+
# @param [String] prompt Your question
|
87
|
+
#
|
88
|
+
# @param [Hash] opts the standard opts to pass along (see #say
|
89
|
+
#
|
90
|
+
# for block {|input| ... }
|
91
|
+
# @yield [input] yields the block with the user input
|
92
|
+
# @yieldparam [String] input user input
|
93
|
+
# @return [String] The user input
|
94
|
+
#
|
95
|
+
def ask(prompt, opts={})
|
96
|
+
opts = { :style => :ask_style,
|
97
|
+
:hint => '',
|
98
|
+
:default => '' }.merge opts
|
99
|
+
force_new_line = !!opts.delete(:force_new_line) unless opts[:hint].empty?
|
100
|
+
prompt = prompt + ' ' unless opts[:force_new_line]
|
101
|
+
say prompt, opts
|
102
|
+
say_hint(opts[:hint], opts.merge({:force_new_line => !!force_new_line})) unless opts[:hint].empty?
|
103
|
+
input = $stdin.gets || '' # multilines ended at start generate a nil
|
104
|
+
input.strip!
|
105
|
+
input = opts[:default].to_s if input.empty?
|
106
|
+
opts.delete(:style) # :style is not passed
|
107
|
+
say_echo input, opts
|
108
|
+
block_given? ? yield(input) : input
|
109
|
+
end
|
110
|
+
|
111
|
+
# Asks for a multiline input
|
112
|
+
#
|
113
|
+
# @param [String] prompt Your question
|
114
|
+
#
|
115
|
+
# @param [Hash] opts the standard opts to pass along (see #say, plus the followings)
|
116
|
+
# @option opts [String] :input_end Used to end the input (default nil: requires ^D to end the input)
|
117
|
+
# @option opts [Boolean] :force_new_line Default to true
|
118
|
+
#
|
119
|
+
# for block {|input| ... }
|
120
|
+
# @yield [input] yields the block with the user input
|
121
|
+
# @yieldparam [String] input user input
|
122
|
+
# @return [String] The user input (with the eventual :input_end line removed)
|
123
|
+
#
|
124
|
+
def ask_multiline(prompt, opts={})
|
125
|
+
opts = { :input_end => nil,
|
126
|
+
:force_new_line => true,
|
127
|
+
:hint => %([end the input by typing "#{opts[:input_end] || '^D'}" in a new line]) }.merge opts
|
128
|
+
old_separator = $/
|
129
|
+
$/ = opts.delete(:input_end)
|
130
|
+
input = ask(prompt, opts).sub(/\n#{$/}$/, '')
|
131
|
+
$/ = old_separator
|
132
|
+
block_given? ? yield(input) : input
|
133
|
+
end
|
134
|
+
|
135
|
+
# Chooses among different choices
|
136
|
+
#
|
137
|
+
# @param [String] prompt Your question
|
138
|
+
# @param [RegExp, Proc] validation Validates the input
|
139
|
+
#
|
140
|
+
# @param [Hash] opts the standard opts to pass along (see #say)
|
141
|
+
#
|
142
|
+
# @yield [input] yields the block with the user-chosen input
|
143
|
+
# @yieldparam [String] input The user input
|
144
|
+
# @return [String] The user-chosen input
|
145
|
+
#
|
146
|
+
def choose(prompt, validation, opts={}, &block)
|
147
|
+
choice = ask prompt, opts
|
148
|
+
if valid_choice?(validation, choice)
|
149
|
+
block_given? ? yield(choice) : choice
|
150
|
+
else
|
151
|
+
say_warning 'Unknown choice!'
|
152
|
+
choose(prompt, validation, opts, &block)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Chooses many among different choices
|
157
|
+
#
|
158
|
+
# @param [String] prompt Your question
|
159
|
+
# @param [RegExp, Proc] validation Validates the input
|
160
|
+
#
|
161
|
+
# @param [Hash] opts the standard opts to pass along (see #say, plus the followings)
|
162
|
+
# @option opts [String, RegExp] :split Used to split the input (see String#split)
|
163
|
+
#
|
164
|
+
# @yield [input] yields the block with the user-chosen array of input
|
165
|
+
# @yieldparam [Array] input The user array of input
|
166
|
+
# @return [Array] The user-chosen array of input
|
167
|
+
#
|
168
|
+
def choose_many(prompt, validation, opts={}, &block)
|
169
|
+
choices = ask(prompt, opts).split(opts[:split])
|
170
|
+
if choices.all? {|c| valid_choice?(validation, c)}
|
171
|
+
block_given? ? yield(choices) : choices
|
172
|
+
else
|
173
|
+
say_warning 'One or more choices are unknown!'
|
174
|
+
choose_many(prompt, validation, opts, &block)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
# Asks a yes/no question and yields the block with the resulting Boolean
|
180
|
+
#
|
181
|
+
# @param [Hash] opts the standard opts to pass along (see #say)
|
182
|
+
#
|
183
|
+
# @yield yields the block always with a Boolean
|
184
|
+
# @yieldparam [Boolean]
|
185
|
+
# @return [Boolean] true for 'y' and false for 'n'
|
186
|
+
#
|
187
|
+
def yes_no?(prompt, opts={})
|
188
|
+
opts = { :hint => '[y|n]' }.merge opts
|
189
|
+
choice = choose(prompt, /^y|n$/i, opts)
|
190
|
+
result = choice.match(/^y$/i) ? true : false
|
191
|
+
block_given? ? yield(result) : result
|
192
|
+
end
|
193
|
+
|
194
|
+
# Asks a yes/no question and yields the block only when the answer is yes
|
195
|
+
#
|
196
|
+
# @param [String] prompt Your question
|
197
|
+
#
|
198
|
+
# @param [Hash] opts the standard opts to pass along (see #say)
|
199
|
+
#
|
200
|
+
# @yield yields the block when the answer is 'y'
|
201
|
+
# @return [Boolean] true for 'y' and false for 'n'
|
202
|
+
#
|
203
|
+
def yes?(prompt, opts={})
|
204
|
+
result = yes_no? prompt, opts
|
205
|
+
(block_given? && result) ? yield : result
|
206
|
+
end
|
207
|
+
|
208
|
+
# Asks a yes/no question and yields the block only when the answer is no
|
209
|
+
#
|
210
|
+
# @param [String] prompt Your question
|
211
|
+
#
|
212
|
+
# @param [Hash] opts the standard opts to pass along (see #say)
|
213
|
+
#
|
214
|
+
# @yield yields the block when the answer is no
|
215
|
+
# @return [Boolean] true for 'y' and false for 'n'
|
216
|
+
#
|
217
|
+
def no?(prompt, opts={})
|
218
|
+
result = yes_no? prompt, opts
|
219
|
+
(block_given? && !result) ? yield : result
|
220
|
+
end
|
221
|
+
|
222
|
+
# Chooses among different choices in an indexed list
|
223
|
+
#
|
224
|
+
# @param [String] prompt Your question
|
225
|
+
# @param [Array] list The list of choices
|
226
|
+
#
|
227
|
+
# @param [Hash] opts the standard opts to pass along (see #say)
|
228
|
+
#
|
229
|
+
# @yield yields the block with the user-chosen input
|
230
|
+
# @yieldparam [Integer] index The index number
|
231
|
+
# @return [Integer] The index number from the list referring to the user-chosen input
|
232
|
+
#
|
233
|
+
def choose_index(prompt, list, opts={})
|
234
|
+
opts = list_choices(prompt, list, opts)
|
235
|
+
choice = choose ">", lambda{|v| (1..list.size).map(&:to_s).include?(v) }, opts
|
236
|
+
index = choice.to_i-1
|
237
|
+
block_given? ? yield(index) : index
|
238
|
+
end
|
239
|
+
|
240
|
+
# Chooses many among different choices in an indexed list
|
241
|
+
#
|
242
|
+
# @param [String] prompt Your question
|
243
|
+
# @param [Array] list The list of choices
|
244
|
+
#
|
245
|
+
# @param [Hash] opts the standard opts to pass along (see #say, plus the followings)
|
246
|
+
# @option opts [String, RegExp] :split Used to split the input (see String#split)
|
247
|
+
#
|
248
|
+
# @yield [input] yields the block with the user-chosen array of input
|
249
|
+
# @yieldparam [Array] indexes The user-chosen array of indexes
|
250
|
+
# @return [Array] The user-chosen array of indexes
|
251
|
+
#
|
252
|
+
def choose_many_index(prompt, list, opts={})
|
253
|
+
opts = list_choices(prompt, list, opts, true)
|
254
|
+
choices = choose_many ">", lambda{|v| (1..list.size).map(&:to_s).include?(v) }, opts
|
255
|
+
indexes = choices.map {|i| i.to_i-1 }
|
256
|
+
block_given? ? yield(indexes) : indexes
|
257
|
+
end
|
258
|
+
|
259
|
+
protected
|
260
|
+
|
261
|
+
# used internally to show a feedback of the input
|
262
|
+
def say_echo(result, opts={})
|
263
|
+
opts = { :style => :say_echo_style,
|
264
|
+
:echo => echo,
|
265
|
+
:prefix => ' ' * prefix.to_s.size }.merge opts
|
266
|
+
say( ('=> ' + result.inspect), opts ) if opts[:echo]
|
267
|
+
result
|
268
|
+
end
|
269
|
+
|
270
|
+
# used internally to show the hints
|
271
|
+
def say_hint(hint, opts={})
|
272
|
+
return if hint.empty?
|
273
|
+
opts.merge!( { :style => :hint_style} ) # hint is always :hint_style
|
274
|
+
opts = {:prefix => '' }.merge opts
|
275
|
+
hint = hint + ' ' unless opts[:force_new_line]
|
276
|
+
say hint, opts
|
277
|
+
end
|
278
|
+
|
279
|
+
private
|
280
|
+
|
281
|
+
def valid_choice?(validation, choice)
|
282
|
+
(validation.is_a?(Regexp) && validation.match(choice)) ||
|
283
|
+
(validation.is_a?(Proc) && validation.call(choice))
|
284
|
+
end
|
285
|
+
|
286
|
+
def list_choices(prompt, list, opts={}, many=false)
|
287
|
+
hint = many ? "[choose one or more in range 1..#{list.size} (#{opts[:split].nil? ? '<space>' : opts[:split].inspect} splitted)]" :
|
288
|
+
"[choose one in range 1..#{list.size}]"
|
289
|
+
opts = { :style => :ask_style,
|
290
|
+
:hint => hint }.merge opts
|
291
|
+
say prompt, opts
|
292
|
+
list.each_with_index do |item, index|
|
293
|
+
say_hint (index+1).to_s.rjust(count_digits(list.size)) + '.', :prefix => ' ' * (prefix.to_s.length)
|
294
|
+
say item, :prefix => ''
|
295
|
+
end
|
296
|
+
opts
|
297
|
+
end
|
298
|
+
|
299
|
+
def count_digits(number)
|
300
|
+
num = number.abs
|
301
|
+
count = 0
|
302
|
+
while num >= 1
|
303
|
+
num = num / 10
|
304
|
+
count += 1
|
305
|
+
end
|
306
|
+
count
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
extend Methods
|
312
|
+
include Methods
|
313
|
+
|
314
|
+
end
|
data/prompter.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
name = File.basename( __FILE__, '.gemspec' )
|
2
|
+
version = File.read(File.expand_path('../VERSION', __FILE__)).strip
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
|
7
|
+
s.authors = ["Domizio Demichelis"]
|
8
|
+
s.email = 'dd.nexus@gmail.com'
|
9
|
+
s.homepage = "http://github.com/ddnexus/#{name}"
|
10
|
+
s.summary = 'Makes your prompts easier to build and prettier to look'
|
11
|
+
s.description = 'A few helpers to create colored, multistep and hierarchical wizards'
|
12
|
+
|
13
|
+
s.add_runtime_dependency('colorer', [">= 0.7.0"])
|
14
|
+
s.add_runtime_dependency('yard', [">= 0.6.3"])
|
15
|
+
s.add_development_dependency('irt', [">= 1.0.9"])
|
16
|
+
|
17
|
+
s.files = `git ls-files -z`.split("\0")
|
18
|
+
|
19
|
+
s.name = name
|
20
|
+
s.version = version
|
21
|
+
s.date = Date.today.to_s
|
22
|
+
|
23
|
+
s.required_rubygems_version = ">= 1.3.6"
|
24
|
+
s.has_rdoc = 'yard'
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
|
27
|
+
end
|
data/test/ask.irt
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
extend Prompter::Methods
|
2
|
+
|
3
|
+
desc "intaractive ask"
|
4
|
+
say "You should see the next line in :ask_style"
|
5
|
+
ask "Please, write the word 'pippo':"
|
6
|
+
_eql?( "pippo" )
|
7
|
+
|
8
|
+
Colorer.def_basic_styles :red, true
|
9
|
+
desc "intaractive default ask"
|
10
|
+
say "You should see the next line in :red, and padded with 4 spaces"
|
11
|
+
ask "Please, type <enter>:", :default => 'default', :style => :red, :prefix => ' '
|
12
|
+
_eql?( "default" )
|
13
|
+
|
14
|
+
desc "ask_multiline default"
|
15
|
+
say "Please, enter some multiline data"
|
16
|
+
ml = ask_multiline "Add your data below..."
|
17
|
+
_eql?( ml )
|
18
|
+
say "<START-INPUT>#{ml}<END-INPUT>"
|
19
|
+
|
20
|
+
desc "ask_multiline with <END> input_end, no hints"
|
21
|
+
say "Please, enter some multiline data"
|
22
|
+
ml = ask_multiline "Add your data below (no hints)... and type <END> to end the input",
|
23
|
+
:input_end => '<END>',
|
24
|
+
:hint => ''
|
25
|
+
_eql?( ml )
|
26
|
+
say "<START-INPUT>#{ml}<END-INPUT>"
|
27
|
+
|
28
|
+
desc "ask_multiline with <END> input_end and hints"
|
29
|
+
say "Please, enter some multiline data"
|
30
|
+
ml = ask_multiline "Add your data below...",
|
31
|
+
:hint => '[end the input by typing <END> in a new line]',
|
32
|
+
:input_end => '<END>'
|
33
|
+
_eql?( ml )
|
34
|
+
say "<START-INPUT>#{ml}<END-INPUT>"
|
35
|
+
|
36
|
+
|
37
|
+
irt
|
38
|
+
|
data/test/choose.irt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
extend Prompter::Methods
|
2
|
+
|
3
|
+
desc "simple choice with lambda"
|
4
|
+
choose "Choose from a, b or c (choose something different from a|b|c first, then choose 'b')", lambda{|v| v.match(/^(a|b|c)$/i)}, :hint => "[a|b|c]"
|
5
|
+
_eql?( "b" )
|
6
|
+
|
7
|
+
desc "simple choice with default and RE"
|
8
|
+
choose "Choose from [a|b|c] (type <enter> for 'b')", /^(a|b|c)$/i, :default => "b"
|
9
|
+
_eql?( "b" )
|
10
|
+
|
11
|
+
desc "choose index"
|
12
|
+
choose_index "Chose one of... (type '3')", %w[alpha beta gamma delta]
|
13
|
+
_eql?( 2 )
|
14
|
+
|
15
|
+
desc "chose many"
|
16
|
+
choose_many "Chose one or more among [a|b|c] (type 'b c')", /^(a|b|c)$/i
|
17
|
+
_eql?( %w[b c] )
|
18
|
+
|
19
|
+
desc "chose many different split"
|
20
|
+
choose_many "Chose one or more among [a b c] (type 'b;c')", /^(a|b|c)$/i, :split => ';'
|
21
|
+
_eql?( %w[b c] )
|
22
|
+
|
23
|
+
desc "chose many index"
|
24
|
+
choose_many_index "Chose one or more... (type '2 3')", %w[alpha beta gamma delta]
|
25
|
+
_eql?( [1, 2] )
|
26
|
+
|
27
|
+
desc "chose many index different split RE"
|
28
|
+
choose_many_index "Chose one or more... (type '2;3')", %w[alpha beta gamma delta], :split => /;/
|
29
|
+
_eql?( [1, 2] )
|
data/test/irt_helper.rb
ADDED
data/test/say.irt
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
desc "say no options"
|
3
|
+
capture {Prompter.say "test"}
|
4
|
+
_eql?( "test\n" )
|
5
|
+
|
6
|
+
desc "say with final space"
|
7
|
+
capture {Prompter.say "test "}
|
8
|
+
_eql?( "test " )
|
9
|
+
|
10
|
+
desc "say without force_new_line"
|
11
|
+
capture {Prompter.say "test", :force_new_line => false}
|
12
|
+
_eql?( "test" )
|
13
|
+
|
14
|
+
desc "prefix default"
|
15
|
+
Prompter.prefix = "--- "
|
16
|
+
capture { Prompter.say "prefix test" }
|
17
|
+
_eql? <<EOS
|
18
|
+
--- prefix test
|
19
|
+
EOS
|
20
|
+
|
21
|
+
desc "prefix override"
|
22
|
+
capture { Prompter.say "prefix test", :prefix => ""}
|
23
|
+
_eql? <<EOS
|
24
|
+
prefix test
|
25
|
+
EOS
|
26
|
+
|
27
|
+
desc "style override"
|
28
|
+
Colorer.def_basic_styles :magenta
|
29
|
+
capture { Prompter.say "style override", :style => :magenta }
|
30
|
+
_eql?( "--- \e[0m\e[35mstyle override\e[0m\n" )
|
31
|
+
|
32
|
+
class ExtendedByPrompter
|
33
|
+
extend Prompter::Methods
|
34
|
+
include Prompter::Methods
|
35
|
+
|
36
|
+
def self.something
|
37
|
+
say "something easy"
|
38
|
+
end
|
39
|
+
|
40
|
+
def something
|
41
|
+
say "something easy"
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.other_padded
|
45
|
+
say "something not padded", :prefix => ""
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
ExtendedByPrompter.prefix = "*** "
|
51
|
+
desc "prefix new default in extended class"
|
52
|
+
capture { ExtendedByPrompter.something }
|
53
|
+
_eql?( "*** something easy\n" )
|
54
|
+
|
55
|
+
desc "prefix override in extended class"
|
56
|
+
capture { ExtendedByPrompter.other_padded }
|
57
|
+
_eql? <<EOS
|
58
|
+
something not padded
|
59
|
+
EOS
|
60
|
+
|
61
|
+
obj = ExtendedByPrompter.new
|
62
|
+
desc "object prefix is default"
|
63
|
+
obj.prefix
|
64
|
+
_eql?( "" )
|
65
|
+
|
66
|
+
desc "object method included"
|
67
|
+
capture { obj.something }
|
68
|
+
_eql? <<EOS
|
69
|
+
something easy
|
70
|
+
EOS
|
71
|
+
|
72
|
+
desc "say no style"
|
73
|
+
capture { Prompter.say "no style", :style => nil }
|
74
|
+
_eql? <<EOS
|
75
|
+
--- no style
|
76
|
+
EOS
|
77
|
+
|
78
|
+
desc "alert sound with say_warning"
|
79
|
+
Prompter.say_warning "You should hear a sound with this message"
|
80
|
+
Prompter.yes_no? "Did you hear it?"
|
81
|
+
_eql?( true )
|
82
|
+
|
83
|
+
desc "silend say_warning"
|
84
|
+
Prompter.say_warning "This should be silent", :mute=>true
|
85
|
+
Prompter.yes_no? "Was it silent?"
|
86
|
+
_eql?( true )
|
87
|
+
|
88
|
+
desc "alert sound with yes/no question"
|
89
|
+
Prompter.yes_no? "\aThis is embedded in the question: did you hear it"
|
90
|
+
_eql?( true )
|
data/test/yes_no.irt
ADDED
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: prompter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Domizio Demichelis
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-27 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: colorer
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 7
|
33
|
+
- 0
|
34
|
+
version: 0.7.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: yard
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 1
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 6
|
49
|
+
- 3
|
50
|
+
version: 0.6.3
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: irt
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 5
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 0
|
65
|
+
- 9
|
66
|
+
version: 1.0.9
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
description: A few helpers to create colored, multistep and hierarchical wizards
|
70
|
+
email: dd.nexus@gmail.com
|
71
|
+
executables: []
|
72
|
+
|
73
|
+
extensions: []
|
74
|
+
|
75
|
+
extra_rdoc_files: []
|
76
|
+
|
77
|
+
files:
|
78
|
+
- .gitignore
|
79
|
+
- LICENSE
|
80
|
+
- README.rdoc
|
81
|
+
- Rakefile
|
82
|
+
- VERSION
|
83
|
+
- examples/wizard.rb
|
84
|
+
- lib/prompter.rb
|
85
|
+
- prompter.gemspec
|
86
|
+
- test/ask.irt
|
87
|
+
- test/choose.irt
|
88
|
+
- test/irt_helper.rb
|
89
|
+
- test/say.irt
|
90
|
+
- test/yes_no.irt
|
91
|
+
has_rdoc: yard
|
92
|
+
homepage: http://github.com/ddnexus/prompter
|
93
|
+
licenses: []
|
94
|
+
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
hash: 23
|
115
|
+
segments:
|
116
|
+
- 1
|
117
|
+
- 3
|
118
|
+
- 6
|
119
|
+
version: 1.3.6
|
120
|
+
requirements: []
|
121
|
+
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 1.3.7
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: Makes your prompts easier to build and prettier to look
|
127
|
+
test_files: []
|
128
|
+
|