dated_backup 0.1.0 → 0.2.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.
@@ -0,0 +1,41 @@
1
+
2
+ module DatedBackup
3
+ class DSL
4
+ module Main
5
+
6
+ attr_reader :hash, :procs
7
+
8
+ def initialize
9
+ @hash = {}
10
+ @procs = {}
11
+ end
12
+
13
+ def before &blk
14
+ raise_without_block &blk
15
+ @procs[:before] = blk
16
+ end
17
+
18
+ def after &blk
19
+ raise_without_block &blk
20
+ @procs[:after] = blk
21
+ end
22
+
23
+ def method_missing sym, *args
24
+ if RSYNC_OPTIONS.include?(sym)
25
+ @hash[sym] = args
26
+ else
27
+ raise InvalidKeyError, "The key '#{sym}' is not a recognized expression"
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ def raise_without_block &blk
34
+ raise NoBlockGiven, "A block (do...end) must be given" if !block_given?
35
+ end
36
+
37
+ RSYNC_OPTIONS = [:source, :sources, :destination, :options, :user_domain]
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,122 @@
1
+
2
+ module DatedBackup
3
+ class DSL
4
+
5
+ # After a run through the DSL,
6
+ # the TimeExtensions#kept method should contain an array
7
+ # of all of the items to keep. Each item will be a hash
8
+ # with two keys: a :constraint key, which contains a
9
+ # range of Times (from oldest to newest), and a :scope
10
+ # key, which will indicate whether the backup to look
11
+ # at is a weekly, monthly, etc. If no :scope key is given,
12
+ # then all of the backups in the time range given by
13
+ # the :constraint key will be assumed to be kept.
14
+ module TimeExtensions
15
+
16
+ attr_reader :last_time, :time_range
17
+ attr_reader :kept
18
+
19
+ def initialize
20
+ @kept = []
21
+ @time_range = {}
22
+ @last_time = nil
23
+ end
24
+
25
+ def backup *args; return self; end
26
+ def from *args; return self; end
27
+
28
+ alias :backups :backup
29
+
30
+ TimeSymbol.valid_symbols.each do |sym|
31
+ time_sym = TimeSymbol.new sym
32
+
33
+ define_method time_sym.singular do |*args|
34
+ self.send :time_component, time_sym.singular
35
+ end
36
+
37
+ alias_method time_sym.plural, time_sym.singular
38
+
39
+ define_method time_sym.adverb do
40
+ @time_range[:scope] = time_sym.adverb
41
+ end
42
+ end
43
+
44
+
45
+ def this arg, now=Time.now
46
+ set_time_range :this, now
47
+ end
48
+
49
+ def last arg, now=Time.now
50
+ set_time_range :last, now
51
+ end
52
+
53
+ def keep arg, now=Time.now
54
+ all(self, now) unless time_range[:constraint]
55
+
56
+ @kept << time_range
57
+ reset_times
58
+ self
59
+ end
60
+
61
+ def all arg=self, now=Time.now
62
+ @time_range[:constraint] = Time.epoch...now
63
+ self
64
+ end
65
+
66
+ protected
67
+
68
+
69
+ def set_time_range sym, now
70
+ if sym == :last
71
+ year, month, week, day = now.last_year, now.last_month, now.last_week, now.yesterday
72
+ elsif sym == :this
73
+ add_end_of_star_singletons(now)
74
+
75
+ # This is another little trick. Normally we would just call the methods
76
+ # below something like: Time.now.beginning_of_year...Time.now.end_of_year
77
+ year, month, week, day = now, now, now, now
78
+ end
79
+
80
+ @time_range[:constraint] =
81
+ case last_time
82
+ when :year
83
+ year.beginning_of_year...year.end_of_year
84
+ when :month
85
+ month.beginning_of_month...month.end_of_month
86
+ when :week
87
+ week.beginning_of_week...week.end_of_week
88
+ when :day
89
+ day.beginning_of_day...day.end_of_day
90
+ end
91
+
92
+ self
93
+ end
94
+
95
+ # This method adds the end_of_* methods to the object passed in. They are used in the default case
96
+ # of the modifyer being "this" and not "last". In the 'this' case, the time
97
+ # *is* now, so end_of_* (end_of_year, end_of_day), etc. should *NOT* need to be called
98
+ # (and in fact, we do not want them to be called, because that would represent
99
+ # some time period in the future). Those methods are overrided to return self
100
+ def add_end_of_star_singletons(now)
101
+ class << now
102
+ [:end_of_year, :end_of_month, :end_of_week, :end_of_day].each do |end_of|
103
+ define_method end_of do
104
+ return self
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def time_component(type)
111
+ @last_time = type
112
+ self
113
+ end
114
+
115
+ def reset_times
116
+ @time_range = {}
117
+ @last_time = nil
118
+ end
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,4 @@
1
+
2
+ require File.dirname(__FILE__) + "/dsl/execution_context"
3
+ require File.dirname(__FILE__) + "/dsl/main"
4
+ require File.dirname(__FILE__) + "/dsl/time_extensions"
@@ -0,0 +1,27 @@
1
+
2
+ class Array
3
+ def car
4
+ first
5
+ end
6
+
7
+ def cdr
8
+ self.[](1..self.length)
9
+ end
10
+
11
+ def to_backup_set
12
+ DatedBackup::Core::BackupSet.new(self)
13
+ end
14
+ end
15
+
16
+ # A subclass of Array, but it calls uniq!, sort!, and reverse!
17
+ # (in that order) after the instance is created
18
+ class ReverseSortedUniqueArray < Array
19
+
20
+ def initialize *args, &blk
21
+ super *args, &blk
22
+ uniq!
23
+ sort!
24
+ reverse!
25
+ end
26
+
27
+ end
@@ -0,0 +1,10 @@
1
+
2
+ class TimeSymbolError < RuntimeError; end
3
+
4
+ class DatedBackupError < RuntimeError; end
5
+
6
+ class DirectoryError < DatedBackupError; end
7
+ class InvalidDirectoryError < DirectoryError; end
8
+ class NoBlockGiven < DatedBackupError; end
9
+ class InvalidKeyError < DatedBackupError; end
10
+
@@ -0,0 +1,8 @@
1
+
2
+ class String
3
+ def to_time
4
+ string = self.dup
5
+ string.sub!(/^(.*?)([\d\-\h\m\s]+)(.*?)$/) { $2 }
6
+ Time.gm(*(string.split('-').map { |element| element.sub /h|m|s/, ''}))
7
+ end
8
+ end
@@ -0,0 +1,50 @@
1
+ require 'active_support/core_ext/time/calculations'
2
+ require 'active_support/core_ext/numeric/time'
3
+
4
+ class Time
5
+ class << self
6
+ include ActiveSupport::CoreExtensions::Time::Calculations::ClassMethods
7
+
8
+ def epoch
9
+ Time.at(0)
10
+ end
11
+
12
+ end
13
+
14
+ include ActiveSupport::CoreExtensions::Time::Calculations
15
+
16
+ def end_of_day
17
+ tomorrow.beginning_of_day - 1
18
+ end
19
+
20
+ def end_of_month
21
+ next_month.beginning_of_month - 1
22
+ end
23
+
24
+ def end_of_year
25
+ next_year.beginning_of_year - 1
26
+ end
27
+
28
+ def end_of_week
29
+ next_week.beginning_of_week - 1
30
+ end
31
+
32
+ def last_week
33
+ 1.week.ago.beginning_of_week
34
+ end
35
+
36
+ alias :at_end_of_week :end_of_week
37
+ alias :at_end_of_month :end_of_month
38
+ alias :at_end_of_day :end_of_day
39
+ alias :at_end_of_year :end_of_year
40
+
41
+ def week
42
+ beginning_of_week...end_of_week
43
+ end
44
+
45
+ end
46
+
47
+ class Fixnum
48
+ include ActiveSupport::CoreExtensions::Numeric::Time
49
+ end
50
+
@@ -0,0 +1,54 @@
1
+
2
+ # Used to change the the TimeSymbols (:year, :month, :day, and :week) into the
3
+ # various natural language forms (the symbols singular, plural, and adverb).
4
+ # Initialize with the singular symbol name, or call TimeSymbol.valid_symbols
5
+ # to give an array of valid symbols.
6
+ class TimeSymbol
7
+ VALID_TIME_COMPONENTS = [:year, :month, :week, :day]
8
+
9
+ class << self
10
+ def all
11
+ VALID_TIME_COMPONENTS.dup
12
+ end
13
+
14
+ alias :valid_symbols :all
15
+ end
16
+
17
+ def initialize(sym)
18
+ if VALID_TIME_COMPONENTS.include? sym
19
+ @sym = sym
20
+ else
21
+ raise TimeSymbolError, "The symbol given must be a valid TimeSymbol (:year, :month, :week, or :day)"
22
+ end
23
+ end
24
+
25
+ def singular
26
+ @sym
27
+ end
28
+
29
+ def plural
30
+ "#{@sym}s".to_sym
31
+ end
32
+
33
+ def adverb
34
+ if @sym == :day
35
+ :daily
36
+ else
37
+ "#{@sym}ly".to_sym
38
+ end
39
+ end
40
+
41
+ def to_sym
42
+ @sym.to_sym
43
+ end
44
+
45
+ def to_s
46
+ @sym.to_s
47
+ end
48
+
49
+ def inspect
50
+ @sym.inspect
51
+ end
52
+ end
53
+
54
+ TimeSymbols = TimeSymbol.all
@@ -0,0 +1,6 @@
1
+
2
+ require File.dirname(__FILE__) + "/extensions/array"
3
+ require File.dirname(__FILE__) + "/extensions/time"
4
+ require File.dirname(__FILE__) + "/extensions/error"
5
+ require File.dirname(__FILE__) + "/extensions/time_symbol"
6
+ require File.dirname(__FILE__) + "/extensions/string"
data/lib/dated_backup.rb CHANGED
@@ -1,2 +1,22 @@
1
+ # DatedBackup, A snapshot backup utility
2
+ # Copyright (C) Scott Taylor (scott@railsnewbie.com), 2007
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ #
1
17
 
