toys-core 0.3.2 → 0.3.3

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: 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