Module: Maid::Tools
- Includes:
- Deprecated
- Defined in:
- lib/maid/tools.rb
Overview
These "tools" are methods available in the Maid DSL.
In general, methods are expected to:
- Automatically expand paths (that is,
'~/Downloads/foo.zip'
becomes'/home/username/Downloads/foo.zip'
) - Respect the
noop
(dry-run
) option if it is set
Some methods are not available on all platforms. An ArgumentError
is raised when a command is not available. See
tags such as: [Mac OS X]
Instance Method Summary collapse
-
#accessed_at(path) ⇒ Object
Get the time that a file was last accessed.
-
#add_tag(path, tag) ⇒ Object
Add a Finder label or a list of labels to a file or directory.
-
#added_at(path) ⇒ Object
The added time of a file on OS X, or ctime on Linux.
-
#checksum_of(path) ⇒ Object
Get a checksum for a file.
-
#contains_tag?(path, tag) ⇒ Boolean
Tell if a file or directory has a certain Finder labels.
-
#content_types(path) ⇒ Object
Get the content types of a path.
-
#copy(sources, destination) ⇒ Object
Copy from
sources
todestination
. -
#created_at(path) ⇒ Object
Get the creation time of a file.
-
#dimensions_px(path) ⇒ Object
Determine the dimensions of GIF, PNG, JPEG, or TIFF images.
-
#dir(globs) ⇒ Object
Give all files matching the given glob.
-
#dir_safe(globs) ⇒ Object
Same as
dir
, but excludes files that are (possibly) being downloaded. -
#disk_usage(path) ⇒ Object
Calculate disk usage of a given path in kilobytes.
-
#downloaded_from(path) ⇒ Object
[Mac OS X] Use Spotlight metadata to determine the site from which a file was downloaded.
-
#downloading?(path) ⇒ Boolean
Detect whether the path is currently being downloaded in Chrome, Firefox or Safari.
-
#dupes_in(globs) ⇒ Object
Find all duplicate files in the given globs.
-
#duration_s(path) ⇒ Object
[Mac OS X] Use Spotlight metadata to determine audio length.
-
#escape_glob(glob) ⇒ Object
Escape characters that have special meaning as a part of path global patterns.
-
#files(globs) ⇒ Object
Give only files matching the given glob.
-
#find(path, &block) ⇒ Object
Find matching files, akin to the Unix utility
find
. - #git_piston(path) ⇒ Object deprecated Deprecated.
-
#has_been_used?(path) ⇒ Boolean
Tell if a file has been used since added.
-
#has_tags?(path) ⇒ Boolean
Tell if a file or directory has any Finder labels.
-
#hidden?(path) ⇒ Boolean
Tell if a file is hidden.
-
#ignore_child_dirs(arr) ⇒ Object
Given an array of directories, return a new array without any child directories whose parent is already present in that array.
- #last_accessed(path) ⇒ Object deprecated Deprecated.
-
#locate(name) ⇒ Object
[Mac OS X] Use Spotlight to locate all files matching the given filename.
-
#location_city(path) ⇒ Object
Determine the city of the given JPEG image.
-
#media_type(path) ⇒ Object
Get the Internet media type of the file.
-
#mime_type(path) ⇒ Object
Get the MIME type of the file.
-
#mkdir(path, options = {}) ⇒ Object
Create a directory and all of its parent directories.
-
#modified_at(path) ⇒ Object
Get the modification time of a file.
-
#move(sources, destination_dir, clobber: true) ⇒ Object
Moves
sources
file(s) to adestination
directory. -
#newest_dupes_in(globs) ⇒ Object
Convenience method that is like
dupes_in
but excludes the oldest dupe. -
#remove(paths, options = {}) ⇒ Object
Delete the files at the given path recursively.
-
#remove_tag(path, tag) ⇒ Object
Remove a Finder label or a list of labels from a file or directory.
-
#rename(source, destination) ⇒ Object
Rename a single file.
-
#set_tag(path, tag) ⇒ Object
Set Finder label of a file or directory to a label or a list of labels.
-
#size_of(path) ⇒ Object
Get the size of a file.
-
#spotlight_content_types(path) ⇒ Object
[Mac OS X] Use Spotlight metadata to determine which content types a file has.
-
#sync(from, to, options = {}) ⇒ Object
Simple sync two files/folders using
rsync
. -
#tags(path) ⇒ Object
Get a list of Finder labels of a file or directory.
-
#trash(paths, options = {}) ⇒ Object
Move the given paths to the user's trash.
-
#tree_empty?(root) ⇒ Boolean
Test whether a directory is either empty, or contains only empty directories/subdirectories.
-
#used_at(path) ⇒ Object
The last used time of a file on OS X, or atime on Linux.
-
#verbose_dupes_in(globs) ⇒ Object
Convenience method for
dupes_in
that excludes the dupe with the shortest name. -
#where_content_type(paths, filter_types) ⇒ Object
Filter an array by content types.
-
#zipfile_contents(path) ⇒ Object
List the contents of a zip file.
Instance Method Details
#accessed_at(path) ⇒ Object
Get the time that a file was last accessed.
In Unix speak, atime
.
Examples
accessed_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011
545 546 547 |
# File 'lib/maid/tools.rb', line 545 def accessed_at(path) File.atime((path)) end |
#add_tag(path, tag) ⇒ Object
Add a Finder label or a list of labels to a file or directory. Only available on OS X when you have tag installed.
Example
add_tag("~/Downloads/a.dmg.download", "Unfinished")
836 837 838 839 840 841 842 843 844 845 |
# File 'lib/maid/tools.rb', line 836 def add_tag(path, tag) return unless has_tag_available_and_warn? path = (path) ts = Array(tag).join(',') log "add tags #{ts} to #{path}" return if @file_options[:noop] cmd("tag -a #{sh_escape(ts)} #{sh_escape(path)}") end |
#added_at(path) ⇒ Object
The added time of a file on OS X, or ctime on Linux.
Example
added_at("foo.zip") # => Sat Apr 09 10:50:01 -0400 2011
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 |
# File 'lib/maid/tools.rb', line 950 def added_at(path) if Maid::Platform.osx? path = (path) raw = cmd("mdls -raw -name kMDItemDateAdded #{sh_escape(path)}") if raw == '(null)' 1.second.ago else begin DateTime.parse(raw).to_time rescue ArgumentError => e created_at(path) end end else created_at(path) end end |
#checksum_of(path) ⇒ Object
Get a checksum for a file.
Examples
checksum_of('foo.zip') # => "67258d750ca654d5d3c7b06bd2a1c792ced2003e"
583 584 585 |
# File 'lib/maid/tools.rb', line 583 def checksum_of(path) Digest::SHA1.hexdigest(File.read(path)) end |
#contains_tag?(path, tag) ⇒ Boolean
Tell if a file or directory has a certain Finder labels. Only available on OS X when you have tag installed.
Example
contains_tag?("~/Downloads/a.dmg.download", "Unfinished") # => true
820 821 822 823 824 825 826 827 828 |
# File 'lib/maid/tools.rb', line 820 def contains_tag?(path, tag) if has_tag_available_and_warn? path = (path) ts = (path) ts.include?(tag) else false end end |
#content_types(path) ⇒ Object
Get the content types of a path.
Content types can be MIME types, Internet media types or Spotlight content types (OS X only).
Examples
content_types('foo.zip') # => ["public.zip-archive", "com.pkware.zip-archive",
"public.archive", "application/zip", "application"]
content_types('bar.jpg') # => ["public.jpeg", "public.image", "image/jpeg", "image"]
674 675 676 |
# File 'lib/maid/tools.rb', line 674 def content_types(path) [spotlight_content_types(path), mime_type(path), media_type(path)].flatten end |
#copy(sources, destination) ⇒ Object
Copy from sources
to destination
The path is not copied if a file already exists at the destination with the
same name. A warning is logged instead. Note: Similar functionality is
provided by the sync tool, but this requires installation of the rsync
binary
Examples
Single path:
copy('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/')
Multiple paths:
copy(['~/Downloads/foo.zip', '~/Downloads/bar.zip'], '~/Archive/Software/Mac OS X/')
copy(dir('~/Downloads/*.zip'), '~/Archive/Software/Mac OS X/')
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/maid/tools.rb', line 188 def copy(sources, destination) destination = (destination) (sources).each do |source| target = File.join(destination, File.basename(source)) if File.exist?(target) warn("skipping copy because #{sh_escape(source)} because #{sh_escape(target)} already exists") else log("cp #{sh_escape(source)} #{sh_escape(destination)}") FileUtils.cp(source, destination, **@file_options) end end end |
#created_at(path) ⇒ Object
Get the creation time of a file.
In Unix speak, ctime
.
Examples
created_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011
534 535 536 |
# File 'lib/maid/tools.rb', line 534 def created_at(path) File.ctime((path)) end |
#dimensions_px(path) ⇒ Object
Determine the dimensions of GIF, PNG, JPEG, or TIFF images.
Value returned is [width, height].
Examples
dimensions_px('image.jpg') # => [1024, 768]
width, height = dimensions_px('image.jpg')
dimensions_px('image.jpg').join('x') # => "1024x768"
470 471 472 |
# File 'lib/maid/tools.rb', line 470 def dimensions_px(path) Dimensions.dimensions(path) end |
#dir(globs) ⇒ Object
Give all files matching the given glob.
Note that the globs are not regexps (they're closer to shell globs). However, some regexp-like notation can be
used, e.g. ?
, [a-z]
, {tgz,zip}
. For more details, see Ruby's documentation on Dir.glob
.
The matches are sorted lexically to aid in readability when using --dry-run
.
Examples
Single glob:
dir('~/Downloads/*.zip')
Specifying multiple extensions succinctly:
dir('~/Downloads/*.{exe,deb,dmg,pkg,rpm}')
Multiple glob (all are equivalent):
dir(['~/Downloads/*.zip', '~/Dropbox/*.zip'])
dir(%w(~/Downloads/*.zip ~/Dropbox/*.zip))
dir('~/{Downloads,Dropbox}/*.zip')
Recursing into subdirectories (see also: find
):
dir('~/Music/**/*.m4a')
268 269 270 271 272 273 |
# File 'lib/maid/tools.rb', line 268 def dir(globs) (globs) .map { |glob| Dir.glob(glob) } .flatten .sort end |
#dir_safe(globs) ⇒ Object
Same as dir
, but excludes files that are (possibly) being
downloaded.
Example
Move Debian/Ubuntu packages that are finished downloading into a software directory.
move dir_safe('~/Downloads/*.deb'), '~/Archive/Software'
284 285 286 287 |
# File 'lib/maid/tools.rb', line 284 def dir_safe(globs) dir(globs) .reject { |path| downloading?(path) } end |
#disk_usage(path) ⇒ Object
Calculate disk usage of a given path in kilobytes.
See also: Maid::NumericExtensions::SizeToKb
.
Examples
disk_usage('foo.zip') # => 136
517 518 519 520 521 522 523 524 525 |
# File 'lib/maid/tools.rb', line 517 def disk_usage(path) raw = cmd("du -s #{sh_escape(path)}") # FIXME: This reports in kilobytes, but should probably report in bytes. usage_kb = raw.split(/\s+/).first.to_i raise "Stopping pessimistically because of unexpected value from du (#{raw.inspect})" if usage_kb.zero? usage_kb end |
#downloaded_from(path) ⇒ Object
[Mac OS X] Use Spotlight metadata to determine the site from which a file was downloaded.
Examples
downloaded_from('foo.zip') # => ['http://www.site.com/foo.zip', 'http://www.site.com/']
383 384 385 |
# File 'lib/maid/tools.rb', line 383 def downloaded_from(path) mdls_to_array(path, 'kMDItemWhereFroms') end |
#downloading?(path) ⇒ Boolean
Detect whether the path is currently being downloaded in Chrome, Firefox or Safari.
See also: dir_safe
390 391 392 |
# File 'lib/maid/tools.rb', line 390 def downloading?(path) Maid::Downloading.downloading?(path) end |
#dupes_in(globs) ⇒ Object
Find all duplicate files in the given globs.
More often than not, you'll want to use newest_dupes_in
or
verbose_dupes_in
instead of using this method directly.
Globs are expanded as in dir
, then all non-files are filtered out. The
remaining files are compared by size, and non-dupes are filtered out. The
remaining candidates are then compared by checksum. Dupes are returned as
an array of arrays.
Examples
dupes_in('~/{Downloads,Desktop}/*') # => [
['~/Downloads/foo.zip', '~/Downloads/foo (1).zip'],
['~/Desktop/bar.txt', '~/Desktop/bar copy.txt']
]
Keep the newest dupe:
dupes_in('~/Desktop/*', '~/Downloads/*').each do |dupes|
trash dupes.sort_by { |p| File.mtime(p) }[0..-2]
end
417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/maid/tools.rb', line 417 def dupes_in(globs) dupes = [] files(globs) # Start by filtering out non-files .group_by { |f| size_of(f) } # ... then grouping by size, since that's fast .reject { |_s, p| p.length < 2 } # ... and filter out any non-dupes .map do |_size, candidates| dupes += candidates .group_by { |p| checksum_of(p) } # Now group our candidates by a slower checksum calculation .reject { |_c, p| p.length < 2 } # ... and filter out any non-dupes .values end dupes end |
#duration_s(path) ⇒ Object
[Mac OS X] Use Spotlight metadata to determine audio length.
Examples
duration_s('foo.mp3') # => 235.705
494 495 496 |
# File 'lib/maid/tools.rb', line 494 def duration_s(path) cmd("mdls -raw -name kMDItemDurationSeconds #{sh_escape(path)}").to_f end |
#escape_glob(glob) ⇒ Object
Escape characters that have special meaning as a part of path global patterns.
Useful when using dir
with file names that may contain { } [ ]
characters.
Example
escape_glob('test [tmp]') # => 'test \\[tmp\\]'
305 306 307 |
# File 'lib/maid/tools.rb', line 305 def escape_glob(glob) glob.gsub(/[{}\[\]]/) { |s| '\\' + s } end |
#files(globs) ⇒ Object
Give only files matching the given glob.
This is the same as dir
but only includes actual files (no directories or symlinks).
293 294 295 296 |
# File 'lib/maid/tools.rb', line 293 def files(globs) dir(globs) .select { |f| File.file?(f) } end |
#find(path, &block) ⇒ Object
Find matching files, akin to the Unix utility find
.
If no block is given, it will return an array. Otherwise, it acts like Find.find
.
Examples
Without a block:
find('~/Downloads/') # => [...]
Recursing and filtering using a regular expression:
find('~/Downloads/').grep(/\.pdf$/)
(Note: It's just Ruby, so any methods in Array
and Enumerable
can be used.)
Recursing with a block:
find('~/Downloads/') do |path|
# ...
end
357 358 359 360 361 362 363 364 365 |
# File 'lib/maid/tools.rb', line 357 def find(path, &block) = (path) if block.nil? Find.find().to_a else Find.find(, &block) end end |
#git_piston(path) ⇒ Object
Pull and push the git
repository at the given path.
Since this is deprecated, you might also be interested in SparkleShare, a great
git
-based file syncronization project.
Examples
git_piston('~/code/projectname')
597 598 599 600 601 |
# File 'lib/maid/tools.rb', line 597 def git_piston(path) full_path = (path) stdout = cmd("cd #{sh_escape(full_path)} && git pull && git push 2>&1") log("Fired git piston on #{sh_escape(full_path)}. STDOUT:\n\n#{stdout}") end |
#has_been_used?(path) ⇒ Boolean
Tell if a file has been used since added
Example
has_been_used?("~/Downloads/downloading.download") # => false
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 |
# File 'lib/maid/tools.rb', line 901 def has_been_used?(path) if Maid::Platform.osx? path = (path) raw = cmd("mdls -raw -name kMDItemLastUsedDate #{sh_escape(path)}") if raw == '(null)' false else begin DateTime.parse(raw).to_time true rescue ArgumentError => e false end end else used_at(path) <=> added_at(path) > 0 end end |
#has_tags?(path) ⇒ Boolean
Tell if a file or directory has any Finder labels. Only available on OS X when you have tag installed.
Example
("~/Downloads/a.dmg.download") # => true
805 806 807 808 809 810 811 812 |
# File 'lib/maid/tools.rb', line 805 def (path) if has_tag_available_and_warn? ts = (path) ts && ts.count > 0 else false end end |
#hidden?(path) ⇒ Boolean
Tell if a file is hidden
Example
hidden?("~/.maid") # => true
886 887 888 889 890 891 892 893 894 |
# File 'lib/maid/tools.rb', line 886 def hidden?(path) if Maid::Platform.osx? raw = cmd("mdls -raw -name kMDItemFSInvisible #{sh_escape(path)}") raw == '1' else p = Pathname.new((path)) p.basename =~ /^\./ end end |
#ignore_child_dirs(arr) ⇒ Object
Given an array of directories, return a new array without any child directories whose parent is already present in that array.
Example
ignore_child_dirs(["foo", "foo/a", "foo/b", "bar"]) # => ["foo", "bar"]
776 777 778 779 780 781 782 |
# File 'lib/maid/tools.rb', line 776 def ignore_child_dirs(arr) arr.sort do |x, y| y.count('/') - x.count('/') end.select do |d| !arr.include?(File.dirname(d)) end end |
#last_accessed(path) ⇒ Object
Alias of accessed_at
.
552 553 554 555 |
# File 'lib/maid/tools.rb', line 552 def last_accessed(path) # Not a normal `alias` so the deprecation notice shows in the docs. accessed_at(path) end |
#locate(name) ⇒ Object
[Mac OS X] Use Spotlight to locate all files matching the given filename.
[Ubuntu] Use locate
to locate all files matching the given filename.
Examples
locate('foo.zip') # => ['/a/foo.zip', '/b/foo.zip']
374 375 376 |
# File 'lib/maid/tools.rb', line 374 def locate(name) cmd("#{Maid::Platform::Commands.locate} #{sh_escape(name)}").split("\n") end |
#location_city(path) ⇒ Object
Determine the city of the given JPEG image.
Examples
loation_city('old_capitol.jpg') # => "Iowa City, IA, US"
479 480 481 482 483 484 485 486 487 |
# File 'lib/maid/tools.rb', line 479 def location_city(path) case mime_type(path) when 'image/jpeg' gps = EXIFR::JPEG.new(path).gps coordinates_string = [gps.latitude, gps.longitude] location = Geocoder.search(coordinates_string).first [location.city, location.province, location.country_code.upcase].join(', ') end end |
#media_type(path) ⇒ Object
Get the Internet media type of the file.
In other words, the first part of mime_type
.
Examples
media_type('bar.jpg') # => "image"
698 699 700 701 702 703 704 |
# File 'lib/maid/tools.rb', line 698 def media_type(path) type = MIME::Types.type_for(path)[0] return unless type type.media_type end |
#mime_type(path) ⇒ Object
Get the MIME type of the file.
Examples
mime_type('bar.jpg') # => "image/jpeg"
683 684 685 686 687 688 689 |
# File 'lib/maid/tools.rb', line 683 def mime_type(path) type = MIME::Types.type_for(path)[0] return unless type [type.media_type, type.sub_type].join('/') end |
#mkdir(path, options = {}) ⇒ Object
Create a directory and all of its parent directories.
The path of the created directory is returned, which allows for chaining (see examples).
Options
:mode
The symbolic and absolute mode can both be used, for example: 0700
, 'u=wr,go=rr'
Examples
Creating a directory with a specific mode:
mkdir('~/Music/Pink Floyd/', :mode => 0644)
Ensuring a directory exists when moving:
move('~/Downloads/Pink Floyd*.mp3', mkdir('~/Music/Pink Floyd/'))
328 329 330 331 332 333 |
# File 'lib/maid/tools.rb', line 328 def mkdir(path, = {}) path = (path) log("mkdir -p #{sh_escape(path)}") FileUtils.mkdir_p(path, **@file_options.merge()) path end |
#modified_at(path) ⇒ Object
Get the modification time of a file.
In Unix speak, mtime
.
Examples
modified_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011
565 566 567 |
# File 'lib/maid/tools.rb', line 565 def modified_at(path) File.mtime((path)) end |
#move(sources, destination_dir, clobber: true) ⇒ Object
Moves sources
file(s) to a destination
directory.
Movement is only allowed to directories that already exist. If your
intention is to rename, see the rename
method.
move
sources
to
file if it exists, false
to skip moving file if it exists
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/maid/tools.rb', line 56 def move(sources, destination_dir, clobber: true) = (destination_dir) if File.directory?() (sources).each do |source| log("move #{sh_escape(source)} #{sh_escape()}") unless skip_move?(source, , clobber) FileUtils.mv(source, , **@file_options) end end else # Unix `mv` warns about the target not being a directory with multiple sources. Maid checks the same. warn("skipping move because #{sh_escape()} " \ "is not a directory (use 'mkdir' to create first, or use 'rename')") end end |
#newest_dupes_in(globs) ⇒ Object
Convenience method that is like dupes_in
but excludes the oldest dupe.
Example
Keep the oldest dupe (trash the others):
trash newest_dupes_in('~/Downloads/*')
439 440 441 442 443 |
# File 'lib/maid/tools.rb', line 439 def newest_dupes_in(globs) dupes_in(globs) .map { |dupes| dupes.sort_by { |p| File.mtime(p) }[1..-1] } .flatten end |
#remove(paths, options = {}) ⇒ Object
Delete the files at the given path recursively.
NOTE: In most cases, trash
is a safer choice, since the files will be
recoverable by retreiving them from the trash. Once you delete a file
using remove
, it's gone! Please use trash
whenever possible and only
use remove
when necessary.
Options
:force => boolean
Force deletion (no error is raised if the file does not exist).
:secure => boolean
Infrequently needed. See FileUtils.remove_entry_secure
Examples
Single path:
remove('~/Downloads/foo.zip')
Multiple path:
remove(['~/Downloads/foo.zip', '~/Downloads/bar.zip'])
remove(dir('~/Downloads/*.zip'))
232 233 234 235 236 237 238 239 |
# File 'lib/maid/tools.rb', line 232 def remove(paths, = {}) (paths).each do |path| = @file_options.merge() log("Removing #{sh_escape(path)}") FileUtils.rm_r(path, **) end end |
#remove_tag(path, tag) ⇒ Object
Remove a Finder label or a list of labels from a file or directory. Only available on OS X when you have tag installed.
Example
remove_tag("~/Downloads/a.dmg", "Unfinished")
853 854 855 856 857 858 859 860 861 862 |
# File 'lib/maid/tools.rb', line 853 def remove_tag(path, tag) return unless has_tag_available_and_warn? path = (path) ts = Array(tag).join(',') log "remove tags #{ts} from #{path}" return if @file_options[:noop] cmd("tag -r #{sh_escape(ts)} #{sh_escape(path)}") end |
#rename(source, destination) ⇒ Object
Rename a single file.
Any directories needed in order to complete the rename are made automatically.
Overwriting is not allowed; it logs a warning. If overwriting is desired,
use remove
to delete the file first, then use rename
.
Examples
Simple rename:
rename('foo.zip', 'baz.zip') # "foo.zip" becomes "baz.zip"
Rename needing directories:
rename('foo.zip', 'bar/baz.zip') # "bar" is created, "foo.zip" becomes "baz.zip" within "bar"
Attempting to overwrite:
rename('foo.zip', 'existing.zip') # "skipping move of..."
94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/maid/tools.rb', line 94 def rename(source, destination) source = (source) destination = (destination) mkdir(File.dirname(destination)) if File.exist?(destination) warn("skipping rename of #{sh_escape(source)} to #{sh_escape(destination)} because it would overwrite") else log("rename #{sh_escape(source)} #{sh_escape(destination)}") FileUtils.mv(source, destination, **@file_options) end end |
#set_tag(path, tag) ⇒ Object
Set Finder label of a file or directory to a label or a list of labels. Only available on OS X when you have tag installed.
Example
set_tag("~/Downloads/a.dmg.download", "Unfinished")
870 871 872 873 874 875 876 877 878 879 |
# File 'lib/maid/tools.rb', line 870 def set_tag(path, tag) return unless has_tag_available_and_warn? path = (path) ts = Array(tag).join(',') log "set tags #{ts} to #{path}" return if @file_options[:noop] cmd("tag -s #{sh_escape(ts)} #{sh_escape(path)}") end |
#size_of(path) ⇒ Object
Get the size of a file.
Examples
size_of('foo.zip') # => 2193
574 575 576 |
# File 'lib/maid/tools.rb', line 574 def size_of(path) File.size(path) end |
#spotlight_content_types(path) ⇒ Object
[Mac OS X] Use Spotlight metadata to determine which content types a file has.
Examples
spotlight_content_types('foo.zip') # => ['public.zip-archive', 'public.archive']
661 662 663 |
# File 'lib/maid/tools.rb', line 661 def spotlight_content_types(path) mdls_to_array(path, 'kMDItemContentTypeTree') end |
#sync(from, to, options = {}) ⇒ Object
Simple sync two files/folders using rsync
.
The host OS must provide rsync
. See the rsync
man page for a detailed description.
man rsync
Options
:delete => boolean
:verbose => boolean
:archive => boolean
(default true
)
:update => boolean
(default true
)
:exclude => string
:prune_empty => boolean
Examples
Syncing a directory to a backup:
sync('~/music', '/backup/music')
Excluding a path:
sync('~/code', '/backup/code', :exclude => '.git')
Excluding multiple paths:
sync('~/code', '/backup/code', :exclude => ['.git', '.rvmrc'])
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 |
# File 'lib/maid/tools.rb', line 633 def sync(from, to, = {}) # expand removes trailing slash # cannot use str[-1] due to ruby 1.8.7 restriction from = (from) + (from.end_with?('/') ? '/' : '') to = (to) + (to.end_with?('/') ? '/' : '') # default options = { archive: true, update: true }.merge() ops = [] ops << '-a' if [:archive] ops << '-v' if [:verbose] ops << '-u' if [:update] ops << '-m' if [:prune_empty] ops << '-n' if @file_options[:noop] Array([:exclude]).each do |path| ops << "--exclude=#{sh_escape(path)}" end ops << '--delete' if [:delete] stdout = cmd("rsync #{ops.join(' ')} #{sh_escape(from)} #{sh_escape(to)} 2>&1") log("Fired sync from #{sh_escape(from)} to #{sh_escape(to)}. STDOUT:\n\n#{stdout}") end |
#tags(path) ⇒ Object
Get a list of Finder labels of a file or directory. Only available on OS X when you have tag installed.
Example
("~/Downloads/a.dmg.download") # => ["Unfinished"]
789 790 791 792 793 794 795 796 797 |
# File 'lib/maid/tools.rb', line 789 def (path) if has_tag_available_and_warn? path = (path) raw = cmd("tag -lN #{sh_escape(path)}") raw.strip.split(',') else [] end end |
#trash(paths, options = {}) ⇒ Object
Move the given paths to the user's trash.
The path is still moved if a file already exists in the trash with the same name. However, the current date and time is appended to the filename.
Note: the OS-native "restore" or "put back" functionality for trashed files is not currently supported. (See issue #63.) However, they can be restored manually, and the Maid log can help assist with this.
Options
:remove_over => Fixnum
(e.g. 1.gigabyte
, 1024.megabytes
)
Delete files over the given size rather than moving to the trash.
See also Maid::NumericExtensions::SizeToKb
Examples
Single path:
trash('~/Downloads/foo.zip')
Multiple paths:
trash(['~/Downloads/foo.zip', '~/Downloads/bar.zip'])
trash(dir('~/Downloads/*.zip'))
135 136 137 138 139 140 141 142 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 168 169 170 |
# File 'lib/maid/tools.rb', line 135 def trash(paths, = {}) # ## Implementation Notes # # Trashing files correctly is surprisingly hard. What Maid ends up doing # is one the easiest, most foolproof solutions: moving the file. # # Unfortunately, that means it's not possile to restore files automatically # in OSX or Ubuntu. The previous location of the file is lost. # # OSX support depends on AppleScript or would require a not-yet-written C # extension to interface with the OS. The AppleScript solution is less # than ideal: the user has to be logged in, Finder has to be running, and # it makes the "trash can sound" every time a file is moved. # # Ubuntu makes it easy to implement, and there's a Python library for doing # so (see `trash-cli`). However, there's not a Ruby equivalent yet. (paths).each do |path| target = File.join(@trash_path, File.basename(path)) safe_trash_path = File.join(@trash_path, "#{File.basename(path)} #{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}") if [:remove_over] && File.exist?(path) && disk_usage(path) > [:remove_over] remove(path) end if File.exist?(path) if File.exist?(target) rename(path, safe_trash_path) else move(path, @trash_path) end end end end |
#tree_empty?(root) ⇒ Boolean
Test whether a directory is either empty, or contains only empty directories/subdirectories.
Example
if tree_empty?(dir('~/Downloads/foo'))
trash('~/Downloads/foo')
end
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 767 768 |
# File 'lib/maid/tools.rb', line 741 def tree_empty?(root) return nil if File.file?(root) return true if Dir.glob(root + '/*').length == 0 ignore = [] # Look for files. return false if Dir.glob(root + '/*').select { |f| File.file?(f) }.length > 0 empty_dirs = Dir.glob(root + '/**/*').select do |d| File.directory?(d) end.reverse.select do |d| # `.reverse` sorts deeper directories first. # If the directory is empty, its parent should ignore it. should_ignore = Dir.glob(d + '/*').select do |n| !ignore.include?(n) end.length == 0 ignore << d if should_ignore should_ignore end Dir.glob(root + '/*').select do |n| !empty_dirs.include?(n) end.length == 0 end |
#used_at(path) ⇒ Object
The last used time of a file on OS X, or atime on Linux.
Example
used_at("foo.zip") # => Sat Apr 09 10:50:01 -0400 2011
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 |
# File 'lib/maid/tools.rb', line 926 def used_at(path) if Maid::Platform.osx? path = (path) raw = cmd("mdls -raw -name kMDItemLastUsedDate #{sh_escape(path)}") if raw == '(null)' nil else begin DateTime.parse(raw).to_time rescue ArgumentError => e accessed_at(path) end end else accessed_at(path) end end |
#verbose_dupes_in(globs) ⇒ Object
Convenience method for dupes_in
that excludes the dupe with the shortest name.
This is ideal for dupes like foo.zip
, foo (1).zip
, foo copy.zip
.
Example
Keep the dupe with the shortest name (trash the others):
trash verbose_dupes_in('~/Downloads/*')
455 456 457 458 459 |
# File 'lib/maid/tools.rb', line 455 def verbose_dupes_in(globs) dupes_in(globs) .map { |dupes| dupes.sort_by { |p| File.basename(p).length }[1..-1] } .flatten end |
#where_content_type(paths, filter_types) ⇒ Object
Filter an array by content types.
Content types can be MIME types, internet media types or Spotlight content types (OS X only).
If you need your rules to work on multiple platforms, it's recommended to avoid using Spotlight content types.
Examples
Using media types
where_content_type(dir('~/Downloads/*'), 'video')
where_content_type(dir('~/Downloads/*'), ['image', 'audio'])
Using MIME types
where_content_type(dir('~/Downloads/*'), 'image/jpeg')
Using Spotlight content types
Less portable, but richer data in some cases.
where_content_type(dir('~/Downloads/*'), 'public.image')
728 729 730 731 |
# File 'lib/maid/tools.rb', line 728 def where_content_type(paths, filter_types) filter_types = Array(filter_types) Array(paths).select { |p| !(filter_types & content_types(p)).empty? } end |
#zipfile_contents(path) ⇒ Object
List the contents of a zip file.
Examples
zipfile_contents('foo.zip') # => ['foo.exe', 'README.txt', 'subdir/anything.txt']
503 504 505 506 507 508 |
# File 'lib/maid/tools.rb', line 503 def zipfile_contents(path) # It might be nice to use `glob` from `Zip::FileSystem`, but it seems buggy. (Subdirectories aren't included.) Zip::File.open(path) do |zip_file| zip_file.entries.map { |entry| entry.name }.sort end end |