Module: Thor::Actions

Defined in:
lib/thor/actions.rb,
lib/thor/actions/directory.rb,
lib/thor/actions/create_file.rb,
lib/thor/actions/create_link.rb,
lib/thor/actions/empty_directory.rb,
lib/thor/actions/inject_into_file.rb,
lib/thor/actions/file_manipulation.rb

Defined Under Namespace

Modules: ClassMethods Classes: CapturableERB, CreateFile, CreateLink, Directory, EmptyDirectory, InjectIntoFile

Constant Summary collapse

WARNINGS =

Injects the given content into a file. Different from gsub_file, this method is reversible.

Parameters

destination<String>

Relative path to the destination root

data<String>

Data to add to the file. Can be given as a block.

config<Hash>

give :verbose => false to not log the status and the flag for injection (:after or :before) or :force => true for insert two or more times the same content.

Examples

insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"

insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
  gems = ask "Which gems would you like to add?"
  gems.split(" ").map{ |gem| "  config.gem :#{gem}" }.join("\n")
end
{unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#behaviorObject

Returns the value of attribute behavior.



10
11
12
# File 'lib/thor/actions.rb', line 10

def behavior
  @behavior
end

Class Method Details

.included(base) ⇒ Object

:nodoc:



12
13
14
15
# File 'lib/thor/actions.rb', line 12

def self.included(base) #:nodoc:
  super(base)
  base.extend ClassMethods
end

Instance Method Details

#action(instance) ⇒ Object

Wraps an action object and call it accordingly to the thor class behavior.



89
90
91
92
93
94
95
# File 'lib/thor/actions.rb', line 89

def action(instance) #:nodoc:
  if behavior == :revoke
    instance.revoke!
  else
    instance.invoke!
  end
end

#append_to_file(path, *args, &block) ⇒ Object Also known as: append_file

Append text to a file. Since it depends on insert_into_file, it’s reversible.

Parameters

path<String>

path of the file to be changed

data<String>

the data to append to the file, can be also given as a block.

config<Hash>

give :verbose => false to not log the status.

Example

append_to_file 'config/environments/test.rb', 'config.gem "rspec"'

append_to_file 'config/environments/test.rb' do
  'config.gem "rspec"'
end


193
194
195
196
197
# File 'lib/thor/actions/file_manipulation.rb', line 193

def append_to_file(path, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config[:before] = /\z/
  insert_into_file(path, *(args << config), &block)
end

#apply(path, config = {}) ⇒ Object

Loads an external file and execute it in the instance binding.

Parameters

path<String>

The path to the file to execute. Can be a web address or a relative path from the source root.

Examples

apply "http://gist.github.com/103208"

apply "recipes/jquery.rb"


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/thor/actions.rb', line 216

def apply(path, config = {})
  verbose = config.fetch(:verbose, true)
  is_uri  = path =~ %r{^https?\://}
  path    = find_in_source_paths(path) unless is_uri

  say_status :apply, path, verbose
  shell.padding += 1 if verbose

  contents = if is_uri
    require "open-uri"
    URI.open(path, "Accept" => "application/x-thor-template", &:read)
  else
    File.open(path, &:read)
  end

  instance_eval(contents, path)
  shell.padding -= 1 if verbose
end

#chmod(path, mode, config = {}) ⇒ Object

Changes the mode of the given file or directory.

Parameters

mode<Integer>

the file mode

path<String>

the name of the file to change mode

config<Hash>

give :verbose => false to not log the status.

Example

chmod "script/server", 0755


146
147
148
149
150
151
152
153
154
# File 'lib/thor/actions/file_manipulation.rb', line 146

def chmod(path, mode, config = {})
  return unless behavior == :invoke
  path = File.expand_path(path, destination_root)
  say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
  unless options[:pretend]
    require "fileutils"
    FileUtils.chmod_R(mode, path)
  end
end

#comment_lines(path, flag, *args) ⇒ Object

Comment all lines matching a given regex. It will leave the space which existed before the beginning of the line in tact and will insert a single space after the comment hash.

Parameters

path<String>

path of the file to be changed

flag<Regexp|String>

the regexp or string used to decide which lines to comment

config<Hash>

give :verbose => false to not log the status.

Example

comment_lines 'config/initializers/session_store.rb', /cookie_store/


310
311
312
313
314
# File 'lib/thor/actions/file_manipulation.rb', line 310

def comment_lines(path, flag, *args)
  flag = flag.respond_to?(:source) ? flag.source : flag

  gsub_file(path, /^(\s*)([^#\n]*#{flag})/, '\1# \2', *args)
end

#copy_file(source, *args, &block) ⇒ Object

Examples

copy_file "README", "doc/README"

copy_file "doc/README"


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/thor/actions/file_manipulation.rb', line 21

def copy_file(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  source = File.expand_path(find_in_source_paths(source.to_s))

  resulting_destination = create_file destination, nil, config do
    content = File.binread(source)
    content = yield(content) if block
    content
  end
  if config[:mode] == :preserve
    mode = File.stat(source).mode
    chmod(resulting_destination, mode, config)
  end
end

#create_file(destination, *args, &block) ⇒ Object Also known as: add_file

Create a new file relative to the destination root with the given data, which is the return value of a block or a data string.

Parameters

destination<String>

the relative path to the destination root.

data<String|NilClass>

the data to append to the file.

config<Hash>

give :verbose => false to not log the status.

Examples

create_file "lib/fun_party.rb" do
  hostname = ask("What is the virtual hostname I should use?")
  "vhost.name = #{hostname}"
end

create_file "config/apache.conf", "your apache config"


22
23
24
25
26
# File 'lib/thor/actions/create_file.rb', line 22

def create_file(destination, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  data = args.first
  action CreateFile.new(self, destination, block || data.to_s, config)
end

Create a new file relative to the destination root from the given source.

Parameters

destination<String>

the relative path to the destination root.

source<String|NilClass>

the relative path to the source root.

config<Hash>

give :verbose => false to not log the status.

give :symbolic => false for hard link.

Examples

create_link "config/apache.conf", "/etc/apache.conf"


17
18
19
20
21
# File 'lib/thor/actions/create_link.rb', line 17

def create_link(destination, *args)
  config = args.last.is_a?(Hash) ? args.pop : {}
  source = args.first
  action CreateLink.new(self, destination, source, config)
end

#destination_rootObject

Returns the root for this thor class (also aliased as destination root).



99
100
101
# File 'lib/thor/actions.rb', line 99

def destination_root
  @destination_stack.last
end

#destination_root=(root) ⇒ Object

Sets the root for this thor class. Relatives path are added to the directory where the script was invoked and expanded.



106
107
108
109
# File 'lib/thor/actions.rb', line 106

def destination_root=(root)
  @destination_stack ||= []
  @destination_stack[0] = File.expand_path(root || "")
end

#directory(source, *args, &block) ⇒ Object

Copies recursively the files from source directory to root directory. If any of the files finishes with .tt, it’s considered to be a template and is placed in the destination without the extension .tt. If any empty directory is found, it’s copied and all .empty_directory files are ignored. If any file name is wrapped within % signs, the text within the % signs will be executed as a method and replaced with the returned value. Let’s suppose a doc directory with the following files:

doc/
  components/.empty_directory
  README
  rdoc.rb.tt
  %app_name%.rb

When invoked as:

directory "doc"

It will create a doc directory in the destination with the following files (assuming that the ‘app_name` method returns the value “blog”):

doc/
  components/
  README
  rdoc.rb
  blog.rb

Encoded path note: Since Thor internals use Object#respond_to? to check if it can expand %something%, this ‘something` should be a public method in the class calling #directory. If a method is private, Thor stack raises PrivateMethodEncodedError.

Parameters

source<String>

the relative path to the source root.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status. If :recursive => false, does not look for paths recursively. If :mode => :preserve, preserve the file mode from the source. If :exclude_pattern => /regexp/, prevents copying files that match that regexp.

Examples

directory "doc"
directory "doc", "docs", :recursive => false


49
50
51
52
53
# File 'lib/thor/actions/directory.rb', line 49

def directory(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  action Directory.new(self, source, destination || source, config, &block)
end

#empty_directory(destination, config = {}) ⇒ Object

Creates an empty directory.

Parameters

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

empty_directory "doc"


13
14
15
# File 'lib/thor/actions/empty_directory.rb', line 13

def empty_directory(destination, config = {})
  action EmptyDirectory.new(self, destination, config)
end

#find_in_source_paths(file) ⇒ Object

Receives a file or directory and search for it in the source paths.

Raises:



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/thor/actions.rb', line 133

def find_in_source_paths(file)
  possible_files = [file, file + TEMPLATE_EXTNAME]
  relative_root = relative_to_original_destination_root(destination_root, false)

  source_paths.each do |source|
    possible_files.each do |f|
      source_file = File.expand_path(f, File.join(source, relative_root))
      return source_file if File.exist?(source_file)
    end
  end

  message = "Could not find #{file.inspect} in any of your source paths. ".dup

  unless self.class.source_root
    message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. "
  end

  message << if source_paths.empty?
               "Currently you have no source paths."
             else
               "Your current source paths are: \n#{source_paths.join("\n")}"
             end

  raise Error, message
end

#get(source, *args, &block) ⇒ Object

Gets the content at the given address and places it at the given relative destination. If a block is given instead of destination, the content of the url is yielded and used as location.

get relies on open-uri, so passing application user input would provide a command injection attack vector.

Parameters

source<String>

the address of the given content.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status, and :http_headers => <Hash> to add headers to an http request.

Examples

get "http://gist.github.com/103208", "doc/README"

get "http://gist.github.com/103208", "doc/README", :http_headers => {"Content-Type" => "application/json"}

get "http://gist.github.com/103208" do |content|
  content.split("\n").first
end


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/thor/actions/file_manipulation.rb', line 82

def get(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first

  render = if source =~ %r{^https?\://}
    require "open-uri"
    URI.send(:open, source, config.fetch(:http_headers, {})) { |input| input.binmode.read }
  else
    source = File.expand_path(find_in_source_paths(source.to_s))
    File.open(source) { |input| input.binmode.read }
  end

  destination ||= if block_given?
    block.arity == 1 ? yield(render) : yield
  else
    File.basename(source)
  end

  create_file destination, render, config
end

#gsub_file(path, flag, *args, &block) ⇒ Object

Run a regular expression replacement on a file.

Parameters

path<String>

path of the file to be changed

flag<Regexp|String>

the regexp or string to be replaced

replacement<String>

the replacement, can be also given as a block

config<Hash>

give :verbose => false to not log the status, and :force => true, to force the replacement regardless of runner behavior.

Example

gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'

gsub_file 'README', /rake/, :green do |match|
  match << " no more. Use thor!"
end


263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/thor/actions/file_manipulation.rb', line 263

def gsub_file(path, flag, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}

  return unless behavior == :invoke || config.fetch(:force, false)

  path = File.expand_path(path, destination_root)
  say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)

  unless options[:pretend]
    content = File.binread(path)
    content.gsub!(flag, *args, &block)
    File.open(path, "wb") { |file| file.write(content) }
  end
end

#in_rootObject

Goes to the root and execute the given block.



200
201
202
# File 'lib/thor/actions.rb', line 200

def in_root
  inside(@destination_stack.first) { yield }
end

#initialize(args = [], options = {}, config = {}) ⇒ Object

Extends initializer to add more configuration options.

Configuration

behavior<Symbol>

The actions default behavior. Can be :invoke or :revoke. It also accepts :force, :skip and :pretend to set the behavior and the respective option.

destination_root<String>

The root directory needed for some actions.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/thor/actions.rb', line 72

def initialize(args = [], options = {}, config = {})
  self.behavior = case config[:behavior].to_s
  when "force", "skip"
    _cleanup_options_and_set(options, config[:behavior])
    :invoke
  when "revoke"
    :revoke
  else
    :invoke
  end

  super
  self.destination_root = config[:destination_root]
end

#inject_into_class(path, klass, *args, &block) ⇒ Object

Injects text right after the class definition. Since it depends on insert_into_file, it’s reversible.

Parameters

path<String>

path of the file to be changed

klass<String|Class>

the class to be manipulated

data<String>

the data to append to the class, can be also given as a block.

config<Hash>

give :verbose => false to not log the status.

Examples

inject_into_class "app/controllers/application_controller.rb", "ApplicationController", "  filter_parameter :password\n"

inject_into_class "app/controllers/application_controller.rb", "ApplicationController" do
  "  filter_parameter :password\n"
end


217
218
219
220
221
# File 'lib/thor/actions/file_manipulation.rb', line 217

def inject_into_class(path, klass, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config[:after] = /class #{klass}\n|class #{klass} .*\n/
  insert_into_file(path, *(args << config), &block)
end

#inject_into_module(path, module_name, *args, &block) ⇒ Object

Injects text right after the module definition. Since it depends on insert_into_file, it’s reversible.

Parameters

path<String>

path of the file to be changed

module_name<String|Class>

the module to be manipulated

data<String>

the data to append to the class, can be also given as a block.

config<Hash>

give :verbose => false to not log the status.

Examples

inject_into_module "app/helpers/application_helper.rb", "ApplicationHelper", "  def help; 'help'; end\n"

inject_into_module "app/helpers/application_helper.rb", "ApplicationHelper" do
  "  def help; 'help'; end\n"
end


240
241
242
243
244
# File 'lib/thor/actions/file_manipulation.rb', line 240

def inject_into_module(path, module_name, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config[:after] = /module #{module_name}\n|module #{module_name} .*\n/
  insert_into_file(path, *(args << config), &block)
end

#insert_into_file(destination, *args, &block) ⇒ Object Also known as: inject_into_file



26
27
28
29
30
31
32
33
# File 'lib/thor/actions/inject_into_file.rb', line 26

def insert_into_file(destination, *args, &block)
  data = block_given? ? block : args.shift

  config = args.shift || {}
  config[:after] = /\z/ unless config.key?(:before) || config.key?(:after)

  action InjectIntoFile.new(self, destination, data, config)
end

#inside(dir = "", config = {}, &block) ⇒ Object

Do something in the root or on a provided subfolder. If a relative path is given it’s referenced from the current root. The full path is yielded to the block you provide. The path is set back to the previous path when the method exits.

Returns the value yielded by the block.

Parameters

dir<String>

the directory to move to.

config<Hash>

give :verbose => true to log and use padding.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/thor/actions.rb', line 170

def inside(dir = "", config = {}, &block)
  verbose = config.fetch(:verbose, false)
  pretend = options[:pretend]

  say_status :inside, dir, verbose
  shell.padding += 1 if verbose
  @destination_stack.push File.expand_path(dir, destination_root)

  # If the directory doesn't exist and we're not pretending
  if !File.exist?(destination_root) && !pretend
    require "fileutils"
    FileUtils.mkdir_p(destination_root)
  end

  result = nil
  if pretend
    # In pretend mode, just yield down to the block
    result = block.arity == 1 ? yield(destination_root) : yield
  else
    require "fileutils"
    FileUtils.cd(destination_root) { result = block.arity == 1 ? yield(destination_root) : yield }
  end

  @destination_stack.pop
  shell.padding -= 1 if verbose
  result
end

Links the file from the relative source to the relative destination. If the destination is not given it’s assumed to be equal to the source.

Parameters

source<String>

the relative path to the source root.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

link_file "README", "doc/README"

link_file "doc/README"


51
52
53
54
55
56
57
# File 'lib/thor/actions/file_manipulation.rb', line 51

def link_file(source, *args)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  source = File.expand_path(find_in_source_paths(source.to_s))

  create_link destination, source, config
end

#prepend_to_file(path, *args, &block) ⇒ Object Also known as: prepend_file

Prepend text to a file. Since it depends on insert_into_file, it’s reversible.

Parameters

path<String>

path of the file to be changed

data<String>

the data to prepend to the file, can be also given as a block.

config<Hash>

give :verbose => false to not log the status.

Example

prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"'

prepend_to_file 'config/environments/test.rb' do
  'config.gem "rspec"'
end


171
172
173
174
175
# File 'lib/thor/actions/file_manipulation.rb', line 171

def prepend_to_file(path, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config[:after] = /\A/
  insert_into_file(path, *(args << config), &block)
end

#relative_to_original_destination_root(path, remove_dot = true) ⇒ Object

Returns the given path relative to the absolute root (ie, root where the script started).



114
115
116
117
118
119
120
121
122
123
# File 'lib/thor/actions.rb', line 114

def relative_to_original_destination_root(path, remove_dot = true)
  root = @destination_stack[0]
  if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ""].include?(path[root.size..root.size])
    path = path.dup
    path[0...root.size] = "."
    remove_dot ? (path[2..-1] || "") : path
  else
    path
  end
end

#remove_file(path, config = {}) ⇒ Object Also known as: remove_dir

Removes a file at the given location.

Parameters

path<String>

path of the file to be changed

config<Hash>

give :verbose => false to not log the status.

Example

remove_file 'README'
remove_file 'app/controllers/application_controller.rb'


327
328
329
330
331
332
333
334
335
336
# File 'lib/thor/actions/file_manipulation.rb', line 327

def remove_file(path, config = {})
  return unless behavior == :invoke
  path = File.expand_path(path, destination_root)

  say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
  if !options[:pretend] && (File.exist?(path) || File.symlink?(path))
    require "fileutils"
    ::FileUtils.rm_rf(path)
  end
end

#run(command, config = {}) ⇒ Object

Executes a command returning the contents of the command.

Parameters

command<String>

the command to be executed.

config<Hash>

give :verbose => false to not log the status, :capture => true to hide to output. Specify :with to append an executable to command execution.

Example

inside('vendor') do
  run('ln -s ~/edge rails')
end


248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/thor/actions.rb', line 248

def run(command, config = {})
  return unless behavior == :invoke

  destination = relative_to_original_destination_root(destination_root, false)
  desc = "#{command} from #{destination.inspect}"

  if config[:with]
    desc = "#{File.basename(config[:with].to_s)} #{desc}"
    command = "#{config[:with]} #{command}"
  end

  say_status :run, desc, config.fetch(:verbose, true)

  return if options[:pretend]

  env_splat = [config[:env]] if config[:env]

  if config[:capture]
    require "open3"
    result, status = Open3.capture2e(*env_splat, command.to_s)
    success = status.success?
  else
    result = system(*env_splat, command.to_s)
    success = result
  end

  abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?)

  result
end

#run_ruby_script(command, config = {}) ⇒ Object

Executes a ruby script (taking into account WIN32 platform quirks).

Parameters

command<String>

the command to be executed.

config<Hash>

give :verbose => false to not log the status.



285
286
287
288
# File 'lib/thor/actions.rb', line 285

def run_ruby_script(command, config = {})
  return unless behavior == :invoke
  run command, config.merge(with: Thor::Util.ruby_command)
end

#source_pathsObject

Holds source paths in instance so they can be manipulated.



127
128
129
# File 'lib/thor/actions.rb', line 127

def source_paths
  @source_paths ||= self.class.source_paths_for_search
end

#template(source, *args, &block) ⇒ Object

Gets an ERB template at the relative source, executes it and makes a copy at the relative destination. If the destination is not given it’s assumed to be equal to the source removing .tt from the filename.

Parameters

source<String>

the relative path to the source root.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

template "README", "doc/README"

template "doc/README"


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/thor/actions/file_manipulation.rb', line 118

def template(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "")

  source  = File.expand_path(find_in_source_paths(source.to_s))
  context = config.delete(:context) || instance_eval("binding")

  create_file destination, nil, config do
    capturable_erb = CapturableERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer")
    content = capturable_erb.tap do |erb|
      erb.filename = source
    end.result(context)
    content = yield(content) if block
    content
  end
end

#thor(command, *args) ⇒ Object

Run a thor command. A hash of options can be given and it’s converted to switches.

Parameters

command<String>

the command to be invoked

args<Array>

arguments to the command

config<Hash>

give :verbose => false to not log the status, :capture => true to hide to output. Other options are given as parameter to Thor.

Examples

thor :install, "http://gist.github.com/103208"
#=> thor install http://gist.github.com/103208

thor :list, :all => true, :substring => 'rails'
#=> thor list --all --substring=rails


308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/thor/actions.rb', line 308

def thor(command, *args)
  config  = args.last.is_a?(Hash) ? args.pop : {}
  verbose = config.key?(:verbose) ? config.delete(:verbose) : true
  pretend = config.key?(:pretend) ? config.delete(:pretend) : false
  capture = config.key?(:capture) ? config.delete(:capture) : false

  args.unshift(command)
  args.push Thor::Options.to_switches(config)
  command = args.join(" ").strip

  run command, with: :thor, verbose: verbose, pretend: pretend, capture: capture
end

#uncomment_lines(path, flag, *args) ⇒ Object

Uncomment all lines matching a given regex. It will leave the space which existed before the comment hash in tact but will remove any spacing between the comment hash and the beginning of the line.

Parameters

path<String>

path of the file to be changed

flag<Regexp|String>

the regexp or string used to decide which lines to uncomment

config<Hash>

give :verbose => false to not log the status.

Example

uncomment_lines 'config/initializers/session_store.rb', /active_record/


291
292
293
294
295
# File 'lib/thor/actions/file_manipulation.rb', line 291

def uncomment_lines(path, flag, *args)
  flag = flag.respond_to?(:source) ? flag.source : flag

  gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args)
end