thor 0.20.0 → 0.20.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +4 -0
- data/README.md +6 -2
- data/lib/thor/actions.rb +14 -4
- data/lib/thor/actions/file_manipulation.rb +11 -2
- data/lib/thor/base.rb +4 -5
- data/lib/thor/error.rb +85 -0
- data/lib/thor/group.rb +2 -2
- data/lib/thor/parser/options.rb +7 -2
- data/lib/thor/shell.rb +1 -1
- data/lib/thor/shell/basic.rb +52 -7
- data/lib/thor/util.rb +1 -1
- data/lib/thor/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dbcaa89ae8040cc061b9243fce97dbe6345e775ec5916d45d37ed9989bd14d79
|
4
|
+
data.tar.gz: b78c5c55482372cb805ac15c7326fe598c0483640b80e089e23ad5a58c574422
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfe4f6047443f79cdd7974ad556f39897e97c83dcbd4803a95bc26770e9f0b9f8225e310391863600779ae11c570543d8d97411ede4c77465435c9a24b82d32d
|
7
|
+
data.tar.gz: 40ba578626f442737b917a6ffd97fbea548bb512d105f6c35f6e99b91be368e862c1fea1803117659ac6f426b22cf5a4ec26614cdc4c619202144cb03eb58105
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# 0.20.1
|
2
|
+
* Support new versions fo ERB.
|
3
|
+
* Fix `check_unknown_options!` to not check the content that was not parsed, i.e. after a `--` or after the first unknown with `stop_on_unknown_option!`
|
4
|
+
|
1
5
|
## 0.20.0
|
2
6
|
* Add `check_default_type!` to check if the default value of an option matches the defined type.
|
3
7
|
It removes the warning on usage and gives the command authors the possibility to check for programming errors.
|
data/README.md
CHANGED
@@ -3,13 +3,11 @@ Thor
|
|
3
3
|
|
4
4
|
[![Gem Version](http://img.shields.io/gem/v/thor.svg)][gem]
|
5
5
|
[![Build Status](http://img.shields.io/travis/erikhuda/thor.svg)][travis]
|
6
|
-
[![Dependency Status](http://img.shields.io/gemnasium/erikhuda/thor.svg)][gemnasium]
|
7
6
|
[![Code Climate](http://img.shields.io/codeclimate/github/erikhuda/thor.svg)][codeclimate]
|
8
7
|
[![Coverage Status](http://img.shields.io/coveralls/erikhuda/thor.svg)][coveralls]
|
9
8
|
|
10
9
|
[gem]: https://rubygems.org/gems/thor
|
11
10
|
[travis]: http://travis-ci.org/erikhuda/thor
|
12
|
-
[gemnasium]: https://gemnasium.com/erikhuda/thor
|
13
11
|
[codeclimate]: https://codeclimate.com/github/erikhuda/thor
|
14
12
|
[coveralls]: https://coveralls.io/r/erikhuda/thor
|
15
13
|
|
@@ -21,7 +19,13 @@ utilities. It removes the pain of parsing command line options, writing
|
|
21
19
|
build tool. The syntax is Rake-like, so it should be familiar to most Rake
|
22
20
|
users.
|
23
21
|
|
22
|
+
Please note: Thor, by design, is a system tool created to allow seamless file and url
|
23
|
+
access, which should not receive application user input. It relies on [open-uri][open-uri],
|
24
|
+
which combined with application user input would provide a command injection attack
|
25
|
+
vector.
|
26
|
+
|
24
27
|
[rake]: https://github.com/ruby/rake
|
28
|
+
[open-uri]: https://ruby-doc.org/stdlib-2.5.1/libdoc/open-uri/rdoc/index.html
|
25
29
|
|
26
30
|
Installation
|
27
31
|
------------
|
data/lib/thor/actions.rb
CHANGED
@@ -113,8 +113,10 @@ class Thor
|
|
113
113
|
# the script started).
|
114
114
|
#
|
115
115
|
def relative_to_original_destination_root(path, remove_dot = true)
|
116
|
-
|
117
|
-
if path.
|
116
|
+
root = @destination_stack[0]
|
117
|
+
if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ''].include?(path[root.size..root.size])
|
118
|
+
path = path.dup
|
119
|
+
path[0...root.size] = '.'
|
118
120
|
remove_dot ? (path[2..-1] || "") : path
|
119
121
|
else
|
120
122
|
path
|
@@ -217,6 +219,7 @@ class Thor
|
|
217
219
|
shell.padding += 1 if verbose
|
218
220
|
|
219
221
|
contents = if is_uri
|
222
|
+
require "open-uri"
|
220
223
|
open(path, "Accept" => "application/x-thor-template", &:read)
|
221
224
|
else
|
222
225
|
open(path, &:read)
|
@@ -252,9 +255,16 @@ class Thor
|
|
252
255
|
|
253
256
|
say_status :run, desc, config.fetch(:verbose, true)
|
254
257
|
|
255
|
-
|
256
|
-
|
258
|
+
return if options[:pretend]
|
259
|
+
|
260
|
+
result = config[:capture] ? `#{command}` : system(command.to_s)
|
261
|
+
|
262
|
+
if config[:abort_on_failure]
|
263
|
+
success = config[:capture] ? $?.success? : result
|
264
|
+
abort unless success
|
257
265
|
end
|
266
|
+
|
267
|
+
result
|
258
268
|
end
|
259
269
|
|
260
270
|
# Executes a ruby script (taking into account WIN32 platform quirks).
|
@@ -60,6 +60,9 @@ class Thor
|
|
60
60
|
# destination. If a block is given instead of destination, the content of
|
61
61
|
# the url is yielded and used as location.
|
62
62
|
#
|
63
|
+
# +get+ relies on open-uri, so passing application user input would provide
|
64
|
+
# a command injection attack vector.
|
65
|
+
#
|
63
66
|
# ==== Parameters
|
64
67
|
# source<String>:: the address of the given content.
|
65
68
|
# destination<String>:: the relative path to the destination root.
|
@@ -117,7 +120,13 @@ class Thor
|
|
117
120
|
context = config.delete(:context) || instance_eval("binding")
|
118
121
|
|
119
122
|
create_file destination, nil, config do
|
120
|
-
|
123
|
+
match = ERB.version.match(/(\d+\.\d+\.\d+)/)
|
124
|
+
capturable_erb = if match && match[1] >= "2.2.0" # Ruby 2.6+
|
125
|
+
CapturableERB.new(::File.binread(source), :trim_mode => "-", :eoutvar => "@output_buffer")
|
126
|
+
else
|
127
|
+
CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer")
|
128
|
+
end
|
129
|
+
content = capturable_erb.tap do |erb|
|
121
130
|
erb.filename = source
|
122
131
|
end.result(context)
|
123
132
|
content = yield(content) if block
|
@@ -301,7 +310,7 @@ class Thor
|
|
301
310
|
def comment_lines(path, flag, *args)
|
302
311
|
flag = flag.respond_to?(:source) ? flag.source : flag
|
303
312
|
|
304
|
-
gsub_file(path, /^(\s*)([
|
313
|
+
gsub_file(path, /^(\s*)([^#\n]*#{flag})/, '\1# \2', *args)
|
305
314
|
end
|
306
315
|
|
307
316
|
# Removes a file at the given location.
|
data/lib/thor/base.rb
CHANGED
@@ -113,7 +113,7 @@ class Thor
|
|
113
113
|
end
|
114
114
|
|
115
115
|
# Whenever a class inherits from Thor or Thor::Group, we should track the
|
116
|
-
# class and the file on Thor::Base. This is the method
|
116
|
+
# class and the file on Thor::Base. This is the method responsible for it.
|
117
117
|
#
|
118
118
|
def register_klass_file(klass) #:nodoc:
|
119
119
|
file = caller[1].match(/(.*):\d+/)[1]
|
@@ -466,13 +466,13 @@ class Thor
|
|
466
466
|
dispatch(nil, given_args.dup, nil, config)
|
467
467
|
rescue Thor::Error => e
|
468
468
|
config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
|
469
|
-
exit(
|
469
|
+
exit(false) if exit_on_failure?
|
470
470
|
rescue Errno::EPIPE
|
471
471
|
# This happens if a thor command is piped to something like `head`,
|
472
472
|
# which closes the pipe when it's done reading. This will also
|
473
473
|
# mean that if the pipe is closed, further unnecessary
|
474
474
|
# computation will not occur.
|
475
|
-
exit(
|
475
|
+
exit(true)
|
476
476
|
end
|
477
477
|
|
478
478
|
# Allows to use private methods from parent in child classes as commands.
|
@@ -493,8 +493,7 @@ class Thor
|
|
493
493
|
alias_method :public_task, :public_command
|
494
494
|
|
495
495
|
def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
|
496
|
-
raise UndefinedCommandError,
|
497
|
-
raise UndefinedCommandError, "Could not find command #{command.inspect}."
|
496
|
+
raise UndefinedCommandError.new(command, all_commands.keys, (namespace if has_namespace))
|
498
497
|
end
|
499
498
|
alias_method :handle_no_task_error, :handle_no_command_error
|
500
499
|
|
data/lib/thor/error.rb
CHANGED
@@ -1,4 +1,25 @@
|
|
1
1
|
class Thor
|
2
|
+
Correctable =
|
3
|
+
begin
|
4
|
+
require 'did_you_mean'
|
5
|
+
|
6
|
+
module DidYouMean
|
7
|
+
# In order to support versions of Ruby that don't have keyword
|
8
|
+
# arguments, we need our own spell checker class that doesn't take key
|
9
|
+
# words. Even though this code wouldn't be hit because of the check
|
10
|
+
# above, it's still necessary because the interpreter would otherwise be
|
11
|
+
# unable to parse the file.
|
12
|
+
class NoKwargSpellChecker < SpellChecker
|
13
|
+
def initialize(dictionary)
|
14
|
+
@dictionary = dictionary
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
DidYouMean::Correctable
|
20
|
+
rescue LoadError
|
21
|
+
end
|
22
|
+
|
2
23
|
# Thor::Error is raised when it's caused by wrong usage of thor classes. Those
|
3
24
|
# errors have their backtrace suppressed and are nicely shown to the user.
|
4
25
|
#
|
@@ -10,6 +31,35 @@ class Thor
|
|
10
31
|
|
11
32
|
# Raised when a command was not found.
|
12
33
|
class UndefinedCommandError < Error
|
34
|
+
class SpellChecker
|
35
|
+
attr_reader :error
|
36
|
+
|
37
|
+
def initialize(error)
|
38
|
+
@error = error
|
39
|
+
end
|
40
|
+
|
41
|
+
def corrections
|
42
|
+
@corrections ||= spell_checker.correct(error.command).map(&:inspect)
|
43
|
+
end
|
44
|
+
|
45
|
+
def spell_checker
|
46
|
+
DidYouMean::NoKwargSpellChecker.new(error.all_commands)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :command, :all_commands
|
51
|
+
|
52
|
+
def initialize(command, all_commands, namespace)
|
53
|
+
@command = command
|
54
|
+
@all_commands = all_commands
|
55
|
+
|
56
|
+
message = "Could not find command #{command.inspect}"
|
57
|
+
message = namespace ? "#{message} in #{namespace.inspect} namespace." : "#{message}."
|
58
|
+
|
59
|
+
super(message)
|
60
|
+
end
|
61
|
+
|
62
|
+
prepend Correctable if Correctable
|
13
63
|
end
|
14
64
|
UndefinedTaskError = UndefinedCommandError
|
15
65
|
|
@@ -22,6 +72,34 @@ class Thor
|
|
22
72
|
end
|
23
73
|
|
24
74
|
class UnknownArgumentError < Error
|
75
|
+
class SpellChecker
|
76
|
+
attr_reader :error
|
77
|
+
|
78
|
+
def initialize(error)
|
79
|
+
@error = error
|
80
|
+
end
|
81
|
+
|
82
|
+
def corrections
|
83
|
+
@corrections ||=
|
84
|
+
error.unknown.flat_map { |unknown| spell_checker.correct(unknown) }.uniq.map(&:inspect)
|
85
|
+
end
|
86
|
+
|
87
|
+
def spell_checker
|
88
|
+
@spell_checker ||=
|
89
|
+
DidYouMean::NoKwargSpellChecker.new(error.switches)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
attr_reader :switches, :unknown
|
94
|
+
|
95
|
+
def initialize(switches, unknown)
|
96
|
+
@switches = switches
|
97
|
+
@unknown = unknown
|
98
|
+
|
99
|
+
super("Unknown switches #{unknown.map(&:inspect).join(', ')}")
|
100
|
+
end
|
101
|
+
|
102
|
+
prepend Correctable if Correctable
|
25
103
|
end
|
26
104
|
|
27
105
|
class RequiredArgumentMissingError < InvocationError
|
@@ -29,4 +107,11 @@ class Thor
|
|
29
107
|
|
30
108
|
class MalformattedArgumentError < InvocationError
|
31
109
|
end
|
110
|
+
|
111
|
+
if Correctable
|
112
|
+
DidYouMean::SPELL_CHECKERS.merge!(
|
113
|
+
'Thor::UndefinedCommandError' => UndefinedCommandError::SpellChecker,
|
114
|
+
'Thor::UnknownArgumentError' => UnknownArgumentError::SpellChecker
|
115
|
+
)
|
116
|
+
end
|
32
117
|
end
|
data/lib/thor/group.rb
CHANGED
@@ -61,7 +61,7 @@ class Thor::Group
|
|
61
61
|
invocations[name] = false
|
62
62
|
invocation_blocks[name] = block if block_given?
|
63
63
|
|
64
|
-
class_eval <<-METHOD, __FILE__, __LINE__
|
64
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
65
65
|
def _invoke_#{name.to_s.gsub(/\W/, '_')}
|
66
66
|
klass, command = self.class.prepare_for_invocation(nil, #{name.inspect})
|
67
67
|
|
@@ -120,7 +120,7 @@ class Thor::Group
|
|
120
120
|
invocations[name] = true
|
121
121
|
invocation_blocks[name] = block if block_given?
|
122
122
|
|
123
|
-
class_eval <<-METHOD, __FILE__, __LINE__
|
123
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
124
124
|
def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
|
125
125
|
return unless options[#{name.inspect}]
|
126
126
|
|
data/lib/thor/parser/options.rb
CHANGED
@@ -44,6 +44,7 @@ class Thor
|
|
44
44
|
@shorts = {}
|
45
45
|
@switches = {}
|
46
46
|
@extra = []
|
47
|
+
@stopped_parsing_after_extra_index = nil
|
47
48
|
|
48
49
|
options.each do |option|
|
49
50
|
@switches[option.switch_name] = option
|
@@ -66,6 +67,7 @@ class Thor
|
|
66
67
|
if result == OPTS_END
|
67
68
|
shift
|
68
69
|
@parsing_options = false
|
70
|
+
@stopped_parsing_after_extra_index ||= @extra.size
|
69
71
|
super
|
70
72
|
else
|
71
73
|
result
|
@@ -99,6 +101,7 @@ class Thor
|
|
99
101
|
elsif @stop_on_unknown
|
100
102
|
@parsing_options = false
|
101
103
|
@extra << shifted
|
104
|
+
@stopped_parsing_after_extra_index ||= @extra.size
|
102
105
|
@extra << shift while peek
|
103
106
|
break
|
104
107
|
elsif match
|
@@ -120,9 +123,11 @@ class Thor
|
|
120
123
|
end
|
121
124
|
|
122
125
|
def check_unknown!
|
126
|
+
to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra
|
127
|
+
|
123
128
|
# an unknown option starts with - or -- and has no more --'s afterward.
|
124
|
-
unknown =
|
125
|
-
raise UnknownArgumentError
|
129
|
+
unknown = to_check.select { |str| str =~ /^--?(?:(?!--).)*$/ }
|
130
|
+
raise UnknownArgumentError.new(@switches.keys, unknown) unless unknown.empty?
|
126
131
|
end
|
127
132
|
|
128
133
|
protected
|
data/lib/thor/shell.rb
CHANGED
@@ -55,7 +55,7 @@ class Thor
|
|
55
55
|
|
56
56
|
# Common methods that are delegated to the shell.
|
57
57
|
SHELL_DELEGATED_METHODS.each do |method|
|
58
|
-
module_eval <<-METHOD, __FILE__, __LINE__
|
58
|
+
module_eval <<-METHOD, __FILE__, __LINE__ + 1
|
59
59
|
def #{method}(*args,&block)
|
60
60
|
shell.#{method}(*args,&block)
|
61
61
|
end
|
data/lib/thor/shell/basic.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
class Thor
|
2
2
|
module Shell
|
3
3
|
class Basic
|
4
|
+
DEFAULT_TERMINAL_WIDTH = 80
|
5
|
+
|
4
6
|
attr_accessor :base
|
5
7
|
attr_reader :padding
|
6
8
|
|
@@ -45,6 +47,10 @@ class Thor
|
|
45
47
|
|
46
48
|
# Asks something to the user and receives a response.
|
47
49
|
#
|
50
|
+
# If a default value is specified it will be presented to the user
|
51
|
+
# and allows them to select that value with an empty response. This
|
52
|
+
# option is ignored when limited answers are supplied.
|
53
|
+
#
|
48
54
|
# If asked to limit the correct responses, you can pass in an
|
49
55
|
# array of acceptable answers. If one of those is not supplied,
|
50
56
|
# they will be shown a message stating that one of those answers
|
@@ -61,6 +67,8 @@ class Thor
|
|
61
67
|
# ==== Example
|
62
68
|
# ask("What is your name?")
|
63
69
|
#
|
70
|
+
# ask("What is the planet furthest from the sun?", :default => "Pluto")
|
71
|
+
#
|
64
72
|
# ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
|
65
73
|
#
|
66
74
|
# ask("What is your password?", :echo => false)
|
@@ -222,8 +230,20 @@ class Thor
|
|
222
230
|
paras = message.split("\n\n")
|
223
231
|
|
224
232
|
paras.map! do |unwrapped|
|
225
|
-
|
226
|
-
|
233
|
+
counter = 0
|
234
|
+
unwrapped.split(" ").inject do |memo, word|
|
235
|
+
word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n")
|
236
|
+
counter = 0 if word.include? "\n"
|
237
|
+
if (counter + word.length + 1) < width
|
238
|
+
memo = "#{memo} #{word}"
|
239
|
+
counter += (word.length + 1)
|
240
|
+
else
|
241
|
+
memo = "#{memo}\n#{word}"
|
242
|
+
counter = word.length
|
243
|
+
end
|
244
|
+
memo
|
245
|
+
end
|
246
|
+
end.compact!
|
227
247
|
|
228
248
|
paras.each do |para|
|
229
249
|
para.split("\n").each do |line|
|
@@ -239,11 +259,11 @@ class Thor
|
|
239
259
|
#
|
240
260
|
# ==== Parameters
|
241
261
|
# destination<String>:: the destination file to solve conflicts
|
242
|
-
# block<Proc>:: an optional block that returns the value to be used in diff
|
262
|
+
# block<Proc>:: an optional block that returns the value to be used in diff and merge
|
243
263
|
#
|
244
264
|
def file_collision(destination)
|
245
265
|
return true if @always_force
|
246
|
-
options = block_given? ? "[
|
266
|
+
options = block_given? ? "[Ynaqdhm]" : "[Ynaqh]"
|
247
267
|
|
248
268
|
loop do
|
249
269
|
answer = ask(
|
@@ -267,6 +287,13 @@ class Thor
|
|
267
287
|
when is?(:diff)
|
268
288
|
show_diff(destination, yield) if block_given?
|
269
289
|
say "Retrying..."
|
290
|
+
when is?(:merge)
|
291
|
+
if block_given? && !merge_tool.empty?
|
292
|
+
merge(destination, yield)
|
293
|
+
return nil
|
294
|
+
end
|
295
|
+
|
296
|
+
say "Please specify merge tool to `THOR_MERGE` env."
|
270
297
|
else
|
271
298
|
say file_collision_help
|
272
299
|
end
|
@@ -279,11 +306,11 @@ class Thor
|
|
279
306
|
result = if ENV["THOR_COLUMNS"]
|
280
307
|
ENV["THOR_COLUMNS"].to_i
|
281
308
|
else
|
282
|
-
unix? ? dynamic_width :
|
309
|
+
unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
|
283
310
|
end
|
284
|
-
result < 10 ?
|
311
|
+
result < 10 ? DEFAULT_TERMINAL_WIDTH : result
|
285
312
|
rescue
|
286
|
-
|
313
|
+
DEFAULT_TERMINAL_WIDTH
|
287
314
|
end
|
288
315
|
|
289
316
|
# Called if something goes wrong during the execution. This is used by Thor
|
@@ -344,6 +371,7 @@ class Thor
|
|
344
371
|
q - quit, abort
|
345
372
|
d - diff, show the differences between the old and the new
|
346
373
|
h - help, show this help
|
374
|
+
m - merge, run merge tool
|
347
375
|
HELP
|
348
376
|
end
|
349
377
|
|
@@ -432,6 +460,23 @@ class Thor
|
|
432
460
|
end
|
433
461
|
correct_answer
|
434
462
|
end
|
463
|
+
|
464
|
+
def merge(destination, content) #:nodoc:
|
465
|
+
require "tempfile"
|
466
|
+
Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
|
467
|
+
temp.write content
|
468
|
+
temp.rewind
|
469
|
+
system %(#{merge_tool} "#{temp.path}" "#{destination}")
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
def merge_tool #:nodoc:
|
474
|
+
@merge_tool ||= ENV["THOR_MERGE"] || git_merge_tool
|
475
|
+
end
|
476
|
+
|
477
|
+
def git_merge_tool #:nodoc:
|
478
|
+
`git config merge.tool`.rstrip rescue ""
|
479
|
+
end
|
435
480
|
end
|
436
481
|
end
|
437
482
|
end
|
data/lib/thor/util.rb
CHANGED
@@ -27,7 +27,7 @@ class Thor
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Receives a constant and converts it to a Thor namespace. Since Thor
|
30
|
-
# commands can be added to a sandbox, this method is also
|
30
|
+
# commands can be added to a sandbox, this method is also responsible for
|
31
31
|
# removing the sandbox namespace.
|
32
32
|
#
|
33
33
|
# This method should not be used in general because it's used to deal with
|
data/lib/thor/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.20.
|
4
|
+
version: 0.20.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yehuda Katz
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-11-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
91
|
version: 1.3.5
|
92
92
|
requirements: []
|
93
93
|
rubyforge_project:
|
94
|
-
rubygems_version: 2.6
|
94
|
+
rubygems_version: 2.7.6
|
95
95
|
signing_key:
|
96
96
|
specification_version: 4
|
97
97
|
summary: Thor is a toolkit for building powerful command-line interfaces.
|