<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>examples/unit/archive_example.rb</filename>
    </added>
    <added>
      <filename>examples/unit/gpg_example.rb</filename>
    </added>
    <added>
      <filename>examples/unit/gzip_example.rb</filename>
    </added>
    <added>
      <filename>examples/unit/local_example.rb</filename>
    </added>
    <added>
      <filename>examples/unit/mysqldump_example.rb</filename>
    </added>
    <added>
      <filename>examples/unit/s3_example.rb</filename>
    </added>
    <added>
      <filename>lib/astrails/safe/backup.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,11 +1,6 @@
 #!/usr/bin/env ruby
 
-
-require 'tempfile'
 require 'rubygems'
-require 'fileutils'
-require &quot;aws/s3&quot;
-require 'yaml'
 
 #require 'ruby-debug'
 #$:.unshift File.join(File.dirname(__FILE__), &quot;..&quot;, &quot;lib&quot;)
@@ -52,8 +47,7 @@ def main
     die &quot;Created default #{$CONFIG_FILE_NAME}. Please edit and run again.&quot;
   end
 
-
   load($CONFIG_FILE_NAME)
 end
 
-main
+main
\ No newline at end of file</diff>
      <filename>bin/astrails-safe</filename>
    </modified>
    <modified>
      <diff>@@ -15,5 +15,4 @@ Micronaut.configure do |c|
   c.color_enabled = not_in_editor?
   c.filter_run :focused =&gt; true
   c.mock_with :rr
-end
-
+end
\ No newline at end of file</diff>
      <filename>examples/example_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -120,7 +120,5 @@ describe Astrails::Safe::Config do
     }
 
     config.to_hash.should == expected
-
   end
-end
-
+end
\ No newline at end of file</diff>
      <filename>examples/unit/config_example.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,9 @@
+require &quot;aws/s3&quot;
+require 'fileutils'
+
+require 'tempfile'
 require 'extensions/mktmpdir'
+
 require 'astrails/safe/tmp_file'
 
 require 'astrails/safe/config/node'
@@ -6,6 +11,10 @@ require 'astrails/safe/config/builder'
 
 require 'astrails/safe/stream'
 
+require 'astrails/safe/backup'
+
+require 'astrails/safe/backup'
+
 require 'astrails/safe/source'
 require 'astrails/safe/mysqldump'
 require 'astrails/safe/archive'
@@ -23,16 +32,21 @@ module Astrails
   module Safe
     ROOT = File.join(File.dirname(__FILE__), &quot;..&quot;, &quot;..&quot;)
 
-    def timestamp
-      @timestamp ||= Time.now.strftime(&quot;%y%m%d-%H%M&quot;)
-    end
-
     def safe(&amp;block)
       config = Config::Node.new(&amp;block)
       #config.dump
 
-      Astrails::Safe::Mysqldump.run(config[:mysqldump, :databases])
-      Astrails::Safe::Archive.run(config[:tar, :archives])
+      if databases = config[:mysqldump, :databases]
+        databases.each do |name, config|
+          Astrails::Safe::Mysqldump.new(name, config).backup.run(config, :gpg, :gzip, :local, :s3)
+        end
+      end
+
+      if archives = config[:tar, :archives]
+        archives.each do |name, config|
+          Astrails::Safe::Archive.new(name, config).backup.run(config, :gpg, :gzip, :local, :s3)
+        end
+      end
 
       Astrails::Safe::TmpFile.cleanup
     end</diff>
      <filename>lib/astrails/safe.rb</filename>
    </modified>
    <modified>
      <diff>@@ -16,9 +16,9 @@ module Astrails
 
       def tar_files
         raise RuntimeError, &quot;missing files for tar&quot; unless @config[:files]
-        [*@config[:files]] * &quot; &quot;
+        [*@config[:files]].map {|s| s.strip} * &quot; &quot;
       end
 
     end
   end
-end
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/archive.rb</filename>
    </modified>
    <modified>
      <diff>@@ -57,4 +57,4 @@ module Astrails
       end
     end
   end
-end
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/config/builder.rb</filename>
    </modified>
    <modified>
      <diff>@@ -60,8 +60,7 @@ module Astrails
             end
           end
         end
