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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +14 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/coverage.yml +3 -3
  6. data/.github/workflows/lint.yml +9 -1
  7. data/.github/workflows/merge-gatekeeper.yml +20 -0
  8. data/.github/workflows/release.yml +13 -20
  9. data/.github/workflows/stale.yml +25 -0
  10. data/.github/workflows/test.yml +8 -7
  11. data/.gitignore +1 -1
  12. data/.release-please-manifest.json +1 -1
  13. data/.rubocop.yml +3 -1
  14. data/.rubocop_todo.yml +105 -107
  15. data/.ruby-version +1 -1
  16. data/CHANGELOG.md +23 -0
  17. data/Dockerfile +13 -0
  18. data/Gemfile.lock +226 -0
  19. data/Guardfile +2 -0
  20. data/README.md +82 -49
  21. data/Rakefile +9 -0
  22. data/SECURITY.md +29 -0
  23. data/fixtures/files/test_rules.rb +3 -0
  24. data/fixtures/vcr_cassettes/Dependency_expectations/Geocoder/translates_latitude_and_longitude_into_street_addresses.yml +42 -0
  25. data/fixtures/vcr_cassettes/Maid_Tools/_location_city/given_a_JPEG_image/reports_the_known_location.yml +42 -0
  26. data/lib/maid/logger/logger.rb +63 -0
  27. data/lib/maid/maid.rb +6 -22
  28. data/lib/maid/repeat.rb +2 -2
  29. data/lib/maid/rule.rb +2 -2
  30. data/lib/maid/rule_container.rb +2 -2
  31. data/lib/maid/tools.rb +3 -3
  32. data/lib/maid/trash_migration.rb +2 -0
  33. data/lib/maid/version.rb +1 -1
  34. data/lib/maid/watch.rb +2 -2
  35. data/lib/maid.rb +3 -2
  36. data/maid.gemspec +14 -9
  37. data/release-please-config.json +18 -0
  38. data/script/docker-test +7 -0
  39. data/spec/dependency_spec.rb +1 -1
  40. data/spec/fakefs_helper.rb +13 -0
  41. data/spec/lib/maid/logger/logger_spec.rb +64 -0
  42. data/spec/lib/maid/maid_spec.rb +113 -103
  43. data/spec/lib/maid/rake/single_rule_spec.rb +1 -1
  44. data/spec/lib/maid/tools_spec.rb +384 -225
  45. data/spec/lib/maid/trash_migration_spec.rb +7 -5
  46. data/spec/spec_helper.rb +17 -1
  47. metadata +124 -44
  48. data/Vagrantfile +0 -14
  49. data/script/vagrant-provision +0 -43
  50. data/script/vagrant-test +0 -7
  51. data/script/vagrant-test-all +0 -34
@@ -1,48 +1,48 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Maid
4
- describe Maid, fakefs: true do
5
- let(:logger) { instance_spy('Logger') }
4
+ describe Maid do
5
+ let(:logger) { class_spy(::Logger) }
6
+ let(:logfile) { '/tmp/maid-specs/test.log' }
7
+ let(:rules_file) { File.expand_path(File.join(File.dirname(__dir__), '../../fixtures/files/test_rules.rb')) }
8
+ let(:test_defaults) { Maid::DEFAULTS.merge({ log_device: logfile, rules_path: rules_file }) }
6
9
 
7
10
  before do
8
- allow(Logger).to receive(:new).and_return(logger)
11
+ FileUtils.mkdir_p(File.dirname(logfile))
12
+ # Avoid FakeFS error when the logfile doesn't already exist.
13
+ FileUtils.touch(logfile) if FakeFS.activated?
9
14
  end
10
15
 
11
- describe '.new' do
12
- it 'sets up a logger with the default path' do
13
- expect(Logger).to receive(:new).with(Maid::DEFAULTS[:log_device], anything, anything)
14
- Maid.new
15
- end
16
-
17
- it 'sets up a logger with the given path, when provided' do
18
- log_device = '/var/log/maid.log'
19
- expect(Logger).to receive(:new).with(log_device, anything, anything)
20
- Maid.new(log_device: log_device)
21
- end
16
+ after do
17
+ # Cleanup afterwards
18
+ FileUtils.rm_rf(File.dirname(logfile))
19
+ end
22
20
 
