Class: FileOverwrite

Inherits:
Object
  • Object
show all
Includes:
FileUtils
Defined in:
lib/file_overwrite/file_overwrite.rb

Overview

Controller class to backup a file and overwrite it

Ruby iterators and chaining-methods are fully exploited to edit the file.

Examples

f1 = FileOverwrite.new('a.txt', noop: true, verbose: true)
  # Treat the content as String
f1.sub(/abc/, 'xyz').gsub(/(.)ef/){|i| $1}.run!
f1.sizes   # => { :old => 40, :new => 50 }
f1.backup  # => 'a.txt.20180915.bak'
           # However, the file has not been created
           # and the original file has not been modified, either,
           # due to the noop option

f2 = FileOverwrite.new('a.txt', suffix: '~')
f2.backup  # => 'a.txt~'
f2.completed?  # => false
  # Treat the content as String inside the block
f2.read{ |str| "\n" + i + "\n" }.gsub(/a\nb/m, '').run!
f2.completed?  # => true
FileOverwrite.new('a.txt', suffix: '~').sub(/a/, '').run!
  # => RuntimeError, because the backup file 'a.txt~' exists.
FileOverwrite.new('a.txt', suffix: '~').sub(/a/, '').run!(clobber: true)
  # => The backup file is overwritten.

f3 = FileOverwrite.new('a.txt', backup: '/tmp/b.txt')
  # Backup file can be explicitly specified.
f3.backup  # => '/tmp/b.txt'
f3.backup = 'original.txt'
f3.backup  # => 'original.txt'
  # Treat the file as IO inside the block
f3.open{ |ior, iow| i + "XYZ" }
f3.reset   # The modification is discarded
f3.reset?  # => true
f3.open{ |ior, iow| i + "XYZ"; raise FileOverwriteError, 'I stop.' }
           # To discard the modification inside the block
f3.reset?  # => true
f3.open{ |ior, iow| "\n" + i + "\n" }
f3.run!(noop: true, verbose: true)  # Dryrun
f3.completed?  # => true
f3.backup = 'change.d'  # => FrozenError (the state can not be modified after run!(), including dryrun)

f4 = FileOverwrite.new('a.txt', suffix: nil)
f4.backup  # => nil (No backup file is created.)
f4.readlines{|ary| ary+["last\n"]}.each{|i| 'XX'+i}.run!
IO.readlines('a.txt')[-1]   # => "XXlast\n"

f5 = FileOverwrite.new('a.txt', suffix: '.bak')
f5.backup  # => 'a.txt.bak'
f5.read{|i| i}.run!
FileUtils.identical? 'a.txt', 'a.txt.bak'       # => true
File.mtime('a.txt') == File.mtime('a.txt.bak')  # => true
  # To forcibly update the Timestamp, give touch option as true
  # either in new() or run!(), ie., run!(touch: true)

Author:

  • Masa Sakano

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fname, backup: nil, suffix: true, noop: false, verbose: $VERBOSE, clobber: false, touch: false) ⇒ FileOverwrite

Returns a new instance of FileOverwrite.

Parameters:

  • fname (String)

    Input filename

  • backup: (String, NilClass) (defaults to: nil)

    File name to which the original file is backed up. If non-Nil, suffix is ignored.

  • suffix: (String, TrueClass, FalseClass, NilClass) (defaults to: true)

    Suffix of the backup file. True for Def, or false if no backup.

  • noop: (Boolean) (defaults to: false)

    no-operationor dryrun

  • verbose: (Boolean, NilClass) (defaults to: $VERBOSE)

    the same as $VERBOSE or the command-line option -W, i.e., the verbosity is (true > false > nil). Forced to be true if $DEBUG

  • clobber: (Boolean) (defaults to: false)

    raise Exception if false(Def) and fname exists and suffix is non-null.

  • touch: (Boolean) (defaults to: false)

    if true (non-Def), when the file content does not change, the timestamp is updated, unless aboslutely no action is taken for the file.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/file_overwrite/file_overwrite.rb', line 115

def initialize(fname, backup: nil, suffix: true, noop: false, verbose: $VERBOSE, clobber: false, touch: false)
  @fname = fname
  @backup = backup
  @suffix = (backup ? true : suffix)
  @noop  = noop
  @verbose = $DEBUG || verbose
  @clobber = clobber
  @touch   = touch

  @ext_enc_old = nil
  @ext_enc_new = nil
  @int_enc     = nil

  @outstr = nil  # String to write.  This is nil if the temporary file was already created with modify().
  @outary = nil  # or Array to write
  @iotmp  = nil  # Temporary file IO to replace the original
  @is_edit_finished = false  # true if the file modification is finished.
  @is_completed = false      # true after all the process has been completed.
  @sizes = nil
