toys-core 0.9.2 → 0.10.2
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 +4 -4
- data/.yardopts +2 -1
- data/CHANGELOG.md +47 -0
- data/LICENSE.md +1 -1
- data/README.md +3 -3
- data/lib/toys-core.rb +14 -21
- data/lib/toys/acceptor.rb +0 -21
- data/lib/toys/arg_parser.rb +1 -22
- data/lib/toys/cli.rb +102 -70
- data/lib/toys/compat.rb +49 -41
- data/lib/toys/completion.rb +0 -21
- data/lib/toys/context.rb +0 -23
- data/lib/toys/core.rb +1 -22
- data/lib/toys/dsl/flag.rb +0 -21
- data/lib/toys/dsl/flag_group.rb +0 -21
- data/lib/toys/dsl/positional_arg.rb +0 -21
- data/lib/toys/dsl/tool.rb +136 -51
- data/lib/toys/errors.rb +1 -22
- data/lib/toys/flag.rb +0 -21
- data/lib/toys/flag_group.rb +0 -21
- data/lib/toys/input_file.rb +0 -21
- data/lib/toys/loader.rb +42 -78
- data/lib/toys/middleware.rb +146 -77
- data/lib/toys/mixin.rb +0 -21
- data/lib/toys/module_lookup.rb +3 -26
- data/lib/toys/positional_arg.rb +0 -21
- data/lib/toys/source_info.rb +49 -38
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +0 -23
- data/lib/toys/standard_middleware/apply_config.rb +42 -0
- data/lib/toys/standard_middleware/handle_usage_errors.rb +7 -28
- data/lib/toys/standard_middleware/set_default_descriptions.rb +0 -23
- data/lib/toys/standard_middleware/show_help.rb +0 -23
- data/lib/toys/standard_middleware/show_root_version.rb +0 -23
- data/lib/toys/standard_mixins/bundler.rb +89 -0
- data/lib/toys/standard_mixins/exec.rb +478 -128
- data/lib/toys/standard_mixins/fileutils.rb +0 -21
- data/lib/toys/standard_mixins/gems.rb +2 -24
- data/lib/toys/standard_mixins/highline.rb +0 -21
- data/lib/toys/standard_mixins/terminal.rb +0 -21
- data/lib/toys/template.rb +0 -21
- data/lib/toys/tool.rb +22 -34
- data/lib/toys/utils/completion_engine.rb +0 -21
- data/lib/toys/utils/exec.rb +142 -71
- data/lib/toys/utils/gems.rb +181 -63
- data/lib/toys/utils/help_text.rb +0 -21
- data/lib/toys/utils/terminal.rb +46 -37
- data/lib/toys/wrappable_string.rb +0 -21
- metadata +25 -9
data/lib/toys/utils/gems.rb
CHANGED
@@ -1,30 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
3
|
+
require "monitor"
|
4
|
+
require "rubygems"
|
23
5
|
|
24
6
|
module Toys
|
25
7
|
module Utils
|
26
8
|
##
|
27
|
-
# A helper
|
9
|
+
# A helper class that activates and installs gems and sets up bundler.
|
28
10
|
#
|
29
11
|
# This class is not loaded by default. Before using it directly, you should
|
30
12
|
# `require "toys/utils/gems"`
|
@@ -58,6 +40,30 @@ module Toys
|
|
58
40
|
end
|
59
41
|
end
|
60
42
|
|
43
|
+
##
|
44
|
+
# Failed to run Bundler
|
45
|
+
#
|
46
|
+
class BundlerFailedError < ::StandardError
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Could not find a Gemfile
|
51
|
+
#
|
52
|
+
class GemfileNotFoundError < BundlerFailedError
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# The bundle is not and could not be installed
|
57
|
+
#
|
58
|
+
class BundleNotInstalledError < BundlerFailedError
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Bundler has already been run; cannot do so again
|
63
|
+
#
|
64
|
+
class AlreadyBundledError < BundlerFailedError
|
65
|
+
end
|
66
|
+
|
61
67
|
##
|
62
68
|
# Activate the given gem. If it is not present, attempt to install it (or
|
63
69
|
# inform the user to update the bundle).
|
@@ -73,24 +79,42 @@ module Toys
|
|
73
79
|
##
|
74
80
|
# Create a new gem activator.
|
75
81
|
#
|
76
|
-
# @param
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
82
|
+
# @param on_missing [:confirm,:error,:install] What to do if a needed gem
|
83
|
+
# is not installed. Possible values:
|
84
|
+
# * `:confirm` - prompt the user on whether to install
|
85
|
+
# * `:error` - raise an exception
|
86
|
+
# * `:install` - just install the gem
|
87
|
+
# The default is `:confirm`.
|
88
|
+
# @param on_conflict [:error,:warn,:ignore] What to do if bundler has
|
89
|
+
# already been run with a different Gemfile. Possible values:
|
90
|
+
# * `:error` - raise an exception
|
91
|
+
# * `:ignore` - just silently proceed without bundling again
|
92
|
+
# * `:warn` - print a warning and proceed without bundling again
|
93
|
+
# The default is `:error`.
|
94
|
+
# @param terminal [Toys::Utils::Terminal] Terminal to use (optional)
|
95
|
+
# @param input [IO] Input IO (optional, defaults to STDIN)
|
96
|
+
# @param output [IO] Output IO (optional, defaults to STDOUT)
|
97
|
+
# @param suppress_confirm [Boolean] Deprecated. Use `on_missing` instead.
|
98
|
+
# @param default_confirm [Boolean] Deprecated. Use `on_missing` instead.
|
83
99
|
#
|
84
|
-
def initialize(
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
@
|
92
|
-
@
|
93
|
-
|
100
|
+
def initialize(on_missing: nil,
|
101
|
+
on_conflict: nil,
|
102
|
+
terminal: nil,
|
103
|
+
input: nil,
|
104
|
+
output: nil,
|
105
|
+
suppress_confirm: nil,
|
106
|
+
default_confirm: nil)
|
107
|
+
@default_confirm = default_confirm || default_confirm.nil? ? true : false
|
108
|
+
@on_missing = on_missing ||
|
109
|
+
if suppress_confirm
|
110
|
+
@default_confirm ? :install : :error
|
111
|
+
else
|
112
|
+
:confirm
|
113
|
+
end
|
114
|
+
@on_conflict = on_conflict || :error
|
115
|
+
@terminal = terminal
|
116
|
+
@input = input || ::STDIN
|
117
|
+
@output = output || ::STDOUT
|
94
118
|
end
|
95
119
|
|
96
120
|
##
|
@@ -102,13 +126,56 @@ module Toys
|
|
102
126
|
# @return [void]
|
103
127
|
#
|
104
128
|
def activate(name, *requirements)
|
105
|
-
|
106
|
-
|
107
|
-
|
129
|
+
Gems.synchronize do
|
130
|
+
begin
|
131
|
+
gem(name, *requirements)
|
132
|
+
rescue ::Gem::LoadError => e
|
133
|
+
handle_activation_error(e, name, requirements)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Set up the bundle.
|
140
|
+
#
|
141
|
+
# @param groups [Array<String>] The groups to include in setup
|
142
|
+
# @param search_dirs [Array<String>] Directories to search for a Gemfile
|
143
|
+
# @return [void]
|
144
|
+
#
|
145
|
+
def bundle(groups: nil,
|
146
|
+
search_dirs: nil)
|
147
|
+
Gems.synchronize do
|
148
|
+
gemfile_path = find_gemfile(Array(search_dirs))
|
149
|
+
activate("bundler", "~> 2.1")
|
150
|
+
if configure_gemfile(gemfile_path)
|
151
|
+
setup_bundle(gemfile_path, groups || [])
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
@global_mutex = ::Monitor.new
|
157
|
+
|
158
|
+
## @private
|
159
|
+
def self.synchronize(&block)
|
160
|
+
@global_mutex.synchronize(&block)
|
108
161
|
end
|
109
162
|
|
110
163
|
private
|
111
164
|
|
165
|
+
def terminal
|
166
|
+
@terminal ||= begin
|
167
|
+
require "toys/utils/terminal"
|
168
|
+
Utils::Terminal.new(input: @input, output: @output)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def exec_util
|
173
|
+
@exec_util ||= begin
|
174
|
+
require "toys/utils/exec"
|
175
|
+
Utils::Exec.new
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
112
179
|
def handle_activation_error(error, name, requirements)
|
113
180
|
is_missing_spec =
|
114
181
|
if defined?(::Gem::MissingSpecError)
|
@@ -116,11 +183,11 @@ module Toys
|
|
116
183
|
else
|
117
184
|
error.message.include?("Could not find")
|
118
185
|
end
|
119
|
-
|
186
|
+
if !is_missing_spec || @on_missing == :error
|
120
187
|
report_error(name, requirements, error)
|
121
188
|
return
|
122
189
|
end
|
123
|
-
|
190
|
+
confirm_and_install_gem(name, requirements)
|
124
191
|
begin
|
125
192
|
gem(name, *requirements)
|
126
193
|
rescue ::Gem::LoadError => e
|
@@ -132,28 +199,16 @@ module Toys
|
|
132
199
|
"#{name.inspect}, #{requirements.map(&:inspect).join(', ')}"
|
133
200
|
end
|
134
201
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
default: @default_confirm)
|
202
|
+
def confirm_and_install_gem(name, requirements)
|
203
|
+
if @on_missing == :confirm
|
204
|
+
requirements_text = gem_requirements_text(name, requirements)
|
205
|
+
response = terminal.confirm("Gem needed: #{requirements_text}. Install? ",
|
206
|
+
default: @default_confirm)
|
207
|
+
unless response
|
208
|
+
raise InstallFailedError, "Canceled installation of needed gem: #{requirements_text}"
|
143
209
|
end
|
144
|
-
unless response
|
145
|
-
raise InstallFailedError, "Canceled installation of needed gem: #{requirements_text}"
|
146
210
|
end
|
147
|
-
|
148
|
-
end
|
149
|
-
|
150
|
-
def perform_install(name, requirements)
|
151
|
-
result = @terminal.spinner(leading_text: "Installing gem #{name}... ",
|
152
|
-
final_text: "Done.\n") do
|
153
|
-
@exec.exec(["gem", "install", name, "--version", requirements.join(",")],
|
154
|
-
out: :capture, err: :capture)
|
155
|
-
end
|
156
|
-
@terminal.puts(result.captured_out + result.captured_err)
|
211
|
+
result = exec_util.exec(["gem", "install", name, "--version", requirements.join(",")])
|
157
212
|
if result.error?
|
158
213
|
raise InstallFailedError, "Failed to install gem #{name}"
|
159
214
|
end
|
@@ -167,6 +222,69 @@ module Toys
|
|
167
222
|
end
|
168
223
|
raise ActivationFailedError, err.message
|
169
224
|
end
|
225
|
+
|
226
|
+
def find_gemfile(search_dirs)
|
227
|
+
search_dirs.each do |dir|
|
228
|
+
gemfile_path = ::File.join(dir, "Gemfile")
|
229
|
+
return gemfile_path if ::File.readable?(gemfile_path)
|
230
|
+
end
|
231
|
+
raise GemfileNotFoundError, "Gemfile not found"
|
232
|
+
end
|
233
|
+
|
234
|
+
def configure_gemfile(gemfile_path)
|
235
|
+
old_path = ::ENV["BUNDLE_GEMFILE"]
|
236
|
+
if old_path && gemfile_path != old_path
|
237
|
+
case @on_conflict
|
238
|
+
when :warn
|
239
|
+
terminal.puts("Warning: could not set up bundler because it is already set up.", :red)
|
240
|
+
when :error
|
241
|
+
raise AlreadyBundledError, "Could not set up bundler because it is already set up"
|
242
|
+
end
|
243
|
+
return false
|
244
|
+
end
|
245
|
+
::ENV["BUNDLE_GEMFILE"] = gemfile_path
|
246
|
+
true
|
247
|
+
end
|
248
|
+
|
249
|
+
def setup_bundle(gemfile_path, groups)
|
250
|
+
require "bundler"
|
251
|
+
begin
|
252
|
+
::Bundler.setup(*groups)
|
253
|
+
rescue ::Bundler::GemNotFound
|
254
|
+
restore_toys_libs
|
255
|
+
install_bundle(gemfile_path)
|
256
|
+
::Bundler.reset!
|
257
|
+
::Bundler.setup(*groups)
|
258
|
+
end
|
259
|
+
restore_toys_libs
|
260
|
+
end
|
261
|
+
|
262
|
+
def restore_toys_libs
|
263
|
+
$LOAD_PATH.unshift(::Toys::CORE_LIB_PATH)
|
264
|
+
$LOAD_PATH.unshift(::Toys::LIB_PATH) if ::Toys.const_defined?(:LIB_PATH)
|
265
|
+
end
|
266
|
+
|
267
|
+
def permission_to_bundle?
|
268
|
+
case @on_missing
|
269
|
+
when :install
|
270
|
+
true
|
271
|
+
when :error
|
272
|
+
false
|
273
|
+
else
|
274
|
+
terminal.confirm("Your bundle is not complete. Install? ", default: @default_confirm)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def install_bundle(gemfile_path)
|
279
|
+
gemfile_dir = ::File.dirname(gemfile_path)
|
280
|
+
unless permission_to_bundle?
|
281
|
+
raise BundleNotInstalledError,
|
282
|
+
"Your bundle is not installed. Consider running" \
|
283
|
+
" `cd #{gemfile_dir} && bundle install`"
|
284
|
+
end
|
285
|
+
require "bundler/cli"
|
286
|
+
::Bundler::CLI.start(["install"])
|
287
|
+
end
|
170
288
|
end
|
171
289
|
end
|
172
290
|
end
|
data/lib/toys/utils/help_text.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
module Toys
|
25
4
|
module Utils
|
26
5
|
##
|
data/lib/toys/utils/terminal.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
require "stringio"
|
25
4
|
require "monitor"
|
26
5
|
|
@@ -146,6 +125,8 @@ module Toys
|
|
146
125
|
styled ? true : false
|
147
126
|
end
|
148
127
|
@named_styles = BUILTIN_STYLE_NAMES.dup
|
128
|
+
@output_mutex = ::Monitor.new
|
129
|
+
@input_mutex = ::Monitor.new
|
149
130
|
end
|
150
131
|
|
151
132
|
##
|
@@ -164,7 +145,7 @@ module Toys
|
|
164
145
|
# Whether output is styled
|
165
146
|
# @return [Boolean]
|
166
147
|
#
|
167
|
-
|
148
|
+
attr_reader :styled
|
168
149
|
|
169
150
|
##
|
170
151
|
# Write a partial line without appending a newline.
|
@@ -175,11 +156,41 @@ module Toys
|
|
175
156
|
# @return [self]
|
176
157
|
#
|
177
158
|
def write(str = "", *styles)
|
178
|
-
|
179
|
-
|
159
|
+
@output_mutex.synchronize do
|
160
|
+
begin
|
161
|
+
output&.write(apply_styles(str, *styles))
|
162
|
+
output&.flush
|
163
|
+
rescue ::IOError
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
end
|
180
167
|
self
|
181
168
|
end
|
182
169
|
|
170
|
+
##
|
171
|
+
# Read a line, blocking until one is available.
|
172
|
+
#
|
173
|
+
# @return [String] the entire string including the temrinating newline
|
174
|
+
# @return [nil] if the input is closed or at eof, or there is no input
|
175
|
+
#
|
176
|
+
def readline
|
177
|
+
@input_mutex.synchronize do
|
178
|
+
begin
|
179
|
+
input&.gets
|
180
|
+
rescue ::IOError
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# This method is defined so that `::Logger` will recognize a terminal as
|
188
|
+
# a log device target, but it does not actually close anything.
|
189
|
+
#
|
190
|
+
def close
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
|
183
194
|
##
|
184
195
|
# Write a line, appending a newline if one is not already present.
|
185
196
|
#
|
@@ -233,7 +244,7 @@ module Toys
|
|
233
244
|
prompt = "#{ptext} #{trailing_text}#{pspaces}"
|
234
245
|
end
|
235
246
|
write(prompt, *styles)
|
236
|
-
resp =
|
247
|
+
resp = readline.to_s.chomp
|
237
248
|
resp.empty? ? default.to_s : resp
|
238
249
|
end
|
239
250
|
|
@@ -296,13 +307,13 @@ module Toys
|
|
296
307
|
return nil unless block_given?
|
297
308
|
frame_length ||= DEFAULT_SPINNER_FRAME_LENGTH
|
298
309
|
frames ||= DEFAULT_SPINNER_FRAMES
|
299
|
-
|
310
|
+
write(leading_text) unless leading_text.empty?
|
300
311
|
spin = SpinDriver.new(self, frames, Array(style), frame_length)
|
301
312
|
begin
|
302
313
|
yield
|
303
314
|
ensure
|
304
315
|
spin.stop
|
305
|
-
|
316
|
+
write(final_text) unless final_text.empty?
|
306
317
|
end
|
307
318
|
end
|
308
319
|
|
@@ -312,8 +323,8 @@ module Toys
|
|
312
323
|
# @return [Array(Integer,Integer)]
|
313
324
|
#
|
314
325
|
def size
|
315
|
-
if
|
316
|
-
|
326
|
+
if output.respond_to?(:tty?) && output.tty? && output.respond_to?(:winsize)
|
327
|
+
output.winsize.reverse
|
317
328
|
else
|
318
329
|
[80, 25]
|
319
330
|
end
|
@@ -422,10 +433,8 @@ module Toys
|
|
422
433
|
|
423
434
|
## @private
|
424
435
|
class SpinDriver
|
425
|
-
include ::MonitorMixin
|
426
|
-
|
427
436
|
def initialize(terminal, frames, style, frame_length)
|
428
|
-
|
437
|
+
@mutex = ::Monitor.new
|
429
438
|
@terminal = terminal
|
430
439
|
@frames = frames.map do |f|
|
431
440
|
[@terminal.apply_styles(f, *style), Terminal.remove_style_escapes(f).size]
|
@@ -433,12 +442,12 @@ module Toys
|
|
433
442
|
@frame_length = frame_length
|
434
443
|
@cur_frame = 0
|
435
444
|
@stopping = false
|
436
|
-
@cond = new_cond
|
445
|
+
@cond = @mutex.new_cond
|
437
446
|
@thread = @terminal.output.tty? ? start_thread : nil
|
438
447
|
end
|
439
448
|
|
440
449
|
def stop
|
441
|
-
synchronize do
|
450
|
+
@mutex.synchronize do
|
442
451
|
@stopping = true
|
443
452
|
@cond.broadcast
|
444
453
|
end
|
@@ -450,12 +459,12 @@ module Toys
|
|
450
459
|
|
451
460
|
def start_thread
|
452
461
|
::Thread.new do
|
453
|
-
synchronize do
|
462
|
+
@mutex.synchronize do
|
454
463
|
until @stopping
|
455
|
-
@terminal.
|
464
|
+
@terminal.write(@frames[@cur_frame][0])
|
456
465
|
@cond.wait(@frame_length)
|
457
466
|
size = @frames[@cur_frame][1]
|
458
|
-
@terminal.
|
467
|
+
@terminal.write("\b" * size + " " * size + "\b" * size)
|
459
468
|
@cur_frame += 1
|
460
469
|
@cur_frame = 0 if @cur_frame >= @frames.size
|
461
470
|
end
|