{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "a2f89a5c-6ee2-4134-9e2d-359eca653be0", "metadata": { "scrolled": true, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: pyyaml in /opt/conda/lib/python3.9/site-packages (6.0)\n", "Requirement already satisfied: minidb in /opt/conda/lib/python3.9/site-packages (2.0.6)\n", "Requirement already satisfied: requests in /opt/conda/lib/python3.9/site-packages (2.27.1)\n", "Requirement already satisfied: keyring in /opt/conda/lib/python3.9/site-packages (23.5.0)\n", "Requirement already satisfied: appdirs in /opt/conda/lib/python3.9/site-packages (1.4.4)\n", "Requirement already satisfied: lxml in /opt/conda/lib/python3.9/site-packages (4.8.0)\n", "Requirement already satisfied: cssselect in /opt/conda/lib/python3.9/site-packages (1.1.0)\n", "Requirement already satisfied: urlwatch in /opt/conda/lib/python3.9/site-packages (2.25)\n", "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /opt/conda/lib/python3.9/site-packages (from requests) (1.26.9)\n", "Requirement already satisfied: charset-normalizer~=2.0.0 in /opt/conda/lib/python3.9/site-packages (from requests) (2.0.12)\n", "Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.9/site-packages (from requests) (2021.10.8)\n", "Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.9/site-packages (from requests) (3.3)\n", "Requirement already satisfied: jeepney>=0.4.2 in /opt/conda/lib/python3.9/site-packages (from keyring) (0.8.0)\n", "Requirement already satisfied: SecretStorage>=3.2 in /opt/conda/lib/python3.9/site-packages (from keyring) (3.3.2)\n", "Requirement already satisfied: importlib-metadata>=3.6 in /opt/conda/lib/python3.9/site-packages (from keyring) (4.11.3)\n", "Requirement already satisfied: zipp>=0.5 in /opt/conda/lib/python3.9/site-packages (from importlib-metadata>=3.6->keyring) (3.8.0)\n", "Requirement already satisfied: cryptography>=2.0 in /opt/conda/lib/python3.9/site-packages (from SecretStorage>=3.2->keyring) (37.0.1)\n", "Requirement already satisfied: cffi>=1.12 in /opt/conda/lib/python3.9/site-packages (from cryptography>=2.0->SecretStorage>=3.2->keyring) (1.15.0)\n", "Requirement already satisfied: pycparser in /opt/conda/lib/python3.9/site-packages (from cffi>=1.12->cryptography>=2.0->SecretStorage>=3.2->keyring) (2.21)\n" ] } ], "source": [ "!python3 -m pip install pyyaml minidb requests keyring appdirs lxml cssselect urlwatch" ] }, { "cell_type": "markdown", "id": "3a72a8cf-7c34-43a3-8d44-9e06084968a3", "metadata": {}, "source": [ "## Settings" ] }, { "cell_type": "code", "execution_count": 10, "id": "b99d3c93-a55d-4995-9b86-9ee5368630e4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Please set $VISUAL or $EDITOR.\n" ] } ], "source": [ "# import sys\n", "# !{sys.executable} -m pip install urlwatch\n", "\n", "import os\n", "\n", "# stream = os.popen('urlwatch --config \"/home/jovyan/code/Unfamiliar tools/urlmonitor/config.yml\"')\n", "\n", "stream = os.popen('urlwatch --edit')\n", "# stream = os.popen('echo $PWD')\n", "output = stream.read()\n", "print(output)" ] }, { "cell_type": "markdown", "id": "ca1f4265-19c9-4af7-a894-19695bc2d052", "metadata": {}, "source": [ "https://urlwatch.readthedocs.io/_/downloads/en/latest/pdf/" ] }, { "cell_type": "code", "execution_count": null, "id": "894bf961-5206-46c7-8f75-c1df83018a96", "metadata": {}, "outputs": [], "source": [ "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "Libraries » maid\n", "(0.7.0) » [Index\n", "(T)](/gems/maid/0.7.0/index) » Maid » Tools\n", "\n", "
\n", "\n", "
\n", "\n", "\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "# Module: Maid::Tools\n", "\n", "
\n", "\n", "Includes: \n", "Deprecated\n", "\n", "\n", "\n", "Defined in: \n", "lib/maid/tools.rb\n", "\n", "
\n", "\n", "## Overview\n", "\n", "
\n", "\n", "
\n", "\n", "These \"tools\" are methods available in the Maid DSL.\n", "\n", "In general, methods are expected to:\n", "\n", "- Automatically expand paths (that is, `'~/Downloads/foo.zip'` becomes\n", " `'/home/username/Downloads/foo.zip'`)\n", "- Respect the `noop` (`dry-run`) option if it is set\n", "\n", "Some methods are not available on all platforms. An `ArgumentError` is\n", "raised when a command is not available. See tags such as: \\[Mac OS X\\]\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "## Instance Method Summary collapse\n", "\n", "- [accessed_at](/gems/maid/0.7.0/Maid/Tools#accessed_at-instance_method \"#accessed_at (instance method)\")\n", "- [checksum_of](/gems/maid/0.7.0/Maid/Tools#checksum_of-instance_method \"#checksum_of (instance method)\")\n", "- [content_types](/gems/maid/0.7.0/Maid/Tools#content_types-instance_method \"#content_types (instance method)\")\n", "- [copy](/gems/maid/0.7.0/Maid/Tools#copy-instance_method \"#copy (instance method)\")\n", "- [created_at](/gems/maid/0.7.0/Maid/Tools#created_at-instance_method \"#created_at (instance method)\")\n", "- [dimensions_px](/gems/maid/0.7.0/Maid/Tools#dimensions_px-instance_method \"#dimensions_px (instance method)\")\n", "- [dir](/gems/maid/0.7.0/Maid/Tools#dir-instance_method \"#dir (instance method)\")\n", "- [dir_safe](/gems/maid/0.7.0/Maid/Tools#dir_safe-instance_method \"#dir_safe (instance method)\")\n", "- [disk_usage](/gems/maid/0.7.0/Maid/Tools#disk_usage-instance_method \"#disk_usage (instance method)\")\n", "- [downloaded_from](/gems/maid/0.7.0/Maid/Tools#downloaded_from-instance_method \"#downloaded_from (instance method)\")\n", "- [downloading?](/gems/maid/0.7.0/Maid/Tools#downloading%3F-instance_method \"#downloading? (instance method)\")\n", "- [dupes_in](/gems/maid/0.7.0/Maid/Tools#dupes_in-instance_method \"#dupes_in (instance method)\")\n", "- [duration_s](/gems/maid/0.7.0/Maid/Tools#duration_s-instance_method \"#duration_s (instance method)\")\n", "- [escape_glob](/gems/maid/0.7.0/Maid/Tools#escape_glob-instance_method \"#escape_glob (instance method)\")\n", "- [files](/gems/maid/0.7.0/Maid/Tools#files-instance_method \"#files (instance method)\")\n", "- [find](/gems/maid/0.7.0/Maid/Tools#find-instance_method \"#find (instance method)\")\n", "- [git_piston](/gems/maid/0.7.0/Maid/Tools#git_piston-instance_method \"#git_piston (instance method)\")\n", "- [ignore_child_dirs](/gems/maid/0.7.0/Maid/Tools#ignore_child_dirs-instance_method \"#ignore_child_dirs (instance method)\")\n", "- [last_accessed](/gems/maid/0.7.0/Maid/Tools#last_accessed-instance_method \"#last_accessed (instance method)\")\n", "- [locate](/gems/maid/0.7.0/Maid/Tools#locate-instance_method \"#locate (instance method)\")\n", "- [location_city](/gems/maid/0.7.0/Maid/Tools#location_city-instance_method \"#location_city (instance method)\")\n", "- [media_type](/gems/maid/0.7.0/Maid/Tools#media_type-instance_method \"#media_type (instance method)\")\n", "- [mime_type](/gems/maid/0.7.0/Maid/Tools#mime_type-instance_method \"#mime_type (instance method)\")\n", "- [mkdir](/gems/maid/0.7.0/Maid/Tools#mkdir-instance_method \"#mkdir (instance method)\")\n", "- [modified_at](/gems/maid/0.7.0/Maid/Tools#modified_at-instance_method \"#modified_at (instance method)\")\n", "- [move](/gems/maid/0.7.0/Maid/Tools#move-instance_method \"#move (instance method)\")\n", "- [newest_dupes_in](/gems/maid/0.7.0/Maid/Tools#newest_dupes_in-instance_method \"#newest_dupes_in (instance method)\")\n", "- [remove](/gems/maid/0.7.0/Maid/Tools#remove-instance_method \"#remove (instance method)\")\n", "- [rename](/gems/maid/0.7.0/Maid/Tools#rename-instance_method \"#rename (instance method)\")\n", "- [size_of](/gems/maid/0.7.0/Maid/Tools#size_of-instance_method \"#size_of (instance method)\")\n", "- [spotlight_content_types](/gems/maid/0.7.0/Maid/Tools#spotlight_content_types-instance_method \"#spotlight_content_types (instance method)\")\n", "- [sync](/gems/maid/0.7.0/Maid/Tools#sync-instance_method \"#sync (instance method)\")\n", "- [trash](/gems/maid/0.7.0/Maid/Tools#trash-instance_method \"#trash (instance method)\")\n", "- [tree_empty?](/gems/maid/0.7.0/Maid/Tools#tree_empty%3F-instance_method \"#tree_empty? (instance method)\")\n", "- [verbose_dupes_in](/gems/maid/0.7.0/Maid/Tools#verbose_dupes_in-instance_method \"#verbose_dupes_in (instance method)\")\n", "- [where_content_type](/gems/maid/0.7.0/Maid/Tools#where_content_type-instance_method \"#where_content_type (instance method)\")\n", "- [zipfile_contents](/gems/maid/0.7.0/Maid/Tools#zipfile_contents-instance_method \"#zipfile_contents (instance method)\")\n", "\n", "\n", "\n", "- [\\#**accessed_at**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#accessed_at-instance_method \"#accessed_at (instance method)\")\n", " \n", "
\n", "\n", " Get the time that a file was last accessed.\n", "\n", "
\n", "- [\\#**checksum_of**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#checksum_of-instance_method \"#checksum_of (instance method)\")\n", " \n", "
\n", "\n", " Get a checksum for a file.\n", "\n", "
\n", "- [\\#**content_types**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#content_types-instance_method \"#content_types (instance method)\")\n", " \n", "
\n", "\n", " Get the content types of a path.\n", "\n", "
\n", "- [\\#**copy**(sources, destination) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#copy-instance_method \"#copy (instance method)\")\n", " \n", "
\n", "\n", " Copy from `sources` to `destination`.\n", "\n", "
\n", "- [\\#**created_at**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#created_at-instance_method \"#created_at (instance method)\")\n", " \n", "
\n", "\n", " Get the creation time of a file.\n", "\n", "
\n", "- [\\#**dimensions_px**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#dimensions_px-instance_method \"#dimensions_px (instance method)\")\n", " \n", "
\n", "\n", " Determine the dimensions of GIF, PNG, JPEG, or TIFF images.\n", "\n", "
\n", "- [\\#**dir**(globs) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#dir-instance_method \"#dir (instance method)\")\n", " \n", "
\n", "\n", " Give all files matching the given glob.\n", "\n", "
\n", "- [\\#**dir_safe**(globs) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#dir_safe-instance_method \"#dir_safe (instance method)\")\n", " \n", "
\n", "\n", " Same as `dir`, but excludes files that are (possibly) being\n", " downloaded.\n", "\n", "
\n", "- [\\#**disk_usage**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#disk_usage-instance_method \"#disk_usage (instance method)\")\n", " \n", "
\n", "\n", " Calculate disk usage of a given path in kilobytes.\n", "\n", "
\n", "- [\\#**downloaded_from**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#downloaded_from-instance_method \"#downloaded_from (instance method)\")\n", " \n", "
\n", "\n", " \\[Mac OS X\\] Use Spotlight metadata to determine the site from which\n", " a file was downloaded.\n", "\n", "
\n", "- [\\#**downloading?**(path) ⇒\n", " Boolean](/gems/maid/0.7.0/Maid/Tools#downloading%3F-instance_method \"#downloading? (instance method)\")\n", " \n", "
\n", "\n", " Detect whether the path is currently being downloaded in Chrome,\n", " Firefox or Safari.\n", "\n", "
\n", "- [\\#**dupes_in**(globs) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#dupes_in-instance_method \"#dupes_in (instance method)\")\n", " \n", "
\n", "\n", " Find all duplicate files in the given globs.\n", "\n", "
\n", "- [\\#**duration_s**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#duration_s-instance_method \"#duration_s (instance method)\")\n", " \n", "
\n", "\n", " \\[Mac OS X\\] Use Spotlight metadata to determine audio length.\n", "\n", "
\n", "- [\\#**escape_glob**(glob) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#escape_glob-instance_method \"#escape_glob (instance method)\")\n", " \n", "
\n", "\n", " Escape characters that have special meaning as a part of path global\n", " patterns.\n", "\n", "
\n", "- [\\#**files**(globs) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#files-instance_method \"#files (instance method)\")\n", " \n", "
\n", "\n", " Give only files matching the given glob.\n", "\n", "
\n", "- [\\#**find**(path, &block) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#find-instance_method \"#find (instance method)\")\n", " \n", "
\n", "\n", " Find matching files, akin to the Unix utility `find`.\n", "\n", "
\n", "- [\\#**git_piston**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#git_piston-instance_method \"#git_piston (instance method)\")\n", " deprecated **Deprecated.** \n", "
\n", "\n", "
\n", "- [\\#**ignore_child_dirs**(arr) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#ignore_child_dirs-instance_method \"#ignore_child_dirs (instance method)\")\n", " \n", "
\n", "\n", " Given an array of directories, return a new array without any child\n", " directories whose parent is already present in that array.\n", "\n", "
\n", "- [\\#**last_accessed**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#last_accessed-instance_method \"#last_accessed (instance method)\")\n", " deprecated **Deprecated.** \n", "
\n", "\n", "
\n", "- [\\#**locate**(name) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#locate-instance_method \"#locate (instance method)\")\n", " \n", "
\n", "\n", " \\[Mac OS X\\] Use Spotlight to locate all files matching the given\n", " filename.\n", "\n", "
\n", "- [\\#**location_city**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#location_city-instance_method \"#location_city (instance method)\")\n", " \n", "
\n", "\n", " Determine the city of the given JPEG image.\n", "\n", "
\n", "- [\\#**media_type**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#media_type-instance_method \"#media_type (instance method)\")\n", " \n", "
\n", "\n", " Get the Internet media type of the file.\n", "\n", "
\n", "- [\\#**mime_type**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#mime_type-instance_method \"#mime_type (instance method)\")\n", " \n", "
\n", "\n", " Get the MIME type of the file.\n", "\n", "
\n", "- [\\#**mkdir**(path, options = {}) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#mkdir-instance_method \"#mkdir (instance method)\")\n", " \n", "
\n", "\n", " Create a directory and all of its parent directories.\n", "\n", "
\n", "- [\\#**modified_at**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#modified_at-instance_method \"#modified_at (instance method)\")\n", " \n", "
\n", "\n", " Get the modification time of a file.\n", "\n", "
\n", "- [\\#**move**(sources, destination) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#move-instance_method \"#move (instance method)\")\n", " \n", "
\n", "\n", " Move `sources` to a `destination` directory.\n", "\n", "
\n", "- [\\#**newest_dupes_in**(globs) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#newest_dupes_in-instance_method \"#newest_dupes_in (instance method)\")\n", " \n", "
\n", "\n", " Convenience method that is like `dupes_in` but excludes the oldest\n", " dupe.\n", "\n", "
\n", "- [\\#**remove**(paths, options = {})\n", " ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#remove-instance_method \"#remove (instance method)\")\n", " \n", "
\n", "\n", " Delete the files at the given path recursively.\n", "\n", "
\n", "- [\\#**rename**(source, destination)\n", " ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#rename-instance_method \"#rename (instance method)\")\n", " \n", "
\n", "\n", " Rename a single file.\n", "\n", "
\n", "- [\\#**size_of**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#size_of-instance_method \"#size_of (instance method)\")\n", " \n", "
\n", "\n", " Get the size of a file.\n", "\n", "
\n", "- \n", " [\\#**spotlight_content_types**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#spotlight_content_types-instance_method \"#spotlight_content_types (instance method)\")\n", " \n", "
\n", "\n", " \\[Mac OS X\\] Use Spotlight metadata to determine which content types\n", " a file has.\n", "\n", "
\n", "- [\\#**sync**(from, to, options = {})\n", " ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#sync-instance_method \"#sync (instance method)\")\n", " \n", "
\n", "\n", " Simple sync two files/folders using `rsync`.\n", "\n", "
\n", "- [\\#**trash**(paths, options = {}) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#trash-instance_method \"#trash (instance method)\")\n", " \n", "
\n", "\n", " Move the given paths to the user's trash.\n", "\n", "
\n", "- [\\#**tree_empty?**(root) ⇒\n", " Boolean](/gems/maid/0.7.0/Maid/Tools#tree_empty%3F-instance_method \"#tree_empty? (instance method)\")\n", " \n", "
\n", "\n", " Test whether a directory is either empty, or contains only empty\n", " directories/subdirectories.\n", "\n", "
\n", "- [\\#**verbose_dupes_in**(globs) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#verbose_dupes_in-instance_method \"#verbose_dupes_in (instance method)\")\n", " \n", "
\n", "\n", " Convenience method for `dupes_in` that excludes the dupe with the\n", " shortest name.\n", "\n", "
\n", "- [\\#**where_content_type**(paths,\n", " filter_types) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#where_content_type-instance_method \"#where_content_type (instance method)\")\n", " \n", "
\n", "\n", " Filter an array by content types.\n", "\n", "
\n", "- [\\#**zipfile_contents**(path) ⇒\n", " Object](/gems/maid/0.7.0/Maid/Tools#zipfile_contents-instance_method \"#zipfile_contents (instance method)\")\n", " \n", "
\n", "\n", " List the contents of a zip file.\n", "\n", "
\n", "\n", "
\n", "\n", "## Instance Method Details\n", "\n", "
\n", "\n", "### permalink \\#**accessed_at**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get the time that a file was last accessed.\n", "\n", "In Unix speak, `atime`.\n", "\n", "## Examples\n", "\n", "``` code\n", "accessed_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
519\n",
    "520\n",
    "521
# File 'lib/maid/tools.rb', line 519\n",
    "\n",
    "def accessed_at(path)\n",
    "  File.atime(expand(path))\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**checksum_of**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get a checksum for a file.\n", "\n", "## Examples\n", "\n", "``` code\n", "checksum_of('foo.zip') # => \"67258d750ca654d5d3c7b06bd2a1c792ced2003e\"\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
557\n",
    "558\n",
    "559
# File 'lib/maid/tools.rb', line 557\n",
    "\n",
    "def checksum_of(path)\n",
    "  Digest::SHA1.hexdigest(File.read(path))\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**content_types**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get the content types of a path.\n", "\n", "Content types can be MIME types, Internet media types or Spotlight\n", "content types (OS X only).\n", "\n", "## Examples\n", "\n", "``` code\n", "content_types('foo.zip') # => [\"public.zip-archive\", \"com.pkware.zip-archive\", \"public.archive\", \"application/zip\", \"application\"]\n", "content_types('bar.jpg') # => [\"public.jpeg\", \"public.image\", \"image/jpeg\", \"image\"]\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
647\n",
    "648\n",
    "649
# File 'lib/maid/tools.rb', line 647\n",
    "\n",
    "def content_types(path)\n",
    "  [spotlight_content_types(path), mime_type(path), media_type(path)].flatten\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**copy**(sources, destination) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Copy from `sources` to `destination`\n", "\n", "The path is not copied if a file already exists at the destination with\n", "the same name. A warning is logged instead. Note: Similar functionality\n", "is provided by the sync tool, but this requires installation of the\n", "`rsync` binary\n", "\n", "## Examples\n", "\n", "Single path:\n", "\n", "``` code\n", "copy('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/')\n", "```\n", "\n", "Multiple paths:\n", "\n", "``` code\n", "copy(['~/Downloads/foo.zip', '~/Downloads/bar.zip'], '~/Archive/Software/Mac OS X/')\n", "copy(dir('~/Downloads/*.zip'), '~/Archive/Software/Mac OS X/')\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
163\n",
    "164\n",
    "165\n",
    "166\n",
    "167\n",
    "168\n",
    "169\n",
    "170\n",
    "171\n",
    "172\n",
    "173\n",
    "174\n",
    "175\n",
    "176
# File 'lib/maid/tools.rb', line 163\n",
    "\n",
    "def copy(sources, destination)\n",
    "  destination = expand(destination)\n",
    "\n",
    "  expand_all(sources).each do |source|\n",
    "      target = File.join(destination, File.basename(source))\n",
    "\n",
    "    unless File.exist?(target)\n",
    "      log("cp #{ sh_escape(source) } #{ sh_escape(destination) }")\n",
    "      FileUtils.cp(source, destination, @file_options)\n",
    "    else\n",
    "      warn("skipping copy because #{ sh_escape(source) } because #{ sh_escape(target) } already exists")\n",
    "    end\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**created_at**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get the creation time of a file.\n", "\n", "In Unix speak, `ctime`.\n", "\n", "## Examples\n", "\n", "``` code\n", "created_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
508\n",
    "509\n",
    "510
# File 'lib/maid/tools.rb', line 508\n",
    "\n",
    "def created_at(path)\n",
    "  File.ctime(expand(path))\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**dimensions_px**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Determine the dimensions of GIF, PNG, JPEG, or TIFF images.\n", "\n", "Value returned is \\[width, height\\].\n", "\n", "## Examples\n", "\n", "``` code\n", "dimensions_px('image.jpg') # => [1024, 768]\n", "width, height = dimensions_px('image.jpg')\n", "dimensions_px('image.jpg').join('x') # => \"1024x768\"\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
442\n",
    "443\n",
    "444
# File 'lib/maid/tools.rb', line 442\n",
    "\n",
    "def dimensions_px(path)\n",
    "  Dimensions.dimensions(path)\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**dir**(globs) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Give all files matching the given glob.\n", "\n", "Note that the globs are *not* regexps (they're closer to shell globs).\n", "However, some regexp-like notation can be used, e.g. `?`, `[a-z]`,\n", "`{tgz,zip}`. For more details, see Ruby's documentation on `Dir.glob`.\n", "\n", "The matches are sorted lexically to aid in readability when using\n", "`--dry-run`.\n", "\n", "## Examples\n", "\n", "Single glob:\n", "\n", "``` code\n", "dir('~/Downloads/*.zip')\n", "```\n", "\n", "Specifying multiple extensions succinctly:\n", "\n", "``` code\n", "dir('~/Downloads/*.{exe,deb,dmg,pkg,rpm}')\n", "```\n", "\n", "Multiple glob (all are equivalent):\n", "\n", "``` code\n", "dir(['~/Downloads/*.zip', '~/Dropbox/*.zip'])\n", "dir(%w(~/Downloads/*.zip ~/Dropbox/*.zip))\n", "dir('~/{Downloads,Dropbox}/*.zip')\n", "```\n", "\n", "Recursing into subdirectories (see also: `find`):\n", "\n", "``` code\n", "dir('~/Music/**/*.m4a')\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
240\n",
    "241\n",
    "242\n",
    "243\n",
    "244\n",
    "245
# File 'lib/maid/tools.rb', line 240\n",
    "\n",
    "def dir(globs)\n",
    "  expand_all(globs).\n",
    "    map { |glob| Dir.glob(glob) }.\n",
    "    flatten.\n",
    "    sort\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**dir_safe**(globs) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Same as `dir`, but excludes files that are (possibly) being downloaded.\n", "\n", "## Example\n", "\n", "Move Debian/Ubuntu packages that are finished downloading into a\n", "software directory.\n", "\n", "``` code\n", "move dir_safe('~/Downloads/*.deb'), '~/Archive/Software'\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
256\n",
    "257\n",
    "258\n",
    "259
# File 'lib/maid/tools.rb', line 256\n",
    "\n",
    "def dir_safe(globs)\n",
    "  dir(globs).\n",
    "    reject { |path| downloading?(path) }\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**disk_usage**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Calculate disk usage of a given path in kilobytes.\n", "\n", "See also: `Maid::NumericExtensions::SizeToKb`.\n", "\n", "## Examples\n", "\n", "``` code\n", "disk_usage('foo.zip') # => 136\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
489\n",
    "490\n",
    "491\n",
    "492\n",
    "493\n",
    "494\n",
    "495\n",
    "496\n",
    "497\n",
    "498\n",
    "499
# File 'lib/maid/tools.rb', line 489\n",
    "\n",
    "def disk_usage(path)\n",
    "  raw = cmd("du -s #{ sh_escape(path) }")\n",
    "  # FIXME: This reports in kilobytes, but should probably report in bytes.\n",
    "  usage_kb = raw.split(/\\s+/).first.to_i\n",
    "\n",
    "  if usage_kb.zero?\n",
    "    raise "Stopping pessimistically because of unexpected value from du (#{ raw.inspect })"\n",
    "  else\n",
    "    usage_kb\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**downloaded_from**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "\\[Mac OS X\\] Use Spotlight metadata to determine the site from which a\n", "file was downloaded.\n", "\n", "## Examples\n", "\n", "``` code\n", "downloaded_from('foo.zip') # => ['http://www.site.com/foo.zip', 'http://www.site.com/']\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
355\n",
    "356\n",
    "357
# File 'lib/maid/tools.rb', line 355\n",
    "\n",
    "def downloaded_from(path)\n",
    "  mdls_to_array(path, 'kMDItemWhereFroms')\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**downloading?**(path) ⇒ Boolean\n", "\n", "
\n", "\n", "
\n", "\n", "Detect whether the path is currently being downloaded in Chrome, Firefox\n", "or Safari.\n", "\n", "See also: `dir_safe`\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "Returns:\n", "\n", "- (Boolean)\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
362\n",
    "363\n",
    "364
# File 'lib/maid/tools.rb', line 362\n",
    "\n",
    "def downloading?(path)\n",
    "  !!(chrome_downloading?(path) || firefox_downloading?(path) || safari_downloading?(path))\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**dupes_in**(globs) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Find all duplicate files in the given globs.\n", "\n", "More often than not, you'll want to use `newest_dupes_in` or\n", "`verbose_dupes_in` instead of using this method directly.\n", "\n", "Globs are expanded as in `dir`, then all non-files are filtered out. The\n", "remaining files are compared by size, and non-dupes are filtered out.\n", "The remaining candidates are then compared by checksum. Dupes are\n", "returned as an array of arrays.\n", "\n", "## Examples\n", "\n", "``` code\n", "dupes_in('~/{Downloads,Desktop}/*') # => [\n", " ['~/Downloads/foo.zip', '~/Downloads/foo (1).zip'],\n", " ['~/Desktop/bar.txt', '~/Desktop/bar copy.txt']\n", " ]\n", "```\n", "\n", "Keep the newest dupe:\n", "\n", "``` code\n", "dupes_in('~/Desktop/*', '~/Downloads/*').each do |dupes|\n", " trash dupes.sort_by { |p| File.mtime(p) }[0..-2]\n", "end\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
389\n",
    "390\n",
    "391\n",
    "392\n",
    "393\n",
    "394\n",
    "395\n",
    "396\n",
    "397\n",
    "398\n",
    "399\n",
    "400\n",
    "401
# File 'lib/maid/tools.rb', line 389\n",
    "\n",
    "def dupes_in(globs)\n",
    "  dupes = []\n",
    "  files(globs).                           # Start by filtering out non-files\n",
    "    group_by { |f| size_of(f) }.          # ... then grouping by size, since that's fast\n",
    "    reject { |s, p| p.length < 2 }.       # ... and filter out any non-dupes\n",
    "    map do |size, candidates|\n",
    "      dupes += candidates.\n",
    "        group_by { |p| checksum_of(p) }.  # Now group our candidates by a slower checksum calculation\n",
    "        reject { |c, p| p.length < 2 }.   # ... and filter out any non-dupes\n",
    "        values\n",
    "    end\n",
    "  dupes\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**duration_s**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "\\[Mac OS X\\] Use Spotlight metadata to determine audio length.\n", "\n", "## Examples\n", "\n", "``` code\n", "duration_s('foo.mp3') # => 235.705\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
466\n",
    "467\n",
    "468
# File 'lib/maid/tools.rb', line 466\n",
    "\n",
    "def duration_s(path)\n",
    "  cmd("mdls -raw -name kMDItemDurationSeconds #{ sh_escape(path) }").to_f\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**escape_glob**(glob) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Escape characters that have special meaning as a part of path global\n", "patterns.\n", "\n", "Useful when using `dir` with file names that may contain `{ } [ ]`\n", "characters.\n", "\n", "## Example\n", "\n", "``` code\n", "escape_glob('test [tmp]') # => 'test \\\\[tmp\\\\]'\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
277\n",
    "278\n",
    "279
# File 'lib/maid/tools.rb', line 277\n",
    "\n",
    "def escape_glob(glob)\n",
    "  glob.gsub(/[\\{\\}\\[\\]]/) { |s| '\\\\' + s }\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**files**(globs) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Give only files matching the given glob.\n", "\n", "This is the same as `dir` but only includes actual files (no directories\n", "or symlinks).\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
265\n",
    "266\n",
    "267\n",
    "268
# File 'lib/maid/tools.rb', line 265\n",
    "\n",
    "def files(globs)\n",
    "  dir(globs).\n",
    "    select { |f| File.file?(f) }\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**find**(path, &block) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Find matching files, akin to the Unix utility `find`.\n", "\n", "If no block is given, it will return an array. Otherwise, it acts like\n", "`Find.find`.\n", "\n", "## Examples\n", "\n", "Without a block:\n", "\n", "``` code\n", "find('~/Downloads/') # => [...]\n", "```\n", "\n", "Recursing and filtering using a regular expression:\n", "\n", "``` code\n", "find('~/Downloads/').grep(/\\.pdf$/)\n", "```\n", "\n", "(**Note:** It's just Ruby, so any methods in `Array` and `Enumerable`\n", "can be used.)\n", "\n", "Recursing with a block:\n", "\n", "``` code\n", "find('~/Downloads/') do |path|\n", " # ...\n", "end\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
329\n",
    "330\n",
    "331\n",
    "332\n",
    "333\n",
    "334\n",
    "335\n",
    "336\n",
    "337
# File 'lib/maid/tools.rb', line 329\n",
    "\n",
    "def find(path, &block)\n",
    "  expanded_path = expand(path)\n",
    "\n",
    "  if block.nil?\n",
    "    Find.find(expanded_path).to_a\n",
    "  else\n",
    "    Find.find(expanded_path, &block)\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**git_piston**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "**Deprecated.**\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "Pull and push the `git` repository at the given path.\n", "\n", "Since this is deprecated, you might also be interested in\n", "[SparkleShare](http://sparkleshare.org/), a great `git`-based file\n", "syncronization project.\n", "\n", "## Examples\n", "\n", "``` code\n", "git_piston('~/code/projectname')\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
571\n",
    "572\n",
    "573\n",
    "574\n",
    "575
# File 'lib/maid/tools.rb', line 571\n",
    "\n",
    "def git_piston(path)\n",
    "  full_path = expand(path)\n",
    "  stdout = cmd("cd #{ sh_escape(full_path) } && git pull && git push 2>&1")\n",
    "  log("Fired git piston on #{ sh_escape(full_path) }.  STDOUT:\\n\\n#{ stdout }")\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**ignore_child_dirs**(arr) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Given an array of directories, return a new array without any child\n", "directories whose parent is already present in that array.\n", "\n", "## Example\n", "\n", "``` code\n", "ignore_child_dirs([\"foo\", \"foo/a\", \"foo/b\", \"bar\"]) # => [\"foo\", \"bar\"]\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
749\n",
    "750\n",
    "751\n",
    "752\n",
    "753\n",
    "754\n",
    "755
# File 'lib/maid/tools.rb', line 749\n",
    "\n",
    "def ignore_child_dirs(arr)\n",
    "  arr.sort { |x, y|\n",
    "    y.count('/') - x.count('/')\n",
    "  }.select { |d|\n",
    "    !arr.include?(File.dirname(d))\n",
    "  }\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**last_accessed**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "**Deprecated.**\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "Alias of `accessed_at`.\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
526\n",
    "527\n",
    "528\n",
    "529
# File 'lib/maid/tools.rb', line 526\n",
    "\n",
    "def last_accessed(path)\n",
    "  # Not a normal `alias` so the deprecation notice shows in the docs.\n",
    "  accessed_at(path)\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**locate**(name) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "\\[Mac OS X\\] Use Spotlight to locate all files matching the given\n", "filename.\n", "\n", "\\[Ubuntu\\] Use `locate` to locate all files matching the given filename.\n", "\n", "## Examples\n", "\n", "``` code\n", "locate('foo.zip') # => ['/a/foo.zip', '/b/foo.zip']\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
346\n",
    "347\n",
    "348
# File 'lib/maid/tools.rb', line 346\n",
    "\n",
    "def locate(name)\n",
    "  cmd("#{Maid::Platform::Commands.locate} #{ sh_escape(name) }").split("\\n")\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**location_city**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Determine the city of the given JPEG image.\n", "\n", "## Examples\n", "\n", "``` code\n", "loation_city('old_capitol.jpg') # => \"Iowa City, IA, US\"\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
451\n",
    "452\n",
    "453\n",
    "454\n",
    "455\n",
    "456\n",
    "457\n",
    "458\n",
    "459
# File 'lib/maid/tools.rb', line 451\n",
    "\n",
    "def location_city(path)\n",
    "  case mime_type(path)\n",
    "  when 'image/jpeg'\n",
    "    gps = EXIFR::JPEG.new(path).gps\n",
    "    coordinates_string = [gps.latitude, gps.longitude]\n",
    "    location = Geocoder.search(coordinates_string).first\n",
    "    [location.city, location.province, location.country_code].join(', ')\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**media_type**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get the Internet media type of the file.\n", "\n", "In other words, the first part of `mime_type`.\n", "\n", "## Examples\n", "\n", "``` code\n", "media_type('bar.jpg') # => \"image\"\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
671\n",
    "672\n",
    "673\n",
    "674\n",
    "675\n",
    "676\n",
    "677
# File 'lib/maid/tools.rb', line 671\n",
    "\n",
    "def media_type(path)\n",
    "  type = MIME::Types.type_for(path)[0]\n",
    "\n",
    "  if type\n",
    "    type.media_type\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**mime_type**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get the MIME type of the file.\n", "\n", "## Examples\n", "\n", "``` code\n", "mime_type('bar.jpg') # => \"image/jpeg\"\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
656\n",
    "657\n",
    "658\n",
    "659\n",
    "660\n",
    "661\n",
    "662
# File 'lib/maid/tools.rb', line 656\n",
    "\n",
    "def mime_type(path)\n",
    "  type = MIME::Types.type_for(path)[0]\n",
    "\n",
    "  if type\n",
    "    [type.media_type, type.sub_type].join('/')\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**mkdir**(path, options = {}) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Create a directory and all of its parent directories.\n", "\n", "The path of the created directory is returned, which allows for chaining\n", "(see examples).\n", "\n", "## Options\n", "\n", "`:mode`\n", "\n", "The symbolic and absolute mode can both be used, for example: `0700`,\n", "`'u=wr,go=rr'`\n", "\n", "## Examples\n", "\n", "Creating a directory with a specific mode:\n", "\n", "``` code\n", "mkdir('~/Music/Pink Floyd/', :mode => 0644)\n", "```\n", "\n", "Ensuring a directory exists when moving:\n", "\n", "``` code\n", "move('~/Downloads/Pink Floyd*.mp3', mkdir('~/Music/Pink Floyd/'))\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
300\n",
    "301\n",
    "302\n",
    "303\n",
    "304\n",
    "305
# File 'lib/maid/tools.rb', line 300\n",
    "\n",
    "def mkdir(path, options = {})\n",
    "  path = expand(path)\n",
    "  log("mkdir -p #{ sh_escape(path) }")\n",
    "  FileUtils.mkdir_p(path, @file_options.merge(options))\n",
    "  path\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**modified_at**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get the modification time of a file.\n", "\n", "In Unix speak, `mtime`.\n", "\n", "## Examples\n", "\n", "``` code\n", "modified_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
539\n",
    "540\n",
    "541
# File 'lib/maid/tools.rb', line 539\n",
    "\n",
    "def modified_at(path)\n",
    "  File.mtime(expand(path))\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**move**(sources, destination) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Move `sources` to a `destination` directory.\n", "\n", "Movement is only allowed to directories that already exist. If your\n", "intention is to rename, see the `rename` method.\n", "\n", "## Examples\n", "\n", "Single path:\n", "\n", "``` code\n", "move('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/')\n", "```\n", "\n", "Multiple paths:\n", "\n", "``` code\n", "move(['~/Downloads/foo.zip', '~/Downloads/bar.zip'], '~/Archive/Software/Mac OS X/')\n", "move(dir('~/Downloads/*.zip'), '~/Archive/Software/Mac OS X/')\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
39\n",
    "40\n",
    "41\n",
    "42\n",
    "43\n",
    "44\n",
    "45\n",
    "46\n",
    "47\n",
    "48\n",
    "49\n",
    "50\n",
    "51
# File 'lib/maid/tools.rb', line 39\n",
    "\n",
    "def move(sources, destination)\n",
    "  destination = expand(destination)\n",
    "\n",
    "  if File.directory?(destination)\n",
    "    expand_all(sources).each do |source|\n",
    "      log("move #{ sh_escape(source) } #{ sh_escape(destination) }")\n",
    "      FileUtils.mv(source, destination, @file_options)\n",
    "    end\n",
    "  else\n",
    "    # Unix `mv` warns about the target not being a directory with multiple sources.  Maid checks the same.\n",
    "    warn("skipping move because #{ sh_escape(destination) } is not a directory (use 'mkdir' to create first, or use 'rename')")\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**newest_dupes_in**(globs) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Convenience method that is like `dupes_in` but excludes the oldest dupe.\n", "\n", "## Example\n", "\n", "Keep the oldest dupe (trash the others):\n", "\n", "``` code\n", "trash newest_dupes_in('~/Downloads/*')\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
411\n",
    "412\n",
    "413\n",
    "414\n",
    "415
# File 'lib/maid/tools.rb', line 411\n",
    "\n",
    "def newest_dupes_in(globs)\n",
    "  dupes_in(globs).\n",
    "    map { |dupes| dupes.sort_by { |p| File.mtime(p) }[1..-1] }.\n",
    "    flatten\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**remove**(paths, options = {}) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Delete the files at the given path recursively.\n", "\n", "**NOTE**: In most cases, `trash` is a safer choice, since the files will\n", "be recoverable by retreiving them from the trash. Once you delete a file\n", "using `remove`, it's gone! Please use `trash` whenever possible and only\n", "use `remove` when necessary.\n", "\n", "## Options\n", "\n", "`:force => boolean`\n", "\n", "Force deletion (no error is raised if the file does not exist).\n", "\n", "`:secure => boolean`\n", "\n", "Infrequently needed. See\n", "[`FileUtils.remove_entry_secure`](https://www.ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure)\n", "\n", "## Examples\n", "\n", "Single path:\n", "\n", "``` code\n", "remove('~/Downloads/foo.zip')\n", "```\n", "\n", "Multiple path:\n", "\n", "``` code\n", "remove(['~/Downloads/foo.zip', '~/Downloads/bar.zip'])\n", "remove(dir('~/Downloads/*.zip'))\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
204\n",
    "205\n",
    "206\n",
    "207\n",
    "208\n",
    "209\n",
    "210\n",
    "211
# File 'lib/maid/tools.rb', line 204\n",
    "\n",
    "def remove(paths, options = {})\n",
    "  expand_all(paths).each do |path|\n",
    "    options = @file_options.merge(options)\n",
    "\n",
    "    log("Removing #{ sh_escape(path) }")\n",
    "    FileUtils.rm_r(path, options)\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**rename**(source, destination) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Rename a single file.\n", "\n", "Any directories needed in order to complete the rename are made\n", "automatically.\n", "\n", "Overwriting is not allowed; it logs a warning. If overwriting is\n", "desired, use `remove` to delete the file first, then use `rename`.\n", "\n", "## Examples\n", "\n", "Simple rename:\n", "\n", "``` code\n", "rename('foo.zip', 'baz.zip') # \"foo.zip\" becomes \"baz.zip\"\n", "```\n", "\n", "Rename needing directories:\n", "\n", "``` code\n", "rename('foo.zip', 'bar/baz.zip') # \"bar\" is created, \"foo.zip\" becomes \"baz.zip\" within \"bar\"\n", "```\n", "\n", "Attempting to overwrite:\n", "\n", "``` code\n", "rename('foo.zip', 'existing.zip') # \"skipping move of...\"\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
72\n",
    "73\n",
    "74\n",
    "75\n",
    "76\n",
    "77\n",
    "78\n",
    "79\n",
    "80\n",
    "81\n",
    "82\n",
    "83\n",
    "84
# File 'lib/maid/tools.rb', line 72\n",
    "\n",
    "def rename(source, destination)\n",
    "  source = expand(source)\n",
    "  destination = expand(destination)\n",
    "\n",
    "  mkdir(File.dirname(destination))\n",
    "\n",
    "  if File.exist?(destination)\n",
    "    warn("skipping rename of #{ sh_escape(source) } to #{ sh_escape(destination) } because it would overwrite")\n",
    "  else\n",
    "    log("rename #{ sh_escape(source) } #{ sh_escape(destination) }")\n",
    "    FileUtils.mv(source, destination, @file_options)\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**size_of**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Get the size of a file.\n", "\n", "## Examples\n", "\n", "``` code\n", "size_of('foo.zip') # => 2193\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
548\n",
    "549\n",
    "550
# File 'lib/maid/tools.rb', line 548\n",
    "\n",
    "def size_of(path)\n",
    "  File.size(path)\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**spotlight_content_types**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "\\[Mac OS X\\] Use Spotlight metadata to determine which content types a\n", "file has.\n", "\n", "## Examples\n", "\n", "``` code\n", "spotlight_content_types('foo.zip') # => ['public.zip-archive', 'public.archive']\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
635\n",
    "636\n",
    "637
# File 'lib/maid/tools.rb', line 635\n",
    "\n",
    "def spotlight_content_types(path)\n",
    "  mdls_to_array(path, 'kMDItemContentTypeTree')\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**sync**(from, to, options = {}) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Simple sync two files/folders using `rsync`.\n", "\n", "The host OS must provide `rsync`. See the `rsync` man page for a\n", "detailed description.\n", "\n", "``` code\n", "man rsync\n", "```\n", "\n", "## Options\n", "\n", "`:delete => boolean` `:verbose => boolean` `:archive => boolean`\n", "(default `true`) `:update => boolean` (default `true`)\n", "`:exclude => string` `:prune_empty => boolean`\n", "\n", "## Examples\n", "\n", "Syncing a directory to a backup:\n", "\n", "``` code\n", "sync('~/music', '/backup/music')\n", "```\n", "\n", "Excluding a path:\n", "\n", "``` code\n", "sync('~/code', '/backup/code', :exclude => '.git')\n", "```\n", "\n", "Excluding multiple paths:\n", "\n", "``` code\n", "sync('~/code', '/backup/code', :exclude => ['.git', '.rvmrc'])\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
607\n",
    "608\n",
    "609\n",
    "610\n",
    "611\n",
    "612\n",
    "613\n",
    "614\n",
    "615\n",
    "616\n",
    "617\n",
    "618\n",
    "619\n",
    "620\n",
    "621\n",
    "622\n",
    "623\n",
    "624\n",
    "625\n",
    "626\n",
    "627\n",
    "628
# File 'lib/maid/tools.rb', line 607\n",
    "\n",
    "def sync(from, to, options = {})\n",
    "  # expand removes trailing slash\n",
    "  # cannot use str[-1] due to ruby 1.8.7 restriction\n",
    "  from = expand(from) + (from.end_with?('/') ? '/' : '')\n",
    "  to = expand(to) + (to.end_with?('/') ? '/' : '')\n",
    "  # default options\n",
    "  options = { :archive => true, :update => true }.merge(options)\n",
    "  ops = []\n",
    "  ops << '-a' if options[:archive]\n",
    "  ops << '-v' if options[:verbose]\n",
    "  ops << '-u' if options[:update]\n",
    "  ops << '-m' if options[:prune_empty]\n",
    "  ops << '-n' if @file_options[:noop]\n",
    "\n",
    "  Array(options[:exclude]).each do |path|\n",
    "    ops << "--exclude=#{ sh_escape(path) }"\n",
    "  end\n",
    "\n",
    "  ops << '--delete' if options[:delete]\n",
    "  stdout = cmd("rsync #{ ops.join(' ') } #{ sh_escape(from) } #{ sh_escape(to) } 2>&1")\n",
    "  log("Fired sync from #{ sh_escape(from) } to #{ sh_escape(to) }.  STDOUT:\\n\\n#{ stdout }")\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**trash**(paths, options = {}) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Move the given paths to the user's trash.\n", "\n", "The path is still moved if a file already exists in the trash with the\n", "same name. However, the current date and time is appended to the\n", "filename.\n", "\n", "**Note:** the OS-native \"restore\" or \"put back\" functionality for\n", "trashed files is not currently supported. (See [issue\n", "\\#63](https://github.com/benjaminoakes/maid/issues/63).) However, they\n", "can be restored manually, and the Maid log can help assist with this.\n", "\n", "## Options\n", "\n", "`:remove_over => Fixnum` (e.g. `1.gigabyte`, `1024.megabytes`)\n", "\n", "Delete files over the given size rather than moving to the trash.\n", "\n", "See also `Maid::NumericExtensions::SizeToKb`\n", "\n", "## Examples\n", "\n", "Single path:\n", "\n", "``` code\n", "trash('~/Downloads/foo.zip')\n", "```\n", "\n", "Multiple paths:\n", "\n", "``` code\n", "trash(['~/Downloads/foo.zip', '~/Downloads/bar.zip'])\n", "trash(dir('~/Downloads/*.zip'))\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
113\n",
    "114\n",
    "115\n",
    "116\n",
    "117\n",
    "118\n",
    "119\n",
    "120\n",
    "121\n",
    "122\n",
    "123\n",
    "124\n",
    "125\n",
    "126\n",
    "127\n",
    "128\n",
    "129\n",
    "130\n",
    "131\n",
    "132\n",
    "133\n",
    "134\n",
    "135\n",
    "136\n",
    "137\n",
    "138\n",
    "139\n",
    "140\n",
    "141\n",
    "142\n",
    "143\n",
    "144\n",
    "145\n",
    "146\n",
    "147
# File 'lib/maid/tools.rb', line 113\n",
    "\n",
    "def trash(paths, options = {})\n",
    "  # ## Implementation Notes\n",
    "  #\n",
    "  # Trashing files correctly is surprisingly hard.  What Maid ends up doing is one the easiest, most foolproof\n",
    "  # solutions:  moving the file.\n",
    "  #\n",
    "  # Unfortunately, that means it's not possile to restore files automatically in OSX or Ubuntu.  The previous location\n",
    "  # of the file is lost.\n",
    "  #\n",
    "  # OSX support depends on AppleScript or would require a not-yet-written C extension to interface with the OS.  The\n",
    "  # AppleScript solution is less than ideal: the user has to be logged in, Finder has to be running, and it makes the\n",
    "  # "trash can sound" every time a file is moved.\n",
    "  #\n",
    "  # Ubuntu makes it easy to implement, and there's a Python library for doing so (see `trash-cli`).  However, there's\n",
    "  # not a Ruby equivalent yet.\n",
    "\n",
    "  expand_all(paths).each do |path|\n",
    "    target = File.join(@trash_path, File.basename(path))\n",
    "    safe_trash_path = File.join(@trash_path, "#{ File.basename(path) } #{ Time.now.strftime('%Y-%m-%d-%H-%M-%S') }")\n",
    "\n",
    "    if options[:remove_over] &&\n",
    "        File.exist?(path) &&\n",
    "        disk_usage(path) > options[:remove_over]\n",
    "      remove(path)\n",
    "    end\n",
    "\n",
    "    if File.exist?(path)\n",
    "      if File.exist?(target)\n",
    "        rename(path, safe_trash_path)\n",
    "      else\n",
    "        move(path, @trash_path)\n",
    "      end\n",
    "    end\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**tree_empty?**(root) ⇒ Boolean\n", "\n", "
\n", "\n", "
\n", "\n", "Test whether a directory is either empty, or contains only empty\n", "directories/subdirectories.\n", "\n", "## Example\n", "\n", "``` code\n", "if tree_empty?(dir('~/Downloads/foo'))\n", " trash('~/Downloads/foo')\n", "end\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "Returns:\n", "\n", "- (Boolean)\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
714\n",
    "715\n",
    "716\n",
    "717\n",
    "718\n",
    "719\n",
    "720\n",
    "721\n",
    "722\n",
    "723\n",
    "724\n",
    "725\n",
    "726\n",
    "727\n",
    "728\n",
    "729\n",
    "730\n",
    "731\n",
    "732\n",
    "733\n",
    "734\n",
    "735\n",
    "736\n",
    "737\n",
    "738\n",
    "739\n",
    "740\n",
    "741
# File 'lib/maid/tools.rb', line 714\n",
    "\n",
    "def tree_empty?(root)\n",
    "  return nil if File.file?(root)\n",
    "  return true if Dir.glob(root + '/*').length == 0\n",
    "\n",
    "  ignore = []\n",
    "\n",
    "  # Look for files.\n",
    "  return false if Dir.glob(root + '/*').select { |f| File.file?(f) }.length > 0\n",
    "\n",
    "  empty_dirs = Dir.glob(root + '/**/*').select { |d|\n",
    "    File.directory?(d)\n",
    "  }.reverse.select { |d|\n",
    "    # `.reverse` sorts deeper directories first.\n",
    "\n",
    "    # If the directory is empty, its parent should ignore it.\n",
    "    should_ignore = Dir.glob(d + '/*').select { |n|\n",
    "      !ignore.include?(n)\n",
    "    }.length == 0\n",
    "\n",
    "    ignore << d if should_ignore\n",
    "\n",
    "    should_ignore\n",
    "  }\n",
    "\n",
    "  Dir.glob(root + '/*').select { |n|\n",
    "    !empty_dirs.include?(n)\n",
    "  }.length == 0\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**verbose_dupes_in**(globs) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Convenience method for `dupes_in` that excludes the dupe with the\n", "shortest name.\n", "\n", "This is ideal for dupes like `foo.zip`, `foo (1).zip`, `foo copy.zip`.\n", "\n", "## Example\n", "\n", "Keep the dupe with the shortest name (trash the others):\n", "\n", "``` code\n", "trash verbose_dupes_in('~/Downloads/*')\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
427\n",
    "428\n",
    "429\n",
    "430\n",
    "431
# File 'lib/maid/tools.rb', line 427\n",
    "\n",
    "def verbose_dupes_in(globs)\n",
    "  dupes_in(globs).\n",
    "    map { |dupes| dupes.sort_by { |p| File.basename(p).length }[1..-1] }.\n",
    "    flatten\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**where_content_type**(paths, filter_types) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "Filter an array by content types.\n", "\n", "Content types can be MIME types, internet media types or Spotlight\n", "content types (OS X only).\n", "\n", "If you need your rules to work on multiple platforms, it's recommended\n", "to avoid using Spotlight content types.\n", "\n", "## Examples\n", "\n", "### Using media types\n", "\n", "``` code\n", "where_content_type(dir('~/Downloads/*'), 'video')\n", "where_content_type(dir('~/Downloads/*'), ['image', 'audio'])\n", "```\n", "\n", "### Using MIME types\n", "\n", "``` code\n", "where_content_type(dir('~/Downloads/*'), 'image/jpeg')\n", "```\n", "\n", "### Using Spotlight content types\n", "\n", "Less portable, but richer data in some cases.\n", "\n", "``` code\n", "where_content_type(dir('~/Downloads/*'), 'public.image')\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
701\n",
    "702\n",
    "703\n",
    "704
# File 'lib/maid/tools.rb', line 701\n",
    "\n",
    "def where_content_type(paths, filter_types)\n",
    "  filter_types = Array(filter_types)\n",
    "  Array(paths).select { |p| !(filter_types & content_types(p)).empty? }\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "### permalink \\#**zipfile_contents**(path) ⇒ Object\n", "\n", "
\n", "\n", "
\n", "\n", "List the contents of a zip file.\n", "\n", "## Examples\n", "\n", "``` code\n", "zipfile_contents('foo.zip') # => ['foo.exe', 'README.txt', 'subdir/anything.txt']\n", "```\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\\[View source\\]\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
475\n",
    "476\n",
    "477\n",
    "478\n",
    "479\n",
    "480
# File 'lib/maid/tools.rb', line 475\n",
    "\n",
    "def zipfile_contents(path)\n",
    "  # It might be nice to use `glob` from `Zip::FileSystem`, but it seems buggy.  (Subdirectories aren't included.)\n",
    "  Zip::File.open(path) do |zip_file|\n",
    "    zip_file.entries.map { |entry| entry.name }.sort\n",
    "  end\n",
    "end
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "Generated on Mon Jul 4 19:46:58 2022 by\n", "yard 0.9.28 (ruby-3.0.2).\n", "\n", "
\n", "\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.12" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 }