diff --git a/.travis.yml b/.travis.yml index f4ee366f..28dd4be6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,15 +3,20 @@ language: ruby sudo: false script: "bundle exec rake test" rvm: - - 1.9.3 - - 2.0.0 - - 2.1.5 - - 2.2.0 - - 2.2.1 - - 2.2.2 + - 1.9 + - 2.0 + - 2.1 + - 2.2 + - ruby-head - rbx-2 + - jruby-19mode # JRuby in 1.9 mode + - jruby-head + notifications: email: false matrix: allow_failures: + - rvm: ruby-head - rvm: rbx-2 + - rvm: jruby-19mode # JRuby in 1.9 mode + - rvm: jruby-head diff --git a/Changelog.md b/Changelog.md index 09fe4f14..00aff0c8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,58 @@ Below is a complete listing of changes for each revision of HighLine. +### 2.0.0-develop.2 / 2015-09-09 + +(by Abinoam P. Marques Jr. - @abinoam) + +#### NOTES + +This version brings greater compatibility with JRuby and Windows. +But we still have a lot of small issues in both platforms. +We were able to unify/converge all approaches into using io/console, +so we could delete old code that relied solely on stty, termios, java api and +windows apis (DL and Fiddle). + +Another improvement is the beginning of what I called "acceptance tests". +If you type ```rake acceptance``` you'll be guided through some tests +where you have to input some thing and see if everything work as expected. +This makes easier to catch bugs that otherwise would be over-sighted. + +#### CHANGES SUMMARY + +* Fix Simplecov - it was reporting erroneous code coverage +* Add new tests. Improves code coverage +* Extract HighLine::BuiltinStyles +* Try to avoid nil checking +* Try to avoid class variables (mis)use +* Fix RDoc include path and some small fixes to the docs +* Move HighLine::String to its own file +* Add HighLine::Terminal::IOConsole + - Add an IOConsoleCompatibility module with some stubbed + methods for using at StringIO, File and Tempfile to help + on tests. + - Any enviroment that can require 'io/console' will + use HighLine::Terminal::IOConsole by default. This kind + of unifies most environments where HighLine runs. For + example, we can use Terminal::IOConsole on JRuby!!! +* Add ruby-head and JRuby (19mode and head) to Travis CI matrix. Yes, this + our first step to a more peaceful JRuby compatibility. +* Add AppVeyor Continuous Integration for Windows +* Add _acceptance_ tests for HighLine + - Use ```rake acceptance``` to run them + - Basically it interactively asks the user to confirm if + some expected HighLine behavior is actually happening. + After that it gather some environment debug information, + so the use could send to the HighLine contributors in case + of failure. +* Remove old and unused files (as a result of relying on io/console) + - JRuby + - Windows (DL and Fiddle) + - Termios +* Fix some small (old and new) bugs +* Make some more tuning for Windows compatibility +* Make some more tuning for JRuby compatibility + ### 2.0.0-develop.1 / 2015-06-11 This is the first development version of the 2.0.0 series. It's the begining of a refactoring phase on HighLine development cycle. diff --git a/README.rdoc b/README.rdoc index edae8493..fd5af908 100644 --- a/README.rdoc +++ b/README.rdoc @@ -3,6 +3,7 @@ by James Edward Gray II {Build Status}[https://travis-ci.org/JEG2/highline] +{AppVeyor Build Status}[https://ci.appveyor.com/project/abinoam/highline] {}[http://rubygems.org/gems/highline] {}[https://codeclimate.com/github/JEG2/highline] {}[https://codeclimate.com/github/JEG2/highline/coverage] diff --git a/Rakefile b/Rakefile index 4c69fc09..0d730475 100644 --- a/Rakefile +++ b/Rakefile @@ -12,13 +12,14 @@ Rake::TestTask.new do |test| test.libs = ["lib", "test"] test.verbose = true test.warning = true + test.test_files = FileList['test/test*.rb'] end RDoc::Task.new do |rdoc| rdoc.rdoc_files.include( "README.rdoc", "INSTALL", "TODO", "Changelog.md", "AUTHORS", "COPYING", - "LICENSE", "lib /*.rb" ) + "LICENSE", "lib/**/*.rb") rdoc.main = "README.rdoc" rdoc.rdoc_dir = "doc/html" rdoc.title = "HighLine Documentation" @@ -27,3 +28,8 @@ end Gem::PackageTask.new(SPEC) do |package| # do nothing: I just need a gem but this block is required end + +desc "Run some interactive acceptance tests" +task :acceptance do + load "test/acceptance/acceptance.rb" +end diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..da28b2be --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,19 @@ +version: '{build}' + +skip_tags: true + +environment: + matrix: + - ruby_version: "21" + - ruby_version: "21-x64" + +install: + - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% + - gem install bundler --no-document -v 1.10.5 + - bundle install --retry=3 + +test_script: + - bundle exec rake + +build: off + diff --git a/examples/asking_for_arrays.rb b/examples/asking_for_arrays.rb index 64caeaf1..fa05a3e5 100644 --- a/examples/asking_for_arrays.rb +++ b/examples/asking_for_arrays.rb @@ -9,6 +9,9 @@ require "highline/import" require "pp" +puts "Using: #{$terminal.terminal.class}" +puts + grades = ask( "Enter test scores (or a blank line to quit):", lambda { |ans| ans =~ /^-?\d+$/ ? Integer(ans) : ans} ) do |q| q.gather = "" diff --git a/examples/basic_usage.rb b/examples/basic_usage.rb index 3f9b5aa4..126c64a7 100644 --- a/examples/basic_usage.rb +++ b/examples/basic_usage.rb @@ -9,6 +9,9 @@ require "highline/import" require "yaml" +puts "Using: #{$terminal.terminal.class}" +puts + contacts = [ ] class NameClass diff --git a/examples/get_character.rb b/examples/get_character.rb index c22d4127..48e4dba7 100644 --- a/examples/get_character.rb +++ b/examples/get_character.rb @@ -3,6 +3,9 @@ require "rubygems" require "highline/import" +puts "Using: #{$terminal.terminal.class}" +puts + choices = "ynaq" answer = ask("Your choice [#{choices}]? ") do |q| q.echo = false diff --git a/examples/limit.rb b/examples/limit.rb index 8b4d1905..6d25cef8 100644 --- a/examples/limit.rb +++ b/examples/limit.rb @@ -8,5 +8,8 @@ require "rubygems" require "highline/import" +puts "Using: #{$terminal.terminal.class}" +puts + text = ask("Enter text (max 10 chars): ") { |q| q.limit = 10 } puts "You entered: #{text}!" diff --git a/examples/menus.rb b/examples/menus.rb index c2bd5e39..ffa63bfd 100644 --- a/examples/menus.rb +++ b/examples/menus.rb @@ -3,6 +3,9 @@ require "rubygems" require "highline/import" +puts "Using: #{$terminal.terminal.class}" +puts + # The old way, using ask() and say()... choices = %w{ruby python perl} say("This is the old way using ask() and say()...") diff --git a/examples/overwrite.rb b/examples/overwrite.rb index 04f3d8b6..83d51a5e 100644 --- a/examples/overwrite.rb +++ b/examples/overwrite.rb @@ -8,6 +8,9 @@ require 'rubygems' require 'highline/import' +puts "Using: #{$terminal.terminal.class}" +puts + prompt = "here is your password:" ask( "#{prompt} <%= color('mypassword', RED, BOLD) %> (Press Any Key to blank) " diff --git a/examples/password.rb b/examples/password.rb index 87fff8dd..a9e591a4 100644 --- a/examples/password.rb +++ b/examples/password.rb @@ -3,5 +3,8 @@ require "rubygems" require "highline/import" +puts "Using: #{$terminal.terminal.class}" +puts + pass = ask("Enter your password: ") { |q| q.echo = false } puts "Your password is #{pass}!" diff --git a/examples/repeat_entry.rb b/examples/repeat_entry.rb index 753262ea..553b7a61 100644 --- a/examples/repeat_entry.rb +++ b/examples/repeat_entry.rb @@ -3,6 +3,9 @@ require "rubygems" require "highline/import" +puts "Using: #{$terminal.terminal.class}" +puts + tounge_twister = ask("... try saying that three times fast") do |q| q.gather = 3 q.verify_match = true diff --git a/lib/highline.rb b/lib/highline.rb index 2a917c22..08fbad4b 100755 --- a/lib/highline.rb +++ b/lib/highline.rb @@ -1,5 +1,6 @@ # coding: utf-8 +#-- # highline.rb # # Created by James Edward Gray II on 2005-04-26. @@ -21,6 +22,7 @@ require "highline/version" require "highline/statement" require "highline/list_renderer" +require "highline/builtin_styles" # # A HighLine object is a "high-level line oriented" shell over an input and an @@ -32,6 +34,8 @@ # checking, convert types, etc. # class HighLine + include BuiltinStyles + # An internal HighLine error. User code does not need to trap this. class QuestionError < StandardError # do nothing, just creating a unique error type @@ -50,16 +54,16 @@ class NoConfirmationQuestionError < QuestionError end # The setting used to disable color output. - @@use_color = true + @use_color = true # Pass +false+ to _setting_ to turn off HighLine's color escapes. def self.use_color=( setting ) - @@use_color = setting + @use_color = setting end # Returns true if HighLine is currently using color escapes. def self.use_color? - @@use_color + @use_color end # For checking if the current version of HighLine supports RGB colors @@ -70,16 +74,16 @@ def self.supports_rgb_color? end # The setting used to disable EOF tracking. - @@track_eof = true + @track_eof = true # Pass +false+ to _setting_ to turn off HighLine's EOF tracking. def self.track_eof=( setting ) - @@track_eof = setting + @track_eof = setting end # Returns true if HighLine is currently tracking EOF for input. def self.track_eof? - @@track_eof + @track_eof end def track_eof? @@ -87,21 +91,21 @@ def track_eof? end # The setting used to control color schemes. - @@color_scheme = nil + @color_scheme = nil # Pass ColorScheme to _setting_ to set a HighLine color scheme. def self.color_scheme=( setting ) - @@color_scheme = setting + @color_scheme = setting end # Returns the current color scheme. def self.color_scheme - @@color_scheme + @color_scheme end # Returns +true+ if HighLine is currently using a color scheme. def self.using_color_scheme? - not @@color_scheme.nil? + !!@color_scheme end # Reset HighLine to default. @@ -115,94 +119,6 @@ def self.reset_color_scheme self.color_scheme = nil end - # - # Embed in a String to clear all previous ANSI sequences. This *MUST* be - # done before the program exits! - # - - ERASE_LINE_STYLE = Style.new(:name=>:erase_line, :builtin=>true, :code=>"\e[K") # Erase the current line of terminal output - ERASE_CHAR_STYLE = Style.new(:name=>:erase_char, :builtin=>true, :code=>"\e[P") # Erase the character under the cursor. - CLEAR_STYLE = Style.new(:name=>:clear, :builtin=>true, :code=>"\e[0m") # Clear color settings - RESET_STYLE = Style.new(:name=>:reset, :builtin=>true, :code=>"\e[0m") # Alias for CLEAR. - BOLD_STYLE = Style.new(:name=>:bold, :builtin=>true, :code=>"\e[1m") # Bold; Note: bold + a color works as you'd expect, - # for example bold black. Bold without a color displays - # the system-defined bold color (e.g. red on Mac iTerm) - DARK_STYLE = Style.new(:name=>:dark, :builtin=>true, :code=>"\e[2m") # Dark; support uncommon - UNDERLINE_STYLE = Style.new(:name=>:underline, :builtin=>true, :code=>"\e[4m") # Underline - UNDERSCORE_STYLE = Style.new(:name=>:underscore, :builtin=>true, :code=>"\e[4m") # Alias for UNDERLINE - BLINK_STYLE = Style.new(:name=>:blink, :builtin=>true, :code=>"\e[5m") # Blink; support uncommon - REVERSE_STYLE = Style.new(:name=>:reverse, :builtin=>true, :code=>"\e[7m") # Reverse foreground and background - CONCEALED_STYLE = Style.new(:name=>:concealed, :builtin=>true, :code=>"\e[8m") # Concealed; support uncommon - - STYLES = %w{CLEAR RESET BOLD DARK UNDERLINE UNDERSCORE BLINK REVERSE CONCEALED} - - # These RGB colors are approximate; see http://en.wikipedia.org/wiki/ANSI_escape_code - BLACK_STYLE = Style.new(:name=>:black, :builtin=>true, :code=>"\e[30m", :rgb=>[ 0, 0, 0]) - RED_STYLE = Style.new(:name=>:red, :builtin=>true, :code=>"\e[31m", :rgb=>[128, 0, 0]) - GREEN_STYLE = Style.new(:name=>:green, :builtin=>true, :code=>"\e[32m", :rgb=>[ 0,128, 0]) - BLUE_STYLE = Style.new(:name=>:blue, :builtin=>true, :code=>"\e[34m", :rgb=>[ 0, 0,128]) - YELLOW_STYLE = Style.new(:name=>:yellow, :builtin=>true, :code=>"\e[33m", :rgb=>[128,128, 0]) - MAGENTA_STYLE = Style.new(:name=>:magenta, :builtin=>true, :code=>"\e[35m", :rgb=>[128, 0,128]) - CYAN_STYLE = Style.new(:name=>:cyan, :builtin=>true, :code=>"\e[36m", :rgb=>[ 0,128,128]) - # On Mac OSX Terminal, white is actually gray - WHITE_STYLE = Style.new(:name=>:white, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192]) - # Alias for WHITE, since WHITE is actually a light gray on Macs - GRAY_STYLE = Style.new(:name=>:gray, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192]) - GREY_STYLE = Style.new(:name=>:grey, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192]) - # On Mac OSX Terminal, this is black foreground, or bright white background. - # Also used as base for RGB colors, if available - NONE_STYLE = Style.new(:name=>:none, :builtin=>true, :code=>"\e[38m", :rgb=>[ 0, 0, 0]) - - BASIC_COLORS = %w{BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE GRAY GREY NONE} - - colors = BASIC_COLORS.dup - BASIC_COLORS.each do |color| - bright_color = "BRIGHT_#{color}" - colors << bright_color - const_set bright_color+'_STYLE', const_get(color + '_STYLE').bright - - light_color = "LIGHT_#{color}" - colors << light_color - const_set light_color+'_STYLE', const_get(color + '_STYLE').light - end - COLORS = colors - - colors.each do |color| - const_set color, const_get("#{color}_STYLE").code - const_set "ON_#{color}_STYLE", const_get("#{color}_STYLE").on - const_set "ON_#{color}", const_get("ON_#{color}_STYLE").code - end - ON_NONE_STYLE.rgb = [255,255,255] # Override; white background - - STYLES.each do |style| - const_set style, const_get("#{style}_STYLE").code - end - - # For RGB colors: - def self.const_missing(name) - if name.to_s =~ /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/ # RGB color - on = $1 - suffix = $4 - if suffix - code_name = $1.to_s + $2 + $3 - else - code_name = name.to_s - end - style_name = code_name + '_STYLE' - style = Style.rgb($3) - style = style.on if on - const_set(style_name, style) - const_set(code_name, style.code) - if suffix - style - else - style.code - end - else - raise NameError, "Bad color or uninitialized constant #{name}" - end - end - # # Create an instance of HighLine, connected to the streams _input_ # and _output_. @@ -219,12 +135,11 @@ def initialize( input = $stdin, output = $stdout, self.wrap_at = wrap_at self.page_at = page_at - @question = nil @header = nil @prompt = nil @key = nil - @terminal = HighLine::Terminal.get_terminal + @terminal = HighLine::Terminal.get_terminal(input, output) end # The current column setting for wrapping output. @@ -242,8 +157,6 @@ def initialize( input = $stdin, output = $stdout, attr_reader :key - attr_reader :question - # System specific that responds to #initialize_system_extensions, # #terminal_size, #raw_no_echo_mode, #restore_mode, #get_character. # It polymorphically handles specific cases for different platforms. @@ -277,20 +190,11 @@ def agree( yes_or_no_question, character = nil ) # HighLine::Question for more information about _answer_type_ and what's # valid in the code block. # - # If @question is set before ask() is called, parameters are - # ignored and that object (must be a HighLine::Question) is used to drive - # the process instead. - # # Raises EOFError if input is exhausted. # - def ask(template_or_question, answer_type = nil, options = {}, &details) # :yields: question - if template_or_question.is_a? Question - @question = template_or_question - else - @question = Question.new(template_or_question, answer_type, &details) - end - - return question.ask_at(self) + def ask(template_or_question, answer_type = nil, &details) + question = Question.build(template_or_question, answer_type, &details) + question.ask_at(self) end # @@ -390,24 +294,24 @@ def list(items, mode = :rows, option = nil) # ends with a space or tab character, a newline will not be appended (output # will be flush()ed). All other cases are passed straight to Kernel.puts(). # - # The _statement_ parameter is processed as an ERb template, supporting - # embedded Ruby code. The template is evaluated with a binding inside - # the HighLine instance, providing easy access to the ANSI color constants - # and the HighLine.color() method. + # The _statement_ argument is processed as an ERb template, supporting + # embedded Ruby code. The template is evaluated within a HighLine + # instance's binding for providing easy access to the ANSI color constants + # and the HighLine#color() method. # - def say( statement ) + def say(statement) statement = render_statement(statement) return if statement.empty? - out = (indentation+statement) + statement = (indentation+statement) # Don't add a newline if statement ends with whitespace, OR # if statement ends with whitespace before a color escape code. if /[ \t](\e\[\d+(;\d+)*m)?\Z/ =~ statement - @output.print(out) - @output.flush + output.print(statement) + output.flush else - @output.puts(out) + output.puts(statement) end end @@ -508,7 +412,7 @@ def new_scope # of the question. # def explain_error(error, question) - say(question.responses[error]) unless error.nil? + say(question.responses[error]) if error say(question.ask_on_error_msg) end @@ -522,7 +426,7 @@ def ask_once(question) # the prompt will not be issued. And we have to account for that now. # Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt # to handle line editing properly. - say(question) unless ((question.readline) and (question.echo == true and question.limit.nil?)) + say(question) unless ((question.readline) and (question.echo == true and !question.limit)) begin question.get_response_or_default(self) @@ -683,13 +587,13 @@ def get_line(question) end def get_response_line_mode(question) - if question.echo == true and question.limit.nil? + if question.echo == true and !question.limit get_line(question) else line = "" terminal.raw_no_echo_mode_exec do - while character = terminal.get_character(@input) + while character = terminal.get_character break if character == "\n" or character == "\r" # honor backspace and delete @@ -732,7 +636,7 @@ def get_response_getc_mode(question) def get_response_character_mode(question) terminal.raw_no_echo_mode_exec do - response = terminal.get_character(@input) + response = terminal.get_character if question.overwrite erase_current_line else @@ -766,4 +670,4 @@ def actual_length(text) end end -require "highline/string_extensions" +require "highline/string" diff --git a/lib/highline/builtin_styles.rb b/lib/highline/builtin_styles.rb new file mode 100644 index 00000000..4c0a3b4f --- /dev/null +++ b/lib/highline/builtin_styles.rb @@ -0,0 +1,98 @@ +#coding: utf-8 + +class HighLine + module BuiltinStyles + def self.included(base) + base.extend ClassMethods + end + # + # Embed in a String to clear all previous ANSI sequences. This *MUST* be + # done before the program exits! + # + + ERASE_LINE_STYLE = Style.new(:name=>:erase_line, :builtin=>true, :code=>"\e[K") # Erase the current line of terminal output + ERASE_CHAR_STYLE = Style.new(:name=>:erase_char, :builtin=>true, :code=>"\e[P") # Erase the character under the cursor. + CLEAR_STYLE = Style.new(:name=>:clear, :builtin=>true, :code=>"\e[0m") # Clear color settings + RESET_STYLE = Style.new(:name=>:reset, :builtin=>true, :code=>"\e[0m") # Alias for CLEAR. + BOLD_STYLE = Style.new(:name=>:bold, :builtin=>true, :code=>"\e[1m") # Bold; Note: bold + a color works as you'd expect, + # for example bold black. Bold without a color displays + # the system-defined bold color (e.g. red on Mac iTerm) + DARK_STYLE = Style.new(:name=>:dark, :builtin=>true, :code=>"\e[2m") # Dark; support uncommon + UNDERLINE_STYLE = Style.new(:name=>:underline, :builtin=>true, :code=>"\e[4m") # Underline + UNDERSCORE_STYLE = Style.new(:name=>:underscore, :builtin=>true, :code=>"\e[4m") # Alias for UNDERLINE + BLINK_STYLE = Style.new(:name=>:blink, :builtin=>true, :code=>"\e[5m") # Blink; support uncommon + REVERSE_STYLE = Style.new(:name=>:reverse, :builtin=>true, :code=>"\e[7m") # Reverse foreground and background + CONCEALED_STYLE = Style.new(:name=>:concealed, :builtin=>true, :code=>"\e[8m") # Concealed; support uncommon + + STYLES = %w{CLEAR RESET BOLD DARK UNDERLINE UNDERSCORE BLINK REVERSE CONCEALED} + + # These RGB colors are approximate; see http://en.wikipedia.org/wiki/ANSI_escape_code + BLACK_STYLE = Style.new(:name=>:black, :builtin=>true, :code=>"\e[30m", :rgb=>[ 0, 0, 0]) + RED_STYLE = Style.new(:name=>:red, :builtin=>true, :code=>"\e[31m", :rgb=>[128, 0, 0]) + GREEN_STYLE = Style.new(:name=>:green, :builtin=>true, :code=>"\e[32m", :rgb=>[ 0,128, 0]) + BLUE_STYLE = Style.new(:name=>:blue, :builtin=>true, :code=>"\e[34m", :rgb=>[ 0, 0,128]) + YELLOW_STYLE = Style.new(:name=>:yellow, :builtin=>true, :code=>"\e[33m", :rgb=>[128,128, 0]) + MAGENTA_STYLE = Style.new(:name=>:magenta, :builtin=>true, :code=>"\e[35m", :rgb=>[128, 0,128]) + CYAN_STYLE = Style.new(:name=>:cyan, :builtin=>true, :code=>"\e[36m", :rgb=>[ 0,128,128]) + # On Mac OSX Terminal, white is actually gray + WHITE_STYLE = Style.new(:name=>:white, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192]) + # Alias for WHITE, since WHITE is actually a light gray on Macs + GRAY_STYLE = Style.new(:name=>:gray, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192]) + GREY_STYLE = Style.new(:name=>:grey, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192]) + # On Mac OSX Terminal, this is black foreground, or bright white background. + # Also used as base for RGB colors, if available + NONE_STYLE = Style.new(:name=>:none, :builtin=>true, :code=>"\e[38m", :rgb=>[ 0, 0, 0]) + + BASIC_COLORS = %w{BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE GRAY GREY NONE} + + colors = BASIC_COLORS.dup + BASIC_COLORS.each do |color| + bright_color = "BRIGHT_#{color}" + colors << bright_color + const_set bright_color+'_STYLE', const_get(color + '_STYLE').bright + + light_color = "LIGHT_#{color}" + colors << light_color + const_set light_color+'_STYLE', const_get(color + '_STYLE').light + end + COLORS = colors + + colors.each do |color| + const_set color, const_get("#{color}_STYLE").code + const_set "ON_#{color}_STYLE", const_get("#{color}_STYLE").on + const_set "ON_#{color}", const_get("ON_#{color}_STYLE").code + end + ON_NONE_STYLE.rgb = [255,255,255] # Override; white background + + STYLES.each do |style| + const_set style, const_get("#{style}_STYLE").code + end + + module ClassMethods + # For RGB colors: + def const_missing(name) + if name.to_s =~ /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/ # RGB color + on = $1 + suffix = $4 + if suffix + code_name = $1.to_s + $2 + $3 + else + code_name = name.to_s + end + style_name = code_name + '_STYLE' + style = Style.rgb($3) + style = style.on if on + const_set(style_name, style) + const_set(code_name, style.code) + if suffix + style + else + style.code + end + else + raise NameError, "Bad color or uninitialized constant #{name}" + end + end + end + end +end \ No newline at end of file diff --git a/lib/highline/color_scheme.rb b/lib/highline/color_scheme.rb index 0d07c423..1d2e2840 100644 --- a/lib/highline/color_scheme.rb +++ b/lib/highline/color_scheme.rb @@ -1,5 +1,6 @@ # coding: utf-8 +#-- # color_scheme.rb # # Created by Jeremy Hinegardner on 2007-01-24 @@ -50,7 +51,7 @@ class ColorScheme # def initialize( h = nil ) @scheme = Hash.new - load_from_hash(h) unless h.nil? + load_from_hash(h) if h yield self if block_given? end diff --git a/lib/highline/import.rb b/lib/highline/import.rb index 7a0e6fa8..ce6ec0ec 100644 --- a/lib/highline/import.rb +++ b/lib/highline/import.rb @@ -35,9 +35,9 @@ class Object # def or_ask( *args, &details ) ask(*args) do |question| - question.first_answer = String(self) unless nil? + question.first_answer = String(self) - details.call(question) unless details.nil? + details.call(question) if details end end end diff --git a/lib/highline/menu.rb b/lib/highline/menu.rb index 71c4f474..425a98d6 100644 --- a/lib/highline/menu.rb +++ b/lib/highline/menu.rb @@ -1,5 +1,6 @@ # coding: utf-8 +#-- # menu.rb # # Created by Gregory Thomas Brown on 2005-05-10. @@ -138,7 +139,7 @@ def initialize( ) def choice( name, help = nil, &action ) @items << [name, action] - @help[name.to_s.downcase] = help unless help.nil? + @help[name.to_s.downcase] = help if help update_responses # rebuild responses based on our settings end @@ -156,7 +157,7 @@ def choices( *names, &action ) def hidden( name, help = nil, &action ) @hidden_items << [name, action] - @help[name.to_s.downcase] = help unless help.nil? + @help[name.to_s.downcase] = help if help end # @@ -305,7 +306,7 @@ def select( highline_context, selection, details = nil ) end # Run or return it. - if not action.nil? + if action @highline = highline_context if @shell result = action.call(name, details) @@ -313,10 +314,8 @@ def select( highline_context, selection, details = nil ) result = action.call(name) end @nil_on_handled ? nil : result - elsif action.nil? - name else - nil + name end ensure # make sure the hidden items are removed, before we return @@ -349,12 +348,12 @@ def to_ary( ) def to_s( ) case @layout when :list - '<%= if header.nil? then '' else "#{header}:\n" end %>' + + %(<%= header ? "#{header}:\n" : '' %>) + "<%= list( menu, #{@flow.inspect}, #{@list_option.inspect} ) %>" + "<%= prompt %>" when :one_line - '<%= if header.nil? then '' else "#{header}: " end %>' + + %(<%= header ? "#{header}: " : '' %>) + "<%= prompt %>" + "(<%= list( menu, #{@flow.inspect}, #{@list_option.inspect} ) %>)" + diff --git a/lib/highline/question.rb b/lib/highline/question.rb index 97b6f655..5723ce76 100755 --- a/lib/highline/question.rb +++ b/lib/highline/question.rb @@ -1,5 +1,6 @@ # coding: utf-8 +#-- # question.rb # # Created by James Edward Gray II on 2005-04-26. @@ -25,6 +26,18 @@ class NoAutoCompleteMatch < StandardError # do nothing, just creating a unique error type end + # + # If _template_or_question_ is already a Question object just return it. + # If not, build it. + # + def self.build(template_or_question, answer_type = nil, &details) + if template_or_question.is_a? Question + template_or_question + else + Question.new(template_or_question, answer_type, &details) + end + end + # # Create an instance of HighLine::Question. Expects a _template_ to ask # (can be "") and an _answer_type_ to convert the answer to. @@ -65,8 +78,6 @@ def initialize( template, answer_type ) build_responses end - attr_reader :directory - # The ERb template of the question to be asked. attr_accessor :template @@ -334,9 +345,9 @@ def choices_complete(answer_string) def expected_range( ) expected = [ ] - expected << "above #{@above}" unless @above.nil? - expected << "below #{@below}" unless @below.nil? - expected << "included in #{@in.inspect}" unless @in.nil? + expected << "above #{@above}" if @above + expected << "below #{@below}" if @below + expected << "included in #{@in.inspect}" if @in case expected.size when 0 then "" @@ -355,7 +366,7 @@ def first_answer( ) # Returns true if _first_answer_ is set. def first_answer?( ) - not @first_answer.nil? + !!@first_answer end # @@ -365,9 +376,9 @@ def first_answer?( ) # are not checked. # def in_range? - (@above.nil? or answer > @above) and - (@below.nil? or answer < @below) and - (@in.nil? or @in.include?(answer)) + (!@above or answer > @above) and + (!@below or answer < @below) and + (!@in or @in.include?(answer)) end # @@ -390,7 +401,7 @@ def in_range? # This process is skipped for single character input. # def remove_whitespace( answer_string ) - if @whitespace.nil? + if !@whitespace answer_string elsif [:strip, :chomp].include?(@whitespace) answer_string.send(@whitespace) @@ -442,7 +453,7 @@ def to_s # and case handling. # def valid_answer? - @validate.nil? or + !@validate or (@validate.is_a?(Regexp) and answer =~ @validate) or (@validate.is_a?(Proc) and @validate[answer]) end diff --git a/lib/highline/simulate.rb b/lib/highline/simulate.rb index 4bad8f96..4bc1cc99 100644 --- a/lib/highline/simulate.rb +++ b/lib/highline/simulate.rb @@ -1,5 +1,6 @@ # coding: utf-8 +#-- # simulate.rb # # Created by Andy Rossmeissl on 2012-04-29. @@ -8,6 +9,7 @@ # This is Free Software. See LICENSE and COPYING for details. # # adapted from https://gist.github.com/194554 + class HighLine # Simulates Highline input for use in tests. diff --git a/lib/highline/string.rb b/lib/highline/string.rb new file mode 100644 index 00000000..94a82717 --- /dev/null +++ b/lib/highline/string.rb @@ -0,0 +1,34 @@ +# coding: utf-8 + +require "highline/string_extensions" + +# +# HighLine::String is a subclass of String with convenience methods added for colorization. +# +# Available convenience methods include: +# * 'color' method e.g. highline_string.color(:bright_blue, :underline) +# * colors e.g. highline_string.magenta +# * RGB colors e.g. highline_string.rgb_ff6000 +# or highline_string.rgb(255,96,0) +# * background colors e.g. highline_string.on_magenta +# * RGB background colors e.g. highline_string.on_rgb_ff6000 +# or highline_string.on_rgb(255,96,0) +# * styles e.g. highline_string.underline +# +# Additionally, convenience methods can be chained, for instance the following are equivalent: +# highline_string.bright_blue.blink.underline +# highline_string.color(:bright_blue, :blink, :underline) +# HighLine.color(highline_string, :bright_blue, :blink, :underline) +# +# For those less squeamish about possible conflicts, the same convenience methods can be +# added to the built-in String class, as follows: +# +# require 'highline' +# Highline.colorize_strings +# + +class HighLine + class String < ::String + include StringExtensions + end +end \ No newline at end of file diff --git a/lib/highline/string_extensions.rb b/lib/highline/string_extensions.rb index a094bb4b..97376e41 100644 --- a/lib/highline/string_extensions.rb +++ b/lib/highline/string_extensions.rb @@ -1,35 +1,12 @@ # coding: utf-8 -# Extensions for class String -# -# HighLine::String is a subclass of String with convenience methods added for colorization. -# -# Available convenience methods include: -# * 'color' method e.g. highline_string.color(:bright_blue, :underline) -# * colors e.g. highline_string.magenta -# * RGB colors e.g. highline_string.rgb_ff6000 -# or highline_string.rgb(255,96,0) -# * background colors e.g. highline_string.on_magenta -# * RGB background colors e.g. highline_string.on_rgb_ff6000 -# or highline_string.on_rgb(255,96,0) -# * styles e.g. highline_string.underline -# -# Additionally, convenience methods can be chained, for instance the following are equivalent: -# highline_string.bright_blue.blink.underline -# highline_string.color(:bright_blue, :blink, :underline) -# HighLine.color(highline_string, :bright_blue, :blink, :underline) -# -# For those less squeamish about possible conflicts, the same convenience methods can be -# added to the built-in String class, as follows: -# -# require 'highline' -# Highline.colorize_strings - class HighLine def self.String(s) HighLine::String.new(s) end + # HighLine extensions for String class + # Included by HighLine::String module StringExtensions def self.included(base) HighLine::COLORS.each do |color| @@ -103,10 +80,6 @@ def method_missing(method, *args, &blk) end end - class HighLine::String < ::String - include StringExtensions - end - def self.colorize_strings ::String.send(:include, StringExtensions) end diff --git a/lib/highline/style.rb b/lib/highline/style.rb index 72871b68..b3b65cd4 100755 --- a/lib/highline/style.rb +++ b/lib/highline/style.rb @@ -1,5 +1,6 @@ # coding: utf-8 +#-- # color_scheme.rb # # Created by Richard LeBer on 2011-06-27. @@ -44,23 +45,23 @@ class Style def self.index(style) if style.name - @@styles ||= {} - @@styles[style.name] = style + @styles ||= {} + @styles[style.name] = style end if !style.list - @@code_index ||= {} - @@code_index[style.code] ||= [] - @@code_index[style.code].reject!{|indexed_style| indexed_style.name == style.name} - @@code_index[style.code] << style + @code_index ||= {} + @code_index[style.code] ||= [] + @code_index[style.code].reject!{|indexed_style| indexed_style.name == style.name} + @code_index[style.code] << style end style end def self.clear_index # reset to builtin only styles - @@styles = list.select { |name, style| style.builtin } - @@code_index = {} - @@styles.each { |name, style| index(style) } + @styles = list.select { |name, style| style.builtin } + @code_index = {} + @styles.each { |name, style| index(style) } end def self.rgb_hex(*colors) @@ -96,11 +97,11 @@ def self.ansi_rgb_to_hex(ansi_number) end def self.list - @@styles ||= {} + @styles ||= {} end def self.code_index - @@code_index ||= {} + @code_index ||= {} end def self.uncolor(string) diff --git a/lib/highline/template_renderer.rb b/lib/highline/template_renderer.rb index ea439053..5b495f20 100644 --- a/lib/highline/template_renderer.rb +++ b/lib/highline/template_renderer.rb @@ -6,7 +6,7 @@ class HighLine class TemplateRenderer extend Forwardable - def_delegators :@highline, :color, :list, :key, :question + def_delegators :@highline, :color, :list, :key def_delegators :@source, :answer_type, :prompt, :header, :answer attr_reader :template, :source, :highline diff --git a/lib/highline/terminal.rb b/lib/highline/terminal.rb index b23fe653..695a16d5 100755 --- a/lib/highline/terminal.rb +++ b/lib/highline/terminal.rb @@ -1,5 +1,6 @@ # coding: utf-8 +#-- # terminal.rb # # Originally created by James Edward Gray II on 2006-06-14 as @@ -12,13 +13,34 @@ class HighLine class Terminal - def self.get_terminal - require 'highline/terminal/unix_stty' - terminal = HighLine::Terminal::UnixStty.new + def self.get_terminal(input, output) + terminal = nil + + # First of all, probe for io/console + begin + require "io/console" + require "highline/terminal/io_console" + terminal = HighLine::Terminal::IOConsole.new(input, output) + rescue LoadError + end + + # Fall back to UnixStty + unless terminal + require 'highline/terminal/unix_stty' + terminal = HighLine::Terminal::UnixStty.new(input, output) + end + terminal.initialize_system_extensions terminal end + attr_reader :input, :output + + def initialize(input, output) + @input = input + @output = output + end + def initialize_system_extensions end @@ -30,11 +52,9 @@ def raw_no_echo_mode def raw_no_echo_mode_exec raw_no_echo_mode - begin - yield - ensure - restore_mode - end + yield + ensure + restore_mode end def restore_mode @@ -46,5 +66,13 @@ def get_character def jruby? defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' end + + def rubinius? + defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx' + end + + def windows? + defined?(RUBY_PLATFORM) && (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) + end end end diff --git a/lib/highline/terminal/io_console.rb b/lib/highline/terminal/io_console.rb new file mode 100644 index 00000000..5515fddf --- /dev/null +++ b/lib/highline/terminal/io_console.rb @@ -0,0 +1,98 @@ +# coding: utf-8 + +class HighLine + class Terminal + class IOConsole < Terminal + def terminal_size + output.winsize.reverse + end + + CHARACTER_MODE = "io_console" # For Debugging purposes only. + + def raw_no_echo_mode + input.echo = false + end + + def restore_mode + input.echo = true + end + + def get_character + input.getch # from ruby io/console + end + + def character_mode + "io_console" + end + + def get_line(question, highline, options={}) + raw_answer = + if question.readline + get_line_with_readline(question, highline, options={}) + else + get_line_default(highline) + end + + question.format_answer(raw_answer) + end + + def get_line_with_readline(question, highline, options={}) + require "readline" # load only if needed + + question_string = highline.render_statement(question) + + raw_answer = readline_read(question_string, question) + + if !raw_answer and highline.track_eof? + raise EOFError, "The input stream is exhausted." + end + + raw_answer || "" + end + + def readline_read(string, question) + # prep auto-completion + unless question.selection.empty? + Readline.completion_proc = lambda do |str| + question.selection.grep(/\A#{Regexp.escape(str)}/) + end + end + + # work-around ugly readline() warnings + old_verbose = $VERBOSE + $VERBOSE = nil + + raw_answer = run_preserving_stty do + Readline.readline(string, true) + end + + $VERBOSE = old_verbose + + raw_answer + end + + def get_line_default(highline) + raise EOFError, "The input stream is exhausted." if highline.track_eof? and + highline.input.eof? + highline.input.gets + end + + private + + def run_preserving_stty + save_stty + yield + ensure + restore_stty + end + + def save_stty + @stty_save = `stty -g`.chomp rescue nil + end + + def restore_stty + system("stty", @stty_save) if @stty_save + end + end + end +end \ No newline at end of file diff --git a/lib/highline/terminal/jruby.rb b/lib/highline/terminal/jruby.rb deleted file mode 100644 index 2c1b6da8..00000000 --- a/lib/highline/terminal/jruby.rb +++ /dev/null @@ -1,37 +0,0 @@ -# coding: utf-8 - -class HighLine - module SystemExtensions - module JRuby - def initialize_system_extensions - require 'java' - require 'readline' - if JRUBY_VERSION =~ /^1.7/ - java_import 'jline.console.ConsoleReader' - - input = @input && @input.to_inputstream - output = @output && @output.to_outputstream - - @java_console = ConsoleReader.new(input, output) - @java_console.set_history_enabled(false) - @java_console.set_bell_enabled(true) - @java_console.set_pagination_enabled(false) - @java_terminal = @java_console.getTerminal - elsif JRUBY_VERSION =~ /^1.6/ - java_import 'java.io.OutputStreamWriter' - java_import 'java.nio.channels.Channels' - java_import 'jline.ConsoleReader' - java_import 'jline.Terminal' - - @java_input = Channels.newInputStream(@input.to_channel) - @java_output = OutputStreamWriter.new(Channels.newOutputStream(@output.to_channel)) - @java_terminal = Terminal.getTerminal - @java_console = ConsoleReader.new(@java_input, @java_output) - @java_console.setUseHistory(false) - @java_console.setBellEnabled(true) - @java_console.setUsePagination(false) - end - end - end - end -end \ No newline at end of file diff --git a/lib/highline/terminal/jruby_jline.rb b/lib/highline/terminal/jruby_jline.rb deleted file mode 100644 index 8e9a47db..00000000 --- a/lib/highline/terminal/jruby_jline.rb +++ /dev/null @@ -1,35 +0,0 @@ -# coding: utf-8 - -class HighLine - module SystemExtensions - module JRubyJLine - CHARACTER_MODE = "jline" # For Debugging purposes only. - - def terminal_size - if JRUBY_VERSION =~ /^1.7/ - [ @java_terminal.get_width, @java_terminal.get_height ] - else - [ @java_terminal.getTerminalWidth, @java_terminal.getTerminalHeight ] - end - end - - def raw_no_echo_mode - @state = @java_console.getEchoCharacter - @java_console.setEchoCharacter 0 - end - - def restore_mode - @java_console.setEchoCharacter @state - end - - # Saving this legacy JRuby code for future reference - def get_line(question, highline, options={}) - statement = highline.render_statement(question) - raw_answer = @java_console.readLine(statement, nil) - - raise EOFError, "The input stream is exhausted." if raw_answer.nil? and - highline.track_eof? - end - end - end -end \ No newline at end of file diff --git a/lib/highline/terminal/ncurses.rb b/lib/highline/terminal/ncurses.rb index cb2b593d..10453138 100644 --- a/lib/highline/terminal/ncurses.rb +++ b/lib/highline/terminal/ncurses.rb @@ -1,5 +1,10 @@ # coding: utf-8 +# TODO: +# Code below to be discussed. +# Will we maintain an ncurses version of HighLine::Terminal? +# If so, port it to the new api. + class HighLine module SystemExtensions module NCurses diff --git a/lib/highline/terminal/stty.rb b/lib/highline/terminal/stty.rb deleted file mode 100644 index 695281cc..00000000 --- a/lib/highline/terminal/stty.rb +++ /dev/null @@ -1,20 +0,0 @@ -# coding: utf-8 - -class HighLine - module SystemExtensions - module Stty - # *WARNING*: This requires the external "stty" program! - CHARACTER_MODE = "stty" # For Debugging purposes only. - - def raw_no_echo_mode - @state = `stty -g` - system "stty raw -echo -icanon isig" - end - - def restore_mode - system "stty #{@state}" - print "\r" - end - end - end -end \ No newline at end of file diff --git a/lib/highline/terminal/unix_stty.rb b/lib/highline/terminal/unix_stty.rb index ba5cc4b7..1d9b3d35 100644 --- a/lib/highline/terminal/unix_stty.rb +++ b/lib/highline/terminal/unix_stty.rb @@ -62,7 +62,7 @@ def get_line_with_readline(question, highline, options={}) raw_answer = readline_read(question_string, question) - if raw_answer.nil? and highline.track_eof? + if !raw_answer and highline.track_eof? raise EOFError, "The input stream is exhausted." end diff --git a/lib/highline/terminal/unix_termios.rb b/lib/highline/terminal/unix_termios.rb deleted file mode 100644 index 3a0a7993..00000000 --- a/lib/highline/terminal/unix_termios.rb +++ /dev/null @@ -1,23 +0,0 @@ -# coding: utf-8 - -class HighLine - module SystemExtensions - module UnixTermios - require "termios" # Unix, first choice termios. - - CHARACTER_MODE = "termios" # For Debugging purposes only. - - def raw_no_echo_mode - @state = Termios.getattr(@input) - new_settings = @state.dup - new_settings.c_lflag &= ~(Termios::ECHO | Termios::ICANON) - new_settings.c_cc[Termios::VMIN] = 1 - Termios.setattr(@input, Termios::TCSANOW, new_settings) - end - - def restore_mode - Termios.setattr(@input, Termios::TCSANOW, @state) - end - end - end -end \ No newline at end of file diff --git a/lib/highline/terminal/windows.rb b/lib/highline/terminal/windows.rb deleted file mode 100644 index 81b1d671..00000000 --- a/lib/highline/terminal/windows.rb +++ /dev/null @@ -1,38 +0,0 @@ -# coding: utf-8 - -class HighLine - module SystemExtensions - module Windows - CHARACTER_MODE = "Win32API" # For Debugging purposes only. - - # - # Windows savvy getc(). - # - # *WARNING*: This method ignores input and reads one - # character from +STDIN+! - # - def get_character( input = STDIN ) - WinAPI._getch - end - - # We do not define a raw_no_echo_mode for Windows as _getch turns off echo - def raw_no_echo_mode - end - - def restore_mode - end - - # A Windows savvy method to fetch the console columns, and rows. - def terminal_size - format = 'SSSSSssssSS' - buf = ([0] * format.size).pack(format) - stdout_handle = WinAPI.GetStdHandle(0xFFFFFFF5) - - WinAPI.GetConsoleScreenBufferInfo(stdout_handle, buf) - _, _, _, _, _, - left, top, right, bottom, _, _ = buf.unpack(format) - return right - left + 1, bottom - top + 1 - end - end - end -end diff --git a/lib/highline/terminal/windows_dl_import.rb b/lib/highline/terminal/windows_dl_import.rb deleted file mode 100644 index 721f0485..00000000 --- a/lib/highline/terminal/windows_dl_import.rb +++ /dev/null @@ -1,37 +0,0 @@ -# coding: utf-8 - -class HighLine - module SystemExtensions - module WindowsDlImport - require "dl/import" - - module WinAPI - if defined?(DL::Importer) - # Ruby 1.9 - extend DL::Importer - else - # Ruby 1.8 - extend DL::Importable - end - begin - dlload "msvcrt", "kernel32" - rescue DL::DLError - dlload "crtdll", "kernel32" - end - extern "unsigned long _getch()" - extern "unsigned long GetConsoleScreenBufferInfo(unsigned long, void*)" - extern "unsigned long GetStdHandle(unsigned long)" - - # Ruby 1.8 DL::Importable.import does mname[0,1].downcase so FooBar becomes fooBar - if defined?(getConsoleScreenBufferInfo) - alias_method :GetConsoleScreenBufferInfo, :getConsoleScreenBufferInfo - module_function :GetConsoleScreenBufferInfo - end - if defined?(getStdHandle) - alias_method :GetStdHandle, :getStdHandle - module_function :GetStdHandle - end - end - end - end -end diff --git a/lib/highline/terminal/windows_fiddle.rb b/lib/highline/terminal/windows_fiddle.rb deleted file mode 100644 index 7e0c8442..00000000 --- a/lib/highline/terminal/windows_fiddle.rb +++ /dev/null @@ -1,32 +0,0 @@ -# coding: utf-8 - -class HighLine - module SystemExtensions - module WindowsFiddle - require "fiddle" - - module WinAPI - include Fiddle - Handle = RUBY_VERSION >= "2.0.0" ? Fiddle::Handle : DL::Handle - Kernel32 = Handle.new("kernel32") - Crt = Handle.new("msvcrt") rescue Handle.new("crtdll") - - def self._getch - @@_m_getch ||= Function.new(Crt["_getch"], [], TYPE_INT) - @@_m_getch.call - end - - def self.GetStdHandle(handle_type) - @@get_std_handle ||= Function.new(Kernel32["GetStdHandle"], [-TYPE_INT], -TYPE_INT) - @@get_std_handle.call(handle_type) - end - - def self.GetConsoleScreenBufferInfo(cons_handle, lp_buffer) - @@get_console_screen_buffer_info ||= - Function.new(Kernel32["GetConsoleScreenBufferInfo"], [TYPE_LONG, TYPE_VOIDP], TYPE_INT) - @@get_console_screen_buffer_info.call(cons_handle, lp_buffer) - end - end - end - end -end \ No newline at end of file diff --git a/lib/highline/version.rb b/lib/highline/version.rb index 288e4fa0..d7cfecdf 100644 --- a/lib/highline/version.rb +++ b/lib/highline/version.rb @@ -2,5 +2,5 @@ class HighLine # The version of the installed library. - VERSION = "2.0.0-develop.1".freeze + VERSION = "2.0.0-develop.2".freeze end diff --git a/test/acceptance/acceptance.rb b/test/acceptance/acceptance.rb new file mode 100644 index 00000000..edef4581 --- /dev/null +++ b/test/acceptance/acceptance.rb @@ -0,0 +1,62 @@ +#!/usr/bin/env ruby +# coding: utf-8 + +current_dir = File.dirname(File.expand_path(__FILE__)) + +# All acceptance test files begins with 'at_' +acceptance_test_files = Dir["#{current_dir}/at_*"] + +# Load each acceptance test file making +# all tests to be run +acceptance_test_files.each { |file| load file } + +# Print a report + +report = < should be green!" + end + + t.question = "Do you see the word 'grass' on green color (y/n)? " +end diff --git a/test/acceptance/at_echo_false.rb b/test/acceptance/at_echo_false.rb new file mode 100644 index 00000000..2466b8d7 --- /dev/null +++ b/test/acceptance/at_echo_false.rb @@ -0,0 +1,23 @@ +# coding: utf-8 + +require_relative 'acceptance_test' + +HighLine::AcceptanceTest.check do |t| + t.desc = + "This step checks if the 'echo = false' " \ + "setting is effective in hiding the user " \ + "typed characters.\n" \ + "This functionality is useful when asking " \ + "for passwords.\n" \ + "When typing the characters you should not " \ + "see any of them on the screen." + + t.action = Proc.new do + answer = ask "Enter some characters and press : " do |q| + q.echo = false + end + puts "You've entered -> #{answer} <-" + end + + t.question = "Were the characters adequately hidden when you typed them (y/n)? " +end diff --git a/test/acceptance/at_readline.rb b/test/acceptance/at_readline.rb new file mode 100644 index 00000000..8a66f092 --- /dev/null +++ b/test/acceptance/at_readline.rb @@ -0,0 +1,37 @@ +# coding: utf-8 + +require_relative 'acceptance_test' + +HighLine::AcceptanceTest.check do |t| + t.desc = + "This step checks if the readline autocomplete " \ + "feature is working. \n" \ + "The test has 5 options you can choose from: " \ + "save, sample, exec, exit and load.\n" \ + "If you type the first character of one of them and then press \n" \ + "the key you should see the options available for autocomplete.\n\n" \ + "For example, if I type 's' and then I press I should see a list\n" \ + "with 'save' and 'sample' as possible options for autocomplete.\n\n" \ + "Although, if I type 'l' and then press the key it should be \n" \ + "readly autcompleted as 'load', because 'load' is the only option\n" \ + "that begins with the 'l' letter in this particular case.\n\n" \ + "If I don't type any character but press two times, I should\n" \ + "be able to see ALL available options.\n\n" \ + "Please, play with Readline autocomplete for a while, pressing \n" \ + "to see that it really gets the selected answer.\n" \ + "When ready, just type 'exit' and the loop will finish.\n\n" \ + "Don't forget to answer 'y' (yes) or 'n' (no) to the question at the end." + + t.action = Proc.new do + loop do + cmd = + ask "Enter command: ", %w{ save sample exec exit load } do |q| + q.readline = true + end + say("Executing \"#{cmd}\"...") + break if cmd == "exit" + end + end + + t.question = "Did the Readline autocomplete work fine (y/n)? " +end diff --git a/test/io_console_compatible.rb b/test/io_console_compatible.rb new file mode 100644 index 00000000..c8e2e707 --- /dev/null +++ b/test/io_console_compatible.rb @@ -0,0 +1,37 @@ +# coding: utf-8 + +require 'stringio' +require 'tempfile' + +# +# On tests, we try to simulate input output with +# StringIO, Tempfile and File objects. +# +# For this to be accomplished, we have to do some +# tweaking so that they respond adequately to the +# called methods during tests. +# + +module IOConsoleCompatible + def getch + getc + end + + attr_accessor :echo + + def winsize + [24, 80] + end +end + +class Tempfile + include IOConsoleCompatible +end + +class File + include IOConsoleCompatible +end + +class StringIO + include IOConsoleCompatible +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 144197b1..449c7d27 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -8,4 +8,19 @@ CodeClimate::TestReporter.start end +# Compatibility module for StringIO, File +# and Tempfile. Necessary for some tests. +require "io_console_compatible" + +require 'highline' +debug_message = "Tests will be run under:\n" +debug_message << " - #{HighLine.new.terminal.class}\n" +debug_message << " - HighLine::VERSION #{HighLine::VERSION}\n" + +if defined? RUBY_DESCRIPTION + debug_message << " - #{RUBY_DESCRIPTION}\n" +end + +puts debug_message + require "minitest/autorun" diff --git a/test/test_highline.rb b/test/test_highline.rb index 0eb803e2..19fd9895 100755 --- a/test/test_highline.rb +++ b/test/test_highline.rb @@ -15,7 +15,6 @@ require "readline" require "tempfile" - =begin if HighLine::CHARACTER_MODE == "Win32API" class HighLine @@ -303,12 +302,20 @@ def test_after_some_chars_backspace_does_not_enter_prompt_when_utf8 end def test_readline_mode - # Rubinius seems to be ignoring Readline input - # and output assignments. This ruins testing. - # but it doesn't mean readline is not working - # properly on rubinius. + # + # Rubinius (and JRuby) seems to be ignoring + # Readline input and output assignments. This + # ruins testing. + # + # But it doesn't mean readline is not working + # properly on rubinius or jruby. + # + + terminal = @terminal.terminal - return if RUBY_ENGINE == "rbx" + if terminal.jruby? or terminal.rubinius? or terminal.windows? + skip "We can't test Readline on JRuby, Rubinius and Windows yet" + end # Creating Tempfiles here because Readline.input # and Readline.output only accepts a File object @@ -952,7 +959,7 @@ def test_lists_with_three_items end def test_mode - assert(%w[Win32API termios ncurses stty unix_stty jline].include?(@terminal.terminal.character_mode), + assert(%w[io_console Win32API termios ncurses stty unix_stty jline].include?(@terminal.terminal.character_mode), "#{@terminal.terminal.character_mode} not in list") end @@ -1021,7 +1028,7 @@ def test_correct_string_encoding_when_echo_false end assert_equal "ação", answer - assert_equal Encoding::default_external, answer.encoding + assert_equal Encoding::UTF_8, answer.encoding end def test_backspace_with_ascii_when_echo_false @@ -1195,8 +1202,8 @@ def test_response_embedding answer = @terminal.ask("Tell me your age.", Integer) do |q| q.in = 0..105 - q.responses[:not_in_range] = "Need a <%= question.answer_type %>" + - " <%= question.expected_range %>." + q.responses[:not_in_range] = "Need a #{q.answer_type}" + + " #{q.expected_range}." end assert_equal(28, answer) assert_equal( "Tell me your age.\n" + @@ -1220,6 +1227,11 @@ def test_say @terminal.say("This will not have a newline. ") assert_equal("This will not have a newline. ", @output.string) + @output.truncate(@output.rewind) + + @terminal.say("This will not have a newline.\t") + assert_equal("This will not have a newline.\t", @output.string) + @output.truncate(@output.rewind) @terminal.say("This will not\n end with a newline. ")