highline 0.2.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.
@@ -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: []