Module: TTY::File
- Defined in:
- lib/tty/file.rb,
lib/tty/file/differ.rb,
lib/tty/file/version.rb,
lib/tty/file/create_file.rb,
lib/tty/file/download_file.rb
Defined Under Namespace
Classes: CreateFile, Differ, DownloadFile
Constant Summary collapse
- U_R =
File permissions
0400- U_W =
0200- U_X =
0100- G_R =
0040- G_W =
0020- G_X =
0010- O_R =
0004- O_W =
0002- O_X =
0001- A_R =
0444- A_W =
0222- A_X =
0111- VERSION =
"0.1.0"- DownloadError =
Class.new(StandardError)
Class Method Summary collapse
-
.append_to_file(relative_path, *args, &block) ⇒ Object
Append to a file.
-
.binary?(relative_path) ⇒ Boolean
Check if file is binary.
-
.chmod(relative_path, permissions, options = {}) ⇒ Object
Change file permissions.
-
.copy_file(source_path, *args, &block) ⇒ Object
Copy file from the relative source to the relative destination running it through ERB.
-
.copy_metadata(src_path, dest_path, options = {}) ⇒ Object
Copy file metadata.
-
.create_file(relative_path, *args, &block) ⇒ Object
Create new file if doesn’t exist.
-
.diff(path_a, path_b, options = {}) ⇒ Object
Diff files line by line.
-
.download_file(uri, *args, &block) ⇒ Object
Download the content from a given address and save at the given relative destination.
-
.inject_into_file(relative_path, *args, &block) ⇒ Object
Inject content into file at a given location.
-
.log_status(cmd, message, verbose, color = false) ⇒ Object
private
Log file operation.
-
.prepend_to_file(relative_path, *args, &block) ⇒ Object
Prepend to a file.
- .private_module_function(method) ⇒ Object
-
.remove_file(relative_path, *args) ⇒ Object
Remove a file or a directory at specified relative path.
-
.replace_in_file(relative_path, *args, &block) ⇒ Object
Replace content of a file matching string.
Instance Method Summary collapse
-
#check_path(path) ⇒ Object
private
Check if path exists.
- #decorate(message, color) ⇒ Object
-
#open_tempfile_if_missing(object, &block) ⇒ Object
private
If content is not a path to a file, create a tempfile and open it instead.
Class Method Details
.append_to_file(relative_path, *args, &block) ⇒ Object
Append to a file
309 310 311 312 313 314 |
# File 'lib/tty/file.rb', line 309 def append_to_file(relative_path, *args, &block) = args.last.is_a?(Hash) ? args.pop : {} log_status(:append, relative_path, .fetch(:verbose, true), :green) .merge!(after: /\z/, verbose: false) inject_into_file(relative_path, *(args << ), &block) end |
.binary?(relative_path) ⇒ Boolean
Check if file is binary
49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/tty/file.rb', line 49 def binary?(relative_path) bytes = ::File.stat(relative_path).blksize bytes = 4096 if bytes > 4096 buffer = ::File.read(relative_path) || '' begin return buffer !~ /\A[\s[[:print:]]]*\z/m rescue ArgumentError => error return true if error. =~ /invalid byte sequence/ raise end end |
.chmod(relative_path, permissions, options = {}) ⇒ Object
Change file permissions
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/tty/file.rb', line 78 def chmod(relative_path, , = {}) mode = ::File.lstat(relative_path).mode if .to_s =~ /\d+/ mode = else .scan(/[ugoa][+-=][rwx]+/) do |setting| who, action = setting[0], setting[1] setting[2..setting.size].each_byte do |perm| mask = const_get("#{who.upcase}_#{perm.chr.upcase}") (action == '+') ? mode |= mask : mode ^= mask end end end log_status(:chmod, relative_path, .fetch(:verbose, true), :green) ::FileUtils.chmod_R(mode, relative_path) unless [:noop] end |
.copy_file(source_path, *args, &block) ⇒ Object
Copy file from the relative source to the relative destination running it through ERB.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/tty/file.rb', line 141 def copy_file(source_path, *args, &block) = args.last.is_a?(Hash) ? args.pop : {} dest_path = args.first || source_path.sub(/\.erb$/, '') if ::File.directory?(dest_path) dest_path = ::File.join(dest_path, ::File.basename(source_path)) end ctx = if (vars = [:context]) vars.instance_eval('binding') else instance_eval('binding') end [:context] ||= self create_file(dest_path, ) do template = ERB.new(::File.binread(source_path), nil, "-", "@output_buffer") content = template.result(ctx) content = block[content] if block content end return unless [:preserve] (source_path, dest_path, ) end |
.copy_metadata(src_path, dest_path, options = {}) ⇒ Object
Copy file metadata
170 171 172 173 174 |
# File 'lib/tty/file.rb', line 170 def (src_path, dest_path, = {}) stats = ::File.lstat(src_path) ::File.utime(stats.atime, stats.mtime, dest_path) chmod(dest_path, stats.mode, ) end |
.create_file(relative_path, *args, &block) ⇒ Object
Create new file if doesn’t exist
114 115 116 117 118 119 120 |
# File 'lib/tty/file.rb', line 114 def create_file(relative_path, *args, &block) = args.last.is_a?(Hash) ? args.pop : {} content = block_given? ? block[] : args.join CreateFile.new(relative_path, content, ).call end |
.diff(path_a, path_b, options = {}) ⇒ Object
Diff files line by line
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/tty/file.rb', line 193 def diff(path_a, path_b, = {}) threshold = [:threshold] || 10_000_000 output = '' open_tempfile_if_missing(path_a) do |file_a| if ::File.size(file_a) > threshold raise ArgumentError, "(file size of #{file_a.path} exceeds #{threshold} bytes, diff output suppressed)" end if binary?(file_a) raise ArgumentError, "(#{file_a.path} is binary, diff output suppressed)" end open_tempfile_if_missing(path_b) do |file_b| if binary?(file_b) raise ArgumentError, "(#{file_a.path} is binary, diff output suppressed)" end if ::File.size(file_b) > threshold return "(file size of #{file_b.path} exceeds #{threshold} bytes, diff output suppressed)" end log_status(:diff, "#{file_a.path} - #{file_b.path}", .fetch(:verbose, true), :green) return output if [:noop] block_size = file_a.lstat.blksize while !file_a.eof? && !file_b.eof? output << Differ.new(file_a.read(block_size), file_b.read(block_size), ).call end end end output end |
.download_file(uri, *args, &block) ⇒ Object
Download the content from a given address and save at the given relative destination. If block is provided in place of destination, the content of of the uri is yielded.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/tty/file.rb', line 252 def download_file(uri, *args, &block) = args.last.is_a?(Hash) ? args.pop : {} dest_path = args.first || ::File.basename(uri) unless uri =~ %r{^https?\://} copy_file(uri, dest_path, ) return end content = DownloadFile.new(uri, dest_path, ).call if block_given? content = (block.arity == 1 ? block[content] : block[]) end create_file(dest_path, content, ) end |
.inject_into_file(relative_path, *args, &block) ⇒ Object
Inject content into file at a given location
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/tty/file.rb', line 344 def inject_into_file(relative_path, *args, &block) = args.last.is_a?(Hash) ? args.pop : {} replacement = block_given? ? block[] : args.join flag, match = if .key?(:after) [:after, .delete(:after)] else [:before, .delete(:before)] end match = match.is_a?(Regexp) ? match : Regexp.escape(match) content = if flag == :after '\0' + replacement else replacement + '\0' end replace_in_file(relative_path, /#{match}/, content, .merge(verbose: false)) log_status(:inject, relative_path, .fetch(:verbose, true), :green) end |
.log_status(cmd, message, verbose, color = false) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Log file operation
458 459 460 461 462 463 464 465 466 467 468 469 |
# File 'lib/tty/file.rb', line 458 def log_status(cmd, , verbose, color = false) return unless verbose cmd = cmd.to_s.rjust(12) cmd = decorate(cmd, color) if color = "#{cmd} #{message}" += "\n" unless .end_with?("\n") @output.print() @output.flush end |
.prepend_to_file(relative_path, *args, &block) ⇒ Object
Prepend to a file
286 287 288 289 290 291 |
# File 'lib/tty/file.rb', line 286 def prepend_to_file(relative_path, *args, &block) = args.last.is_a?(Hash) ? args.pop : {} log_status(:prepend, relative_path, .fetch(:verbose, true), :green) .merge!(before: /\A/, verbose: false) inject_into_file(relative_path, *(args << ), &block) end |
.private_module_function(method) ⇒ Object
15 16 17 18 |
# File 'lib/tty/file.rb', line 15 def self.private_module_function(method) module_function(method) private_class_method(method) end |
.remove_file(relative_path, *args) ⇒ Object
Remove a file or a directory at specified relative path.
425 426 427 428 429 430 431 432 433 |
# File 'lib/tty/file.rb', line 425 def remove_file(relative_path, *args) = args.last.is_a?(Hash) ? args.pop : {} log_status(:remove, relative_path, .fetch(:verbose, true), :red) return if [:noop] ::FileUtils.rm_r(relative_path, force: [:force], secure: true) end |
.replace_in_file(relative_path, *args, &block) ⇒ Object
Replace content of a file matching string
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/tty/file.rb', line 386 def replace_in_file(relative_path, *args, &block) check_path(relative_path) = args.last.is_a?(Hash) ? args.pop : {} contents = IO.read(relative_path) replacement = (block ? block[] : args[1..-1].join).gsub('\0', '') log_status(:replace, relative_path, .fetch(:verbose, true), :green) return if [:noop] if [:force] || !contents.include?(replacement) if !contents.gsub!(*args, &block) find = args[0] raise "#{find.inspect} not found in #{relative_path}" end ::File.open(relative_path, 'w') do |file| file.write(contents) end end end |
Instance Method Details
#check_path(path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Check if path exists
441 442 443 444 |
# File 'lib/tty/file.rb', line 441 def check_path(path) return if ::File.exist?(path) raise ArgumentError, "File path #{path} does not exist." end |
#decorate(message, color) ⇒ Object
450 451 452 |
# File 'lib/tty/file.rb', line 450 def decorate(, color) @pastel.send(color, ) end |
#open_tempfile_if_missing(object, &block) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
If content is not a path to a file, create a tempfile and open it instead.
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
# File 'lib/tty/file.rb', line 479 def open_tempfile_if_missing(object, &block) if ::FileTest.file?(object) ::File.open(object, &block) else tempfile = Tempfile.new('tty-file-diff') tempfile << object tempfile.rewind block[tempfile] unless tempfile.nil? tempfile.close tempfile.unlink end end end |