2
- require File.dirname(__FILE__) + "/dated_backup/dated_backup"
18
+
19
+ require 'rubygems'
20
+ require File.dirname(__FILE__) + "/dated_backup/extensions"
21
+ require File.dirname(__FILE__) + "/dated_backup/dsl"
22
+ require File.dirname(__FILE__) + "/dated_backup/core"
metadata CHANGED
@@ -3,9 +3,9 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: dated_backup
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2007-07-09 00:00:00 -04:00
8
- summary: Incremental Backups Using Rsync
6
+ version: 0.2.0
7
+ date: 2007-08-02 00:00:00 -04:00
8
+ summary: Incremental Dated Backups Using Rsync
9
9
  require_paths:
10
10
  - lib
11
11
  email: scott@railsnewbie.com
@@ -15,7 +15,7 @@ description:
15
15
  autorequire:
16
16
  default_executable:
17
17
  bindir: bin
18
- has_rdoc: false
18
+ has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
21
  - - ">"
@@ -29,24 +29,61 @@ post_install_message:
29
29
  authors:
30
30
  - Scott Taylor
31
31
  files:
32
+ - bin/dbackup
32
33
  - lib/dated_backup
33
- - lib/dated_backup/command_line.rb
34
- - lib/dated_backup/dated_backup.rb
34
+ - lib/dated_backup/core
35
+ - lib/dated_backup/core/backup_remover.rb
36
+ - lib/dated_backup/core/backup_set.rb
37
+ - lib/dated_backup/core/command_line.rb
38
+ - lib/dated_backup/core/dated_backup.rb
39
+ - lib/dated_backup/core/tasks.rb
40
+ - lib/dated_backup/core/warnings.rb
41
+ - lib/dated_backup/core.rb
42
+ - lib/dated_backup/dsl
43
+ - lib/dated_backup/dsl/execution_context.rb
44
+ - lib/dated_backup/dsl/main.rb
45
+ - lib/dated_backup/dsl/time_extensions.rb
46
+ - lib/dated_backup/dsl.rb
47
+ - lib/dated_backup/extensions
48
+ - lib/dated_backup/extensions/array.rb
49
+ - lib/dated_backup/extensions/error.rb
50
+ - lib/dated_backup/extensions/string.rb
51
+ - lib/dated_backup/extensions/time.rb
52
+ - lib/dated_backup/extensions/time_symbol.rb
53
+ - lib/dated_backup/extensions.rb
35
54
  - lib/dated_backup.rb
