maid 0.10.0 → 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/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 +16 -0
- data/Dockerfile +13 -0
- data/Gemfile.lock +226 -0
- data/Guardfile +2 -0
- data/README.md +82 -49
- data/Rakefile +9 -0
- data/fixtures/files/test_rules.rb +3 -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 +12 -9
- data/release-please-config.json +18 -0
- data/script/docker-test +7 -0
- 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 +383 -224
- data/spec/lib/maid/trash_migration_spec.rb +7 -5
- data/spec/spec_helper.rb +9 -1
- metadata +89 -42
- data/Vagrantfile +0 -14
- data/script/vagrant-provision +0 -43
- data/script/vagrant-test +0 -7
- data/script/vagrant-test-all +0 -34
    
        data/spec/lib/maid/tools_spec.rb
    CHANGED
    
    | @@ -1,19 +1,32 @@ | |
| 1 1 | 
             
            require 'spec_helper'
         | 
| 2 2 |  | 
| 3 3 | 
             
            module Maid
         | 
| 4 | 
            -
              # NOTE:  | 
| 5 | 
            -
              #
         | 
| 6 | 
            -
              #  | 
| 7 | 
            -
              #
         | 
| 8 | 
            -
              # *  | 
| 9 | 
            -
               | 
| 4 | 
            +
              # NOTE: FakeFS is disabled intentionally, because it causes weird and subtle
         | 
| 5 | 
            +
              # issues that are hard to debug. See https://github.com/maid/maid/issues/315
         | 
| 6 | 
            +
              # FIXME: Split test suite into smaller files, likely one file per described
         | 
| 7 | 
            +
              # method.
         | 
| 8 | 
            +
              # FIXME: Replace *all* occurences of writing to `~/` in favour or `/tmp/`.
         | 
| 9 | 
            +
              # FIXME: Fix all the RSpec/MultipleMemoizeHelpers issues in Rubocop.
         | 
| 10 | 
            +
              describe Tools, fakefs: false do
         | 
| 11 | 
            +
                let(:filefixtures_path) { File.expand_path(File.dirname(__FILE__) + '../../../fixtures/files/') }
         | 
| 12 | 
            +
                let(:filefixtures_glob) { "#{filefixtures_path}/*" }
         | 
| 13 | 
            +
                let(:image_path) { File.join(filefixtures_path, 'ruby.jpg') }
         | 
| 14 | 
            +
                let(:unknown_path) { File.join(filefixtures_path, 'unknown.foo') }
         | 
| 15 | 
            +
                let(:test_basedir) { '/tmp/maid-specs' }
         | 
| 16 | 
            +
                let(:logfile) { "#{test_basedir}/test.log" }
         | 
| 17 | 
            +
                let(:maid) { Maid.new({ log_device: logfile }) }
         | 
| 18 | 
            +
             | 
| 10 19 | 
             
                before do
         | 
| 20 | 
            +
                  FileUtils.mkdir_p(File.dirname(logfile))
         | 
| 21 | 
            +
             | 
| 11 22 | 
             
                  @home = File.expand_path('~')
         | 
| 12 23 | 
             
                  @now = Time.now
         | 
| 13 24 |  | 
| 14 25 | 
             
                  expect(Maid.ancestors).to include(Tools)
         | 
| 15 26 |  | 
| 16 | 
            -
                   | 
| 27 | 
            +
                  # FIXME: Keeping the double to avoid a large refactor for now, but this
         | 
| 28 | 
            +
                  # probably isn't necessary anymore.
         | 
| 29 | 
            +
                  @logger = double('::Logger').as_null_object
         | 
| 17 30 | 
             
                  @maid = Maid.new(logger: @logger)
         | 
| 18 31 |  | 
| 19 32 | 
             
                  # Prevent warnings from showing when testing deprecated methods:
         | 
| @@ -23,19 +36,24 @@ module Maid | |
| 23 36 | 
             
                  allow(@maid).to receive(:cmd)
         | 
| 24 37 | 
             
                end
         | 
| 25 38 |  | 
| 39 | 
            +
                after do
         | 
| 40 | 
            +
                  # Ensure each test has a fresh logfile
         | 
| 41 | 
            +
                  FileUtils.rm_rf(File.dirname(logfile))
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 26 44 | 
             
                describe '#move' do
         | 
| 27 45 | 
             
                  before do
         | 
