toys-core 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87e34f50eb16af11bc970c170e7750509e026b237ec37352be05198860bfdeae
4
- data.tar.gz: 6bdaeaa6577d75c25b0efd17406792c24e3f6dd57326a52badd60495dc9170cc
3
+ metadata.gz: 5ebb136d89b19617cfdaf8de9c870cce070857748abd208ab8faa4896b7e7b36
4
+ data.tar.gz: eb079b488d1da544e5f31ab0fe0f9730d77d6878549c673bc6ba4711a5d6c295
5
5
  SHA512:
6
- metadata.gz: ee18128015b86041547bcdab1e7ec4bea05a41e0eff06c30a4003f032cc1ca335c7e7b593236ea140e65f43b4f31a752ceee5c57c787227b88d3144eac22b147
7
- data.tar.gz: e4c7b13f825d6b4131db5e08a56a306652e2f532de1095758fde3be3fe509635629cc7725241b4bd3206ce1b414ed0cc4bd09aa480bbe4f335e8b3abd4ffc7c7
6
+ metadata.gz: 864c7bc16399227e29e013372611ed70df77339185593d3830a31a5092e7dea6540d62ed83704e23ce5d78410ce2b4e34ae5ce9f709d67d8bb82e5075ed09cc3
7
+ data.tar.gz: 5eb3e60cefb50d7a289ef2283a91574ede820bf1d2bb5769d8038d60d760e999f36e49882899c06f2de6dd37a23d828ee25bd1e2a0764e11cc0e31553764368c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Release History
2
2
 
3
+ ### 0.3.3 / 2018-05-09
4
+
5
+ * CHANGED: Renamed file_utils helper to fileutils.
6
+ * CHANGED: Renamed doc: parameter to docs:
7
+ * CHANGED: SwitchDefinition has separate fields for acceptor and docs.
8
+ * CHANGED: Description and long description are now arrays of strings.
9
+ * FIXED: Documentation strings that begin with "--" no longer cause problems.
10
+ * ADDED: Highline helper
11
+ * ADDED: Spinner helper
12
+ * ADDED: WrappableString for descriptions and docs
13
+ * IMPROVED: Usage can now customize the left column width and indent
14
+ * IMPROVED: Newlines in documentation are properly indented
15
+
3
16
  ### 0.3.2 / 2018-05-07
4
17
 
5
18
  * CHANGED: Split core engine out into "toys-core" from the "toys" gem.
@@ -164,12 +164,12 @@ module Toys
164
164
  # Set the long description for the current tool. The long description is
165
165
  # displayed in the usage documentation for the tool itself.
166
166
  #
167
- # @param [String] desc The long description string.
167
+ # @param [String,Toys::Utils::WrappableString...] strs The long description
168
168
  #
169
- def long_desc(desc)
169
+ def long_desc(*strs)
170
170
  return self if _cur_tool.nil?
171
171
  _cur_tool.definition_path = @path
172
- _cur_tool.long_desc = desc
172
+ _cur_tool.long_desc = strs
173
173
  self
174
174
  end
175
175
 
@@ -178,12 +178,12 @@ module Toys
178
178
  # displayed with the tool in a command list. You may also use the
179
179
  # equivalent method `short_desc`.
180
180
  #
181
- # @param [String] desc The short description string.
181
+ # @param [String,Toys::Utils::WrappableString...] strs The short description
182
182
  #
183
- def desc(desc)
183
+ def desc(*strs)
184
184
  return self if _cur_tool.nil?
185
185
  _cur_tool.definition_path = @path
186
- _cur_tool.desc = desc
186
+ _cur_tool.desc = strs
187
187
  self
188
188
  end
189
189
  alias short_desc desc
@@ -200,8 +200,9 @@ module Toys
200
200
  # @param [Object] default The default value. This is the value that will
201
201
  # be set in the context if this switch is not provided on the command
202
202
  # line. Defaults to `nil`.
