rshare 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format progress --fail-fast
@@ -0,0 +1,8 @@
1
+ before_install: gem install bundler --pre
2
+
3
+ script: bundle exec rspec
4
+
5
+ rvm:
6
+ - 1.9.2
7
+ - 1.9.3
8
+ - ruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rshare.gemspec
4
+ gemspec
@@ -0,0 +1,5 @@
1
+ guard 'rspec', :cli => '--options spec/.rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Johannes Huning
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ # rshare
2
+
3
+ Tiny ruby app allowing for automated file sharing between friends using rsync
4
+
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'rshare'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install rshare
19
+
20
+
21
+ ## Usage
22
+
23
+ 1. Install rshare
24
+ 2. Create a JSON configuration file for your repository:
25
+
26
+ {
27
+ "local": "~/public/",
28
+ "remote": "user@somehost.com:~/public/",
29
+ "options": [ "--bwidth=10", "--progress" ]
30
+ }
31
+ *The trailing slash tells rshare/rsync to sync the directory's contents instead of the directories themselves*
32
+
33
+ 3. **Either** pull files from the repository: `rshare pull conf.json`
34
+ 4. **Or** mark some of your repository's directories to be pushed by placing a `.rshare` file in them:
35
+
36
+ touch path/to/shared/dir/.rshare
37
+ Then push your files:
38
+
39
+ rshare push conf.json
40
+
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
4
+
5
+ require 'rshare'
6
+ require 'rshare/exec'
7
+ require 'rshare/opts'
8
+
9
+ RShare::Exec.run!(ARGV)
@@ -0,0 +1,4 @@
1
+ require "rshare/version"
2
+ require "rshare/conf"
3
+ require "rshare/repo"
4
+ require "rshare/rsync"
@@ -0,0 +1,16 @@
1
+ require 'json'
2
+
3
+ module RShare
4
+ module Configuration
5
+ extend self
6
+
7
+ def load(filename)
8
+ conf = File.open(filename, "rt") { |f| JSON.load(f.read()) }
9
+ # Expand local paths
10
+ had_trailing_slash = conf['local'][-1].eql?('/')
11
+ conf['local'] = File.expand_path(conf['local']) +
12
+ (had_trailing_slash ? '/' : '')
13
+ conf
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ require 'logger'
2
+
3
+ module RShare
4
+ module Exec
5
+ def self.run!(args)
6
+ $opts = Options.parse!(args)
7
+
8
+ if ARGV.length < 2 or ARGV.first !~ /^push|pull$/
9
+ # Invalid arguments: Pretend the user asked for help
10
+ Options.parse! %w{--help}
11
+ end
12
+
13
+ # Log to $stdout by default
14
+ $log = Logger.new($stdout, shift_age = 'weekly')
15
+ $log.level = $opts[:verbose] ? Logger::DEBUG : Logger::INFO
16
+
17
+ # Daemonize if asked to
18
+ if $opts[:daemon]
19
+ $stdout.reopen File.open("./rshare.log", 'a')
20
+ Process.daemon(nochdir=true, noclose=true)
21
+ $log.info "Daemonized with PID #{Process.pid}"
22
+ end
23
+
24
+ confs = ARGV[1, ARGV.length].map { |f| Configuration.load(f) }
25
+ confs.each { |c| ARGV.first =~ /push/ ? RSync.push!(c) : RSync.pull!(c) }
26
+
27
+ $log.info "Exiting (PID #{Process.pid})"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ require 'optparse'
2
+
3
+ module RShare
4
+ module Options
5
+ def self.parse!(args)
6
+ opts = {}
7
+
8
+ op = OptionParser.new { |op|
9
+ op.banner = "Usage: #{$0} <push|pull> <configuration...> [option...]"
10
+ op.separator ""
11
+ op.separator "Options:"
12
+
13
+ op.on("-d", "--daemonize", "Daemonize podshot") { |p|
14
+ opts[:daemon] = true
15
+ }
16
+
17
+ op.on_tail("-h", "--help", "Show this message") {
18
+ puts op
19
+ Kernel.exit(1)
20
+ }
21
+
22
+ op.on_tail("-v", "--version", "Show version") {
23
+ puts RShare::VERSION
24
+ Kernel.exit(2)
25
+ }
26
+ }
27
+
28
+ op.parse!(args)
29
+ opts
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ module RShare
2
+ module Repository
3
+ extend self
4
+
5
+ def includes(dir)
6
+ dir = File.expand_path(dir)
7
+ dirs = Dir[dir + "/**/*"].keep_if { |f| File.directory?(f) }
8
+ dirs.keep_if { |d| File.exist?(d + "/.rshare") }
9
+ dirs.map { |d| d.gsub(dir, '') }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ require 'tempfile'
2
+
3
+ module RShare
4
+ module RSync
5
+ extend self
6
+
7
+ def push!(config, log = $log, provider = Kernel)
8
+ rsync!(push_command(config), log, provider)
9
+ end
10
+
11
+ def pull!(config, log = $log, provider = Kernel)
12
+ rsync!(pull_command(config), log, provider)
13
+ end
14
+
15
+ private
16
+
17
+ def push_command(config)
18
+ options = config['options'] + %w{--delete --append-verify --recursive}
19
+
20
+ filelist = Tempfile.new("includes")
21
+ filelist.puts Repository.includes(config['local']).join("\n")
22
+ filelist.close
23
+
24
+ options << "--files-from=#{filelist.path}"
25
+ options.uniq!
26
+ rsync_command(config['local'], config['remote'], options)
27
+ end
28
+
29
+ def pull_command(config)
30
+ options = config['options'] + %w{--append-verify --recursive}
31
+ options.delete("--delete")
32
+ rsync_command(config['remote'], config['local'], options)
33
+ end
34
+
35
+ def rsync_command(source, target, options)
36
+ options_string = options.join(' ')
37
+ 'rsync "%s" "%s" %s' % [ source, target, options_string ]
38
+ end
39
+
40
+ def rsync!(command, log, system_provider)
41
+ log.info "Running RSYNC:\n\t#{command}" if log
42
+ system_provider.system(command)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module RShare
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rshare/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Johannes Huning"]
6
+ gem.email = ["johannesh@allnightdrive.com"]
7
+ gem.description =
8
+ "Tiny ruby app allowing for automated file sharing between friends " +
9
+ "using rsync"
10
+ gem.summary =
11
+ "Automate file sharing between friends"
12
+ gem.homepage = "https://github.com/johannesh/rshare"
13
+
14
+ gem.executables =
15
+ `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ gem.name = "rshare"
19
+ gem.require_paths = ["lib"]
20
+ gem.version = RShare::VERSION
21
+
22
+ %w{rspec dusel}.each { |dep|
23
+ gem.add_development_dependency(dep)
24
+ }
25
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + "/spec_helper"
2
+ require 'tempfile'
3
+ require 'json'
4
+
5
+ describe 'RShare::Configuration' do
6
+ describe "#load" do
7
+ it "should throw an exception if given missing file" do
8
+ expect { RShare::Configuration.load('/tmp/rshare') }.to
9
+ raise_exception(Errno::ENOENT)
10
+ end
11
+
12
+ it "should correctly load existing configuration and expand paths" do
13
+ config = {
14
+ 'local' => "~/public/",
15
+ 'remote' => "foo@bar.com:~/public/",
16
+ 'options' => [ "--bwidth=10", "--progress" ]
17
+ }
18
+ local = config['local']
19
+ expanded_config = config.merge({
20
+ 'local' => File.expand_path(local) + (local[-1].eql?('/') ? '/' : '')
21
+ })
22
+ conf_file = Tempfile.open("rshare") { |file|
23
+ JSON.dump(config, file)
24
+ file.close
25
+ file
26
+ }
27
+ RShare::Configuration.load(conf_file.path).should eq(expanded_config)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'RShare::Repository' do
4
+ include RShare::Repository
5
+
6
+ describe "#includes" do
7
+ it "should return [] for missing directories" do
8
+ includes("/foo/bar/baz").should eq([])
9
+ end
10
+
11
+ it "should return [] for directories without .rshare files" do
12
+ Dusel.dir { |d|
13
+ includes(d.path).should eq([])
14
+ d.collapse
15
+ }
16
+ end
17
+
18
+ it "should return directories with .rshare files by relative path" do
19
+ Dusel.dir { |outer|
20
+ outer.dir { |inner|
21
+ inner.file(".rshare")
22
+ includes(outer.path).should eq([inner.path.gsub(outer.path, '')])
23
+ }
24
+ outer.collapse
25
+ }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,101 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'shellwords'
3
+
4
+ describe 'RShare::RSync' do
5
+ include RShare::RSync
6
+
7
+ before :each do
8
+ @config = {
9
+ 'local' => "~/public/",
10
+ 'remote' => "foo@bar.com:~/public/",
11
+ 'options' => [ "--bwidth=10", "--progress" ]
12
+ }
13
+ @kernel = double('Kernel')
14
+ @log = double('Log')
15
+ end
16
+
17
+ describe "#push_command" do
18
+ it "should include the default push options" do
19
+ cmd = push_command(@config)
20
+ %w{--append-verify --recursive --delete}.each { |o|
21
+ cmd.include?(o).should be_true
22
+ }
23
+ end
24
+
25
+ it "should have the local path as the first argument" do
26
+ push_command(@config).shellsplit()[1].should eq(@config['local'])
27
+ end
28
+
29
+ it "should have the remote path as the second argument" do
30
+ push_command(@config).shellsplit()[2].should eq(@config['remote'])
31
+ end
32
+
33
+ it "should add an option to only sync included files" do
34
+ Dusel.dir { |outer|
35
+ outer.dir { |inner|
36
+ inner.file(".rshare")
37
+
38
+ @config['local'] = outer.path
39
+ cmd = push_command(@config)
40
+ cmd.include?("--files-from").should be_true
41
+ /--files-from=([^ ]+)/.match(cmd) { |md|
42
+ File.exist?(md[1]).should be_true
43
+ File.open(md[1], "r") { |f|
44
+ f.read.strip.should eq(RShare::Repository.includes(outer).first)
45
+ }
46
+ }
47
+ }
48
+
49
+ outer.collapse
50
+ }
51
+ end
52
+ end
53
+
54
+ describe "#pull_command" do
55
+ it "should include the default pull options" do
56
+ cmd = pull_command(@config)
57
+ %w{--append-verify --recursive}.each { |o| cmd.include?(o).should be_true }
58
+ end
59
+
60
+ it "should never include the --delete options" do
61
+ @config['options'] << "--delete"
62
+ pull_command(@config).include?("--delete").should be_false
63
+ end
64
+
65
+ it "should have the local path as the second argument" do
66
+ pull_command(@config).shellsplit()[2].should eq(@config['local'])
67
+ end
68
+
69
+ it "should have the remote path as the first argument" do
70
+ pull_command(@config).shellsplit()[1].should eq(@config['remote'])
71
+ end
72
+ end
73
+
74
+ describe "#rsync_command" do
75
+ it "should generate a proper rsync command" do
76
+ source = "source"
77
+ target = "target"
78
+ options = %w{--option --option2}
79
+ rsync_command(source, target, options).should \
80
+ eq('rsync "%s" "%s" %s' % [source, target, options.join(' ')])
81
+ end
82
+
83
+ it "should quote source and target for shell usage" do
84
+ source = target = "~/public/name with spaces"
85
+ quoted_source, quoted_target, *_ =
86
+ rsync_command(source, target, []).shellsplit()[1, 3]
87
+
88
+ quoted_source.should eq(source)
89
+ quoted_target.should eq(target)
90
+ end
91
+ end
92
+
93
+ describe "#rsync!" do
94
+ it "should log the given command with level INFO" do
95
+ command = "command"
96
+ @log.should_receive(:info).with("Running RSYNC:\n\t#{command}")
97
+ @kernel.should_receive(:system).with(command)
98
+ rsync!("command", @log, @kernel)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,10 @@
1
+ require 'rshare'
2
+ require 'dusel'
3
+
4
+ # Requires supporting ruby files with custom matchers and macros, etc,
5
+ # in spec/support/ and its subdirectories.
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_with :rspec
10
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rshare
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Johannes Huning
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-23 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &5126840 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *5126840
25
+ - !ruby/object:Gem::Dependency
26
+ name: dusel
27
+ requirement: &5126570 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *5126570
36
+ description: Tiny ruby app allowing for automated file sharing between friends using
37
+ rsync
38
+ email:
39
+ - johannesh@allnightdrive.com
40
+ executables:
41
+ - rshare
42
+ extensions: []
43
+ extra_rdoc_files: []
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - .rspec
48
+ - .travis.yml
49
+ - Gemfile
50
+ - Guardfile
51
+ - LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - bin/rshare
55
+ - lib/rshare.rb
56
+ - lib/rshare/conf.rb
57
+ - lib/rshare/exec.rb
58
+ - lib/rshare/opts.rb
59
+ - lib/rshare/repo.rb
60
+ - lib/rshare/rsync.rb
61
+ - lib/rshare/version.rb
62
+ - rshare.gemspec
63
+ - spec/conf_spec.rb
64
+ - spec/repo_spec.rb
65
+ - spec/rsync_spec.rb
66
+ - spec/spec_helper.rb
67
+ homepage: https://github.com/johannesh/rshare
68
+ licenses: []
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 1.8.10
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Automate file sharing between friends
91
+ test_files:
92
+ - spec/conf_spec.rb
93
+ - spec/repo_spec.rb
94
+ - spec/rsync_spec.rb
95
+ - spec/spec_helper.rb
96
+ has_rdoc: