highline 2.1.0 → 3.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0bb41764d2e42ab3b3c52ca1735f0cd6a587ff5ed3bb4525d0e7703dc4ccb61f
4
- data.tar.gz: 0424d1467c1f787881abfe6558afdc0aceeb0e854d05efac8c5c6db9d07ba3cf
3
+ metadata.gz: 68bd1dba0eea8f51020e4b42bf027a432313b312efcf1365c20a6d5ca2b13244
4
+ data.tar.gz: d1dd90401ba1b29581afe8e89ef5a2ad3b443184e61f73dd7a60252404c30172
5
5
  SHA512:
6
- metadata.gz: df0dd1fa4d206aace9512f7ad414bc1e6bdcbfe804fd91b3811a2bc5860dbe7862d3ab81a502fc812747134be1122a647ac9fdd8e95e88e3f9388f7a05f38c22
7
- data.tar.gz: 70cbe01e01aeff63d8321a4768caaa43992082030ad4e3f93533693d7f7908b30175852c11286fe3a744c4a3a3be63ad3328e7b5b605a045ad4f634887e1340d
6
+ metadata.gz: b7bcd45c71419a9e90c9cdc9f1dba9c42be76618068cadc8e30e41600272413b16df3cd95d95266fc4d24d70c00c53668f6fe7134fb1e5cee4780740eabc2378
7
+ data.tar.gz: 20cfca01c3f433766f9d0884b5ea32dcfa690e6b3652fc6aeb9e691f5eb62f1ffc99ec9941fe724603684b660d374af23b8819054a1e5dfffb5655ff8107df1e
@@ -10,14 +10,10 @@ jobs:
10
10
  os: [ubuntu-latest]
11
11
  ruby-version:
12
12
  - head
13
+ - '3.3'
13
14
  - '3.2'
14
15
  - '3.1'
15
16
  - '3.0'
16
- - '2.7'
17
- - '2.6'
18
- - '2.5'
19
- - '2.4'
20
- - '2.3'
21
17
  - jruby
22
18
  - jruby-head
23
19
  - truffleruby
@@ -27,7 +23,7 @@ jobs:
27
23
  - os: windows-latest
28
24
  ruby-version: head
29
25
  - os: windows-latest
30
- ruby-version: '3.1'
26
+ ruby-version: '3.3'
31
27
  - os: windows-latest
32
28
  ruby-version: mingw
33
29
  - os: windows-latest
@@ -37,10 +33,10 @@ jobs:
37
33
  - os: macos-latest
38
34
  ruby-version: 'head'
39
35
  - os: macos-latest
40
- ruby-version: '3.1'
36
+ ruby-version: '3.3'
41
37
  runs-on: ${{ matrix.os }}
42
38
  steps:
43
- - uses: actions/checkout@v3
39
+ - uses: actions/checkout@v4
44
40
  - name: Set up Ruby ${{ matrix.ruby-version }}
45
41
  uses: ruby/setup-ruby@v1
46
42
  with:
data/Changelog.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  Below is a complete listing of changes for each revision of HighLine.
4
4
 
5
+ ### 3.1.0 / 2024-07-15
6
+ * PR #272 / I #271 - Readline is now completed deprecated over Reline (@abinoam, issue by @64kramsystem)
7
+ * PR #269 - Provide a 'Changelog' link on rubygems.org/gems/highline (@mark-young-atg)
8
+
9
+ ### 3.0.1 / 2024-01-20
10
+ * PR #268 - Remove unused abbrev dependency (@zvkemp)
11
+
12
+ ### 3.0.0 / 2024-01-05
13
+ * PR #265 - Change Readline for Reline for Ruby 3.3 compat (@abinoam)
14
+ * PR #264 - Add abbrev gem as dependency (@mathieujobin)
15
+ * PR #263 - Release 3.0.0.pre.1
16
+ * Raise minimum Ruby version requirement to 3.0
17
+ * PR #262 - Do not call stty on non-tty (@kbrock)
18
+ * PR #260 / I #43 - Ctrl-U (erase line) handling (@abinoam, issue by @gutenye)
19
+ * PR #259 / I #236 - Handle Ctrl-C when Question#echo = false (@abinoam, @Fahhetah, issue by @aspyct)
20
+ * PR #258 / I #246 - Add validation class support (@abinoam, issue by @Joshfindit)
21
+ * Make it dry-types compatible through the use of `#valid?`
22
+ * Solve the multiple answers in one line problem with a combination of custom coercion (parser) and custom validation
23
+ * PR #257 / I #233 - Show Question#default hint for non String values (@abinoam, issue by @branch14)
24
+ * Add Question#default_hint_show to allow disabling it.
25
+ * PR #256 / I #249 - Fix Array validation in Question#in (@abinoam, issue by @esotericpig)
26
+
5
27
  ### 2.1.0 / 2022-12-31