end

Instance Attribute Details

#backup(suffix = nil, backupfile: nil) ⇒ String, NilClass

Gets a path of the filename for backup

If suffix is given, the default suffix and backup filename are ignored, and the (backup) filename with the given suffix is returned (so you can tell what the backup filename would be if the suffix was set).

Parameters:

  • suffix (String, TrueClass, FalseClass, NilClass) (defaults to: nil)

    Suffix of the backup file. True for Def, or false if no backup.

  • backupfile: (String, NilClass) (defaults to: nil)

    Explicilty specify the backup filename.

Returns:

  • (String, NilClass)


149
150
151
152
153
154
155
# File 'lib/file_overwrite/file_overwrite.rb', line 149

def backup(suffix=nil, backupfile: nil)
  return backup_from_suffix(suffix)  if suffix   # non-nil suffix explicitly given
  return backupfile                  if backupfile
  return @backup                     if @backup
  return backup_from_suffix(@suffix) if @suffix
  nil
end

#ext_encEncoding

Returns:

  • (Encoding)


96
# File 'lib/file_overwrite/file_overwrite.rb', line 96

attr_accessor :ext_enc_old

#ext_enc_newObject

Encoding of the content of the output file. Default is nil (unspecified).



101
102
103
# File 'lib/file_overwrite/file_overwrite.rb', line 101

def ext_enc_new
  @ext_enc_new
end

#ext_enc_oldObject

Encoding of the content of the input file. Default is nil (unspecified).



96
97
98
# File 'lib/file_overwrite/file_overwrite.rb', line 96

def ext_enc_old
  @ext_enc_old
end

#int_encEncoding

Returns:

  • (Encoding)


106
107
108
# File 'lib/file_overwrite/file_overwrite.rb', line 106

def int_enc
  @int_enc
end

#sizesObject (readonly)

Hash of the file sizes of before (:old) and after (:new).

This is set after #run! and if setsize option in #run! is given true (Default) or if verbose option is true (Def: false). Else, nil is returned.



86
87
88
# File 'lib/file_overwrite/file_overwrite.rb', line 86

def sizes
  @sizes
end

#verboseBoolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/file_overwrite/file_overwrite.rb', line 91

def verbose
  @verbose
end

Class Method Details

.readlines(fname, *rest, **kwd) { ... } ⇒ FileOverwrite

Shorthand of #initialize.#readlines, taking parameters for both

Parameters:

Yields:

Returns:

See Also:



831
832
833
# File 'lib/file_overwrite/file_overwrite.rb', line 831

def self.readlines(fname, *rest, **kwd, &bloc)
  new(fname, *rest, **kwd).send(__method__, *rest, **kwd, &bloc)
end

.readlines!(*rest, **kwd) { ... } ⇒ FileOverwrite

Shorthand of readlines.#run!

Parameters:

Yields:

Returns:

See Also:



842
843
844
# File 'lib/file_overwrite/file_overwrite.rb', line 842

def self.readlines!(*rest, **kwd, &bloc)
  readlines(*rest, **kwd, &bloc).run!(**kwd)
end

Instance Method Details

#chainable?Boolean, NilClass

Returns true if the instance is chainable.

In other words, whether a further process like #gsub can be run. This returns nil if #fresh? is true.

Returns:

  • (Boolean, NilClass)


164
165
166
167
168
# File 'lib/file_overwrite/file_overwrite.rb', line 164

def chainable?
  return nil if fresh?
  return false if completed?
  return !@is_edit_finished   # ie., (@outary || @outstr) b/c one of the three must be non-false after the 2 clauses above.
end

#completed?Boolean

Returns true if the process has been completed.

Returns:

  • (Boolean)


172
173
174
# File 'lib/file_overwrite/file_overwrite.rb', line 172

def completed?
  @is_completed
end

#dumpString

Returns the (current) content as String to supercede the input file

If the file has been already overwritten, this returns the content of the new one. Note it would be impossible to return the old one anyway, if no backup is left, as the user chooses.

Even if the returned string is destructively modified, it has no effect on the final output to the overwritten file.