23
- it 'rotates the log with the default settings' do
24
- expect(Logger).to receive(:new).with(anything, Maid::DEFAULTS[:log_shift_age],
25
- Maid::DEFAULTS[:log_shift_size],)
26
- Maid.new
27
- end
21
+ describe '.new' do
22
+ context 'with the default options' do
23
+ before { Maid.new(**test_defaults, logger: logger) }
28
24
 
29
- it 'rotates the log with the given settings, when provided' do
30
- expect(Logger).to receive(:new).with(anything, 42, 1_000_000)
31
- Maid.new(log_shift_age: 42, log_shift_size: 1_000_000)
25
+ it 'sets up a logger with the default path' do
26
+ expect(logger).to have_received(:new).with(device: test_defaults[:log_device])
27
+ end
32
28
  end
33
29
 
34
- it 'makes the log directory in case it does not exist' do
35
- expect(File.exist?('/home/username/log')).to be false
30
+ context 'with a custom logfile path' do
31
+ let(:device) { '/tmp/maid-specs/overridden-maid.log' }
36
32
 
37
- Maid.new(log_device: '/home/username/log/maid.log')
33
+ before { Maid.new(log_device: device, logger: logger) }
38
34
 
39
- expect(File.exist?('/home/username/log')).to be true
35
+ it 'sets up a logger with the given path, when provided' do
36
+ expect(logger).to have_received(:new).with(device: device)
37
+ end
40
38
  end
41
39
 
42
- it 'takes a logger object during intialization' do
43
- allow(Logger).to receive(:new).and_call_original
44
- maid = Maid.new(logger: logger)
45
- expect(maid.logger).to eq(logger)
40
+ context 'with a custom logger' do
41
+ let(:maid) { Maid.new(logger: logger) }
42
+
43
+ it 'uses it' do
44
+ expect(maid.logger).to eq(logger)
45
+ end
46
46
  end
47
47
 
48
48
  describe 'platform-specific behavior' do
@@ -87,25 +87,14 @@ module Maid
87
87
  end
88
88
  end
89
89
 
90
- it 'sets the trash to the given path, if provided' do
91
- trash_path = '/home/username/tmp/my_trash/'
92
-
93
- maid = Maid.new(trash_path: trash_path)
94
-
95
- expect(maid.trash_path).not_to be_nil
96
- expect(maid.trash_path).to eq(trash_path)
97
- end
98
-
99
- it 'sets the progname for the logger' do
100
- Maid.new
101
-
102
- expect(logger).to have_received(:progname=).with(Maid::DEFAULTS[:progname])
103
- end
104
-
105
- it 'sets the progname for the logger to the given name, if provided' do
106
- Maid.new(progname: 'Fran')
90
+ context 'with a custom trash path' do
91
+ let(:trash_path) { '/tmp/maid-specs/my_trash/' }
92
+ let(:maid) { Maid.new(log_device: test_defaults[:log_device], trash_path: trash_path) }
107
93
 
108
- expect(logger).to have_received(:progname=).with('Fran')
94
+ it 'sets the trash to the given path' do
95
+ expect(maid.trash_path).not_to be_nil
96
+ expect(maid.trash_path).to eq(trash_path)
97
+ end
109
98
  end
110
99
 
111
100
  it 'sets the file options to the defaults' do
@@ -133,21 +122,34 @@ module Maid
133
122
  end
134
123
 
135
124
  describe '#clean' do
125
+ let(:maid) { Maid.new(**test_defaults) }
126
+
136
127
  before do
137
- @maid = Maid.new
138
- allow(logger).to receive(:info)
128
+ # Start with a clean logfile
129
+ FileUtils.rm_rf(logfile)
130
+ # Create the files that the test rules will impact
131
+ FileUtils.mkdir_p('/tmp/maid-specs')
132
+ FileUtils.touch('/tmp/maid-specs/perfect_man')
133
+
134
+ maid.load_rules
135
+ maid.clean
136
+ end
137
+
138
+ after do
139
+ FileUtils.rm_rf('/tmp/maid-specs')
139
140
  end
