geoffreywiseman-prune 1.1.1 → 1.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/README.mdown CHANGED
@@ -8,17 +8,21 @@ Prune is written as a Ruby library with an optional command-line interface and a
8
8
 
9
9
  ## Usage
10
10
 
11
- Although prune has a command-line interface, at the moment the retention policy which determines what to
12
- do with each of the files (retain, remove, archive) and how to take action (archiving files by month in
13
- a .tar.gz) is basically hard-coded for our current needs, although it's easy to change.
11
+ Prune has a command-line interface and a configurable retention policy. It is packaged as a gem, which
12
+ can be installed easily:
14
13
 
15
- To invoke prune:
14
+ gem install geoffreywiseman-prune
16
15
 
17
- bin/prune <folder>
16
+ At which point you should be able to prune a directory using the default retention policy:
18
17
 
19
- To get help on the various command-line options:
18
+ prune <directory>
20
19
 
21
- bin/prune --help
20
+ And get more information on using it:
22
21
 
23
- In the long run, I expect this will be packaged as a gem and have the retention policy externalized
24
- where it can be configured for your own needs.
22
+ prune --help
23
+
24
+ The retention policy is [configurable](http://geoffreywiseman.github.com/prune/configure.html).
25
+
26
+ ## Continuous Integration
27
+
28
+ Prune is built by [Travis CI](http://travis-ci.org/#!/geoffreywiseman/prune).
data/Rakefile CHANGED
@@ -21,7 +21,7 @@ end
21
21
 
22
22
  spec = Gem::Specification.new do |spec|
23
23
  spec.name = 'geoffreywiseman-prune'
24
- spec.version = '1.1.1'
24
+ spec.version = '1.2.0.rc1'
25
25
  spec.date = '2011-09-09'
26
26
  spec.summary = 'Prunes files from a folder based on a retention policy, often time-based.'
27
27
  spec.description = 'Prune is meant to analyze a folder full of files, run them against a retention policy and decide which to keep, which to remove and which to archive. It is extensible and embeddable.'
@@ -3,6 +3,7 @@ require 'rubygems'
3
3
  require 'date'
4
4
  require 'zlib'
5
5
  require 'archive/tar/minitar'
6
+ require 'tmpdir'
6
7
  include Archive::Tar
7
8
 
8
9
  module Prune
@@ -29,6 +30,29 @@ module Prune
29
30
  raise IOError, "Archive folder #{@destination} does not exist and cannot be created."
30
31
  end
31
32
  end
33
+
34
+ def get_filenames( root )
35
+ Dir.entries( root ).map { |tmpfile| File.join( root, tmpfile ) }.reject { |path| File.directory? path }
36
+ end
37
+
38
+ def update_archive( archive_path, paths )
39
+ puts "Archive file #{archive_path} exists." if @verbose
40
+ Dir.mktmpdir do |tmp_dir|
41
+ puts "Created temporary directory #{tmp_dir} to extract contents of existing archive file." if @verbose
42
+ tgz = Zlib::GzipReader.new( File.open( archive_path, 'rb' ) )
43
+ Minitar.unpack( tgz, tmp_dir )
44
+ extracted_paths = get_filenames( tmp_dir )
45
+ tgz = Zlib::GzipWriter.new( File.open( archive_path, 'wb' ) )
46
+ Minitar.pack( extracted_paths + paths, tgz )
47
+ puts "Added #{paths.size} file(s) to #{archive_path} archive already containing #{extracted_paths.size} file(s)." if @verbose
48
+ end
49
+ end
50
+
51
+ def create_archive( archive_path, paths )
52
+ tgz = Zlib::GzipWriter.new( File.open( archive_path, 'wb' ) )
53
+ Minitar.pack( paths, tgz )
54
+ puts "Compressed #{paths.size} file(s) into #{archive_path} archive." if @verbose
55
+ end
32
56
 
33
57
  def archive( group_name, files )
34
58
  make_destination_dir
@@ -36,21 +60,9 @@ module Prune
36
60
  paths = files.map { |file| File.join( @source, file ) }
37
61
 
38
62
  if File.exists?( archive_path ) then
39
- puts "Archive file #{archive_path} exists." if @verbose
40
- Dir.mktmpdir do |tmp_dir|
41
- puts "Created temporary directory #{tmp_dir} to extract contents of existing archive file." if @verbose
42
- tgz = Zlib::GzipReader.new( File.open( archive_path, 'rb' ) )
43
- Minitar.unpack( tgz, tmp_dir )
44
- extracted_paths = Dir.entries( tmp_dir ).map { |tmpfile| File.join( tmp_dir, tmpfile ) }.reject { |path| File.directory? path }
45
- combined_paths = extracted_paths + paths
46
- tgz = Zlib::GzipWriter.new( File.open( archive_path, 'wb' ) )
47
- Minitar.pack( combined_paths, tgz )
48
- puts "Added #{files.size} file(s) to #{archive_path} archive already containing #{extracted_paths.size} file(s)." if @verbose
49
- end
63
+ update_archive( archive_path, paths )
50
64
  else
51
- tgz = Zlib::GzipWriter.new( File.open( archive_path, 'wb' ) )
52
- Minitar.pack( paths, tgz )
53
- puts "Compressed #{files.size} file(s) into #{archive_path} archive." if @verbose
65
+ create_archive( archive_path, paths )
54
66
  end
55
67
 
56
68
  File.delete( *paths )
@@ -27,4 +27,8 @@ class Category
27
27
  @quiet
28
28
  end
29
29
 
30
+ def to_s
31
+ @description
32
+ end
33
+
30
34
  end
data/lib/prune/cli.rb CHANGED
@@ -8,17 +8,17 @@ module Prune
8
8
 
9
9
  class CommandLineInterface
10
10
 
11
- DEFAULT_OPTIONS = { :verbose => false, :did_work => false, :dry_run => false, :prompt => true, :archive => true }
11
+ DEFAULT_OPTIONS = { :verbose => false, :did_work => false, :dry_run => false, :prompt => true, :archive => true, :configure => false }
12
12
 
13
- def self.parse_and_run
14
- options = DEFAULT_OPTIONS.dup
15
- parser = OptionParser.new do |opts|
13
+ def self.make_parser( options )
14
+ OptionParser.new do |opts|
16
15
  opts.banner = "Usage: prune [options] folder"
17
16
  opts.on( "-v", "--verbose", "Prints much more frequently during execution about what it's doing." ) { options[:verbose] = true }
18
17
  opts.on( "-d", "--dry-run", "Categorizes files, but does not take any actions on them." ) { options[:dry_run] = true }
19
18
  opts.on( "-f", "--force", "--no-prompt", "Will take action without asking permissions; useful for automation." ) { options[:prompt] = false }
20
19
  opts.on( "-a", "--archive-folder FOLDER", "The folder in which archives should be stored; defaults to <folder>/../<folder-name>-archives." ) { |path| options[:archive_path] = path }
21
20
  opts.on( "--no-archive", "Don't perform archival; typically if the files you're pruning are already compressed." ) { options[:archive] = false }
21
+ opts.on( "--config", "Configure the retention policy for the specified folder." ) { options[:configure] = true }
22
22
  opts.on_tail( "--version", "Displays version information." ) do
23
23
  options[:did_work] = true
24
24
  print "Prune #{VERSION.join('.')}, by Geoffrey Wiseman."
@@ -28,17 +28,26 @@ module Prune
28
28
  puts opts
29
29
  end
30
30
  end
31
+ end
31
32
 
33
+ def self.parse_and_run
34
+ options = DEFAULT_OPTIONS.dup
35
+ parser = make_parser options
32
36
  begin
33
37
  parser.parse!
34
38
 
35
- if ARGV.size != 1 then
36
- print parser.help unless options[:did_work]
39
+ if ARGV.size == 1 then
40
+ if options[:configure] then
41
+ configurer = Configurer.new( ARGV.first, options )
42
+ configurer.configure
43
+ else
44
+ Pruner.new( options ).prune( ARGV.first )
45
+ end
37
46
  else
38
- Pruner.new( options ).prune( ARGV.first )
47
+ print parser.help unless options[:did_work]
39
48
  end
40
49
  rescue OptionParser::ParseError
41
- $stderr.print "Error: " + $! + "\n"
50
+ $stderr.print "Error: " + $!.message + "\n"
42
51
  end
43
52
 
44
53
  end
@@ -0,0 +1,55 @@
1
+ require 'fileutils'
2
+
3
+ module Prune
4
+ class Configurer
5
+
6
+ def initialize( folder, options = {} )
7
+ @config_file = File.join( folder, ".prune" )
8
+ end
9
+
10
+ def configure
11
+ if File.file? @config_file then
12
+ if File.writable? @config_file then
13
+ edit_config
14
+ else
15
+ puts "Configuration file #{@config_file} exists, but is not writeable; cannot edit configuration."
16
+ end
17
+ elsif File.directory? @config_file
18
+ puts "Configuration file #{@config_file} exists, but is a directory; cannot create or edit configuration file."
19
+ else
20
+ create_config
21
+ edit_config
22
+ end
23
+ end
24
+
25
+ def has_config?
26
+ File.file? @config_file
27
+ end
28
+
29
+ def create_config
30
+ FileUtils.cp default_retention, @config_file
31
+ puts "Copied default retention policy to #{@config_file}"
32
+ end
33
+
34
+ def default_retention
35
+ source_folder = File.dirname( File.expand_path( __FILE__ ) )
36
+ File.join( source_folder, 'default_retention.rb' )
37
+ end
38
+
39
+ def edit_config
40
+ editor = ENV[ 'VISUAL' ] || ENV[ 'EDITOR' ]
41
+ if editor.nil? then
42
+ puts "No editor defined in 'VISUAL' or 'EDITOR' variables. Edit #{@config_file} in your favorite editor."
43
+ else
44
+ puts "Editing configuration #{@config_file} with #{editor}"
45
+ if system( "#{editor} #{@config_file}" ) then
46
+ puts "Configuration complete."
47
+ else
48
+ puts "Failed to edit file."
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
data/lib/prune/pruner.rb CHANGED
@@ -32,7 +32,7 @@ module Prune
32
32
  end
33
33
  print "\n" if @options[:verbose]
34
34
 
35
- display_categories policy
35
+ display_categories( @categories )
36
36
  print "\t#{@analyzed_count} file(s) analyzed\n"
37
37
  end
38
38
 
@@ -69,7 +69,7 @@ module Prune
69
69
  result = take_action( action, folder_name, files )
70
70
  if !result.nil? then
71
71
  puts result
72
- actions += 1
72
+ actions += files.size
73
73
  end
74
74
  end
75
75
  print "No actions necessary.\n" if actions == 0
@@ -78,6 +78,16 @@ module Prune
78
78
  def take_action( action, folder_name, files )
79
79
  case action
80
80
  when :remove
81
+ take_remove_action folder_name, files
82
+ when :archive
83
+ take_archive_action folder_name, files
84
+ end
85
+ end
86
+
87
+ def take_remove_action( folder_name, files )
88
+ if files.empty? then
89
+ "No files categorized to be removed."
90
+ else
81
91
  paths = files.map { |file| File.join folder_name, file }
82
92
  begin
83
93
  File.delete *paths
@@ -85,32 +95,47 @@ module Prune
85
95
  rescue
86
96
  raise IOError, "Could not remove file(s): #{$!}"
87
97
  end
88
- when :archive
89
- if @options[:archive] then
98
+ end
99
+ end
100
+
101
+ def take_archive_action( folder_name, files )
102
+ if @options[:archive] then
103
+ if files.empty? then
104
+ "No files categorized for archival, so no archives created."
105
+ else
90
106
  archiver = Archiver.new( @options[:archive_path], folder_name, @options[:verbose] )
91
107
  grouper = Grouper.new( archiver )
92
108
  grouper.group( folder_name, files );
93
109
  grouper.archive
94
- else
95
- "Archive option disabled. Archive(s) not created."
96
110
  end
111
+ else
112
+ "Archive option disabled. Archive(s) not created."
97
113
  end
98
114
  end
99
115
 
100
- def display_categories( policy )
101
- @categories.each_pair do |category,files|
102
- if !category.quiet? || @options[:verbose] then
116
+ def display_categories( categories )
117
+ categories.each_pair do |category,files|
118
+ if should_display?( category, files ) then
103
119
  print "\t#{category.description}:\n\t\t"
104
- puts files.join( "\n\t\t")
120
+ if files.empty? then
121
+ puts "none"
122
+ else
123
+ puts files.join( "\n\t\t" )
124
+ end
105
125
  end
106
126
  end
107
127
  end
128
+
129
+ def should_display?( category, files )
130
+ @options[:verbose] || !( category.quiet? || files.empty? )
131
+ end
108
132
 
109
133
  def analyze_file( policy, file )
110
134
  category = policy.categorize( file )
111
135
  @categories[ category ] << file unless category.nil?
112
136
  @analyzed_count += 1
113
137
  end
138
+
114
139
  end
115
140
 
116
- end
141
+ end
@@ -6,15 +6,21 @@ require 'pathname'
6
6
  module Prune
7
7
 
8
8
  class RetentionPolicy
9
-
9
+ DEFAULT_OPTIONS={ load_dsl: true }
10
+
10
11
  attr_accessor :categories
11
12
 
12
- def initialize( folder_name )
13
+ def initialize( folder_name, options = {} )
14
+ options = DEFAULT_OPTIONS.merge( options )
13
15
  @folder_name = folder_name
14
16
  @today = Date.today
15
17
  @categories = Array.new
16
18
  @default_category = Category.new "Unmatched Files", :retain, true
17
- instance_eval *get_retention_dsl( folder_name )
19
+ load_retention_dsl if options[:load_dsl]
20
+ end
21
+
22
+ def load_retention_dsl()
23
+ instance_eval *get_retention_dsl( @folder_name )
18
24
  end
19
25
 
20
26
  def categorize( file_name )
data/lib/prune.rb CHANGED
@@ -6,4 +6,5 @@ require 'prune/pruner'
6
6
  require 'prune/retention'
7
7
  require 'prune/grouper'
8
8
  require 'prune/category'
9
+ require 'prune/configurer'
9
10
 
@@ -4,6 +4,10 @@ require 'rspec'
4
4
 
5
5
  describe Prune::Archiver do
6
6
  SOURCE='/mysql'
7
+
8
+ before do
9
+ surpress_messages
10
+ end
7
11
 
8
12
  describe "with a #{SOURCE} source" do
9
13
  DESTINATION = '/mysql-archives'
data/spec/cli_spec.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'prune/cli'
2
2
  require 'prune/pruner'
3
+ require 'prune/configurer'
3
4
  require 'spec_helper'
4
5
  require 'rspec'
5
6
 
@@ -84,6 +85,16 @@ describe Prune::CommandLineInterface do
84
85
  assert_arg_to_option "--no-prompt", :prompt => false
85
86
  end
86
87
  end
88
+
89
+ describe "and a --config argument" do
90
+ it "should set the configure option to true" do
91
+ configurer = double( "Configurer" )
92
+ Prune::Configurer.should_receive( :new ).with( PATH, hash_including( :configure => true ) ).and_return( configurer )
93
+ configurer.should_receive( :configure )
94
+ ARGV.push( "--config" )
95
+ Prune::CommandLineInterface::parse_and_run
96
+ end
97
+ end
87
98
 
88
99
  describe "and a -a argument" do
89
100
  before(:each) do
@@ -0,0 +1,91 @@
1
+ require 'prune'
2
+ require 'spec_helper'
3
+ require 'rspec'
4
+
5
+ describe Prune::Configurer do
6
+
7
+ PRUNE_PATH = '/example/prune/folder'
8
+ CONFIG_FILE = "#{PRUNE_PATH}/.prune"
9
+ subject { Prune::Configurer.new(PRUNE_PATH) }
10
+
11
+ before do
12
+ ENV.delete( 'EDITOR' )
13
+ ENV.delete( 'VISUAL' )
14
+ end
15
+
16
+ context "with no config file present" do
17
+ it "should copy core retention policy if no config file" do
18
+ surpress_messages
19
+ FileUtils.should_receive(:cp).with( match(/default_retention.rb/), CONFIG_FILE )
20
+ subject.configure
21
+ end
22
+ end
23
+
24
+ context "with a directory in place of the config file" do
25
+ before do
26
+ stub_messages
27
+ File.stub( :directory? ) { true }
28
+ end
29
+ it "should warn that it can't create file" do
30
+ subject.configure
31
+ @messages.should include_match( /cannot create or edit configuration/ )
32
+ end
33
+ end
34
+
35
+ context "with config file that canot be written" do
36
+ before do
37
+ File.stub( :directory? ).with( CONFIG_FILE ) { false }
38
+ File.stub( :file? ).with( CONFIG_FILE ) { true }
39
+ File.stub( :writable? ).with( CONFIG_FILE ) { false }
40
+ stub_messages
41
+ end
42
+ it "should warn that cannot edit config" do
43
+ subject.configure
44
+ @messages.should include_match( /cannot edit configuration/ )
45
+ end
46
+ end
47
+
48
+ context "with a writeable config file" do
49
+
50
+ before do
51
+ File.stub( :directory? ).with( CONFIG_FILE ) { false }
52
+ File.stub( :file? ).with( CONFIG_FILE ) { true }
53
+ File.stub( :writable? ).with( CONFIG_FILE ) { true }
54
+ end
55
+
56
+ it "should warn if no editor defined" do
57
+ stub_messages
58
+ subject.configure
59
+ @messages.should include_match( /No editor defined/ )
60
+ end
61
+
62
+ context "with EDITOR environment variable defined" do
63
+ it "should invoke editor" do
64
+ surpress_messages
65
+ ENV['EDITOR']='ed'
66
+ subject.should_receive( :system ).with( "ed #{CONFIG_FILE}" ).and_return( true )
67
+ subject.configure
68
+ end
69
+ end
70
+
71
+ context "with VISUAL and EDITOR environment variables defined" do
72
+ it "should invoke visual editor" do
73
+ surpress_messages
74
+ ENV['VISUAL']='gedit'
75
+ ENV['EDITOR']='ed'
76
+ subject.should_receive( :system ).with( "gedit #{CONFIG_FILE}" ).and_return( true )
77
+ subject.configure
78
+ end
79
+ end
80
+
81
+ context "with VISUAL environment variable defined" do
82
+ it "should invoke visual editor if defined in VISUAL" do
83
+ surpress_messages
84
+ ENV['VISUAL']='gedit'
85
+ subject.should_receive( :system ).with( "gedit #{CONFIG_FILE}" ).and_return( true )
86
+ subject.configure
87
+ end
88
+ end
89
+ end
90
+
91
+ end
data/spec/pruner_spec.rb CHANGED
@@ -8,8 +8,9 @@ describe Prune::Pruner do
8
8
  subject { Prune::Pruner.new Hash.new }
9
9
 
10
10
  before( :each ) do
11
+ @categories = [ Category.new( "Unmatched Files", :retain, true ) ]
11
12
  @retention_policy = double( "RetentionPolicy" )
12
- @retention_policy.stub( :categories ) { [ Category.new( "Unmatched Files", :retain, true ) ] }
13
+ @retention_policy.stub( :categories ) { @categories }
13
14
  Prune::RetentionPolicy.stub( :new ) { @retention_policy }
14
15
  end
15
16
 
@@ -75,19 +76,53 @@ describe Prune::Pruner do
75
76
  end
76
77
 
77
78
  context "with file categorized as :remove" do
78
- it "should delete file" do
79
- filename = 'delete-me.txt'
80
- stub_files filename
79
+ FILENAME = 'delete-me.txt'
80
+
81
+ before( :each ) do
82
+ stub_messages
83
+
84
+ stub_files FILENAME
81
85
 
82
86
  category = double( category )
83
87
  category.stub( :description ) { "Old" }
84
88
  category.stub( :action ) { :remove }
85
89
  category.stub( :quiet? ) { false }
90
+ @retention_policy.should_receive( :categorize ).with( FILENAME ) { category }
91
+ end
92
+
93
+ it "should delete file" do
94
+ File.should_receive( :delete ).with( File.join( PRUNE_PATH, FILENAME ) )
95
+ subject.prune PRUNE_PATH
96
+ end
97
+
98
+ it "should display file deleted message" do
99
+ File.stub( :delete )
100
+ subject.prune PRUNE_PATH
101
+ @messages.should include_match( /file\(s\) deleted/ )
102
+ end
103
+ end
104
+
105
+ context "with no files categorized as archive" do
106
+
107
+ before do
108
+ subject.options[:archive] = true
109
+ stub_files
110
+ stub_messages
111
+
112
+ category = double( "category" )
113
+ category.stub( :action ) { :archive }
114
+ category.stub( :description ) { "Archive" }
115
+ category.stub( :quiet? ) { false }
86
116
 
87
- @retention_policy.should_receive( :categorize ).with( filename ) { category }
88
- File.should_receive( :delete ).with( File.join( PRUNE_PATH, filename ) )
117
+ @retention_policy.stub( :categories ) { [ category ]}
118
+ end
119
+
120
+ it "should indicate no archive necessary" do
89
121
  subject.prune PRUNE_PATH
122
+ puts "Messages: #{@messages}"
123
+ @messages.should include_match( /No files categorized for archival/ )
90
124
  end
125
+
91
126
  end
92
127
 
93
128
  context "with files categorized as :archive" do
@@ -96,15 +131,16 @@ describe Prune::Pruner do
96
131
  before do
97
132
  subject.options[:archive] = true
98
133
  stub_files files
99
- end
134
+ stub_messages
100
135
 
101
- it "should archive files in groups" do
102
136
  category = double( "category" )
103
137
  @retention_policy.stub( :categorize ) { category }
104
138
  category.stub( :action ) { :archive }
105
139
  category.stub( :description ) { "Ancient" }
106
140
  category.stub( :quiet? ) { false }
107
-
141
+ end
142
+
143
+ it "should archive files in groups" do
108
144
  grouper = double( "Grouper" )
109
145
  Prune::Grouper.stub( :new ) { grouper }
110
146
  grouper.should_receive( :group ).with( PRUNE_PATH, files )
@@ -112,8 +148,66 @@ describe Prune::Pruner do
112
148
 
113
149
  subject.prune PRUNE_PATH
114
150
  end
151
+
152
+ it "should display message if archive option disabled" do
153
+ subject.options[:archive] = false
154
+ subject.prune PRUNE_PATH
155
+ @messages.should include_match( /Archive option disabled/ )
156
+ end
115
157
  end
158
+
159
+ end
160
+
161
+ describe "when displaying categories" do
162
+
163
+ before do
164
+ stub_messages
165
+ end
166
+
167
+ describe "when verbose" do
168
+
169
+ before do
170
+ subject.options[:verbose]=true
171
+ end
172
+
173
+ it "should display empty categories" do
174
+ subject.display_categories( { Category.new( "Empty Category", :retain ) => [] } )
175
+ @messages.should include_match( /Empty Category/ )
176
+ end
177
+
178
+ it "should display quiet categories" do
179
+ subject.display_categories( { Category.new( "Quiet Category", :retain, true ) => [ 'quiet.txt' ] } )
180
+ @messages.should include_match( /Quiet Category/ )
181
+ end
182
+
183
+ it "should display categories with files" do
184
+ subject.display_categories( { Category.new( "Normal Category", :retain ) => [ 'normal.txt' ] } )
185
+ @messages.should include_match( /Normal Category/ )
186
+ end
187
+ end
188
+
189
+ describe "when not verbose" do
190
+
191
+ before do
192
+ subject.options[:verbose]=false
193
+ end
194
+
195
+ it "should not display empty categories" do
196
+ subject.display_categories( { Category.new( "Empty Category", :retain, true ) => [] } )
197
+ @messages.should_not include_match( /Empty Category/ )
198
+ end
199
+
200
+ it "should not display quiet categories" do
201
+ subject.display_categories( { Category.new( "Quiet Category", :retain, true ) => [ 'shhh.txt' ] } )
202
+ @messages.should_not include_match( /Quiet Category/ )
203
+ end
204
+
205
+ it "should display categories with files" do
206
+ subject.display_categories( { Category.new( "Normal Category", :retain ) => [ 'normal.txt' ] } )
207
+ @messages.should include_match( /Normal Category/ )
208
+ end
116
209
 
210
+ end
117
211
  end
118
212
 
119
213
  describe "Confirmation Prompt" do
@@ -198,9 +292,4 @@ describe Prune::Pruner do
198
292
  end
199
293
  end
200
294
 
201
- def stub_messages
202
- @messages = []
203
- $stdout.stub( :write ) { |message| @messages << message }
204
- end
205
-
206
295
  end
@@ -1,7 +1,7 @@
1
1
  require 'prune/retention'
2
2
  require 'prune/category'
3
3
  require 'rspec'
4
- require 'spec/spec_helper'
4
+ require 'spec_helper'
5
5
 
6
6
  DAY = 24 * 60 * 60
7
7
 
@@ -11,10 +11,14 @@ describe Prune::RetentionPolicy do
11
11
  SOURCE_FILE = "source_file"
12
12
  SOURCE_PATH = "#{SOURCE_DIR}/#{SOURCE_FILE}"
13
13
 
14
- subject { Prune::RetentionPolicy.new SOURCE_DIR }
14
+ before do
15
+ surpress_messages
16
+ end
15
17
 
16
18
  describe "default retention policy" do
17
19
 
20
+ subject { Prune::RetentionPolicy.new SOURCE_DIR }
21
+
18
22
  it "should return categories in dsl order" do
19
23
  cats = subject.categories
20
24
  cats.shift.description.should include( "Ignoring directories" )
@@ -137,6 +141,65 @@ describe Prune::RetentionPolicy do
137
141
  end
138
142
  end
139
143
  end
144
+
145
+ describe "filename-based retention policy" do
146
+
147
+ subject { Prune::RetentionPolicy.new SOURCE_DIR, load_dsl: false }
148
+
149
+ before do
150
+ subject.preprocess do |file|
151
+ if file.name =~ /-(\d{8})./ then
152
+ file.fyear = Date.parse($1).year
153
+ else
154
+ file.fyear = nil
155
+ end
156
+ end
157
+ subject.category "Ignoring" do
158
+ match { |f| File.directory?(f.name) || f.fyear.nil? }
159
+ ignore
160
+ quiet
161
+ end
162
+ subject.category "2010" do
163
+ match { |f| File.file?(f.name) && f.fyear == 2010 }
164
+ retain
165
+ end
166
+ subject.category "2011" do
167
+ match { |f| File.file?(f.name) && f.fyear == 2011 }
168
+ retain
169
+ end
170
+ end
171
+
172
+ it "should ignore directories" do
173
+ subject.categorize( "." ).description.should eq( "Ignoring" )
174
+ end
175
+
176
+ it "should ignore files not matching pattern" do
177
+ File.stub(:file?) { true }
178
+ subject.categorize( 'readme.txt' ).description.should eq( 'Ignoring' )
179
+ end
180
+
181
+ describe "with a file named with 20101001" do
182
+ before do
183
+ File.stub(:directory?) { false }
184
+ File.stub(:file?) { true }
185
+ end
186
+
187
+ it "should categorize as 2010" do
188
+ subject.categorize( 'mysql-prod-20101001.sql.gz' ).description.should eq( '2010' )
189
+ end
190
+ end
191
+
192
+ describe "with a file named with 20110215" do
193
+ before do
194
+ File.stub(:directory?) { false }
195
+ File.stub(:file?) { true }
196
+ end
197
+
198
+ it "should categorize as 2011" do
199
+ subject.categorize( 'subversion-20110215.tar.gz' ).description.should eq( '2011' )
200
+ end
201
+ end
202
+ end
140
203
 
141
204
  end
142
205
 
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,12 @@
1
+ def stub_messages
2
+ @messages = []
3
+ $stdout.stub( :write ) { |message| @messages << message }
4
+ end
5
+
6
+ def surpress_messages
7
+ $stdout.stub( :write )
8
+ end
9
+
1
10
  RSpec::Matchers.define :include_match do |expected|
2
11
  match do |actual|
3
12
  !actual.grep( expected ).empty?
metadata CHANGED
@@ -1,50 +1,40 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: geoffreywiseman-prune
3
- version: !ruby/object:Gem::Version
4
- hash: 17
5
- prerelease:
6
- segments:
7
- - 1
8
- - 1
9
- - 1
10
- version: 1.1.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0.rc1
5
+ prerelease: 6
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Geoffrey Wiseman
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-09-09 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2011-09-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: minitar
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70305339547980 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 13
29
- segments:
30
- - 0
31
- - 5
32
- - 3
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
33
21
  version: 0.5.3
34
22
  type: :runtime
35
- version_requirements: *id001
36
- description: Prune is meant to analyze a folder full of files, run them against a retention policy and decide which to keep, which to remove and which to archive. It is extensible and embeddable.
23
+ prerelease: false
24
+ version_requirements: *70305339547980
25
+ description: Prune is meant to analyze a folder full of files, run them against a
26
+ retention policy and decide which to keep, which to remove and which to archive.
27
+ It is extensible and embeddable.
37
28
  email: geoffrey.wiseman@codiform.com
38
- executables:
29
+ executables:
39
30
  - prune
40
31
  extensions: []
41
-
42
32
  extra_rdoc_files: []
43
-
44
- files:
33
+ files:
45
34
  - lib/prune/archiver.rb
46
35
  - lib/prune/category.rb
47
36
  - lib/prune/cli.rb
37
+ - lib/prune/configurer.rb
48
38
  - lib/prune/default_retention.rb
49
39
  - lib/prune/grouper.rb
50
40
  - lib/prune/pruner.rb
@@ -52,6 +42,7 @@ files:
52
42
  - lib/prune.rb
53
43
  - spec/archiver_spec.rb
54
44
  - spec/cli_spec.rb
45
+ - spec/configurer_spec.rb
55
46
  - spec/grouper_spec.rb
56
47
  - spec/pruner_spec.rb
57
48
  - spec/retention_spec.rb
@@ -62,36 +53,26 @@ files:
62
53
  - UNLICENSE
63
54
  homepage: http://geoffreywiseman.github.com/prune
64
55
  licenses: []
65
-
66
56
  post_install_message:
67
57
  rdoc_options: []
68
-
69
- require_paths:
58
+ require_paths:
70
59
  - lib
71
- required_ruby_version: !ruby/object:Gem::Requirement
60
+ required_ruby_version: !ruby/object:Gem::Requirement
72
61
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- hash: 3
77
- segments:
78
- - 0
79
- version: "0"
80
- required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
67
  none: false
82
- requirements:
83
- - - ">="
84
- - !ruby/object:Gem::Version
85
- hash: 3
86
- segments:
87
- - 0
88
- version: "0"
68
+ requirements:
69
+ - - ! '>'
70
+ - !ruby/object:Gem::Version
71
+ version: 1.3.1
89
72
  requirements: []
90
-
91
73
  rubyforge_project:
92
- rubygems_version: 1.8.10
74
+ rubygems_version: 1.8.17
93
75
  signing_key:
94
76
  specification_version: 3
95
77
  summary: Prunes files from a folder based on a retention policy, often time-based.
96
78
  test_files: []
97
-