Returns:

  • (String)


187
188
189
190
191
192
# File 'lib/file_overwrite/file_overwrite.rb', line 187

def dump
  return @outstr.dup   if @outstr
  return join_outary() if @outary
  return File.read(@iotmp.path) if @is_edit_finished
  File.read(@fname)
end

#each_line(*rest, **kwd) {|str| ... } ⇒ self

Takes a block in which each line of the file (or current content) is passed.

In the block each line as String is given as a block argument. Each iterator must return a String (or an object having to_s method), which replaces the input String to be output to the overwritten file later.

This method can be chained, as String-type processing.

Parameters:

  • *rest (Array)

    separator etc

  • **kwd (Hash)

    ext_enc, int_enc

Yield Parameters:

  • str (String)

Yield Returns:

  • (String)

    to be written back to the original file

Returns:

  • (self)

Raises:

  • (ArgumentError)

    if a block is not given



544
545
546
547
548
549
# File 'lib/file_overwrite/file_overwrite.rb', line 544

def each_line(*rest, **kwd, &bloc)
  raise ArgumentError, 'Block must be given.' if !block_given?
  read(**kwd){ |outstr|
    outstr.each_line(*rest).map{|i| yield(i).to_s}.join('')
  }
end

#each_line!(*rest, **kwd) { ... } ⇒ self

Alias to self.#sub.#run!

Parameters:

  • *rest (Array<Regexp,String>)
  • **kwd (Hash)

    setsize: etc

Yields:

  • the same as String#sub!

Returns:

  • (self)


557
558
559
# File 'lib/file_overwrite/file_overwrite.rb', line 557

def each_line!(*rest, **kwd, &bloc)
  send(__method__.to_s.chop, *rest, **kwd, &bloc).run!(**kwd)
end

#empty?String

True if the (current) content to supercede the input file is empty.

Returns:

  • (String)


198
199
200
# File 'lib/file_overwrite/file_overwrite.rb', line 198

def empty?
  dump.empty?
end

#encode(*rest, **kwd) ⇒ String

Implement String#encode

If it is in the middle of the process, the internal encoding for String (or Array) changes. Note if the current proces is in the IO-mode, everything has been already written in a temporary file, and hence there is no effect.

Once this is called, @int_enc is overwritten (or set), and it remains so even after reset() is called.

It is advisable to call #force_encoding or #ext_enc_old= before this is called to set the encoding of the input file.

Parameters:

  • *rest (Array)
  • **kwd (Hash)

Returns:

  • (String)

See Also:



220
221
222
223
224
225
226
227
228
229
230
# File 'lib/file_overwrite/file_overwrite.rb', line 220

def encode(*rest, **kwd)
  enc = (rest[0] || Encoding.default_internal)
  @int_enc = enc  # raises an Exception if called after "completed"
  return enc if @is_edit_finished || fresh?
  return @outstr.encode(*rest, **kwd) if @outstr
  if @outary
    @outary.map!{|i| i.encode(*rest, **kwd)}
    return enc
  end
  raise 'Should not happen.  Contact the code developer.'
end

#end_with?(*rest) ⇒ String

True if the (current) content to supercede the input file end with the specified.

Wrapper of String#end_with?

Returns:

  • (String)


238
239
240
# File 'lib/file_overwrite/file_overwrite.rb', line 238

def end_with?(*rest)
  dump.end_with?(*rest)
end

#force_encoding(enc) ⇒ Encoding

Implement String#force_encoding

Once this is called, @ext_enc_old is overwritten (or set), and it remains so even after reset() is called.



250
251
252
253
254
255
256
257
258
259
# File 'lib/file_overwrite/file_overwrite.rb', line 250

def force_encoding(enc)
  @ext_enc_old = enc  # raises an Exception if called after "completed"
  return enc if @is_edit_finished || fresh?
  return @outstr.force_encoding(enc) if @outstr
  if @outary
    @outary.map!{|i| i.force_encoding(enc)}
    return enc
  end
  raise 'Should not happen.  Contact the code developer.'
end

#fresh?Boolean Also known as: reset?

Returns true if the process has not yet started.

Returns:

  • (Boolean)


263
264
265
# File 'lib/file_overwrite/file_overwrite.rb', line 263

def fresh?
  !state
end

#gsub(*rest, max: 0, **kwd) { ... } ⇒ self

Note:

