clean_files 1.0.1 → 1.1.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/bin/clean_files +3 -3
- data/clean_files-1.0.1.gem +0 -0
- data/clean_files.gemspec +8 -6
- data/lib/clean_files/cleaner.rb +69 -0
- data/lib/clean_files/runner.rb +90 -0
- data/lib/clean_files.rb +2 -89
- data/spec/cleaner_spec.rb +109 -107
- metadata +9 -6
- data/VERSION.yml +0 -4
- data/lib/cleaner.rb +0 -66
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c64267955da36b6816003e6e589ff0d14225e8ba
|
|
4
|
+
data.tar.gz: e57e92355da67ce51b39f4c7df3131d99d3c7191
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b527857e49d5497b7d5ea76f9e2574f9fbd0caaa2ea86d5b9ec64a0c4f8822f7457ed099ffcdd22e5f9ee9ffeafef285bd8b87ad2fa491d94f68c1c189ad41b5
|
|
7
|
+
data.tar.gz: b999c2eb25042e65f4fc7a0b1f582b478360c7d21c29f4d8893edea2a789dcf971f07d59adfec976a59b066d07355488219103d87658a7215b7f1e78c4395be1
|
data/bin/clean_files
CHANGED
|
Binary file
|
data/clean_files.gemspec
CHANGED
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = "clean_files"
|
|
5
|
-
s.version = "1.0
|
|
5
|
+
s.version = "1.1.0"
|
|
6
6
|
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
8
8
|
s.authors = ["Tor Erik Linnerud"]
|
|
9
|
-
s.date =
|
|
10
|
-
s.default_executable =
|
|
11
|
-
s.email =
|
|
9
|
+
s.date = "2009-05-05"
|
|
10
|
+
s.default_executable = "clean_files"
|
|
11
|
+
s.email = "torerik.linnerud@alphasights.com"
|
|
12
12
|
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
13
13
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
|
14
14
|
s.files = `git ls-files`.split($/)
|
|
15
15
|
s.has_rdoc = true
|
|
16
|
-
s.homepage =
|
|
16
|
+
s.homepage = "http://github.com/alphasights/clean_files"
|
|
17
17
|
s.require_paths = ["lib"]
|
|
18
|
-
s.summary =
|
|
18
|
+
s.summary = "Executable to delete files which fit certain criteria"
|
|
19
|
+
s.description = "Executable to delete files which fit certain criteria e.g. you can delete files older than 1 month keeping one weekly file"
|
|
19
20
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
|
21
|
+
s.license = 'MIT'
|
|
20
22
|
|
|
21
23
|
s.add_dependency 'rake'
|
|
22
24
|
s.add_dependency 'rspec'
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'active_support/core_ext'
|
|
2
|
+
|
|
3
|
+
module CleanFiles
|
|
4
|
+
class Cleaner
|
|
5
|
+
attr_reader :paths, :options
|
|
6
|
+
|
|
7
|
+
VALID_OPTIONS = [:threshold, :pretend, :verbose, :recursive]
|
|
8
|
+
VALID_INTERVALS = [:hourly, :daily, :weekly, :monthly, :yearly]
|
|
9
|
+
TIME_INTERVALS = [:hour, :day, :cweek, :month, :year]
|
|
10
|
+
|
|
11
|
+
SimpleFile = Struct.new(:path, :stat) do
|
|
12
|
+
delegate :ctime, :file?, :directory?, :to => :stat
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(paths, options = {})
|
|
16
|
+
@paths = [paths].flatten
|
|
17
|
+
@options = options.reverse_merge(:threshold => 30.days.ago)
|
|
18
|
+
options.assert_valid_keys(VALID_OPTIONS + VALID_INTERVALS)
|
|
19
|
+
given_intervals = VALID_INTERVALS & options.keys
|
|
20
|
+
if given_intervals.present?
|
|
21
|
+
lowest_selected_position = VALID_INTERVALS.index(given_intervals.first)
|
|
22
|
+
@key_intervals = TIME_INTERVALS[lowest_selected_position..-1]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def start
|
|
27
|
+
files = files_to_delete.map(&:path)
|
|
28
|
+
puts files.join("\n") if options[:verbose]
|
|
29
|
+
FileUtils.rm_rf(files, :noop => !!options[:pretend])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def files
|
|
33
|
+
if paths.any?{|path| path.include?('*')}
|
|
34
|
+
paths.map!{|path| Dir.glob(path)}
|
|
35
|
+
paths.flatten!
|
|
36
|
+
end
|
|
37
|
+
@_files ||= paths.map do |file_path|
|
|
38
|
+
begin
|
|
39
|
+
SimpleFile.new(file_path, File.stat(file_path))
|
|
40
|
+
rescue Errno::EOPNOTSUPP
|
|
41
|
+
nil
|
|
42
|
+
rescue Errno::ENOENT => e
|
|
43
|
+
puts e.to_s
|
|
44
|
+
exit -2
|
|
45
|
+
end
|
|
46
|
+
end.compact.select do |file|
|
|
47
|
+
file.file? || (options[:recursive] && file.directory?)
|
|
48
|
+
end.sort_by(&:ctime)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def files_to_delete
|
|
52
|
+
files_before_threshold - files_to_preserve
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def files_before_threshold
|
|
56
|
+
files.select{|file| file.ctime < options[:threshold]}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def files_to_preserve
|
|
60
|
+
return [] if @key_intervals.nil?
|
|
61
|
+
files_before_threshold.group_by do |file|
|
|
62
|
+
cdate = file.ctime.to_datetime
|
|
63
|
+
@key_intervals.map{|interval| cdate.send(interval)}
|
|
64
|
+
end.map do |_, files|
|
|
65
|
+
files.first
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'active_support'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
require 'clean_files/cleaner'
|
|
6
|
+
|
|
7
|
+
module CleanFiles
|
|
8
|
+
class Runner
|
|
9
|
+
attr_reader :options
|
|
10
|
+
|
|
11
|
+
def initialize(arguments)
|
|
12
|
+
@arguments = arguments
|
|
13
|
+
|
|
14
|
+
# Set defaults
|
|
15
|
+
@options = Hash.new
|
|
16
|
+
@options[:threshold] = 1.month.ago
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Parse options, check arguments, then process the command
|
|
20
|
+
def run
|
|
21
|
+
|
|
22
|
+
if parsed_options? && arguments_valid?
|
|
23
|
+
Cleaner.new(@options.delete(:paths), @options).start
|
|
24
|
+
else
|
|
25
|
+
output_usage
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
protected
|
|
31
|
+
|
|
32
|
+
def parsed_options?
|
|
33
|
+
|
|
34
|
+
# Specify options
|
|
35
|
+
opts = OptionParser.new
|
|
36
|
+
opts.on('-h', '--help') { output_help }
|
|
37
|
+
opts.on('-v', '--verbose') { @options[:verbose] = true }
|
|
38
|
+
opts.on('-d', '--pretend') { @options[:pretend] = true
|
|
39
|
+
@options[:verbose] = true }
|
|
40
|
+
opts.on('-r', '--recursive') {@options[:recursive] = true }
|
|
41
|
+
|
|
42
|
+
opts.on('-t', '--treshold [DAYS]') do |days|
|
|
43
|
+
raise OptionParser::InvalidOption unless days.to_i > 0 || days == "0"
|
|
44
|
+
@options[:threshold] = days.to_i.send(:days).ago
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Cleaner::VALID_INTERVALS.each do |interval|
|
|
48
|
+
opts.on("-#{interval.to_s.first.upcase}", "--#{interval}") {@options[interval] = true}
|
|
49
|
+
end
|
|
50
|
+
return false unless opts.parse!(@arguments)
|
|
51
|
+
|
|
52
|
+
@options[:paths] = @arguments.dup
|
|
53
|
+
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def arguments_valid?
|
|
58
|
+
@options[:paths].present?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def output_help
|
|
62
|
+
rdoc
|
|
63
|
+
exit -1
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def output_usage
|
|
67
|
+
rdoc('Usage', 'Options')
|
|
68
|
+
exit -1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def rdoc(*sections)
|
|
72
|
+
readme_path = File.join(File.dirname(__FILE__), '/../README.rdoc')
|
|
73
|
+
comment = File.read(readme_path)
|
|
74
|
+
|
|
75
|
+
markup = SM::SimpleMarkup.new
|
|
76
|
+
flow_convertor = SM::ToFlow.new
|
|
77
|
+
flow = markup.convert(comment, flow_convertor)
|
|
78
|
+
|
|
79
|
+
unless sections.empty?
|
|
80
|
+
flow = RDoc.extract_sections(flow, sections)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
options = RI::Options.instance
|
|
84
|
+
|
|
85
|
+
formatter = options.formatter.new(options, "")
|
|
86
|
+
formatter.display_flow(flow)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/clean_files.rb
CHANGED
|
@@ -1,91 +1,4 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require 'active_support'
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
|
|
5
|
-
require 'cleaner'
|
|
6
|
-
|
|
7
|
-
class CleanFiles
|
|
8
|
-
VERSION = '0.1.0'
|
|
9
|
-
|
|
10
|
-
attr_reader :options
|
|
11
|
-
|
|
12
|
-
def initialize(arguments)
|
|
13
|
-
@arguments = arguments
|
|
14
|
-
|
|
15
|
-
# Set defaults
|
|
16
|
-
@options = Hash.new
|
|
17
|
-
@options[:threshold] = 1.month.ago
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Parse options, check arguments, then process the command
|
|
21
|
-
def run
|
|
22
|
-
|
|
23
|
-
if parsed_options? && arguments_valid?
|
|
24
|
-
Cleaner.new(@options.delete(:paths), @options).start
|
|
25
|
-
else
|
|
26
|
-
output_usage
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
protected
|
|
32
|
-
|
|
33
|
-
def parsed_options?
|
|
34
|
-
|
|
35
|
-
# Specify options
|
|
36
|
-
opts = OptionParser.new
|
|
37
|
-
opts.on('-h', '--help') { output_help }
|
|
38
|
-
opts.on('-v', '--verbose') { @options[:verbose] = true }
|
|
39
|
-
opts.on('-d', '--pretend') { @options[:pretend] = true
|
|
40
|
-
@options[:verbose] = true }
|
|
41
|
-
opts.on('-r', '--recursive') {@options[:recursive] = true }
|
|
42
|
-
|
|
43
|
-
opts.on('-t', '--treshold [DAYS]') do |days|
|
|
44
|
-
raise OptionParser::InvalidOption unless days.to_i > 0 || days == "0"
|
|
45
|
-
@options[:threshold] = days.to_i.send(:days).ago
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
Cleaner::VALID_INTERVALS.each do |interval|
|
|
49
|
-
opts.on("-#{interval.to_s.first.upcase}", "--#{interval}") {@options[interval] = true}
|
|
50
|
-
end
|
|
51
|
-
return false unless opts.parse!(@arguments)
|
|
52
|
-
|
|
53
|
-
@options[:paths] = @arguments.dup
|
|
54
|
-
|
|
55
|
-
true
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def arguments_valid?
|
|
59
|
-
@options[:paths].present?
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def output_help
|
|
63
|
-
rdoc
|
|
64
|
-
exit -1
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def output_usage
|
|
68
|
-
rdoc('Usage', 'Options')
|
|
69
|
-
exit -1
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def rdoc(*sections)
|
|
73
|
-
readme_path = File.join(File.dirname(__FILE__), '/../README.rdoc')
|
|
74
|
-
comment = File.read(readme_path)
|
|
75
|
-
|
|
76
|
-
markup = SM::SimpleMarkup.new
|
|
77
|
-
flow_convertor = SM::ToFlow.new
|
|
78
|
-
flow = markup.convert(comment, flow_convertor)
|
|
79
|
-
format = "plain"
|
|
80
|
-
|
|
81
|
-
unless sections.empty?
|
|
82
|
-
flow = RDoc.extract_sections(flow, sections)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
options = RI::Options.instance
|
|
86
|
-
|
|
87
|
-
formatter = options.formatter.new(options, "")
|
|
88
|
-
formatter.display_flow(flow)
|
|
89
|
-
end
|
|
1
|
+
require 'clean_files/runner'
|
|
90
2
|
|
|
3
|
+
module CleanFiles
|
|
91
4
|
end
|
data/spec/cleaner_spec.rb
CHANGED
|
@@ -1,117 +1,119 @@
|
|
|
1
1
|
require File.join(File.dirname(__FILE__), '/spec_helper')
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
it 'has has files to delete' do
|
|
28
|
-
cleaner = Cleaner.new('/path')
|
|
29
|
-
cleaner.should_receive(:files_before_threshold).and_return(mock_files('12:00', '13:00'))
|
|
30
|
-
cleaner.should_receive(:files_to_preserve).and_return(mock_files('13:00'))
|
|
31
|
-
cleaner.files_to_delete.should == mock_files('12:00')
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
it 'prints name of file to be deleted if specifying verbose => true' do
|
|
35
|
-
cleaner = Cleaner.new('/path', :verbose => true)
|
|
36
|
-
files = mock_files('00:50', '13:30')
|
|
37
|
-
cleaner.should_receive(:files_to_delete).and_return(files)
|
|
38
|
-
FileUtils.should_receive(:rm_rf).with(files.map(&:path), :noop => false)
|
|
39
|
-
cleaner.should_receive(:puts).with("01 May 00:50\n01 May 13:30")
|
|
40
|
-
cleaner.start
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'preserves the first file of the hour' do
|
|
44
|
-
should_preserve(:hourly,
|
|
45
|
-
'12:05' => true,
|
|
46
|
-
'12:10' => false,
|
|
47
|
-
'13:15' => true,
|
|
48
|
-
'13:16' => false)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it 'preserves the first file of the day' do
|
|
52
|
-
should_preserve(:daily,
|
|
53
|
-
'01 10:00' => true,
|
|
54
|
-
'01 12:00' => false,
|
|
55
|
-
'02 00:00' => true,
|
|
56
|
-
'02 00:01' => false)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it 'preserves the first file of the week' do
|
|
60
|
-
should_preserve(:weekly,
|
|
61
|
-
'Sun Jan 02 03:40 00' => true,
|
|
62
|
-
'Tue Jan 04 03:40 00' => true,
|
|
63
|
-
'Wed Jan 05 03:40 00' => false)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it 'preserves the first file of the month' do
|
|
67
|
-
should_preserve(:monthly,
|
|
68
|
-
'Sun Jan 02 12:00 00' => true,
|
|
69
|
-
'Tue Feb 01 12:00 00' => true,
|
|
70
|
-
'Wed Feb 02 12:00 00' => false)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
it 'preserves the first file of the year' do
|
|
74
|
-
should_preserve(:yearly,
|
|
75
|
-
'Sun Jan 02 03:40 01' => true,
|
|
76
|
-
'Tue Jan 04 03:40 02' => true,
|
|
77
|
-
'Wed Jan 05 03:40 02' => false)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
MockFile = Struct.new(:ctime) do
|
|
81
|
-
def ==(other)
|
|
82
|
-
(ctime.to_i - other.ctime.to_i) < 2
|
|
2
|
+
|
|
3
|
+
module CleanFiles
|
|
4
|
+
describe Cleaner do
|
|
5
|
+
|
|
6
|
+
it 'find files older than threshold' do
|
|
7
|
+
cleaner = Cleaner.new('/path', :threshold => 5.days.ago)
|
|
8
|
+
cleaner.should_receive(:files).and_return(mock_files(7.days.ago, 4.days.ago))
|
|
9
|
+
cleaner.files_before_threshold.should == mock_files(7.days.ago)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'deletes files' do
|
|
13
|
+
cleaner = Cleaner.new('/path')
|
|
14
|
+
files = mock_files(3.days.ago, 2.days.ago, 7.days.ago)
|
|
15
|
+
cleaner.should_receive(:files_to_delete).and_return(files)
|
|
16
|
+
FileUtils.should_receive(:rm_rf).with(files.map(&:path), :noop => false)
|
|
17
|
+
cleaner.start
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'does not delete any files if specifying pretend => true' do
|
|
21
|
+
cleaner = Cleaner.new('/path', :pretend => true)
|
|
22
|
+
files = mock_files(3.days.ago, 2.days.ago, 7.days.ago)
|
|
23
|
+
cleaner.should_receive(:files_to_delete).and_return(files)
|
|
24
|
+
FileUtils.should_receive(:rm_rf).with(files.map(&:path), :noop => true)
|
|
25
|
+
cleaner.start
|
|
83
26
|
end
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
27
|
+
|
|
28
|
+
it 'has has files to delete' do
|
|
29
|
+
cleaner = Cleaner.new('/path')
|
|
30
|
+
cleaner.should_receive(:files_before_threshold).and_return(mock_files('12:00', '13:00'))
|
|
31
|
+
cleaner.should_receive(:files_to_preserve).and_return(mock_files('13:00'))
|
|
32
|
+
cleaner.files_to_delete.should == mock_files('12:00')
|
|
87
33
|
end
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
34
|
+
|
|
35
|
+
it 'prints name of file to be deleted if specifying verbose => true' do
|
|
36
|
+
cleaner = Cleaner.new('/path', :verbose => true)
|
|
37
|
+
files = mock_files('00:50', '13:30')
|
|
38
|
+
cleaner.should_receive(:files_to_delete).and_return(files)
|
|
39
|
+
FileUtils.should_receive(:rm_rf).with(files.map(&:path), :noop => false)
|
|
40
|
+
cleaner.should_receive(:puts).with("01 May 00:50\n01 May 13:30")
|
|
41
|
+
cleaner.start
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'preserves the first file of the hour' do
|
|
45
|
+
should_preserve(:hourly,
|
|
46
|
+
'12:05' => true,
|
|
47
|
+
'12:10' => false,
|
|
48
|
+
'13:15' => true,
|
|
49
|
+
'13:16' => false)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'preserves the first file of the day' do
|
|
53
|
+
should_preserve(:daily,
|
|
54
|
+
'01 10:00' => true,
|
|
55
|
+
'01 12:00' => false,
|
|
56
|
+
'02 00:00' => true,
|
|
57
|
+
'02 00:01' => false)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'preserves the first file of the week' do
|
|
61
|
+
should_preserve(:weekly,
|
|
62
|
+
'Sun Jan 02 03:40 00' => true,
|
|
63
|
+
'Tue Jan 04 03:40 00' => true,
|
|
64
|
+
'Wed Jan 05 03:40 00' => false)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'preserves the first file of the month' do
|
|
68
|
+
should_preserve(:monthly,
|
|
69
|
+
'Sun Jan 02 12:00 00' => true,
|
|
70
|
+
'Tue Feb 01 12:00 00' => true,
|
|
71
|
+
'Wed Feb 02 12:00 00' => false)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'preserves the first file of the year' do
|
|
75
|
+
should_preserve(:yearly,
|
|
76
|
+
'Sun Jan 02 03:40 01' => true,
|
|
77
|
+
'Tue Jan 04 03:40 02' => true,
|
|
78
|
+
'Wed Jan 05 03:40 02' => false)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
MockFile = Struct.new(:ctime) do
|
|
82
|
+
def ==(other)
|
|
83
|
+
(ctime.to_i - other.ctime.to_i) < 2
|
|
93
84
|
end
|
|
94
|
-
|
|
85
|
+
|
|
86
|
+
def inspect
|
|
87
|
+
ctime.to_s(:short)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def stat
|
|
91
|
+
stat = Object.new
|
|
92
|
+
def stat.file?
|
|
93
|
+
true
|
|
94
|
+
end
|
|
95
|
+
stat
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
alias path inspect
|
|
95
99
|
end
|
|
96
|
-
|
|
97
|
-
alias path inspect
|
|
98
|
-
end
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
def mock_files(*ctimes)
|
|
102
|
+
ctimes.map do |ctime|
|
|
103
|
+
if ctime.is_a?(String)
|
|
104
|
+
ctime = "1 May #{ctime}" unless ctime =~ /\d\d /
|
|
105
|
+
ctime = "May #{ctime} 2009" unless ctime =~ /\d{2}/
|
|
106
|
+
ctime = Time.parse(ctime)
|
|
107
|
+
end
|
|
108
|
+
MockFile.new(ctime)
|
|
106
109
|
end
|
|
107
|
-
MockFile.new(ctime)
|
|
108
110
|
end
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
|
|
112
|
+
def should_preserve(interval, files)
|
|
113
|
+
cleaner = Cleaner.new('/path', interval => true)
|
|
114
|
+
cleaner.should_receive(:files_before_threshold).and_return(mock_files(*files.keys.sort))
|
|
115
|
+
expected_files = files.select{|_, select| select == true}.map{|file, _| file}
|
|
116
|
+
cleaner.files_to_preserve.to_set.should == mock_files(*expected_files).to_set
|
|
117
|
+
end
|
|
116
118
|
end
|
|
117
119
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clean_files
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tor Erik Linnerud
|
|
@@ -66,7 +66,8 @@ dependencies:
|
|
|
66
66
|
- - '>='
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
|
-
description:
|
|
69
|
+
description: Executable to delete files which fit certain criteria e.g. you can delete
|
|
70
|
+
files older than 1 month keeping one weekly file
|
|
70
71
|
email: torerik.linnerud@alphasights.com
|
|
71
72
|
executables: []
|
|
72
73
|
extensions: []
|
|
@@ -80,15 +81,17 @@ files:
|
|
|
80
81
|
- Gemfile.lock
|
|
81
82
|
- LICENSE
|
|
82
83
|
- README.rdoc
|
|
83
|
-
- VERSION.yml
|
|
84
84
|
- bin/clean_files
|
|
85
|
+
- clean_files-1.0.1.gem
|
|
85
86
|
- clean_files.gemspec
|
|
86
87
|
- lib/clean_files.rb
|
|
87
|
-
- lib/cleaner.rb
|
|
88
|
+
- lib/clean_files/cleaner.rb
|
|
89
|
+
- lib/clean_files/runner.rb
|
|
88
90
|
- spec/cleaner_spec.rb
|
|
89
91
|
- spec/spec_helper.rb
|
|
90
|
-
homepage: http://github.com/alphasights/
|
|
91
|
-
licenses:
|
|
92
|
+
homepage: http://github.com/alphasights/clean_files
|
|
93
|
+
licenses:
|
|
94
|
+
- MIT
|
|
92
95
|
metadata: {}
|
|
93
96
|
post_install_message:
|
|
94
97
|
rdoc_options:
|
data/VERSION.yml
DELETED
data/lib/cleaner.rb
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
require 'active_support/core_ext'
|
|
2
|
-
class Cleaner
|
|
3
|
-
attr_reader :paths, :options
|
|
4
|
-
|
|
5
|
-
VALID_OPTIONS = [:threshold, :pretend, :verbose, :recursive]
|
|
6
|
-
VALID_INTERVALS = [:hourly, :daily, :weekly, :monthly, :yearly]
|
|
7
|
-
TIME_INTERVALS = [:hour, :day, :cweek, :month, :year]
|
|
8
|
-
|
|
9
|
-
SimpleFile = Struct.new(:path, :stat) do
|
|
10
|
-
delegate :ctime, :file?, :directory?, :to => :stat
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def initialize(paths, options = {})
|
|
14
|
-
@paths = [paths].flatten
|
|
15
|
-
@options = options.reverse_merge(:threshold => 30.days.ago)
|
|
16
|
-
options.assert_valid_keys(VALID_OPTIONS + VALID_INTERVALS)
|
|
17
|
-
given_intervals = VALID_INTERVALS & options.keys
|
|
18
|
-
if given_intervals.present?
|
|
19
|
-
lowest_selected_position = VALID_INTERVALS.index(given_intervals.first)
|
|
20
|
-
@key_intervals = TIME_INTERVALS[lowest_selected_position..-1]
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def start
|
|
25
|
-
files = files_to_delete.map(&:path)
|
|
26
|
-
puts files.join("\n") if options[:verbose]
|
|
27
|
-
FileUtils.rm_rf(files, :noop => !!options[:pretend])
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def files
|
|
31
|
-
if paths.any?{|path| path.include?('*')}
|
|
32
|
-
paths.map!{|path| Dir.glob(path)}
|
|
33
|
-
paths.flatten!
|
|
34
|
-
end
|
|
35
|
-
@_files ||= paths.map do |file_path|
|
|
36
|
-
begin
|
|
37
|
-
SimpleFile.new(file_path, File.stat(file_path))
|
|
38
|
-
rescue Errno::EOPNOTSUPP
|
|
39
|
-
nil
|
|
40
|
-
rescue Errno::ENOENT => e
|
|
41
|
-
puts e.to_s
|
|
42
|
-
exit -2
|
|
43
|
-
end
|
|
44
|
-
end.compact.select do |file|
|
|
45
|
-
file.file? || (options[:recursive] && file.directory?)
|
|
46
|
-
end.sort_by(&:ctime)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def files_to_delete
|
|
50
|
-
files_before_threshold - files_to_preserve
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def files_before_threshold
|
|
54
|
-
files.select{|file| file.ctime < options[:threshold]}
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def files_to_preserve
|
|
58
|
-
return [] if @key_intervals.nil?
|
|
59
|
-
files_before_threshold.group_by do |file|
|
|
60
|
-
cdate = file.ctime.to_datetime
|
|
61
|
-
@key_intervals.map{|interval| cdate.send(interval)}
|
|
62
|
-
end.map do |_, files|
|
|
63
|
-
files.first
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|