thor 0.19.4 → 1.0.1

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
- SHA1:
3
- metadata.gz: 5fd71663b46487af27e6f0b6a0206f5140d0d196
4
- data.tar.gz: d67e482d506417552648630f8a7bc4b1886fc06c
2
+ SHA256:
3
+ metadata.gz: 2c23d1fdac6ea485b4cbfe91abad33a46e03c00baf2e7e0068c9f8d4ce41e607
4
+ data.tar.gz: 368fe9c87e8a426eaf38278acaba2b7a3171c96c99298ca5e2eddf3b1b466ff6
5
5
  SHA512:
6
- metadata.gz: f15702a93adea15d623fd708962193b42bc38bf2b86dff66fe43f1078971fc38f5dedff0d6a78bd1bf9241315e83ede0c216ffedea8c452c55b57aff17d0d051
7
- data.tar.gz: 1e7093648f0913e9c7e32c796ea795e819ca59331588885059bfcae9867172ea50144033c342d22c4d5c8ab628f135821bb9a0ab8645f7784c396482370b98c0
6
+ metadata.gz: 6f2e3b52a657bc5318fed8a5e3f15368d734f62e7b8039a296add626ff6bdc415e6c8a85cea31bdd591a629960854f0d34e47ef81161c63acf4ef140457a7bdb
7
+ data.tar.gz: 8086cd52e16a12fb63dad7df484f0c079feed33d7aa6078d8683f895ddcd92da21be58d26a3b33f89e44d822ae9b01e73ef36672aa77a7befcfa3198523df253
@@ -1,3 +1,60 @@
1
+ # 1.0.1
2
+ * Fix thor when `thor/base` and `thor/group` are required without `thor.rb`.
3
+ * Handle relative source path in `create_link`.
4
+
5
+ # 1.0.0
6
+ * Drop support to Ruby 1.8 and 1.9.
7
+ * Deprecate relying on default `exit_on_failure?`.
8
+ In preparation to make Thor commands exit when there is a failure we are deprecating
9
+ defining a command without defining what behavior is expected when there is a failure.
10
+
11
+ To fix the deprecation you need to define a class method called `exit_on_failure?` returning
12
+
13
+ `false` if you want the current behavior or `true` if you want the new behavior.
14
+ * Deprecate defining an option with the default value using a different type as defined in the option.
15
+ * Allow options to be repeatable. See #674.
16
+
17
+ # 0.20.3
18
+ * Support old versions of `did_you_mean`.
19
+
20
+ # 0.20.2
21
+ * Fix `did_you_mean` support.
22
+
23
+ # 0.20.1
24
+ * Support new versions of ERB.
25
+ * 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!`
26
+ * Add `did_you_mean` support.
27
+
28
+ ## 0.20.0
29
+ * Add `check_default_type!` to check if the default value of an option matches the defined type.
30
+ It removes the warning on usage and gives the command authors the possibility to check for programming errors.
31
+
32
+ * Add `disable_required_check!` to disable check for required options in some commands.
33
+ It is a substitute of `disable_class_options` that was not working as intended.
34
+
35
+ * Add `inject_into_module`.
36
+
37
+ ## 0.19.4, release 2016-11-28
38
+ * Rename `Thor::Base#thor_reserved_word?` to `#is_thor_reserved_word?`
39
+
40
+ ## 0.19.3, release 2016-11-27
41
+ * Output a warning instead of raising an exception when a default option value doesn't match its specified type
42
+
43
+ ## 0.19.2, release 2016-11-26
44
+ * Fix bug with handling of colors passed to `ask` (and methods like `yes?` and `no?` which it underpins)
45
+ * Allow numeric arguments to be negative
46
+ * 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
47
+ * Add `Thor::Shell::Basic#indent` method for intending output
48
+ * Fix `remove_command` for an inherited command (see #451)
49
+ * Allow hash arguments to only have each key provided once (see #455)
50
+ * Allow commands to disable class options, for instance for "help" commands (see #363)
51
+ * Do not generate a negative option (`--no-no-foo`) for already negative boolean options (`--no-foo`)
52
+ * Improve compatibility of `Thor::CoreExt::HashWithIndifferentAccess` with Ruby standard library `Hash`
53
+ * Allow specifying a custom binding for template evaluation (e.g. `#key?` and `#fetch`)
54
+ * Fix support for subcommand-specific "help"s
55
+ * Use a string buffer when handling ERB for Ruby 2.3 compatibility
56
+ * Update dependencies
57
+
1
58
  ## 0.19.1, release 2014-03-24
2
59
  * Fix `say` non-String break regression
3
60
 
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
  ------------
@@ -1,5 +1,5 @@
1
1
  require "set"
2
- require "thor/base"
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,31 @@ 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
+
329
347
  protected
330
348
 
331
349
  def stop_on_unknown_option #:nodoc:
332
350
  @stop_on_unknown_option ||= Set.new
333
351
  end
334
352
 
353
+ # help command has the required check disabled by default.
354
+ def disable_required_check #:nodoc:
355
+ @disable_required_check ||= Set.new([:help])
356
+ end
357
+
335
358
  # The method responsible for dispatching given the args.
336
359
  def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
337
360
  meth ||= retrieve_command_name(given_args)
@@ -375,7 +398,10 @@ class Thor
375
398
  # the namespace should be displayed as arguments.
376
399
  #
377
400
  def banner(command, namespace = nil, subcommand = false)
378
- "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
401
+ $thor_runner ||= false
402
+ command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage|
403
+ "#{basename} #{formatted_usage}"
404
+ end.join("\n")
379
405
  end
380
406
 
381
407
  def baseclass #:nodoc:
@@ -390,12 +416,12 @@ class Thor
390
416
  @usage ||= nil
391
417
  @desc ||= nil
392
418
  @long_desc ||= nil
393
- @disable_class_options ||= nil
419
+ @hide ||= nil
394
420
 
395
421
  if @usage && @desc
396
422
  base_class = @hide ? Thor::HiddenCommand : Thor::Command
397
- commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options, @disable_class_options)
398
- @usage, @desc, @long_desc, @method_options, @hide, @disable_class_options = nil
423
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
424
+ @usage, @desc, @long_desc, @method_options, @hide = nil
399
425
  true
400
426
  elsif all_commands[meth] || meth == "method_missing"
401
427
  true
@@ -477,7 +503,6 @@ class Thor
477
503
  map HELP_MAPPINGS => :help
478
504
 
479
505
  desc "help [COMMAND]", "Describe available commands or one specific command"
480
- disable_class_options
481
506
  def help(command = nil, subcommand = false)
482
507
  if command
483
508
  if self.class.subcommands.include? command
@@ -1,18 +1,16 @@
1
- require "fileutils"
2
- require "uri"
3
- require "thor/core_ext/io_binary_read"
4
- require "thor/actions/create_file"
5
- require "thor/actions/create_link"
6
- require "thor/actions/directory"
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
- path = path.dup
118
- if path.gsub!(@destination_stack[0], ".")
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
- !options[:pretend] && config[:capture] ? `#{command}` : system(command.to_s)
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
- require "thor/actions/empty_directory"
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
- require "thor/actions/create_file"
1
+ require_relative "create_file"
2
2
 
3
3
  class Thor
4
4
  module Actions
@@ -33,11 +33,13 @@ class Thor
33
33
  # Boolean:: true if it is identical, false otherwise.
34
34
  #
35
35
  def identical?
36
- exists? && File.identical?(render, destination)
36
+ source = File.expand_path(render, File.dirname(destination))
37
+ exists? && File.identical?(source, destination)
37
38
  end
38
39
 
39
40
  def invoke!
40
41
  invoke_with_conflict_check do
42
+ require "fileutils"
41
43
  FileUtils.mkdir_p(File.dirname(destination))
42
44
  # Create a symlink by default
43
45
  config[:symbolic] = true if config[:symbolic].nil?
@@ -1,4 +1,4 @@
1
- require "thor/actions/empty_directory"
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
- if RUBY_VERSION < "2.0"
100
- def file_level_lookup(previous_lookup)
101
- File.join(previous_lookup, "{*,.[a-z]*}")
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
- def files(lookup)
113
- Dir.glob(lookup, File::FNM_DOTMATCH)
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(destination, mode, config)
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
- source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ %r{^https?\://}
82
- render = open(source) { |input| input.binmode.read }
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
- content = CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
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
- FileUtils.chmod_R(mode, path) unless options[:pretend]
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*)([^#|\n]*#{flag})/, '\1# \2', *args)
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
- ::FileUtils.rm_rf(path) if !options[:pretend] && File.exist?(path)
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
- self.output_buffer, old_buffer = buf, output_buffer
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