easyprompt 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/easyprompt.rb +40 -14
  2. data/lib/easyprompt.rb~ +178 -0
  3. 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.0'
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
- @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
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
- @match_count = Hash.new( 1 )
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
@@ -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.1
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.0
7
- date: 2005-01-13
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: []