easyprompt 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/easyprompt.rb +40 -14
- data/lib/easyprompt.rb~ +178 -0
- metadata +6 -4
data/lib/easyprompt.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'lafcadio'
|
2
|
-
|
3
1
|
# EasyPrompt is a utility for command-line scripts. It handles prompts and default values, and also provides a testing facility for mocking out the command-line user.
|
4
2
|
#
|
5
3
|
# For example, here's an irb session that illustrates what EasyPrompt does:
|
@@ -19,8 +17,13 @@ require 'lafcadio'
|
|
19
17
|
# => true
|
20
18
|
#
|
21
19
|
# In the first example, we ask for the user's first name and get "John" as the response. In the second example, we supply the default of "Doe", which the user chooses by just pressing the "Enter" key. In the third example, we supply the default of +true+, which the user chooses as well. We received the boolean value +true+ as opposed to the string "true" or "y", because we specified <tt>:boolean</tt> as the +response_class+.
|
20
|
+
#
|
21
|
+
# The Rubyforge project page can be found at http://rubyforge.org/projects/easyprompt/ .
|
22
|
+
|
23
|
+
require 'lafcadio'
|
24
|
+
|
22
25
|
class EasyPrompt
|
23
|
-
Version = '0.1.
|
26
|
+
Version = '0.1.1'
|
24
27
|
|
25
28
|
def initialize; @stdout = MockableStdout.get_mockable_stdout; end
|
26
29
|
|
@@ -30,15 +33,32 @@ class EasyPrompt
|
|
30
33
|
# response_class:: The sort of value that EasyPrompt#ask should return. Valid response classes are:
|
31
34
|
# [:string] This is the default.
|
32
35
|
# [:boolean] Values will be turned into <tt>true</tt> or <tt>false</tt> depending on whether the user enters "y" or "n".
|
36
|
+
# [:regexp] Values will be read as a regular expression source. If the user enters an invalid regexp source, EasyPrompt will re-prompt until a valid regexp source is entered.
|
33
37
|
def ask( msg, default = nil, response_class = :string )
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
success = false
|
39
|
+
last_error = nil
|
40
|
+
until success
|
41
|
+
success = true
|
42
|
+
full_msg = msg
|
43
|
+
full_msg = last_error + ' ' + full_msg if last_error
|
44
|
+
@stdout.write( prompt( full_msg, default, response_class ) + ' ' )
|
45
|
+
stdin = MockableStdin.get_mockable_stdin
|
46
|
+
response = stdin.gets
|
47
|
+
response.chomp!
|
48
|
+
if response == ''
|
49
|
+
response = default
|
50
|
+
else
|
51
|
+
if response_class == :boolean
|
52
|
+
response = response =~ /^y/i
|
53
|
+
elsif response_class == :regexp
|
54
|
+
begin
|
55
|
+
response = Regexp.new response
|
56
|
+
rescue RegexpError
|
57
|
+
success = false
|
58
|
+
last_error = "I'm sorry, but that was not a valid regular expression."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
42
62
|
end
|
43
63
|
response
|
44
64
|
end
|
@@ -48,6 +68,8 @@ class EasyPrompt
|
|
48
68
|
unless default.nil?
|
49
69
|
if response_class == :boolean
|
50
70
|
default_str = default ? 'y' : 'n'
|
71
|
+
elsif response_class == :regexp
|
72
|
+
default_str = default.source
|
51
73
|
else
|
52
74
|
default_str = default.to_s
|
53
75
|
end
|
@@ -101,7 +123,7 @@ class EasyPrompt
|
|
101
123
|
# is no match, an error will be raised. Use set_response to add a response.
|
102
124
|
def initialize
|
103
125
|
@responses = {}
|
104
|
-
|
126
|
+
flush
|
105
127
|
context = Lafcadio::Context.instance
|
106
128
|
@mock_stdin = StringIO.new
|
107
129
|
@mock_stdin.add_observer( self )
|
@@ -109,6 +131,10 @@ class EasyPrompt
|
|
109
131
|
@mock_stdout = StringIO.new
|
110
132
|
context.set_resource( MockableStdout, @mock_stdout )
|
111
133
|
end
|
134
|
+
|
135
|
+
# Clears out the running count of how many times each response has been
|
136
|
+
# used.
|
137
|
+
def flush; @match_count = Hash.new( 1 ); end
|
112
138
|
|
113
139
|
def match_regexp #:nodoc:
|
114
140
|
arg = @mock_stdout.string
|
@@ -170,9 +196,9 @@ class StringIO
|
|
170
196
|
|
171
197
|
alias_method :old_gets, :gets
|
172
198
|
|
173
|
-
def gets
|
199
|
+
def gets( separator = $/ )
|
174
200
|
changed
|
175
201
|
notify_observers
|
176
|
-
old_gets
|
202
|
+
old_gets( separator )
|
177
203
|
end
|
178
204
|
end
|
data/lib/easyprompt.rb~
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'lafcadio'
|
2
|
+
|
3
|
+
# EasyPrompt is a utility for command-line scripts. It handles prompts and default values, and also provides a testing facility for mocking out the command-line user.
|
4
|
+
#
|
5
|
+
# For example, here's an irb session that illustrates what EasyPrompt does:
|
6
|
+
#
|
7
|
+
# irb(main):001:0> require 'easyprompt'
|
8
|
+
# => true
|
9
|
+
# irb(main):002:0> prompt = EasyPrompt.new
|
10
|
+
# => #<EasyPrompt:0x5a42a0 @stdout=#<EasyPrompt::MockableStdout:0x5a3e04>>
|
11
|
+
# irb(main):003:0> fname = prompt.ask( "What's your first name?" )
|
12
|
+
# What's your first name? John
|
13
|
+
# => "John"
|
14
|
+
# irb(main):004:0> lname = prompt.ask( "What's your last name?", "Doe" )
|
15
|
+
# What's your last name? [Doe]
|
16
|
+
# => "Doe"
|
17
|
+
# irb(main):005:0> correct = prompt.ask( "Is your name #{ fname } #{ lname }?", true, :boolean )
|
18
|
+
# Is your name John Doe? [y]
|
19
|
+
# => true
|
20
|
+
#
|
21
|
+
# In the first example, we ask for the user's first name and get "John" as the response. In the second example, we supply the default of "Doe", which the user chooses by just pressing the "Enter" key. In the third example, we supply the default of +true+, which the user chooses as well. We received the boolean value +true+ as opposed to the string "true" or "y", because we specified <tt>:boolean</tt> as the +response_class+.
|
22
|
+
class EasyPrompt
|
23
|
+
Version = '0.1.0'
|
24
|
+
|
25
|
+
def initialize; @stdout = MockableStdout.get_mockable_stdout; end
|
26
|
+
|
27
|
+
# Asks the user for input.
|
28
|
+
# msg:: The prompt that tells the user what information to enter next.
|
29
|
+
# default:: The default value that will be returned if the user enters a newline (usually by pressing "Enter") without typing any information. The default value will be displayed in square brackets after +msg+.
|
30
|
+
# response_class:: The sort of value that EasyPrompt#ask should return. Valid response classes are:
|
31
|
+
# [:string] This is the default.
|
32
|
+
# [:boolean] Values will be turned into <tt>true</tt> or <tt>false</tt> depending on whether the user enters "y" or "n".
|
33
|
+
def ask( msg, default = nil, response_class = :string )
|
34
|
+
@stdout.write( prompt( msg, default, response_class ) + ' ' )
|
35
|
+
stdin = MockableStdin.get_mockable_stdin
|
36
|
+
response = stdin.gets
|
37
|
+
response.chomp!
|
38
|
+
if response == ''
|
39
|
+
response = default
|
40
|
+
else
|
41
|
+
response = response =~ /^y/i if response_class == :boolean
|
42
|
+
end
|
43
|
+
response
|
44
|
+
end
|
45
|
+
|
46
|
+
def prompt( msg, default = nil, response_class = :string ) #:nodoc:
|
47
|
+
prompt = msg
|
48
|
+
unless default.nil?
|
49
|
+
if response_class == :boolean
|
50
|
+
default_str = default ? 'y' : 'n'
|
51
|
+
else
|
52
|
+
default_str = default.to_s
|
53
|
+
end
|
54
|
+
prompt += " [#{ default_str }]"
|
55
|
+
end
|
56
|
+
prompt
|
57
|
+
end
|
58
|
+
|
59
|
+
# The MockCommandLineUser can be used if you want to unit-test the command-line part of a program.
|
60
|
+
#
|
61
|
+
# irb(main):001:0> require 'easyprompt'
|
62
|
+
# => true
|
63
|
+
# irb(main):002:0> user = EasyPrompt::MockCommandLineUser.new
|
64
|
+
# => #<EasyPrompt::MockCommandLineUser:0x595818 @match_count={},
|
65
|
+
# @mock_stdout=#<StringIO:0x5949b8>, @mock_stdin=#<StringIO:0x595750>,
|
66
|
+
# @responses={}>
|
67
|
+
# irb(main):003:0> prompt = EasyPrompt.new
|
68
|
+
# => #<EasyPrompt:0x57d894 @stdout=#<StringIO:0x5949b8>>
|
69
|
+
# irb(main):004:0> prompt.ask( "Prepared for this one?" )
|
70
|
+
# RuntimeError: Can't match "Prepared for this one? "
|
71
|
+
# from /usr/local/lib/ruby/site_ruby/1.8/easyprompt.rb:79:in `match_regexp'
|
72
|
+
# from /usr/local/lib/ruby/site_ruby/1.8/easyprompt.rb:95:in `update'
|
73
|
+
# from /usr/local/lib/ruby/1.8/observer.rb:185:in `notify_observers'
|
74
|
+
# from /usr/local/lib/ruby/1.8/observer.rb:184:in `each'
|
75
|
+
# from /usr/local/lib/ruby/1.8/observer.rb:184:in `notify_observers'
|
76
|
+
# from /usr/local/lib/ruby/site_ruby/1.8/easyprompt.rb:128:in `gets'
|
77
|
+
# from /usr/local/lib/ruby/site_ruby/1.8/easyprompt.rb:36:in `ask'
|
78
|
+
# from (irb):4
|
79
|
+
# irb(main):005:0> user.set_response( /about this one/, "sure!" )
|
80
|
+
# => ["sure!", nil]
|
81
|
+
# irb(main):006:0> prompt.ask( "How about this one?" )
|
82
|
+
# => "sure!"
|
83
|
+
# irb(main):007:0> user.set_response( /twice/, "no", 1 )
|
84
|
+
# => ["no", 1]
|
85
|
+
# irb(main):008:0> prompt.ask( "Can I ask you this twice?" )
|
86
|
+
# => "no"
|
87
|
+
# irb(main):009:0> prompt.ask( "Can I ask you this twice?" )
|
88
|
+
# RuntimeError: Exceeded limit of 1 for (?-mix:twice)
|
89
|
+
# from /usr/local/lib/ruby/site_ruby/1.8/easyprompt.rb:101:in `update'
|
90
|
+
# from /usr/local/lib/ruby/1.8/observer.rb:185:in `notify_observers'
|
91
|
+
# from /usr/local/lib/ruby/1.8/observer.rb:184:in `each'
|
92
|
+
# from /usr/local/lib/ruby/1.8/observer.rb:184:in `notify_observers'
|
93
|
+
# from /usr/local/lib/ruby/site_ruby/1.8/easyprompt.rb:128:in `gets'
|
94
|
+
# from /usr/local/lib/ruby/site_ruby/1.8/easyprompt.rb:36:in `ask'
|
95
|
+
# from (irb):9
|
96
|
+
class MockCommandLineUser
|
97
|
+
# Initializes the MockCommandLineUser. Once a MockCommandLineUser has been
|
98
|
+
# instantiated, it will automatically intercept the calls that EasyPrompt
|
99
|
+
# makes to STDIN and STDOUT. Future calls to EasyPrompt#ask will check
|
100
|
+
# against the list of responses contained in MockCommandLineUser; if there
|
101
|
+
# is no match, an error will be raised. Use set_response to add a response.
|
102
|
+
def initialize
|
103
|
+
@responses = {}
|
104
|
+
@match_count = Hash.new( 1 )
|
105
|
+
context = Lafcadio::Context.instance
|
106
|
+
@mock_stdin = StringIO.new
|
107
|
+
@mock_stdin.add_observer( self )
|
108
|
+
context.set_resource( MockableStdin, @mock_stdin )
|
109
|
+
@mock_stdout = StringIO.new
|
110
|
+
context.set_resource( MockableStdout, @mock_stdout )
|
111
|
+
end
|
112
|
+
|
113
|
+
def match_regexp #:nodoc:
|
114
|
+
arg = @mock_stdout.string
|
115
|
+
@mock_stdout.string = ''
|
116
|
+
matching = @responses.map { |regexp, response_pair |
|
117
|
+
( loc = arg =~ regexp ) ? [ regexp, loc ] : nil
|
118
|
+
}
|
119
|
+
matching.compact!
|
120
|
+
fail "Can't match \"#{ arg }\"" if matching.empty?
|
121
|
+
( matching.sort_by { |regexp, loc| loc } ).last.first
|
122
|
+
end
|
123
|
+
|
124
|
+
def respond( response ) #:nodoc:
|
125
|
+
response = response.instance_of?( String ) ? response : response.call
|
126
|
+
@mock_stdin.string = ''
|
127
|
+
@mock_stdin.puts( response )
|
128
|
+
@mock_stdin.rewind
|
129
|
+
end
|
130
|
+
|
131
|
+
# Adds a response to the list of responses.
|
132
|
+
# input_regexp:: The regexp that each EasyPrompt#ask prompt is compared to.
|
133
|
+
# response:: The value returned.
|
134
|
+
# limit:: The maximum number of times this response can be used.
|
135
|
+
def set_response( input_regexp, response, limit = nil )
|
136
|
+
@responses[input_regexp] = [ response, limit ]
|
137
|
+
end
|
138
|
+
|
139
|
+
def update #:nodoc:
|
140
|
+
regexp = match_regexp
|
141
|
+
if regexp && ( @responses[regexp].last.nil? ||
|
142
|
+
@match_count[regexp] <= @responses[regexp].last )
|
143
|
+
respond( @responses[regexp].first )
|
144
|
+
@match_count[regexp] += 1
|
145
|
+
elsif regexp
|
146
|
+
raise "Exceeded limit of #{ @responses[regexp].last } for #{ regexp }"
|
147
|
+
else
|
148
|
+
raise "Can't find a response for " + arg
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class MockableStdin < Lafcadio::ContextualService #:nodoc:
|
154
|
+
def method_missing( symbol, *args )
|
155
|
+
$stdin.send( symbol, *args )
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class MockableStdout < Lafcadio::ContextualService #:nodoc:
|
160
|
+
def method_missing( symbol, *args )
|
161
|
+
$stdout.send( symbol, *args )
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# EasyPrompt modifies StringIO to be Observable; whenever gets is called, the
|
167
|
+
# StringIO's observers are notified.
|
168
|
+
class StringIO
|
169
|
+
include Observable
|
170
|
+
|
171
|
+
alias_method :old_gets, :gets
|
172
|
+
|
173
|
+
def gets( separator = $/ )
|
174
|
+
changed
|
175
|
+
notify_observers
|
176
|
+
old_gets( separator )
|
177
|
+
end
|
178
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.
|
2
|
+
rubygems_version: 0.8.6
|
3
3
|
specification_version: 1
|
4
4
|
name: easyprompt
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2005-
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2005-04-11
|
8
8
|
summary: EasyPrompt is a utility for command-line scripts.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
|
-
author: Francis Hwang
|
12
11
|
email: sera@fhwang.net
|
13
12
|
homepage: http://easyprompt.rubyforge.org/
|
14
13
|
rubyforge_project:
|
@@ -27,8 +26,11 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
27
26
|
version: 0.0.0
|
28
27
|
version:
|
29
28
|
platform: ruby
|
29
|
+
authors:
|
30
|
+
- Francis Hwang
|
30
31
|
files:
|
31
32
|
- lib/easyprompt.rb
|
33
|
+
- lib/easyprompt.rb~
|
32
34
|
test_files: []
|
33
35
|
rdoc_options: []
|
34
36
|
extra_rdoc_files: []
|