maid 0.1.4.alpha.2 → 0.2.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/ChangeLog +17 -3
- data/Gemfile +1 -1
- data/Guardfile +2 -2
- data/README.md +1 -1
- data/Rakefile +12 -2
- data/lib/maid.rb +13 -11
- data/lib/maid/app.rb +15 -11
- data/lib/maid/maid.rb +15 -11
- data/lib/maid/tools.rb +170 -94
- data/lib/maid/user_agent.rb +21 -0
- data/lib/maid/version.rb +1 -1
- data/maid.gemspec +5 -2
- data/spec/dependency_spec.rb +38 -0
- data/spec/lib/maid/app_spec.rb +28 -8
- data/spec/lib/maid/maid_spec.rb +9 -9
- data/spec/lib/maid/numeric_extensions_spec.rb +6 -6
- data/spec/lib/maid/rule_spec.rb +1 -1
- data/spec/lib/maid/tools_spec.rb +14 -8
- data/spec/lib/maid/user_agent_spec.rb +27 -0
- data/spec/spec_helper.rb +2 -2
- metadata +62 -25
data/.gitignore
CHANGED
data/ChangeLog
CHANGED
@@ -1,7 +1,21 @@
|
|
1
|
-
maid (0.
|
1
|
+
maid (0.2.0.alpha.1) unstable; urgency=low
|
2
2
|
|
3
|
-
*
|
4
|
-
|
3
|
+
* Started semver.org-like version numbering. Total adoption is
|
4
|
+
forthcoming. This forced v0.2.0 vs. v0.1.4.
|
5
|
+
* Changed "dir" tool to always sort. (Closes: #62)
|
6
|
+
* Added "version --long" command which gives information about the platform
|
7
|
+
version and Ruby version. (Closes: #65)
|
8
|
+
* Improved user documentation, now in Markdown. (Closes: #66)
|
9
|
+
* Updated development dependencies.
|
10
|
+
* Various minor internal changes.
|
11
|
+
|
12
|
+
-- Benjamin Oakes <hello@benjaminoakes.com> Mon, 26 Nov 2012 14:51:13 +0000
|
13
|
+
|
14
|
+
maid (0.1.4.alpha.2) unstable; urgency=low
|
15
|
+
|
16
|
+
* Noted license in gemspec. Was incorrect value in alpha.1. Made an early
|
17
|
+
alpha release to test effect on RubyGems.org listing. (Closes: #61)
|
18
|
+
* Minor development-only documentation changes.
|
5
19
|
|
6
20
|
-- Benjamin Oakes <hello@benjaminoakes.com> Sat, 24 Nov 2012 00:00:00 +0000
|
7
21
|
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -3,6 +3,6 @@
|
|
3
3
|
|
4
4
|
guard 'rspec' do
|
5
5
|
watch(%r{^spec/.+_spec\.rb$})
|
6
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
-
watch('spec/spec_helper.rb') {
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{ m[1] }_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
8
8
|
end
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Be lazy! Let Maid clean up after you, based on rules you define.
|
4
4
|
|
5
|
-
[Installation](https://github.com/benjaminoakes/maid#installation) | [Tutorial](https://github.com/benjaminoakes/maid#tutorial) | [Documentation](http://rubydoc.info/gems/maid/Maid/Tools)
|
5
|
+
[Installation](https://github.com/benjaminoakes/maid#installation) | [Tutorial](https://github.com/benjaminoakes/maid#tutorial) | [Documentation](http://rubydoc.info/gems/maid/Maid/Tools) | [Change Log](https://github.com/benjaminoakes/maid/blob/master/ChangeLog)
|
6
6
|
|
7
7
|
Maid keeps files from sitting around too long, untouched. Many of the downloads and other files you collect can easily be categorized and handled appropriately by rules you define. Let the maid in your computer take care of the easy stuff, so you can spend more of your time on what matters.
|
8
8
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
require 'bundler'
|
1
2
|
require 'rake'
|
2
3
|
require 'rspec/core/rake_task'
|
3
|
-
require '
|
4
|
+
require 'yard'
|
4
5
|
|
5
6
|
task :default => :spec
|
6
7
|
|
@@ -8,6 +9,15 @@ Bundler::GemHelper.install_tasks
|
|
8
9
|
|
9
10
|
RSpec::Core::RakeTask.new(:spec)
|
10
11
|
|
12
|
+
YARD::Rake::YardocTask.new do |t|
|
13
|
+
t.files = %w(
|
14
|
+
lib/maid/app.rb
|
15
|
+
lib/maid/tools.rb
|
16
|
+
lib/maid/numeric_extensions.rb
|
17
|
+
)
|
18
|
+
t.options = %w(--no-private --markup markdown)
|
19
|
+
end
|
20
|
+
|
11
21
|
task :console do
|
12
|
-
sh
|
22
|
+
sh('irb -I lib -r maid')
|
13
23
|
end
|
data/lib/maid.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
require 'deprecated'
|
2
2
|
Deprecated.set_action(:warn)
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
# Must be in this order:
|
5
|
+
require 'maid/version'
|
6
|
+
require 'maid/tools'
|
7
|
+
require 'maid/maid'
|
8
|
+
|
9
|
+
# Alphabetical:
|
10
|
+
require 'maid/app'
|
11
|
+
require 'maid/numeric_extensions'
|
12
|
+
require 'maid/platform'
|
13
|
+
require 'maid/rule'
|
14
|
+
require 'maid/trash_migration'
|
15
|
+
require 'maid/user_agent'
|
13
16
|
|
17
|
+
module Maid
|
14
18
|
class << self
|
15
19
|
# Execute the block with the Maid instance set to <tt>instance</tt>.
|
16
20
|
def with_instance(instance)
|
@@ -31,5 +35,3 @@ class Numeric
|
|
31
35
|
include Maid::NumericExtensions::Time
|
32
36
|
include Maid::NumericExtensions::SizeToKb
|
33
37
|
end
|
34
|
-
|
35
|
-
# TODO Is there a no-conflict way of including the extensions?
|
data/lib/maid/app.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
3
|
require 'thor'
|
5
4
|
|
6
5
|
class Maid::App < Thor
|
@@ -12,9 +11,9 @@ class Maid::App < Thor
|
|
12
11
|
end
|
13
12
|
|
14
13
|
desc 'clean', 'Clean based on rules'
|
15
|
-
method_option :rules, :type => :string, :aliases => %w
|
16
|
-
method_option :noop, :type => :boolean, :aliases => %w
|
17
|
-
method_option :silent, :type => :boolean, :aliases => %w
|
14
|
+
method_option :rules, :type => :string, :aliases => %w(-r)
|
15
|
+
method_option :noop, :type => :boolean, :aliases => %w(-n --dry-run)
|
16
|
+
method_option :silent, :type => :boolean, :aliases => %w(-s)
|
18
17
|
def clean
|
19
18
|
maid = Maid::Maid.new(maid_options(options))
|
20
19
|
|
@@ -24,26 +23,31 @@ class Maid::App < Thor
|
|
24
23
|
end
|
25
24
|
|
26
25
|
unless options.silent? || options.noop?
|
27
|
-
say "Logging actions to #{maid.log_device.inspect}"
|
26
|
+
say "Logging actions to #{ maid.log_device.inspect }"
|
28
27
|
end
|
29
28
|
|
30
29
|
maid.load_rules
|
31
30
|
maid.clean
|
32
31
|
end
|
33
32
|
|
34
|
-
desc 'version', 'Print version
|
33
|
+
desc 'version', 'Print version information (optionally: system info)'
|
34
|
+
method_option :long, :type => :boolean, :aliases => %w(-l)
|
35
35
|
def version
|
36
|
-
|
36
|
+
if options.long?
|
37
|
+
say Maid::UserAgent.value
|
38
|
+
else
|
39
|
+
say Maid::VERSION
|
40
|
+
end
|
37
41
|
end
|
38
42
|
|
39
|
-
desc 'sample', "Create sample rules at #{self.sample_rules_path}"
|
43
|
+
desc 'sample', "Create sample rules at #{ self.sample_rules_path }"
|
40
44
|
def sample
|
41
45
|
path = self.class.sample_rules_path
|
42
46
|
|
43
47
|
FileUtils.mkdir_p(File.dirname(path))
|
44
48
|
File.open(path, 'w').puts(File.read(File.join(File.dirname(__FILE__), 'rules.sample.rb')))
|
45
49
|
|
46
|
-
say "Sample rules created at #{path.inspect}", :green
|
50
|
+
say "Sample rules created at #{ path.inspect }", :green
|
47
51
|
end
|
48
52
|
|
49
53
|
no_tasks do
|
@@ -52,12 +56,12 @@ class Maid::App < Thor
|
|
52
56
|
|
53
57
|
if options['noop']
|
54
58
|
# You're testing, so a simple log goes to STDOUT and no actions are taken
|
55
|
-
h[:file_options] = {:noop => true}
|
59
|
+
h[:file_options] = { :noop => true }
|
56
60
|
|
57
61
|
unless options['silent']
|
58
62
|
h[:logger] = false
|
59
63
|
h[:log_device] = STDOUT
|
60
|
-
h[:log_formatter] = lambda { |_, _, _, msg| "#{msg}\n" }
|
64
|
+
h[:log_formatter] = lambda { |_, _, _, msg| "#{ msg }\n" }
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
data/lib/maid/maid.rb
CHANGED
@@ -9,7 +9,7 @@ class Maid::Maid
|
|
9
9
|
:progname => 'Maid',
|
10
10
|
:log_device => File.expand_path('~/.maid/maid.log'),
|
11
11
|
:rules_path => File.expand_path('~/.maid/rules.rb'),
|
12
|
-
:file_options => {:noop => false}, # for FileUtils
|
12
|
+
:file_options => { :noop => false }, # for `FileUtils`
|
13
13
|
}.freeze
|
14
14
|
|
15
15
|
attr_reader :file_options, :logger, :log_device, :rules, :rules_path, :trash_path
|
@@ -19,7 +19,7 @@ class Maid::Maid
|
|
19
19
|
#
|
20
20
|
# Sane defaults for a log and trash path are set for Mac OS X, but they can easily be overridden like so:
|
21
21
|
#
|
22
|
-
#
|
22
|
+
# Maid::Maid.new(:log_device => '/home/username/log/maid.log', :trash_path => '/home/username/my_trash')
|
23
23
|
#
|
24
24
|
def initialize(options = {})
|
25
25
|
options = DEFAULTS.merge(options.reject { |k, v| v.nil? })
|
@@ -50,7 +50,7 @@ class Maid::Maid
|
|
50
50
|
# Start cleaning, based on the rules defined at rules_path.
|
51
51
|
def clean
|
52
52
|
unless @log_device.kind_of?(IO)
|
53
|
-
@logger.info "v#{Maid::VERSION}"
|
53
|
+
@logger.info "v#{ Maid::VERSION }"
|
54
54
|
@logger.info 'Started'
|
55
55
|
end
|
56
56
|
|
@@ -66,8 +66,9 @@ class Maid::Maid
|
|
66
66
|
path = @rules_path
|
67
67
|
|
68
68
|
Maid.with_instance(self) do
|
69
|
-
# Using
|
70
|
-
#
|
69
|
+
# Using `Kernel` here to help with testability.
|
70
|
+
#
|
71
|
+
# `Kernel.load` must be used for non-".rb" files to be required, it seems.
|
71
72
|
Kernel.load(path)
|
72
73
|
end
|
73
74
|
rescue LoadError => e
|
@@ -82,23 +83,23 @@ class Maid::Maid
|
|
82
83
|
# Follow all registered rules.
|
83
84
|
def follow_rules
|
84
85
|
@rules.each do |rule|
|
85
|
-
@logger.info("Rule: #{rule.description}")
|
86
|
+
@logger.info("Rule: #{ rule.description }")
|
86
87
|
rule.follow
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
90
91
|
# Run a shell command.
|
91
92
|
#--
|
92
|
-
# Delegates to Kernel
|
93
|
+
# Delegates to `Kernel.\``. Made primarily for testing other commands and some error handling.
|
93
94
|
def cmd(command) #:nodoc:
|
94
95
|
if supported_command?(command)
|
95
|
-
%x(#{command})
|
96
|
+
%x(#{ command })
|
96
97
|
else
|
97
|
-
raise ArgumentError, "Unsupported system command: #{command.inspect}"
|
98
|
+
raise ArgumentError, "Unsupported system command: #{ command.inspect }"
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
101
|
-
private
|
102
|
+
private
|
102
103
|
|
103
104
|
# Does the OS support this command?
|
104
105
|
def supported_command?(command) #:nodoc:
|
@@ -106,7 +107,10 @@ private
|
|
106
107
|
|
107
108
|
command_name = command.strip.split(/\s+/)[0]
|
108
109
|
supported = @@supported_commands[command_name]
|
109
|
-
|
110
|
+
# TODO: Instead of using `which`, use an alternative listed at:
|
111
|
+
#
|
112
|
+
# http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script
|
113
|
+
@@supported_commands[command_name] = supported ? supported : !%x(which #{ command_name }).empty?
|
110
114
|
end
|
111
115
|
|
112
116
|
def default_trash_path
|
data/lib/maid/tools.rb
CHANGED
@@ -2,59 +2,70 @@ require 'fileutils'
|
|
2
2
|
require 'find'
|
3
3
|
require 'time'
|
4
4
|
|
5
|
-
#
|
5
|
+
# These "tools" are methods available in the Maid DSL.
|
6
6
|
#
|
7
|
-
# In general,
|
7
|
+
# In general, methods are expected to:
|
8
8
|
#
|
9
|
-
#
|
9
|
+
# * Automatically expand paths (that is, `'~/Downloads/foo.zip'` becomes `'/home/username/Downloads/foo.zip'`)
|
10
|
+
# * Respect the `noop` (`dry-run`) option if it is set
|
11
|
+
#
|
12
|
+
# 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]
|
10
13
|
module Maid::Tools
|
11
14
|
include Deprecated
|
12
15
|
|
13
|
-
# Move from
|
16
|
+
# Move from `sources` to `destination`
|
14
17
|
#
|
15
18
|
# The path is not moved if a file already exists at the destination with the same name. A warning is logged instead.
|
16
19
|
#
|
17
|
-
#
|
20
|
+
# ## Examples
|
21
|
+
#
|
22
|
+
# Single path:
|
18
23
|
#
|
19
|
-
#
|
24
|
+
# move('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/')
|
20
25
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
def move(
|
26
|
-
Array(
|
27
|
-
|
28
|
-
|
29
|
-
target = File.join(
|
26
|
+
# Multiple paths:
|
27
|
+
#
|
28
|
+
# move(['~/Downloads/foo.zip', '~/Downloads/bar.zip'], '~/Archive/Software/Mac OS X/')
|
29
|
+
# move(dir('~/Downloads/*.zip'), '~/Archive/Software/Mac OS X/')
|
30
|
+
def move(sources, destination)
|
31
|
+
Array(sources).each do |source|
|
32
|
+
source = File.expand_path(source)
|
33
|
+
destination = File.expand_path(destination)
|
34
|
+
target = File.join(destination, File.basename(source))
|
30
35
|
|
31
36
|
unless File.exist?(target)
|
32
|
-
@logger.info
|
33
|
-
FileUtils.mv(
|
37
|
+
@logger.info("mv #{ source.inspect } #{ destination.inspect }")
|
38
|
+
FileUtils.mv(source, destination, @file_options)
|
34
39
|
else
|
35
|
-
@logger.warn
|
40
|
+
@logger.warn("skipping #{ source.inspect } because #{ target.inspect } already exists")
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
40
|
-
# Move the given
|
45
|
+
# Move the given paths to the user's trash.
|
41
46
|
#
|
42
|
-
# The path is moved if a file already exists in the trash with the same name. However, the current date and time is appended to the filename.
|
47
|
+
# 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.
|
43
48
|
#
|
44
|
-
# Note
|
49
|
+
# **Note:** the OS-native "restore" or "put back" functionality for trashed files is not currently supported. (See [issue #63](https://github.com/benjaminoakes/maid/issues/63).) However, they can be restored manually, and the Maid log can help assist with this.
|
45
50
|
#
|
46
|
-
# Options
|
51
|
+
# ## Options
|
52
|
+
#
|
53
|
+
# `:remove_over => Fixnum` (e.g. `1.gigabyte`, `1024.megabytes`)
|
54
|
+
#
|
55
|
+
# Delete files over the given size rather than moving to the trash.
|
56
|
+
#
|
57
|
+
# See also `Maid::NumericExtensions::SizeToKb`
|
58
|
+
#
|
59
|
+
# ## Examples
|
47
60
|
#
|
48
|
-
#
|
49
|
-
# Remove files over the given size rather than moving to the trash.
|
50
|
-
# See also Maid::NumericExtensions::SizeToKb
|
61
|
+
# Single path:
|
51
62
|
#
|
52
|
-
#
|
63
|
+
# trash('~/Downloads/foo.zip')
|
53
64
|
#
|
54
|
-
#
|
65
|
+
# Multiple paths:
|
55
66
|
#
|
56
|
-
#
|
57
|
-
#
|
67
|
+
# trash(['~/Downloads/foo.zip', '~/Downloads/bar.zip'])
|
68
|
+
# trash(dir('~/Downloads/*.zip'))
|
58
69
|
def trash(paths, options = {})
|
59
70
|
# ## Implementation Notes
|
60
71
|
#
|
@@ -70,7 +81,7 @@ module Maid::Tools
|
|
70
81
|
path = File.expand_path(path)
|
71
82
|
|
72
83
|
target = File.join(@trash_path, File.basename(path))
|
73
|
-
safe_trash_path = File.join(@trash_path, "#{File.basename(path)} #{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}")
|
84
|
+
safe_trash_path = File.join(@trash_path, "#{ File.basename(path) } #{ Time.now.strftime('%Y-%m-%d-%H-%M-%S') }")
|
74
85
|
|
75
86
|
if options[:remove_over] &&
|
76
87
|
File.exist?(path) &&
|
@@ -88,59 +99,79 @@ module Maid::Tools
|
|
88
99
|
end
|
89
100
|
end
|
90
101
|
|
91
|
-
#
|
102
|
+
# Delete the files at the given path recursively.
|
92
103
|
#
|
93
|
-
# Options
|
104
|
+
# ## Options
|
105
|
+
#
|
106
|
+
# `:force => boolean`
|
107
|
+
#
|
108
|
+
# Force deletion (no error is raised if the file does not exist).
|
109
|
+
#
|
110
|
+
# `:secure => boolean`
|
111
|
+
#
|
112
|
+
# Infrequently needed. See [`FileUtils.remove_entry_secure`](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure)
|
113
|
+
#
|
114
|
+
# ## Examples
|
94
115
|
#
|
95
|
-
#
|
96
|
-
# - :secure => boolean (See FileUtils.remove_entry_secure for further details)
|
116
|
+
# Single path:
|
97
117
|
#
|
98
|
-
#
|
118
|
+
# remove('~/Downloads/foo.zip')
|
99
119
|
#
|
100
|
-
#
|
120
|
+
# Multiple path:
|
101
121
|
#
|
102
|
-
#
|
103
|
-
#
|
122
|
+
# remove(['~/Downloads/foo.zip', '~/Downloads/bar.zip'])
|
123
|
+
# remove(dir('~/Downloads/*.zip'))
|
104
124
|
def remove(paths, options = {})
|
105
125
|
Array(paths).each do |path|
|
106
126
|
path = File.expand_path(path)
|
107
127
|
options = @file_options.merge(options)
|
108
128
|
|
109
|
-
@logger.info
|
110
|
-
FileUtils.rm_r(path,options)
|
129
|
+
@logger.info("Removing #{ path.inspect }")
|
130
|
+
FileUtils.rm_r(path, options)
|
111
131
|
end
|
112
132
|
end
|
113
133
|
|
114
134
|
# Give all files matching the given glob.
|
115
135
|
#
|
116
|
-
#
|
136
|
+
# The matches are sorted lexically to aid in readability when using `--dry-run`.
|
137
|
+
#
|
138
|
+
# ## Examples
|
139
|
+
#
|
140
|
+
# dir('~/Downloads/*.zip')
|
117
141
|
def dir(glob)
|
118
|
-
Dir[File.expand_path(glob)]
|
142
|
+
Dir[File.expand_path(glob)].sort
|
119
143
|
end
|
120
144
|
|
121
|
-
# Creates a directory and all its parent directories.
|
145
|
+
# Creates a directory and all of its parent directories.
|
146
|
+
#
|
147
|
+
# ## Options
|
122
148
|
#
|
123
|
-
#
|
149
|
+
# `:mode`
|
124
150
|
#
|
125
|
-
#
|
126
|
-
# eg. 0700, 'u=wr,go=rr'
|
151
|
+
# The symbolic and absolute mode can both be used, for example: `0700`, `'u=wr,go=rr'`
|
127
152
|
#
|
128
|
-
#
|
153
|
+
# ## Examples
|
154
|
+
#
|
155
|
+
# mkdir('~/Downloads/Music/Pink.Floyd/', :mode => 0644)
|
129
156
|
def mkdir(path, options = {})
|
130
157
|
FileUtils.mkdir_p(File.expand_path(path), options)
|
131
158
|
end
|
132
159
|
|
133
|
-
# Find matching files, akin to the Unix utility
|
160
|
+
# Find matching files, akin to the Unix utility `find`.
|
161
|
+
#
|
162
|
+
# If no block is given, it will return an array. Otherwise, it acts like `Find.find`.
|
134
163
|
#
|
135
|
-
#
|
164
|
+
# ## Examples
|
136
165
|
#
|
137
|
-
#
|
166
|
+
# Without a block:
|
138
167
|
#
|
139
|
-
#
|
168
|
+
# find('~/Downloads/') # => [...]
|
140
169
|
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
170
|
+
# Recursing with a block:
|
171
|
+
#
|
172
|
+
# find('~/Downloads/') do |path|
|
173
|
+
# # ...
|
174
|
+
# end
|
144
175
|
#
|
145
176
|
def find(path, &block)
|
146
177
|
expanded_path = File.expand_path(path)
|
@@ -156,84 +187,115 @@ module Maid::Tools
|
|
156
187
|
|
157
188
|
# [Mac OS X] Use Spotlight to locate all files matching the given filename.
|
158
189
|
#
|
159
|
-
#
|
160
|
-
|
161
|
-
#
|
190
|
+
# [Ubuntu] Not currently supported. See [issue #67](https://github.com/benjaminoakes/maid/issues/67).
|
191
|
+
#
|
192
|
+
# ## Examples
|
193
|
+
#
|
194
|
+
# locate('foo.zip') # => ['/a/foo.zip', '/b/foo.zip']
|
162
195
|
def locate(name)
|
163
|
-
cmd("mdfind -name #{name.inspect}").split("\n")
|
196
|
+
cmd("mdfind -name #{ name.inspect }").split("\n")
|
164
197
|
end
|
165
198
|
|
166
199
|
# [Mac OS X] Use Spotlight metadata to determine the site from which a file was downloaded.
|
167
200
|
#
|
168
|
-
#
|
201
|
+
# ## Examples
|
202
|
+
#
|
203
|
+
# downloaded_from('foo.zip') # => ['http://www.site.com/foo.zip', 'http://www.site.com/']
|
169
204
|
def downloaded_from(path)
|
170
|
-
raw = cmd("mdls -raw -name kMDItemWhereFroms #{path.inspect}")
|
205
|
+
raw = cmd("mdls -raw -name kMDItemWhereFroms #{ path.inspect }")
|
171
206
|
clean = raw[1, raw.length - 2]
|
172
207
|
clean.split(/,\s+/).map { |s| t = s.strip; t[1, t.length - 2] }
|
173
208
|
end
|
174
209
|
|
175
210
|
# [Mac OS X] Use Spotlight metadata to determine audio length.
|
176
211
|
#
|
177
|
-
#
|
212
|
+
# ## Examples
|
213
|
+
#
|
214
|
+
# duration_s('foo.mp3') # => 235.705
|
178
215
|
def duration_s(path)
|
179
|
-
cmd("mdls -raw -name kMDItemDurationSeconds #{path.inspect}").to_f
|
216
|
+
cmd("mdls -raw -name kMDItemDurationSeconds #{ path.inspect }").to_f
|
180
217
|
end
|
181
218
|
|
182
|
-
#
|
219
|
+
# List the contents of a zip file.
|
183
220
|
#
|
184
|
-
#
|
221
|
+
# ## Examples
|
222
|
+
#
|
223
|
+
# zipfile_contents('foo.zip') # => ['foo/foo.exe', 'foo/README.txt']
|
185
224
|
def zipfile_contents(path)
|
186
|
-
raw = cmd("unzip -Z1 #{path.inspect}")
|
225
|
+
raw = cmd("unzip -Z1 #{ path.inspect }")
|
187
226
|
raw.split("\n")
|
188
227
|
end
|
189
228
|
|
190
|
-
# Calculate disk usage of a given path.
|
229
|
+
# Calculate disk usage of a given path in kilobytes.
|
230
|
+
#
|
231
|
+
# See also: `Maid::NumericExtensions::SizeToKb`.
|
191
232
|
#
|
192
|
-
#
|
233
|
+
# ## Examples
|
193
234
|
#
|
194
|
-
#
|
235
|
+
# disk_usage('foo.zip') # => 136
|
195
236
|
def disk_usage(path)
|
196
|
-
raw = cmd("du -s #{path.inspect}")
|
237
|
+
raw = cmd("du -s #{ path.inspect }")
|
238
|
+
# FIXME: This reports in kilobytes, but should probably report in bytes.
|
197
239
|
usage_kb = raw.split(/\s+/).first.to_i
|
198
240
|
|
199
241
|
if usage_kb.zero?
|
200
|
-
raise "Stopping pessimistically because of unexpected value from du (#{raw.inspect})"
|
242
|
+
raise "Stopping pessimistically because of unexpected value from du (#{ raw.inspect })"
|
201
243
|
else
|
202
244
|
usage_kb
|
203
245
|
end
|
204
246
|
end
|
205
247
|
|
206
|
-
#
|
248
|
+
# Get the creation time of a file.
|
249
|
+
#
|
250
|
+
# In Unix speak, `ctime`.
|
207
251
|
#
|
208
|
-
#
|
252
|
+
# ## Examples
|
253
|
+
#
|
254
|
+
# created_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011
|
209
255
|
def created_at(path)
|
210
256
|
File.ctime(File.expand_path(path))
|
211
257
|
end
|
212
258
|
|
213
|
-
#
|
259
|
+
# Get the time that a file was last accessed.
|
260
|
+
#
|
261
|
+
# In Unix speak, `atime`.
|
262
|
+
#
|
263
|
+
# ## Examples
|
214
264
|
#
|
215
|
-
#
|
265
|
+
# accessed_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011
|
216
266
|
def accessed_at(path)
|
217
267
|
File.atime(File.expand_path(path))
|
218
268
|
end
|
219
269
|
|
220
|
-
|
270
|
+
# @deprecated
|
271
|
+
#
|
272
|
+
# Alias of `accessed_at`.
|
273
|
+
def last_accessed(path)
|
274
|
+
# Not a normal `alias` so the deprecation notice shows in the docs.
|
275
|
+
accessed_at(path)
|
276
|
+
end
|
221
277
|
deprecated :last_accessed, :accessed_at
|
222
278
|
|
223
|
-
#
|
279
|
+
# Get the modification time of a file.
|
280
|
+
#
|
281
|
+
# In Unix speak, `mtime`.
|
282
|
+
#
|
283
|
+
# ## Examples
|
224
284
|
#
|
225
|
-
#
|
285
|
+
# modified_at('foo.zip') # => Sat Apr 09 10:50:01 -0400 2011
|
226
286
|
def modified_at(path)
|
227
287
|
File.mtime(File.expand_path(path))
|
228
288
|
end
|
229
289
|
|
230
|
-
#
|
290
|
+
# @deprecated
|
231
291
|
#
|
232
|
-
#
|
292
|
+
# Pulls and pushes the `git` repository at the given path.
|
233
293
|
#
|
234
|
-
#
|
294
|
+
# Since this is deprecated, you might also be interested in [SparkleShare](http://sparkleshare.org/), a great `git`-based file syncronization project.
|
235
295
|
#
|
236
|
-
#
|
296
|
+
# ## Examples
|
297
|
+
#
|
298
|
+
# git_piston('~/code/projectname')
|
237
299
|
def git_piston(path)
|
238
300
|
full_path = File.expand_path(path)
|
239
301
|
stdout = cmd("cd #{full_path.inspect} && git pull && git push 2>&1")
|
@@ -242,20 +304,34 @@ module Maid::Tools
|
|
242
304
|
|
243
305
|
deprecated :git_piston, 'SparkleShare (http://sparkleshare.org/)'
|
244
306
|
|
245
|
-
#
|
307
|
+
# Simple sync of two files/folders using `rsync`.
|
246
308
|
#
|
247
|
-
# See rsync man page for a detailed description.
|
309
|
+
# The host OS must provide `rsync`. See the `rsync` man page for a detailed description.
|
310
|
+
#
|
311
|
+
# man rsync
|
248
312
|
#
|
249
|
-
# Options
|
313
|
+
# ## Options
|
314
|
+
#
|
315
|
+
# `:delete => boolean`
|
316
|
+
# `:verbose => boolean`
|
317
|
+
# `:archive => boolean` (default `true`)
|
318
|
+
# `:update => boolean` (default `true`)
|
319
|
+
# `:exclude => string`
|
320
|
+
# `:prune_empty => boolean`
|
321
|
+
#
|
322
|
+
# ## Examples
|
323
|
+
#
|
324
|
+
# Syncing a directory to a backup:
|
325
|
+
#
|
326
|
+
# sync('~/music', '/backup/music')
|
327
|
+
#
|
328
|
+
# Excluding a path:
|
329
|
+
#
|
330
|
+
# sync('~/code', '/backup/code', :exclude => '.git')
|
250
331
|
#
|
251
|
-
#
|
252
|
-
# - :verbose => boolean
|
253
|
-
# - :archive => boolean (default true)
|
254
|
-
# - :update => boolean (default true)
|
255
|
-
# - :exclude => string EXE :exclude => ".git" or :exclude => [".git", ".rvmrc"]
|
256
|
-
# - :prune_empty => boolean
|
332
|
+
# Excluding multiple paths:
|
257
333
|
#
|
258
|
-
#
|
334
|
+
# sync('~/code', '/backup/code', :exclude => ['.git', '.rvmrc'])
|
259
335
|
def sync(from, to, options = {})
|
260
336
|
# expand path removes trailing slash
|
261
337
|
# cannot use str[-1] due to ruby 1.8.7 restriction
|
@@ -271,11 +347,11 @@ module Maid::Tools
|
|
271
347
|
ops << '-n' if @file_options[:noop]
|
272
348
|
|
273
349
|
Array(options[:exclude]).each do |path|
|
274
|
-
ops << "--exclude=#{path.inspect}"
|
350
|
+
ops << "--exclude=#{ path.inspect }"
|
275
351
|
end
|
276
352
|
|
277
353
|
ops << '--delete' if options[:delete]
|
278
|
-
stdout = cmd("rsync #{ops.join(' ')} #{from.inspect} #{to.inspect} 2>&1")
|
279
|
-
@logger.info
|
354
|
+
stdout = cmd("rsync #{ ops.join(' ') } #{ from.inspect } #{ to.inspect } 2>&1")
|
355
|
+
@logger.info("Fired sync from #{ from.inspect } to #{ to.inspect }. STDOUT:\n\n#{ stdout }")
|
280
356
|
end
|
281
357
|
end
|