maid 0.10.0.pre.alpha.2 → 0.10.0.pre.alpha.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8d0fa40b9069dd973a12021c729407e1f98719ea96e8ede17aff4426cd8d9eb
4
- data.tar.gz: ed334d76e2cdc9ba723e600a6f371efab241ea24c080e7009425dee9a99d52f4
3
+ metadata.gz: 0be19c0367daaad7154c2649987ff99d97702a964ca2c6af5331fd876da16d7a
4
+ data.tar.gz: cb3598e65e48db7529b0584c041eb3f87fdbf66b57030d83f2ac981251de74a3
5
5
  SHA512:
6
- metadata.gz: 4be4e2a2771566d670b9b7a5d5966c334e3ca58c7c204dab7182654821b47b6076dc2ceb0e880de1a2e6966f110fee7422d289ffa058c9ba6861287afd2f60be
7
- data.tar.gz: 124d6e7f02282b4772b4a849acfad62efdc3277c08ea0696cd32b57672e199104bf314e53b609c47960e026b2073f4aba55d441de5061600fa9f499f9e90ceae
6
+ metadata.gz: d887908840f3369f2250226d888563444431f95cd662a50cdaddde49b0883bfb82958788516e9dc56d5329a8eefbdf470c761c1645d004cba2193c050bab60c2
7
+ data.tar.gz: a6349bb2e1d6e1b7dbd8358c7cf7e5f1955f2ae873d5b3286212c955e3ae11bb3ab743f3291f25c633bbda571caa0f0e90364a151f150789c2c8020698ab84ab
@@ -39,7 +39,7 @@ jobs:
39
39
  # Uncomment for pre-releases, see
40
40
  # https://github.com/maid/maid/wiki/Release-Process
41
41
  prerelease: true
42
- release-as: v0.10.0-alpha.2
42
+ release-as: v0.10.0-alpha.3
43
43
  - name: Set up Ruby
44
44
  uses: ruby/setup-ruby@v1
45
45
  with:
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.10.0-alpha.2"
2
+ ".": "0.10.0-alpha.3"
3
3
  }
data/.rubocop_todo.yml CHANGED
@@ -66,7 +66,7 @@ Metrics/MethodLength:
66
66
  # Offense count: 4
67
67
  # Configuration parameters: CountComments, CountAsOne.
68
68
  Metrics/ModuleLength:
69
- Max: 711
69
+ Max: 753
70
70
 
71
71
  # Offense count: 2
72
72
  # Configuration parameters: AllowedMethods, AllowedPatterns.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.0-alpha.3](https://github.com/maid/maid/compare/v0.10.0-alpha.2...v0.10.0-alpha.3) (2023-04-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * **maid:** improve `#watch` error message ([#287](https://github.com/maid/maid/issues/287)) ([0894cd6](https://github.com/maid/maid/commit/0894cd69665d5d9fe775b6b3df5a247f22f217d6))
