Class: Path
Overview
Path: An object-oriented wrapper for files. (Combines useful methods from FileUtils, File, Dir, and more!)
To create a path object, or array of path objects, throw whatever you want into Path[]:
These returns a single path object:
passwd = Path["/etc/passwd"]
also_passwd = Path["/etc"] / "passwd" # joins two paths
parent_dir = Path["/usr/local/bin"] / ".." # joins two paths (up one dir)
These return an array of path objects:
pictures = Path["photos/*.{jpg,png}"] # globbing
notes = Path["notes/2014/**/*.txt"] # recursive globbing
everything = Path["/etc"].ls
Each Path object has the following attributes, which can all be modified:
path => the absolute path, as a string
filename => just the name and extension
basename => just the filename (without extension)
ext => just the extension
dir => just the directory
dirs => an array of directories
Some commonly used methods:
path.file?
path.exists?
path.dir?
path.mtime
path.xattrs
path.symlink?
path.broken_symlink?
path.symlink_target
path.executable?
path.chmod(0o666)
Interesting examples:
Path["*.jpeg"].each { |path| path.rename(:ext=>"jpg") } # renames .jpeg to .jpg
files = Path["/etc"].ls # all files in directory
morefiles = Path["/etc"].ls_R # all files in directory tree
Path["*.txt"].each(&:gzip!)
Path["filename.txt"] << "Append data!" # appends data to a file
string = Path["filename.txt"].read # read all file data into a string
json = Path["filename.json"].read_json # read and parse JSON
doc = Path["filename.html"].read_html # read and parse HTML
xml = Path["filename.xml"].parse # figure out the format and parse it (as XML)
Path["saved_data.marshal"].write(data.marshal) # Save your data!
data = Path["saved_data.marshal"].unmarshal # Load your data!
Path["unknown_file"].mimetype # sniff the file to determine its mimetype
Path["unknown_file"].mimetype.image? # ...is this some kind of image?
Path["otherdir/"].cd do # temporarily change to "otherdir/"
p Path.ls
end
p Path.ls
The ‘Path#dirs` attribute is a split up version of the directory (eg: Path.dirs => [“usr”, “local”, “bin”]).
You can modify the dirs array to change subsets of the directory. Here’s an example that finds out if you’re in a git repo:
def inside_a_git_repo?
path = Path.pwd # start at the current directory
while path.dirs.any?
if (path/".git").exists?
return true
else
path.dirs.pop # go up one level
end
end
false
end
Swap two files:
a, b = Path["file_a", "file_b"]
temp = a.with(:ext => a.ext+".swapping") # return a modified version of this object
a.mv(temp)
b.mv(a)
temp.mv(b)
Paths can be created for existant and non-existant files.
To create a nonexistant path object that thinks it’s a directory, just add a ‘/’ at the end. (eg: Path).
Performance has been an important factor in Path’s design, so doing crazy things with Path usually doesn’t kill performance. Go nuts!
Defined Under Namespace
Constant Summary collapse
- AUTOGENERATED_CLASS_METHODS =
FileUtils-like class-method versions of instance methods (eg: ‘Path.mv(src, dest)`)
Note: Methods with cardinality 1 (‘method/1`) are instance methods that take one parameter, and hence, class methods that take two parameters.
%w[ mkdir mkdir_p sha1 sha2 md5 rm truncate realpath mv/1 move/1 chmod/1 chown/1 chown_R/1 chmod_R/1 ].each do |spec| method, cardinality = spec.split("/") cardinality = cardinality.to_i class_eval %{ def self.#{method}(path#{", *args" if cardinality > 0}) Path[path].#{method}#{"(*args)" if cardinality > 0} end } end
- PATH_SEPARATOR =
":"- BINARY_EXTENSION =
""
Instance Attribute Summary collapse
-
#base ⇒ Object
(also: #basename)
The filename without an extension.
-
#dirs ⇒ Object
The directories in the path, split into an array.
-
#ext ⇒ Object
(also: #extname, #extension)
The file extension, including the .
Class Method Summary collapse
- .[](path) ⇒ Object
-
.cd(dest) ⇒ Object
Change into the directory “dest”.
- .escape(str) ⇒ Object
-
.expand_path(orig_path) ⇒ Object
Same as File.expand_path, except preserves the trailing ‘/’.
-
.getfattr(path) ⇒ Object
Read xattrs from file (requires “getfattr” to be in the path).
- .glob(str, hints = {}) ⇒ Object
- .home ⇒ Object
- .ln_s(src, dest) ⇒ Object
- .ls(path) ⇒ Object
- .ls_r(path) ⇒ Object
- .popd ⇒ Object
- .pushd ⇒ Object
- .pwd ⇒ Object
-
.setfattr(path, key, value) ⇒ Object
Set xattrs on a file (requires “setfattr” to be in the path).
- .tmpdir(prefix = "tmp") ⇒ Object
-
.tmpfile(prefix = "tmp") {|path| ... } ⇒ Object
TODO: Remove the tempfile when the Path object is garbage collected or freed.
-
.which(bin, *extras) ⇒ Object
A clone of ‘/usr/bin/which`: pass in the name of a binary, and it’ll search the PATH returning the absolute location of the binary if it exists, or ‘nil` otherwise.
Instance Method Summary collapse
- #/(other) ⇒ Object
- #<=>(other) ⇒ Object
- #==(other) ⇒ Object
-
#=~(pattern) ⇒ Object
Match the full path against a regular expression.
-
#[](key) ⇒ Object
Retrieve one of this file’s xattrs.
-
#[]=(key, value) ⇒ Object
Set this file’s xattr.
-
#append(data = nil) ⇒ Object
(also: #<<)
Append data to this file (accepts a string, an IO, or it can yield the file handle to a block.).
- #atime ⇒ Object
- #atime=(new_atime) ⇒ Object
-
#attrs ⇒ Object
(also: #xattrs)
Return a hash of all of this file’s xattrs.
-
#attrs=(new_attrs) ⇒ Object
Set this file’s xattrs.
-
#backup! ⇒ Object
Rename this file, “filename.ext”, to “filename.ext.bak”.
-
#backup_file ⇒ Object
Return a copy of this Path with “.bak” at the end.
- #broken_symlink? ⇒ Boolean
-
#cd(&block) ⇒ Object
Change into the directory.
- #child_of?(parent) ⇒ Boolean
-
#chmod(mode) ⇒ Object
Owners and permissions.
- #chmod_R(mode) ⇒ Object
- #chown(usergroup) ⇒ Object
- #chown_R(usergroup) ⇒ Object
- #cp(dest) ⇒ Object
- #cp_r(dest) ⇒ Object
- #ctime ⇒ Object
-
#deflate(level = nil) ⇒ Object
(also: #gzip)
gzip the file, returning the result as a string.
-
#dir ⇒ Object
(also: #dirname, #directory)
The current directory (with a trailing /).
- #dir=(newdir) ⇒ Object (also: #dirname=, #directory=)
- #dir? ⇒ Boolean (also: #directory?)
-
#each_chunk(chunk_size = 2**14) ⇒ Object
Read the contents of the file, yielding it in 16k chunks.
-
#each_line ⇒ Object
(also: #lines)
All the lines in this file, chomped.
- #endswith(s) ⇒ Object
- #executable? ⇒ Boolean (also: #exe?)
-
#exists? ⇒ Boolean
(also: #exist?)
fstat.
- #exts ⇒ Object
- #file? ⇒ Boolean
- #filename ⇒ Object
- #filename=(newfilename) ⇒ Object
- #grep(pat) ⇒ Object
-
#gunzip! ⇒ Object
Quickly gunzip a file, creating a new file, without removing the original, and returning a Path to that new file.
-
#gzip!(level = nil) ⇒ Object
Quickly gzip a file, creating a new .gz file, without removing the original, and returning a Path to that new file.
-
#inflate ⇒ Object
(also: #gunzip)
gunzip the file, returning the result as a string.
-
#initialize(newpath, hints = {}) ⇒ Path
constructor
Initializers.
- #initialize_copy(other) ⇒ Object
-
#inspect ⇒ Object
inspect.
-
#join(other) ⇒ Object
Path.join(“anything{}”).path == “/etc/anything{}” (globs ignored).
- #ln_s(dest) ⇒ Object
- #ls ⇒ Object
- #ls_dirs ⇒ Object
- #ls_files ⇒ Object
- #ls_R ⇒ Object
- #ls_r(symlinks = false) ⇒ Object
- #lstat ⇒ Object
-
#magic ⇒ Object
Find the file’s mimetype (by magic).
- #md5 ⇒ Object (also: #md5sum)
-
#mimetype ⇒ Object
(also: #identify)
Find the file’s mimetype (first from file extension, then by magic).
-
#mimetype_from_ext ⇒ Object
Find the file’s mimetype (only using the file extension).
- #mode ⇒ Object
- #mtime ⇒ Object
- #mtime=(new_mtime) ⇒ Object
-
#mv(arg) ⇒ Object
(also: #move)
Works the same as “rename”, but the destination can be on another disk.
-
#mv!(arg) ⇒ Object
(also: #move!)
Moves the file (overwriting the destination if it already exists).
- #name ⇒ Object
- #nicelines ⇒ Object
-
#numbered_backup! ⇒ Object
Rename this file, “filename.ext”, to “filename (1).ext” (or (2), or (3), or whatever number is available.) (Does not modify this Path object.).
-
#numbered_backup_file ⇒ Object
Find a backup filename that doesn’t exist yet by appending “(1)”, “(2)”, etc.
-
#open(mode = "rb", &block) ⇒ Object
(also: #io, #stream)
Opening/Reading files.
-
#owner? ⇒ Boolean
FIXME: Does the current user own this file?.
-
#parent ⇒ Object
Find the parent directory.
- #parent_of?(child) ⇒ Boolean
-
#parse ⇒ Object
Parse the file based on the file extension.
-
#path ⇒ Object
(also: #to_path, #to_str, #to_s, #pathname)
Joins and returns the full path.
-
#path=(newpath, hints = {}) ⇒ Object
This is the core that initializes the whole class.
- #read(length = nil, offset = nil) ⇒ Object
- #read_bson ⇒ Object
-
#read_csv(opts = {}) ⇒ Object
(also: #from_csv)
Parse the file as CSV.
- #read_html ⇒ Object (also: #from_html)
-
#read_json ⇒ Object
(also: #from_json)
Parse the file as JSON.
- #read_marshal ⇒ Object
- #read_xml ⇒ Object
-
#read_yaml ⇒ Object
(also: #from_yaml)
Parse the file as YAML.
- #readable? ⇒ Boolean
- #realpath ⇒ Object
-
#relative ⇒ Object
Path relative to current directory (Path.pwd).
-
#relative? ⇒ Boolean
Is this a relative path?.
- #relative_to(anchor) ⇒ Object
-
#reload! ⇒ Object
Reload this path (updates cached values.).
-
#rename(arg) ⇒ Object
(also: #ren)
Renames the file, but doesn’t change the current Path object, and returns a Path that points at the new filename.
-
#rename!(arg) ⇒ Object
(also: #ren!)
Rename the file and change this Path object so that it points to the destination file.
-
#reset! ⇒ Object
Clear out the internal state of this object, so that it can be reinitialized.
-
#rm ⇒ Object
(also: #delete!, #unlink!, #remove!)
Dangerous methods.
-
#sha1 ⇒ Object
Checksums.
- #sha2 ⇒ Object
- #siblings ⇒ Object
- #size ⇒ Object
-
#sort_attrs ⇒ Object
An array of attributes which will be used sort paths (case insensitive, directories come first).
- #startswith(s) ⇒ Object
- #symlink? ⇒ Boolean
- #symlink_target ⇒ Object (also: #readlink, #target)
-
#to_Path ⇒ Object
No-op (returns self).
- #touch ⇒ Object
- #truncate(offset = 0) ⇒ Object
-
#type ⇒ Object
Returns the filetype (as a standard file extension), verified with Magic.
- #unmarshal ⇒ Object
- #update(other) ⇒ Object
- #uri? ⇒ Boolean
- #url? ⇒ Boolean
- #writable? ⇒ Boolean
-
#write(data = nil) ⇒ Object
Overwrite the data in this file (accepts a string, an IO, or it can yield the file handle to a block.).
- #write_bson(object) ⇒ Object
-
#write_json(object) ⇒ Object
Convert the object to JSON and write it to the file (overwriting the existing file).
- #write_marshal(object) ⇒ Object
-
#write_yaml(object) ⇒ Object
Convert the object to YAML and write it to the file (overwriting the existing file).
Constructor Details
#initialize(newpath, hints = {}) ⇒ Path
Initializers
125 126 127 |
# File 'lib/epitools/path.rb', line 125 def initialize(newpath, hints={}) send("path=", newpath, hints) end |
Instance Attribute Details
#base ⇒ Object Also known as: basename
The filename without an extension
115 116 117 |
# File 'lib/epitools/path.rb', line 115 def base @base end |
#dirs ⇒ Object
The directories in the path, split into an array. (eg: [‘usr’, ‘src’, ‘linux’])
112 113 114 |
# File 'lib/epitools/path.rb', line 112 def dirs @dirs end |
#ext ⇒ Object Also known as: extname, extension
The file extension, including the . (eg: “.mp3”)
118 119 120 |
# File 'lib/epitools/path.rb', line 118 def ext @ext end |
Class Method Details
.[](path) ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/epitools/path.rb', line 143 def self.[](path) case path when Path path when String if path =~ %r{^[a-z\-]+://}i # URL? Path::URL.new(path) elsif path =~ /^javascript:/ Path::JS.new(path) else # TODO: highlight backgrounds of codeblocks to show indent level & put boxes (or rules?) around (between?) double-spaced regions path = Path.(path) unless path =~ /(^|[^\\])[\?\*\{\}]/ # contains unescaped glob chars? new(path) else glob(path) end end end end |
.cd(dest) ⇒ Object
Change into the directory “dest”. If a block is given, it changes into the directory for the duration of the block, then puts you back where you came from once the block is finished.
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
# File 'lib/epitools/path.rb', line 1296 def self.cd(dest) dest = Path[dest] raise "Can't 'cd' into #{dest}" unless dest.dir? if block_given? orig = pwd Dir.chdir(dest) result = yield dest Dir.chdir(orig) result else Dir.chdir(dest) dest end end |
.escape(str) ⇒ Object
135 136 137 |
# File 'lib/epitools/path.rb', line 135 def self.escape(str) Shellwords.escape(str) end |
.expand_path(orig_path) ⇒ Object
Same as File.expand_path, except preserves the trailing ‘/’.
1247 1248 1249 1250 1251 |
# File 'lib/epitools/path.rb', line 1247 def self.(orig_path) new_path = File. orig_path new_path << "/" if orig_path.endswith "/" new_path end |
.getfattr(path) ⇒ Object
Read xattrs from file (requires “getfattr” to be in the path)
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/epitools/path.rb', line 513 def self.getfattr(path) # # file: Scissor_Sisters_-_Invisible_Light.flv # user.m.options="-c" cmd = %w[getfattr -d -m - -e base64] + [path] attrs = {} IO.popen(cmd, "rb", :err=>[:child, :out]) do |io| io.each_line do |line| if line =~ /^([^=]+)=0s(.+)/ key = $1 value = $2.from_base64 # unpack base64 string # value = value.encode("UTF-8", "UTF-8") # set string's encoding to UTF-8 value = value.force_encoding("UTF-8").scrub # set string's encoding to UTF-8 # value = value.encode("UTF-8", "UTF-8") # set string's encoding to UTF-8 attrs[key] = value end end end attrs end |
.glob(str, hints = {}) ⇒ Object
139 140 141 |
# File 'lib/epitools/path.rb', line 139 def self.glob(str, hints={}) Dir[str].map { |entry| new(entry, hints) } end |
.home ⇒ Object
1273 1274 1275 |
# File 'lib/epitools/path.rb', line 1273 def self.home Path[ENV['HOME']] end |
.ln_s(src, dest) ⇒ Object
1319 1320 1321 1322 |
# File 'lib/epitools/path.rb', line 1319 def self.ln_s(src, dest) FileUtils.ln_s(src, dest) Path[dest] end |
.ls(path) ⇒ Object
1315 |
# File 'lib/epitools/path.rb', line 1315 def self.ls(path); Path[path].ls end |
.ls_r(path) ⇒ Object
1317 |
# File 'lib/epitools/path.rb', line 1317 def self.ls_r(path); Path[path].ls_r; end |
.popd ⇒ Object
1286 1287 1288 1289 |
# File 'lib/epitools/path.rb', line 1286 def self.popd @@dir_stack ||= [pwd] @@dir_stack.pop end |
.pushd ⇒ Object
1281 1282 1283 1284 |
# File 'lib/epitools/path.rb', line 1281 def self.pushd @@dir_stack ||= [] @@dir_stack.push pwd end |
.pwd ⇒ Object
1277 1278 1279 |
# File 'lib/epitools/path.rb', line 1277 def self.pwd Path.new (Dir.pwd) end |
.setfattr(path, key, value) ⇒ Object
Set xattrs on a file (requires “setfattr” to be in the path)
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 |
# File 'lib/epitools/path.rb', line 541 def self.setfattr(path, key, value) cmd = %w[setfattr] if value == nil # delete cmd += ["-x", key] else # set cmd += ["-n", key, "-v", value.to_s.strip] end cmd << path IO.popen(cmd, "rb", :err=>[:child, :out]) do |io| result = io.each_line.to_a error = {:cmd => cmd, :result => result.to_s}.inspect raise error if result.any? end end |
.tmpdir(prefix = "tmp") ⇒ Object
1264 1265 1266 1267 1268 1269 1270 1271 |
# File 'lib/epitools/path.rb', line 1264 def self.tmpdir(prefix="tmp") t = tmpfile # FIXME: These operations should be atomic t.rm; t.mkdir t end |
.tmpfile(prefix = "tmp") {|path| ... } ⇒ Object
TODO: Remove the tempfile when the Path object is garbage collected or freed.
1256 1257 1258 1259 1260 |
# File 'lib/epitools/path.rb', line 1256 def self.tmpfile(prefix="tmp") path = Path[ Tempfile.new(prefix).path ] yield path if block_given? path end |
.which(bin, *extras) ⇒ Object
A clone of ‘/usr/bin/which`: pass in the name of a binary, and it’ll search the PATH returning the absolute location of the binary if it exists, or ‘nil` otherwise.
(Note: If you pass more than one argument, it’ll return an array of ‘Path`s instead of
a single path.)
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 |
# File 'lib/epitools/path.rb', line 1343 def self.which(bin, *extras) if extras.empty? ENV["PATH"].split(PATH_SEPARATOR).find do |path| result = (Path[path] / (bin + BINARY_EXTENSION)) return result if result.exists? end nil else ([bin] + extras).map { |bin| which(bin) } end end |
Instance Method Details
#/(other) ⇒ Object
499 500 501 502 503 504 |
# File 'lib/epitools/path.rb', line 499 def /(other) # / <- fixes jedit syntax highlighting bug. # TODO: make it work for "/dir/dir"/"/dir/file" #Path.new( File.join(self, other) ) Path[ File.join(self, other) ] end |
#<=>(other) ⇒ Object
467 468 469 470 471 472 473 474 475 476 |
# File 'lib/epitools/path.rb', line 467 def <=>(other) case other when Path sort_attrs <=> other.sort_attrs when String path <=> other else raise "Invalid comparison: Path to #{other.class}" end end |
#==(other) ⇒ Object
478 479 480 |
# File 'lib/epitools/path.rb', line 478 def ==(other) self.path == other.to_s end |
#=~(pattern) ⇒ Object
Match the full path against a regular expression
1090 1091 1092 |
# File 'lib/epitools/path.rb', line 1090 def =~(pattern) to_s =~ pattern end |
#[](key) ⇒ Object
Retrieve one of this file’s xattrs
589 590 591 |
# File 'lib/epitools/path.rb', line 589 def [](key) attrs[key] end |
#[]=(key, value) ⇒ Object
Set this file’s xattr
596 597 598 599 |
# File 'lib/epitools/path.rb', line 596 def []=(key, value) Path.setfattr(path, key, value) @attrs = nil # clear cached xattrs end |
#append(data = nil) ⇒ Object Also known as: <<
Append data to this file (accepts a string, an IO, or it can yield the file handle to a block.)
693 694 695 696 697 698 699 700 701 702 703 704 705 706 |
# File 'lib/epitools/path.rb', line 693 def append(data=nil) self.open("ab") do |f| if data and not block_given? if data.is_an? IO IO.copy_stream(data, f) else f.write(data) end else yield f end end self end |
#atime ⇒ Object
383 384 385 |
# File 'lib/epitools/path.rb', line 383 def atime lstat.atime end |
#atime=(new_atime) ⇒ Object
387 388 389 390 391 |
# File 'lib/epitools/path.rb', line 387 def atime=(new_atime) File.utime(new_atime, mtime, path) @lstat = nil new_atime end |
#attrs ⇒ Object Also known as: xattrs
Return a hash of all of this file’s xattrs. (Metadata key=>valuse pairs, supported by most modern filesystems.)
565 566 567 |
# File 'lib/epitools/path.rb', line 565 def attrs @attrs ||= Path.getfattr(path) end |
#attrs=(new_attrs) ⇒ Object
Set this file’s xattrs. (Optimized so that only changed attrs are written to disk.)
573 574 575 576 577 578 579 580 581 582 583 584 |
# File 'lib/epitools/path.rb', line 573 def attrs=(new_attrs) changes = attrs.diff(new_attrs) changes.each do |key, (old, new)| case new when String, Numeric, true, false, nil self[key] = new else raise "Error: Can't use a #{new.class} as an xattr value. Try passing a String." end end end |
#backup! ⇒ Object
Rename this file, “filename.ext”, to “filename.ext.bak”. (Does not modify this Path object.)
920 921 922 |
# File 'lib/epitools/path.rb', line 920 def backup! rename(backup_file) end |
#backup_file ⇒ Object
Return a copy of this Path with “.bak” at the end
904 905 906 |
# File 'lib/epitools/path.rb', line 904 def backup_file with(:filename => filename+".bak") end |
#broken_symlink? ⇒ Boolean
423 424 425 |
# File 'lib/epitools/path.rb', line 423 def broken_symlink? File.symlink?(path) and not File.exists?(path) end |
#cd(&block) ⇒ Object
Change into the directory. If a block is given, it changes into the directory for the duration of the block, then puts you back where you came from once the block is finished.
818 819 820 |
# File 'lib/epitools/path.rb', line 818 def cd(&block) Path.cd(path, &block) end |
#child_of?(parent) ⇒ Boolean
446 447 448 |
# File 'lib/epitools/path.rb', line 446 def child_of?(parent) parent.parent_of? self end |
#chmod(mode) ⇒ Object
Owners and permissions
968 969 970 971 |
# File 'lib/epitools/path.rb', line 968 def chmod(mode) FileUtils.chmod(mode, self) self end |
#chmod_R(mode) ⇒ Object
979 980 981 982 983 984 985 986 |
# File 'lib/epitools/path.rb', line 979 def chmod_R(mode) if directory? FileUtils.chmod_R(mode, self) self else raise "Not a directory." end end |
#chown(usergroup) ⇒ Object
973 974 975 976 977 |
# File 'lib/epitools/path.rb', line 973 def chown(usergroup) user, group = usergroup.split(":") FileUtils.chown(user, group, self) self end |
#chown_R(usergroup) ⇒ Object
988 989 990 991 992 993 994 995 996 |
# File 'lib/epitools/path.rb', line 988 def chown_R(usergroup) user, group = usergroup.split(":") if directory? FileUtils.chown_R(user, group, self) self else raise "Not a directory." end end |
#cp(dest) ⇒ Object
953 954 955 956 |
# File 'lib/epitools/path.rb', line 953 def cp(dest) FileUtils.cp(path, dest) dest end |
#cp_r(dest) ⇒ Object
948 949 950 951 |
# File 'lib/epitools/path.rb', line 948 def cp_r(dest) FileUtils.cp_r(path, dest) #if Path[dest].exists? dest end |
#ctime ⇒ Object
379 380 381 |
# File 'lib/epitools/path.rb', line 379 def ctime lstat.ctime end |
#deflate(level = nil) ⇒ Object Also known as: gzip
gzip the file, returning the result as a string
1037 1038 1039 |
# File 'lib/epitools/path.rb', line 1037 def deflate(level=nil) Zlib.deflate(read, level) end |
#dir ⇒ Object Also known as: dirname, directory
The current directory (with a trailing /)
306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/epitools/path.rb', line 306 def dir if dirs if relative? File.join(*dirs) else File.join("", *dirs) end else nil end end |
#dir=(newdir) ⇒ Object Also known as: dirname=, directory=
222 223 224 225 226 227 |
# File 'lib/epitools/path.rb', line 222 def dir=(newdir) dirs = File.(newdir).split(File::SEPARATOR) dirs = dirs[1..-1] if dirs.size > 0 @dirs = dirs end |
#dir? ⇒ Boolean Also known as: directory?
411 412 413 |
# File 'lib/epitools/path.rb', line 411 def dir? File.directory? path end |
#each_chunk(chunk_size = 2**14) ⇒ Object
Read the contents of the file, yielding it in 16k chunks. (default chunk size is 16k)
622 623 624 625 626 |
# File 'lib/epitools/path.rb', line 622 def each_chunk(chunk_size=2**14) open do |f| yield f.read(chunk_size) until f.eof? end end |
#each_line ⇒ Object Also known as: lines
All the lines in this file, chomped.
632 633 634 |
# File 'lib/epitools/path.rb', line 632 def each_line io.each_line end |
#endswith(s) ⇒ Object
1106 |
# File 'lib/epitools/path.rb', line 1106 def endswith(s); path.endswith(s); end |
#executable? ⇒ Boolean Also known as: exe?
398 399 400 |
# File 'lib/epitools/path.rb', line 398 def executable? mode & 0o111 > 0 end |
#exists? ⇒ Boolean Also known as: exist?
fstat
352 353 354 |
# File 'lib/epitools/path.rb', line 352 def exists? File.exists? path end |
#exts ⇒ Object
334 335 336 337 338 |
# File 'lib/epitools/path.rb', line 334 def exts extensions = basename.split('.')[1..-1] extensions += [@ext] if @ext extensions end |
#file? ⇒ Boolean
415 416 417 |
# File 'lib/epitools/path.rb', line 415 def file? File.file? path end |
#filename ⇒ Object
318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/epitools/path.rb', line 318 def filename if base if ext base + "." + ext else base end else nil end end |
#filename=(newfilename) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/epitools/path.rb', line 204 def filename=(newfilename) if newfilename.nil? @ext, @base = nil, nil else ext = File.extname(newfilename) if ext.blank? @ext = nil @base = newfilename else self.ext = ext if pos = newfilename.rindex(ext) @base = newfilename[0...pos] end end end end |
#grep(pat) ⇒ Object
637 638 639 640 641 642 643 |
# File 'lib/epitools/path.rb', line 637 def grep(pat) return to_enum(:grep, pat).to_a unless block_given? each_line do |line| yield line if line =~ pat end end |
#gunzip! ⇒ Object
Quickly gunzip a file, creating a new file, without removing the original, and returning a Path to that new file.
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 |
# File 'lib/epitools/path.rb', line 1073 def gunzip! raise "Not a .gz file" unless ext == "gz" regular_file = self.with(:ext=>nil) regular_file.open("wb") do |output| Zlib::GzipReader.open(self) do |gzreader| IO.copy_stream(gzreader, output) end end update(regular_file) end |
#gzip!(level = nil) ⇒ Object
Quickly gzip a file, creating a new .gz file, without removing the original, and returning a Path to that new file.
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 |
# File 'lib/epitools/path.rb', line 1055 def gzip!(level=nil) gz_file = self.with(:filename=>filename+".gz") raise "#{gz_file} already exists" if gz_file.exists? open("rb") do |input| Zlib::GzipWriter.open(gz_file) do |gzwriter| IO.copy_stream(input, gzwriter) end end update(gz_file) end |
#inflate ⇒ Object Also known as: gunzip
gunzip the file, returning the result as a string
1046 1047 1048 |
# File 'lib/epitools/path.rb', line 1046 def inflate Zlib.inflate(read) end |
#initialize_copy(other) ⇒ Object
129 130 131 132 133 |
# File 'lib/epitools/path.rb', line 129 def initialize_copy(other) @dirs = other.dirs && other.dirs.dup @base = other.base && other.base.dup @ext = other.ext && other.ext.dup end |
#inspect ⇒ Object
inspect
344 345 346 |
# File 'lib/epitools/path.rb', line 344 def inspect "#<Path:#{path}>" end |
#join(other) ⇒ Object
Path.join(“anything{}”).path == “/etc/anything{}” (globs ignored)
491 492 493 |
# File 'lib/epitools/path.rb', line 491 def join(other) Path.new File.join(self, other) end |
#ln_s(dest) ⇒ Object
958 959 960 961 962 963 964 |
# File 'lib/epitools/path.rb', line 958 def ln_s(dest) if dest.startswith("/") Path.ln_s(self, dest) else Path.ln_s(self, self / dest ) end end |
#ls ⇒ Object
653 654 655 656 657 |
# File 'lib/epitools/path.rb', line 653 def ls Dir.foreach(path). reject {|fn| fn == "." or fn == ".." }. map {|fn| Path.new(fn) } end |
#ls_dirs ⇒ Object
666 667 668 669 |
# File 'lib/epitools/path.rb', line 666 def ls_dirs ls.select(&:dir?) #Dir.glob("#{path}*/", File::FNM_DOTMATCH).map { |s| Path.new(s, :type=>:dir) } end |
#ls_files ⇒ Object
671 672 673 674 |
# File 'lib/epitools/path.rb', line 671 def ls_files ls.select(&:file?) #Dir.glob("#{path}*", File::FNM_DOTMATCH).map { |s| Path.new(s, :type=>:file) } end |
#ls_R ⇒ Object
664 665 666 667 668 |
# File 'lib/epitools/path.rb', line 664 def ls_r(symlinks=false) # glob = symlinks ? "**{,/*/**}/*" : "**/*" # Path[File.join(path, glob)] Find.find(path).drop(1).map {|fn| Path.new(fn) } end |
#ls_r(symlinks = false) ⇒ Object
659 660 661 662 663 |
# File 'lib/epitools/path.rb', line 659 def ls_r(symlinks=false) # glob = symlinks ? "**{,/*/**}/*" : "**/*" # Path[File.join(path, glob)] Find.find(path).drop(1).map {|fn| Path.new(fn) } end |
#lstat ⇒ Object
360 361 362 363 |
# File 'lib/epitools/path.rb', line 360 def lstat @lstat ||= File.lstat self # to cache, or not to cache? that is the question. # File.lstat self # ...answer: not to cache! end |
#magic ⇒ Object
Find the file’s mimetype (by magic)
1141 1142 1143 |
# File 'lib/epitools/path.rb', line 1141 def magic open { |io| MimeMagic.by_magic(io) } end |
#md5 ⇒ Object Also known as: md5sum
1026 1027 1028 |
# File 'lib/epitools/path.rb', line 1026 def md5 Digest::MD5.file(self).hexdigest end |
#mimetype ⇒ Object Also known as: identify
Find the file’s mimetype (first from file extension, then by magic)
1126 1127 1128 |
# File 'lib/epitools/path.rb', line 1126 def mimetype mimetype_from_ext || magic end |
#mimetype_from_ext ⇒ Object
Find the file’s mimetype (only using the file extension)
1134 1135 1136 |
# File 'lib/epitools/path.rb', line 1134 def mimetype_from_ext MimeMagic.by_extension(ext) end |
#mode ⇒ Object
365 366 367 |
# File 'lib/epitools/path.rb', line 365 def mode lstat.mode end |
#mtime ⇒ Object
369 370 371 |
# File 'lib/epitools/path.rb', line 369 def mtime lstat.mtime end |
#mtime=(new_mtime) ⇒ Object
373 374 375 376 377 |
# File 'lib/epitools/path.rb', line 373 def mtime=(new_mtime) File.utime(atime, new_mtime, path) @lstat = nil new_mtime end |
#mv(arg) ⇒ Object Also known as: move
Works the same as “rename”, but the destination can be on another disk.
861 862 863 864 865 866 867 868 |
# File 'lib/epitools/path.rb', line 861 def mv(arg) dest = arg_to_path(arg) raise "Error: can't move #{self.inspect} because source location doesn't exist." unless exists? FileUtils.mv(path, dest) dest end |
#mv!(arg) ⇒ Object Also known as: move!
Moves the file (overwriting the destination if it already exists). Also points the current Path object at the new destination.
882 883 884 |
# File 'lib/epitools/path.rb', line 882 def mv!(arg) update(mv(arg)) end |
#name ⇒ Object
330 331 332 |
# File 'lib/epitools/path.rb', line 330 def name filename || "#{dirs.last}/" end |
#nicelines ⇒ Object
645 646 647 |
# File 'lib/epitools/path.rb', line 645 def nicelines lines.map(&:chomp) end |
#numbered_backup! ⇒ Object
Rename this file, “filename.ext”, to “filename (1).ext” (or (2), or (3), or whatever number is available.) (Does not modify this Path object.)
912 913 914 |
# File 'lib/epitools/path.rb', line 912 def numbered_backup! rename(numbered_backup_file) end |
#numbered_backup_file ⇒ Object
Find a backup filename that doesn’t exist yet by appending “(1)”, “(2)”, etc. to the current filename.
890 891 892 893 894 895 896 897 898 899 |
# File 'lib/epitools/path.rb', line 890 def numbered_backup_file return self unless exists? n = 1 loop do new_file = with(:basename => "#{basename} (#{n})") return new_file unless new_file.exists? n += 1 end end |
#open(mode = "rb", &block) ⇒ Object Also known as: io, stream
Opening/Reading files
605 606 607 608 609 610 611 |
# File 'lib/epitools/path.rb', line 605 def open(mode="rb", &block) if block_given? File.open(path, mode, &block) else File.open(path, mode) end end |
#owner? ⇒ Boolean
FIXME: Does the current user own this file?
394 395 396 |
# File 'lib/epitools/path.rb', line 394 def owner? raise "STUB" end |
#parent ⇒ Object
Find the parent directory. If the ‘Path` is a filename, it returns the containing directory.
1097 1098 1099 1100 1101 1102 1103 |
# File 'lib/epitools/path.rb', line 1097 def parent if file? with(:filename=>nil) else with(:dirs=>dirs[0...-1]) end end |
#parent_of?(child) ⇒ Boolean
450 451 452 |
# File 'lib/epitools/path.rb', line 450 def parent_of?(child) dirs == child.dirs[0...dirs.size] end |
#parse ⇒ Object
Parse the file based on the file extension. (Handles json, html, yaml, xml, bson, and marshal.)
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 |
# File 'lib/epitools/path.rb', line 734 def parse case ext.downcase when 'json' read_json when 'html', 'htm' read_html when 'yaml', 'yml' read_yaml when 'xml', 'rdf', 'rss' read_xml when 'csv' read_csv when 'marshal' read_marshal when 'bson' read_bson else raise "Unrecognized format: #{ext}" end end |
#path ⇒ Object Also known as: to_path, to_str, to_s, pathname
Joins and returns the full path
275 276 277 278 279 280 281 |
# File 'lib/epitools/path.rb', line 275 def path if d = dir File.join(d, (filename || "") ) else "" end end |
#path=(newpath, hints = {}) ⇒ Object
This is the core that initializes the whole class.
Note: The ‘hints` parameter contains options so `path=` doesn’t have to touch the filesytem as much.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/epitools/path.rb', line 181 def path=(newpath, hints={}) if hints[:type] or File.exists? newpath if hints[:type] == :dir or File.directory? newpath self.dir = newpath else self.dir, self.filename = File.split(newpath) end else if newpath.endswith(File::SEPARATOR) # ends in '/' self.dir = newpath else self.dir, self.filename = File.split(newpath) end end # FIXME: Make this work with globs. if hints[:relative] update(relative_to(Path.pwd)) elsif hints[:relative_to] update(relative_to(hints[:relative_to])) end end |
#read(length = nil, offset = nil) ⇒ Object
615 616 617 |
# File 'lib/epitools/path.rb', line 615 def read(length=nil, offset=nil) File.read(path, length, offset) end |
#read_bson ⇒ Object
805 806 807 |
# File 'lib/epitools/path.rb', line 805 def read_bson BSON.deserialize(read) end |
#read_csv(opts = {}) ⇒ Object Also known as: from_csv
Parse the file as CSV
785 786 787 |
# File 'lib/epitools/path.rb', line 785 def read_csv(opts={}) CSV.open(io, opts).each end |
#read_html ⇒ Object Also known as: from_html
767 768 769 |
# File 'lib/epitools/path.rb', line 767 def read_html Nokogiri::HTML(io) end |
#read_json ⇒ Object Also known as: from_json
Parse the file as JSON
756 757 758 |
# File 'lib/epitools/path.rb', line 756 def read_json JSON.load(io) end |
#read_marshal ⇒ Object
796 797 798 |
# File 'lib/epitools/path.rb', line 796 def read_marshal Marshal.load(io) end |
#read_xml ⇒ Object
791 792 793 |
# File 'lib/epitools/path.rb', line 791 def read_xml Nokogiri::XML(io) end |
#read_yaml ⇒ Object Also known as: from_yaml
Parse the file as YAML
779 780 781 |
# File 'lib/epitools/path.rb', line 779 def read_yaml YAML.load(io) end |
#readable? ⇒ Boolean
407 408 409 |
# File 'lib/epitools/path.rb', line 407 def readable? mode & 0o444 > 0 end |
#realpath ⇒ Object
1112 1113 1114 |
# File 'lib/epitools/path.rb', line 1112 def realpath Path.new File.realpath(path) end |
#relative ⇒ Object
Path relative to current directory (Path.pwd)
295 296 297 |
# File 'lib/epitools/path.rb', line 295 def relative relative_to(pwd) end |
#relative? ⇒ Boolean
Is this a relative path?
286 287 288 289 290 |
# File 'lib/epitools/path.rb', line 286 def relative? # FIXME: Need a Path::Relative subclass, so that "dir/filename" can be valid. # (If the user changes dirs, the relative path should change too.) dirs.first == ".." end |
#relative_to(anchor) ⇒ Object
299 300 301 302 303 |
# File 'lib/epitools/path.rb', line 299 def relative_to(anchor) anchor = anchor.to_s anchor += "/" unless anchor[/\/$/] to_s.gsub(/^#{Regexp.escape(anchor)}/, '') end |
#reload! ⇒ Object
Reload this path (updates cached values.)
257 258 259 260 261 262 263 264 |
# File 'lib/epitools/path.rb', line 257 def reload! temp = path reset! self.path = temp @attrs = nil self end |
#rename(arg) ⇒ Object Also known as: ren
Renames the file, but doesn’t change the current Path object, and returns a Path that points at the new filename.
Examples:
Path["file"].rename("newfile") #=> Path["newfile"]
Path["SongySong.mp3"].rename(:basename=>"Songy Song")
Path["Songy Song.mp3"].rename(:ext=>"aac")
Path["Songy Song.aac"].rename(:dir=>"/music2")
Path["/music2/Songy Song.aac"].exists? #=> true
847 848 849 850 851 852 853 854 855 |
# File 'lib/epitools/path.rb', line 847 def rename(arg) dest = arg_to_path(arg) raise "Error: destination (#{dest.inspect}) already exists" if dest.exists? raise "Error: can't rename #{self.inspect} because source location doesn't exist." unless exists? File.rename(path, dest) dest end |
#rename!(arg) ⇒ Object Also known as: ren!
Rename the file and change this Path object so that it points to the destination file.
874 875 876 |
# File 'lib/epitools/path.rb', line 874 def rename!(arg) update(rename(arg)) end |
#reset! ⇒ Object
Clear out the internal state of this object, so that it can be reinitialized.
249 250 251 252 |
# File 'lib/epitools/path.rb', line 249 def reset! [:@dirs, :@base, :@ext].each { |var| remove_instance_variable(var) rescue nil } self end |
#rm ⇒ Object Also known as: delete!, unlink!, remove!
Dangerous methods.
1000 1001 1002 1003 1004 1005 1006 |
# File 'lib/epitools/path.rb', line 1000 def rm if directory? and not symlink? Dir.rmdir(self) == 0 else File.unlink(self) == 1 end end |
#sha1 ⇒ Object
Checksums
1018 1019 1020 |
# File 'lib/epitools/path.rb', line 1018 def sha1 Digest::SHA1.file(self).hexdigest end |
#sha2 ⇒ Object
1022 1023 1024 |
# File 'lib/epitools/path.rb', line 1022 def sha2 Digest::SHA2.file(self).hexdigest end |
#siblings ⇒ Object
676 677 678 |
# File 'lib/epitools/path.rb', line 676 def siblings Path[dir].ls - [self] end |
#size ⇒ Object
356 357 358 |
# File 'lib/epitools/path.rb', line 356 def size File.size path end |
#sort_attrs ⇒ Object
An array of attributes which will be used sort paths (case insensitive, directories come first)
463 464 465 |
# File 'lib/epitools/path.rb', line 463 def sort_attrs [filename ? 1 : 0, path.downcase] end |
#startswith(s) ⇒ Object
1105 |
# File 'lib/epitools/path.rb', line 1105 def startswith(s); path.startswith(s); end |
#symlink? ⇒ Boolean
419 420 421 |
# File 'lib/epitools/path.rb', line 419 def symlink? File.symlink? path end |
#symlink_target ⇒ Object Also known as: readlink, target
427 428 429 430 431 432 433 434 |
# File 'lib/epitools/path.rb', line 427 def symlink_target target = File.readlink(path.gsub(/\/$/, '')) if target.startswith("/") Path[target] else Path[dir] / target end end |
#to_Path ⇒ Object
No-op (returns self)
1358 1359 1360 |
# File 'lib/epitools/path.rb', line 1358 def to_Path self end |
#touch ⇒ Object
680 681 682 683 |
# File 'lib/epitools/path.rb', line 680 def touch open("a") { } self end |
#truncate(offset = 0) ⇒ Object
1011 1012 1013 |
# File 'lib/epitools/path.rb', line 1011 def truncate(offset=0) File.truncate(self, offset) if exists? end |
#type ⇒ Object
Returns the filetype (as a standard file extension), verified with Magic.
(In other words, this will give you the true extension, even if the file’s extension is wrong.)
Note: Prefers long extensions (eg: jpeg over jpg)
TODO: rename type => magicext?
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 |
# File 'lib/epitools/path.rb', line 1155 def type @cached_type ||= begin if file? or symlink? ext = self.ext magic = self.magic if ext and magic if magic.extensions.include? ext ext else magic.ext # in case the supplied extension is wrong... end elsif !ext and magic magic.ext elsif ext and !magic ext else # !ext and !magic :unknown end elsif dir? :directory end end end |
#unmarshal ⇒ Object
649 650 651 |
# File 'lib/epitools/path.rb', line 649 def unmarshal read.unmarshal end |
#update(other) ⇒ Object
266 267 268 269 270 |
# File 'lib/epitools/path.rb', line 266 def update(other) @dirs = other.dirs @base = other.base @ext = other.ext end |
#uri? ⇒ Boolean
438 439 440 |
# File 'lib/epitools/path.rb', line 438 def uri? false end |
#url? ⇒ Boolean
442 443 444 |
# File 'lib/epitools/path.rb', line 442 def url? uri? end |
#writable? ⇒ Boolean
403 404 405 |
# File 'lib/epitools/path.rb', line 403 def writable? mode & 0o222 > 0 end |
#write(data = nil) ⇒ Object
Overwrite the data in this file (accepts a string, an IO, or it can yield the file handle to a block.)
712 713 714 715 716 717 718 719 720 721 722 723 724 |
# File 'lib/epitools/path.rb', line 712 def write(data=nil) self.open("wb") do |f| if data and not block_given? if data.is_an? IO IO.copy_stream(data, f) else f.write(data) end else yield f end end end |
#write_bson(object) ⇒ Object
809 810 811 |
# File 'lib/epitools/path.rb', line 809 def write_bson(object) write BSON.serialize(object) end |
#write_json(object) ⇒ Object
Convert the object to JSON and write it to the file (overwriting the existing file).
762 763 764 |
# File 'lib/epitools/path.rb', line 762 def write_json(object) write object.to_json end |
#write_marshal(object) ⇒ Object
800 801 802 |
# File 'lib/epitools/path.rb', line 800 def write_marshal(object) write object.marshal end |
#write_yaml(object) ⇒ Object
Convert the object to YAML and write it to the file (overwriting the existing file).
774 775 776 |
# File 'lib/epitools/path.rb', line 774 def write_yaml(object) write object.to_yaml end |