backup_organizer 0.0.1
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.
- data/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/README.md +73 -0
- data/Rakefile +2 -0
- data/backup_organizer.gemspec +21 -0
- data/lib/backup_organizer.rb +22 -0
- data/lib/backup_organizer/configuration.rb +25 -0
- data/lib/backup_organizer/error/base_error.rb +6 -0
- data/lib/backup_organizer/error/configuration_error.rb +8 -0
- data/lib/backup_organizer/error/error_with_custom_message.rb +11 -0
- data/lib/backup_organizer/error/invalid_path_error.rb +6 -0
- data/lib/backup_organizer/error/setup_error.rb +8 -0
- data/lib/backup_organizer/extensions/file_extensions.rb +66 -0
- data/lib/backup_organizer/file_age.rb +31 -0
- data/lib/backup_organizer/file_mover.rb +18 -0
- data/lib/backup_organizer/file_utils.rb +27 -0
- data/lib/backup_organizer/pattern.rb +21 -0
- data/lib/backup_organizer/rule.rb +20 -0
- data/lib/backup_organizer/setup.rb +16 -0
- data/lib/backup_organizer/version.rb +3 -0
- data/spec/configuration_spec.rb +26 -0
- data/spec/extensions/file_extensions_spec.rb +123 -0
- data/spec/file_age_spec.rb +11 -0
- data/spec/file_mover_spec.rb +60 -0
- data/spec/file_utils_spec.rb +12 -0
- data/spec/integration_spec.rb +96 -0
- data/spec/pattern_spec.rb +15 -0
- data/spec/rule_spec.rb +25 -0
- data/spec/setup_spec.rb +59 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/helpers.rb +43 -0
- data/spec/support/temp_dir.rb +12 -0
- metadata +153 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec', :version => 2, :cli => "--color --format doc" do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Thorben Schröder
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# BackupOrganizer [](http://travis-ci.org/walski/backup_organizer)
|
2
|
+
|
3
|
+
This gem helps you to keep your backups organized and to let the density of kept files to fade out over time.
|
4
|
+
|
5
|
+
The rules of how you want your backups organized are described in a Pattern like this:
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'backup_organizer'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install backup_organizer
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Scenario
|
24
|
+
* You are doing daily backups.
|
25
|
+
* The goal is to have a script that you can run daily and that ensures, that
|
26
|
+
* all the backups of the last 30 days are kept
|
27
|
+
* one file per month for the last year is kept
|
28
|
+
* one file per year is kept forever
|
29
|
+
* all other files are deleted
|
30
|
+
|
31
|
+
### Solution
|
32
|
+
|
33
|
+
#### How it looks like
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
# Load ActiveSupport to make date wrangling a bit more convenient
|
37
|
+
require 'rubygems'
|
38
|
+
require 'active_support/core_ext'
|
39
|
+
|
40
|
+
# Define the pattern
|
41
|
+
BackupOrganizer.organize('/basepath/to/your/backups') do |files|
|
42
|
+
files.stored_in('daily').if {|file| file.age < 30.days}
|
43
|
+
|
44
|
+
files.stored_in('monthly').if do |file|
|
45
|
+
file.age < 1.year &&
|
46
|
+
file.most_recent_in_its_month?
|
47
|
+
end
|
48
|
+
|
49
|
+
files.stored_in('yearly').if {|file| file.most_recent_in_its_year?}
|
50
|
+
end
|
51
|
+
````
|
52
|
+
|
53
|
+
#### What it does
|
54
|
+
|
55
|
+
Given that pattern you should drop all your backups in ``/basepath/to/your/backups/daily``. The organizer then does the following:
|
56
|
+
|
57
|
+
1. Move all files in ``/basepath/to/your/backups/daily`` which
|
58
|
+
* are not created in the current month
|
59
|
+
|
60
|
+
to ``/basepath/to/your/backups/monthly``
|
61
|
+
|
62
|
+
2. Move all files in ``/basepath/to/your/backups/monthly`` which
|
63
|
+
* are not created in the current year
|
64
|
+
* are among all the files created in the same month **not** most recent file
|
65
|
+
|
66
|
+
to ``/basepath/to/your/backups/yearly``
|
67
|
+
|
68
|
+
3. **Delete** (as this is the last rule) all files in ``/basepath/to/your/backups/monthly`` which
|
69
|
+
* are among all the files created in the same year **not** the most recent file
|
70
|
+
|
71
|
+
#### What else?
|
72
|
+
|
73
|
+
If you run the script make sure at least the ``/basepath/to/your/backups`` directory exists, the BackupOrganizer will take care of getting everything else in place.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/backup_organizer/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Thorben Schröder"]
|
6
|
+
gem.email = ["stillepost@gmail.com"]
|
7
|
+
gem.description = %q{Organizes backup files in folders in patterns that can be defined by the user.}
|
8
|
+
gem.summary = %q{If backups should only be kept around for a certain time and be stored sparsely (weekly, monthly, yearly, whatever) after that backup_organizer can do that for you.}
|
9
|
+
gem.homepage = "http://github.com/walski/backup_organizer"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "backup_organizer"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = BackupOrganizer::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rspec', '>= 2'
|
19
|
+
gem.add_development_dependency 'guard-rspec'
|
20
|
+
gem.add_development_dependency 'ruby_gntp'
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "backup_organizer/version"
|
2
|
+
require "backup_organizer/file_age"
|
3
|
+
require "backup_organizer/extensions/file_extensions"
|
4
|
+
require "backup_organizer/error/base_error"
|
5
|
+
Dir[File.expand_path('../backup_organizer/error/**/*.rb', __FILE__)].each {|f| require f}
|
6
|
+
require "backup_organizer/file_utils"
|
7
|
+
require "backup_organizer/rule"
|
8
|
+
require "backup_organizer/pattern"
|
9
|
+
require "backup_organizer/configuration"
|
10
|
+
require "backup_organizer/setup"
|
11
|
+
require "backup_organizer/file_mover"
|
12
|
+
|
13
|
+
module BackupOrganizer
|
14
|
+
def self.organize(path)
|
15
|
+
pattern = Pattern.new do |files|
|
16
|
+
yield files
|
17
|
+
end
|
18
|
+
configuration = Configuration.new(:path => path, :pattern => pattern)
|
19
|
+
Setup.create_structure(configuration)
|
20
|
+
configuration.move_all_files
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "backup_organizer/file_mover"
|
2
|
+
|
3
|
+
module BackupOrganizer
|
4
|
+
class Configuration
|
5
|
+
attr_reader :path, :pattern
|
6
|
+
def initialize(options)
|
7
|
+
@path, @pattern = options[:path], options[:pattern]
|
8
|
+
|
9
|
+
raise Error::ConfigurationError.new('No path set') unless @path
|
10
|
+
raise Error::ConfigurationError.new('No pattern set') unless @path
|
11
|
+
end
|
12
|
+
|
13
|
+
def directories
|
14
|
+
pattern.directories.map {|directory| File.expand_path("./#{directory}", @path)}
|
15
|
+
end
|
16
|
+
|
17
|
+
def move_all_files
|
18
|
+
sources = directories
|
19
|
+
destinations = (directories << :delete)[1..-1]
|
20
|
+
sources.each_with_index do |source, i|
|
21
|
+
FileMover.move_files(source, @pattern.rules[i], destinations[i])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "backup_organizer/file_age"
|
2
|
+
require 'backup_organizer/file_utils'
|
3
|
+
|
4
|
+
module BackupOrganizer
|
5
|
+
module Extensions
|
6
|
+
module FileExtensions
|
7
|
+
def age
|
8
|
+
FileAge.new(BackupOrganizer::FileUtils.age_of(self))
|
9
|
+
end
|
10
|
+
|
11
|
+
def most_recent_in_its_hour?
|
12
|
+
mtime = BackupOrganizer::FileUtils.mtime(self)
|
13
|
+
hour = Time.local(mtime.year, mtime.month, mtime.day, mtime.hour)
|
14
|
+
one_hour = 60 * 60
|
15
|
+
|
16
|
+
most_recent_in?(hour...(hour + one_hour))
|
17
|
+
end
|
18
|
+
|
19
|
+
def most_recent_in_its_day?
|
20
|
+
mtime = BackupOrganizer::FileUtils.mtime(self)
|
21
|
+
midnight = Time.local(mtime.year, mtime.month, mtime.day)
|
22
|
+
one_day = 24 * 60 * 60
|
23
|
+
|
24
|
+
most_recent_in?(midnight...(midnight + one_day))
|
25
|
+
end
|
26
|
+
|
27
|
+
def most_recent_in_its_week?
|
28
|
+
mtime = BackupOrganizer::FileUtils.mtime(self)
|
29
|
+
midnight = Time.local(mtime.year, mtime.month, mtime.day)
|
30
|
+
one_day = 24 * 60 * 60
|
31
|
+
monday = midnight - ((midnight.wday - 1) * one_day)
|
32
|
+
monday = monday - (7 * one_day) if midnight.wday == 0
|
33
|
+
|
34
|
+
most_recent_in?(monday...(monday + (7 * one_day)))
|
35
|
+
end
|
36
|
+
|
37
|
+
def most_recent_in_its_month?
|
38
|
+
mtime = BackupOrganizer::FileUtils.mtime(self)
|
39
|
+
this_month = Time.local(mtime.year, mtime.month, 1)
|
40
|
+
mtime = this_month + 32.days
|
41
|
+
next_month = Time.local(mtime.year, mtime.month, 1)
|
42
|
+
|
43
|
+
most_recent_in?(this_month...next_month)
|
44
|
+
end
|
45
|
+
|
46
|
+
def most_recent_in_its_year?
|
47
|
+
mtime = BackupOrganizer::FileUtils.mtime(self)
|
48
|
+
this_year = Time.local(mtime.year, 1, 1)
|
49
|
+
next_year = Time.local(this_year.year + 1, 1, 1)
|
50
|
+
|
51
|
+
most_recent_in?(this_year...next_year)
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
def most_recent_in?(period)
|
56
|
+
dir = File.dirname(self)
|
57
|
+
|
58
|
+
files_in_dir = Dir[File.expand_path('./*', dir)]
|
59
|
+
method = Range.instance_methods.include?(:cover?) ? :cover? : :include?
|
60
|
+
files_this_month = files_in_dir.select {|f| period.send(method, FileUtils.mtime(f))}
|
61
|
+
|
62
|
+
self == files_this_month.sort {|a,b| File.mtime(a) <=> File.mtime(b)}.last
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module BackupOrganizer
|
2
|
+
class BasicObject #:nodoc:
|
3
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
|
4
|
+
end unless defined?(BasicObject)
|
5
|
+
|
6
|
+
class FileAge < BasicObject
|
7
|
+
include ::Comparable
|
8
|
+
|
9
|
+
def initialize(age)
|
10
|
+
@age = age
|
11
|
+
end
|
12
|
+
|
13
|
+
def <=>(b)
|
14
|
+
b = b.to_f if b.respond_to?(:to_f)
|
15
|
+
@age.<=>(b)
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to?(method)
|
19
|
+
@age.respond_to?(method)
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_a?(klass)
|
23
|
+
FileAge == klass || @age.is_a?(klass)
|
24
|
+
end
|
25
|
+
alias :kind_of? :is_a?
|
26
|
+
|
27
|
+
def method_missing(name, *attrs)
|
28
|
+
@age.send(name, *attrs)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'backup_organizer/file_utils'
|
2
|
+
|
3
|
+
module BackupOrganizer
|
4
|
+
class FileMover
|
5
|
+
def self.move_files(source, rule, destination)
|
6
|
+
Dir[File.expand_path('./*', source)].each do |file|
|
7
|
+
move_file(file, rule, destination)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.move_file(file, rule, destination)
|
12
|
+
return if rule.applies_for?(file)
|
13
|
+
return FileUtils.rm_rf(file) if destination == :delete
|
14
|
+
|
15
|
+
FileUtils.mv(file, destination)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module BackupOrganizer
|
2
|
+
class FileUtils
|
3
|
+
def self.mv(from, to)
|
4
|
+
::FileUtils.mv(from, to)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.rm_rf(path)
|
8
|
+
::FileUtils.rm_rf(path)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.touch(path)
|
12
|
+
::FileUtils.touch(path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.mkdir(path)
|
16
|
+
Dir.mkdir(path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.age_of(path)
|
20
|
+
Time.now - mtime(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.mtime(path)
|
24
|
+
File.mtime(path)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module BackupOrganizer
|
2
|
+
class Pattern
|
3
|
+
def initialize
|
4
|
+
yield self
|
5
|
+
end
|
6
|
+
|
7
|
+
def stored_in(directory)
|
8
|
+
rule = Rule.new(directory)
|
9
|
+
rules << rule
|
10
|
+
rule
|
11
|
+
end
|
12
|
+
|
13
|
+
def directories
|
14
|
+
rules.map &:directory
|
15
|
+
end
|
16
|
+
|
17
|
+
def rules
|
18
|
+
@rules ||= []
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module BackupOrganizer
|
2
|
+
class Rule
|
3
|
+
def initialize(directory)
|
4
|
+
@directory = directory
|
5
|
+
end
|
6
|
+
|
7
|
+
def if(&rule)
|
8
|
+
@rule = rule
|
9
|
+
end
|
10
|
+
|
11
|
+
def applies_for?(file)
|
12
|
+
file.extend(Extensions::FileExtensions)
|
13
|
+
@rule.call(file)
|
14
|
+
end
|
15
|
+
|
16
|
+
def directory
|
17
|
+
@directory
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'backup_organizer/file_utils'
|
2
|
+
|
3
|
+
module BackupOrganizer
|
4
|
+
class Setup
|
5
|
+
def self.create_structure(configuration)
|
6
|
+
@configuration = configuration
|
7
|
+
raise Error::InvalidPathError unless File.exist?(@configuration.path) && File.directory?(@configuration.path)
|
8
|
+
|
9
|
+
@configuration.directories.each do |necessary_directory|
|
10
|
+
next if File.directory?(necessary_directory)
|
11
|
+
raise Error::SetupError.new('`#{necessary_directory}` should be a directory but is a file.') if File.exist?(necessary_directory)
|
12
|
+
BackupOrganizer::FileUtils.mkdir(necessary_directory)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe BackupOrganizer::Configuration do
|
5
|
+
it "returns the full path to all the directories of it's pattern" do
|
6
|
+
path = Tempdir.new
|
7
|
+
pattern = mock(BackupOrganizer::Pattern, :directories => %w{daily weekly yearly})
|
8
|
+
configuration = BackupOrganizer::Configuration.new(:path => path, :pattern => pattern)
|
9
|
+
configuration.directories.should eq pattern.directories.map {|d| File.expand_path("./#{d}", path)}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "runs the file mover for all rules/destinations" do
|
13
|
+
path = Tempdir.new
|
14
|
+
rule1 = mock(BackupOrganizer::Rule, :path => 'daily')
|
15
|
+
rule2 = mock(BackupOrganizer::Rule, :path => 'weekly')
|
16
|
+
rule3 = mock(BackupOrganizer::Rule, :path => 'yearly')
|
17
|
+
pattern = mock(BackupOrganizer::Pattern, :directories => %w{daily weekly yearly}, :rules => [rule1, rule2, rule3])
|
18
|
+
configuration = BackupOrganizer::Configuration.new(:path => path, :pattern => pattern)
|
19
|
+
|
20
|
+
BackupOrganizer::FileMover.should_receive(:move_files).with(File.expand_path("./#{rule1.path}", path), rule1, File.expand_path("./#{rule2.path}", path)).ordered
|
21
|
+
BackupOrganizer::FileMover.should_receive(:move_files).with(File.expand_path("./#{rule2.path}", path), rule2, File.expand_path("./#{rule3.path}", path)).ordered
|
22
|
+
BackupOrganizer::FileMover.should_receive(:move_files).with(File.expand_path("./#{rule3.path}", path), rule3, :delete).ordered
|
23
|
+
|
24
|
+
configuration.move_all_files
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
def check_expectations(expectations, method)
|
4
|
+
dir = Tempdir.new
|
5
|
+
files = []
|
6
|
+
|
7
|
+
# Create all the files first
|
8
|
+
expectations.each do |date, expectation|
|
9
|
+
path = temp_file_at(date, dir).extend(BackupOrganizer::Extensions::FileExtensions)
|
10
|
+
files << [date, path, expectation]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Check for the expectations
|
14
|
+
files.each do |date, path, expectation|
|
15
|
+
path.send(method).should be(expectation), "File with mtime of #{date} should return #{expectation} to #{method} but didn't"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe BackupOrganizer::Extensions::FileExtensions do
|
20
|
+
it "can tell a file's age" do
|
21
|
+
file = '/tmp/some/path'
|
22
|
+
file.extend BackupOrganizer::Extensions::FileExtensions
|
23
|
+
BackupOrganizer::FileUtils.should_receive(:age_of).with(file).and_return(1.234)
|
24
|
+
age = file.age
|
25
|
+
age.kind_of?(BackupOrganizer::FileAge).should be true
|
26
|
+
age.should eq 1.234
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can tell if a file is the most recent one in the same hour of it's last modification (mtime)" do
|
30
|
+
time = Time.now
|
31
|
+
|
32
|
+
first_of_this_hour = Time.local(time.year, time.month, time.day, time.hour)
|
33
|
+
last_of_last_hours = first_of_this_hour - 1
|
34
|
+
mid_of_this_hour = first_of_this_hour + 30.minutes
|
35
|
+
first_of_next_hour = first_of_this_hour + 1.hour
|
36
|
+
|
37
|
+
expectations = {
|
38
|
+
first_of_this_hour => false,
|
39
|
+
last_of_last_hours => true,
|
40
|
+
mid_of_this_hour => true,
|
41
|
+
first_of_next_hour => true
|
42
|
+
}
|
43
|
+
|
44
|
+
check_expectations(expectations, :most_recent_in_its_hour?)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "can tell if a file is the most recent one in the same day of it's last modification (mtime)" do
|
48
|
+
time = Time.now
|
49
|
+
|
50
|
+
midnight = Time.local(time.year, time.month, time.day)
|
51
|
+
|
52
|
+
first_of_this_day = midnight
|
53
|
+
last_of_last_day = first_of_this_day - 1
|
54
|
+
mid_of_this_day = first_of_this_day + 12.hours
|
55
|
+
first_of_next_day = first_of_this_day + 24.hours
|
56
|
+
|
57
|
+
expectations = {
|
58
|
+
first_of_this_day => false,
|
59
|
+
last_of_last_day => true,
|
60
|
+
mid_of_this_day => true,
|
61
|
+
first_of_next_day => true
|
62
|
+
}
|
63
|
+
|
64
|
+
check_expectations(expectations, :most_recent_in_its_day?)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can tell if a file is the most recent one in the same week of it's last modification (mtime)" do
|
68
|
+
time = Time.now
|
69
|
+
|
70
|
+
midnight = Time.local(time.year, time.month, time.day)
|
71
|
+
first_of_this_week = midnight - (midnight.wday - 1).days
|
72
|
+
first_of_this_week -= 7.days if midnight.wday == 0
|
73
|
+
last_of_last_week = first_of_this_week - 1
|
74
|
+
mid_of_this_week = first_of_this_week + 3.days
|
75
|
+
first_of_next_week = first_of_this_week + 7.days
|
76
|
+
|
77
|
+
expectations = {
|
78
|
+
first_of_this_week => false,
|
79
|
+
last_of_last_week => true,
|
80
|
+
mid_of_this_week => true,
|
81
|
+
first_of_next_week => true
|
82
|
+
}
|
83
|
+
|
84
|
+
check_expectations(expectations, :most_recent_in_its_week?)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "can tell if a file is the most recent one in the same month of it's last modification (mtime)" do
|
88
|
+
time = Time.now
|
89
|
+
|
90
|
+
first_of_this_month = Time.local(time.year, time.month, 1)
|
91
|
+
last_of_last_month = first_of_this_month - 1
|
92
|
+
mid_of_this_month = first_of_this_month + 15.days
|
93
|
+
next_month = (first_of_this_month + 32.days)
|
94
|
+
first_of_next_month = Time.local(next_month.year, next_month.month, 1)
|
95
|
+
|
96
|
+
expectations = {
|
97
|
+
first_of_this_month => false,
|
98
|
+
last_of_last_month => true,
|
99
|
+
mid_of_this_month => true,
|
100
|
+
first_of_next_month => true
|
101
|
+
}
|
102
|
+
|
103
|
+
check_expectations(expectations, :most_recent_in_its_month?)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "can tell if a file is the most recent one in the same year of it's last modification (mtime)" do
|
107
|
+
time = Time.now
|
108
|
+
|
109
|
+
first_of_this_year = Time.local(time.year, 1, 1)
|
110
|
+
last_of_last_year = first_of_this_year - 1
|
111
|
+
mid_of_this_year = first_of_this_year + 180.days
|
112
|
+
first_of_next_year = Time.local(first_of_this_year.year + 1, 1, 1)
|
113
|
+
|
114
|
+
expectations = {
|
115
|
+
first_of_this_year => false,
|
116
|
+
last_of_last_year => true,
|
117
|
+
mid_of_this_year => true,
|
118
|
+
first_of_next_year => true
|
119
|
+
}
|
120
|
+
|
121
|
+
check_expectations(expectations, :most_recent_in_its_year?)
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BackupOrganizer::FileAge do
|
4
|
+
it "can be compared with Time" do
|
5
|
+
age = BackupOrganizer::FileAge.new(Time.now - 2.days)
|
6
|
+
age.should < (Time.now - 1.day)
|
7
|
+
age.should_not > (Time.now - 1.day)
|
8
|
+
age.should > (Time.now - 3.days)
|
9
|
+
age.should_not < (Time.now - 3.days)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BackupOrganizer::FileMover do
|
4
|
+
describe "moving a single file to a destination" do
|
5
|
+
before do
|
6
|
+
@destination = Tempdir.new
|
7
|
+
@path = Tempfile.new('backup_organizer_file').path
|
8
|
+
@expected_path = File.expand_path("./#{File.basename(@path)}", @destination)
|
9
|
+
File.exist?(@path).should be true
|
10
|
+
File.exist?(@expected_path).should be false
|
11
|
+
end
|
12
|
+
|
13
|
+
it "is not carried out when the rule applies" do
|
14
|
+
rule = mock(BackupOrganizer::Rule, :applies_for? => true)
|
15
|
+
BackupOrganizer::FileMover.move_file(@path, rule, @destination)
|
16
|
+
File.exist?(@path).should be true
|
17
|
+
File.exist?(@expected_path).should be false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "is carried out when the does not rule applies" do
|
21
|
+
rule = mock(BackupOrganizer::Rule, :applies_for? => false)
|
22
|
+
BackupOrganizer::FileMover.move_file(@path, rule, @destination)
|
23
|
+
File.exist?(@path).should be false
|
24
|
+
File.exist?(@expected_path).should be true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "deletes the file if the destination is :delete" do
|
28
|
+
rule = mock(BackupOrganizer::Rule, :applies_for? => false)
|
29
|
+
BackupOrganizer::FileMover.move_file(@path, rule, :delete)
|
30
|
+
File.exist?(@path).should be false
|
31
|
+
File.exist?(@expected_path).should be false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "moves all files in a directory that match a rule" do
|
36
|
+
directory = Tempdir.new
|
37
|
+
%w{some test-1 files with different-1 looks}.each {|f| BackupOrganizer::FileUtils.touch(File.expand_path("./#{f}", directory))}
|
38
|
+
rule = mock(BackupOrganizer::Rule)
|
39
|
+
def rule.applies_for?(file)
|
40
|
+
file !~ /-1$/
|
41
|
+
end
|
42
|
+
destination = Tempdir.new
|
43
|
+
|
44
|
+
BackupOrganizer::FileMover.move_files(directory, rule, destination)
|
45
|
+
|
46
|
+
File.exist?(File.expand_path("./some", directory) ).should be true
|
47
|
+
File.exist?(File.expand_path("./files", directory) ).should be true
|
48
|
+
File.exist?(File.expand_path("./with", directory) ).should be true
|
49
|
+
File.exist?(File.expand_path("./looks", directory) ).should be true
|
50
|
+
File.exist?(File.expand_path("./test-1", directory) ).should be false
|
51
|
+
File.exist?(File.expand_path("./different-1", directory)).should be false
|
52
|
+
|
53
|
+
File.exist?(File.expand_path("./some", destination) ).should be false
|
54
|
+
File.exist?(File.expand_path("./files", destination) ).should be false
|
55
|
+
File.exist?(File.expand_path("./with", destination) ).should be false
|
56
|
+
File.exist?(File.expand_path("./looks", destination) ).should be false
|
57
|
+
File.exist?(File.expand_path("./test-1", destination) ).should be true
|
58
|
+
File.exist?(File.expand_path("./different-1", destination)).should be true
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BackupOrganizer::FileUtils do
|
4
|
+
it "knows the age of a file" do
|
5
|
+
path = temp_file_at(Time.now - 3.days)
|
6
|
+
BackupOrganizer::FileUtils.age_of(path).should be_within(1).of(3.days)
|
7
|
+
path = temp_file_at(Time.now - 99.days)
|
8
|
+
BackupOrganizer::FileUtils.age_of(path).should be_within(1).of(99.days)
|
9
|
+
path = temp_file_at(Time.now - 17.years)
|
10
|
+
BackupOrganizer::FileUtils.age_of(path).should be_within(1).of(17.years)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'find'
|
3
|
+
|
4
|
+
def include_files(dir_name, actual, expected, expectation)
|
5
|
+
expected.each do |file|
|
6
|
+
basename = File.basename(file)
|
7
|
+
actual.include?(basename).should be(expectation), "#{dir_name} should#{expectation ? '' : ' not'} include #{basename} but does #{expectation ? 'not' : ''}."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "integration of the whole system" do
|
12
|
+
before do
|
13
|
+
@now = Time.local(Time.now.year, 12, 30)
|
14
|
+
Time.stub(:now).and_return(@now)
|
15
|
+
|
16
|
+
@base_path = Tempdir.new
|
17
|
+
@daily_destination = File.expand_path("./daily", @base_path)
|
18
|
+
@monthly_destination = File.expand_path("./monthly", @base_path)
|
19
|
+
@yearly_destination = File.expand_path("./yearly", @base_path)
|
20
|
+
BackupOrganizer::FileUtils.mkdir(@daily_destination)
|
21
|
+
BackupOrganizer::FileUtils.mkdir(@yearly_destination)
|
22
|
+
|
23
|
+
@file_01_day_ago = temp_file_at(Time.now - 1.day + 2, @daily_destination)
|
24
|
+
@file_04_days_ago = temp_file_at(Time.now - 10.days + 2, @yearly_destination)
|
25
|
+
@file_10_days_ago = temp_file_at(Time.now - 10.days + 2, @daily_destination)
|
26
|
+
@file_30_days_ago = temp_file_at(Time.now - 30.days + 2, @daily_destination)
|
27
|
+
@file_31_days_ago = temp_file_at(Time.now - 31.days + 2, @daily_destination)
|
28
|
+
@file_02_month_ago = temp_file_at(Time.now - 60.days + 2, @daily_destination)
|
29
|
+
@file_02_month_and_a_bit_ago = temp_file_at(Time.now - 65.days + 2, @daily_destination)
|
30
|
+
@file_06_month_ago = temp_file_at(Time.now - 180.days + 2, @daily_destination)
|
31
|
+
@file_07_month_ago = temp_file_at(Time.now - 240.days + 2, @yearly_destination)
|
32
|
+
@file_365_days_ago = temp_file_at(Time.now - 365.days + 2, @daily_destination)
|
33
|
+
@file_366_days_ago = temp_file_at(Time.now - 366.days + 2, @daily_destination)
|
34
|
+
time_366_days_ago = Time.now - 366.days
|
35
|
+
@file_a_year_and_a_bit_ago = temp_file_at(Time.local(time_366_days_ago.year, 1, 1) + 2, @daily_destination)
|
36
|
+
@file_three_years_ago = temp_file_at(Time.now - 3.years + 2, @yearly_destination)
|
37
|
+
@file_ten_years_ago = temp_file_at(Time.now - 10.years + 2, @yearly_destination)
|
38
|
+
end
|
39
|
+
|
40
|
+
after do
|
41
|
+
Time.unstub(:now)
|
42
|
+
BackupOrganizer::FileUtils.rm_rf @base_path
|
43
|
+
end
|
44
|
+
|
45
|
+
it "works fine" do
|
46
|
+
BackupOrganizer.organize(@base_path) do |files|
|
47
|
+
files.stored_in('daily').if {|file| file.age < 30.days}
|
48
|
+
|
49
|
+
files.stored_in('monthly').if do |file|
|
50
|
+
file.age < 1.year &&
|
51
|
+
file.most_recent_in_its_month?
|
52
|
+
end
|
53
|
+
|
54
|
+
files.stored_in('yearly').if {|file| file.most_recent_in_its_year?}
|
55
|
+
end
|
56
|
+
|
57
|
+
expected_in_daily = [
|
58
|
+
@file_01_day_ago,
|
59
|
+
@file_10_days_ago,
|
60
|
+
@file_30_days_ago
|
61
|
+
]
|
62
|
+
|
63
|
+
expected_in_monthly = [
|
64
|
+
@file_31_days_ago,
|
65
|
+
@file_02_month_ago,
|
66
|
+
@file_06_month_ago,
|
67
|
+
@file_365_days_ago
|
68
|
+
]
|
69
|
+
|
70
|
+
expected_in_yearly = [
|
71
|
+
@file_04_days_ago,
|
72
|
+
@file_366_days_ago,
|
73
|
+
@file_three_years_ago,
|
74
|
+
@file_ten_years_ago
|
75
|
+
]
|
76
|
+
|
77
|
+
expected_deleted = [
|
78
|
+
@file_07_month_ago,
|
79
|
+
@file_a_year_and_a_bit_ago,
|
80
|
+
@file_02_month_and_a_bit_ago
|
81
|
+
]
|
82
|
+
|
83
|
+
dailies = files_in(File.expand_path('./daily', @base_path))
|
84
|
+
monthlies = files_in(File.expand_path('./monthly', @base_path))
|
85
|
+
yearlies = files_in(File.expand_path('./yearly', @base_path))
|
86
|
+
|
87
|
+
include_files(:dailies, dailies, expected_in_daily, true)
|
88
|
+
include_files(:dailies, dailies, expected_in_monthly + expected_in_yearly + expected_deleted, false)
|
89
|
+
|
90
|
+
include_files(:monthlies, monthlies, expected_in_monthly, true)
|
91
|
+
include_files(:monthlies, monthlies, expected_in_daily + expected_in_yearly + expected_deleted, false)
|
92
|
+
|
93
|
+
include_files(:yearlies, yearlies, expected_in_yearly, true)
|
94
|
+
include_files(:yearlies, yearlies, expected_in_daily + expected_in_monthly + expected_deleted, false)
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BackupOrganizer::Pattern do
|
4
|
+
subject {
|
5
|
+
BackupOrganizer::Pattern.new do |files|
|
6
|
+
files.stored_in('daily' ).if {|f| true}
|
7
|
+
files.stored_in('weekly').if {|f| true}
|
8
|
+
files.stored_in('yearly').if {|f| true}
|
9
|
+
end
|
10
|
+
}
|
11
|
+
|
12
|
+
it "can list all directories it needs" do
|
13
|
+
subject.directories.should eq %w{daily weekly yearly}
|
14
|
+
end
|
15
|
+
end
|
data/spec/rule_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BackupOrganizer::Rule do
|
4
|
+
subject {BackupOrganizer::Rule.new('/tmp/dir')}
|
5
|
+
|
6
|
+
it "knows it's directory" do
|
7
|
+
subject.directory.should eq '/tmp/dir'
|
8
|
+
end
|
9
|
+
|
10
|
+
it "can tell if the rule apply to a certain file" do
|
11
|
+
subject.if {|f| f.applies}
|
12
|
+
|
13
|
+
file = mock(File, :applies => false)
|
14
|
+
subject.applies_for?(file).should be false
|
15
|
+
|
16
|
+
file = mock(File, :applies => true)
|
17
|
+
subject.applies_for?(file).should be true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "mixes the FileExtensions in to each yielded path" do
|
21
|
+
subject.if{|f| (class << f; self; end).included_modules.include?(BackupOrganizer::Extensions::FileExtensions).should be true}
|
22
|
+
file = mock(File, :applies => true)
|
23
|
+
subject.applies_for?(file)
|
24
|
+
end
|
25
|
+
end
|
data/spec/setup_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe BackupOrganizer::Setup do
|
5
|
+
before do
|
6
|
+
path = Tempdir.new
|
7
|
+
@configuration = mock(BackupOrganizer::Configuration, :path => path, :directories => %w{daily weekly yearly}.map {|e| File.expand_path("./#{e}", path)})
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "creating the structure" do
|
11
|
+
describe "raises an error when" do
|
12
|
+
it "the path is not a directory" do
|
13
|
+
file = Tempfile.new('backup_organizer_fake_dir')
|
14
|
+
path = file.path
|
15
|
+
@configuration.stub(:path).and_return(path)
|
16
|
+
file.unlink
|
17
|
+
lambda {
|
18
|
+
BackupOrganizer::Setup.create_structure(@configuration)
|
19
|
+
}.should raise_error(BackupOrganizer::Error::InvalidPathError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "the path is a file" do
|
23
|
+
file = Tempfile.new('backup_organizer_fake_dir')
|
24
|
+
path = file.path
|
25
|
+
@configuration.stub(:path).and_return(path)
|
26
|
+
lambda {
|
27
|
+
BackupOrganizer::Setup.create_structure(@configuration)
|
28
|
+
}.should raise_error(BackupOrganizer::Error::InvalidPathError)
|
29
|
+
file.unlink
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "results in having all the needed directories present" do
|
34
|
+
BackupOrganizer::Setup.create_structure(@configuration)
|
35
|
+
@configuration.directories.each do |necessary_directory|
|
36
|
+
File.directory?(necessary_directory).should be true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "leaves an existing structure intact" do
|
41
|
+
BackupOrganizer::Setup.create_structure(@configuration)
|
42
|
+
files = %w{some test files}
|
43
|
+
require 'backup_organizer/file_utils'
|
44
|
+
@configuration.directories.each do |directory|
|
45
|
+
files.each do |file|
|
46
|
+
BackupOrganizer::FileUtils.touch(File.expand_path("./#{file}", directory))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
BackupOrganizer::Setup.create_structure(@configuration)
|
51
|
+
|
52
|
+
@configuration.directories.each do |directory|
|
53
|
+
files.each do |file|
|
54
|
+
File.exist?(File.expand_path("./#{file}", directory)).should be true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
def temp_file_at(time, destination = nil)
|
2
|
+
path = Tempfile.new('backup_organizer_integration_spec').path
|
3
|
+
File.utime(time, time, path)
|
4
|
+
if destination
|
5
|
+
BackupOrganizer::FileUtils.mv path, destination
|
6
|
+
File.expand_path("./#{File.basename(path)}", destination)
|
7
|
+
else
|
8
|
+
path
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def files_in(path)
|
13
|
+
path = "#{File.expand_path('./', path)}/"
|
14
|
+
files = []
|
15
|
+
Find.find(path) {|file| files << file.gsub(/^#{Regexp.escape(path)}/, '')}
|
16
|
+
files.select {|e| !e.empty?}
|
17
|
+
end
|
18
|
+
|
19
|
+
class Fixnum
|
20
|
+
def minutes
|
21
|
+
self * 60
|
22
|
+
end
|
23
|
+
alias minute minutes
|
24
|
+
|
25
|
+
def hours
|
26
|
+
self * 60.minutes
|
27
|
+
end
|
28
|
+
alias hour hours
|
29
|
+
|
30
|
+
def days
|
31
|
+
self * 24.hours
|
32
|
+
end
|
33
|
+
alias day days
|
34
|
+
|
35
|
+
def years
|
36
|
+
self * 365.days
|
37
|
+
end
|
38
|
+
alias year years
|
39
|
+
|
40
|
+
def ago
|
41
|
+
Time.now - self
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: backup_organizer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Thorben Schr\xC3\xB6der"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-01-24 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 7
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
version: "2"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: guard-rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: ruby_gntp
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
description: Organizes backup files in folders in patterns that can be defined by the user.
|
63
|
+
email:
|
64
|
+
- stillepost@gmail.com
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
71
|
+
files:
|
72
|
+
- .gitignore
|
73
|
+
- .rvmrc
|
74
|
+
- .travis.yml
|
75
|
+
- Gemfile
|
76
|
+
- Guardfile
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- backup_organizer.gemspec
|
81
|
+
- lib/backup_organizer.rb
|
82
|
+
- lib/backup_organizer/configuration.rb
|
83
|
+
- lib/backup_organizer/error/base_error.rb
|
84
|
+
- lib/backup_organizer/error/configuration_error.rb
|
85
|
+
- lib/backup_organizer/error/error_with_custom_message.rb
|
86
|
+
- lib/backup_organizer/error/invalid_path_error.rb
|
87
|
+
- lib/backup_organizer/error/setup_error.rb
|
88
|
+
- lib/backup_organizer/extensions/file_extensions.rb
|
89
|
+
- lib/backup_organizer/file_age.rb
|
90
|
+
- lib/backup_organizer/file_mover.rb
|
91
|
+
- lib/backup_organizer/file_utils.rb
|
92
|
+
- lib/backup_organizer/pattern.rb
|
93
|
+
- lib/backup_organizer/rule.rb
|
94
|
+
- lib/backup_organizer/setup.rb
|
95
|
+
- lib/backup_organizer/version.rb
|
96
|
+
- spec/configuration_spec.rb
|
97
|
+
- spec/extensions/file_extensions_spec.rb
|
98
|
+
- spec/file_age_spec.rb
|
99
|
+
- spec/file_mover_spec.rb
|
100
|
+
- spec/file_utils_spec.rb
|
101
|
+
- spec/integration_spec.rb
|
102
|
+
- spec/pattern_spec.rb
|
103
|
+
- spec/rule_spec.rb
|
104
|
+
- spec/setup_spec.rb
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/support/helpers.rb
|
107
|
+
- spec/support/temp_dir.rb
|
108
|
+
homepage: http://github.com/walski/backup_organizer
|
109
|
+
licenses: []
|
110
|
+
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: 3
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
hash: 3
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
version: "0"
|
134
|
+
requirements: []
|
135
|
+
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 1.8.10
|
138
|
+
signing_key:
|
139
|
+
specification_version: 3
|
140
|
+
summary: If backups should only be kept around for a certain time and be stored sparsely (weekly, monthly, yearly, whatever) after that backup_organizer can do that for you.
|
141
|
+
test_files:
|
142
|
+
- spec/configuration_spec.rb
|
143
|
+
- spec/extensions/file_extensions_spec.rb
|
144
|
+
- spec/file_age_spec.rb
|
145
|
+
- spec/file_mover_spec.rb
|
146
|
+
- spec/file_utils_spec.rb
|
147
|
+
- spec/integration_spec.rb
|
148
|
+
- spec/pattern_spec.rb
|
149
|
+
- spec/rule_spec.rb
|
150
|
+
- spec/setup_spec.rb
|
151
|
+
- spec/spec_helper.rb
|
152
|
+
- spec/support/helpers.rb
|
153
|
+
- spec/support/temp_dir.rb
|