tty-prompt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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