6
28
  * PR #255 - Change minimum Ruby version requirement to 2.3 (@abinoam)
7
29
  * PR #254 - Improve Github Actions file (@abinoam)
data/README.md CHANGED
@@ -43,12 +43,48 @@ puts "You have answered: #{answer}"
43
43
 
44
44
  cli.ask("Company? ") { |q| q.default = "none" }
45
45
 
46
+ ## Disable default value hint showing
47
+
48
+ my_special_default_object = Object.new
49
+
50
+ cli.ask("Question? ") do |q|
51
+ q.default = my_special_default_object
52
+ q.default_hint_show = false
53
+ end
54
+
46
55
 
47
56
  # Validation
48
57
 
49
58
  cli.ask("Age? ", Integer) { |q| q.in = 0..105 }
50
59
  cli.ask("Name? (last, first) ") { |q| q.validate = /\A\w+, ?\w+\Z/ }
51
60
 
61
+ ## Validation with custom class
62
+ class ZeroToTwentyFourValidator
63
+ def self.valid?(answer)
64
+ (0..24).include? answer.to_i
65
+ end
66
+
67
+ def self.inspect
68
+ "(0..24) rule"
69
+ end
70
+ end
71
+
72
+ cli.ask("What hour of the day is it?: ", Integer) do |q|
73
+ q.validate = ZeroToTwentyFourValidator
74
+ end
75
+
76
+ ## Validation with Dry::Types
77
+ ## `Dry::Types` provides a `valid?` method so it can be used effortlessly
78
+
79
+ require 'dry-type'
80
+
81
+ module Types
82
+ include Dry.Types
83
+ end
84
+
85
+ cli.ask("Type an integer:", Integer) do |q|
86
+ q.validate = Types::Coercible::Integer
87
+ end
52
88
 
53
89
  # Type conversion for answers:
54
90
 
@@ -103,7 +139,7 @@ For more examples see the examples/ directory of this project.
103
139
  Requirements
104
140
  ------------
105
141
 
106
- HighLine from version >= 1.7.0 requires ruby >= 1.9.3
142
+ HighLine from version >= 3.0.0 requires ruby >= 3.0.0
107
143
 
108
144
  Installing
109
145
  ----------
@@ -0,0 +1,39 @@
1
+ require 'highline'
2
+
3
+ cli = HighLine.new
4
+
5
+ # The parser
6
+ class ArrayOfNumbersFromString
7
+ def self.parse(string)
8
+ string.scan(/\d+/).map(&:to_i)
9
+ end
10
+ end
11
+
12
+ # The validator
13
+ class ArrayOfNumbersFromStringInRange
14
+ def self.in?(range)
15
+ new(range)
16
+ end
17
+
18
+ attr_reader :range
19
+
20
+ def initialize(range)
21
+ @range = range
22
+ end
23
+
24
+ def valid?(answer)
25
+ ary = ArrayOfNumbersFromString.parse(answer)
26
+ ary.all? ->(number) { range.include? number }
27
+ end
28
+
29
+ def inspect
30
+ "in range #@range validator"
31
+ end
32
+ end
33
+
34
+ answer = cli.ask("Which number? (0 or <Enter> to skip): ", ArrayOfNumbersFromString) { |q|
35
+ q.validate = ArrayOfNumbersFromStringInRange.in?(0..10)
36
+ q.default = 0
37
+ }
38
+
39
+ puts "Your answer was: #{answer} and it was correctly validated and coerced into an #{answer.class}"
data/highline.gemspec CHANGED
@@ -27,9 +27,14 @@ DESCRIPTION
27
27
 
28
28
  spec.extra_rdoc_files = %w[README.md TODO Changelog.md LICENSE]
29
29
 
30
- spec.required_ruby_version = ">= 2.3"
30
+ spec.required_ruby_version = ">= 3.0"
31
31
 
32
32
  spec.add_development_dependency "bundler"
33
33
  spec.add_development_dependency "rake"
34
34
  spec.add_development_dependency "minitest"
