Skip to content

Commit 4f92183

Browse files
committed
feat: add gsub_file! method
1 parent e1333f5 commit 4f92183

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

lib/thor/actions/file_manipulation.rb

+26
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,32 @@ def inject_into_module(path, module_name, *args, &block)
243243
insert_into_file(path, *(args << config), &block)
244244
end
245245

246+
# Run a regular expression replacement on a file, raising an error if the
247+
# contents of the file are not changed.
248+
#
249+
# ==== Parameters
250+
# path<String>:: path of the file to be changed
251+
# flag<Regexp|String>:: the regexp or string to be replaced
252+
# replacement<String>:: the replacement, can be also given as a block
253+
# config<Hash>:: give :verbose => false to not log the status, and
254+
# :force => true, to force the replacement regardless of runner behavior.
255+
#
256+
# ==== Example
257+
#
258+
# gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
259+
#
260+
# gsub_file 'README', /rake/, :green do |match|
261+
# match << " no more. Use thor!"
262+
# end
263+
#
264+
def gsub_file!(path, flag, *args, &block)
265+
config = args.last.is_a?(Hash) ? args.pop : {}
266+
267+
config[:error_on_no_change] = true
268+
269+
gsub_file(path, flag, *args, config, &block)
270+
end
271+
246272
# Run a regular expression replacement on a file.
247273
#
248274
# ==== Parameters

spec/actions/file_manipulation_spec.rb

+98
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,104 @@ def file
293293
end
294294
end
295295

296+
describe "#gsub_file!" do
297+
context "with invoke behavior" do
298+
it "replaces the content in the file" do
299+
action :gsub_file!, "doc/README", "__start__", "START"
300+
expect(File.binread(file)).to eq("START\nREADME\n__end__\n")
301+
end
302+
303+
it "does not replace if pretending" do
304+
runner(pretend: true)
305+
action :gsub_file!, "doc/README", "__start__", "START"
306+
expect(File.binread(file)).to eq("__start__\nREADME\n__end__\n")
307+
end
308+
309+
it "accepts a block" do
310+
action(:gsub_file!, "doc/README", "__start__") { |match| match.gsub("__", "").upcase }
311+
expect(File.binread(file)).to eq("START\nREADME\n__end__\n")
312+
end
313+
314+
it "logs status" do
315+
expect(action(:gsub_file!, "doc/README", "__start__", "START")).to eq(" gsub doc/README\n")
316+
end
317+
318+
it "does not log status if required" do
319+
expect(action(:gsub_file!, file, "__", verbose: false) { |match| match * 2 }).to be_empty
320+
end
321+
322+
it "cares if the file contents did not change" do
323+
expect do
324+
action :gsub_file!, "doc/README", "___start___", "START"
325+
end.to raise_error(Thor::Error)
326+
327+
expect(File.binread(file)).to eq("__start__\nREADME\n__end__\n")
328+
end
329+
end
330+
331+
context "with revoke behavior" do
332+
context "and no force option" do
333+
it "does not replace the content in the file" do
334+
runner({}, :revoke)
335+
action :gsub_file!, "doc/README", "__start__", "START"
336+
expect(File.binread(file)).to eq("__start__\nREADME\n__end__\n")
337+
end
338+
339+
it "does not replace if pretending" do
340+
runner({pretend: true}, :revoke)
341+
action :gsub_file!, "doc/README", "__start__", "START"
342+
expect(File.binread(file)).to eq("__start__\nREADME\n__end__\n")
343+
end
344+
345+
it "does not replace the content in the file when given a block" do
346+
runner({}, :revoke)
347+
action(:gsub_file!, "doc/README", "__start__") { |match| match.gsub("__", "").upcase }
348+
expect(File.binread(file)).to eq("__start__\nREADME\n__end__\n")
349+
end
350+
351+
it "does not log status" do
352+
runner({}, :revoke)
353+
expect(action(:gsub_file!, "doc/README", "__start__", "START")).to be_empty
354+
end
355+
356+
it "does not log status if required" do
357+
runner({}, :revoke)
358+
expect(action(:gsub_file!, file, "__", verbose: false) { |match| match * 2 }).to be_empty
359+
end
360+
end
361+
362+
context "and force option" do
363+
it "replaces the content in the file" do
364+
runner({}, :revoke)
365+
action :gsub_file!, "doc/README", "__start__", "START", force: true
366+
expect(File.binread(file)).to eq("START\nREADME\n__end__\n")
367+
end
368+
369+
it "does not replace if pretending" do
370+
runner({pretend: true}, :revoke)
371+
action :gsub_file!, "doc/README", "__start__", "START", force: true
372+
expect(File.binread(file)).to eq("__start__\nREADME\n__end__\n")
373+
end
374+
375+
it "replaces the content in the file when given a block" do
376+
runner({}, :revoke)
377+
action(:gsub_file!, "doc/README", "__start__", force: true) { |match| match.gsub("__", "").upcase }
378+
expect(File.binread(file)).to eq("START\nREADME\n__end__\n")
379+
end
380+
381+
it "logs status" do
382+
runner({}, :revoke)
383+
expect(action(:gsub_file!, "doc/README", "__start__", "START", force: true)).to eq(" gsub doc/README\n")
384+
end
385+
386+
it "does not log status if required" do
387+
runner({}, :revoke)
388+
expect(action(:gsub_file!, file, "__", verbose: false, force: true) { |match| match * 2 }).to be_empty
389+
end
390+
end
391+
end
392+
end
393+
296394
describe "#gsub_file" do
297395
context "with invoke behavior" do
298396
it "replaces the content in the file" do

0 commit comments

Comments
 (0)