203
- # @param [String,nil] doc The documentation for the switch, which appears
204
- # in the usage documentation. Defaults to `nil` for no documentation.
203
+ # @param [String,Toys::Utils::WrappableString,
204
+ # Array<String,Toys::Utils::WrappableString>] docs Documentation for
205
+ # the switch. Defaults to empty array.
205
206
  # @param [Boolean] only_unique If true, any switches that are already
206
207
  # defined in this tool are removed from this switch. For example, if
207
208
  # an earlier switch uses `-a`, and this switch wants to use both
@@ -214,11 +215,11 @@ module Toys
214
215
  # value. i.e. the default is effectively `-> (val, _prev) { val }`.
215
216
  #
216
217
  def switch(key, *switches,
217
- accept: nil, default: nil, doc: nil, only_unique: false, handler: nil)
218
+ accept: nil, default: nil, docs: nil, only_unique: false, handler: nil)
218
219
  return self if _cur_tool.nil?
219
220
  _cur_tool.definition_path = @path
220
221
  _cur_tool.add_switch(key, *switches,
221
- accept: accept, default: default, doc: doc,
222
+ accept: accept, default: default, docs: docs,
222
223
  only_unique: only_unique, handler: handler)
223
224
  self
224
225
  end
@@ -231,13 +232,14 @@ module Toys
231
232
  # @param [Symbol] key The key to use to retrieve the value from the
232
233
  # execution context.
233
234
  # @param [Object,nil] accept An OptionParser acceptor. Optional.
234
- # @param [String,nil] doc The documentation for the switch, which appears
235
- # in the usage documentation. Defaults to `nil` for no documentation.
235
+ # @param [String,Toys::Utils::WrappableString,
236
+ # Array<String,Toys::Utils::WrappableString>] docs Documentation for the
237
+ # arg. Defaults to empty array.
236
238
  #
237
- def required_arg(key, accept: nil, doc: nil)
239
+ def required_arg(key, accept: nil, docs: nil)
238
240
  return self if _cur_tool.nil?
239
241
  _cur_tool.definition_path = @path
240
- _cur_tool.add_required_arg(key, accept: accept, doc: doc)
242
+ _cur_tool.add_required_arg(key, accept: accept, docs: docs)
241
243
  self
242
244
  end
243
245
 
@@ -253,13 +255,14 @@ module Toys
253
255
  # @param [Object] default The default value. This is the value that will
254
256
  # be set in the context if this argument is not provided on the command
255
257
  # line. Defaults to `nil`.
256
- # @param [String,nil] doc The documentation for the argument, which appears
257
- # in the usage documentation. Defaults to `nil` for no documentation.
258
+ # @param [String,Toys::Utils::WrappableString,
259
+ # Array<String,Toys::Utils::WrappableString>] docs Documentation for the
260
+ # arg. Defaults to empty array.
258
261
  #
259
- def optional_arg(key, accept: nil, default: nil, doc: nil)
262
+ def optional_arg(key, accept: nil, default: nil, docs: nil)
260
263
  return self if _cur_tool.nil?
261
264
  _cur_tool.definition_path = @path
262
- _cur_tool.add_optional_arg(key, accept: accept, default: default, doc: doc)
265
+ _cur_tool.add_optional_arg(key, accept: accept, default: default, docs: docs)
263
266
  self
264
267
  end
265
268
 
@@ -274,14 +277,14 @@ module Toys
274
277
  # @param [Object] default The default value. This is the value that will
275
278
  # be set in the context if no unmatched arguments are provided on the
276
279
  # command line. Defaults to the empty array `[]`.
277
- # @param [String,nil] doc The documentation for the remaining arguments,
278
- # which appears in the usage documentation. Defaults to `nil` for no
279
- # documentation.
280
+ # @param [String,Toys::Utils::WrappableString,
281
+ # Array<String,Toys::Utils::WrappableString>] docs Documentation for the
282
+ # args. Defaults to empty array.
280
283
  #
281
- def remaining_args(key, accept: nil, default: [], doc: nil)
284
+ def remaining_args(key, accept: nil, default: [], docs: nil)
282
285
  return self if _cur_tool.nil?
283
286
  _cur_tool.definition_path = @path