35
+ spec.add_development_dependency "dry-types"
36
+
37
+ spec.add_runtime_dependency "reline"
38
+
39
+ spec.metadata["changelog_uri"] = spec.homepage + "/blob/master/Changelog.md"
35
40
  end
@@ -13,7 +13,7 @@ require "tempfile"
13
13
  #
14
14
 
15
15
  module IOConsoleCompatible
16
- def getch
16
+ def getch(min:nil, time:nil, intr: nil)
17
17
  getc
18
18
  end
19
19
 
@@ -9,7 +9,7 @@ class HighLine
9
9
  extend Forwardable
10
10
 
11
11
  def_delegators :@question,
12
- :answer, :answer=, :check_range,
12
+ :answer, :answer=,
13
13
  :directory, :answer_type, :choices_complete
14
14
 
15
15
  # It should be initialized with a Question object.
@@ -26,10 +26,7 @@ class HighLine
26
26
  # it makes the conversion and returns the answer.
27
27
  # @return [Object] the converted answer.
28
28
  def convert
29
- return unless answer_type
30
-
31
- self.answer = convert_by_answer_type
32
- check_range
29
+ self.answer = convert_by_answer_type if answer_type
33
30
  answer
34
31
  end
35
32
 
@@ -56,6 +56,7 @@ class HighLine
56
56
  @completion = @answer_type
57
57
 
58
58
  @echo = true
59
+ @default_hint_show = true
59
60
  @whitespace = :strip
60
61
  @case = nil
61
62
  @in = nil
@@ -115,7 +116,7 @@ class HighLine
115
116
  #
116
117
  attr_accessor :echo
117
118
  #
118
- # Use the Readline library to fetch input. This allows input editing as
119
+ # Use the Reline library to fetch input. This allows input editing as
119
120
  # well as keeping a history. In addition, tab will auto-complete
120
121
  # within an Array of choices or a file listing.
121
122
  #
@@ -124,6 +125,7 @@ class HighLine
124
125
  # specified _input_ stream.
125
126
  #
126
127
  attr_accessor :readline
128
+
127
129
  #
128
130
  # Used to control whitespace processing for the answer to this question.
129
131
  # See HighLine::Question.remove_whitespace() for acceptable settings.
@@ -136,10 +138,17 @@ class HighLine
136
138
  attr_accessor :case
137
139
  # Used to provide a default answer to this question.
138
140
  attr_accessor :default
141
+ # Set it to a truthy or falsy value to enable or disable showing the default
142
+ # value hint between vertical bars (pipes) when asking the question.
143
+ # Defaults to +true+
144
+ attr_accessor :default_hint_show
139
145
  #
140
146
  # If set to a Regexp, the answer must match (before type conversion).
141
147
  # Can also be set to a Proc which will be called with the provided
142
148
  # answer to validate with a +true+ or +false+ return.
149
+ # It's possible to use a custom validator class. It must respond to
150
+ # `#valid?`. The result of `#inspect` will be used in error messages.
151
+ # See README.md for details.
143
152
  #
144
153
  attr_accessor :validate
145
154
  # Used to control range checks for answer.
@@ -252,7 +261,7 @@ class HighLine
252
261
  # Same as {#answer_type}.
253
262
 
254
263
  def build_responses(message_source = answer_type)
255
- append_default if [::String, Symbol].include? default.class
264
+ append_default_to_template if default_hint_show
256
265
 
257
266
  new_hash = build_responses_new_hash(message_source)
258
267
  # Update our internal responses with the new hash
@@ -497,7 +506,8 @@ class HighLine
497
506
  def valid_answer?
498
507
  !validate ||
499
508
  (validate.is_a?(Regexp) && answer =~ validate) ||
500
- (validate.is_a?(Proc) && validate[answer])
509
+ (validate.is_a?(Proc) && validate[answer]) ||
510
+ (validate.respond_to?(:valid?) && validate.valid?(answer))
501
511
  end
502
512
 
503
513
  #
@@ -571,11 +581,6 @@ class HighLine
571
581
  end
572
582
  end
573
583
 
574
- # readline() needs to handle its own output, but readline only supports
575
- # full line reading. Therefore if question.echo is anything but true,
576
- # the prompt will not be issued. And we have to account for that now.
577
- # Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt
578
- # to handle line editing properly.
579
584
  # @param highline [HighLine] context
580
585
  # @return [void]
581
586
  def show_question(highline)
@@ -607,15 +612,20 @@ class HighLine
607
612
  # Trailing whitespace is preserved so the function of HighLine.say() is
608
613
  # not affected.
609
614
  #
