rsync-1.0.9/0000755000175000017500000000000012667615512012122 5ustar sbadiasbadiarsync-1.0.9/README.md0000644000175000017500000000215412667615512013403 0ustar sbadiasbadia# Rsync [![Build Status](https://travis-ci.org/jbussdieker/ruby-rsync.png?branch=master)](https://travis-ci.org/jbussdieker/ruby-rsync) [![Code Climate](https://codeclimate.com/github/jbussdieker/ruby-rsync.png)](https://codeclimate.com/github/jbussdieker/ruby-rsync) [![Gem Version](https://badge.fury.io/rb/rsync.png)](http://badge.fury.io/rb/rsync) [![Coverage Status](https://coveralls.io/repos/jbussdieker/ruby-rsync/badge.png)](https://coveralls.io/r/jbussdieker/ruby-rsync) [![Dependency Status](https://gemnasium.com/jbussdieker/ruby-rsync.svg)](https://gemnasium.com/jbussdieker/ruby-rsync) Ruby/Rsync is a Ruby library that can syncronize files between remote hosts by wrapping a call to the rsync binary. ## Usage Minimal example ```ruby require "rsync" result = Rsync.run("/path/to/src", "/path/to/dest") ``` Complete example ```ruby require "rsync" Rsync.run("/path/to/src", "/path/to/dest") do |result| if result.success? result.changes.each do |change| puts "#{change.filename} (#{change.summary})" end else puts result.error end end ``` rsync-1.0.9/rsync.gemspec0000644000175000017500000000165212667615512014631 0ustar sbadiasbadia# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'rsync/version' Gem::Specification.new do |spec| spec.name = "rsync" spec.version = Rsync::VERSION spec.authors = ["Joshua Bussdieker"] spec.email = ["jbussdieker@gmail.com"] spec.summary = %q{Ruby/Rsync is a Ruby library that can syncronize files between remote hosts by wrapping a call to the rsync binary.} spec.homepage = "http://github.com/jbussdieker/ruby-rsync" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" spec.add_development_dependency "rspec" end rsync-1.0.9/Gemfile0000644000175000017500000000017312667615512013416 0ustar sbadiasbadiasource 'https://rubygems.org' # Specify your gem's dependencies in rsync.gemspec gemspec gem 'coveralls', require: false rsync-1.0.9/.travis.yml0000644000175000017500000000010512667615512014227 0ustar sbadiasbadia--- script: 'rake spec' rvm: - 1.9.2 - 1.9.3 - 2.0.0 - 2.1.2 rsync-1.0.9/LICENSE.txt0000644000175000017500000000206212667615512013745 0ustar sbadiasbadiaCopyright (c) 2013 Joshua Bussdieker MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rsync-1.0.9/.gitignore0000644000175000017500000000025312667615512014112 0ustar sbadiasbadia*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp test.rb test.txt rsync-1.0.9/spec/0000755000175000017500000000000012667615512013054 5ustar sbadiasbadiarsync-1.0.9/spec/spec_helper.rb0000644000175000017500000000110212667615512015664 0ustar sbadiasbadiarequire 'tmpdir' require 'coveralls' Coveralls.wear! class TempDir attr_accessor :path def initialize(root, subpath) @path = File.join(root, subpath) Dir.mkdir(@path) end def tree #`cd #{@path}; tree -pugAD` #`cd #{@path}; find . -printf "%A@ %p\n"` `cd #{@path}; find . -printf "%p\n"` end def mkdir(path) Dir.mkdir(File.join(@path, path)) end def eql? other tree == other.tree end def to_s tree end def self.create(&block) Dir.mktmpdir do |dir| yield new(dir, "src"), new(dir, "dest") end end end rsync-1.0.9/spec/rsync/0000755000175000017500000000000012667615512014212 5ustar sbadiasbadiarsync-1.0.9/spec/rsync/command_spec.rb0000644000175000017500000000026412667615512017171 0ustar sbadiasbadiarequire 'rsync/command' describe Rsync::Command do it "should work" do Rsync::Command.run("/path/to/src/", "/path/to/dest", "-a").should be_kind_of(Rsync::Result) end end rsync-1.0.9/spec/rsync/result_spec.rb0000644000175000017500000000142512667615512017071 0ustar sbadiasbadiarequire 'rsync/result' describe Rsync::Result do it "should handle basic example" do result = Rsync::Result.new("", 0) result.changes.should eql([]) result.error.should eql("Success") result.success?.should eql(true) result.exitcode.should eql(0) end it "should handle basic example with changes" do result = Rsync::Result.new(">f......... filename\n", 0) result.changes.length.should eql(1) result.error.should eql("Success") result.success?.should eql(true) result.exitcode.should eql(0) end it "should handle syntax error" do result = Rsync::Result.new("", 1) result.changes.should eql([]) result.error.should eql("Syntax or usage error") result.success?.should eql(false) result.exitcode.should eql(1) end end rsync-1.0.9/spec/rsync/change_spec.rb0000644000175000017500000000274012667615512017001 0ustar sbadiasbadiarequire 'rsync/change' describe Rsync::Change do it "should handle filename" do Rsync::Change.new(" filename").filename.should eql("filename") end it "should handle message type" do Rsync::Change.new("*deleting ").summary.should eql("deleting") end it "should handle update types" do Rsync::Change.new("< ").update_type.should eql(:sent) Rsync::Change.new("> ").update_type.should eql(:recv) Rsync::Change.new("c ").update_type.should eql(:change) Rsync::Change.new("h ").update_type.should eql(:hard_link) Rsync::Change.new(". ").update_type.should eql(:no_update) Rsync::Change.new("* ").update_type.should eql(:message) end it "should handle file types" do Rsync::Change.new(" f ").file_type.should eql(:file) Rsync::Change.new(" d ").file_type.should eql(:directory) Rsync::Change.new(" L ").file_type.should eql(:symlink) Rsync::Change.new(" D ").file_type.should eql(:device) Rsync::Change.new(" S ").file_type.should eql(:special) end it "should handle checksum info" do Rsync::Change.new(" c ").checksum.should eql(:changed) Rsync::Change.new(" . ").checksum.should eql(:no_change) Rsync::Change.new(" ").checksum.should eql(:identical) Rsync::Change.new(" + ").checksum.should eql(:new) Rsync::Change.new(" ? ").checksum.should eql(:unknown) end end rsync-1.0.9/spec/rsync_spec.rb0000644000175000017500000000230412667615512015550 0ustar sbadiasbadiarequire 'spec_helper' require 'rsync' describe Rsync do context 'with configurations' do before do Rsync.configure do |config| config.host = 'root@127.0.0.1' end end it "should respond to host" do Rsync.should respond_to(:host) Rsync.host.should == 'root@127.0.0.1' end describe "run" do it "prepend the host to the destination" do Rsync::Command.stub(:run) Rsync.run('/foo1', '/foo2', ["-a"]) Rsync::Command.should have_received(:run).with('/foo1', 'root@127.0.0.1:/foo2', ["-a"]) end end end around(:each) do |example| TempDir.create do |src, dest| @src = src @dest = dest example.run end end it "should work" do @src.mkdir("blah") Rsync.run(@src.path + "/", @dest.path, ["-a"]) @dest.should eql(@src) end it "should dry run" do @src.mkdir("blah") Rsync.run(@src.path + "/", @dest.path, ["-a", "-n"]) @dest.should_not eql(@src) end it "should list changes" do @src.mkdir("blah") result = Rsync.run(@src.path + "/", @dest.path, ["-a"]) result.should be_success result.changes.length.should eql(1) @dest.should eql(@src) end end rsync-1.0.9/Rakefile0000644000175000017500000000013512667615512013566 0ustar sbadiasbadiarequire "bundler/gem_tasks" require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) rsync-1.0.9/metadata.yml0000644000175000017500000000477212667615512014437 0ustar sbadiasbadia--- !ruby/object:Gem::Specification name: rsync version: !ruby/object:Gem::Version version: 1.0.9 platform: ruby authors: - Joshua Bussdieker autorequire: bindir: bin cert_chain: [] date: 2014-10-01 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' description: email: - jbussdieker@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".gitignore" - ".rspec" - ".travis.yml" - Gemfile - LICENSE.txt - README.md - Rakefile - lib/rsync.rb - lib/rsync/change.rb - lib/rsync/command.rb - lib/rsync/configure.rb - lib/rsync/result.rb - lib/rsync/version.rb - rsync.gemspec - spec/rsync/change_spec.rb - spec/rsync/command_spec.rb - spec/rsync/result_spec.rb - spec/rsync_spec.rb - spec/spec_helper.rb homepage: http://github.com/jbussdieker/ruby-rsync licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.2.2 signing_key: specification_version: 4 summary: Ruby/Rsync is a Ruby library that can syncronize files between remote hosts by wrapping a call to the rsync binary. test_files: - spec/rsync/change_spec.rb - spec/rsync/command_spec.rb - spec/rsync/result_spec.rb - spec/rsync_spec.rb - spec/spec_helper.rb rsync-1.0.9/lib/0000755000175000017500000000000012667615512012670 5ustar sbadiasbadiarsync-1.0.9/lib/rsync.rb0000644000175000017500000000107312667615512014354 0ustar sbadiasbadiarequire "rsync/version" require "rsync/command" require "rsync/result" require 'rsync/configure' # The main interface to rsync module Rsync extend Configure # Creates and runs an rsync {Command} and return the {Result} # @param source {String} # @param destination {String} # @param args {Array} # @return {Result} # @yield {Result} def self.run(source, destination, args = [], &block) destination = "#{self.host}:#{destination}" if self.host result = Command.run(source, destination, args) yield(result) if block_given? result end end rsync-1.0.9/lib/rsync/0000755000175000017500000000000012667615512014026 5ustar sbadiasbadiarsync-1.0.9/lib/rsync/version.rb0000644000175000017500000000007112667615512016036 0ustar sbadiasbadiamodule Rsync # Project version VERSION = "1.0.9" end rsync-1.0.9/lib/rsync/result.rb0000644000175000017500000000427712667615512015703 0ustar sbadiasbadiarequire 'rsync/change' module Rsync # The result of a sync. class Result # Exit code returned by rsync attr_accessor :exitcode # Error messages by exit code ERROR_CODES = { "0" => "Success", "1" => "Syntax or usage error", "2" => "Protocol incompatibility", "3" => "Errors selecting input/output files, dirs", "4" => "Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that can not support them; or an option was specified that is supported by the client and not by the server.", "5" => "Error starting client-server protocol", "6" => "Daemon unable to append to log-file", "10" => "Error in socket I/O", "11" => "Error in file I/O", "12" => "Error in rsync protocol data stream", "13" => "Errors with program diagnostics", "14" => "Error in IPC code", "20" => "Received SIGUSR1 or SIGINT", "21" => "Some error returned by waitpid()", "22" => "Error allocating core memory buffers", "23" => "Partial transfer due to error", "24" => "Partial transfer due to vanished source files", "25" => "The --max-delete limit stopped deletions", "30" => "Timeout in data send/receive", "35" => "Timeout waiting for daemon connection" } # @!visibility private def initialize(raw, exitcode) @raw = raw @exitcode = exitcode end # Whether the rsync job was run without errors. # @return {Boolean} def success? @exitcode == 0 end # The error message based on exit code. # @return {String} def error error_key = @exitcode.to_s if ERROR_CODES.has_key? error_key ERROR_CODES[error_key] else "Unknown Error" end end # List of changes made during this run. # # @return {Array} def changes change_list end private def change_list list = [] @raw.split("\n").each do |line| #if line =~ /^([<>ch.*][fdLDS][ .+\?cstTpoguax]{9}) (.*)$/ if line =~ /^([<>ch+\.\*].{10}) (.*)$/ detail = Change.new(line) list << detail if detail.changed? end end list end end end rsync-1.0.9/lib/rsync/configure.rb0000644000175000017500000000026712667615512016341 0ustar sbadiasbadiamodule Rsync module Configure VALID_OPTION_KEYS = [ :host ].freeze attr_accessor *VALID_OPTION_KEYS def configure yield self end end end rsync-1.0.9/lib/rsync/change.rb0000644000175000017500000000661612667615512015611 0ustar sbadiasbadiamodule Rsync # Provides details about changes made to a specific file. # # Change Flags: # # :no_change # :identical # :new # :unknown # :changed class Change def initialize(data) @data = data end # The filename associated with this change. # @return [String] def filename @data[12..-1] end # Whether the file was changed or not. # @return [Boolean] def changed? if update_type == :no_change false else true end end # Simple description of the change. # @return [String] def summary if update_type == :message message elsif update_type == :recv and @data[2,9] == "+++++++++" "creating local" elsif update_type == :recv "updating local" elsif update_type == :sent and @data[2,9] == "+++++++++" "creating remote" elsif update_type == :sent "updating remote" else changes = [] [:checksum, :size, :timestamp, :permissions, :owner, :group, :acl].each do |prop| changes << prop if send(prop) == :changed end changes.join(", ") end end # @!group Change Flags # The change, if any, to the checksum of the file. # @return [Symbol] def checksum attribute_prop(2) end # The change, if any, to the size of the file. # @return [Symbol] def size attribute_prop(3) end # The change, if any, to the timestamp of the file. # @return [Symbol] def timestamp attribute_prop(4) end # The change, if any, to the file permissions. # @return [Symbol] def permissions attribute_prop(5) end # The change, if any, to the owner of the file. # @return [Symbol] def owner attribute_prop(6) end # The change, if any, to the group of the file. # @return [Symbol] def group attribute_prop(7) end # The change, if any, to the file ACL. # @return [Symbol] def acl attribute_prop(9) end # The change, if any, to the file's extended attributes. # @return [Symbol] def ext_attr attribute_prop(10) end # @!endgroup # The type of update made to the file. # # :sent # :recv # :change # :hard_link # :no_update # :message # # @return [Symbol] def update_type case raw_update_type when '<' :sent when '>' :recv when 'c' :change when 'h' :hard_link when '.' :no_update when '*' :message end end # The type of file. # # :file # :directory # :symlink # :device # :special # # @return [Symbol] def file_type case raw_file_type when 'f' :file when 'd' :directory when 'L' :symlink when 'D' :device when 'S' :special end end private def message @data[1..10].strip end def raw_update_type @data[0,1] end def raw_file_type @data[1,1] end def attribute_prop(index) case @data[index,1] when '.' :no_change when ' ' :identical when '+' :new when '?' :unknown else :changed end end end end rsync-1.0.9/lib/rsync/command.rb0000644000175000017500000000073712667615512016000 0ustar sbadiasbadiamodule Rsync # An rsync command to be run class Command # Runs the rsync job and returns the results # # @param args {Array} # @return {Result} def self.run(*args) output = run_command("rsync --itemize-changes #{args.join(" ")}") Result.new(output, $?.exitstatus) end private def self.run_command(cmd, &block) if block_given? IO.popen("#{cmd} 2>&1", &block) else `#{cmd} 2>&1` end end end end rsync-1.0.9/.rspec0000644000175000017500000000001012667615512013226 0ustar sbadiasbadia--color