36
- - example_scripts/example.com.rb
37
- - example_scripts/local_etc_backup.rb
38
- - example_scripts/samba_shares.rb
55
+ - example_configs/example.com
56
+ - example_configs/local_etc_backup
57
+ - example_configs/samba_shares
58
+ - README
59
+ - COPYRIGHT
60
+ - RELEASES
61
+ - CHANGELOG
39
62
  test_files: []
40
63
 
41
64
  rdoc_options: []
42
65
 
43
- extra_rdoc_files: []
44
-
45
- executables: []
46
-
66
+ extra_rdoc_files:
67
+ - example_configs/example.com
68
+ - example_configs/local_etc_backup
69
+ - example_configs/samba_shares
70
+ - README
71
+ - COPYRIGHT
72
+ - RELEASES
73
+ - CHANGELOG
74
+ executables:
75
+ - dbackup
47
76
  extensions: []
48
77
 
49
78
  requirements: []
50
79
 
51
- dependencies: []
52
-
80
+ dependencies:
81
+ - !ruby/object:Gem::Dependency
82
+ name: activesupport
83
+ version_requirement:
84
+ version_requirements: !ruby/object:Gem::Version::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 1.4.2
89
+ version:
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # A script to back up etc, locally, in /root/etc_backup
4
-
5
- require "dated_backup"
6
-
7
- DatedBackup.new(
8
- :source => "/etc",
9
- :destination => "/root/etc_backup"
10
- ).run
@@ -1,8 +0,0 @@
1
- class DatedBackup
2
- module CommandLine
3
- def execute(cmd)
4
- puts "* running: #{cmd}"
5
- puts %x(#{cmd})
6
- end
7
- end
8
- end
@@ -1,80 +0,0 @@
1
- require File.dirname(__FILE__) + "/command_line"
2
-
3
- class DatedBackup
4
-
5
- include DatedBackup::CommandLine
6
-
7
- attr_accessor :source, :destination, :opts, :backup_root, :user_domain
8
-
9
- def initialize(h={})
10
- @backup_root = h[:destination]
11
- @opts = h[:options] || ""
12
- @user_domain = h[:user_domain]
13
- @destination = generate_backup_filename
14
-
15
- @sources = h[:sources] || [h[:source]]
16
-
17
- if @user_domain
18
- @sources.map! { |src| "#{@user_domain}:#{src}" }
19
- end
20
-
21
- end
22
-
23
- # create the first backup, if non-existent
24
- # otherwise cp -al (or # replace cp -al a b with cd a && find . -print | cpio -dpl ../b )
25
- # and then create the backup of the dirs using rsync -a --delete
26
- # the files, in the end, should be read only and undeletable
27
- def run
28
- create_main_backup_directory
29
- cp_last_backup_to_new_backup unless Dir.glob("#{@backup_root}/*").empty?
30
- create_backup
31
- #set_permissions(dest)
32
- end
33
-
34
- def create_backup
35
- @sources.each do |source|
36
- cmd = "rsync -a --delete #{opts} #{source} #{@destination}"
37
- execute(cmd)
38
- puts "\n\n"
39
- end
40
- end
41
-
42
- def cp_last_backup_to_new_backup
43
- last_backup_dir = find_last_backup_filename
44
-
45
- cmd = "cp -al #{last_backup_dir} #{@destination}"
46
- execute(cmd)
47
- end
48
-
49
- def create_main_backup_directory
50
- begin
51
- unless File.exists? backup_root
52
- puts "* Creating main backup directory #{backup_root}"
53
- Dir.mkdir backup_root
54
- end
55
- rescue Errno::EACCES => e
56
- puts "-- Exiting script because main directory could not be created. \n Error Message: #{e}"
57
- exit
58
- end
59
- end
60
-
61
- #def set_permissions(file, permission="400", user="root", group="root")
62
- # cmd = "chmod -R #{permission} #{file}"
63
- # execute(cmd)
64
- # cmd = "chown -R #{user}:#{group} #{file}"
65
- # execute(cmd)
66
- #end
67
-
68
- private
69
-
70
- def generate_backup_filename
71
- timestamp = Time.now.strftime "%Y-%m-%d-%Hh-%Mm-%Ss"
72
- "#{@backup_root}/#{timestamp}"
73
- end
74
-
75
- def find_last_backup_filename
76
- Dir.glob("#{@backup_root}/*").sort.last
77
- end
78
- end
79
-
80
-