610
- def append_default
615
+ def append_default_to_template
616
+ return unless default.respond_to? :to_s
617
+
618
+ default_str = default.to_s
619
+ return if default_str.empty?
620
+
611
621
  if template =~ /([\t ]+)\Z/
612
- template << "|#{default}|#{Regexp.last_match(1)}"
622
+ template << "|#{default_str}|#{Regexp.last_match(1)}"
613
623
  elsif template == ""
614
- template << "|#{default}| "
624
+ template << "|#{default_str}| "
615
625
  elsif template[-1, 1] == "\n"
616
- template[-2, 0] = " |#{default}|"
626
+ template[-2, 0] = " |#{default_str}|"
617
627
  else
618
- template << " |#{default}|"
628
+ template << " |#{default_str}|"
619
629
  end
620
630
  end
621
631
 
@@ -24,13 +24,15 @@ class HighLine
24
24
  #
25
25
  # @return [String] answer
26
26
  def ask_once
27
- question.show_question(@highline)
27
+ # If in readline mode, let reline take care of the prompt
28
+ question.show_question(@highline) unless question.readline
28
29
 
29
30
  begin
30
31
  question.get_response_or_default(@highline)
31
32
  raise NotValidQuestionError unless question.valid_answer?
32
33
 
33
34
  question.convert
35
+ question.check_range
34
36
 
35
37
  if question.confirm
36
38
  confirmation = @highline.send(:confirm, question)
@@ -27,7 +27,7 @@ class HighLine
27
27
 
28
28
  # (see Terminal#get_character)
29
29
  def get_character
30
- input.getch # from ruby io/console
30
+ input.getch(intr: true) # from ruby io/console
31
31
  rescue Errno::ENOTTY
32
32
  input.getc
33
33
  end
@@ -20,7 +20,9 @@ class HighLine
20
20
  rescue LoadError
21
21
  end
22
22
 