9
+ * **tools:** add option to disable clobbering destination for `#move` ([#284](https://github.com/maid/maid/issues/284)) ([979413f](https://github.com/maid/maid/commit/979413fe284b61b43b33ba2169e72ed23043bcca))
10
+
3
11
  ## [0.10.0-alpha.2](https://github.com/maid/maid/compare/v0.10.0-alpha.1...v0.10.0-alpha.2) (2023-03-28)
4
12
 
5
13
 
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ require 'bundler'
2
2
  require 'rake'
3
3
  require 'rspec/core/rake_task'
4
4
  require 'yard'
5
+ require 'rake/notes/rake_task'
5
6
 
6
7
  task default: :spec
7
8
 
data/lib/maid/maid.rb CHANGED
@@ -88,6 +88,15 @@ class Maid::Maid
88
88
  end
89
89
 
90
90
  def watch(path, options = {}, &block)
91
+ full_path = File.expand_path(path)
92
+
93
+ unless File.directory?(full_path)
94
+ message = "#{full_path} cannot be a file and it must exist in order to watch it"
95
+
96
+ warn(message)
97
+ raise message
98
+ end
99
+
91
100
  @watches << ::Maid::Watch.new(self, path, options, &block)
92
101
  end
93
102
 
@@ -140,7 +149,7 @@ class Maid::Maid
140
149
  def default_trash_path
141
150
  # TODO: Refactor module declaration so this can be `Platform`
142
151
  if Maid::Platform.linux?
143
- # See the [FreeDesktop.org Trash specification](http://www.ramendik.ru/docs/trashspec.html)
152
+ # See the [FreeDesktop.org Trash specification](https://archive.is/cXir4)
144
153
  path = "#{XDG['DATA_HOME']}/Trash/files"
145
154
  elsif Maid::Platform.osx?
146
155
  path = File.expand_path('~/.Trash')
data/lib/maid/tools.rb CHANGED
@@ -24,33 +24,50 @@ module Maid::Tools
24
24
  # For showing deprecation notices
25
25
  include Deprecated
26
26
 
27
- # Move `sources` to a `destination` directory.
27
+ # Moves `sources` file(s) to a `destination` directory.
28
28
  #
29
29
  # Movement is only allowed to directories that already exist. If your
30
30
  # intention is to rename, see the `rename` method.
31
31
  #
32
- # ## Examples
33
- #
34
- # Single path:
35
- #
36
- # move('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/')
37
- #
38
- # Multiple paths:
39
- #
40
- # move(['~/Downloads/foo.zip', '~/Downloads/bar.zip'], '~/Archive/Software/Mac OS X/')
41
- # move(dir('~/Downloads/*.zip'), '~/Archive/Software/Mac OS X/')
42
- def move(sources, destination)
43
- expanded_destination = expand(destination)
44
-
45
- if File.directory?(expanded_destination)
32
+ # @example Single source file
33
+ # move('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/')
34
+ #
35
+ # @example Multiple source files
36
+ # move(['~/Downloads/foo.zip', '~/Downloads/bar.zip'],
37
+ # '~/Archive/Software/Mac OS X/')
38
+ # move(dir('~/Downloads/*.zip'), '~/Archive/Software/Mac OS X/')
39
+ #
40
+ # @example Overwrite destination file if it already exists
41
+ # move('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/')
42
+ # move('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/', clobber:
43
+ # true)
44
+ #
45
+ # @example Skip file if it already exists at destination
46
+ # move('~/Downloads/foo.zip', '~/Archive/Software/Mac OS X/', clobber:
47
+ # false)
48
+ #
49
+ # @param sources [String, Array<String>] the paths to the source files to
50
+ # move
51
+ # @param destination_dir [String] path of the directory where to move
52
+ # `sources` to
53
+ # @param [Hash] kwargs the arguments to modify behaviour
54
+ # @option kwargs [Boolean] :clobber (true) `true` to overwrite destination
55
+ # file if it exists, `false` to skip moving file if it exists
56
+ def move(sources, destination_dir, clobber: true)
57
+ expanded_destination_dir = expand(destination_dir)
58
+
59
+ if File.directory?(expanded_destination_dir)
46
60
  expand_all(sources).each do |source|
47
- log("move #{sh_escape(source)} #{sh_escape(expanded_destination)}")
48
- FileUtils.mv(source, expanded_destination, **@file_options)
61
+ log("move #{sh_escape(source)} #{sh_escape(expanded_destination_dir)}")
62
+
63
+ unless skip_move?(source, expanded_destination_dir, clobber)
64
+ FileUtils.mv(source, expanded_destination_dir, **@file_options)
65
+ end
49
66
  end
50
67
  else
51
68
  # Unix `mv` warns about the target not being a directory with multiple sources. Maid checks the same.
52
- warn("skipping move because #{sh_escape(expanded_destination)} is not a" \
53
- "directory (use 'mkdir' to create first, or use 'rename')")
69
+ warn("skipping move because #{sh_escape(expanded_destination_dir)} " \
70
+ "is not a directory (use 'mkdir' to create first, or use 'rename')")
54
71
  end
55
72
  end
56
73
 
@@ -1006,4 +1023,29 @@ module Maid::Tools
1006
1023
  []
1007
1024
  end
1008
1025
  end
1026
+
1027
+ # Predicate to tell whether the file should be skipped when moving.
1028
+ # @param source_path [String] the path to the source file
1029
+ # @param destination_dir [String] the path to the destination directory
1030
+ # @param clobber [Boolean] `true` to overwrite existing destination file,
1031
+ # `false` otherwise
1032
+ # @return [Boolean] whether to skip the move
1033
+ def skip_move?(source_path, destination_dir, clobber)
1034
+ destination_path = File.join(destination_dir, File.basename(source_path))
1035
+
1036
+ # if the destination file doesn't exist, we can move.
1037
+ return false unless File.exist?(destination_path)
1038
+
1039
+ log("#{destination_path} already exists")
1040
+
1041
+ # figure out whether to overwrite the existing destination file.
1042
+ if clobber
1043
+ log("clobbering enabled, moving #{File.basename(source_path)} anyway")
1044
+
1045
+ return false
1046
+ end
1047
+ log("clobbering disabled, skipping move for #{File.basename(source_path)}")
1048
+
1049
+ true
1050
+ end
1009
1051
  end
data/lib/maid/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Maid
4
- VERSION = '0.10.0-alpha.2'
4
+ VERSION = '0.10.0-alpha.3'
5
5
  SUMMARY = 'Be lazy. Let Maid clean up after you, based on rules you define. ' \
6
6
  'Think of it as "Hazel for hackers".'
7
7
  end
data/maid.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ['Benjamin Oakes', 'Coaxial']
9
9
  s.email = ['hello@benjaminoakes.com', 'c+rubygems@64b.it']
10
- s.license = 'GPLv2'
10
+ s.license = 'GPL-2.0'
11
11
  s.homepage = 'http://github.com/maid/maid'
12
12
  s.summary = Maid::SUMMARY
13
13
  s.description = s.summary
@@ -53,6 +53,7 @@ Gem::Specification.new do |s|
53
53
  s.add_development_dependency('guard-rubocop')
54
54
  s.add_development_dependency('pry-byebug')
55
55
  s.add_development_dependency('rake', '~> 13.0.6')
56
+ s.add_development_dependency('rake-notes')
56
57
  s.add_development_dependency('redcarpet', '~> 3.6.0') # Soft dependency of `yard`
57
58
  s.add_development_dependency('rspec', '~> 3.12.0')
58
59
  s.add_development_dependency('rubocop')
@@ -1,12 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Maid
4
- describe Maid do
4
+ describe Maid, fakefs: true do
5
5
  let(:logger) { instance_spy('Logger') }
6
6
 
7
7
  before do
8
8
  allow(Logger).to receive(:new).and_return(logger)
9
- allow(FileUtils).to receive(:mkdir_p)
10
9
  end
11
10
 
12
11
  describe '.new' do
@@ -33,8 +32,11 @@ module Maid
33
32
  end
34
33
 
35
34
  it 'makes the log directory in case it does not exist' do
36
- expect(FileUtils).to receive(:mkdir_p).with('/home/username/log')
35
+ expect(File.exist?('/home/username/log')).to be false
36
+
37
37
  Maid.new(log_device: '/home/username/log/maid.log')
38
+
39
+ expect(File.exist?('/home/username/log')).to be true
38
40
  end
39
41
 
40
42
  it 'takes a logger object during intialization' do
@@ -58,8 +60,9 @@ module Maid
58
60
 
59
61
  it 'set the trash to the correct default path' do
60
62
  trash_path = "#{@home}/.local/share/Trash/files/"
61
- expect(FileUtils).to receive(:mkdir_p).with(trash_path).once
63
+
62
64
  maid = Maid.new
65
+
63
66
  expect(maid.trash_path).to eq(trash_path)
64
67
  end
65
68
  end
@@ -71,7 +74,7 @@ module Maid
71
74
 
72
75
  it 'sets the trash to the correct default path' do
73
76
  trash_path = "#{@home}/.Trash/"
74
- expect(FileUtils).to receive(:mkdir_p).with(trash_path).once
77
+
75
78
  maid = Maid.new
76
79
  expect(maid.trash_path).to eq(trash_path)
77
80
  end
@@ -86,8 +89,9 @@ module Maid
86
89
 
87
90
  it 'sets the trash to the given path, if provided' do
88
91
  trash_path = '/home/username/tmp/my_trash/'
89
- expect(FileUtils).to receive(:mkdir_p).with(trash_path).once
92
+
90
93
  maid = Maid.new(trash_path: trash_path)
94
+
91
95
  expect(maid.trash_path).not_to be_nil
92
96
  expect(maid.trash_path).to eq(trash_path)
93
97
  end
@@ -198,6 +202,7 @@ module Maid
198
202
  before do
199
203
  allow(Listen).to receive(:to)
200
204
  allow(Listen).to receive(:start)
205
+ FileUtils.mkdir_p('watch_dir')
201
206
  @maid = Maid.new
202
207
  end
203
208
 
@@ -215,6 +220,8 @@ module Maid
215
220
  # FIXME: Example is too long, shouldn't need the rubocop::disable
216
221
  it 'accepts a hash of options and passes them to Listen' do # rubocop:disable RSpec/ExampleLength
217
222
  hash = { some: :options }
223
+ FileUtils.mkdir_p('some_dir')
224
+
218
225
  @maid.watch('some_dir', hash) do
219
226
  rule 'test' do
220
227
  end
@@ -232,9 +239,29 @@ module Maid
232
239
 
233
240
  @maid.watches.last.run
234
241
  end
242
+
243
+ context('with a non-existent directory') do
244
+ let(:maid) { Maid.new }
245
+
246
+ it 'raises with an intelligible message' do
247
+ expect { maid.watch('/doesnt_exist/') }.to raise_error(/file.*exist/)
248
+ end
249
+
250
+ it 'logs an intelligible message' do
251
+ begin
252
+ maid.watch('/doesnt_exist')
253
+ # Suppressing the exception is fine, because we just want to test
254
+ # that the message is logged when it throws and the test above
255
+ # checks that the exception is raised.
256
+ rescue StandardError # rubocop:disable Lint/SuppressedException
257
+ end
258
+
259
+ expect(logger).to have_received(:warn).with(/file.*exist/)
260
+ end
261
+ end
235
262
  end
236
263
 
237
- describe '#repeat' do
264
+ describe '#repeat', fake_zoneinfo: true do
238
265
  before do
239
266
  @maid = Maid.new
240
267
  end
@@ -73,6 +73,59 @@ module Maid
73
73
  @maid.move([@src_file, another_file], @dst_dir)
74
74
  end
75
75
  end
76
+
77
+ context 'when the destination file already exists' do
78
+ let(:src_file) { '/tmp/src/test_file' }
79
+ let(:dst_dir) { '/tmp/dest/' }
80
+ let(:dst_file) { File.join(dst_dir, File.basename(src_file)) }
81
+ let(:maid) { Maid.new(logger: @logger) }
82
+
83
+ before do
84
+ FileUtils.mkdir_p(File.dirname(src_file))
85
+ FileUtils.mkdir_p(dst_dir)
86
+ FileUtils.touch(src_file)
87
+ FileUtils.touch(dst_file)
88
+ end
89
+
90
+ after do
91
+ FileUtils.rm_rf([src_file, dst_file])
92
+ end
93
+
94
+ context 'by default' do
95
+ let!(:original_mtime) { File.stat(dst_file).mtime }
96
+
97
+ before do
98
+ maid.move(src_file, dst_dir)
99
+ end
100
+
101
+ it 'logs an info message' do
102
+ expect(@logger).to have_received(:info).with(/already/)
103
+ expect(@logger).to have_received(:info).with(/anyway/)
104
+ expect(@logger).not_to have_received(:info).with(/skipping/)
105
+ end
106
+
107
+ it 'overwrites destination' do
108
+ expect(File.stat(dst_file).mtime).not_to eq(original_mtime)
109
+ end
110
+ end
111
+
112
+ context 'when clobber: false' do
113
+ let!(:original_mtime) { File.stat(dst_file).mtime }
114
+
115
+ before do
116
+ maid.move(src_file, dst_dir, clobber: false)
117
+ end
118
+
119
+ it 'logs an info message' do
120
+ expect(@logger).not_to have_received(:info).with(/anyway/)
121
+ expect(@logger).to have_received(:info).with(/skipping/)
122
+ end
123
+
124
+ it "doesn't overwrite the destination file" do
125
+ expect(File.stat(dst_file).mtime).to eq(original_mtime)
126
+ end
127
+ end
128
+ end
76
129
  end
77
130
 
78
131
  describe '#rename' do
data/spec/spec_helper.rb CHANGED
@@ -25,6 +25,39 @@ RSpec.configure do |config|
25
25
  end
26
26
  config.include(FakeFS::SpecHelpers, fakefs: true)
27
27
  config.raise_errors_for_deprecations!
28
+
29
+ config.filter_run focus: true
30
+ config.run_all_when_everything_filtered = true
31
+
32
+ # NOTE: If a test fails because ENOENT /usr/share/zoneinfo/Africa/Abidjan,
33
+ # add `fake_zoneinfo: true` to the describe:
34
+ # `describe(MyClass, fake_zoneinfo: true do`
35
+ config.before(:context, fake_zoneinfo: true) do
36
+ # Rufus needs zoneinfo data to run, but when using FakeFS,
37
+ # /usr/share/zoneinfo doesn't exist on the FakeFS.
38
+ # On Linux, we just have to FakeFS::FileSystem.clone the directory and it
39
+ # just works.
40
+ # OSX is, of course, special. /usr/share/zoneinfo is a symlink on that
41
+ # platform, and `.clone` doesn't seem to be following symlinks. Instead, we
42
+ # have to copy the zoneinfo data to a temporary directory on the live
43
+ # filesystem, enable the FakeFS, clone that temporary directory, create
44
+ # /usr/share/zoneinfo onto the FakeFS, and finally copy the files into it.
45
+ # This way, they're available in the FakeFS where Rufus can find them.
46
+ include FakeFS::SpecHelpers
47
+ FakeFS.activate!
48
+
49
+ if Maid::Platform.osx?
50
+ FakeFS.deactivate!
51
+ FileUtils.mkdir_p('/tmp/')
52
+ FileUtils.cp_r('/usr/share/zoneinfo/', '/tmp/')
53
+ FakeFS.activate!
54
+ FakeFS::FileSystem.clone('/tmp/zoneinfo/')
55
+ FileUtils.mkdir_p('/usr/share/')
56
+ FileUtils.cp_r('/tmp/zoneinfo/', '/usr/share/')
57
+ end
58
+
59
+ FakeFS::FileSystem.clone('/usr/share/zoneinfo') if Maid::Platform.linux?
60
+ end
28
61
  end
29
62
 
30
63
  RSpec::Matchers.define :have_deprecated_method do |expected|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0.pre.alpha.2
4
+ version: 0.10.0.pre.alpha.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Oakes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-03-31 00:00:00.000000000 Z
12
+ date: 2023-04-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: deprecated
@@ -295,6 +295,20 @@ dependencies:
295
295
  - - "~>"
296
296
  - !ruby/object:Gem::Version
297
297
  version: 13.0.6
298
+ - !ruby/object:Gem::Dependency
299
+ name: rake-notes
300
+ requirement: !ruby/object:Gem::Requirement
301
+ requirements:
302
+ - - ">="
303
+ - !ruby/object:Gem::Version
304
+ version: '0'
305
+ type: :development
306
+ prerelease: false
307
+ version_requirements: !ruby/object:Gem::Requirement
308
+ requirements:
309
+ - - ">="
310
+ - !ruby/object:Gem::Version
311
+ version: '0'
298
312
  - !ruby/object:Gem::Dependency
299
313
  name: redcarpet
300
314
  requirement: !ruby/object:Gem::Requirement
@@ -518,7 +532,7 @@ files:
518
532
  - spec/spec_helper.rb
519
533
  homepage: http://github.com/maid/maid
520
534
  licenses:
521
- - GPLv2
535
+ - GPL-2.0
522
536
  metadata:
523
537
  bug_tracker_uri: https://github.com/maid/maid/issues
524
538
  changelog_uri: https://github.com/maid/maid/blob/master/CHANGELOG.md
@@ -542,7 +556,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
542
556
  - !ruby/object:Gem::Version
543
557
  version: 1.3.1
544
558
  requirements: []
545
- rubygems_version: 3.3.26
559
+ rubygems_version: 3.4.6
546
560
  signing_key:
547
561
  specification_version: 4
548
562
  summary: Be lazy. Let Maid clean up after you, based on rules you define. Think of