-
       end
     end
   end
-end
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/config/node.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,15 +2,14 @@ module Astrails
   module Safe
     class Gpg &lt; Pipe
 
-      def compressed?
-        active? || @parent.compressed?
-      end
-
       protected
 
+      def post_process
+        @backup.compressed = true
+      end
+
       def pipe
         if key
-          rise RuntimeError, &quot;can't use both gpg password and pubkey&quot; if password
           &quot;|gpg -e -r #{key}&quot;
         elsif password
           &quot;|gpg -c --passphrase-file #{gpg_password_file(password)}&quot;
@@ -18,19 +17,23 @@ module Astrails
       end
 
       def extension
-        &quot;.gpg&quot; if active?
+        &quot;.gpg&quot;
       end
 
       def active?
+        raise RuntimeError, &quot;can't use both gpg password and pubkey&quot; if key &amp;&amp; password
+
         password || key
       end
 
+      private
+
       def password
-        @password ||= config[:gpg, :password]
+        @password ||= @config[:gpg, :password]
       end
 
       def key
-        @key ||= config[:gpg, :key]
+        @key ||= @config[:gpg, :key]
       end
 
       def gpg_password_file(pass)
@@ -39,4 +42,4 @@ module Astrails
       end
     end
   end
-end
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/gpg.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,24 +2,24 @@ module Astrails
   module Safe
     class Gzip &lt; Pipe
 
-      def compressed?
-        true
-      end
-
       protected
 
+      def post_process
+        @backup.compressed = true
+      end
+
       def pipe
-        &quot;|gzip&quot; if active?
+        &quot;|gzip&quot;
       end
 
       def extension
-        &quot;.gz&quot; if active?
+        &quot;.gz&quot;
       end
 
       def active?
-        !@parent.compressed?
+        !@backup.compressed
       end
 
     end
   end
-end
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/gzip.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,45 +2,37 @@ module Astrails
   module Safe
     class Local &lt; Sink
 
-      def open(&amp;block)
-        return @parent.open(&amp;block) unless active?
-        run
-        File.open(path, &amp;block) unless $DRY_RUN
-      end
-
       protected
 
       def active?
         # S3 can't upload from pipe. it needs to know file size, so we must pass through :local
-        # will change once we add SSH sink
+        # will change once we add SSH/FTP sink
         true
       end
 
       def prefix
-        @prefix ||= File.expand_path(expand(@config[:local, :path] || raise(RuntimeError, &quot;missing :local/:path in configuration&quot;)))
-      end
-
-      def command
-        &quot;#{@parent.command} &gt; #{path}&quot;
+        @prefix ||= File.expand_path(expand(@config[:local, :path] || raise(RuntimeError, &quot;missing :local/:path&quot;)))
       end
 
       def save
-        puts &quot;command: #{command}&quot; if $_VERBOSE
+        puts &quot;command: #{@backup.command}&quot; if $_VERBOSE
+
         unless $DRY_RUN
           FileUtils.mkdir_p(prefix) unless File.directory?(prefix)
-          system command
+          system &quot;#{@backup.command}&gt;#{@backup.path = path}&quot;
         end
+
       end
 
       def cleanup
         return unless keep = @config[:keep, :local]
 