284
- _cur_tool.set_remaining_args(key, accept: accept, default: default, doc: doc)
287
+ _cur_tool.set_remaining_args(key, accept: accept, default: default, docs: docs)
285
288
  self
286
289
  end
287
290
 
@@ -32,5 +32,5 @@ module Toys
32
32
  # Current version of Toys core
33
33
  # @return [String]
34
34
  #
35
- CORE_VERSION = "0.3.2".freeze
35
+ CORE_VERSION = "0.3.3".freeze
36
36
  end
data/lib/toys/helpers.rb CHANGED
@@ -40,7 +40,9 @@ module Toys
40
40
  # Currently recognized module names are:
41
41
  #
42
42
  # * `:exec` : Methods to help execute subcommands.
43
- # * `:file_utils` : The FileUtils standard library methods.
43
+ # * `:fileutils` : The FileUtils standard library methods.
44
+ # * `:highline` : Methods from the highline gem.
45
+ # * `:spinner` : Displays a spinner on the terminal.
44
46
  #
45
47
  # @param [String,Symbol] name Name of the helper module to return
46
48
  # @return [Module,nil] The module, or `nil` if not found
@@ -32,8 +32,13 @@ require "fileutils"
32
32
  module Toys
33
33
  module Helpers
34
34
  ##
35
- # File system utilities. See the "fileutils" standard library.
35
+ # A module that provides all methods in the "fileutils" standard library.
36
36
  #
37
- FileUtils = ::FileUtils
37
+ module Fileutils
38
+ ## @private
39
+ def self.extend_object(obj)
40
+ obj.extend(::FileUtils)
41
+ end
42
+ end
38
43
  end
39
44
  end
