file_discard 0.0.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +16 -0
- data/LICENSE +21 -0
- data/README.md +169 -0
- data/Rakefile +67 -0
- data/bin/discard +54 -0
- data/lib/file_discard.rb +42 -16
- data/spec/file_discard_spec.rb +148 -0
- metadata +15 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14285e9f6da20d84c8fcef7ab05e5925d23227da
|
4
|
+
data.tar.gz: ba1853b5b923df31f372412dd4e4eae15f51851e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aef2386c47392dd81be16d1b80b65db910dc96dfd7a2e67e8d1b5ab921136c65d9b40234170e93b06b18514065677acf383a31ddc2edbc9a11124c926df6ddc7
|
7
|
+
data.tar.gz: 618672724fac62bbcef2ea0a57571cb4c4f3f8317ecdae0d026424365dfbe8be1a53f4c38cf84bec81675bc9cf0176e45672bd5192170f802a9b9a0acade83af
|
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Brad Robel-Forrest
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
`FileDiscard` is a simple helper to make it easy for applications to move files to the correct trash folder. The location is determined by the platform and what file is being discarded (the latter is important so that files on other volumes/mountpoints are moved in to the appropriate trash folder).
|
2
|
+
|
3
|
+
## Getting Started
|
4
|
+
|
5
|
+
### Using the Executable
|
6
|
+
|
7
|
+
Part of the `file_discard` gem is an executable that can be used as a drop-in replacement for "rm":
|
8
|
+
|
9
|
+
```shell
|
10
|
+
> discard
|
11
|
+
Usage: discard [options] file ...
|
12
|
+
-v, --verbose show where files are discarded
|
13
|
+
-h, --help show this message
|
14
|
+
--version show version
|
15
|
+
|
16
|
+
Options ignored to provide compatibility with "rm":
|
17
|
+
-d
|
18
|
+
-f
|
19
|
+
-i
|
20
|
+
-P
|
21
|
+
-R
|
22
|
+
-r
|
23
|
+
```
|
24
|
+
|
25
|
+
### Using the Library
|
26
|
+
|
27
|
+
There are two ways for making discard requests. The easiest method extends Ruby's [`File`](http://www.ruby-doc.org/core/File.html) and [`Pathname`](http://www.ruby-doc.org/stdlib/libdoc/pathname/rdoc/Pathname.html) classes to use the new `discard` method instead of `unlink`:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'file_discard'
|
31
|
+
|
32
|
+
FileDiscard.mix_it_in!
|
33
|
+
|
34
|
+
# open a file and discard it from the instance object...
|
35
|
+
f = File.open 'file1.txt', 'w'
|
36
|
+
f.puts 'one'
|
37
|
+
f.close
|
38
|
+
f.discard
|
39
|
+
|
40
|
+
# create a pathname and discard it from the instance object...
|
41
|
+
p = Pathname.new 'file2.txt'
|
42
|
+
p.open('w') {|io| io.puts 'two'}
|
43
|
+
p.discard
|
44
|
+
|
45
|
+
# open a file and discard it using the class...
|
46
|
+
f = File.open 'file3.txt', 'w'
|
47
|
+
f.puts 'three'
|
48
|
+
f.close
|
49
|
+
File.discard 'file3.txt'
|
50
|
+
|
51
|
+
# create a pathname and discard it using the class...
|
52
|
+
p = Pathname.new 'file4.txt'
|
53
|
+
p.open('w') {|io| io.puts 'four'}
|
54
|
+
Pathname.discard 'file4.txt'
|
55
|
+
```
|
56
|
+
|
57
|
+
Another approach is to leave Ruby's [`File`](http://www.ruby-doc.org/core/File.html) and [`Pathname`](http://www.ruby-doc.org/stdlib/libdoc/pathname/rdoc/Pathname.html) classes alone and work directly with `FileDiscard`:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
require 'file_discard'
|
61
|
+
|
62
|
+
# open a file and discard it...
|
63
|
+
f = File.open 'file5.txt', 'w'
|
64
|
+
f.puts 'five'
|
65
|
+
f.close
|
66
|
+
FileDiscard.discard 'file5.txt'
|
67
|
+
|
68
|
+
# create a pathname and discard it...
|
69
|
+
p = Pathname.new 'file6.txt'
|
70
|
+
p.open('w') {|io| io.puts 'six'}
|
71
|
+
FileDiscard.discard 'file6.txt'
|
72
|
+
```
|
73
|
+
|
74
|
+
#### More Options
|
75
|
+
|
76
|
+
Under the covers, `FileDiscard` makes use of [`FileUtils.mv`](http://ruby-doc.org/stdlib/libdoc/fileutils/rdoc/FileUtils.html#method-c-mv) and passes any other options through:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
require 'file_discard'
|
80
|
+
|
81
|
+
FileDiscard.mix_it_in!
|
82
|
+
|
83
|
+
# open a file and discard it from the instance object...
|
84
|
+
f = File.open 'file7.txt', 'w'
|
85
|
+
f.puts 'seven'
|
86
|
+
f.close
|
87
|
+
f.discard verbose:true
|
88
|
+
# ===> mv /path/to/file7.txt /Users/brad/.Trash/file7.txt
|
89
|
+
|
90
|
+
# create a pathname and discard it using the class...
|
91
|
+
p = Pathname.new 'file8.txt'
|
92
|
+
p.open('w') {|io| io.puts 'eight'}
|
93
|
+
Pathname.discard 'file8.txt', verbose:true
|
94
|
+
# ===> mv /path/to/file8.txt /Users/brad/.Trash/file8.txt
|
95
|
+
```
|
96
|
+
|
97
|
+
Also of note is that `FileDiscard` will not blindly stomp on existing files already present in the trash. Instead, much like OS X's Finder, `FileDiscard` creates new file names based on the time when a collision occurs:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
require 'file_discard'
|
101
|
+
|
102
|
+
FileDiscard.mix_it_in!
|
103
|
+
|
104
|
+
# open a file and discard it from the instance object...
|
105
|
+
f = File.open 'samename.txt', 'w'
|
106
|
+
f.puts 'samename'
|
107
|
+
f.close
|
108
|
+
f.discard verbose:true
|
109
|
+
# ===> mv /path/to/samename.txt /Users/brad/.Trash/samename.txt
|
110
|
+
|
111
|
+
# create a pathname and discard it using the class...
|
112
|
+
p = Pathname.new 'samename.txt'
|
113
|
+
p.open('w') {|io| io.puts 'samename'}
|
114
|
+
Pathname.discard 'samename.txt', verbose:true
|
115
|
+
# ===> mv /path/to/samename.txt /Users/brad/.Trash/samename 17.49.44.txt
|
116
|
+
```
|
117
|
+
|
118
|
+
#### Exceptions
|
119
|
+
|
120
|
+
The most common exception raised will be if a discard request is made for a file that does not exist:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
require 'file_discard'
|
124
|
+
|
125
|
+
FileDiscard.mix_it_in!
|
126
|
+
|
127
|
+
p = Pathname.new 'missing1.txt'
|
128
|
+
p.discard
|
129
|
+
# ===> Errno::ENOENT: No such file or directory @ realpath_rec - /path/to/missing1.txt
|
130
|
+
```
|
131
|
+
|
132
|
+
## Creating New Discarders
|
133
|
+
|
134
|
+
If the built-in trash support isn't working for your platform, creating one is quite simple:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
require 'file_discard'
|
138
|
+
|
139
|
+
FileDiscard.mix_it_in!
|
140
|
+
|
141
|
+
class MyDiscarder < FileDiscard::Discarder
|
142
|
+
def initialize(home = '/path/to/my/home')
|
143
|
+
super home, 'a/trash/folder', '.place-for-mounted-trash-%s'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
FileDiscard.discarder = MyDiscarder.new
|
148
|
+
|
149
|
+
p = Pathname.new 'myfile.txt'
|
150
|
+
p.open('w') {|io| io.puts 'myfile'}
|
151
|
+
p.discard 'myfile.txt'
|
152
|
+
|
153
|
+
Pathname.new('/path/to/my/home/a/trash/folder').children
|
154
|
+
# ===> [#<Pathname:/path/to/my/home/a/trash/folder/myfile.txt>]
|
155
|
+
```
|
156
|
+
|
157
|
+
Each `Discarder` is expected to provide the following (as passed to the base class initializer):
|
158
|
+
|
159
|
+
1. `home`: An _absolute_ path to the home directory of the current user. `FileDiscard` will expand the path, so on systems that support it, special variables can be used (e.g. a tilde (~) will expand to the current user's home directory on OS X and Linux).
|
160
|
+
|
161
|
+
2. `home_trash`: A _relative_ path where the trash is expected from the `home` directory.
|
162
|
+
|
163
|
+
3. `mountpoint_trash_fmt`: A _relative_ path where the trash is expected from any given mountpoint. This string can optionally include a `%s` format specifier which will be replaced by the current user's numeric ID (ie. UID).
|
164
|
+
|
165
|
+
The base `Discarder` will rely on comparison between mountpoints (as determined by [`Pathname#mountpoint?`](http://www.ruby-doc.org/stdlib/libdoc/pathname/rdoc/Pathname.html#method-i-mountpoint-3F)) to decide if the home trash should be used or if a shared trash for another mounted volume should be used. In other words, if the mountpoint of the file being trashed is _not_ the same as the `home` directory's mountpoint, the `Discarder` will use the trash located in the mountpoint associated with the file being discarded using the `mountpoint_trash_fmt` relative result as presented by the discarder.
|
166
|
+
|
167
|
+
If the trash location does not already exist, the `Discarder` will not automatically create it. Instead, it will raise a `Errno::ENOENT` exception for any discard request that attempts to move a file to a trash that does not exist. It is expected that the caller will ensure the trash directories are present before attempting to discard files.
|
168
|
+
|
169
|
+
For a more complex example, check out the [`FileDiscard::LinuxDiscarder`](lib/file_discard.rb#L144-L164).
|
data/Rakefile
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rubygems/package_task'
|
5
|
+
|
6
|
+
task default: :test
|
7
|
+
task spec: :test
|
8
|
+
task build: :package
|
9
|
+
|
10
|
+
PKG_VERSION = '0.1.2'
|
11
|
+
NOW = Time.now.utc
|
12
|
+
|
13
|
+
# delay updating the version file unless building the gem or package
|
14
|
+
task :update_version do
|
15
|
+
File.open('lib/file_discard_version.rb','w') do |f|
|
16
|
+
f.puts <<EOF
|
17
|
+
module FileDiscard
|
18
|
+
VERSION = '#{PKG_VERSION}'
|
19
|
+
RELEASE = '#{`git rev-parse --short HEAD`.chomp}:#{NOW.strftime('%Y%m%d%H%M%S')}'
|
20
|
+
end
|
21
|
+
EOF
|
22
|
+
end
|
23
|
+
end
|
24
|
+
task package: :update_version
|
25
|
+
task gem: :update_version
|
26
|
+
|
27
|
+
Rake::TestTask.new do |t|
|
28
|
+
t.pattern = "spec/*_spec.rb"
|
29
|
+
end
|
30
|
+
|
31
|
+
def list_files
|
32
|
+
if Dir.exist? '.git'
|
33
|
+
`git ls-files -z`.split("\x0")
|
34
|
+
else
|
35
|
+
# e.g. when installed and tasks are run from there...
|
36
|
+
Dir.glob('**/*').select{|e| File.file? e}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
spec = Gem::Specification.new do |s|
|
41
|
+
s.name = 'file_discard'
|
42
|
+
s.version = PKG_VERSION
|
43
|
+
s.date = NOW.strftime('%Y-%m-%d')
|
44
|
+
s.summary = 'Move files to the trash'
|
45
|
+
s.description = 'Simple helper to move files to the trash folder'
|
46
|
+
s.authors = ['Brad Robel-Forrest']
|
47
|
+
s.email = 'brad+filediscard@gigglewax.com'
|
48
|
+
s.files = list_files << 'lib/file_discard_version.rb'
|
49
|
+
s.test_files = s.files.grep(%r{^spec/})
|
50
|
+
s.executables = %w(discard)
|
51
|
+
s.homepage = 'https://github.com/bradrf/file_discard#readme'
|
52
|
+
s.license = 'MIT'
|
53
|
+
|
54
|
+
s.required_ruby_version = '>= 1.9.0'
|
55
|
+
end
|
56
|
+
|
57
|
+
Gem::PackageTask.new(spec) do |pkg|
|
58
|
+
pkg.need_zip = true
|
59
|
+
pkg.need_tar = true
|
60
|
+
end
|
61
|
+
|
62
|
+
desc 'Start GitHub Readme Instant Preview service (see https://github.com/joeyespo/grip)'
|
63
|
+
task :grip do
|
64
|
+
exec 'grip --gfm --context=bradrf/file_discard'
|
65
|
+
end
|
66
|
+
|
67
|
+
CLOBBER.add 'coverage'
|
data/bin/discard
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'file_discard'
|
6
|
+
|
7
|
+
options = OpenStruct.new
|
8
|
+
parser = OptionParser.new do |opts|
|
9
|
+
Version = FileDiscard::VERSION
|
10
|
+
Release = FileDiscard::RELEASE
|
11
|
+
|
12
|
+
opts.on('-v', '--verbose', 'show where files are discarded') do |v|
|
13
|
+
options.verbose = v
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.on('-h', '--help', 'show this message') do
|
17
|
+
puts opts
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on('--version', 'show version') do
|
22
|
+
puts opts.ver
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.separator('')
|
27
|
+
opts.separator('Options ignored to provide compatibility with "rm":')
|
28
|
+
[?d,?f,?i,?P,?R,?r].each {|a| opts.on("-#{a}")}
|
29
|
+
|
30
|
+
opts.banner << ' file ...'
|
31
|
+
end
|
32
|
+
|
33
|
+
parser.parse!
|
34
|
+
|
35
|
+
if ARGV.size < 1
|
36
|
+
puts parser
|
37
|
+
exit 64 # use same exit status as rm with no files
|
38
|
+
end
|
39
|
+
|
40
|
+
status = 0
|
41
|
+
ARGV.each do |file|
|
42
|
+
begin
|
43
|
+
FileDiscard.discard(file, options.marshal_dump)
|
44
|
+
rescue Errno::EINVAL, Errno::ENOENT => ex
|
45
|
+
$stderr.puts "#{parser.program_name}: #{file}: #{ex.message}"
|
46
|
+
status = 1
|
47
|
+
rescue Exception => ex
|
48
|
+
$stderr.puts "#{parser.program_name}: #{file}: #{ex.class} #{ex.message}"
|
49
|
+
$stderr.puts ex.backtrace if options.verbose
|
50
|
+
status = 2
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
exit status
|
data/lib/file_discard.rb
CHANGED
@@ -25,14 +25,14 @@ require 'fileutils'
|
|
25
25
|
|
26
26
|
module FileDiscard
|
27
27
|
|
28
|
-
|
28
|
+
load "#{File.dirname(__FILE__)}/file_discard_version.rb"
|
29
29
|
|
30
30
|
######################################################################
|
31
31
|
# Module Methods
|
32
32
|
|
33
33
|
def self.mix_it_in!
|
34
|
-
[File, Pathname].each do |
|
35
|
-
|
34
|
+
[File, Pathname].each do |klass|
|
35
|
+
klass.class_eval do
|
36
36
|
def self.discard(*args)
|
37
37
|
FileDiscard.discarder.discard(*args)
|
38
38
|
end
|
@@ -66,8 +66,10 @@ module FileDiscard
|
|
66
66
|
# Discarders
|
67
67
|
|
68
68
|
class Discarder
|
69
|
-
|
70
|
-
|
69
|
+
SPECIAL_DIRS = ['.','..']
|
70
|
+
|
71
|
+
def initialize(home, home_trash, mountpoint_trash_fmt)
|
72
|
+
home = pathname_for(home).expand_path
|
71
73
|
@home_trash = home.join(home_trash)
|
72
74
|
@home_mountpoint = mountpoint_of home
|
73
75
|
@mountpoint_trash_fmt = mountpoint_trash_fmt
|
@@ -75,10 +77,12 @@ module FileDiscard
|
|
75
77
|
|
76
78
|
def discard(obj, move_options = {})
|
77
79
|
pn = pathname_for obj
|
80
|
+
if SPECIAL_DIRS.include?(pn.basename.to_s)
|
81
|
+
raise Errno::EINVAL.new(SPECIAL_DIRS.join(' and ') << ' may not be removed')
|
82
|
+
end
|
78
83
|
trash = find_trash_for pn
|
79
84
|
raise Errno::ENOENT.new(trash.to_s) unless trash.exist?
|
80
|
-
|
81
|
-
FileUtils.mv pn.expand_path, dst, move_options
|
85
|
+
move(pn, trash, move_options)
|
82
86
|
end
|
83
87
|
|
84
88
|
private
|
@@ -104,36 +108,58 @@ module FileDiscard
|
|
104
108
|
end
|
105
109
|
end
|
106
110
|
|
111
|
+
def move(src, dst, options)
|
112
|
+
src = src.expand_path
|
113
|
+
dst = uniquify(dst.join(src.basename))
|
114
|
+
FileUtils.mv src, dst, options
|
115
|
+
yield src, dst if block_given?
|
116
|
+
end
|
117
|
+
|
107
118
|
def uniquify(pn)
|
108
119
|
return pn unless pn.exist?
|
109
120
|
|
121
|
+
dn = pn.dirname
|
110
122
|
ext = pn.extname
|
111
123
|
base = pn.basename(ext).to_s
|
112
|
-
dn = pn.dirname
|
113
124
|
|
114
|
-
count = 0
|
115
125
|
fmt = bfmt = '%H.%M.%S'
|
116
126
|
|
117
|
-
|
127
|
+
10.times do |i|
|
118
128
|
ts = Time.now.strftime(fmt)
|
119
129
|
pn = dn.join("#{base} #{ts}#{ext}")
|
120
130
|
return pn unless pn.exist?
|
121
|
-
fmt = bfmt + ".%#{
|
122
|
-
count += 1
|
131
|
+
fmt = bfmt + ".%#{i}N" # use fractional seconds, with increasing precision
|
123
132
|
end
|
133
|
+
|
134
|
+
raise RuntimeError.new(%{Unable to uniquify "#{base}" (last attempt: #{pn})})
|
124
135
|
end
|
125
136
|
end # class Discarder
|
126
137
|
|
127
138
|
class OsxDiscarder < Discarder
|
128
|
-
def initialize
|
129
|
-
super '.Trash', '.Trashes/%s'
|
139
|
+
def initialize(home = '~')
|
140
|
+
super home, '.Trash', '.Trashes/%s'
|
130
141
|
end
|
131
142
|
end
|
132
143
|
|
133
144
|
class LinuxDiscarder < Discarder
|
134
|
-
def initialize
|
135
|
-
super '.local/share/Trash', '.Trash-%s'
|
145
|
+
def initialize(home = '~')
|
146
|
+
super home, '.local/share/Trash/files', '.Trash-%s/files'
|
136
147
|
end
|
148
|
+
|
149
|
+
# Linux has a special layout for the trash folder and tracking for restore.
|
150
|
+
# See http://www.freedesktop.org/wiki/Specifications/trash-spec/
|
151
|
+
private
|
152
|
+
def move(*args)
|
153
|
+
super do |src, dst|
|
154
|
+
dst.dirname.dirname.join('info',"#{dst.basename}.trashinfo").open('w') do |io|
|
155
|
+
io.write <<EOF
|
156
|
+
[Trash Info]
|
157
|
+
Path=#{src}
|
158
|
+
DeletionDate=#{Time.now.strftime('%Y-%m-%dT%H:%M:%S')}
|
159
|
+
EOF
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
137
163
|
end
|
138
164
|
|
139
165
|
end # module FileDiscard
|
@@ -0,0 +1,148 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start
|
6
|
+
rescue LoadError
|
7
|
+
# not required, but nice for reports on what code the tests have touched
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'tmpdir'
|
11
|
+
require 'minitest/spec'
|
12
|
+
require 'minitest/autorun'
|
13
|
+
|
14
|
+
load File.expand_path(File.join(File.dirname(__FILE__),'..','lib','file_discard.rb'))
|
15
|
+
|
16
|
+
describe FileDiscard do
|
17
|
+
|
18
|
+
describe :Discarder do
|
19
|
+
before do
|
20
|
+
@base = Pathname.new(Dir.mktmpdir(File.basename(__FILE__,'.rb') + '_'))
|
21
|
+
@home = @base.join('home')
|
22
|
+
@home.mkdir
|
23
|
+
@home_trash = '.Trash'
|
24
|
+
|
25
|
+
@discarder = FileDiscard::OsxDiscarder.new(@home)
|
26
|
+
FileDiscard.discarder = @discarder
|
27
|
+
end
|
28
|
+
|
29
|
+
after do
|
30
|
+
@base.rmtree if @base && @base.exist?
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should not allow removal of special directories' do
|
34
|
+
['.','..'].each do |dir|
|
35
|
+
->{ FileDiscard.discard(dir) }.must_raise Errno::EINVAL
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'when mixed in' do
|
40
|
+
before do
|
41
|
+
FileDiscard.mix_it_in!
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should extend classes' do
|
45
|
+
[File, Pathname].each do |klass|
|
46
|
+
klass.must_respond_to :discard
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should fail without trash' do
|
51
|
+
f = File.new(@base.join('file.txt').to_s, 'w')
|
52
|
+
->{ f.discard }.must_raise Errno::ENOENT
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'with trash in the home' do
|
56
|
+
before do
|
57
|
+
@trash = @home.join(@home_trash)
|
58
|
+
@trash.mkdir
|
59
|
+
end
|
60
|
+
|
61
|
+
def sorted_trash
|
62
|
+
@trash.children(false).collect(&:to_s).sort
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should discard a file' do
|
66
|
+
f = File.new(@base.join('file.txt').to_s, 'w')
|
67
|
+
f.discard
|
68
|
+
sorted_trash.must_equal ['file.txt']
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should discard a pathname' do
|
72
|
+
f = @base.join('file.txt')
|
73
|
+
f.open('w') {|io| io.puts 'nothing'}
|
74
|
+
f.discard
|
75
|
+
sorted_trash.must_equal ['file.txt']
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'with control over time' do
|
79
|
+
before do
|
80
|
+
# replace Time's strftime which FileDiscard relies on for uniquify
|
81
|
+
class Time
|
82
|
+
alias :orig_strftime :strftime
|
83
|
+
@@strftime_count = 0
|
84
|
+
def strftime(fmt)
|
85
|
+
@@strftime_count += 1
|
86
|
+
"9.8.#{@@strftime_count}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
after do
|
92
|
+
if Time.now.respond_to? :orig_strftime
|
93
|
+
class Time
|
94
|
+
alias :strftime :orig_strftime
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should not overwite other trashed files' do
|
100
|
+
f = @trash.join('file.txt')
|
101
|
+
f.open('w') {|io| io.puts 'nothing'}
|
102
|
+
|
103
|
+
f = @base.join('file.txt')
|
104
|
+
f.open('w') {|io| io.puts 'nothing'}
|
105
|
+
FileDiscard.discard(f.to_s)
|
106
|
+
sorted_trash.must_equal ['file 9.8.1.txt','file.txt']
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should use increasing precision for collisions' do
|
110
|
+
3.times do |i|
|
111
|
+
f = @trash.join(%{file#{i == 0 ? '' : " 9.8.#{i}"}.txt})
|
112
|
+
f.open('w') {|io| io.puts 'nothing'}
|
113
|
+
end
|
114
|
+
|
115
|
+
f = @base.join('file.txt')
|
116
|
+
f.open('w') {|io| io.puts 'nothing'}
|
117
|
+
File.discard(f.to_s)
|
118
|
+
sorted_trash
|
119
|
+
.must_equal ['file 9.8.1.txt', 'file 9.8.2.txt', 'file 9.8.3.txt', 'file.txt']
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'with control over mountpoints' do
|
124
|
+
class MyDiscarder < FileDiscard::Discarder
|
125
|
+
private
|
126
|
+
def mountpoint_of(pn)
|
127
|
+
pn
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
before do
|
132
|
+
@trash = @base.join('mytrash-%s' % Process.uid)
|
133
|
+
@trash.mkdir
|
134
|
+
@discarder = MyDiscarder.new(@home, 'mytrash', 'mytrash-%s')
|
135
|
+
FileDiscard.discarder = @discarder
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should not use the home trash' do
|
139
|
+
f = @base.join('file.txt')
|
140
|
+
f.open('w') {|io| io.puts 'nothing'}
|
141
|
+
f.discard
|
142
|
+
sorted_trash.must_equal ['file.txt']
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
metadata
CHANGED
@@ -1,22 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: file_discard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brad Robel-Forrest
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description: Simple helper to move files to the trash
|
13
|
+
description: Simple helper to move files to the trash folder
|
14
14
|
email: brad+filediscard@gigglewax.com
|
15
|
-
executables:
|
15
|
+
executables:
|
16
|
+
- discard
|
16
17
|
extensions: []
|
17
18
|
extra_rdoc_files: []
|
18
19
|
files:
|
20
|
+
- ".gitignore"
|
21
|
+
- LICENSE
|
22
|
+
- README.md
|
23
|
+
- Rakefile
|
24
|
+
- bin/discard
|
19
25
|
- lib/file_discard.rb
|
26
|
+
- lib/file_discard_version.rb
|
27
|
+
- spec/file_discard_spec.rb
|
20
28
|
homepage: https://github.com/bradrf/file_discard#readme
|
21
29
|
licenses:
|
22
30
|
- MIT
|
@@ -29,7 +37,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
29
37
|
requirements:
|
30
38
|
- - ">="
|
31
39
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
40
|
+
version: 1.9.0
|
33
41
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
42
|
requirements:
|
35
43
|
- - ">="
|
@@ -41,4 +49,5 @@ rubygems_version: 2.2.2
|
|
41
49
|
signing_key:
|
42
50
|
specification_version: 4
|
43
51
|
summary: Move files to the trash
|
44
|
-
test_files:
|
52
|
+
test_files:
|
53
|
+
- spec/file_discard_spec.rb
|