tty-prompt 0.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +24 -0
  6. data/Gemfile +16 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +199 -0
  9. data/Rakefile +8 -0
  10. data/lib/tty-prompt.rb +15 -0
  11. data/lib/tty/prompt.rb +206 -0
  12. data/lib/tty/prompt/distance.rb +49 -0
  13. data/lib/tty/prompt/error.rb +26 -0
  14. data/lib/tty/prompt/history.rb +16 -0
  15. data/lib/tty/prompt/mode.rb +64 -0
  16. data/lib/tty/prompt/mode/echo.rb +40 -0
  17. data/lib/tty/prompt/mode/raw.rb +40 -0
  18. data/lib/tty/prompt/question.rb +338 -0
  19. data/lib/tty/prompt/question/modifier.rb +93 -0
  20. data/lib/tty/prompt/question/validation.rb +92 -0
  21. data/lib/tty/prompt/reader.rb +113 -0
  22. data/lib/tty/prompt/response.rb +252 -0
  23. data/lib/tty/prompt/response_delegation.rb +41 -0
  24. data/lib/tty/prompt/statement.rb +60 -0
  25. data/lib/tty/prompt/suggestion.rb +113 -0
  26. data/lib/tty/prompt/utils.rb +16 -0
  27. data/lib/tty/prompt/version.rb +7 -0
  28. data/spec/spec_helper.rb +45 -0
  29. data/spec/unit/ask_spec.rb +77 -0
  30. data/spec/unit/distance/distance_spec.rb +75 -0
  31. data/spec/unit/error_spec.rb +30 -0
  32. data/spec/unit/question/argument_spec.rb +30 -0
  33. data/spec/unit/question/character_spec.rb +24 -0
  34. data/spec/unit/question/default_spec.rb +25 -0
  35. data/spec/unit/question/in_spec.rb +23 -0
  36. data/spec/unit/question/initialize_spec.rb +24 -0
  37. data/spec/unit/question/modifier/apply_to_spec.rb +31 -0
  38. data/spec/unit/question/modifier/letter_case_spec.rb +22 -0
  39. data/spec/unit/question/modifier/whitespace_spec.rb +33 -0
  40. data/spec/unit/question/modify_spec.rb +44 -0
  41. data/spec/unit/question/valid_spec.rb +46 -0
  42. data/spec/unit/question/validate_spec.rb +30 -0
  43. data/spec/unit/question/validation/coerce_spec.rb +24 -0
  44. data/spec/unit/question/validation/valid_value_spec.rb +22 -0
  45. data/spec/unit/reader/getc_spec.rb +42 -0
  46. data/spec/unit/response/read_bool_spec.rb +47 -0
  47. data/spec/unit/response/read_char_spec.rb +16 -0
  48. data/spec/unit/response/read_date_spec.rb +20 -0
  49. data/spec/unit/response/read_email_spec.rb +42 -0
  50. data/spec/unit/response/read_multiple_spec.rb +23 -0
  51. data/spec/unit/response/read_number_spec.rb +28 -0
  52. data/spec/unit/response/read_range_spec.rb +26 -0
  53. data/spec/unit/response/read_spec.rb +68 -0
  54. data/spec/unit/response/read_string_spec.rb +19 -0
  55. data/spec/unit/say_spec.rb +66 -0
  56. data/spec/unit/statement/initialize_spec.rb +19 -0
  57. data/spec/unit/suggest_spec.rb +33 -0
  58. data/spec/unit/warn_spec.rb +30 -0
  59. data/tasks/console.rake +10 -0
  60. data/tasks/coverage.rake +11 -0
  61. data/tasks/spec.rake +29 -0
  62. data/tty-prompt.gemspec +26 -0
  63. metadata +194 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 74106ace23d50cc8593146b1b90c3b19ceca9914