Disclaimer When a block is not given but arguments only (and not expecting Enumerator to return), this method simply calls String#gsub . However, when only 1 argument and a block is given, this method must iterate on its own, which is implemented. I am not 100% confident if this method works in the completely same way as String#gsub in every single situation (except the local variables like $1, $2, etc are not on the #gsub context; see #sub), given the regular expression has so many possibilities; I have made the best effort and so far I have not found any cases where this method breaks. This method is more inefficient and slower than the original String#gsub as this method scans/matches the string twice as many times as String#gsub (which is unavoidable to implement it properly, I think), and the implementation is in pure Ruby.

Similar to String#gsub

This method can be chained. This method never returns an Enumerator.

This method supplies the MatchData of the match as the second block parameter in addition to the matched string as in String#sub.

Being different from the standard Srrint#gsub, this method accepts the optional parameter max, which specifies the maximum number of times of the matches and is valid ONLY WHEN a block is given.

Parameters:

  • *rest (Array<Regexp,String>)
  • max: (Integer) (defaults to: 0)

    the number of the maximum matches. 0 means no limit (as in String#gsub). Valid only if a block is given.

  • **kwd (Hash)

    ext_enc, int_enc

Yields:

  • the same as String#gsub

Returns:

  • (self)

See Also:



723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
# File 'lib/file_overwrite/file_overwrite.rb', line 723

def gsub(*rest, max: 0, **kwd, &bloc)
  return sub(*rest, max: 1, **kwd, &bloc) if 1 == max  # Note: Error message would be labelled as 'sub'
  return self if sub_gsub_args_only(*rest, max: max, **kwd)

  if !block_given?
    raise ArgumentError, full_method_name+' does not support the format to return an enumerator.'
  end

  max = 5.0/0 if max.to_i <= 0

  scans = @outstr.scan(rest[0])
  return self if scans.empty?  # no matches

  scans.map!{|i| [i].flatten}  # Originally, it can be a double array.
  regbase_str = rest[0].to_s
  prematch = ''
  ret = ''
  imatch = 0  # Number of matches
  begin
    scans.each do |ea_sc|
      str_matched = ea_sc[0]
      imatch += 1
      pre_size = prematch.size
      pos_end_p1 = @outstr.index(str_matched, pre_size) # End+1
      str_between = @outstr[pre_size...pos_end_p1]
      prematch << str_between
      ret      << str_between
      regex = Regexp.new( sprintf('(?<=\A%s)%s', Regexp.quote(prematch), regbase_str) )
      #regex = rest[0] if prematch.empty?  # The first run
      m = regex.match(@outstr)
      prematch << str_matched
      # Not to break the specification of sub(), but just to extend.
      ret << yield(m[0], m).to_s
      break if imatch >= max
    end
    ret << Regexp.last_match.post_match  # Guaranteed to be non-nil.

    @outstr = ret
    return self
  rescue NoMethodError => err
    warn_for_sub_gsub(err)
    raise
  end
end

#gsub!(*rest, **kwd) { ... } ⇒ self

Alias to self.#gsub.#run!

Yields:

  • the same as String#gsub!

Returns:

  • (self)


773
774
775
# File 'lib/file_overwrite/file_overwrite.rb', line 773

def gsub!(*rest, **kwd, &bloc)
  gsub(*rest, &bloc).run!(**kwd)
end

#modify(**kwd) {|ioin, @iotmp| ... } ⇒ self Also known as: open

Modify the content in the block (though not committed, yet)

Two parameters are passed to the block: io_r and io_w. The former is the read-descriptor to read from the original file and the latter is the write-descriptor to write whatever to the temporary file, which is later moved back to the original file when you #run!.

Note the IO pointer for the input file is reset after this method. Hence, chaining this method makes no effect (warning is issued), but only the last one is taken into account.

If you want to halt, undo and reset your modification process in the middle, issue

raise FileOverwriteError [Your_Message]

and it will be rescued. Your_Message is printed to STDERR if verbose was specified in #initialize or $DEBUG

Examples:

fo.modify do |io_r, io_w|
  io_w.print( "\n" + io_r.read + "\n" )
end

Parameters:

  • **kwd (Hash)

    keyword parameters passed to File.open. Notably, ext_enc and int_enc .

Yield Parameters:

  • ioin (IO)

    Read IO instance from the original file

  • @iotmp (IO)

    Write IO instance to the temporary file

Yield Returns:

  • (Object)

    ignored

Returns:

  • (self)

Raises:

  • (ArgumentError)

    if a block is not given



458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
# File 'lib/file_overwrite/file_overwrite.rb', line 458

def modify(**kwd)
  raise ArgumentError, 'Block must be given.' if !block_given?
  normalize_status(:@is_edit_finished)

  kwd_def = {}
  kwd_def[:ext_enc] = @ext_enc_old if @ext_enc_old
  kwd_def[:int_enc] = @int_enc     if @int_enc
  kwd = kwd_def.merge kwd

  begin
    File.open(@fname, **kwd) { |ioin|
      @iotmp = tempfile_io
      yield(ioin, @iotmp)
    }
  rescue FileOverwriteError => err
    warn err.message if @verbose
    reset
  end
  self
end

#modify!(**kwd) {|ioin, @iotmp| ... } ⇒ self Also known as: open!

Alias to self.#modify.#run!

Yield Parameters:

  • ioin (IO)

    Read IO instance from the original file

  • @iotmp (IO)

    Write IO instance to the temporary file

Yield Returns:

  • (Object)

    ignored

Returns:

  • (self)

Raises:

  • (ArgumentError)

    if a block is not given



488
489
490
# File 'lib/file_overwrite/file_overwrite.rb', line 488

def modify!(**kwd, &bloc)
  modify(&bloc).run!(**kwd)
end

#read(**kwd) {|str| ... } ⇒ self

Handler to process the entire string of the file (or current content)

If block is not given, just sets the state as String

Else, File.read(infile) is given to the block. Then, the returned value is held as a String, hence this method can be chained. If the block returns nil (or Boolean), FileOverwriteError is raised. Make sure to return a String (whether an empty string or “true”)

Note this method does not take arguments as in IO.read

Parameters:

  • **kwd (Hash)

    ext_enc, int_enc

Yield Parameters:

  • str (String)

Yield Returns:

  • (String)

    to be written back to the original file

Returns:

  • (self)

Raises:



594
595
596
597
598
599
600
601
602
603
604
# File 'lib/file_overwrite/file_overwrite.rb', line 594

def read(**kwd, &bloc)
  if :first == normalize_status(:@outstr)
    adjust_input_encoding(**kwd){ |f|  # @fname
      @outstr = File.read f
    }
  end
    
  @outstr = yield(@outstr) if block_given?
  raise FileOverwriteError, 'ERROR: The returned value from the block in read() can not be nil or Boolean.' if !@outstr || true == @outstr
  self
end

#read!(**kwd) { ... } ⇒ self

Alias to self.#read.#run!

Parameters:

  • **kwd (Hash)

    ext_enc, int_enc

Yields:

  • Should return string

Returns:

  • (self)


612
613
614
# File 'lib/file_overwrite/file_overwrite.rb', line 612

def read!(**kwd, &bloc)
  read(**kwd, &bloc).run!(**kwd)
end

#readlines(*rest, **kwd) {|str| ... } ⇒ self

Takes a block in which the entire String of the file is passed.

IO.readlines(infile) is given to the block, where Encode may be taken into account if specified already.

The block must return an Array, the number of the elements of which can be allowed to differ from the input. The elements of the Array will be joined to output to the overwritten file in the end.

Parameters:

  • *rest (Array)

    separator etc

  • **kwd (Hash)

    ext_enc, int_enc

Yield Parameters:

  • str (String)

Yield Returns:

  • (String)

    to be written back to the original file

Returns:

  • (self)

Raises:

  • (ArgumentError)


512
513
514
515
516
517
518
519
520
521
522
523
# File 'lib/file_overwrite/file_overwrite.rb', line 512

def readlines(*rest, **kwd, &bloc)
  raise ArgumentError, 'Block must be given.' if !block_given?

  if :first == normalize_status(:@outary)
    adjust_input_encoding(**kwd){ |f|  # @fname
      @outary = IO.readlines(f, *rest)
    }
  end

  @outary = yield(@outary)
  self
end

#ready?Boolean

Returns true if the instance is ready to run (to execute overwriting the file).

Returns:

  • (Boolean)


270
271
272
# File 'lib/file_overwrite/file_overwrite.rb', line 270

def ready?
  !fresh? && !completed?
end

#replace_with(str) ⇒ self

Similar to String#replace but the original is not modified

This method can be chained.

Parameters:

  • str (String)

    the content will be replaced with this

Returns:

  • (self)


623
624
625
626
627
# File 'lib/file_overwrite/file_overwrite.rb', line 623

def replace_with(str)
  read
  @outstr = str.to_s
  self
end

#replace_with!(str, **kwd) { ... } ⇒ self

Alias to self.#replace_with.#run!

Yields:

  • the same as String#gsub!

Returns:

  • (self)


634
635
636
# File 'lib/file_overwrite/file_overwrite.rb', line 634

def replace_with!(str, **kwd)
  replace_with(str).run!(**kwd)
end

#resetNilClass

Reset all the modification which is to be applied

Returns:

  • (NilClass)


279
280
281
282
283
284
285
286
# File 'lib/file_overwrite/file_overwrite.rb', line 279

def reset
  @outstr = nil
  @outary = nil
  @is_edit_finished = nil
  close_iotmp  # @iotmp=nil; immediate deletion of the temporary file
  warn "The modification process is reset." if $DEBUG
  nil
end

#run!(backup: @backup, suffix: @suffix, noop: @noop, verbose: @verbose, clobber: @clobber, touch: @touch, setsize: true, **kwd) ⇒ NilClass, self Also known as: run

Actually performs the file modification

If setsize option is true (Default) or verbose, method #sizes is activated after this method, which returns a hash of file sizes in bytes before and after, so you can chain it. Note this method returns nil if the input file is not opened at all.

The folloing optional parameters are taken into account. Any other options are ignored.

Examples:

With setsize option

fo.run!(setsize: true).sizes
  # => { :old => 40, :new => 50 }

One case where this returns nil

fo.new('test.f').run!  # => nil

Parameters:

  • backup: (String, NilClass) (defaults to: @backup)

    File name to which the original file is backed up. If non-Nil, suffix is ignored.

  • suffix: (String, TrueClass, FalseClass, NilClass) (defaults to: @suffix)

    Suffix of the backup file. True for Def, or false if no backup.

  • noop: (Boolean) (defaults to: @noop)
  • verbose: (Boolean) (defaults to: @verbose)
  • clobber: (Boolean) (defaults to: @clobber)

    raise Exception if false(Def) and fname exists and suffix is non-null.

  • touch: (Boolean) (defaults to: @touch)

    Even if true (non-Def), when the file content does not change, the timestamp is updated, unless aboslutely no action has been taken for the file.

  • setsize: (Boolean) (defaults to: true)

Returns:

  • (NilClass, self)

    If the input file is not touched, nil is returned, else self.

Raises:



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/file_overwrite/file_overwrite.rb', line 383

def run!(backup: @backup, suffix: @suffix, noop: @noop, verbose: @verbose, clobber: @clobber, touch: @touch, setsize: true, **kwd)
  raise FileOverwriteError, 'The process has been already completed.' if completed?

  bkupname = get_bkupname(backup, suffix, noop, verbose, clobber)
  sizes = write_new(verbose, setsize)
  return nil if !sizes

  return self if run_identical?(noop, verbose, touch)

  if bkupname
    msg4bkup = ", Backup: " + bkupname if verbose
  else
    io2del = tempfile_io
    io2delname = io2del.path
  end

  fname_to = (bkupname || io2delname)
  mv(  @fname,    fname_to, noop: noop, verbose: $DEBUG) # defined in FileUtils
  begin
    mv(@iotmp.path, @fname, noop: noop, verbose: $DEBUG) # defined in FileUtils
  rescue
    msg = sprintf("Process halted! File system error in renaming the temporary file %s back to the original %s", @iotmp.path, @fname) 
    warn msg
    raise
  end

  # @iotmp.close(true)  # to immediate delete the temporary file
                        # If commented out, GC looks after it.

  File.unlink io2delname if io2delname && !noop
  # if noop, GC will delete it.

  if verbose
    msg = sprintf("%sFile %s updated (Size: %d => %d bytes%s)\n", prefix(noop), @fname, sizes[:old], sizes[:new], msg4bkup)
    fu_output_message msg
  end

  @is_completed = true
  self.freeze

  return self
end

#stateClass, ...

Returns the current state

nil if no modification has been attempted. IO if the modification has been made and it is wating to run. String or Array (or their equivalent), depending how it has been chained so far. true if the process has been completed.

Returns:

  • (Class, TrueClass, NilClass)


307
308
309
310
311
312
313
# File 'lib/file_overwrite/file_overwrite.rb', line 307

def state
  return true     if completed?
  return IO            if @is_edit_finished
  return @outstr.class if @outstr
  return @outary.class if @outary
  nil
end

#sub(*rest, max: 1, **kwd) { ... } ⇒ self

Similar to String#sub

This method can be chained. This method never returns an Enumerator.

WARNING: Do not use the local variables like $1, $2, $‘, and Regexp.last_match inside the block supplied. They would NOT be interpreted in the context of this method, but that in the caller, which is most likely not to be what you want.

Instead, this method supplies the MatchData of the match as the second block parameter in addition to the matched string as in String#sub.

Parameters:

  • *rest (Array<Regexp,String>)
  • max: (Integer) (defaults to: 1)

    the number of the maximum matches. If it is not 1, #gsub is called, instead. See #gsub for detail.

  • **kwd (Hash)

    ext_enc, int_enc

Yields:

  • the same as String#sub

Returns:

  • (self)


656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
# File 'lib/file_overwrite/file_overwrite.rb', line 656

def sub(*rest, max: 1, **kwd, &bloc)
  return self if sub_gsub_args_only(*rest, max: max, **kwd)

  if !block_given?
    raise ArgumentError, full_method_name+' does not support the format to return an enumerator.'
  end

  if max.to_i != 1
    return gsub(*rest, max: max, **kwd, &bloc)
  end

  begin
    m = rest[0].match(@outstr)
    # Returning nil, Integer etc is accepted in the block of sub/gsub
    @outstr = m.pre_match + yield(m[0], m).to_s + m.post_match if m
    # Not to break the specification of sub(), but just to extend.
    return self
  rescue NoMethodError => err
    warn_for_sub_gsub(err)
    raise
  end
end

#sub!(*rest, **kwd) { ... } ⇒ self

Alias to self.#sub.#run!

Parameters:

  • *rest (Array<Regexp,String>)
  • **kwd (Hash)

    setsize: etc

Yields:

  • the same as String#sub!

Returns:

  • (self)


686
687
688
# File 'lib/file_overwrite/file_overwrite.rb', line 686

def sub!(*rest, **kwd, &bloc)
  sub(*rest, &bloc).run!(**kwd)
end

#temporary_filenameString, NilClass

Returns the temporary filename (or nil), maybe for debugging

It may not be open?

Returns:

  • (String, NilClass)

    Filename if exists, else nil



294
295
296
# File 'lib/file_overwrite/file_overwrite.rb', line 294

def temporary_filename
  @iotmp ? @iotmp.path : nil
end

#tr(*rest, **kwd) ⇒ self

Similar to String#tr

This method can be chained.

Parameters:

  • *rest (Array)

    replacers etc

  • **kwd (Hash)

    ext_enc, int_enc

Returns:

  • (self)


785
786
787
788
789
# File 'lib/file_overwrite/file_overwrite.rb', line 785

def tr(*rest, **kwd)
  read(**kwd){ |outstr|
    outstr.tr!(*rest) || outstr
  }
end

#tr!(*rest, **kwd) ⇒ self

Alias to self.#tr.#run!

Returns:

  • (self)


794
795
796
# File 'lib/file_overwrite/file_overwrite.rb', line 794

def tr!(*rest, **kwd)
  tr(*rest, **kwd).run!(**kwd)
end

#tr_s(*rest, **kwd) ⇒ self

Similar to String#tr_s

This method can be chained.

Parameters:

  • *rest (Array)

    replacers etc

  • **kwd (Hash)

    ext_enc, int_enc

Returns:

  • (self)


805
806
807
808
809
# File 'lib/file_overwrite/file_overwrite.rb', line 805

def tr_s(*rest, **kwd)
  read(**kwd){ |outstr|
    outstr.tr_s!(*rest) || outstr
  }
end

#tr_s!(*rest, **kwd) ⇒ self

Alias to self.#tr.#run!

Returns:

  • (self)


814
815
816
# File 'lib/file_overwrite/file_overwrite.rb', line 814

def tr_s!(*rest, **kwd)
  tr_s(*rest, **kwd).run!(**kwd)
end

#valid_encoding?Boolean, NilClass

String#valid_encoding?()

returns nil if the process has been already completed.

Returns:

  • (Boolean, NilClass)


320
321
322
323
# File 'lib/file_overwrite/file_overwrite.rb', line 320

def valid_encoding?()
  return nil if completed?
  dump.valid_encoding?
end