-        base = File.basename(filename).split(&quot;.&quot;).first
+        base = File.basename(@backup.filename).split(&quot;.&quot;).first
 
         pattern = File.join(prefix, &quot;#{base}*&quot;)
         puts &quot;listing files #{pattern.inspect}&quot; if $_VERBOSE
         files = Dir[pattern] .
-          select{|f| File.file?(f)} .
+          select{|f| File.file?(f) &amp;&amp; File.size(f) &gt; 0} .
           sort
 
         cleanup_with_limit(files, keep) do |f|
@@ -48,7 +40,6 @@ module Astrails
           File.unlink(f) unless $DRY_RUN
         end
       end
-
     end
   end
 end</diff>
      <filename>lib/astrails/safe/local.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ module Astrails
     class Mysqldump &lt; Source
 
       def command
-        @commanbd ||= &quot;mysqldump --defaults-extra-file=#{mysql_password_file} #{@config[:options]} #{mysql_skip_tables} #{@id}&quot;
+        &quot;mysqldump --defaults-extra-file=#{mysql_password_file} #{@config[:options]} #{mysql_skip_tables} #{@id}&quot;
       end
 
       def extension; '.sql'; end
@@ -28,4 +28,4 @@ module Astrails
 
     end
   end
-end
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/mysqldump.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,15 +1,13 @@
 module Astrails
   module Safe
     class Pipe &lt; Stream
+      def process
+        return unless active?
 
-      def command
-        &quot;#{@parent.command}#{pipe}&quot;
+        @backup.command &lt;&lt; pipe
+        @backup.extension &lt;&lt; extension
+        post_process
       end
-
-      def filename
-        &quot;#{@parent.filename}#{extension}&quot;
-      end
-
     end
   end
 end</diff>
      <filename>lib/astrails/safe/pipe.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,32 +9,30 @@ module Astrails
       end
 
       def prefix
-        @prefix ||= expand(config[:s3, :path] || expand(config[:local, :path] || &quot;:kind/:id&quot;))
+        @prefix ||= expand(config[:s3, :path] || config[:local, :path] || &quot;:kind/:id&quot;)
       end
 
       def save
+        raise RuntimeError, &quot;pipe-streaming not supported for S3.&quot; unless @backup.path
+
         # needed in cleanup even on dry run
         AWS::S3::Base.establish_connection!(:access_key_id =&gt; key, :secret_access_key =&gt; secret, :use_ssl =&gt; true) unless $LOCAL
 
-        file = @parent.open
         puts &quot;Uploading #{bucket}:#{path}&quot; if $_VERBOSE || $DRY_RUN
         unless $DRY_RUN || $LOCAL
           AWS::S3::Bucket.create(bucket)
-          AWS::S3::S3Object.store(path, file, bucket)
+          File.open(@backup.path) do |file|
+            AWS::S3::S3Object.store(path, file, bucket)
+          end
           puts &quot;...done&quot; if $_VERBOSE
         end
-        file.close if file
-
       end
 
       def cleanup
-
         return if $LOCAL
 
         return unless keep = @config[:keep, :s3]
 
-        bucket = @config[:s3, :bucket]
-
         base = File.basename(filename).split(&quot;.&quot;).first
 
         puts &quot;listing files in #{bucket}:#{prefix}/#{base}&quot;
@@ -52,15 +50,15 @@ module Astrails
       end
 
       def bucket
-        config[:s3, :bucket]
+        @config[:s3, :bucket]
       end
 
       def key
-        config[:s3, :key]
+        @config[:s3, :key]
       end
 
       def secret
-        config[:s3, :secret]
+        @config[:s3, :secret]
       end
 
     end</diff>
      <filename>lib/astrails/safe/s3.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,20 +2,18 @@ module Astrails
   module Safe
     class Sink &lt; Stream
 
-      def run
-        if active?
-          save
-          cleanup
-        else
-          @parent.run
-        end
+      def process
+        return unless active?
+
+        save
+        cleanup
       end
 
       protected
 
       # prefix is defined in subclass
       def path
-        @path ||= File.join(prefix, filename)
+        @path ||= File.join(prefix, @backup.filename) + @backup.extension
       end
 
       # call block on files to be removed (all except for the LAST 'limit' files
@@ -26,8 +24,6 @@ module Astrails
         # TODO: validate here
         to_remove.each(&amp;block)
       end
-
     end
   end
-end
-
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/sink.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,27 +2,42 @@ module Astrails
   module Safe
     class Source &lt; Stream
 
+      attr_accessor :id
       def initialize(id, config)
-        @id, @config = id, config
+        @id, @config = id.to_s, config
+      end
+
+      def timestamp
+        Time.now.strftime(&quot;%y%m%d-%H%M&quot;)
+      end
+
+      def kind
+        self.class.human_name
       end
 
       def filename
-        @filename ||= expand(&quot;:kind-:id.:timestamp#{extension}&quot;)
+        @filename ||= expand(&quot;:kind-:id.:timestamp&quot;)
       end
 
-      # process each config key as source (with full pipe)
-      def self.run(config)
-        unless config
-          puts &quot;No configuration found for #{human_name}&quot;
-          return
-        end
-
-        config.each do |key, value|
-          stream = [Gpg, Gzip, Local, S3].inject(new(key, value)) do |res, klass|
-            klass.new(res)
-          end
-          stream.run
-        end
+      def backup
+        return @backup if @backup
+        @backup = Backup.new(
+          :id        =&gt; @id,
+          :kind      =&gt; kind,
+          :extension =&gt; extension,
+          :command   =&gt; command,
+          :timestamp =&gt; timestamp
+        )
+        # can't do this in the initializer hash above since
+        # filename() calls expand() which requires @backup
+        @backup.filename = filename
+        @backup
+      end
+
+      protected
+
+      def self.human_name
+        name.split('::').last.downcase
       end
 
     end</diff>
      <filename>lib/astrails/safe/source.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,44 +2,18 @@ module Astrails
   module Safe
     class Stream
 
-      def initialize(parent)
-        @parent = parent
-      end
-
-      def id
-        @id ||= @parent.id
-      end
-
-      def config
-        @config ||= @parent.config
-      end
-
-      def filename
-        @parent.filename
-      end
-
-      def compressed?
-        @parent &amp;&amp; @parent.compressed?
-      end
-
-      protected
-
-      def self.human_name
-        name.split('::').last.downcase
-      end
-
-      def kind
-        @parent ? @parent.kind : self.class.human_name
+      attr_accessor :config, :backup
+      def initialize(config, backup)
+        @config, @backup = config, backup
       end
 
       def expand(path)
         path .
-          gsub(/:kind\b/, kind) .
-          gsub(/:id\b/, id) .
-          gsub(/:timestamp\b/, timestamp)
+        gsub(/:kind\b/, @backup.kind.to_s) .
+        gsub(/:id\b/, @backup.id.to_s) .
+        gsub(/:timestamp\b/, @backup.timestamp)
       end
 
     end
   end
-end
-
+end
\ No newline at end of file</diff>
      <filename>lib/astrails/safe/stream.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,22 +2,26 @@ require 'tmpdir'
 module Astrails
   module Safe
     module TmpFile
-      @KEEP_FILES = []
-      TMPDIR = Dir.mktmpdir
+      @keep_files = []
+
+      def self.tmproot
+        @tmproot ||= Dir.mktmpdir
+      end
 
       def self.cleanup
-        FileUtils.remove_entry_secure TMPDIR
+        FileUtils.remove_entry_secure tmproot
+        @tmproot = nil
       end
 
       def self.create(name)
         # create temp directory
 
-        file = Tempfile.new(name, TMPDIR)
+        file = Tempfile.new(name, tmproot)
 
         yield file
 
         file.close
-        @KEEP_FILES &lt;&lt; file # so that it will not get gcollected and removed from filesystem until the end
+        @keep_files &lt;&lt; file # so that it will not get gcollected and removed from filesystem until the end
         file.path
       end
     end</diff>
      <filename>lib/astrails/safe/tmp_file.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,13 +6,13 @@ Gem::Specification.new do |s|
 
   s.required_rubygems_version = Gem::Requirement.new(&quot;&gt;= 0&quot;) if s.respond_to? :required_rubygems_version=
   s.authors = [&quot;Astrails Ltd.&quot;]
-  s.date = %q{2009-04-16}
+  s.date = %q{2009-04-25}
   s.default_executable = %q{astrails-safe}
   s.description = %q{Simple tool to backup MySQL databases and filesystem locally or to Amazon S3 (with optional encryption)}
   s.email = %q{we@astrails.com}
   s.executables = [&quot;astrails-safe&quot;]
   s.extra_rdoc_files = [&quot;README.markdown&quot;, &quot;LICENSE&quot;]
-  s.files = [&quot;README.markdown&quot;, &quot;VERSION.yml&quot;, &quot;bin/astrails-safe&quot;, &quot;examples/example_helper.rb&quot;, &quot;examples/unit&quot;, &quot;examples/unit/config_example.rb&quot;, &quot;examples/unit/stream_example.rb&quot;, &quot;lib/astrails&quot;, &quot;lib/astrails/safe&quot;, &quot;lib/astrails/safe/archive.rb&quot;, &quot;lib/astrails/safe/config&quot;, &quot;lib/astrails/safe/config/builder.rb&quot;, &quot;lib/astrails/safe/config/node.rb&quot;, &quot;lib/astrails/safe/gpg.rb&quot;, &quot;lib/astrails/safe/gzip.rb&quot;, &quot;lib/astrails/safe/local.rb&quot;, &quot;lib/astrails/safe/mysqldump.rb&quot;, &quot;lib/astrails/safe/pipe.rb&quot;, &quot;lib/astrails/safe/s3.rb&quot;, &quot;lib/astrails/safe/sink.rb&quot;, &quot;lib/astrails/safe/source.rb&quot;, &quot;lib/astrails/safe/stream.rb&quot;, &quot;lib/astrails/safe/tmp_file.rb&quot;, &quot;lib/astrails/safe.rb&quot;, &quot;lib/extensions&quot;, &quot;lib/extensions/mktmpdir.rb&quot;, &quot;templates/script.rb&quot;, &quot;Rakefile&quot;, &quot;LICENSE&quot;]
+  s.files = [&quot;README.markdown&quot;, &quot;VERSION.yml&quot;, &quot;bin/astrails-safe&quot;, &quot;examples/example_helper.rb&quot;, &quot;examples/unit&quot;, &quot;examples/unit/archive_example.rb&quot;, &quot;examples/unit/config_example.rb&quot;, &quot;examples/unit/gpg_example.rb&quot;, &quot;examples/unit/gzip_example.rb&quot;, &quot;examples/unit/local_example.rb&quot;, &quot;examples/unit/mysqldump_example.rb&quot;, &quot;examples/unit/s3_example.rb&quot;, &quot;lib/astrails&quot;, &quot;lib/astrails/safe&quot;, &quot;lib/astrails/safe/archive.rb&quot;, &quot;lib/astrails/safe/backup.rb&quot;, &quot;lib/astrails/safe/config&quot;, &quot;lib/astrails/safe/config/builder.rb&quot;, &quot;lib/astrails/safe/config/node.rb&quot;, &quot;lib/astrails/safe/gpg.rb&quot;, &quot;lib/astrails/safe/gzip.rb&quot;, &quot;lib/astrails/safe/local.rb&quot;, &quot;lib/astrails/safe/mysqldump.rb&quot;, &quot;lib/astrails/safe/pipe.rb&quot;, &quot;lib/astrails/safe/s3.rb&quot;, &quot;lib/astrails/safe/sink.rb&quot;, &quot;lib/astrails/safe/source.rb&quot;, &quot;lib/astrails/safe/stream.rb&quot;, &quot;lib/astrails/safe/tmp_file.rb&quot;, &quot;lib/astrails/safe.rb&quot;, &quot;lib/extensions&quot;, &quot;lib/extensions/mktmpdir.rb&quot;, &quot;templates/script.rb&quot;, &quot;Rakefile&quot;, &quot;LICENSE&quot;]
   s.has_rdoc = true
   s.homepage = %q{http://github.com/astrails/safe}
   s.rdoc_options = [&quot;--inline-source&quot;, &quot;--charset=UTF-8&quot;]</diff>
      <filename>safe.gemspec</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>examples/unit/stream_example.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>80cac2bf99ce4512c206ad77d54bf9a36684f97e</id>
    </parent>
  </parents>
  <author>
    <name>Vitaly Kushner</name>
    <email>vitaly@astrails.com</email>
  </author>
  <url>http://github.com/astrails/safe/commit/c2c572a2624d800275726f15451a162ed072bc47</url>
  <id>c2c572a2624d800275726f15451a162ed072bc47</id>
  <committed-date>2009-04-24T14:25:23-07:00</committed-date>
  <authored-date>2009-04-24T08:25:58-07:00</authored-date>
  <message>refactoring + testing</message>
  <tree>6df514eb1426d85ce7d6a6562ac4516811fe7018</tree>
  <committer>
    <name>Vitaly Kushner</name>
    <email>vitaly@astrails.com</email>
  </committer>
</commit>