4
+ data.tar.gz: d1719d051b15c93a321df9192315c8e4633b9e4d
5
+ SHA512:
6
+ metadata.gz: 6a5d268782541918e2f59b3e5c260c25fcd015e14004a1233ae69d5163ac8d76ffda6467ad29a2750efe561575639e355593403b426dd6fcbfb0aeda83fa4d77
7
+ data.tar.gz: df72ebf11ee221b0d9a49c9c4e8d6398afe161e4db3f48fed012aebf9f3a15dfe11caab3bb46def1837baddf864ff8d2291e522df4bb63ab49532ed1651f0f63
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --warnings
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,24 @@
1
+ language: ruby
2
+ bundler_args: --without yard benchmarks
3
+ script: "bundle exec rake ci"
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0
7
+ - 2.1
8
+ - 2.2
9
+ - ruby-head
10
+ matrix:
11
+ include:
12
+ - rvm: jruby-19mode
13
+ - rvm: jruby-20mode
14
+ - rvm: jruby-21mode
15
+ - rvm: jruby-head
16
+ - rvm: rbx-2
17
+ allow_failures:
18
+ - rvm: ruby-head
19
+ - rvm: jruby-head
20
+ - rvm: jruby-20mode
21
+ - rvm: jruby-21mode
22
+ fast_finish: true
23
+ branches:
24
+ only: master
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rake', '~> 10.4.2'
7
+ gem 'rspec', '~> 3.3.0'
8
+ gem 'yard', '~> 0.8.7'
9
+ gem 'benchmark-ips', '~> 2.0.0'
10
+ end
11
+
12
+ group :metrics do
13
+ gem 'coveralls', '~> 0.8.2'
14
+ gem 'simplecov', '~> 0.10.0'
15
+ gem 'yardstick', '~> 0.9.9'
16
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Piotr Murach
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # TTY::Prompt
2
+ [![Gem Version](https://badge.fury.io/rb/tty-prompt.svg)][gem]
3
+ [![Build Status](https://secure.travis-ci.org/peter-murach/tty-prompt.svg?branch=master)][travis]
4
+ [![Code Climate](https://codeclimate.com/github/peter-murach/tty-prompt/badges/gpa.svg)][codeclimate]
5
+ [![Coverage Status](https://coveralls.io/repos/peter-murach/tty-prompt/badge.svg)][coverage]
6
+ [![Inline docs](http://inch-ci.org/github/peter-murach/tty-prompt.svg?branch=master)][inchpages]
7
+
8
+ [gem]: http://badge.fury.io/rb/tty-prompt
9
+ [travis]: http://travis-ci.org/peter-murach/tty-prompt
10
+ [codeclimate]: https://codeclimate.com/github/peter-murach/tty-prompt
11
+ [coverage]: https://coveralls.io/r/peter-murach/tty-prompt
12
+ [inchpages]: http://inch-ci.org/github/peter-murach/tty-prompt
13
+
14
+ > A beautiful and powerful interactive command line prompt.
15
+
16
+ **TTY::Prompt** provides independent prompt component for [TTY](https://github.com/peter-murach/tty) toolkit.
17
+
18
+ ## Features
19
+
20
+ * A robust API for getting and validating complex inputs
21
+ * Number of coercion methods for converting response into Ruby types
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ ```ruby
28
+ gem 'tty-prompt'
29
+ ```
30
+
31
+ And then execute:
32
+
33
+ $ bundle
34
+
35
+ Or install it yourself as:
36
+
37
+ $ gem install tty-prompt
38
+
39
+ ## Contents
40
+
41
+ * [1. Usage](#1-usage)
42
+ * [2. Interface](#2-interface)
43
+ * [2.1 ask](#21-ask)
44
+ * [2.2 read](#22-read)
45
+ * [2.3 say](#23-say)
46
+ * [2.4 suggest](#24-suggest)
47
+
48
+
49
+ ## 1. Usage
50
+
51
+ In order to start asking questions on the command line, create prompt:
52
+
53
+ ```ruby
54
+ prompt = TTY::Prompt.new
55
+ ```
56
+
57
+ and then call `ask` with the question message:
58
+
59
+ ```ruby
60
+ question = prompt.ask('Do you like Ruby?')
61
+ ```
62
+
63
+ Finally, read and convert answer back to Ruby built-in type:
64
+
65
+ ```ruby
66
+ answer = question.read_bool
67
+ ```
68
+
69
+ ## 2. Interface
70
+
71
+ ### 2.1 ask
72
+
73
+ In order to ask a basic question and parse an answer do:
74
+
75
+ ```ruby
76
+ answer = prompt.ask("What is your name?").read_string
77
+ ```
78
+
79
+ The **TTY::Prompt** provides small DSL to help with parsing and asking precise questions
80
+
81
+ ```ruby
82
+ argument # :required or :optional
83
+ char # turn character based input, otherwise line (default: false)
84
+ clean # reset question
85
+ default # default value used if none is provided
86
+ echo # turn echo on and off (default: true)
87
+ mask # mask characters i.e '****' (default: false)
88
+ modify # apply answer modification :upcase, :downcase, :trim, :chomp etc..
89
+ in # specify range '0-9', '0..9', '0...9' or negative '-1..-9'
90
+ validate # regex against which stdin input is checked
91
+ valid # a list of expected valid options
92
+ ```
93
+
94
+ You can chain question methods or configure them inside a block:
95
+
96
+ ```ruby
97
+ prompt.ask("What is your name?").argument(:required).default('Piotr').validate(/\w+\s\w+/).read_string
98
+
99
+ prompt.ask "What is your name?" do
100
+ argument :required
101
+ default 'Piotr'
102
+ validate /\w+\s\w+/
103
+ valid ['Piotr', 'Piotrek']
104
+ modify :capitalize
105
+ end.read_string
106
+ ```
107
+
108
+ ### 2.2 read
109
+
110
+ To start reading the input from stdin simply call `read` method:
111
+
112
+ ```ruby
113
+ prompt.read
114
+ ```
115
+
116
+ However, there will be cases when your codebase expects answer to be of certain type. **TTY::Prompt** allows reading of answers and converting them into required types with custom readers:
117
+
118
+ ```ruby
119
+ read_bool # return true or false for strings such as "Yes", "No"
120
+ read_char # return first character
121
+ read_date # return date type
122
+ read_datetime # return datetime type
123
+ read_email # validate answer against email regex
124
+ read_file # return a File object
125
+ read_float # return decimal or error if cannot convert
126
+ read_int # return integer or error if cannot convert
127
+ read_multiple # return multiple line string
128
+ read_password # return string with echo turned off
129
+ read_range # return range type
130
+ read_regex # return regex expression
131
+ read_string # return string
132
+ read_symbol # return symbol
133
+ read_text # return multiline string
134
+ read_keypress # return the key pressed
135
+ ```
136
+
137
+ For example, if we wanted to ask a user for a single digit in given range
138
+
139
+ ```ruby
140
+ ask("Provide number in range: 0-9").in('0-9') do
141
+ on_error :retry
142
+ end.read_int
143
+ ```
144
+
145
+ on the other hand, if we are interested in range answer then
146
+
147
+ ```ruby
148
+ ask("Provide range of numbers?").read_range
149
+ ```
150
+
151
+ ### 2.3 say
152
+
153
+ To simply print message out to stdout use `say` like so:
154
+
155
+ ```ruby
156
+ prompt.say(...) # print message to stdout
157
+ ```
158
+
159
+ **TTY::Prompt** provides more specific versions of `say` method to better express intenation behind the message:
160
+
161
+ ```ruby
162
+ prompt.confirm # print message(s) in green
163
+ prompt.warn # print message(s) in yellow
164
+ prompt.error # print message(s) in red
165
+ ```
166
+
167
+ ### 2.4 suggest
168
+
169
+ To suggest possible matches for the user input use `suggest` method like so:
170
+
171
+ ```ruby
172
+ prompt.suggest('sta', ['stage', 'stash', 'commit', 'branch'])
173
+ # =>
174
+ Did you mean one of these?
175
+ stage
176
+ stash
177
+ ```
178
+
179
+ To cusomize query text presented pass `:single_text` and `:plural_text` options to respectively change the message when one match is found or many.
180
+
181
+ ```ruby
182
+ possible = %w(status stage stash commit branch blame)
183
+ prompt.suggest('b', possible, indent: 4, single_text: 'Perhaps you meant?')
184
+ # =>
185
+ Perhaps you meant?
186
+ blame
187
+ ```
188
+
189
+ ## Contributing
190
+
191
+ 1. Fork it ( https://github.com/peter-murach/tty-prompt/fork )
192
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
193
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
194
+ 4. Push to the branch (`git push origin my-new-feature`)
195
+ 5. Create a new Pull Request
196
+
197
+ ## Copyright
198
+
199
+ Copyright (c) 2015 Piotr Murach. See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ FileList['tasks/**/*.rake'].each(&method(:import))
6
+
7
+ desc 'Run all specs'
8
+ task ci: %w[ spec ]
data/lib/tty-prompt.rb ADDED
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'necromancer'
4
+ require 'pastel'
5
+ require 'tty-platform'
6
+
7
+ require 'tty/prompt'
8
+ require 'tty/prompt/mode'
9
+ require 'tty/prompt/question'
10
+ require 'tty/prompt/reader'
11
+ require 'tty/prompt/response'
12
+ require 'tty/prompt/statement'
13
+ require 'tty/prompt/suggestion'
14
+ require 'tty/prompt/utils'
15
+ require 'tty/prompt/version'
data/lib/tty/prompt.rb ADDED
@@ -0,0 +1,206 @@
1
+ # encoding: utf-8
2
+
3
+ module TTY
4
+ class Prompt
5
+ # Raised when the passed in validation argument is of wrong type
6
+ class ValidationCoercion < TypeError; end
7
+
8
+ # Raised when the required argument is not supplied
9
+ class ArgumentRequired < ArgumentError; end
10
+
11
+ # Raised when the argument validation fails
12
+ class ArgumentValidation < ArgumentError; end
13
+
14
+ # Raised when the argument is not expected
15
+ class InvalidArgument < ArgumentError; end
16
+
17
+ # @api private
18
+ attr_reader :input
19
+
20
+ # @api private
21
+ attr_reader :output
22
+
23
+ # Shell prompt prefix
24
+ #
25
+ # @api private
26
+ attr_reader :prefix
27
+
28
+ # Initialize a Shell
29
+ #
30
+ # @api public
31
+ def initialize(input = stdin, output = stdout, options = {})
32
+ @input = input
33
+ @output = output
34
+ @prefix = options.fetch(:prefix) { '' }
35
+ end
36
+
37
+ # Ask a question.
38
+ #
39
+ # @example
40
+ # shell = TTY::Shell.new
41
+ # shell.ask("What is your name?")
42
+ #
43
+ # @param [String] statement
44
+ # string question to be asked
45
+ #
46
+ # @yieldparam [TTY::Question] question
47
+ # further configure the question
48
+ #
49
+ # @yield [question]
50
+ #
51
+ # @return [TTY::Question]
52
+ #
53
+ # @api public
54
+ def ask(statement, *args, &block)
55
+ options = Utils.extract_options!(args)
56
+
57
+ question = Question.new self, options
58
+ question.instance_eval(&block) if block_given?
59
+ question.prompt(statement)
60
+ end
61
+
62
+ # A shortcut method to ask the user positive question and return
63
+ # true for 'yes' reply.
64
+ #
65
+ # @return [Boolean]
66
+ #
67
+ # @api public
68
+ def yes?(statement, *args, &block)
69
+ ask(statement, *args, &block).read_bool
70
+ end
71
+
72
+ # A shortcut method to ask the user negative question and return
73
+ # true for 'no' reply.
74
+ #
75
+ # @return [Boolean]
76
+ #
77
+ # @api public
78
+ def no?(statement, *args, &block)
79
+ !yes?(statement, *args, &block)
80
+ end
81
+
82
+ # Print statement out. If the supplied message ends with a space or
83
+ # tab character, a new line will not be appended.
84
+ #
85
+ # @example
86
+ # say("Simple things.")
87
+ #
88
+ # @param [String] message
89
+ #
90
+ # @return [String]
91
+ #
92
+ # @api public
93
+ def say(message = '', options = {})
94
+ message = message.to_str
95
+ return unless message.length > 0
96
+
97
+ statement = Statement.new(self, options)
98
+ statement.declare message
99
+ end
100
+
101
+ # Print statement(s) out in red green.
102
+ #
103
+ # @example
104
+ # shell.confirm "Are you sure?"
105
+ # shell.confirm "All is fine!", "This is fine too."
106
+ #
107
+ # @param [Array] messages
108
+ #
109
+ # @return [Array] messages
110
+ #
111
+ # @api public
112
+ def confirm(*args)
113
+ options = Utils.extract_options!(args)
114
+ args.each { |message| say message, options.merge(color: :green) }
115
+ end
116
+
117
+ # Print statement(s) out in yellow color.
118
+ #
119
+ # @example
120
+ # shell.warn "This action can have dire consequences"
121
+ # shell.warn "Carefull young apprentice", "This is potentially dangerous"
122
+ #
123
+ # @param [Array] messages
124
+ #
125
+ # @return [Array] messages
126
+ #
127
+ # @api public
128
+ def warn(*args)
129
+ options = Utils.extract_options!(args)
130
+ args.each { |message| say message, options.merge(color: :yellow) }
131
+ end
132
+
133
+ # Print statement(s) out in red color.
134
+ #
135
+ # @example
136
+ # shell.error "Shutting down all systems!"
137
+ # shell.error "Nothing is fine!", "All is broken!"
138
+ #
139
+ # @param [Array] messages
140
+ #
141
+ # @return [Array] messages
142
+ #
143
+ # @api public
144
+ def error(*args)
145
+ options = Utils.extract_options!(args)
146
+ args.each { |message| say message, options.merge(color: :red) }
147
+ end
148
+
149
+ # Takes the string provided by the user and compare it with other possible
150
+ # matches to suggest an unambigous string
151
+ #
152
+ # @example
153
+ # shell.suggest('sta', ['status', 'stage', 'commit', 'branch'])
154
+ # # => "status, stage"
155
+ #
156
+ # @param [String] message
157
+ #
158
+ # @param [Array] possibilities
159
+ #
160
+ # @param [Hash] options
161
+ # @option options [String] :indent
162
+ # The number of spaces for indentation
163
+ # @option options [String] :single_text
164
+ # The text for a single suggestion
165
+ # @option options [String] :plural_text
166
+ # The text for multiple suggestions
167
+ #
168
+ # @return [String]
169
+ #
170
+ # @api public
171
+ def suggest(message, possibilities, options = {})
172
+ suggestion = Suggestion.new(options)
173
+ say(suggestion.suggest(message, possibilities))
174
+ end
175
+
176
+ # Check if outputing to shell
177
+ #
178
+ # @return [Boolean]
179
+ #
180
+ # @api public
181
+ def tty?
182
+ stdout.tty?
183
+ end
184
+
185
+ # Return standard in
186
+ #
187
+ # @api private
188
+ def stdin
189
+ $stdin
190
+ end
191
+
192
+ # Return standard out
193
+ #
194
+ # @api private
195
+ def stdout
196
+ $stdout
197
+ end
198
+
199
+ # Return standard error
200
+ #
201
+ # @api private
202
+ def stderr
203
+ $stderr
204
+ end
205
+ end # Prompt
206
+ end # TTY