| 28 | 
            -
                    @src_dir = File.join( | 
| 29 | 
            -
                    @ | 
| 30 | 
            -
                    @src_file = File.join(@src_dir, @ | 
| 31 | 
            -
                    @dst_dir = File.join( | 
| 46 | 
            +
                    @src_dir = File.join(test_basedir, 'Source')
         | 
| 47 | 
            +
                    @filename = 'foo.zip'
         | 
| 48 | 
            +
                    @src_file = File.join(@src_dir, @filename)
         | 
| 49 | 
            +
                    @dst_dir = File.join(test_basedir, 'Destination')
         | 
| 32 50 | 
             
                    FileUtils.mkdir_p(File.expand_path(@src_dir))
         | 
| 33 51 | 
             
                    FileUtils.touch(File.expand_path(@src_file))
         | 
| 34 52 | 
             
                    FileUtils.mkdir_p(File.expand_path(@dst_dir))
         | 
| 35 53 | 
             
                  end
         | 
| 36 54 |  | 
| 37 55 | 
             
                  it 'moves expanded paths, passing file_options' do
         | 
| 38 | 
            -
                    dest_file = File.expand_path(File.join(@dst_dir, @ | 
| 56 | 
            +
                    dest_file = File.expand_path(File.join(@dst_dir, @filename))
         | 
| 39 57 |  | 
| 40 58 | 
             
                    @maid.move(@src_file, @dst_dir)
         | 
| 41 59 | 
             
                    expect(File.exist?(dest_file)).to be(true)
         | 
| @@ -48,12 +66,12 @@ module Maid | |
| 48 66 |  | 
| 49 67 | 
             
                  # FIXME: Example is too long, shouldn't need the rubocop::disable
         | 
| 50 68 | 
             
                  it 'handles multiple from paths' do # rubocop:disable RSpec/ExampleLength
         | 
| 51 | 
            -
                     | 
| 52 | 
            -
                    second_src_file = File.join(@src_dir,  | 
| 69 | 
            +
                    second_filename = 'bar.zip'
         | 
| 70 | 
            +
                    second_src_file = File.join(@src_dir, second_filename)
         | 
| 53 71 | 
             
                    FileUtils.touch(File.expand_path(second_src_file))
         | 
| 54 72 | 
             
                    src_files = [@src_file, second_src_file]
         | 
| 55 | 
            -
                    dst_file = File.expand_path(File.join(@dst_dir, @ | 
| 56 | 
            -
                    second_dst_file = File.expand_path(File.join(@dst_dir,  | 
| 73 | 
            +
                    dst_file = File.expand_path(File.join(@dst_dir, @filename))
         | 
| 74 | 
            +
                    second_dst_file = File.expand_path(File.join(@dst_dir, second_filename))
         | 
| 57 75 |  | 
| 58 76 | 
             
                    @maid.move(src_files, @dst_dir)
         | 
| 59 77 | 
             
                    expect(File.exist?(dst_file)).to be(true)
         | 
| @@ -61,47 +79,64 @@ module Maid | |
| 61 79 | 
             
                  end
         | 
| 62 80 |  | 
| 63 81 | 
             
                  context 'given the destination directory does not exist' do
         | 
| 82 | 
            +
                    let(:src_file) { File.join(test_basedir, 'test_file') }
         | 
| 83 | 
            +
                    let(:dst_dir) { File.join(test_basedir, 'dest') }
         | 
| 84 | 
            +
                    let(:dst_file) { File.join(dst_dir, File.basename(src_file)) }
         | 
| 85 | 
            +
             | 
| 64 86 | 
             
                    before do
         | 
| 65 | 
            -
                      FileUtils. | 
| 87 | 
            +
                      FileUtils.mkdir_p(File.dirname(src_file))
         | 
| 88 | 
            +
                      FileUtils.touch(src_file)
         | 
| 89 | 
            +
                      FileUtils.mkdir_p(dst_dir)
         | 
| 90 | 
            +
                      FileUtils.rmdir(dst_dir)
         | 
| 91 | 
            +
                      FileUtils.rm_rf(logfile)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                      maid.move(src_file, dst_dir)
         | 
| 66 94 | 
             
                    end
         | 
| 67 95 |  | 
| 68 | 
            -
                     | 
| 69 | 
            -
                       | 
| 70 | 
            -
             | 
| 96 | 
            +
                    after do
         | 
| 97 | 
            +
                      FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    it "doesn't move the file" do
         | 
| 101 | 
            +
                      expect(File.exist?(dst_file)).to be false
         | 
| 102 | 
            +
                    end
         | 
| 71 103 |  | 
| 72 | 
            -
             | 
| 73 | 
            -
                       | 
| 104 | 
            +
                    it 'logs a warning about it' do
         | 
| 105 | 
            +
                      expect(File.read(logfile)).to match(/skipping move.*#{File.dirname(dst_file)}.*not.*directory/)
         | 
| 74 106 | 
             
                    end
         | 
| 75 107 | 
             
                  end
         | 
| 76 108 |  | 
| 77 109 | 
             
                  context 'when the destination file already exists' do
         | 
| 78 | 
            -
                    let(:src_file) { ' | 
| 79 | 
            -
                    let(:dst_dir) { ' | 
| 110 | 
            +
                    let(:src_file) { File.join(test_basedir, 'test_file') }
         | 
| 111 | 
            +
                    let(:dst_dir) { File.join(test_basedir, 'dest') }
         | 
| 80 112 | 
             
                    let(:dst_file) { File.join(dst_dir, File.basename(src_file)) }
         | 
| 81 | 
            -
                    let(:maid) { Maid.new(logger: @logger) }
         | 
| 82 113 |  | 
| 83 114 | 
             
                    before do
         | 
| 84 115 | 
             
                      FileUtils.mkdir_p(File.dirname(src_file))
         | 
| 85 116 | 
             
                      FileUtils.mkdir_p(dst_dir)
         | 
| 86 117 | 
             
                      FileUtils.touch(src_file)
         | 
| 118 | 
            +
                      # Necessary to have different mtimes, they're identical otherwise
         | 
| 119 | 
            +
                      sleep 0.05
         | 
| 87 120 | 
             
                      FileUtils.touch(dst_file)
         | 
| 88 121 | 
             
                    end
         | 
| 89 122 |  | 
| 90 123 | 
             
                    after do
         | 
| 91 | 
            -
                      FileUtils.rm_rf([src_file, dst_file])
         | 
| 124 | 
            +
                      FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
         | 
| 92 125 | 
             
                    end
         | 
| 93 126 |  | 
| 94 127 | 
             
                    context 'by default' do
         | 
| 95 128 | 
             
                      let!(:original_mtime) { File.stat(dst_file).mtime }
         | 
| 96 129 |  | 
| 97 130 | 
             
                      before do
         | 
| 131 | 
            +
                        FileUtils.rm_rf(logfile)
         | 
| 132 | 
            +
             | 
| 98 133 | 
             
                        maid.move(src_file, dst_dir)
         | 
| 99 134 | 
             
                      end
         | 
| 100 135 |  | 
| 101 136 | 
             
                      it 'logs an info message' do
         | 
| 102 | 
            -
                        expect( | 
| 103 | 
            -
                        expect( | 
| 104 | 
            -
                        expect( | 
| 137 | 
            +
                        expect(File.read(logfile)).to match(/INFO.*already/)
         | 
| 138 | 
            +
                        expect(File.read(logfile)).to match(/INFO.*anyway/)
         | 
| 139 | 
            +
                        expect(File.read(logfile)).not_to match(/INFO.*skipping/)
         | 
| 105 140 | 
             
                      end
         | 
| 106 141 |  | 
| 107 142 | 
             
                      it 'overwrites destination' do
         | 
| @@ -113,12 +148,14 @@ module Maid | |
| 113 148 | 
             
                      let!(:original_mtime) { File.stat(dst_file).mtime }
         | 
| 114 149 |  | 
| 115 150 | 
             
                      before do
         | 
| 151 | 
            +
                        FileUtils.rm_rf(logfile)
         | 
| 152 | 
            +
             | 
| 116 153 | 
             
                        maid.move(src_file, dst_dir, clobber: false)
         | 
| 117 154 | 
             
                      end
         | 
| 118 155 |  | 
| 119 156 | 
             
                      it 'logs an info message' do
         | 
| 120 | 
            -
                        expect( | 
| 121 | 
            -
                        expect( | 
| 157 | 
            +
                        expect(File.read(logfile)).not_to match(/INFO.*anyway/)
         | 
| 158 | 
            +
                        expect(File.read(logfile)).to match(/INFO.*skipping/)
         | 
| 122 159 | 
             
                      end
         | 
| 123 160 |  | 
| 124 161 | 
             
                      it "doesn't overwrite the destination file" do
         | 
| @@ -129,138 +166,225 @@ module Maid | |
| 129 166 | 
             
                end
         | 
| 130 167 |  | 
| 131 168 | 
             
                describe '#rename' do
         | 
| 169 | 
            +
                  let(:src_file) { File.join(test_basedir, 'test_file') }
         | 
| 170 | 
            +
                  let(:dst_dir) { File.join(test_basedir, 'dest') }
         | 
| 171 | 
            +
                  let(:dst_file) { File.join(dst_dir, 'dest_test_file') }
         | 
| 172 | 
            +
             | 
| 132 173 | 
             
                  before do
         | 
| 133 | 
            -
                     | 
| 134 | 
            -
                    FileUtils. | 
| 135 | 
            -
             | 
| 136 | 
            -
                    @expanded_src_name = "#{@home}/Source/foo.zip"
         | 
| 174 | 
            +
                    FileUtils.mkdir_p(File.dirname(src_file))
         | 
| 175 | 
            +
                    FileUtils.touch(src_file)
         | 
| 176 | 
            +
                  end
         | 
| 137 177 |  | 
| 138 | 
            -
             | 
| 139 | 
            -
                     | 
| 140 | 
            -
                    @expanded_dst_name = "#{@home}/Destination/bar.zip"
         | 
| 178 | 
            +
                  after do
         | 
| 179 | 
            +
                    FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
         | 
| 141 180 | 
             
                  end
         | 
| 142 181 |  | 
| 143 182 | 
             
                  it 'creates needed directories' do
         | 
| 144 | 
            -
                    expect(File.directory?( | 
| 145 | 
            -
             | 
| 146 | 
            -
                     | 
| 183 | 
            +
                    expect(File.directory?(dst_dir)).to be(false)
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    maid.rename(src_file, dst_file)
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                    expect(File.directory?(dst_dir)).to be(true)
         | 
| 147 188 | 
             
                  end
         | 
| 148 189 |  | 
| 149 190 | 
             
                  it 'moves the file from the source to the destination' do
         | 
| 150 | 
            -
                    expect(File.exist?( | 
| 151 | 
            -
                    expect(File.exist?( | 
| 152 | 
            -
             | 
| 153 | 
            -
                     | 
| 154 | 
            -
             | 
| 191 | 
            +
                    expect(File.exist?(src_file)).to be(true)
         | 
| 192 | 
            +
                    expect(File.exist?(dst_file)).to be(false)
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                    maid.rename(src_file, dst_file)
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                    expect(File.exist?(src_file)).to be(false)
         | 
| 197 | 
            +
                    expect(File.exist?(dst_file)).to be(true)
         | 
| 155 198 | 
             
                  end
         | 
| 156 199 |  | 
| 157 200 | 
             
                  context 'given the target already exists' do
         | 
| 201 | 
            +
                    let(:src_file) { File.join(test_basedir, 'test_file') }
         | 
| 202 | 
            +
                    let(:dst_dir) { File.join(test_basedir, 'dest') }
         | 
| 203 | 
            +
                    let(:dst_file) { File.join(dst_dir, 'dest_test_file') }
         | 
| 204 | 
            +
             | 
| 158 205 | 
             
                    before do
         | 
| 159 | 
            -
                      FileUtils.mkdir_p( | 
| 160 | 
            -
                      FileUtils.touch( | 
| 206 | 
            +
                      FileUtils.mkdir_p(File.dirname(src_file))
         | 
| 207 | 
            +
                      FileUtils.touch(src_file)
         | 
| 208 | 
            +
                      # Necessary to have different mtimes, they're identical otherwise
         | 
| 209 | 
            +
                      sleep 0.05
         | 
| 210 | 
            +
                      FileUtils.mkdir_p(File.dirname(dst_file))
         | 
| 211 | 
            +
                      FileUtils.touch(dst_file)
         | 
| 212 | 
            +
             | 
| 213 | 
            +
                      maid.rename(src_file, dst_file)
         | 
| 214 | 
            +
                    end
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                    after do
         | 
| 217 | 
            +
                      FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
         | 
| 161 218 | 
             
                    end
         | 
| 162 219 |  | 
| 163 220 | 
             
                    it 'does not move' do
         | 
| 164 | 
            -
                      expect( | 
| 221 | 
            +
                      expect(File.stat(src_file).mtime).not_to eq(File.stat(dst_file).mtime)
         | 
| 222 | 
            +
                    end
         | 
| 165 223 |  | 
| 166 | 
            -
             | 
| 224 | 
            +
                    it 'logs a message' do
         | 
| 225 | 
            +
                      expect(File.read(logfile)).to match(/WARN.*skipping rename.*overwrite/)
         | 
| 167 226 | 
             
                    end
         | 
| 168 227 | 
             
                  end
         | 
| 169 228 | 
             
                end
         | 
| 170 229 |  | 
| 171 230 | 
             
                describe '#trash' do
         | 
| 231 | 
            +
                  let(:src_file) { File.join(test_basedir, 'test_file') }
         | 
| 232 | 
            +
                  let(:trash_file) { File.join(maid.trash_path, File.basename(src_file)) }
         | 
| 233 | 
            +
                  let(:dst_dir) { File.join(test_basedir, 'dest') }
         | 
| 234 | 
            +
                  let(:dst_file) { File.join(dst_dir, File.basename(src_file)) }
         | 
| 235 | 
            +
             | 
| 172 236 | 
             
                  before do
         | 
| 173 | 
            -
                     | 
| 174 | 
            -
                     | 
| 175 | 
            -
             | 
| 176 | 
            -
                    @src_file = File.join(@src_dir, @file_name)
         | 
| 177 | 
            -
                    FileUtils.mkdir_p(File.expand_path(@src_dir))
         | 
| 178 | 
            -
                    FileUtils.touch(File.expand_path(@src_file))
         | 
| 237 | 
            +
                    FileUtils.mkdir_p(File.dirname(src_file))
         | 
| 238 | 
            +
                    FileUtils.touch(src_file)
         | 
| 239 | 
            +
                  end
         | 
| 179 240 |  | 
| 180 | 
            -
             | 
| 241 | 
            +
                  after do
         | 
| 242 | 
            +
                    FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
         | 
| 181 243 | 
             
                  end
         | 
| 182 244 |  | 
| 183 245 | 
             
                  it 'moves the path to the trash' do
         | 
| 184 | 
            -
                     | 
| 185 | 
            -
                    expect(File.exist?( | 
| 246 | 
            +
                    maid.trash(src_file)
         | 
| 247 | 
            +
                    expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).to be(true)
         | 
| 186 248 | 
             
                  end
         | 
| 187 249 |  | 
| 188 250 | 
             
                  it 'uses a safe path if the target exists' do
         | 
| 189 251 | 
             
                    # Without an offset, ISO8601 parses to local time, which is what we want here.
         | 
| 190 252 | 
             
                    Timecop.freeze(Time.parse('2011-05-22T16:53:52')) do
         | 
| 191 | 
            -
                      FileUtils.touch( | 
| 192 | 
            -
                       | 
| 193 | 
            -
                      new_trash_file = File.join( | 
| 253 | 
            +
                      FileUtils.touch(trash_file)
         | 
| 254 | 
            +
                      maid.trash(src_file)
         | 
| 255 | 
            +
                      new_trash_file = File.join(maid.trash_path, File.basename(src_file) + ' 2011-05-22-16-53-52')
         | 
| 256 | 
            +
             | 
| 194 257 | 
             
                      expect(File.exist?(new_trash_file)).to be(true)
         | 
| 195 258 | 
             
                    end
         | 
| 196 259 | 
             
                  end
         | 
| 197 260 |  | 
| 198 | 
            -
                   | 
| 199 | 
            -
                     | 
| 200 | 
            -
                    second_src_file = File.join(@src_dir, second_file_name)
         | 
| 201 | 
            -
                    FileUtils.touch(File.expand_path(second_src_file))
         | 
| 202 | 
            -
                    src_files = [@src_file, second_src_file]
         | 
| 203 | 
            -
                    second_trash_file = File.join(@trash_path, second_file_name)
         | 
| 261 | 
            +
                  context 'with multiple files' do
         | 
| 262 | 
            +
                    let(:src_file2) { "#{src_file}_2" }
         | 
| 204 263 |  | 
| 205 | 
            -
                     | 
| 264 | 
            +
                    before do
         | 
| 265 | 
            +
                      FileUtils.touch(File.expand_path(src_file2))
         | 
| 266 | 
            +
                      maid.trash([src_file, src_file2])
         | 
| 267 | 
            +
                    end
         | 
| 206 268 |  | 
| 207 | 
            -
                     | 
| 208 | 
            -
             | 
| 209 | 
            -
             | 
| 269 | 
            +
                    after do
         | 
| 270 | 
            +
                      FileUtils.rm_rf([src_file, src_file2])
         | 
| 271 | 
            +
                      trash_file = File.join(maid.trash_path, File.basename(src_file))
         | 
| 272 | 
            +
                      trash_file2 = File.join(maid.trash_path, File.basename(src_file2))
         | 
| 273 | 
            +
                      FileUtils.rm_rf([trash_file, trash_file2])
         | 
| 274 | 
            +
                    end
         | 
| 210 275 |  | 
| 211 | 
            -
             | 
| 212 | 
            -
             | 
| 213 | 
            -
             | 
| 214 | 
            -
                     | 
| 215 | 
            -
                    expect(File.exist?(@trash_file)).not_to be(true)
         | 
| 276 | 
            +
                    it 'deletes all files' do
         | 
| 277 | 
            +
                      expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).to be(true)
         | 
| 278 | 
            +
                      expect(File.exist?(File.join(maid.trash_path, File.basename(src_file2)))).to be(true)
         | 
| 279 | 
            +
                    end
         | 
| 216 280 | 
             
                  end
         | 
| 217 281 |  | 
| 218 | 
            -
                   | 
| 219 | 
            -
                     | 
| 220 | 
            -
             | 
| 221 | 
            -
             | 
| 282 | 
            +
                  context 'when given the `:remove_over` option' do
         | 
| 283 | 
            +
                    context 'with files larger than :remove_over' do
         | 
| 284 | 
            +
                      let(:trash_file) { File.join(maid.trash_path, File.basename(src_file)) }
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                      before do
         | 
| 287 | 
            +
                        allow(maid).to receive(:disk_usage).and_return(1025)
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                        maid.trash(src_file, remove_over: 1.mb)
         | 
| 290 | 
            +
                      end
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                      after do
         | 
| 293 | 
            +
                        FileUtils.rm_rf(trash_file)
         | 
| 294 | 
            +
                      end
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                      it 'removes matching files' do
         | 
| 297 | 
            +
                        expect(File.exist?(src_file)).not_to be(true)
         | 
| 298 | 
            +
                      end
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                      it "doesn't move file to trash" do
         | 
| 301 | 
            +
                        expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).not_to be(true)
         | 
| 302 | 
            +
                      end
         | 
| 303 | 
            +
                    end
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                    context 'with files smaller than :remove_over' do
         | 
| 306 | 
            +
                      before do
         | 
| 307 | 
            +
                        allow(maid).to receive(:disk_usage).and_return(1023)
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                        maid.trash(src_file, remove_over: 1.mb)
         | 
| 310 | 
            +
                      end
         | 
| 311 | 
            +
             | 
| 312 | 
            +
                      it "doesn't delete them" do
         | 
| 313 | 
            +
                        expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).to be(true)
         | 
| 314 | 
            +
                      end
         | 
| 315 | 
            +
                    end
         | 
| 222 316 | 
             
                  end
         | 
| 223 317 | 
             
                end
         | 
| 224 318 |  | 
| 225 319 | 
             
                describe '#remove' do
         | 
| 320 | 
            +
                  let(:src_file) { File.join(test_basedir, 'test_file') }
         | 
| 321 | 
            +
             | 
| 226 322 | 
             
                  before do
         | 
| 227 | 
            -
                     | 
| 228 | 
            -
                     | 
| 229 | 
            -
                     | 
| 230 | 
            -
                    FileUtils.mkdir_p(File.expand_path(@src_dir))
         | 
| 231 | 
            -
                    FileUtils.touch(File.expand_path(@src_file))
         | 
| 323 | 
            +
                    FileUtils.mkdir_p(File.dirname(src_file))
         | 
| 324 | 
            +
                    FileUtils.touch(src_file)
         | 
| 325 | 
            +
                    FileUtils.rm_rf(logfile)
         | 
| 232 326 | 
             
                  end
         | 
| 233 327 |  | 
| 234 | 
            -
                   | 
| 235 | 
            -
                     | 
| 236 | 
            -
                    expect(File.exist?(@src_file)).to be(false)
         | 
| 328 | 
            +
                  after do
         | 
| 329 | 
            +
                    FileUtils.rm_rf(File.dirname(src_file))
         | 
| 237 330 | 
             
                  end
         | 
| 238 331 |  | 
| 239 | 
            -
                   | 
| 240 | 
            -
                     | 
| 241 | 
            -
             | 
| 332 | 
            +
                  context 'with the default options' do
         | 
| 333 | 
            +
                    before do
         | 
| 334 | 
            +
                      maid.remove(src_file)
         | 
| 335 | 
            +
                    end
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                    it 'removes expanded paths, passing options' do
         | 
| 338 | 
            +
                      expect(File.exist?(src_file)).to be(false)
         | 
| 339 | 
            +
                    end
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                    it 'logs the remove' do
         | 
| 342 | 
            +
                      expect(File.read(logfile)).to match(/INFO.*Removing #{src_file}/)
         | 
| 343 | 
            +
                    end
         | 
| 242 344 | 
             
                  end
         | 
| 243 345 |  | 
| 244 | 
            -
                   | 
| 245 | 
            -
                     | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 346 | 
            +
                  context 'with the :secure option set' do
         | 
| 347 | 
            +
                    before do
         | 
| 348 | 
            +
                      allow(FileUtils).to receive(:rm_r).and_call_original
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                      maid.remove(src_file, { secure: true })
         | 
| 351 | 
            +
                    end
         | 
| 352 | 
            +
             | 
| 353 | 
            +
                    it 'passes it to FileUtils.rm_r' do
         | 
| 354 | 
            +
                      expected_args = [File.expand_path(src_file), hash_including(secure: true)]
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                      expect(FileUtils).to have_received(:rm_r).with(*expected_args)
         | 
| 357 | 
            +
                    end
         | 
| 248 358 | 
             
                  end
         | 
| 249 359 |  | 
| 250 | 
            -
                   | 
| 251 | 
            -
                     | 
| 252 | 
            -
             | 
| 253 | 
            -
             | 
| 360 | 
            +
                  context 'with for :force option set' do
         | 
| 361 | 
            +
                    before do
         | 
| 362 | 
            +
                      allow(FileUtils).to receive(:rm_r).and_call_original
         | 
| 363 | 
            +
             | 
| 364 | 
            +
                      maid.remove(src_file, { force: true })
         | 
| 365 | 
            +
                    end
         | 
| 366 | 
            +
             | 
| 367 | 
            +
                    it 'sets the force option' do
         | 
| 368 | 
            +
                      expected_args = [File.expand_path(src_file), hash_including(force: true)]
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                      expect(FileUtils).to have_received(:rm_r).with(*expected_args)
         | 
| 371 | 
            +
                    end
         | 
| 254 372 | 
             
                  end
         | 
| 255 373 |  | 
| 256 | 
            -
                   | 
| 257 | 
            -
                     | 
| 258 | 
            -
                    FileUtils.touch(File.expand_path(second_src_file))
         | 
| 259 | 
            -
                    @src_files = [@src_file, second_src_file]
         | 
| 374 | 
            +
                  context 'with multiple paths' do
         | 
| 375 | 
            +
                    let(:src_files) { [src_file, "#{src_file}_2"] }
         | 
| 260 376 |  | 
| 261 | 
            -
                     | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 377 | 
            +
                    before do
         | 
| 378 | 
            +
                      FileUtils.touch(src_files)
         | 
| 379 | 
            +
             | 
| 380 | 
            +
                      maid.remove(src_files)
         | 
| 381 | 
            +
                    end
         | 
| 382 | 
            +
             | 
| 383 | 
            +
                    it 'deletes every path' do
         | 
| 384 | 
            +
                      src_files.each do |file|
         | 
| 385 | 
            +
                        expect(File.exist?(file)).to be(false)
         | 
| 386 | 
            +
                      end
         | 
| 387 | 
            +
                    end
         | 
| 264 388 | 
             
                  end
         | 
| 265 389 | 
             
                end
         | 
| 266 390 |  | 
| @@ -299,69 +423,96 @@ module Maid | |
| 299 423 | 
             
                  end
         | 
| 300 424 |  | 
| 301 425 | 
             
                  context 'with multiple directories' do
         | 
| 426 | 
            +
                    let(:src_file) { File.join(test_basedir, 'multi', 'test_file') }
         | 
| 427 | 
            +
                    let(:src_file2) { "#{src_file}_2" }
         | 
| 428 | 
            +
             | 
| 302 429 | 
             
                    before do
         | 
| 303 | 
            -
                       | 
| 304 | 
            -
                      FileUtils.touch( | 
| 305 | 
            -
                      FileUtils. | 
| 306 | 
            -
                      FileUtils.touch(@other_file)
         | 
| 430 | 
            +
                      FileUtils.mkdir_p(File.dirname(src_file))
         | 
| 431 | 
            +
                      FileUtils.touch(src_file)
         | 
| 432 | 
            +
                      FileUtils.touch(src_file2)
         | 
| 307 433 | 
             
                    end
         | 
| 308 434 |  | 
| 309 435 | 
             
                    it 'lists files in directories when using regexp-like glob patterns' do
         | 
| 310 | 
            -
                      expect( | 
| 436 | 
            +
                      expect(maid.dir("#{File.dirname(src_file)}/*")).to eq([src_file, src_file2])
         | 
| 311 437 | 
             
                    end
         | 
| 312 438 |  | 
| 313 439 | 
             
                    it 'lists files in directories when using recursive glob patterns' do
         | 
| 314 | 
            -
                       | 
| 440 | 
            +
                      # TODO: Once we ditch Ruby 2.7, we can do this instead:
         | 
| 441 | 
            +
                      # expect(maid.dir("#{File.dirname(src_file, 2)}/**/test_*")).to eq([src_file, src_file2])
         | 
| 442 | 
            +
                      expect(maid.dir("#{File.dirname(src_file)}/../**/test_*")).to eq([src_file, src_file2])
         | 
| 315 443 | 
             
                    end
         | 
| 316 444 | 
             
                  end
         | 
| 317 445 | 
             
                end
         | 
| 318 446 |  | 
| 319 447 | 
             
                describe '#files' do
         | 
| 320 | 
            -
                   | 
| 321 | 
            -
                    @file = (@dir = "#{@home}/Downloads") + '/foo.zip'
         | 
| 322 | 
            -
                    FileUtils.mkdir_p(@dir)
         | 
| 323 | 
            -
                    FileUtils.mkdir(@dir + '/notfile')
         | 
| 324 | 
            -
                  end
         | 
| 448 | 
            +
                  let(:file) { File.join(test_basedir, 'test_file') }
         | 
| 325 449 |  | 
| 326 | 
            -
                   | 
| 327 | 
            -
                     | 
| 328 | 
            -
             | 
| 329 | 
            -
             | 
| 450 | 
            +
                  context 'with a single file' do
         | 
| 451 | 
            +
                    before do
         | 
| 452 | 
            +
                      FileUtils.mkdir_p(File.dirname(file))
         | 
| 453 | 
            +
                      FileUtils.touch(file)
         | 
| 454 | 
            +
                    end
         | 
| 330 455 |  | 
| 331 | 
            -
             | 
| 332 | 
            -
             | 
| 333 | 
            -
                     | 
| 334 | 
            -
             | 
| 335 | 
            -
                     | 
| 456 | 
            +
                    after do
         | 
| 457 | 
            +
                      FileUtils.rm_rf(File.dirname(file))
         | 
| 458 | 
            +
                    end
         | 
| 459 | 
            +
             | 
| 460 | 
            +
                    it 'lists only files in a directory' do
         | 
| 461 | 
            +
                      expect(maid.files("#{File.dirname(file)}/*_file")).to eq([file])
         | 
| 462 | 
            +
                    end
         | 
| 336 463 | 
             
                  end
         | 
| 337 464 |  | 
| 338 465 | 
             
                  context 'with multiple files' do
         | 
| 466 | 
            +
                    let(:first_file) { "#{file}_1" }
         | 
| 467 | 
            +
                    let(:second_file) { "#{file}_2" }
         | 
| 468 | 
            +
             | 
| 339 469 | 
             
                    before do
         | 
| 340 | 
            -
                       | 
| 341 | 
            -
                      FileUtils.touch( | 
| 342 | 
            -
                      FileUtils.touch( | 
| 470 | 
            +
                      FileUtils.mkdir_p(File.dirname(first_file))
         | 
| 471 | 
            +
                      FileUtils.touch(first_file)
         | 
| 472 | 
            +
                      FileUtils.touch(second_file)
         | 
| 473 | 
            +
                    end
         | 
| 474 | 
            +
             | 
| 475 | 
            +
                    after do
         | 
| 476 | 
            +
                      FileUtils.rm_rf(File.dirname(first_file))
         | 
| 477 | 
            +
                      FileUtils.rm_rf(File.dirname(second_file))
         | 
| 343 478 | 
             
                    end
         | 
| 344 479 |  | 
| 345 480 | 
             
                    it 'list files in all provided globs' do
         | 
| 346 | 
            -
                      expect( | 
| 481 | 
            +
                      expect(maid.dir(["#{File.dirname(first_file)}/*_1",
         | 
| 482 | 
            +
                                       "#{File.dirname(second_file)}/*_2",])).to eq([first_file, second_file])
         | 
| 347 483 | 
             
                    end
         | 
| 348 484 |  | 
| 349 485 | 
             
                    it 'lists files when using regexp-like glob patterns' do
         | 
| 350 | 
            -
                      expect(@maid.dir( | 
| 486 | 
            +
                      expect(@maid.dir("#{File.dirname(first_file)}/*{1,2}")).to eq([first_file, second_file])
         | 
| 351 487 | 
             
                    end
         | 
| 352 488 | 
             
                  end
         | 
| 353 489 |  | 
| 354 490 | 
             
                  context 'with multiple directories' do
         | 
| 491 | 
            +
                    let(:first_file) { "#{file}_1" }
         | 
| 492 | 
            +
                    let(:second_file) { "#{file}_2" }
         | 
| 493 | 
            +
                    let(:first_dir) { "#{File.dirname(file)}/test_dir_1" }
         | 
| 494 | 
            +
                    let(:second_dir) { "#{File.dirname(file)}/test_dir_2" }
         | 
| 495 | 
            +
                    let(:test_dir) { File.dirname(file) }
         | 
| 496 | 
            +
             | 
| 355 497 | 
             
                    before do
         | 
| 356 | 
            -
                       | 
| 357 | 
            -
                       | 
| 358 | 
            -
                       | 
| 359 | 
            -
                       | 
| 360 | 
            -
                       | 
| 498 | 
            +
                      # Building this:
         | 
| 499 | 
            +
                      # "#{test_dir}/"
         | 
| 500 | 
            +
                      # ├── test_dir_1
         | 
| 501 | 
            +
                      # ├── test_dir_2
         | 
| 502 | 
            +
                      # ├── test_first_file
         | 
| 503 | 
            +
                      # └── test_second_file
         | 
| 504 | 
            +
                      FileUtils.mkdir_p(first_dir)
         | 
| 505 | 
            +
                      FileUtils.mkdir_p(second_dir)
         | 
| 506 | 
            +
                      FileUtils.touch(first_file)
         | 
| 507 | 
            +
                      FileUtils.touch(second_file)
         | 
| 361 508 | 
             
                    end
         | 
| 362 509 |  | 
| 363 | 
            -
                     | 
| 364 | 
            -
                       | 
| 510 | 
            +
                    after do
         | 
| 511 | 
            +
                      FileUtils.rm_rf(test_dir)
         | 
| 512 | 
            +
                    end
         | 
| 513 | 
            +
             | 
| 514 | 
            +
                    it 'only lists the files' do
         | 
| 515 | 
            +
                      expect(maid.dir("#{test_dir}/**/*{file}*")).to eq([first_file, second_file])
         | 
| 365 516 | 
             
                    end
         | 
| 366 517 | 
             
                  end
         | 
| 367 518 | 
             
                end
         | 
| @@ -397,23 +548,27 @@ module Maid | |
| 397 548 |  | 
| 398 549 | 
             
                describe '#find' do
         | 
| 399 550 | 
             
                  before do
         | 
| 400 | 
            -
                    @dir = File.join( | 
| 401 | 
            -
                    @ | 
| 402 | 
            -
                    @file = File.join(@dir, @ | 
| 551 | 
            +
                    @dir = File.join(test_basedir, 'Source')
         | 
| 552 | 
            +
                    @filename = 'foo.zip'
         | 
| 553 | 
            +
                    @file = File.join(@dir, @filename)
         | 
| 403 554 | 
             
                    FileUtils.mkdir_p(File.expand_path(@dir))
         | 
| 404 555 | 
             
                    FileUtils.touch(File.expand_path(@file))
         | 
| 405 556 | 
             
                    @dir_expand_path = File.expand_path(@dir)
         | 
| 406 | 
            -
                    @ | 
| 557 | 
            +
                    @fileexpand_path = File.expand_path(@file)
         | 
| 558 | 
            +
                  end
         | 
| 559 | 
            +
             | 
| 560 | 
            +
                  after do
         | 
| 561 | 
            +
                    FileUtils.rm_rf(@dir)
         | 
| 407 562 | 
             
                  end
         | 
| 408 563 |  | 
| 409 564 | 
             
                  it 'delegates to Find.find with an expanded path' do
         | 
| 410 565 | 
             
                    f = ->(arg) {}
         | 
| 411 | 
            -
                    expect(Find).to receive(:find).with(@ | 
| 566 | 
            +
                    expect(Find).to receive(:find).with(@fileexpand_path, &f)
         | 
| 412 567 | 
             
                    @maid.find(@file, &f)
         | 
| 413 568 | 
             
                  end
         | 
| 414 569 |  | 
| 415 570 | 
             
                  it "returns an array of all the files' names when no block is given" do
         | 
| 416 | 
            -
                    expect(@maid.find(@dir)).to contain_exactly(@dir_expand_path, @ | 
| 571 | 
            +
                    expect(@maid.find(@dir)).to contain_exactly(@dir_expand_path, @fileexpand_path)
         | 
| 417 572 | 
             
                  end
         | 
| 418 573 | 
             
                end
         | 
| 419 574 |  | 
| @@ -477,22 +632,26 @@ module Maid | |
| 477 632 |  | 
| 478 633 | 
             
                  context 'when the file does not exist' do
         | 
| 479 634 | 
             
                    it 'raises an error' do
         | 
| 480 | 
            -
                      expect(@maid).to receive(:cmd).and_return( | 
| 635 | 
            +
                      expect(@maid).to receive(:cmd).and_return("du: cannot access `foo.zip': No such file or directory")
         | 
| 481 636 | 
             
                      expect { @maid.disk_usage('foo.zip') }.to raise_error(RuntimeError)
         | 
| 482 637 | 
             
                    end
         | 
| 483 638 | 
             
                  end
         | 
| 484 639 | 
             
                end
         | 
| 485 640 |  | 
| 486 641 | 
             
                describe '#created_at' do
         | 
| 642 | 
            +
                  let(:file) { File.join(test_basedir, 'test_file') }
         | 
| 643 | 
            +
             | 
| 487 644 | 
             
                  before do
         | 
| 488 | 
            -
                     | 
| 645 | 
            +
                    FileUtils.mkdir_p(File.dirname(file))
         | 
| 646 | 
            +
                    FileUtils.touch(file)
         | 
| 647 | 
            +
                  end
         | 
| 648 | 
            +
             | 
| 649 | 
            +
                  after do
         | 
| 650 | 
            +
                    FileUtils.rm_rf(File.dirname(file))
         | 
| 489 651 | 
             
                  end
         | 
| 490 652 |  | 
| 491 653 | 
             
                  it 'gives the created time of the file' do
         | 
| 492 | 
            -
                     | 
| 493 | 
            -
                      FileUtils.touch(File.expand_path(@path))
         | 
| 494 | 
            -
                      expect(@maid.created_at(@path)).to eq(Time.now)
         | 
| 495 | 
            -
                    end
         | 
| 654 | 
            +
                    expect(maid.created_at(file)).to eq(File.stat(file).ctime)
         | 
| 496 655 | 
             
                  end
         | 
| 497 656 | 
             
                end
         | 
| 498 657 |  | 
| @@ -613,15 +772,21 @@ module Maid | |
| 613 772 | 
             
                end
         | 
| 614 773 |  | 
| 615 774 | 
             
                describe '#copy' do
         | 
| 616 | 
            -
                  let(:src_file) { File.join( | 
| 775 | 
            +
                  let(:src_file) { File.join(test_basedir, 'src', 'test_file') }
         | 
| 617 776 | 
             
                  let(:src_dir) { File.dirname(src_file) }
         | 
| 618 | 
            -
                  let(:dst_file) { File.join( | 
| 777 | 
            +
                  let(:dst_file) { File.join(test_basedir, 'dest', 'test_file') }
         | 
| 619 778 | 
             
                  let(:dst_dir) { File.dirname(dst_file) }
         | 
| 620 779 |  | 
| 621 780 | 
             
                  before do
         | 
| 622 781 | 
             
                    FileUtils.mkdir_p(File.expand_path(src_dir))
         | 
| 623 782 | 
             
                    FileUtils.touch(File.expand_path(src_file))
         | 
| 624 783 | 
             
                    FileUtils.mkdir_p(File.expand_path(dst_dir))
         | 
| 784 | 
            +
                    FileUtils.rm_rf(logfile)
         | 
| 785 | 
            +
                  end
         | 
| 786 | 
            +
             | 
| 787 | 
            +
                  after do
         | 
| 788 | 
            +
                    FileUtils.rm_rf(src_dir)
         | 
| 789 | 
            +
                    FileUtils.rm_rf(dst_dir)
         | 
| 625 790 | 
             
                  end
         | 
| 626 791 |  | 
| 627 792 | 
             
                  it 'copies expanded paths, passing file_options' do
         | 
| @@ -629,16 +794,23 @@ module Maid | |
| 629 794 | 
             
                    expect(File.exist?(File.expand_path(dst_file))).to be_truthy
         | 
| 630 795 | 
             
                  end
         | 
| 631 796 |  | 
| 632 | 
            -
                   | 
| 633 | 
            -
                     | 
| 634 | 
            -
             | 
| 797 | 
            +
                  context "when destination doesn't exist" do
         | 
| 798 | 
            +
                    before do
         | 
| 799 | 
            +
                      maid.copy(src_file, dst_dir)
         | 
| 800 | 
            +
                    end
         | 
| 801 | 
            +
             | 
| 802 | 
            +
                    it 'logs the copy' do
         | 
| 803 | 
            +
                      expect(File.read(logfile)).to match(/INFO.*cp.*#{dst_dir}/)
         | 
| 804 | 
            +
                    end
         | 
| 635 805 | 
             
                  end
         | 
| 636 806 |  | 
| 637 | 
            -
                   | 
| 638 | 
            -
                     | 
| 639 | 
            -
             | 
| 807 | 
            +
                  context 'when destination exists' do
         | 
| 808 | 
            +
                    it 'does not copy if the target already exists' do
         | 
| 809 | 
            +
                      FileUtils.touch(File.expand_path(dst_file))
         | 
| 810 | 
            +
                      expect(@logger).to receive(:warn)
         | 
| 640 811 |  | 
| 641 | 
            -
             | 
| 812 | 
            +
                      @maid.copy(src_file, dst_dir)
         | 
| 813 | 
            +
                    end
         | 
| 642 814 | 
             
                  end
         | 
| 643 815 |  | 
| 644 816 | 
             
                  context 'with multiple `from` paths' do
         | 
| @@ -658,22 +830,10 @@ module Maid | |
| 658 830 | 
             
                    end
         | 
| 659 831 | 
             
                  end
         | 
| 660 832 | 
             
                end
         | 
| 661 | 
            -
              end
         | 
| 662 | 
            -
             | 
| 663 | 
            -
              describe Tools, fakefs: false do
         | 
| 664 | 
            -
                let(:file_fixtures_path) { File.expand_path(File.dirname(__FILE__) + '../../../fixtures/files/') }
         | 
| 665 | 
            -
                let(:file_fixtures_glob) { "#{file_fixtures_path}/*" }
         | 
| 666 | 
            -
                let(:image_path) { File.join(file_fixtures_path, 'ruby.jpg') }
         | 
| 667 | 
            -
                let(:unknown_path) { File.join(file_fixtures_path, 'unknown.foo') }
         | 
| 668 | 
            -
             | 
| 669 | 
            -
                before do
         | 
| 670 | 
            -
                  @logger = double('Logger').as_null_object
         | 
| 671 | 
            -
                  @maid = Maid.new(logger: @logger)
         | 
| 672 | 
            -
                end
         | 
| 673 833 |  | 
| 674 834 | 
             
                describe '#dupes_in' do
         | 
| 675 835 | 
             
                  it 'lists duplicate files in arrays' do
         | 
| 676 | 
            -
                    dupes =  | 
| 836 | 
            +
                    dupes = maid.dupes_in(filefixtures_glob)
         | 
| 677 837 | 
             
                    expect(dupes.first).to be_a(Array)
         | 
| 678 838 |  | 
| 679 839 | 
             
                    basenames = dupes.flatten.map { |p| File.basename(p) }
         | 
| @@ -683,7 +843,7 @@ module Maid | |
| 683 843 |  | 
| 684 844 | 
             
                describe '#verbose_dupes_in' do
         | 
| 685 845 | 
             
                  it 'lists all but the shortest-named dupe' do
         | 
| 686 | 
            -
                    dupes =  | 
| 846 | 
            +
                    dupes = maid.verbose_dupes_in(filefixtures_glob)
         | 
| 687 847 |  | 
| 688 848 | 
             
                    basenames = dupes.flatten.map { |p| File.basename(p) }
         | 
| 689 849 | 
             
                    expect(basenames).to eq(%w[bar.zip foo.zip])
         | 
| @@ -694,13 +854,13 @@ module Maid | |
| 694 854 | 
             
                  it 'lists all but the oldest dupe' do
         | 
| 695 855 | 
             
                    # FIXME: Broken on Ruby 2.1.0-preview2, maybe because of FakeFS
         | 
| 696 856 | 
             
                    #
         | 
| 697 | 
            -
                    #     oldest_path = "#{ | 
| 857 | 
            +
                    #     oldest_path = "#{filefixtures_path}/foo.zip"
         | 
| 698 858 | 
             
                    #     FileUtils.touch(oldest_path, :mtime => Time.new(1970, 1, 1))
         | 
| 699 859 |  | 
| 700 | 
            -
                    FileUtils.touch("#{ | 
| 701 | 
            -
                    FileUtils.touch("#{ | 
| 860 | 
            +
                    FileUtils.touch("#{filefixtures_path}/bar.zip")
         | 
| 861 | 
            +
                    FileUtils.touch("#{filefixtures_path}/1.zip")
         | 
| 702 862 |  | 
| 703 | 
            -
                    dupes =  | 
| 863 | 
            +
                    dupes = maid.newest_dupes_in(filefixtures_glob)
         | 
| 704 864 |  | 
| 705 865 | 
             
                    basenames = dupes.flatten.map { |p| File.basename(p) }
         | 
| 706 866 | 
             
                    expect(basenames).to match_array(%w[bar.zip 1.zip])
         | 
| @@ -710,13 +870,13 @@ module Maid | |
| 710 870 | 
             
                describe '#dimensions_px' do
         | 
| 711 871 | 
             
                  context 'given a JPEG image' do
         | 
| 712 872 | 
             
                    it 'reports the known size' do
         | 
| 713 | 
            -
                      expect( | 
| 873 | 
            +
                      expect(maid.dimensions_px(image_path)).to eq([32, 32])
         | 
| 714 874 | 
             
                    end
         | 
| 715 875 | 
             
                  end
         | 
| 716 876 |  | 
| 717 877 | 
             
                  context 'given an unknown type' do
         | 
| 718 878 | 
             
                    it 'returns nil' do
         | 
| 719 | 
            -
                      expect( | 
| 879 | 
            +
                      expect(maid.dimensions_px(unknown_path)).to be_nil
         | 
| 720 880 | 
             
                    end
         | 
| 721 881 | 
             
                  end
         | 
| 722 882 | 
             
                end
         | 
| @@ -724,14 +884,14 @@ module Maid | |
| 724 884 | 
             
                describe '#location_city' do
         | 
| 725 885 | 
             
                  context 'given a JPEG image' do
         | 
| 726 886 | 
             
                    it 'reports the known location', vcr: { record: :new_episodes } do
         | 
| 727 | 
            -
                      sydney_path = File.join( | 
| 728 | 
            -
                      expect( | 
| 887 | 
            +
                      sydney_path = File.join(filefixtures_path, 'sydney.jpg')
         | 
| 888 | 
            +
                      expect(maid.location_city(sydney_path)).to eq('Sydney, New South Wales, AU')
         | 
| 729 889 | 
             
                    end
         | 
| 730 890 | 
             
                  end
         | 
| 731 891 |  | 
| 732 892 | 
             
                  context 'given an unknown type' do
         | 
| 733 893 | 
             
                    it 'returns nil' do
         | 
| 734 | 
            -
                      expect( | 
| 894 | 
            +
                      expect(maid.location_city(unknown_path)).to be_nil
         | 
| 735 895 | 
             
                    end
         | 
| 736 896 | 
             
                  end
         | 
| 737 897 | 
             
                end
         | 
| @@ -739,13 +899,13 @@ module Maid | |
| 739 899 | 
             
                describe '#mime_type' do
         | 
| 740 900 | 
             
                  context 'given a JPEG image' do
         | 
| 741 901 | 
             
                    it 'reports "image/jpeg"' do
         | 
| 742 | 
            -
                      expect( | 
| 902 | 
            +
                      expect(maid.mime_type(image_path)).to eq('image/jpeg')
         | 
| 743 903 | 
             
                    end
         | 
| 744 904 | 
             
                  end
         | 
| 745 905 |  | 
| 746 906 | 
             
                  context 'given an unknown type' do
         | 
| 747 907 | 
             
                    it 'returns nil' do
         | 
| 748 | 
            -
                      expect( | 
| 908 | 
            +
                      expect(maid.mime_type(unknown_path)).to be_nil
         | 
| 749 909 | 
             
                    end
         | 
| 750 910 | 
             
                  end
         | 
| 751 911 | 
             
                end
         | 
| @@ -753,13 +913,13 @@ module Maid | |
| 753 913 | 
             
                describe '#media_type' do
         | 
| 754 914 | 
             
                  context 'given a JPEG image' do
         | 
| 755 915 | 
             
                    it 'reports "image"' do
         | 
| 756 | 
            -
                      expect( | 
| 916 | 
            +
                      expect(maid.media_type(image_path)).to eq('image')
         | 
| 757 917 | 
             
                    end
         | 
| 758 918 | 
             
                  end
         | 
| 759 919 |  | 
| 760 920 | 
             
                  context 'given an unknown type' do
         | 
| 761 921 | 
             
                    it 'returns nil' do
         | 
| 762 | 
            -
                      expect( | 
| 922 | 
            +
                      expect(maid.media_type(unknown_path)).to be_nil
         | 
| 763 923 | 
             
                    end
         | 
| 764 924 | 
             
                  end
         | 
| 765 925 | 
             
                end
         | 
| @@ -767,7 +927,7 @@ module Maid | |
| 767 927 | 
             
                describe '#where_content_type' do
         | 
| 768 928 | 
             
                  context 'given "image"' do
         | 
| 769 929 | 
             
                    it 'only lists the fixture JPEGs' do
         | 
| 770 | 
            -
                      matches =  | 
| 930 | 
            +
                      matches = maid.where_content_type(maid.dir(filefixtures_glob), 'image')
         | 
| 771 931 |  | 
| 772 932 | 
             
                      expect(matches.length).to eq(2)
         | 
| 773 933 | 
             
                      expect(matches.first).to end_with('spec/fixtures/files/ruby.jpg')
         | 
| @@ -787,19 +947,19 @@ module Maid | |
| 787 947 | 
             
                  end
         | 
| 788 948 |  | 
| 789 949 | 
             
                  it 'returns false for non-empty directories' do
         | 
| 790 | 
            -
                    expect( | 
| 950 | 
            +
                    expect(maid.tree_empty?(@non_empty_dir)).to be(false)
         | 
| 791 951 | 
             
                  end
         | 
| 792 952 |  | 
| 793 953 | 
             
                  it 'returns true for empty directories' do
         | 
| 794 | 
            -
                    expect( | 
| 954 | 
            +
                    expect(maid.tree_empty?(@empty_dir)).to be(true)
         | 
| 795 955 | 
             
                  end
         | 
| 796 956 |  | 
| 797 957 | 
             
                  it 'returns true for directories with empty subdirectories' do
         | 
| 798 | 
            -
                    expect( | 
| 958 | 
            +
                    expect(maid.tree_empty?(@parent_of_empty_dir)).to be(true)
         | 
| 799 959 | 
             
                  end
         | 
| 800 960 |  | 
| 801 961 | 
             
                  it 'returns false for directories with non-empty subdirectories' do
         | 
| 802 | 
            -
                    expect( | 
| 962 | 
            +
                    expect(maid.tree_empty?(@root)).to be(false)
         | 
| 803 963 | 
             
                  end
         | 
| 804 964 | 
             
                end
         | 
| 805 965 |  | 
| @@ -837,45 +997,44 @@ module Maid | |
| 837 997 | 
             
                      'g/y/a/c',
         | 
| 838 998 | 
             
                    ].sort
         | 
| 839 999 |  | 
| 840 | 
            -
                    expect( | 
| 1000 | 
            +
                    expect(maid.ignore_child_dirs(src).sort).to eq(expected)
         | 
| 841 1001 | 
             
                  end
         | 
| 842 1002 | 
             
                end
         | 
| 843 1003 | 
             
              end
         | 
| 844 1004 |  | 
| 845 1005 | 
             
              describe 'OSX tag support', fakefs: false do
         | 
| 846 | 
            -
                let(: | 
| 1006 | 
            +
                let(:test_basedir) { '/tmp/maid-specs' }
         | 
| 1007 | 
            +
                let(:test_file) { File.join(test_basedir, 'tag.zip') }
         | 
| 847 1008 | 
             
                let(:test_dir) { File.dirname(test_file) }
         | 
| 848 | 
            -
                let(: | 
| 849 | 
            -
                let(:original_file_options) {  | 
| 1009 | 
            +
                let(:filename) { File.basename(test_file) }
         | 
| 1010 | 
            +
                let(:original_file_options) { maid.file_options.clone }
         | 
| 1011 | 
            +
                let(:maid) { Maid.new(log_device: File::NULL) }
         | 
| 850 1012 |  | 
| 851 1013 | 
             
                before do
         | 
| 852 | 
            -
                  @logger = double('Logger').as_null_object
         | 
| 853 | 
            -
                  @maid = Maid.new(logger: @logger)
         | 
| 854 | 
            -
             | 
| 855 1014 | 
             
                  FileUtils.mkdir_p(test_dir)
         | 
| 856 1015 | 
             
                  FileUtils.touch(test_file)
         | 
| 857 | 
            -
                   | 
| 1016 | 
            +
                  maid.file_options[:noop] = false
         | 
| 858 1017 | 
             
                end
         | 
| 859 1018 |  | 
| 860 1019 | 
             
                after do
         | 
| 861 1020 | 
             
                  FileUtils.rm_r(test_dir)
         | 
| 862 | 
            -
                   | 
| 1021 | 
            +
                  maid.file_options[:noop] = original_file_options[:noop]
         | 
| 863 1022 | 
             
                end
         | 
| 864 1023 |  | 
| 865 1024 | 
             
                describe '#tags' do
         | 
| 866 1025 | 
             
                  it 'returns tags from a file that has one' do
         | 
| 867 1026 | 
             
                    if Platform.has_tag_available?
         | 
| 868 | 
            -
                       | 
| 869 | 
            -
                       | 
| 870 | 
            -
                      expect( | 
| 1027 | 
            +
                      maid.file_options[:noop] = false
         | 
| 1028 | 
            +
                      maid.add_tag(test_file, 'Test')
         | 
| 1029 | 
            +
                      expect(maid.tags(test_file)).to eq(['Test'])
         | 
| 871 1030 | 
             
                    end
         | 
| 872 1031 | 
             
                  end
         | 
| 873 1032 |  | 
| 874 1033 | 
             
                  it 'returns tags from a file that has serveral tags' do
         | 
| 875 1034 | 
             
                    if Platform.has_tag_available?
         | 
| 876 | 
            -
                       | 
| 877 | 
            -
                       | 
| 878 | 
            -
                      expect( | 
| 1035 | 
            +
                      maid.file_options[:noop] = false
         | 
| 1036 | 
            +
                      maid.add_tag(test_file, %w[Test Twice])
         | 
| 1037 | 
            +
                      expect(maid.tags(test_file)).to eq(%w[Test Twice])
         | 
| 879 1038 | 
             
                    end
         | 
| 880 1039 | 
             
                  end
         | 
| 881 1040 | 
             
                end
         | 
| @@ -883,22 +1042,22 @@ module Maid | |
| 883 1042 | 
             
                describe '#has_tags?' do
         | 
| 884 1043 | 
             
                  it 'returns true for a file with tags' do
         | 
| 885 1044 | 
             
                    if Platform.has_tag_available?
         | 
| 886 | 
            -
                       | 
| 887 | 
            -
                      expect( | 
| 1045 | 
            +
                      maid.add_tag(test_file, 'Test')
         | 
| 1046 | 
            +
                      expect(maid.has_tags?(test_file)).to be(true)
         | 
| 888 1047 | 
             
                    end
         | 
| 889 1048 | 
             
                  end
         | 
| 890 1049 |  | 
| 891 1050 | 
             
                  it 'returns false for a file without tags' do
         | 
| 892 | 
            -
                    expect( | 
| 1051 | 
            +
                    expect(maid.has_tags?(test_file)).to be(false)
         | 
| 893 1052 | 
             
                  end
         | 
| 894 1053 | 
             
                end
         | 
| 895 1054 |  | 
| 896 1055 | 
             
                describe '#contains_tag?' do
         | 
| 897 1056 | 
             
                  it 'returns true a file with the given tag' do
         | 
| 898 1057 | 
             
                    if Platform.has_tag_available?
         | 
| 899 | 
            -
                       | 
| 900 | 
            -
                      expect( | 
| 901 | 
            -
                      expect( | 
| 1058 | 
            +
                      maid.add_tag(test_file, 'Test')
         | 
| 1059 | 
            +
                      expect(maid.contains_tag?(test_file, 'Test')).to be(true)
         | 
| 1060 | 
            +
                      expect(maid.contains_tag?(test_file, 'Not there')).to be(false)
         | 
| 902 1061 | 
             
                    end
         | 
| 903 1062 | 
             
                  end
         | 
| 904 1063 | 
             
                end
         | 
| @@ -906,8 +1065,8 @@ module Maid | |
| 906 1065 | 
             
                describe '#add_tag' do
         | 
| 907 1066 | 
             
                  it 'adds the given tag to a file' do
         | 
| 908 1067 | 
             
                    if Platform.has_tag_available?
         | 
| 909 | 
            -
                       | 
| 910 | 
            -
                      expect( | 
| 1068 | 
            +
                      maid.add_tag(test_file, 'Test')
         | 
| 1069 | 
            +
                      expect(maid.contains_tag?(test_file, 'Test')).to be(true)
         | 
| 911 1070 | 
             
                    end
         | 
| 912 1071 | 
             
                  end
         | 
| 913 1072 | 
             
                end
         | 
| @@ -915,10 +1074,10 @@ module Maid | |
| 915 1074 | 
             
                describe '#remove_tag' do
         | 
| 916 1075 | 
             
                  it 'removes the given tag from a file' do
         | 
| 917 1076 | 
             
                    if Platform.has_tag_available?
         | 
| 918 | 
            -
                       | 
| 919 | 
            -
                      expect( | 
| 920 | 
            -
                       | 
| 921 | 
            -
                      expect( | 
| 1077 | 
            +
                      maid.add_tag(test_file, 'Test')
         | 
| 1078 | 
            +
                      expect(maid.contains_tag?(test_file, 'Test')).to be(true)
         | 
| 1079 | 
            +
                      maid.remove_tag(test_file, 'Test')
         | 
| 1080 | 
            +
                      expect(maid.contains_tag?(test_file, 'Test')).to be(false)
         | 
| 922 1081 | 
             
                    end
         | 
| 923 1082 | 
             
                  end
         | 
| 924 1083 | 
             
                end
         | 
| @@ -926,11 +1085,11 @@ module Maid | |
| 926 1085 | 
             
                describe '#set_tag' do
         | 
| 927 1086 | 
             
                  it 'sets the given tags on a file' do
         | 
| 928 1087 | 
             
                    if Platform.has_tag_available?
         | 
| 929 | 
            -
                       | 
| 930 | 
            -
                      expect( | 
| 931 | 
            -
                       | 
| 932 | 
            -
                      expect( | 
| 933 | 
            -
                      expect( | 
| 1088 | 
            +
                      maid.set_tag(test_file, 'Test')
         | 
| 1089 | 
            +
                      expect(maid.contains_tag?(test_file, 'Test')).to be(true)
         | 
| 1090 | 
            +
                      maid.set_tag(test_file, %w[Test Twice])
         | 
| 1091 | 
            +
                      expect(maid.contains_tag?(test_file, 'Test')).to be(true)
         | 
| 1092 | 
            +
                      expect(maid.contains_tag?(test_file, 'Twice')).to be(true)
         | 
| 934 1093 | 
             
                    end
         | 
| 935 1094 | 
             
                  end
         | 
| 936 1095 | 
             
                end
         |