highline 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ = Change Log
2
+
3
+ Below is a complete listing of changes for each revision of HighLine.
4
+
5
+ == 0.2.0
6
+
7
+ * Added Unit Tests to cover an already fixed output bug in the future.
8
+ * Added Rakefile and setup test action (default).
9
+ * Renamed HighLine::Answer to HighLine::Question to better illustrate its role.
10
+ * Renamed fetch_line() to get_response() to better define its goal.
11
+ * Simplified explain_error in terms of the Question object.
12
+ * Renamed accept?() to in_range?() to better define purpose.
13
+ * Reworked valid?() into valid_answer?() to better fit Question object.
14
+ * Reworked <tt>@member</tt> into <tt>@in</tt>, to make it easier to remember and
15
+ switched implementation to include?().
16
+ * Added range checks for @above and @below.
17
+ * Fixed the bug causing ask() to swallow NoMethodErrors.
18
+ * Rolled ask_on_error() into responses.
19
+ * Redirected imports to Kernel from Object.
20
+ * Added support for <tt>validate lambda { ... }</tt>.
21
+ * Added default answer support.
22
+ * Fixed bug that caused ask() to die with an empty question.
23
+ * Added complete documentation.
24
+ * Improve the implemetation of agree() to be the intended "yes" or "no" only
25
+ question.
26
+
27
+ == 0.1.0
28
+
29
+ * Initial release as the solution to
30
+ {Ruby Quiz #29}[http://www.rubyquiz.com/quiz29.html].
data/INSTALL ADDED
@@ -0,0 +1,10 @@
1
+ = Installing the Gem
2
+
3
+ HighLine is intended to be installed via the
4
+ RubyGems[http://rubyforge.org/projects/rubygems/] system. To get the latest
5
+ version, simply enter the following into your command prompt:
6
+
7
+ sudo gem install highline
8
+
9
+ You must have RubyGems[http://rubyforge.org/projects/rubygems/] installed for
10
+ the above to work.
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ = License Terms
2
+
3
+ Distributed under the
4
+ {Ruby software license}[http://www.ruby-lang.org/en/LICENSE.txt] by
5
+ James Edward Gray II.
6
+
7
+ Please email James[mailto:james@grayproductions.net] with any questions.
data/README ADDED
@@ -0,0 +1,31 @@
1
+ = Read Me
2
+
3
+ by James Edward Gray II
4
+
5
+ == Description
6
+
7
+ Welcome to HighLine.
8
+
9
+ This library was designed to ease the tedious tasks of doing console input and
10
+ output with low-level methods like gets() and puts(). This library provides a
11
+ robust system to requesting data from a user, without needing to code all the
12
+ error checking and validation rules and without needing to convert the typed
13
+ Strings into what your program really needs. Just tell HighLine what you're
14
+ after, and leg it do all the leg work.
15
+
16
+ == Documentation
17
+
18
+ See HighLine and HighLine::Question for documentation.
19
+
20
+ == Examples
21
+
22
+ See the examples/ directory of this project for code examples.
23
+
24
+ == Installing
25
+
26
+ See the INSTALL file for instructions.
27
+
28
+ == Questions
29
+
30
+ Feel free to email {James Edward Gray II}[mailto:james@grayproductions.net] with
31
+ any questions.
@@ -0,0 +1,52 @@
1
+ require "rake/rdoctask"
2
+ require "rake/testtask"
3
+ require "rake/gempackagetask"
4
+
5
+ require "rubygems"
6
+
7
+ task :default => [:test]
8
+
9
+ Rake::TestTask.new do |test|
10
+ test.libs << "test"
11
+ test.test_files = [ "test/ts_all.rb" ]
12
+ test.verbose = true
13
+ end
14
+
15
+ Rake::RDocTask.new do |rdoc|
16
+ rdoc.main = "README"
17
+ rdoc.rdoc_files.include( "README", "INSTALL",
18
+ "TODO", "CHANGELOG",
19
+ "LICENSE", "lib/" )
20
+ rdoc.rdoc_dir = "doc/html"
21
+ rdoc.title = "HighLine Documentation"
22
+ end
23
+
24
+ spec = Gem::Specification.new do |spec|
25
+ spec.name = "highline"
26
+ spec.version = "0.2.0"
27
+ spec.platform = Gem::Platform::RUBY
28
+ spec.summary = "HighLine is a high-level line oriented console interface."
29
+ spec.files = Dir.glob("{examples,lib,test}/**/*.rb").
30
+ delete_if { |item| item.include?("CVS") } + ["Rakefile"]
31
+ spec.test_suite_file = "test/ts_all.rb"
32
+ spec.has_rdoc = true
33
+ spec.extra_rdoc_files = %w{README INSTALL TODO CHANGELOG LICENSE}
34
+ spec.rdoc_options << '--title' << 'HighLine Documentation' <<
35
+ '--main' << 'README'
36
+ spec.require_path = 'lib'
37
+ spec.autorequire = "highline"
38
+ spec.author = "James Edward Gray II"
39
+ spec.email = "james@grayproductions.net"
40
+ spec.rubyforge_project = "highline"
41
+ spec.homepage = "http://highline.rubyforge.org"
42
+ spec.description = <<END_DESC
43
+ A "high-level line oriented" input/output library that grew out of my solution
44
+ to Ruby Quiz #29. This library attempts to make standard console input and
45
+ output robust and painless.
46
+ END_DESC
47
+ end
48
+
49
+ Rake::GemPackageTask.new(spec) do |pkg|
50
+ pkg.need_zip = true
51
+ pkg.need_tar = true
52
+ end
data/TODO ADDED
@@ -0,0 +1,16 @@
1
+ = To Do List
2
+
3
+ The following is a list of planned expansions for HighLine, in no particular
4
+ order.
5
+
6
+ * Expose response replacements for user code strings.
7
+ * Support character oriented input.
8
+ * Support <tt>ask(..., Array)</tt>, better than the current
9
+ <tt>ask(..., lambda { |arr| arr.split(",") })</tt> or similar.
10
+ * Support <tt>ask(..., Hash)</tt>.
11
+ * Provide for <tt>ask(..., MyClass)</tt>.
12
+ * Add ANSI color support to say().
13
+ * Add word wrap support to say().
14
+ * Allow user code to specify whitespace handling for <tt>@input</tt>
15
+ (chomp(), strip(), etc.).
16
+ * Support install with setup.rb.
@@ -0,0 +1,51 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # basic_usage.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-28.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ require "rubygems"
9
+ require "highline/import"
10
+ require "yaml"
11
+
12
+ contacts = [ ]
13
+
14
+ begin
15
+ entry = Hash.new
16
+
17
+ # basic output
18
+ say("Enter a contact:")
19
+
20
+ # basic input
21
+ entry[:name] = ask("Name? (last, first) ") do |q|
22
+ q.validate = /\A\w+, ?\w+\Z/
23
+ end
24
+ entry[:company] = ask("Company? ") { |q| q.default = "none" }
25
+ entry[:address] = ask("Address? ")
26
+ entry[:city] = ask("City? ")
27
+ entry[:state] = ask("State? ") { |q| q.validate = /\A[A-Z]{2}\Z/ }
28
+ entry[:zip] = ask("Zip? ") do |q|
29
+ q.validate = /\A\d{5}(?:-?\d{4})?\Z/
30
+ end
31
+ entry[:phone] = ask( "Phone? ",
32
+ lambda { |p| p.delete("^0-9").
33
+ sub(/\A(\d{3})/, '(\1) ').
34
+ sub(/(\d{4})\Z/, '-\1') } ) do |q|
35
+ q.validate = lambda { |p| p.delete("^0-9").length == 10 }
36
+ q.responses[:not_valid] = "Enter a phone numer with area code."
37
+ end
38
+ entry[:age] = ask("Age? ", Integer) { |q| q.in = 0..105 }
39
+ entry[:birthday] = ask("Birthday? ", Date)
40
+ entry[:interests] = ask( "Interests? (comma separated list) ",
41
+ lambda { |str| str.split(/,\s*/) } )
42
+ entry[:description] = ask("Enter a description for this contact.")
43
+
44
+ contacts << entry
45
+ # shortcut for yes and no questions
46
+ end while agree("Enter another contact? ")
47
+
48
+ if agree("Save these contacts? ")
49
+ file_name = ask("Enter a file name: ") { |q| q.validate = /\A\w+\Z/ }
50
+ File.open("#{file_name}.yaml", "w") { |file| YAML.dump(contacts, file) }
51
+ end
@@ -0,0 +1,125 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # highline.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+ #
8
+ # See HighLine for documentation.
9
+
10
+ require "highline/question"
11
+
12
+ #
13
+ # A HighLine object is a "high-level line oriented" shell over an input and an
14
+ # output stream. HighLine simplifies common console interaction, effectively
15
+ # replacing puts() and gets(). User code can simply specify the question to ask
16
+ # and any details about user interaction, then leave the rest of the work to
17
+ # HighLine. When HighLine.ask() returns, you'll have to answer you requested,
18
+ # even if HighLine had to ask many times, validate results, perform range
19
+ # checking, convert types, etc.
20
+ #
21
+ class HighLine
22
+ # An internal HighLine error. User code does not need to trap this.
23
+ class QuestionError < StandardError
24
+ # do nothing, just creating a unique error type
25
+ end
26
+
27
+ # Create an instance of HighLine, connected to the streams _input_
28
+ # and _output_.
29
+ def initialize( input = $stdin, output = $stdout )
30
+ @input = input
31
+ @output = output
32
+ end
33
+
34
+ #
35
+ # A shortcut to HighLine.ask() a question that only accepts "yes" or "no"
36
+ # answers ("y" and "n" are allowed) and returns +true+ or +false+
37
+ # (+true+ for "yes").
38
+ #
39
+ def agree( yes_or_no_question )
40
+ ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q|
41
+ q.validate = /\Ay(?:es)?|no?\Z/i
42
+ q.responses[:not_valid] = 'Please enter "yes" or "no".'
43
+ q.responses[:ask_on_error] = :question
44
+ end
45
+ end
46
+
47
+ #
48
+ # This method is the primary interface for user input. Just provide a
49
+ # _question_ to ask the user, the _answer_type_ you want returned, and
50
+ # optionally a code block setting up details of how you want the question
51
+ # handled. See HighLine.say() for details on the format of _question_, and
52
+ # HighLine::Question for more information about _answer_type_ and what's
53
+ # valid in the code block.
54
+ #
55
+ def ask( question, answer_type = String, &details ) # :yields: question
56
+ question = Question.new(question, answer_type, &details)
57
+
58
+ say(question)
59
+ begin
60
+ answer = question.answer_or_default(get_response)
61
+ unless question.valid_answer?(answer)
62
+ explain_error(question, :not_valid)
63
+ raise QuestionError
64
+ end
65
+
66
+ answer = question.convert(answer)
67
+
68
+ if question.in_range?(answer)
69
+ answer
70
+ else
71
+ explain_error(question, :not_in_range)
72
+ raise QuestionError
73
+ end
74
+ rescue QuestionError
75
+ retry
76
+ rescue ArgumentError
77
+ explain_error(question, :invalid_type)
78
+ retry
79
+ rescue NameError
80
+ raise if $!.is_a?(NoMethodError)
81
+ explain_error(question, :ambiguous_completion)
82
+ retry
83
+ end
84
+ end
85
+
86
+ #
87
+ # The basic output method for HighLine objects. If the provided _statement_
88
+ # ends with a space or tab character, a newline will not be appended (output
89
+ # will be flush()ed). All other cases are passed straight to Kernel.puts().
90
+ #
91
+ def say( statement )
92
+ statement = statement.to_s
93
+ return unless statement.length > 0
94
+
95
+ if statement[-1, 1] == " " or statement[-1, 1] == "\t"
96
+ @output.print(statement)
97
+ @output.flush
98
+ else
99
+ @output.puts(statement)
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ #
106
+ # A helper method for sending the output stream and error and repeat
107
+ # of the question.
108
+ #
109
+ def explain_error( question, error )
110
+ say(question.responses[error])
111
+ if question.responses[:ask_on_error] == :question
112
+ say(question)
113
+ elsif question.responses[:ask_on_error]
114
+ say(question.responses[:ask_on_error])
115
+ end
116
+ end
117
+
118
+ #
119
+ # Read a line of input from the input stream, chomp()ing any newline
120
+ # characters.
121
+ #
122
+ def get_response( )
123
+ @input.gets.chomp
124
+ end
125
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # import.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ require "highline"
9
+ require "forwardable"
10
+
11
+ $terminal = HighLine.new
12
+
13
+ #
14
+ # <tt>require "highline/import"</tt> adds shorcut methods to Kernel, making
15
+ # agree(), ask(), and say() globally available. This is handy for quick and
16
+ # dirty input and output. These methods use the HighLine object in the global
17
+ # variable <tt>$terminal</tt>, which is initialized to used <tt>$stdin</tt> and
18
+ # <tt>$stdout</tt> (you are free to change this). Otherwise, these methods are
19
+ # identical to their HighLine counterparts, see that class for detailed
20
+ # explinations.
21
+ #
22
+ module Kernel
23
+ extend Forwardable
24
+ def_delegators :$terminal, :agree, :ask, :say
25
+ end
@@ -0,0 +1,208 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # question.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ require "optparse"
9
+ require "date"
10
+
11
+ class HighLine
12
+ #
13
+ # Question objects contain all the details of a single invocation of
14
+ # HighLine.ask(). The object is initialized by the parameters passed to
15
+ # HighLine.ask() and then queried to make sure each step of the input
16
+ # process is handled according to the users wishes.
17
+ #
18
+ class Question
19
+ #
20
+ # Create an instance of HighLine::Question. Expects a _question_ to ask
21
+ # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
22
+ # The _answer_type_ parameter must be a type recongnized by
23
+ # Question.convert(). If given, a block is yeilded the new Question
24
+ # object to allow custom initializaion.
25
+ #
26
+ def initialize( question, answer_type )
27
+ # initialize instance data
28
+ @question = question
29
+ @answer_type = answer_type
30
+
31
+ @default = nil
32
+ @validate = nil
33
+ @above = nil
34
+ @below = nil
35
+ @in = nil
36
+ @responses = Hash.new
37
+
38
+ # allow block to override settings
39
+ yield self if block_given?
40
+
41
+ # finalize responses based on settings
42
+ append_default unless default.nil?
43
+ @responses = { :ambiguous_completion =>
44
+ "Ambiguous choice. " +
45
+ "Please choose one of #{@answer_type.inspect}.",
46
+ :ask_on_error =>
47
+ "? ",
48
+ :invalid_type =>
49
+ "You must enter a valid #{@answer_type}.",
50
+ :not_in_range =>
51
+ "Your answer isn't within the expected range " +
52
+ "(#{expected_range}).",
53
+ :not_valid =>
54
+ "Your answer isn't valid (must match " +
55
+ "#{@validate.inspect})." }.merge(@responses)
56
+ end
57
+
58
+ # Used to provide a default answer to this question.
59
+ attr_accessor :default
60
+ #
61
+ # If set to a Regexp, the answer must match (before type conversion).
62
+ # Can also be set to a Proc which will be called with the provided
63
+ # answer to validate with a +true+ or +false+ return.
64
+ #
65
+ attr_accessor :validate
66
+ # Used to control range checks for answer.
67
+ attr_accessor :above, :below
68
+ # If set, answer must pass an include?() check on this object.
69
+ attr_accessor :in
70
+ #
71
+ # A Hash that stores the various responses used by HighLine to notify
72
+ # the user. The currently used responses and their purpose are as
73
+ # follows:
74
+ #
75
+ # <tt>:ambiguous_completion</tt>:: Used to notify the user of an
76
+ # ambiguous answer the auto-completion
77
+ # system cannot resolve.
78
+ # <tt>:ask_on_error</tt>:: This is the question that will be
79
+ # redisplayed to the user in the event
80
+ # of an error. Can be set to
81
+ # <tt>:question</tt> to repeat the
82
+ # original question.
83
+ # <tt>:invalid_type</tt>:: The error message shown when a type
84
+ # conversion fails.
85
+ # <tt>:not_in_range</tt>:: Used to notify the user that a
86
+ # provided answer did not satisfy
87
+ # the range requirement tests.
88
+ # <tt>:not_valid</tt>:: The error message shown when
89
+ # validation checks fail.
90
+ #
91
+ attr_reader :responses
92
+
93
+ #
94
+ # Transforms the given _answer_string_ into the expected type for this
95
+ # Question. Currently supported conversions are:
96
+ #
97
+ # <tt>[...]</tt>:: Answer must be a member of the passed Array.
98
+ # Auto-completion is used to expand partial
99
+ # answers.
100
+ # <tt>lambda {...}</tt>:: Answer is passed to lambda for conversion.
101
+ # Date:: Date.parse() is called with answer.
102
+ # DateTime:: DateTime.parse() is called with answer.
103
+ # Float:: Answer is converted with Kernel.Float().
104
+ # Integer:: Answer is converted with Kernel.Integer().
105
+ # +nil+:: Answer is left in String format.
106
+ # String:: Answer is converted with Kernel.String().
107
+ # Regexp:: Answer is fed to Regexp.new().
108
+ # Symbol:: The method to_sym() is called on answer and
109
+ # the result returned.
110
+ #
111
+ # This method throws ArgumentError, if the conversion cannot be
112
+ # completed for any reason.
113
+ #
114
+ def convert( answer_string )
115
+ if @answer_type.nil?
116
+ answer_string
117
+ elsif [Float, Integer, String].include?(@answer_type)
118
+ Kernel.send(@answer_type.to_s.to_sym, answer_string)
119
+ elsif @answer_type == Symbol
120
+ answer_string.to_sym
121
+ elsif @answer_type == Regexp
122
+ Regexp.new(answer_string)
123
+ elsif @answer_type.is_a?(Array)
124
+ # cheating, using OptionParser's Completion module
125
+ @answer_type.extend(OptionParser::Completion)
126
+ @answer_type.complete(answer_string).last
127
+ elsif [Date, DateTime].include?(@answer_type)
128
+ @answer_type.parse(answer_string)
129
+ elsif @answer_type.is_a?(Proc)
130
+ @answer_type[answer_string]
131
+ end
132
+ end
133
+
134
+ #
135
+ # Returns the provided _answer_string_ or the default answer for this
136
+ # Question if a default was set and the answer is empty.
137
+ #
138
+ def answer_or_default( answer_string )
139
+ if answer_string.length == 0 and not @default.nil?
140
+ @default
141
+ else
142
+ answer_string
143
+ end
144
+ end
145
+
146
+ #
147
+ # Returns +true+ if the _answer_object_ is greater than the _above_
148
+ # attribute, less than the _below_ attribute and included?()ed in the
149
+ # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
150
+ # are not checked.
151
+ #
152
+ def in_range?( answer_object )
153
+ (@above.nil? or answer_object > @above) and
154
+ (@below.nil? or answer_object < @below) and
155
+ (@in.nil? or @in.include?(answer_object))
156
+ end
157
+
158
+ # Stringifies the question to be asked.
159
+ def to_s( )
160
+ @question
161
+ end
162
+
163
+ #
164
+ # Returns +true+ if the provided _answer_string_ is accepted by the
165
+ # _validate_ attribute or +false+ if it's not.
166
+ #
167
+ def valid_answer?( answer_string )
168
+ @validate.nil? or
169
+ (@validate.is_a?(Regexp) and answer_string =~ @validate) or
170
+ (@validate.is_a?(Proc) and @validate[answer_string])
171
+ end
172
+
173
+ private
174
+
175
+ #
176
+ # Adds the default choice to the end of question between <tt>|...|</tt>.
177
+ # Trailing whitespace is preserved so the function of HighLine.say() is
178
+ # not affected.
179
+ #
180
+ def append_default( )
181
+ if @question =~ /([\t ]+)\Z/
182
+ @question << "|#{@default}|#{$1}"
183
+ elsif @question == ""
184
+ @question << "|#{@default}| "
185
+ elsif @question[-1, 1] == "\n"
186
+ @question[-2, 0] = " |#{@default}|"
187
+ else
188
+ @question << " |#{@default}|"
189
+ end
190
+ end
191
+
192
+ # Returns a english explination of the current range settings.
193
+ def expected_range( )
194
+ expected = [ ]
195
+
196
+ expected << "above #{@above}" unless @above.nil?
197
+ expected << "below #{@below}" unless @below.nil?
198
+ expected << "included in #{@in.inspect}" unless @in.nil?
199
+
200
+ case expected.size
201
+ when 0 then ""
202
+ when 1 then expected.first
203
+ when 2 then expected.join(" and ")
204
+ else expected[0..-2].join(", ") + ", and #{expected.last}"
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,301 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # tc_highline.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ $test_lib_dir ||= File.join(File.dirname(__FILE__), "..", "lib")
9
+ $:.unshift($test_lib_dir) unless $:.include?($test_lib_dir)
10
+
11
+ require "test/unit"
12
+
13
+ require "highline"
14
+ require "stringio"
15
+
16
+ class TestHighLine < Test::Unit::TestCase
17
+ def setup
18
+ @input = StringIO.new
19
+ @output = StringIO.new
20
+ @terminal = HighLine.new(@input, @output)
21
+ end
22
+
23
+ def test_agree
24
+ @input << "y\nyes\nYES\nHell no!\nNo\n"
25
+ @input.rewind
26
+
27
+ assert_equal(true, @terminal.agree("Yes or no? "))
28
+ assert_equal(true, @terminal.agree("Yes or no? "))
29
+ assert_equal(true, @terminal.agree("Yes or no? "))
30
+ assert_equal(false, @terminal.agree("Yes or no? "))
31
+ end
32
+
33
+ def test_ask
34
+ name = "James Edward Gray II"
35
+ @input << name << "\n"
36
+ @input.rewind
37
+
38
+ assert_equal(name, @terminal.ask("What is your name? "))
39
+ end
40
+
41
+ def test_defaults
42
+ @input << "\nNo Comment\n"
43
+ @input.rewind
44
+
45
+ answer = @terminal.ask("Are you sexually active? ") do |q|
46
+ q.validate = /\Ay(?:es)?|no?|no comment\Z/i
47
+ end
48
+ assert_equal("No Comment", answer)
49
+
50
+ @input.truncate(@input.rewind)
51
+ @input << "\nYes\n"
52
+ @input.rewind
53
+ @output.truncate(@output.rewind)
54
+
55
+ answer = @terminal.ask("Are you sexually active? ") do |q|
56
+ q.default = "No Comment"
57
+ q.validate = /\Ay(?:es)?|no?|no comment\Z/i
58
+ end
59
+ assert_equal("No Comment", answer)
60
+ assert_equal( "Are you sexually active? |No Comment| ",
61
+ @output.string )
62
+ end
63
+
64
+ def test_empty
65
+ @input << "\n"
66
+ @input.rewind
67
+
68
+ answer = @terminal.ask("") do |q|
69
+ q.default = "yes"
70
+ q.validate = /\Ay(?:es)?|no?\Z/i
71
+ end
72
+ assert_equal("yes", answer)
73
+ end
74
+
75
+ def test_range_requirements
76
+ @input << "112\n-541\n28\n"
77
+ @input.rewind
78
+
79
+ answer = @terminal.ask("Tell me your age.", Integer) do |q|
80
+ q.in = 0..105
81
+ end
82
+ assert_equal(28, answer)
83
+ assert_equal( "Tell me your age.\n" +
84
+ "Your answer isn't within the expected range " +
85
+ "(included in 0..105).\n" +
86
+ "? " +
87
+ "Your answer isn't within the expected range " +
88
+ "(included in 0..105).\n" +
89
+ "? ", @output.string )
90
+
91
+ @input.truncate(@input.rewind)
92
+ @input << "1\n-541\n28\n"
93
+ @input.rewind
94
+ @output.truncate(@output.rewind)
95
+
96
+ answer = @terminal.ask("Tell me your age.", Integer) do |q|
97
+ q.above = 3
98
+ end
99
+ assert_equal(28, answer)
100
+ assert_equal( "Tell me your age.\n" +
101
+ "Your answer isn't within the expected range " +
102
+ "(above 3).\n" +
103
+ "? " +
104
+ "Your answer isn't within the expected range " +
105
+ "(above 3).\n" +
106
+ "? ", @output.string )
107
+
108
+ @input.truncate(@input.rewind)
109
+ @input << "1\n28\n-541\n"
110
+ @input.rewind
111
+ @output.truncate(@output.rewind)
112
+
113
+ answer = @terminal.ask("Lowest numer you can think of?", Integer) do |q|
114
+ q.below = 0
115
+ end
116
+ assert_equal(-541, answer)
117
+ assert_equal( "Lowest numer you can think of?\n" +
118
+ "Your answer isn't within the expected range " +
119
+ "(below 0).\n" +
120
+ "? " +
121
+ "Your answer isn't within the expected range " +
122
+ "(below 0).\n" +
123
+ "? ", @output.string )
124
+
125
+ @input.truncate(@input.rewind)
126
+ @input << "1\n-541\n6\n"
127
+ @input.rewind
128
+ @output.truncate(@output.rewind)
129
+
130
+ answer = @terminal.ask("Enter a low even number: ", Integer) do |q|
131
+ q.above = 0
132
+ q.below = 10
133
+ q.in = [2, 4, 6, 8]
134
+ end
135
+ assert_equal(6, answer)
136
+ assert_equal( "Enter a low even number: " +
137
+ "Your answer isn't within the expected range " +
138
+ "(above 0, below 10, and included in [2, 4, 6, 8]).\n" +
139
+ "? " +
140
+ "Your answer isn't within the expected range " +
141
+ "(above 0, below 10, and included in [2, 4, 6, 8]).\n" +
142
+ "? ", @output.string )
143
+ end
144
+
145
+ def test_reask
146
+ number = 61676
147
+ @input << "Junk!\n" << number << "\n"
148
+ @input.rewind
149
+
150
+ answer = @terminal.ask("Favorite number? ", Integer)
151
+ assert_kind_of(Integer, number)
152
+ assert_instance_of(Fixnum, number)
153
+ assert_equal(number, answer)
154
+ assert_equal( "Favorite number? " +
155
+ "You must enter a valid Integer.\n" +
156
+ "? ", @output.string )
157
+
158
+ @input.rewind
159
+ @output.truncate(@output.rewind)
160
+
161
+ answer = @terminal.ask("Favorite number? ", Integer) do |q|
162
+ q.responses[:ask_on_error] = :question
163
+ q.responses[:invalid_type] = "Not a valid number!"
164
+ end
165
+ assert_kind_of(Integer, number)
166
+ assert_instance_of(Fixnum, number)
167
+ assert_equal(number, answer)
168
+ assert_equal( "Favorite number? " +
169
+ "Not a valid number!\n" +
170
+ "Favorite number? ", @output.string )
171
+
172
+ @input.truncate(@input.rewind)
173
+ @input << "gen\ngene\n"
174
+ @input.rewind
175
+ @output.truncate(@output.rewind)
176
+
177
+ answer = @terminal.ask("Select a mode: ", [:generate, :gentle])
178
+ assert_instance_of(Symbol, answer)
179
+ assert_equal(:generate, answer)
180
+ assert_equal("Select a mode: " +
181
+ "Ambiguous choice. " +
182
+ "Please choose one of [:generate, :gentle].\n" +
183
+ "? ", @output.string)
184
+ end
185
+
186
+ def test_say
187
+ @terminal.say("This will have a newline.")
188
+ assert_equal("This will have a newline.\n", @output.string)
189
+
190
+ @output.truncate(@output.rewind)
191
+
192
+ @terminal.say("This will also have one newline.\n")
193
+ assert_equal("This will also have one newline.\n", @output.string)
194
+
195
+ @output.truncate(@output.rewind)
196
+
197
+ @terminal.say("This will not have a newline. ")
198
+ assert_equal("This will not have a newline. ", @output.string)
199
+ end
200
+
201
+ def test_type_conversion
202
+ number = 61676
203
+ @input << number << "\n"
204
+ @input.rewind
205
+
206
+ answer = @terminal.ask("Favorite number? ", Integer)
207
+ assert_kind_of(Integer, answer)
208
+ assert_instance_of(Fixnum, answer)
209
+ assert_equal(number, answer)
210
+
211
+ @input.truncate(@input.rewind)
212
+ number = 1_000_000_000_000_000_000_000_000_000_000
213
+ @input << number << "\n"
214
+ @input.rewind
215
+
216
+ answer = @terminal.ask("Favorite number? ", Integer)
217
+ assert_kind_of(Integer, answer)
218
+ assert_instance_of(Bignum, answer)
219
+ assert_equal(number, answer)
220
+
221
+ @input.truncate(@input.rewind)
222
+ number = 10.5002
223
+ @input << number << "\n"
224
+ @input.rewind
225
+
226
+ answer = @terminal.ask( "Favorite number? ",
227
+ lambda { |n| n.to_f.abs.round } )
228
+ assert_kind_of(Integer, answer)
229
+ assert_instance_of(Fixnum, answer)
230
+ assert_equal(11, answer)
231
+
232
+ @input.truncate(@input.rewind)
233
+ animal = :dog
234
+ @input << animal << "\n"
235
+ @input.rewind
236
+
237
+ answer = @terminal.ask("Favorite animal? ", Symbol)
238
+ assert_instance_of(Symbol, answer)
239
+ assert_equal(animal, answer)
240
+
241
+ @input.truncate(@input.rewind)
242
+ @input << "6/16/76\n"
243
+ @input.rewind
244
+
245
+ answer = @terminal.ask("Enter your birthday.", Date)
246
+ assert_instance_of(Date, answer)
247
+ assert_equal(16, answer.day)
248
+ assert_equal(6, answer.month)
249
+ assert_equal(76, answer.year)
250
+
251
+ @input.truncate(@input.rewind)
252
+ pattern = "^yes|no$"
253
+ @input << pattern << "\n"
254
+ @input.rewind
255
+
256
+ answer = @terminal.ask("Give me a pattern to match with: ", Regexp)
257
+ assert_instance_of(Regexp, answer)
258
+ assert_equal(/#{pattern}/, answer)
259
+
260
+ @input.truncate(@input.rewind)
261
+ @input << "gen\n"
262
+ @input.rewind
263
+
264
+ answer = @terminal.ask("Select a mode: ", [:generate, :run])
265
+ assert_instance_of(Symbol, answer)
266
+ assert_equal(:generate, answer)
267
+ end
268
+
269
+ def test_validation
270
+ @input << "system 'rm -rf /'\n105\n0b101_001\n"
271
+ @input.rewind
272
+
273
+ answer = @terminal.ask("Enter a binary number: ") do |q|
274
+ q.validate = /\A(?:0b)?[01_]+\Z/
275
+ end
276
+ assert_equal("0b101_001", answer)
277
+ assert_equal( "Enter a binary number: " +
278
+ "Your answer isn't valid " +
279
+ "(must match /\\A(?:0b)?[01_]+\\Z/).\n" +
280
+ "? " +
281
+ "Your answer isn't valid " +
282
+ "(must match /\\A(?:0b)?[01_]+\\Z/).\n" +
283
+ "? ", @output.string )
284
+
285
+ @input.truncate(@input.rewind)
286
+ @input << "Gray II, James Edward\n" +
287
+ "Gray, Dana Ann Leslie\n" +
288
+ "Gray, James Edward\n"
289
+ @input.rewind
290
+
291
+ answer = @terminal.ask("Your name? ") do |q|
292
+ q.validate = lambda do |name|
293
+ names = name.split(/,\s*/)
294
+ return false unless names.size == 2
295
+ return false if names.first =~ /\s/
296
+ names.last.split.size == 2
297
+ end
298
+ end
299
+ assert_equal("Gray, James Edward", answer)
300
+ end
301
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # tc_import.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ $test_lib_dir ||= File.join(File.dirname(__FILE__), "..", "lib")
9
+ $:.unshift($test_lib_dir) unless $:.include?($test_lib_dir)
10
+
11
+ require "test/unit"
12
+
13
+ require "highline/import"
14
+ require "stringio"
15
+
16
+ class TestImport < Test::Unit::TestCase
17
+ def test_import
18
+ assert_respond_to(self, :agree)
19
+ assert_respond_to(self, :ask)
20
+ assert_respond_to(self, :say)
21
+ end
22
+
23
+ def test_redirection
24
+ $terminal = HighLine.new(nil, (output = StringIO.new))
25
+ say("Testing...")
26
+ assert_equal("Testing...\n", output.string)
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # ts_all.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ $test_dir ||= File.dirname(__FILE__)
9
+ $:.unshift($test_dir) unless $:.include?($test_dir)
10
+
11
+ require "test/unit"
12
+
13
+ require "tc_highline"
14
+ require "tc_import"
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.10
3
+ specification_version: 1
4
+ name: highline
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.2.0
7
+ date: 2005-04-29
8
+ summary: HighLine is a high-level line oriented console interface.
9
+ require_paths:
10
+ - lib
11
+ email: james@grayproductions.net
12
+ homepage: http://highline.rubyforge.org
13
+ rubyforge_project: highline
14
+ description: "A \"high-level line oriented\" input/output library that grew out of my solution
15
+ to Ruby Quiz #29. This library attempts to make standard console input and
16
+ output robust and painless."
17
+ autorequire: highline
18
+ default_executable:
19
+ bindir: bin
20
+ has_rdoc: true
21
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
22
+ requirements:
23
+ -
24
+ - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.0
27
+ version:
28
+ platform: ruby
29
+ authors:
30
+ - James Edward Gray II
31
+ files:
32
+ - examples/basic_usage.rb
33
+ - lib/highline.rb
34
+ - lib/highline/import.rb
35
+ - lib/highline/question.rb
36
+ - test/tc_highline.rb
37
+ - test/tc_import.rb
38
+ - test/ts_all.rb
39
+ - Rakefile
40
+ - README
41
+ - INSTALL
42
+ - TODO
43
+ - CHANGELOG
44
+ - LICENSE
45
+ test_files:
46
+ - test/ts_all.rb
47
+ rdoc_options:
48
+ - "--title"
49
+ - HighLine Documentation
50
+ - "--main"
51
+ - README
52
+ extra_rdoc_files:
53
+ - README
54
+ - INSTALL
55
+ - TODO
56
+ - CHANGELOG
57
+ - LICENSE
58
+ executables: []
59
+ extensions: []
60
+ requirements: []
61
+ dependencies: []