maid 0.10.0.pre.alpha.3 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +14 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/coverage.yml +3 -3
- data/.github/workflows/lint.yml +9 -1
- data/.github/workflows/merge-gatekeeper.yml +20 -0
- data/.github/workflows/release.yml +13 -20
- data/.github/workflows/stale.yml +25 -0
- data/.github/workflows/test.yml +8 -7
- data/.gitignore +1 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +3 -1
- data/.rubocop_todo.yml +105 -107
- data/.ruby-version +1 -1
- data/CHANGELOG.md +23 -0
- data/Dockerfile +13 -0
- data/Gemfile.lock +226 -0
- data/Guardfile +2 -0
- data/README.md +82 -49
- data/Rakefile +9 -0
- data/SECURITY.md +29 -0
- data/fixtures/files/test_rules.rb +3 -0
- data/fixtures/vcr_cassettes/Dependency_expectations/Geocoder/translates_latitude_and_longitude_into_street_addresses.yml +42 -0
- data/fixtures/vcr_cassettes/Maid_Tools/_location_city/given_a_JPEG_image/reports_the_known_location.yml +42 -0
- data/lib/maid/logger/logger.rb +63 -0
- data/lib/maid/maid.rb +6 -22
- data/lib/maid/repeat.rb +2 -2
- data/lib/maid/rule.rb +2 -2
- data/lib/maid/rule_container.rb +2 -2
- data/lib/maid/tools.rb +3 -3
- data/lib/maid/trash_migration.rb +2 -0
- data/lib/maid/version.rb +1 -1
- data/lib/maid/watch.rb +2 -2
- data/lib/maid.rb +3 -2
- data/maid.gemspec +14 -9
- data/release-please-config.json +18 -0
- data/script/docker-test +7 -0
- data/spec/dependency_spec.rb +1 -1
- data/spec/fakefs_helper.rb +13 -0
- data/spec/lib/maid/logger/logger_spec.rb +64 -0
- data/spec/lib/maid/maid_spec.rb +113 -103
- data/spec/lib/maid/rake/single_rule_spec.rb +1 -1
- data/spec/lib/maid/tools_spec.rb +384 -225
- data/spec/lib/maid/trash_migration_spec.rb +7 -5
- data/spec/spec_helper.rb +17 -1
- metadata +124 -44
- data/Vagrantfile +0 -14
- data/script/vagrant-provision +0 -43
- data/script/vagrant-test +0 -7
- data/script/vagrant-test-all +0 -34
data/lib/maid/maid.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'fileutils'
|
2
|
-
require 'logger'
|
3
2
|
require 'xdg'
|
4
3
|
|
5
4
|
# Maid cleans up according to the given rules, logging what it does.
|
@@ -9,13 +8,8 @@ require 'xdg'
|
|
9
8
|
class Maid::Maid
|
10
9
|
include Maid::RuleContainer
|
11
10
|
DEFAULTS = {
|
12
|
-
progname: 'Maid',
|
13
|
-
|
14
11
|
log_device: File.expand_path('~/.maid/maid.log'),
|
15
|
-
|
16
|
-
# too bad. (We're going with a larger size just for safety right now.)
|
17
|
-
log_shift_age: 5,
|
18
|
-
log_shift_size: 10 * 1_048_576, # 10 * 1 MB
|
12
|
+
logger: ::Maid::Logger,
|
19
13
|
|
20
14
|
rules_path: File.expand_path('~/.maid/rules.rb'),
|
21
15
|
file_options: { noop: false }, # for `FileUtils`
|
@@ -34,17 +28,7 @@ class Maid::Maid
|
|
34
28
|
def initialize(options = {})
|
35
29
|
options = DEFAULTS.merge(options.reject { |_k, v| v.nil? })
|
36
30
|
|
37
|
-
|
38
|
-
@logger = if options[:logger]
|
39
|
-
options[:logger]
|
40
|
-
else
|
41
|
-
@log_device = options[:log_device]
|
42
|
-
FileUtils.mkdir_p(File.dirname(@log_device)) unless @log_device.is_a?(IO)
|
43
|
-
@logger = Logger.new(@log_device, options[:log_shift_age], options[:log_shift_size])
|
44
|
-
end
|
45
|
-
|
46
|
-
@logger.progname = options[:progname]
|
47
|
-
@logger.formatter = options[:log_formatter] if options[:log_formatter]
|
31
|
+
@logger = options[:logger].new(device: options[:log_device])
|
48
32
|
|
49
33
|
@rules_path = options[:rules_path]
|
50
34
|
@trash_path = options[:trash_path] || default_trash_path
|
@@ -87,7 +71,7 @@ class Maid::Maid
|
|
87
71
|
warn e.message
|
88
72
|
end
|
89
73
|
|
90
|
-
def watch(path, options = {}, &
|
74
|
+
def watch(path, options = {}, &)
|
91
75
|
full_path = File.expand_path(path)
|
92
76
|
|
93
77
|
unless File.directory?(full_path)
|
@@ -97,11 +81,11 @@ class Maid::Maid
|
|
97
81
|
raise message
|
98
82
|
end
|
99
83
|
|
100
|
-
@watches << ::Maid::Watch.new(self, path, options, &
|
84
|
+
@watches << ::Maid::Watch.new(self, path, options, &)
|
101
85
|
end
|
102
86
|
|
103
|
-
def repeat(timestring, options = {}, &
|
104
|
-
@repeats << ::Maid::Repeat.new(self, timestring, options, &
|
87
|
+
def repeat(timestring, options = {}, &)
|
88
|
+
@repeats << ::Maid::Repeat.new(self, timestring, options, &)
|
105
89
|
end
|
106
90
|
|
107
91
|
# Daemonizes the process by starting all watches and repeats and joining
|
data/lib/maid/repeat.rb
CHANGED
@@ -4,13 +4,13 @@ class Maid::Repeat
|
|
4
4
|
|
5
5
|
attr_reader :timestring, :scheduler, :logger
|
6
6
|
|
7
|
-
def initialize(maid, timestring, options = {}, &
|
7
|
+
def initialize(maid, timestring, options = {}, &)
|
8
8
|
@maid = maid
|
9
9
|
@logger = maid.logger # TODO: Maybe it's better to create seperate loggers?
|
10
10
|
@scheduler = Rufus::Scheduler.singleton
|
11
11
|
@timestring = timestring
|
12
12
|
@options = options
|
13
|
-
initialize_rules(&
|
13
|
+
initialize_rules(&)
|
14
14
|
end
|
15
15
|
|
16
16
|
def run
|
data/lib/maid/rule.rb
CHANGED
data/lib/maid/rule_container.rb
CHANGED
@@ -3,9 +3,9 @@ module Maid::RuleContainer
|
|
3
3
|
attr_reader :rules
|
4
4
|
|
5
5
|
# initialize_rules
|
6
|
-
def initialize_rules(&
|
6
|
+
def initialize_rules(&)
|
7
7
|
@rules ||= []
|
8
|
-
instance_exec(&
|
8
|
+
instance_exec(&)
|
9
9
|
end
|
10
10
|
|
11
11
|
# Register a rule with a description and instructions (lambda function).
|
data/lib/maid/tools.rb
CHANGED
@@ -328,7 +328,7 @@ module Maid::Tools
|
|
328
328
|
def mkdir(path, options = {})
|
329
329
|
path = expand(path)
|
330
330
|
log("mkdir -p #{sh_escape(path)}")
|
331
|
-
FileUtils.mkdir_p(path, **@file_options
|
331
|
+
FileUtils.mkdir_p(path, **@file_options, **options)
|
332
332
|
path
|
333
333
|
end
|
334
334
|
|
@@ -727,7 +727,7 @@ module Maid::Tools
|
|
727
727
|
# where_content_type(dir('~/Downloads/*'), 'public.image')
|
728
728
|
def where_content_type(paths, filter_types)
|
729
729
|
filter_types = Array(filter_types)
|
730
|
-
Array(paths).select { |p|
|
730
|
+
Array(paths).select { |p| filter_types.intersect?(content_types(p)) }
|
731
731
|
end
|
732
732
|
|
733
733
|
# Test whether a directory is either empty, or contains only empty
|
@@ -739,7 +739,7 @@ module Maid::Tools
|
|
739
739
|
# trash('~/Downloads/foo')
|
740
740
|
# end
|
741
741
|
def tree_empty?(root)
|
742
|
-
return
|
742
|
+
return false if File.file?(root)
|
743
743
|
return true if Dir.glob(root + '/*').length == 0
|
744
744
|
|
745
745
|
ignore = []
|
data/lib/maid/trash_migration.rb
CHANGED
@@ -20,6 +20,8 @@ module Maid
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def perform
|
23
|
+
# FIXME: This is tightly coupled, we can't pass a custom log device
|
24
|
+
# etc.
|
23
25
|
maid = ::Maid::Maid.new(trash_path: correct_trash)
|
24
26
|
# Use local variable so it's available in the closure used by `instance_eval`
|
25
27
|
path = incorrect_trash
|
data/lib/maid/version.rb
CHANGED
data/lib/maid/watch.rb
CHANGED
@@ -5,7 +5,7 @@ class Maid::Watch
|
|
5
5
|
|
6
6
|
attr_reader :path, :listener, :logger
|
7
7
|
|
8
|
-
def initialize(maid, path, options = {}, &
|
8
|
+
def initialize(maid, path, options = {}, &)
|
9
9
|
@maid = maid
|
10
10
|
|
11
11
|
if options.nil? || options.empty?
|
@@ -19,7 +19,7 @@ class Maid::Watch
|
|
19
19
|
|
20
20
|
@logger = maid.logger # TODO: Maybe it's better to create seperate loggers?
|
21
21
|
@path = File.expand_path(path)
|
22
|
-
initialize_rules(&
|
22
|
+
initialize_rules(&)
|
23
23
|
end
|
24
24
|
|
25
25
|
def run
|
data/lib/maid.rb
CHANGED
@@ -4,6 +4,7 @@ Deprecated.set_action(:warn)
|
|
4
4
|
|
5
5
|
# Must be in this order:
|
6
6
|
require 'maid/version'
|
7
|
+
require 'maid/logger/logger'
|
7
8
|
require 'maid/downloading'
|
8
9
|
require 'maid/tools'
|
9
10
|
require 'maid/rule_container'
|
@@ -32,8 +33,8 @@ module Maid
|
|
32
33
|
end
|
33
34
|
|
34
35
|
# Define rules for the Maid instance.
|
35
|
-
def rules(&
|
36
|
-
@instance.instance_exec(&
|
36
|
+
def rules(&)
|
37
|
+
@instance.instance_exec(&)
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
data/maid.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
|
24
24
|
s.rubyforge_project = 'maid'
|
25
25
|
|
26
|
-
s.required_ruby_version = '>= 2.
|
26
|
+
s.required_ruby_version = '>= 3.2.0'
|
27
27
|
|
28
28
|
# Strategy: if possible, use ranges (so there are fewer chances of version conflicts)
|
29
29
|
s.add_dependency('deprecated', '~> 3.0.0')
|
@@ -46,21 +46,26 @@ Gem::Specification.new do |s|
|
|
46
46
|
|
47
47
|
# Strategy: specific versions (since they're just for development)
|
48
48
|
s.add_development_dependency('fakefs', '~> 2.4.0')
|
49
|
-
s.add_development_dependency('fuubar')
|
49
|
+
s.add_development_dependency('fuubar', '~> 2.5.1')
|
50
50
|
s.add_development_dependency('guard', '~> 2.18.0')
|
51
51
|
s.add_development_dependency('guard-bundler', '~> 3.0.1')
|
52
52
|
s.add_development_dependency('guard-rspec', '~> 4.7.3')
|
53
|
-
s.add_development_dependency('guard-rubocop')
|
54
|
-
s.add_development_dependency('
|
53
|
+
s.add_development_dependency('guard-rubocop', '~> 1.5.0')
|
54
|
+
s.add_development_dependency('irb', '~> 1.15.1')
|
55
|
+
s.add_development_dependency('ostruct', '~> 0.6.1')
|
56
|
+
s.add_development_dependency('pry-byebug', '~> 3.10.1')
|
55
57
|
s.add_development_dependency('rake', '~> 13.0.6')
|
56
|
-
s.add_development_dependency('rake-notes')
|
58
|
+
s.add_development_dependency('rake-notes', '~> 0.2.2')
|
57
59
|
s.add_development_dependency('redcarpet', '~> 3.6.0') # Soft dependency of `yard`
|
60
|
+
s.add_development_dependency('reline', '~> 0.6.0')
|
58
61
|
s.add_development_dependency('rspec', '~> 3.12.0')
|
59
|
-
s.add_development_dependency('rubocop')
|
60
|
-
s.add_development_dependency('rubocop-rake')
|
61
|
-
s.add_development_dependency('rubocop-rspec')
|
62
|
-
s.add_development_dependency('simplecov')
|
62
|
+
s.add_development_dependency('rubocop', '~> 1.50')
|
63
|
+
s.add_development_dependency('rubocop-rake', '~> 0.6.0')
|
64
|
+
s.add_development_dependency('rubocop-rspec', '~> 3.5.0')
|
65
|
+
s.add_development_dependency('simplecov', '~> 0.22.0')
|
63
66
|
s.add_development_dependency('timecop', '~> 0.9.6')
|
67
|
+
s.add_development_dependency('vcr', '~> 6.1.0')
|
68
|
+
s.add_development_dependency('webmock', '~> 3.18.1')
|
64
69
|
s.add_development_dependency('yard', '>= 0.9.11')
|
65
70
|
|
66
71
|
# In Vagrant, polling won't cross over the OS boundary if you develop in the host OS but run your tests in the
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"always-update": true,
|
3
|
+
"bootstrap-sha": "d4901ad446c8a614ef80472ed5e6f1ba8c702ed5",
|
4
|
+
"bump-minor-pre-major": true,
|
5
|
+
"bump-patch-for-minor-pre-major": true,
|
6
|
+
"commit-search-depth": 500,
|
7
|
+
"group-pull-request-title-pattern": "chore: release ${version}",
|
8
|
+
"release-search-depth": 400,
|
9
|
+
"release-type": "ruby",
|
10
|
+
"sequential-calls": false,
|
11
|
+
"packages": {
|
12
|
+
".": {
|
13
|
+
"release-type": "ruby",
|
14
|
+
"package-name": "maid",
|
15
|
+
"version-file": "lib/maid/version.rb"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
data/script/docker-test
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# Run RSpec in a docker container, to avoid mistakenly writing to the live
|
4
|
+
# filesystem while developing.
|
5
|
+
docker rm -f maid-dev
|
6
|
+
docker build -t maid-dev .
|
7
|
+
docker run -it --rm --name maid-dev --mount type=bind,src="$(pwd)",target=/usr/src/app maid-dev
|
data/spec/dependency_spec.rb
CHANGED
@@ -95,7 +95,7 @@ describe 'Dependency expectations' do
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
describe Geocoder do
|
98
|
+
describe Geocoder, vcr: { record: :new_episodes } do
|
99
99
|
it 'translates latitude and longitude into street addresses' do
|
100
100
|
city = Geocoder.search('-33.85608611111111,151.219925').map { |location| location.city }.uniq.compact
|
101
101
|
expect(city).to eq(['Sydney'])
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# FakeFS is missing #flock, see https://github.com/fakefs/fakefs/issues/433 and
|
2
|
+
# https://github.com/whitesmith/rubycritic/commit/57edc6244a9ebea8078a9c1dba32204ee7d1d895
|
3
|
+
|
4
|
+
# NOTE: This avoid NotImplementedError on File.flock, but causes a myriad of
|
5
|
+
# other issues since it doesn't really provide any locking in practice.
|
6
|
+
# If required, include this file in spec_helper.rb to monkey-patch FakeFS.
|
7
|
+
module FakeFS
|
8
|
+
class File < StringIO
|
9
|
+
def flock(*)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'maid/logger/logger'
|
3
|
+
|
4
|
+
module Maid
|
5
|
+
# FakeFS not required because we're writing the log to /tmp/ and deleting it
|
6
|
+
# after the test.
|
7
|
+
describe Logger do
|
8
|
+
let(:logfile) { '/tmp/maid/test.log' }
|
9
|
+
let(:logger) { described_class.new(device: logfile) }
|
10
|
+
|
11
|
+
after { FileUtils.rm('/tmp/maid/test.log', force: true) }
|
12
|
+
|
13
|
+
levels = %i[debug info warn error fatal unknown]
|
14
|
+
levels.each do |level|
|
15
|
+
it "responds to #{level}" do
|
16
|
+
expect(logger).to respond_to(level)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with a filename' do
|
21
|
+
before { logger.info('test message') }
|
22
|
+
|
23
|
+
it 'creates that file' do
|
24
|
+
expect(File.exist?(logfile)).to be true
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'sets the default progname' do
|
28
|
+
expect(File.read(logfile)).to match(/Maid: /)
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with the ::Logger::DEBUG log level' do
|
32
|
+
let(:logger) { described_class.new(device: logfile, level: ::Logger::DEBUG) }
|
33
|
+
|
34
|
+
levels.each do |level|
|
35
|
+
it "logs #{level} messages" do
|
36
|
+
logger.send(level, "#{level} test message")
|
37
|
+
|
38
|
+
expect(File.read(logfile)).to match("#{level} test message")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'works with a string' do
|
44
|
+
logger.info('test message')
|
45
|
+
|
46
|
+
expect(File.read(logfile)).to match 'test message'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'works with a custom progname and a block' do
|
50
|
+
logger.info('TestProgname') { 'test message' }
|
51
|
+
|
52
|
+
expect(File.read(logfile)).to match 'TestProgname: test message'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with an IO' do
|
57
|
+
let(:logger) { described_class.new(device: StringIO.new) }
|
58
|
+
|
59
|
+
it 'is happy' do
|
60
|
+
expect { logger.info('test message') }.not_to raise_error
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|