23
- if /solaris/ =~ RUBY_PLATFORM &&
23
+ if !@output.tty?
24
+ [80, 24]
25
+ elsif /solaris/ =~ RUBY_PLATFORM &&
24
26
  `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/
25
27
  [Regexp.last_match(2), Regexp.last_match(1)].map(&:to_i)
26
28
  elsif `stty size` =~ /^(\d+)\s(\d+)$/
@@ -32,13 +34,13 @@ class HighLine
32
34
 
33
35
  # (see Terminal#raw_no_echo_mode)
34
36
  def raw_no_echo_mode
35
- @state = `stty -g`
36
- system "stty raw -echo -icanon isig"
37
+ save_stty
38
+ system "stty raw -echo -icanon isig" if input.tty?
37
39
  end
38
40
 
39
41
  # (see Terminal#restore_mode)
40
42
  def restore_mode
41
- system "stty #{@state}"
43
+ restore_stty
42
44
  print "\r"
43
45
  end
44
46
 
@@ -95,9 +95,9 @@ class HighLine
95
95
  # Get one line using #readline_read
96
96
  # @param (see #get_line)
97
97
  def get_line_with_readline(question, highline)
98
- require "readline" # load only if needed
98
+ require "reline" # load only if needed
99
99
 
100
- raw_answer = readline_read(question)
100
+ raw_answer = readline_read(question, highline)
101
101
 
102
102
  if !raw_answer && highline.track_eof?
103
103
  raise EOFError, "The input stream is exhausted."
@@ -109,20 +109,22 @@ class HighLine
109
109
  # Use readline to read one line
110
110
  # @param question [HighLine::Question] question from where to get
111
111
  # autocomplete candidate strings
112
- def readline_read(question)
112
+ def readline_read(question, highline)
113
113
  # prep auto-completion
114
114
  unless question.selection.empty?
115
- Readline.completion_proc = lambda do |str|
115
+ Reline.completion_proc = lambda do |str|
116
116
  question.selection.grep(/\A#{Regexp.escape(str)}/)
117
117
  end
118
118
  end
119
119
 
120
+ # TODO: Check if this is still needed after Reline
120
121
  # work-around ugly readline() warnings
121
122
  old_verbose = $VERBOSE
122
123
  $VERBOSE = nil
123
124
 
124
125
  raw_answer = run_preserving_stty do
125
- Readline.readline("", true)
126
+ prompt = highline.render_and_ident_statement(question)
127
+ Reline.readline(prompt, true)
126
128
  end
127
129
 
128
130
  $VERBOSE = old_verbose
@@ -176,7 +178,7 @@ class HighLine
176
178
  # Saves terminal state using shell stty command.
177
179
  def save_stty
178
180
  @stty_save = begin
179
- `stty -g`.chomp
181
+ `stty -g`.chomp if input.tty?
180
182
  rescue StandardError
181
183
  nil
182
184
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  class HighLine
4
4
  # The version of the installed library.
5
- VERSION = "2.1.0".freeze
5
+ VERSION = "3.1.0".freeze
6
6
  end
data/lib/highline.rb CHANGED
@@ -14,7 +14,6 @@ require "English"
14
14
  require "erb"
15
15
  require "optparse"
16
16
  require "stringio"
17
- require "abbrev"
18
17
  require "highline/terminal"
19
18
  require "highline/custom_errors"
20
19
  require "highline/question"
@@ -371,10 +370,8 @@ class HighLine
371
370
  #
372
371
  # @param statement [Statement, String] what to be said
373
372
  def say(statement)
374
- statement = render_statement(statement)
375
- return if statement.empty?
376
-
377
- statement = (indentation + statement)
373
+ statement = render_and_ident_statement(statement)
374
+ return statement if statement.empty?
378
375
 
379
376
  # Don't add a newline if statement ends with whitespace, OR
380
377
  # if statement ends with whitespace before a color escape code.
@@ -386,6 +383,18 @@ class HighLine
386
383
  end
387
384
  end
388
385
 
386
+ # Renders and indents a statement.
387
+ #
388
+ # Note: extracted here to be used by readline to render its prompt.
389
+ #
390
+ # @param statement [String] The statement to be rendered and indented.
391
+ # @return [String] The rendered and indented statement.
392
+ def render_and_ident_statement(statement)
393
+ statement = render_statement(statement)
394
+ statement = (indentation + statement) unless statement.empty?
395
+ statement
396
+ end
397
+
389
398
  # Renders a statement using {HighLine::Statement}
390
399
  # @param statement [String] any string
391
400
  # @return [Statement] rendered statement
@@ -538,6 +547,7 @@ class HighLine
538
547
  terminal.raw_no_echo_mode_exec do
539
548
  loop do
540
549
  character = terminal.get_character
550
+ raise Interrupt if character == "\u0003"
541
551
  break unless character
542
552
  break if ["\n", "\r"].include? character
543
553
 
@@ -545,6 +555,9 @@ class HighLine
545
555
  if character == "\b" || character == "\u007F"
546
556
  chopped = line.chop!
547
557
  output_erase_char if chopped && question.echo
558
+ elsif character == "\cU"
559
+ line.size.times { output_erase_char } if question.echo
560
+ line = ""
548
561
  elsif character == "\e"
549
562
  ignore_arrow_key
550
563
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: highline
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Edward Gray II
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-31 00:00:00.000000000 Z
11
+ date: 2024-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dry-types
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: reline
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  description: |
56
84
  A high-level IO library that provides validation, type conversion, and more for
57
85
  command-line interfaces. HighLine also includes a complete menu system that can
@@ -83,6 +111,7 @@ files:
83
111
  - examples/asking_for_arrays.rb
84
112
  - examples/basic_usage.rb
85
113
  - examples/color_scheme.rb
114
+ - examples/custom_parser_custom_validator.rb
86
115
  - examples/get_character.rb
87
116
  - examples/limit.rb
88
117
  - examples/menus.rb
@@ -127,8 +156,9 @@ files:
127
156
  homepage: https://github.com/JEG2/highline
128
157
  licenses:
129
158
  - Ruby
130
- metadata: {}
131
- post_install_message:
159
+ metadata:
160
+ changelog_uri: https://github.com/JEG2/highline/blob/master/Changelog.md
161
+ post_install_message:
132
162
  rdoc_options: []
133
163
  require_paths:
134
164
  - lib
@@ -136,15 +166,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
136
166
  requirements:
137
167
  - - ">="
138
168
  - !ruby/object:Gem::Version
139
- version: '2.3'
169
+ version: '3.0'
140
170
  required_rubygems_version: !ruby/object:Gem::Requirement
141
171
  requirements:
142
172
  - - ">="
143
173
  - !ruby/object:Gem::Version
144
174
  version: '0'
145
175
  requirements: []
146
- rubygems_version: 3.4.1
147
- signing_key:
176
+ rubygems_version: 3.5.9
177
+ signing_key:
148
178
  specification_version: 4
149
179
  summary: HighLine is a high-level command-line IO library.
150
180
  test_files: []