140
141
 
141
- it 'logs start and finish' do
142
- @maid.clean
142
+ it 'logs the beginning' do
143
+ expect(File.read(logfile)).to match(/Started/)
144
+ end
143
145
 
144
- expect(logger).to have_received(:info).with('Started')
145
- expect(logger).to have_received(:info).with('Finished')
146
+ it 'logs the end' do
147
+ expect(File.read(logfile)).to match(/Finished/)
146
148
  end
147
149
 
148
150
  it 'follows the given rules' do
149
- expect(@maid).to receive(:follow_rules)
150
- @maid.clean
151
+ expect(File.exist?('/tmp/maid-specs/perfect_man')).to be false
152
+ expect(File.exist?('/tmp/maid-specs/buffalo_fuzz')).to be true
151
153
  end
152
154
  end
153
155
 
@@ -166,17 +168,17 @@ module Maid
166
168
  end
167
169
 
168
170
  context 'when there is a LoadError' do
169
- let(:maid) { Maid.new }
171
+ let(:maid) { Maid.new(**test_defaults) }
170
172
 
171
173
  before do
172
174
  allow(Kernel).to receive(:load).and_raise(LoadError)
173
- allow(Logger).to receive(:warn)
175
+ FileUtils.rm_rf(logfile)
174
176
  end
175
177
 
176
178
  it 'gives an error on STDERR if there is a LoadError' do
177
179
  maid.load_rules
178
180
 
179
- expect(logger).to have_received(:warn).once
181
+ expect(File.read(logfile)).to match(/LoadError/)
180
182
  end
181
183
  end
182
184
  end
@@ -218,30 +220,37 @@ module Maid
218
220
  end
219
221
 
220
222
  # FIXME: Example is too long, shouldn't need the rubocop::disable
221
- it 'accepts a hash of options and passes them to Listen' do # rubocop:disable RSpec/ExampleLength
222
- hash = { some: :options }
223
- FileUtils.mkdir_p('some_dir')
223
+ context 'with a hash of options' do
224
+ let(:hash) { { some: :options } }
225
+ let(:listener) { double('listener') }
224
226
 
225
- @maid.watch('some_dir', hash) do
226
- rule 'test' do
227
+ before do
228
+ FileUtils.mkdir_p('some_dir')
229
+ @maid.watch('some_dir', hash) do
230
+ rule 'test' do
231
+ end
227
232
  end
228
233
  end
229
234
 
230
- listener = double('listener')
231
-
232
- expect(Listen).to receive(:to) do |dir, opts|
233
- expect(dir).to eq File.expand_path('some_dir')
234
- expect(opts).to eq(hash)
235
- listener
236
- end
235
+ it 'passes them to Listen' do
236
+ expect(Listen).to receive(:to) do |dir, opts|
237
+ expect(dir).to eq File.expand_path('some_dir')
238
+ expect(opts).to eq(hash)
239
+ listener
240
+ end
237
241
 
238
- expect(listener).to receive(:start)
242
+ expect(listener).to receive(:start)
239
243
 
240
- @maid.watches.last.run
244
+ @maid.watches.last.run
245
+ end
241
246
  end
242
247
 
243
248
  context('with a non-existent directory') do
244
- let(:maid) { Maid.new }
249
+ let(:maid) { Maid.new(**test_defaults) }
250
+
251
+ before do
252
+ FileUtils.rm_rf(logfile)
253
+ end
245
254
 
246
255
  it 'raises with an intelligible message' do
247
256
  expect { maid.watch('/doesnt_exist/') }.to raise_error(/file.*exist/)
@@ -256,14 +265,18 @@ module Maid
256
265
  rescue StandardError # rubocop:disable Lint/SuppressedException
257
266
  end
258
267
 
259
- expect(logger).to have_received(:warn).with(/file.*exist/)
268
+ expect(File.read(logfile)).to match(/file.*exist/)
260
269
  end