@@ -0,0 +1,127 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ gem "highline", "~> 1.7"
31
+
32
+ require "highline"
33
+
34
+ module Toys
35
+ module Helpers
36
+ ##
37
+ # A module that provides access to highline.
38
+ #
39
+ module Highline
40
+ ##
41
+ # Returns a global highline instance
42
+ # @return [::HighLine]
43
+ #
44
+ def self.highline
45
+ @highline ||= ::HighLine.new
46
+ end
47
+
48
+ ##
49
+ # Returns a global highline instance
50
+ # @return [::HighLine]
51
+ #
52
+ def highline
53
+ Highline.highline
54
+ end
55
+
56
+ ##
57
+ # @see https://www.rubydoc.info/gems/highline/HighLine:agree HighLine#agree
58
+ #
59
+ def agree(*args, &block)
60
+ highline.agree(*args, &block)
61
+ end
62
+
63
+ ##
64
+ # @see https://www.rubydoc.info/gems/highline/HighLine:ask HighLine#ask
65
+ #
66
+ def ask(*args, &block)
67
+ highline.ask(*args, &block)
68
+ end
69
+
70
+ ##
71
+ # @see https://www.rubydoc.info/gems/highline/HighLine:choose HighLine#choose
72
+ #
73
+ def choose(*args, &block)
74
+ highline.choose(*args, &block)
75
+ end
76
+
77
+ ##
78
+ # @see https://www.rubydoc.info/gems/highline/HighLine:list HighLine#list
79
+ #
80
+ def list(*args, &block)
81
+ highline.list(*args, &block)
82
+ end
83
+
84
+ ##
85
+ # @see https://www.rubydoc.info/gems/highline/HighLine:say HighLine#say
86
+ #
87
+ def say(*args, &block)
88
+ highline.say(*args, &block)
89
+ end
90
+
91
+ ##
92
+ # @see https://www.rubydoc.info/gems/highline/HighLine.color HighLine.color
93
+ #
94
+ def color(*args, &block)
95
+ ::HighLine.color(*args, &block)
96
+ end
97
+
98
+ ##
99
+ # @see https://www.rubydoc.info/gems/highline/HighLine.color_code HighLine.color_code
100
+ #
101
+ def color_code(*args, &block)
102
+ ::HighLine.color_code(*args, &block)
103
+ end
104
+
105
+ ##
106
+ # @see https://www.rubydoc.info/gems/highline/HighLine.uncolor HighLine.uncolor
107
+ #
108
+ def uncolor(*args, &block)
109
+ ::HighLine.uncolor(*args, &block)
110
+ end
111
+
112
+ ##
113
+ # @see https://www.rubydoc.info/gems/highline/HighLine:indent HighLine#indent
114
+ #
115
+ def indent(*args, &block)
116
+ highline.indent(*args, &block)
117
+ end
118
+
119
+ ##
120
+ # @see https://www.rubydoc.info/gems/highline/HighLine:newline HighLine#newline
121
+ #
122
+ def newline(*args, &block)
123
+ highline.newline(*args, &block)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,136 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ require "monitor"
31
+
32
+ module Toys
33
+ module Helpers
34
+ ##
35
+ # A module that provides a spinner output.
36
+ #
37
+ module Spinner
38
+ ##
39
+ # Default length of a single frame, in seconds.
40
+ # @return [Float]
41
+ #
42
+ DEFAULT_FRAME_LENGTH = 0.1
43
+
44
+ ##
45
+ # Default set of frames.
46
+ # @return [Array<String,Array(String,Integer)>]
47
+ #
48
+ DEFAULT_FRAMES = ["-", "\\", "|", "/"].freeze
49
+
50
+ ##
51
+ # Display a spinner during a task. You should provide a block that
52
+ # performs the long-running task. While the block is executing, a
53
+ # spinner will be displayed.
54
+ #
55
+ # @param [String] leading_text Optional leading string to display to the
56
+ # left of the spinner.
57
+ # @param [Float] frame_length Length of a single frame, in seconds.
58
+ # Defaults to {DEFAULT_FRAME_LENGTH}.
59
+ # @param [Array<String,Array<String>>] frames An array of frames. Each
60
+ # frame should be either a string, or a two-element array of string
61
+ # and integer, where the integer is the visible length of the frame
62
+ # on screen. The latter form should be used if the frame string
63
+ # contains non-printing characters such as ANSI escape codes.
64
+ # Defaults to {DEFAULT_FRAMES}.
65
+ # @param [IO] stream Stream to output the spinner to. Defaults to STDOUT.
66
+ # Note the spinner will be disabled if this stream is not a tty.
67
+ # @param [String] final_text Optional final string to display when the
68
+ # spinner is complete.
69
+ #
70
+ def spinner(leading_text: "",
71
+ frame_length: DEFAULT_FRAME_LENGTH,
72
+ frames: DEFAULT_FRAMES,
73
+ stream: $stdout,
74
+ final_text: "")
75
+ return nil unless block_given?
76
+ unless leading_text.empty?
77
+ stream.write(leading_text)
78
+ stream.flush
79
+ end
80
+ spin = SpinDriver.new(stream, frames, frame_length)
81
+ begin
82
+ yield
83
+ ensure
84
+ spin.stop
85
+ unless final_text.empty?
86
+ stream.write(final_text)
87
+ stream.flush
88
+ end
89
+ end
90
+ end
91
+
92
+ ## @private
93
+ class SpinDriver
94
+ include ::MonitorMixin
95
+
96
+ def initialize(stream, frames, frame_length)
97
+ @stream = stream
98
+ @frames = frames.map { |f| f.is_a?(::Array) ? f : [f, f.size] }
99
+ @frame_length = frame_length
100
+ @cur_frame = 0
101
+ @stopping = false
102
+ @cond = new_cond
103
+ super()
104
+ @thread = @stream.tty? ? start_thread : nil
105
+ end
106
+
107
+ def stop
108
+ synchronize do
109
+ @stopping = true
110
+ @cond.broadcast
111
+ end
112
+ @thread.join if @thread
113
+ self
114
+ end
115
+
116
+ private
117
+
118
+ def start_thread
119
+ ::Thread.new do
120
+ synchronize do
121
+ until @stopping
122
+ @stream.write(@frames[@cur_frame][0])
123
+ @stream.flush
124
+ @cond.wait(@frame_length)
125
+ size = @frames[@cur_frame][1]
126
+ @stream.write("\b" * size + " " * size + "\b" * size)
127
+ @cur_frame += 1
128
+ @cur_frame = 0 if @cur_frame >= @frames.size
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end