thor 0.19.4 → 1.0.0
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 +5 -5
- data/CHANGELOG.md +53 -0
- data/README.md +6 -2
- data/lib/thor.rb +44 -12
- data/lib/thor/actions.rb +31 -13
- data/lib/thor/actions/create_file.rb +2 -1
- data/lib/thor/actions/create_link.rb +2 -1
- data/lib/thor/actions/directory.rb +7 -17
- data/lib/thor/actions/empty_directory.rb +9 -1
- data/lib/thor/actions/file_manipulation.rb +58 -12
- data/lib/thor/actions/inject_into_file.rb +27 -10
- data/lib/thor/base.rb +75 -41
- data/lib/thor/command.rb +30 -21
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +12 -0
- data/lib/thor/error.rb +78 -0
- data/lib/thor/group.rb +4 -4
- data/lib/thor/invocation.rb +1 -0
- data/lib/thor/line_editor.rb +2 -2
- data/lib/thor/line_editor/basic.rb +2 -0
- data/lib/thor/line_editor/readline.rb +6 -6
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/parser/arguments.rb +2 -2
- data/lib/thor/parser/option.rb +22 -9
- data/lib/thor/parser/options.rb +25 -9
- data/lib/thor/rake_compat.rb +1 -0
- data/lib/thor/runner.rb +9 -6
- data/lib/thor/shell.rb +4 -4
- data/lib/thor/shell/basic.rb +72 -17
- data/lib/thor/shell/color.rb +6 -2
- data/lib/thor/shell/html.rb +3 -3
- data/lib/thor/util.rb +17 -1
- data/lib/thor/version.rb +1 -1
- data/thor.gemspec +2 -2
- metadata +13 -9
- data/lib/thor/core_ext/io_binary_read.rb +0 -12
- data/lib/thor/core_ext/ordered_hash.rb +0 -129
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0fc8dc6eec2f5a8963a04be9954614a50272049dc1ee40ac444a8de4961bc982
|
4
|
+
data.tar.gz: 0ea9eb9c0a1685ebf30a7c3b245d7f364dc03a1d3bd52a9af90135a561fd8e5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88f22ea77e8939f637d5b01c7178ec0b4ce12c92dfeab0d747a5d4b00c5fef90caff0a95fef0ff3b7d80c360ac15afdc222f77322729ec1de55252cdf76fd644
|
7
|
+
data.tar.gz: 49364d76cf884f66a68bc12536668881bd144237017aad4d38bc5bd9d041672b9b68ce0642139d97e98990e39899c522b6d4c37eb6591f826d276a26a1bf5e11
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,56 @@
|
|
1
|
+
# 1.0.0
|
2
|
+
* Drop support to Ruby 1.8 and 1.9.
|
3
|
+
* Deprecate relying on default `exit_on_failure?`.
|
4
|
+
In preparation to make Thor commands exit when there is a failure we are deprecating
|
5
|
+
defining a command without defining what behavior is expected when there is a failure.
|
6
|
+
|
7
|
+
To fix the deprecation you need to define a class method called `exit_on_failure?` returning
|
8
|
+
|
9
|
+
`false` if you want the current behavior or `true` if you want the new behavior.
|
10
|
+
* Deprecate defining an option with the default value using a different type as defined in the option.
|
11
|
+
* Allow options to be repeatable. See #674.
|
12
|
+
|
13
|
+
# 0.20.3
|
14
|
+
* Support old versions of `did_you_mean`.
|
15
|
+
|
16
|
+
# 0.20.2
|
17
|
+
* Fix `did_you_mean` support.
|
18
|
+
|
19
|
+
# 0.20.1
|
20
|
+
* Support new versions of ERB.
|
21
|
+
* 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!`
|
22
|
+
* Add `did_you_mean` support.
|
23
|
+
|
24
|
+
## 0.20.0
|
25
|
+
* Add `check_default_type!` to check if the default value of an option matches the defined type.
|
26
|
+
It removes the warning on usage and gives the command authors the possibility to check for programming errors.
|
27
|
+
|
28
|
+
* Add `disable_required_check!` to disable check for required options in some commands.
|
29
|
+
It is a substitute of `disable_class_options` that was not working as intended.
|
30
|
+
|
31
|
+
* Add `inject_into_module`.
|
32
|
+
|
33
|
+
## 0.19.4, release 2016-11-28
|
34
|
+
* Rename `Thor::Base#thor_reserved_word?` to `#is_thor_reserved_word?`
|
35
|
+
|
36
|
+
## 0.19.3, release 2016-11-27
|
37
|
+
* Output a warning instead of raising an exception when a default option value doesn't match its specified type
|
38
|
+
|
39
|
+
## 0.19.2, release 2016-11-26
|
40
|
+
* Fix bug with handling of colors passed to `ask` (and methods like `yes?` and `no?` which it underpins)
|
41
|
+
* Allow numeric arguments to be negative
|
42
|
+
* Ensure that default option values are of the specified type (e.g. you can't specify `"foo"` as the default for a numeric option), but make symbols and strings interchangeable
|
43
|
+
* Add `Thor::Shell::Basic#indent` method for intending output
|
44
|
+
* Fix `remove_command` for an inherited command (see #451)
|
45
|
+
* Allow hash arguments to only have each key provided once (see #455)
|
46
|
+
* Allow commands to disable class options, for instance for "help" commands (see #363)
|
47
|
+
* Do not generate a negative option (`--no-no-foo`) for already negative boolean options (`--no-foo`)
|
48
|
+
* Improve compatibility of `Thor::CoreExt::HashWithIndifferentAccess` with Ruby standard library `Hash`
|
49
|
+
* Allow specifying a custom binding for template evaluation (e.g. `#key?` and `#fetch`)
|
50
|
+
* Fix support for subcommand-specific "help"s
|
51
|
+
* Use a string buffer when handling ERB for Ruby 2.3 compatibility
|
52
|
+
* Update dependencies
|
53
|
+
|
1
54
|
## 0.19.1, release 2014-03-24
|
2
55
|
* Fix `say` non-String break regression
|
3
56
|
|
data/README.md
CHANGED
@@ -3,13 +3,11 @@ Thor
|
|
3
3
|
|
4
4
|
[][gem]
|
5
5
|
[][travis]
|
6
|
-
[][gemnasium]
|
7
6
|
[][codeclimate]
|
8
7
|
[][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.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require "set"
|
2
|
-
|
2
|
+
require_relative "thor/base"
|
3
3
|
|
4
4
|
class Thor
|
5
5
|
class << self
|
@@ -90,9 +90,14 @@ class Thor
|
|
90
90
|
# ==== Parameters
|
91
91
|
# Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
|
92
92
|
#
|
93
|
-
def map(mappings = nil)
|
93
|
+
def map(mappings = nil, **kw)
|
94
94
|
@map ||= from_superclass(:map, {})
|
95
95
|
|
96
|
+
if mappings && !kw.empty?
|
97
|
+
mappings = kw.merge!(mappings)
|
98
|
+
else
|
99
|
+
mappings ||= kw
|
100
|
+
end
|
96
101
|
if mappings
|
97
102
|
mappings.each do |key, value|
|
98
103
|
if key.respond_to?(:each)
|
@@ -158,10 +163,6 @@ class Thor
|
|
158
163
|
end
|
159
164
|
alias_method :option, :method_option
|
160
165
|
|
161
|
-
def disable_class_options
|
162
|
-
@disable_class_options = true
|
163
|
-
end
|
164
|
-
|
165
166
|
# Prints help information for the given command.
|
166
167
|
#
|
167
168
|
# ==== Parameters
|
@@ -174,7 +175,7 @@ class Thor
|
|
174
175
|
handle_no_command_error(meth) unless command
|
175
176
|
|
176
177
|
shell.say "Usage:"
|
177
|
-
shell.say " #{banner(command)}"
|
178
|
+
shell.say " #{banner(command).split("\n").join("\n ")}"
|
178
179
|
shell.say
|
179
180
|
class_options_help(shell, nil => command.options.values)
|
180
181
|
if command.long_description
|
@@ -241,6 +242,9 @@ class Thor
|
|
241
242
|
invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
|
242
243
|
invoke subcommand_class, *invoke_args
|
243
244
|
end
|
245
|
+
subcommand_class.commands.each do |_meth, command|
|
246
|
+
command.ancestor_name = subcommand
|
247
|
+
end
|
244
248
|
end
|
245
249
|
alias_method :subtask, :subcommand
|
246
250
|
|
@@ -326,12 +330,38 @@ class Thor
|
|
326
330
|
command && stop_on_unknown_option.include?(command.name.to_sym)
|
327
331
|
end
|
328
332
|
|
333
|
+
# Disable the check for required options for the given commands.
|
334
|
+
# This is useful if you have a command that does not need the required options
|
335
|
+
# to work, like help.
|
336
|
+
#
|
337
|
+
# ==== Parameters
|
338
|
+
# Symbol ...:: A list of commands that should be affected.
|
339
|
+
def disable_required_check!(*command_names)
|
340
|
+
disable_required_check.merge(command_names)
|
341
|
+
end
|
342
|
+
|
343
|
+
def disable_required_check?(command) #:nodoc:
|
344
|
+
command && disable_required_check.include?(command.name.to_sym)
|
345
|
+
end
|
346
|
+
|
347
|
+
def deprecation_warning(message) #:nodoc:
|
348
|
+
unless ENV['THOR_SILENCE_DEPRECATION']
|
349
|
+
warn "Deprecation warning: #{message}\n" +
|
350
|
+
'You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.'
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
329
354
|
protected
|
330
355
|
|
331
356
|
def stop_on_unknown_option #:nodoc:
|
332
357
|
@stop_on_unknown_option ||= Set.new
|
333
358
|
end
|
334
359
|
|
360
|
+
# help command has the required check disabled by default.
|
361
|
+
def disable_required_check #:nodoc:
|
362
|
+
@disable_required_check ||= Set.new([:help])
|
363
|
+
end
|
364
|
+
|
335
365
|
# The method responsible for dispatching given the args.
|
336
366
|
def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
|
337
367
|
meth ||= retrieve_command_name(given_args)
|
@@ -375,7 +405,10 @@ class Thor
|
|
375
405
|
# the namespace should be displayed as arguments.
|
376
406
|
#
|
377
407
|
def banner(command, namespace = nil, subcommand = false)
|
378
|
-
|
408
|
+
$thor_runner ||= false
|
409
|
+
command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage|
|
410
|
+
"#{basename} #{formatted_usage}"
|
411
|
+
end.join("\n")
|
379
412
|
end
|
380
413
|
|
381
414
|
def baseclass #:nodoc:
|
@@ -390,12 +423,12 @@ class Thor
|
|
390
423
|
@usage ||= nil
|
391
424
|
@desc ||= nil
|
392
425
|
@long_desc ||= nil
|
393
|
-
@
|
426
|
+
@hide ||= nil
|
394
427
|
|
395
428
|
if @usage && @desc
|
396
429
|
base_class = @hide ? Thor::HiddenCommand : Thor::Command
|
397
|
-
commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options
|
398
|
-
@usage, @desc, @long_desc, @method_options, @hide
|
430
|
+
commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
|
431
|
+
@usage, @desc, @long_desc, @method_options, @hide = nil
|
399
432
|
true
|
400
433
|
elsif all_commands[meth] || meth == "method_missing"
|
401
434
|
true
|
@@ -477,7 +510,6 @@ class Thor
|
|
477
510
|
map HELP_MAPPINGS => :help
|
478
511
|
|
479
512
|
desc "help [COMMAND]", "Describe available commands or one specific command"
|
480
|
-
disable_class_options
|
481
513
|
def help(command = nil, subcommand = false)
|
482
514
|
if command
|
483
515
|
if self.class.subcommands.include? command
|
data/lib/thor/actions.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require "thor/actions/empty_directory"
|
8
|
-
require "thor/actions/file_manipulation"
|
9
|
-
require "thor/actions/inject_into_file"
|
1
|
+
require_relative "actions/create_file"
|
2
|
+
require_relative "actions/create_link"
|
3
|
+
require_relative "actions/directory"
|
4
|
+
require_relative "actions/empty_directory"
|
5
|
+
require_relative "actions/file_manipulation"
|
6
|
+
require_relative "actions/inject_into_file"
|
10
7
|
|
11
8
|
class Thor
|
12
9
|
module Actions
|
13
10
|
attr_accessor :behavior
|
14
11
|
|
15
12
|
def self.included(base) #:nodoc:
|
13
|
+
super(base)
|
16
14
|
base.extend ClassMethods
|
17
15
|
end
|
18
16
|
|
@@ -114,8 +112,10 @@ class Thor
|
|
114
112
|
# the script started).
|
115
113
|
#
|
116
114
|
def relative_to_original_destination_root(path, remove_dot = true)
|
117
|
-
|
118
|
-
if path.
|
115
|
+
root = @destination_stack[0]
|
116
|
+
if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ''].include?(path[root.size..root.size])
|
117
|
+
path = path.dup
|
118
|
+
path[0...root.size] = '.'
|
119
119
|
remove_dot ? (path[2..-1] || "") : path
|
120
120
|
else
|
121
121
|
path
|
@@ -141,7 +141,7 @@ class Thor
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
-
message = "Could not find #{file.inspect} in any of your source paths. "
|
144
|
+
message = "Could not find #{file.inspect} in any of your source paths. ".dup
|
145
145
|
|
146
146
|
unless self.class.source_root
|
147
147
|
message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. "
|
@@ -175,6 +175,7 @@ class Thor
|
|
175
175
|
|
176
176
|
# If the directory doesnt exist and we're not pretending
|
177
177
|
if !File.exist?(destination_root) && !pretend
|
178
|
+
require "fileutils"
|
178
179
|
FileUtils.mkdir_p(destination_root)
|
179
180
|
end
|
180
181
|
|
@@ -182,6 +183,7 @@ class Thor
|
|
182
183
|
# In pretend mode, just yield down to the block
|
183
184
|
block.arity == 1 ? yield(destination_root) : yield
|
184
185
|
else
|
186
|
+
require "fileutils"
|
185
187
|
FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
|
186
188
|
end
|
187
189
|
|
@@ -216,6 +218,7 @@ class Thor
|
|
216
218
|
shell.padding += 1 if verbose
|
217
219
|
|
218
220
|
contents = if is_uri
|
221
|
+
require "open-uri"
|
219
222
|
open(path, "Accept" => "application/x-thor-template", &:read)
|
220
223
|
else
|
221
224
|
open(path, &:read)
|
@@ -251,7 +254,22 @@ class Thor
|
|
251
254
|
|
252
255
|
say_status :run, desc, config.fetch(:verbose, true)
|
253
256
|
|
254
|
-
|
257
|
+
return if options[:pretend]
|
258
|
+
|
259
|
+
env_splat = [config[:env]] if config[:env]
|
260
|
+
|
261
|
+
if config[:capture]
|
262
|
+
require "open3"
|
263
|
+
result, status = Open3.capture2e(*env_splat, command.to_s)
|
264
|
+
success = status.success?
|
265
|
+
else
|
266
|
+
result = system(*env_splat, command.to_s)
|
267
|
+
success = result
|
268
|
+
end
|
269
|
+
|
270
|
+
abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?)
|
271
|
+
|
272
|
+
result
|
255
273
|
end
|
256
274
|
|
257
275
|
# Executes a ruby script (taking into account WIN32 platform quirks).
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "empty_directory"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Actions
|
@@ -58,6 +58,7 @@ class Thor
|
|
58
58
|
|
59
59
|
def invoke!
|
60
60
|
invoke_with_conflict_check do
|
61
|
+
require "fileutils"
|
61
62
|
FileUtils.mkdir_p(File.dirname(destination))
|
62
63
|
File.open(destination, "wb") { |f| f.write render }
|
63
64
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "create_file"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Actions
|
@@ -38,6 +38,7 @@ class Thor
|
|
38
38
|
|
39
39
|
def invoke!
|
40
40
|
invoke_with_conflict_check do
|
41
|
+
require "fileutils"
|
41
42
|
FileUtils.mkdir_p(File.dirname(destination))
|
42
43
|
# Create a symlink by default
|
43
44
|
config[:symbolic] = true if config[:symbolic].nil?
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "empty_directory"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Actions
|
@@ -56,7 +56,7 @@ class Thor
|
|
56
56
|
attr_reader :source
|
57
57
|
|
58
58
|
def initialize(base, source, destination = nil, config = {}, &block)
|
59
|
-
@source = File.expand_path(base.find_in_source_paths(source.to_s))
|
59
|
+
@source = File.expand_path(Dir[Util.escape_globs(base.find_in_source_paths(source.to_s))].first)
|
60
60
|
@block = block
|
61
61
|
super(base, destination, {:recursive => true}.merge(config))
|
62
62
|
end
|
@@ -96,22 +96,12 @@ class Thor
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
def files(lookup)
|
105
|
-
Dir[lookup]
|
106
|
-
end
|
107
|
-
else
|
108
|
-
def file_level_lookup(previous_lookup)
|
109
|
-
File.join(previous_lookup, "*")
|
110
|
-
end
|
99
|
+
def file_level_lookup(previous_lookup)
|
100
|
+
File.join(previous_lookup, "*")
|
101
|
+
end
|
111
102
|
|
112
|
-
|
113
|
-
|
114
|
-
end
|
103
|
+
def files(lookup)
|
104
|
+
Dir.glob(lookup, File::FNM_DOTMATCH)
|
115
105
|
end
|
116
106
|
end
|
117
107
|
end
|
@@ -48,12 +48,14 @@ class Thor
|
|
48
48
|
|
49
49
|
def invoke!
|
50
50
|
invoke_with_conflict_check do
|
51
|
+
require "fileutils"
|
51
52
|
::FileUtils.mkdir_p(destination)
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
55
56
|
def revoke!
|
56
57
|
say_status :remove, :red
|
58
|
+
require "fileutils"
|
57
59
|
::FileUtils.rm_rf(destination) if !pretend? && exists?
|
58
60
|
given_destination
|
59
61
|
end
|
@@ -112,11 +114,17 @@ class Thor
|
|
112
114
|
if exists?
|
113
115
|
on_conflict_behavior(&block)
|
114
116
|
else
|
115
|
-
say_status :create, :green
|
116
117
|
yield unless pretend?
|
118
|
+
say_status :create, :green
|
117
119
|
end
|
118
120
|
|
119
121
|
destination
|
122
|
+
rescue Errno::EISDIR, Errno::EEXIST
|
123
|
+
on_file_clash_behavior
|
124
|
+
end
|
125
|
+
|
126
|
+
def on_file_clash_behavior
|
127
|
+
say_status :file_clash, :red
|
120
128
|
end
|
121
129
|
|
122
130
|
# What to do when the destination file already exists.
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require "erb"
|
2
|
-
require "open-uri"
|
3
2
|
|
4
3
|
class Thor
|
5
4
|
module Actions
|
@@ -24,14 +23,14 @@ class Thor
|
|
24
23
|
destination = args.first || source
|
25
24
|
source = File.expand_path(find_in_source_paths(source.to_s))
|
26
25
|
|
27
|
-
create_file destination, nil, config do
|
26
|
+
resulting_destination = create_file destination, nil, config do
|
28
27
|
content = File.binread(source)
|
29
28
|
content = yield(content) if block
|
30
29
|
content
|
31
30
|
end
|
32
31
|
if config[:mode] == :preserve
|
33
32
|
mode = File.stat(source).mode
|
34
|
-
chmod(
|
33
|
+
chmod(resulting_destination, mode, config)
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
@@ -61,6 +60,9 @@ class Thor
|
|
61
60
|
# destination. If a block is given instead of destination, the content of
|
62
61
|
# the url is yielded and used as location.
|
63
62
|
#
|
63
|
+
# +get+ relies on open-uri, so passing application user input would provide
|
64
|
+
# a command injection attack vector.
|
65
|
+
#
|
64
66
|
# ==== Parameters
|
65
67
|
# source<String>:: the address of the given content.
|
66
68
|
# destination<String>:: the relative path to the destination root.
|
@@ -78,8 +80,13 @@ class Thor
|
|
78
80
|
config = args.last.is_a?(Hash) ? args.pop : {}
|
79
81
|
destination = args.first
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
+
render = if source =~ %r{^https?\://}
|
84
|
+
require "open-uri"
|
85
|
+
URI.send(:open, source) { |input| input.binmode.read }
|
86
|
+
else
|
87
|
+
source = File.expand_path(find_in_source_paths(source.to_s))
|
88
|
+
open(source) { |input| input.binmode.read }
|
89
|
+
end
|
83
90
|
|
84
91
|
destination ||= if block_given?
|
85
92
|
block.arity == 1 ? yield(render) : yield
|
@@ -113,7 +120,15 @@ class Thor
|
|
113
120
|
context = config.delete(:context) || instance_eval("binding")
|
114
121
|
|
115
122
|
create_file destination, nil, config do
|
116
|
-
|
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|
|
130
|
+
erb.filename = source
|
131
|
+
end.result(context)
|
117
132
|
content = yield(content) if block
|
118
133
|
content
|
119
134
|
end
|
@@ -134,7 +149,10 @@ class Thor
|
|
134
149
|
return unless behavior == :invoke
|
135
150
|
path = File.expand_path(path, destination_root)
|
136
151
|
say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
137
|
-
|
152
|
+
unless options[:pretend]
|
153
|
+
require "fileutils"
|
154
|
+
FileUtils.chmod_R(mode, path)
|
155
|
+
end
|
138
156
|
end
|
139
157
|
|
140
158
|
# Prepend text to a file. Since it depends on insert_into_file, it's reversible.
|
@@ -204,6 +222,29 @@ class Thor
|
|
204
222
|
insert_into_file(path, *(args << config), &block)
|
205
223
|
end
|
206
224
|
|
225
|
+
# Injects text right after the module definition. Since it depends on
|
226
|
+
# insert_into_file, it's reversible.
|
227
|
+
#
|
228
|
+
# ==== Parameters
|
229
|
+
# path<String>:: path of the file to be changed
|
230
|
+
# module_name<String|Class>:: the module to be manipulated
|
231
|
+
# data<String>:: the data to append to the class, can be also given as a block.
|
232
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
233
|
+
#
|
234
|
+
# ==== Examples
|
235
|
+
#
|
236
|
+
# inject_into_module "app/helpers/application_helper.rb", ApplicationHelper, " def help; 'help'; end\n"
|
237
|
+
#
|
238
|
+
# inject_into_module "app/helpers/application_helper.rb", ApplicationHelper do
|
239
|
+
# " def help; 'help'; end\n"
|
240
|
+
# end
|
241
|
+
#
|
242
|
+
def inject_into_module(path, module_name, *args, &block)
|
243
|
+
config = args.last.is_a?(Hash) ? args.pop : {}
|
244
|
+
config[:after] = /module #{module_name}\n|module #{module_name} .*\n/
|
245
|
+
insert_into_file(path, *(args << config), &block)
|
246
|
+
end
|
247
|
+
|
207
248
|
# Run a regular expression replacement on a file.
|
208
249
|
#
|
209
250
|
# ==== Parameters
|
@@ -269,7 +310,7 @@ class Thor
|
|
269
310
|
def comment_lines(path, flag, *args)
|
270
311
|
flag = flag.respond_to?(:source) ? flag.source : flag
|
271
312
|
|
272
|
-
gsub_file(path, /^(\s*)([
|
313
|
+
gsub_file(path, /^(\s*)([^#\n]*#{flag})/, '\1# \2', *args)
|
273
314
|
end
|
274
315
|
|
275
316
|
# Removes a file at the given location.
|
@@ -288,7 +329,10 @@ class Thor
|
|
288
329
|
path = File.expand_path(path, destination_root)
|
289
330
|
|
290
331
|
say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
291
|
-
|
332
|
+
if !options[:pretend] && File.exist?(path)
|
333
|
+
require "fileutils"
|
334
|
+
::FileUtils.rm_rf(path)
|
335
|
+
end
|
292
336
|
end
|
293
337
|
alias_method :remove_dir, :remove_file
|
294
338
|
|
@@ -305,8 +349,10 @@ class Thor
|
|
305
349
|
with_output_buffer { yield(*args) }
|
306
350
|
end
|
307
351
|
|
308
|
-
def with_output_buffer(buf = "") #:nodoc:
|
309
|
-
|
352
|
+
def with_output_buffer(buf = "".dup) #:nodoc:
|
353
|
+
raise ArgumentError, "Buffer can not be a frozen object" if buf.frozen?
|
354
|
+
old_buffer = output_buffer
|
355
|
+
self.output_buffer = buf
|
310
356
|
yield
|
311
357
|
output_buffer
|
312
358
|
ensure
|
@@ -319,7 +365,7 @@ class Thor
|
|
319
365
|
def set_eoutvar(compiler, eoutvar = "_erbout")
|
320
366
|
compiler.put_cmd = "#{eoutvar}.concat"
|
321
367
|
compiler.insert_cmd = "#{eoutvar}.concat"
|
322
|
-
compiler.pre_cmd = ["#{eoutvar} = ''"]
|
368
|
+
compiler.pre_cmd = ["#{eoutvar} = ''.dup"]
|
323
369
|
compiler.post_cmd = [eoutvar]
|
324
370
|
end
|
325
371
|
end
|