261
270
  end
262
271
  end
263
272
 
264
- describe '#repeat', fake_zoneinfo: true do
273
+ describe '#repeat', fake_zoneinfo: false do
265
274
  before do
266
- @maid = Maid.new
275
+ # Avoid FakeFS error when the logfile doesn't already exist.
276
+ FileUtils.mkdir_p(File.dirname(logfile))
277
+ FileUtils.touch(logfile)
278
+
279
+ @maid = Maid.new(log_device: logfile)
267
280
  end
268
281
 
269
282
  it 'adds a repeat to the list of repeats' do
@@ -277,53 +290,50 @@ module Maid
277
290
  expect(@maid.repeats.first.timestring).to eq('1s')
278
291
  end
279
292
 
280
- # FIXME: Example is too long, shouldn't need the rubocop::disable
281
- it 'accepts a hash of options and passes them to Rufus' do # rubocop:disable RSpec/ExampleLength
282
- scheduler = double('scheduler')
283
- expect(Rufus::Scheduler).to receive(:singleton).and_return(scheduler)
293
+ context 'with a hash of options' do
294
+ let(:scheduler) { double('scheduler') }
295
+ let(:hash) { { some: :options } }
296
+
297
+ before do
298
+ allow(Rufus::Scheduler).to receive(:singleton).and_return(scheduler)
284
299
 
285
- hash = { some: :options }
286
- @maid.repeat('1s', hash) do
287
- rule 'test' do
300
+ @maid.repeat('1s', hash) do
301
+ rule 'test' do
302
+ end
288
303
  end
289
304
  end
290
305
 
291
- expect(scheduler).to receive(:repeat).with('1s', hash)
306
+ it 'passes them to Rufus' do
307
+ expect(scheduler).to receive(:repeat).with('1s', hash)
292
308
 
293
- @maid.repeats.last.run
309
+ @maid.repeats.last.run
310
+ end
294
311
  end
295
312
  end
296
313
 
297
314
  describe '#follow_rules' do
298
- # FIXME: Example is too long, shouldn't need the rubocop::disable
299
- it 'follows each rule' do # rubocop:disable RSpec/ExampleLength
300
- n = 3
301
- maid = Maid.new
302
-
303
- rules = (1..n).map do |i|
304
- d = double("rule ##{i}", description: 'description')
305
- expect(d).to receive(:follow)
306
- d
307
- end
308
- maid.instance_eval { @rules = rules }
315
+ let(:maid) { Maid.new(**test_defaults) }
309
316
 
317
+ it 'follows each rule' do
318
+ # FIXME: This should run in a before and rules should be a let, but it
319
+ # fails when arranged that way.
320
+ rules = [spy(Rule), spy(Rule), spy(Rule)]
321
+ maid.instance_eval { @rules = rules }
310
322
  maid.follow_rules
311
323
 
312
- expect(logger).to have_received(:info).exactly(n).times
324
+ expect(rules).to all(have_received(:follow).once)
313
325
  end
314
326
  end
315
327
 
316
328
  describe '#cmd' do
317
- before do
318
- @maid = Maid.new
319
- end
329
+ let(:maid) { Maid.new(log_device: logfile) }
320
330
 
321
331
  it 'reports `not-a-real-command` as not being a supported command' do
322
- expect { @maid.cmd('not-a-real-command arg1 arg2') }.to raise_error(NotImplementedError)
332
+ expect { maid.cmd('not-a-real-command arg1 arg2') }.to raise_error(NotImplementedError)
323
333
  end
324
334
 
325
335
  it 'reports `echo` as a real command' do
326
- expect { @maid.cmd('echo .') }.not_to raise_error
336
+ expect { maid.cmd('echo .') }.not_to raise_error
327
337
  end
328
338
  end
329
339
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module Maid
4
4
  module Rake
5
- describe SingleRule, fakefs: true do
5
+ describe SingleRule, :fakefs do
6
6
  subject(:single_rule) { described_class.new name, task }
7
7
  let(:name) { double(:rule_description) }
8
8
  let(:task) { proc {} }