photocopier 0.0.4 → 0.0.5

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 CHANGED
@@ -1,2 +1 @@
1
- spec
2
1
  pkg
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@photocopier
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- photocopier (0.0.1)
4
+ photocopier (0.0.4)
5
5
  activesupport
6
6
  escape
7
7
  i18n
@@ -12,18 +12,16 @@ PATH
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
- activesupport (3.2.8)
15
+ activesupport (3.2.9)
16
16
  i18n (~> 0.6)
17
17
  multi_json (~> 1.0)
18
18
  diff-lcs (1.1.3)
19
19
  escape (0.0.4)
20
20
  i18n (0.6.1)
21
- jruby-pageant (1.1.1)
22
- multi_json (1.3.6)
21
+ multi_json (1.4.0)
23
22
  net-scp (1.0.4)
24
23
  net-ssh (>= 1.99.1)
25
- net-ssh (2.6.0)
26
- jruby-pageant (>= 1.1.1)
24
+ net-ssh (2.6.2)
27
25
  net-ssh-gateway (1.1.0)
28
26
  net-ssh (>= 1.99.1)
29
27
  rspec (2.11.0)
data/README.md CHANGED
@@ -47,6 +47,9 @@ ssh.get_directory('remote_dir', './local_dir')
47
47
 
48
48
  # and viceversa
49
49
  ssh.put_directory('./local_dir', 'remote_dir')
50
+
51
+ # execs a command and waits for the result, returns output and exit code
52
+ ssh.exec!('pwd') # => [ "/home/128423/users/.home\n", 0 ]
50
53
  ```
51
54
  The very same commands are valid for the `Photocopier::FTP` adapter.
52
55
 
@@ -1,14 +1,13 @@
1
1
  require 'escape'
2
2
  require 'tempfile'
3
3
  require 'logger'
4
+ require 'active_support/all'
4
5
 
5
6
  module Photocopier
6
7
 
7
8
  class Adapter
8
9
  attr_accessor :logger
9
10
 
10
- def get(remote_path, file_path = nil); end
11
-
12
11
  def put(file_path_or_string, remote_path)
13
12
  if File.exists? file_path_or_string
14
13
  put_file(file_path_or_string, remote_path)
@@ -21,10 +20,13 @@ module Photocopier
21
20
  end
22
21
  end
23
22
 
24
- def delete(remote_path); end
23
+ def put_file(file_path, remote_path); end
24
+ def put_directory(local_path, remote_path, exclude = []); end
25
25
 
26
+ def get(remote_path, file_path = nil); end
26
27
  def get_directory(remote_path, local_path, exclude = []); end
27
- def put_directory(local_path, remote_path, exclude = []); end
28
+
29
+ def delete(remote_path); end
28
30
 
29
31
  protected
30
32
 
@@ -5,7 +5,7 @@ require 'fileutils'
5
5
  module Photocopier
6
6
  class FTP < Adapter
7
7
 
8
- def initialize(options)
8
+ def initialize(options = {})
9
9
  @options = options
10
10
  end
11
11
 
@@ -76,16 +76,5 @@ module Photocopier
76
76
  end
77
77
  mirror
78
78
  end
79
-
80
- def lftp_mirrir_arguments(local, remote, reverse)
81
- arguments = []
82
- if gateway_options.any?
83
- arguments << ssh_command(gateway_options)
84
- end
85
- arguments << ssh_command(options)
86
- arguments.join(" ")
87
- end
88
-
89
79
  end
90
-
91
80
  end
@@ -8,7 +8,7 @@ require 'photocopier/adapter'
8
8
  module Photocopier
9
9
  class SSH < Adapter
10
10
 
11
- def initialize(options)
11
+ def initialize(options = {})
12
12
  @options = options
13
13
  end
14
14
 
@@ -25,7 +25,7 @@ module Photocopier
25
25
  end
26
26
 
27
27
  def delete(remote_path)
28
- session.exec!("rm -rf #{remote_path}")
28
+ exec!("rm -rf #{remote_path}")
29
29
  end
30
30
 
31
31
  def get_directory(remote_path, local_path, exclude = [])
@@ -50,9 +50,21 @@ module Photocopier
50
50
  end
51
51
  end
52
52
 
53
+ def exec!(cmd)
54
+ result = ""
55
+ exit_code = nil
56
+ session.exec!(cmd) do |ch, type, data|
57
+ result << data
58
+ ch.on_request("exit-status") do |ch, data|
59
+ exit_code = data.read_long
60
+ end
61
+ end
62
+ [ result, exit_code ]
63
+ end
64
+
53
65
  private
54
66
 
55
- def rsync(source, destination, exclude)
67
+ def rsync(source, destination, exclude = [])
56
68
  command = [
57
69
  "rsync", "--progress", "-e", rsh_arguments, "--archive", "--compress",
58
70
  "--omit-dir-times", "--delete"
@@ -100,6 +112,5 @@ module Photocopier
100
112
  def gateway_options
101
113
  options[:gateway] || {}
102
114
  end
103
-
104
115
  end
105
116
  end
@@ -1,3 +1,3 @@
1
1
  module Photocopier
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,6 @@
1
+ require 'photocopier/adapter'
2
+ require 'shared_examples_for_adapter'
3
+
4
+ describe Photocopier::Adapter do
5
+ it_behaves_like "a Photocopier adapter"
6
+ end
data/spec/ftp_spec.rb ADDED
@@ -0,0 +1,162 @@
1
+ require 'photocopier/ftp'
2
+ require 'shared_examples_for_adapter'
3
+
4
+ describe Photocopier::FTP do
5
+ it_behaves_like "a Photocopier adapter"
6
+
7
+ let(:ftp) { Photocopier::FTP.new(options) }
8
+ let(:options) do { :host => "host", :user => "user", :password => "password" } end
9
+
10
+ context "#session" do
11
+ it "retrieves an FTP session" do
12
+ Net::FTP.should_receive(:open).with("host", "user", "password")
13
+ ftp.session
14
+ end
15
+
16
+ context "passive mode" do
17
+ let(:options) do { :host => "host", :passive => true } end
18
+
19
+ it "should enable passive mode" do
20
+ Net::FTP.stub(:open).and_return(stub.as_null_object)
21
+ ftp.session.should be_passive
22
+ end
23
+ end
24
+ end
25
+
26
+ context "#remote_ftp_url" do
27
+ let(:options) do { :host => "host" } end
28
+
29
+ it "should build an ftp url" do
30
+ ftp.send(:remote_ftp_url).should == "ftp://host"
31
+ end
32
+
33
+ context "given an username" do
34
+ let(:options) do { :host => "host", :user => "user" } end
35
+
36
+ it "should add it to the url" do
37
+ ftp.send(:remote_ftp_url).should == "ftp://user@host"
38
+ end
39
+
40
+ context "given a password" do
41
+ let(:options) do { :host => "host", :user => "user", :password => "password" } end
42
+
43
+ it "should add it to the url" do
44
+ ftp.send(:remote_ftp_url).should == "ftp://user:password@host"
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ context "#lftp_mirror_arguments" do
51
+ let(:lftp_arguments) do
52
+ %w(
53
+ mirror
54
+ --delete
55
+ --use-cache
56
+ --verbose
57
+ --allow-chown
58
+ --allow-suid
59
+ --no-umask
60
+ --parallel=2
61
+ )
62
+ end
63
+
64
+ it "should build arguments for lftp" do
65
+ ftp.send(:lftp_mirror_arguments, false, []).should == lftp_arguments.join(" ")
66
+ end
67
+
68
+ it "should build args for reverse mirroring" do
69
+ lftp_arguments << "--reverse"
70
+ ftp.send(:lftp_mirror_arguments, true, []).should == lftp_arguments.join(" ")
71
+ end
72
+
73
+ it "should exclude files" do
74
+ lftp_arguments << "--exclude-glob .git"
75
+ ftp.send(:lftp_mirror_arguments, false, [".git"]).should == lftp_arguments.join(" ")
76
+ end
77
+ end
78
+
79
+ context "#lftp" do
80
+
81
+ let(:remote) { "remote" }
82
+ let(:local) { "local" }
83
+
84
+ before(:each) do
85
+ ftp.stub(:remote_ftp_url).and_return("remote_ftp_url")
86
+ ftp.stub(:lftp_mirror_arguments).and_return("lftp_mirror_arguments")
87
+ end
88
+
89
+ let(:lftp_command) do
90
+ [
91
+ "lftp",
92
+ "-c",
93
+ [
94
+ "set ftp:list-options -a",
95
+ "open remote_ftp_url",
96
+ "mkdir -p #{remote}",
97
+ "cd #{remote}",
98
+ "lcd #{local}",
99
+ "lftp_mirror_arguments"
100
+ ].join("; ")
101
+ ]
102
+ end
103
+
104
+ it "should build a lftp command" do
105
+ ftp.should_receive(:run).with(*lftp_command)
106
+ ftp.send(:lftp, local, remote, false, [])
107
+ end
108
+ end
109
+
110
+ context "adapter interface" do
111
+
112
+ let(:remote_path) { stub }
113
+ let(:local_path) { stub }
114
+ let(:file_path) { stub }
115
+ let(:session) { stub }
116
+
117
+ before(:each) do
118
+ ftp.stub(:session).and_return(session)
119
+ end
120
+
121
+ context "#get" do
122
+ it "should get a remote path" do
123
+ session.should_receive(:get).with(remote_path, file_path)
124
+ ftp.get(remote_path, file_path)
125
+ end
126
+ end
127
+
128
+ context "#put_file" do
129
+ it "should send a file to remote" do
130
+ session.should_receive(:put).with(file_path, remote_path)
131
+ ftp.put_file(file_path, remote_path)
132
+ end
133
+ end
134
+
135
+ context "#delete" do
136
+ it "should delete a remote path" do
137
+ session.should_receive(:delete).with(remote_path)
138
+ ftp.delete(remote_path)
139
+ end
140
+ end
141
+
142
+ context "directories management" do
143
+ let(:remote_path) { "remote_path" }
144
+ let(:exclude_list) { [] }
145
+
146
+ context "#get_directory" do
147
+ it "should get a remote directory" do
148
+ FileUtils.should_receive(:mkdir_p).with(local_path)
149
+ ftp.should_receive(:lftp).with(local_path, remote_path, false, exclude_list)
150
+ ftp.get_directory(remote_path, local_path, exclude_list)
151
+ end
152
+ end
153
+
154
+ context "#put_directory" do
155
+ it "should send a directory to remote" do
156
+ ftp.should_receive(:lftp).with(local_path, remote_path, true, exclude_list)
157
+ ftp.put_directory(local_path, remote_path, exclude_list)
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,71 @@
1
+ require 'photocopier/adapter'
2
+
3
+ shared_examples_for "a Photocopier adapter" do
4
+ let(:adapter) { described_class.new }
5
+
6
+ context "adapter interface" do
7
+ it "should expose all adapter methods" do
8
+ [
9
+ :put,
10
+ :put_file,
11
+ :put_directory,
12
+ :get,
13
+ :get_directory,
14
+ :delete
15
+ ].each do |sym|
16
+ adapter.should respond_to(sym)
17
+ end
18
+ end
19
+ end
20
+
21
+ context "#put" do
22
+ let(:file_path) { Tempfile.new("tmp").path }
23
+ let(:remote_path) { stub }
24
+
25
+ context "given a real file path" do
26
+ it "should put a file" do
27
+ adapter.should_receive(:put_file).with(file_path, remote_path)
28
+ adapter.put(file_path, remote_path)
29
+ end
30
+ end
31
+
32
+ context "given a string" do
33
+ let(:string) { "foobar" }
34
+ let(:file) { stub(:path => "path") }
35
+
36
+ it "should write it to file, put it and remove the file" do
37
+ Tempfile.stub(:new).and_return(file)
38
+
39
+ file.should_receive(:write).with(string)
40
+ file.should_receive(:close)
41
+ adapter.should_receive(:put_file).with("path", remote_path)
42
+ file.should_receive(:unlink)
43
+
44
+ adapter.put(string, remote_path)
45
+ end
46
+ end
47
+
48
+ context "#run" do
49
+ let(:arg) { stub }
50
+ let(:command) { stub }
51
+ before(:each) {
52
+ Escape.stub(:shell_command).with([arg]).and_return(command)
53
+ }
54
+
55
+ it "should delegate to Kernel system" do
56
+ adapter.should_receive(:system).with(command)
57
+ adapter.send(:run, arg)
58
+ end
59
+
60
+ context "given a logger" do
61
+ let(:logger) { stub }
62
+
63
+ it "should send the command to the logger" do
64
+ adapter.logger = logger
65
+ logger.should_receive(:info).with(command)
66
+ adapter.send(:run, arg)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
data/spec/ssh_spec.rb ADDED
@@ -0,0 +1,178 @@
1
+ require 'photocopier/ssh'
2
+ require 'shared_examples_for_adapter'
3
+
4
+ describe Photocopier::SSH do
5
+ it_behaves_like "a Photocopier adapter"
6
+
7
+ let(:ssh) { Photocopier::SSH.new(options) }
8
+ let(:options) do { :host => "host", :user => "user" } end
9
+ let(:gateway_config) do { :host => "gate_host", :user => "gate_user" } end
10
+ let(:options_with_gateway) do
11
+ {
12
+ :host => "host",
13
+ :user => "user",
14
+ :gateway => gateway_config
15
+ }
16
+ end
17
+
18
+ context "#session" do
19
+ it "retrieves an SSH session" do
20
+ Net::SSH.should_receive(:start).with("host", "user", {})
21
+ ssh.session
22
+ end
23
+
24
+ context "given a gateway " do
25
+ let(:options) { options_with_gateway }
26
+ let(:gateway) { stub }
27
+
28
+ it "goes through it to retrieve a session" do
29
+ Net::SSH::Gateway.stub(:new).with("gate_host", "gate_user", {}).and_return(gateway)
30
+ gateway.should_receive(:ssh).with("host", "user", {})
31
+ ssh.session
32
+ end
33
+ end
34
+ end
35
+
36
+ context "#ssh_command" do
37
+ let(:options) do { :host => "host" } end
38
+
39
+ it "should build an ssh command" do
40
+ ssh.send(:ssh_command, options).should == "ssh host"
41
+ end
42
+
43
+ context "given a port" do
44
+ let(:options) do { :host => "host", :port => "port" } end
45
+ it "should be added to the command" do
46
+ ssh.send(:ssh_command, options).should == "ssh -p port host"
47
+ end
48
+ end
49
+
50
+ context "given a user" do
51
+ let(:options) do { :host => "host", :user => "user" } end
52
+ it "should be added to the command" do
53
+ ssh.send(:ssh_command, options).should == "ssh user@host"
54
+ end
55
+ end
56
+
57
+ context "given a password" do
58
+ let(:options) do { :host => "host", :password => "password" } end
59
+
60
+ context "sshpass is enabled" do
61
+ it "should be added to the command" do
62
+ ssh.send(:ssh_command, options, true).should == "sshpass -p password ssh host"
63
+ end
64
+ end
65
+
66
+ context "sshpass is disabled" do
67
+ it "should not change the command" do
68
+ ssh.send(:ssh_command, options, false).should == "ssh host"
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ context "#rsh_arguments" do
75
+ it "should build arguments for rsync" do
76
+ ssh.should_receive(:ssh_command).with(options, anything)
77
+ ssh.send(:rsh_arguments)
78
+ end
79
+
80
+ context "given a gateway" do
81
+ let(:options) { options_with_gateway }
82
+ it "should include gateway options" do
83
+ ssh.should_receive(:ssh_command).with(gateway_config, anything)
84
+ ssh.should_receive(:ssh_command).with(options, anything)
85
+ ssh.send(:rsh_arguments)
86
+ end
87
+ end
88
+ end
89
+
90
+ context "#rsync" do
91
+ before(:each) do
92
+ ssh.stub(:rsh_arguments).and_return("rsh_arguments")
93
+ end
94
+
95
+ let(:rsync_command) {
96
+ %w(
97
+ rsync
98
+ --progress
99
+ -e
100
+ rsh_arguments
101
+ --archive
102
+ --compress
103
+ --omit-dir-times
104
+ --delete
105
+ )
106
+ }
107
+
108
+ it "should build an rsync command" do
109
+ rsync_command << "source/" << "destination"
110
+ ssh.should_receive(:run).with(*rsync_command)
111
+ ssh.send(:rsync, "source", "destination")
112
+ end
113
+
114
+ context "given an exclude list" do
115
+ it "should skip excluded paths" do
116
+ rsync_command << "--exclude" << ".git"
117
+ rsync_command << "source/" << "destination"
118
+ ssh.should_receive(:run).with(*rsync_command)
119
+ ssh.send(:rsync, "source", "destination", [".git"])
120
+ end
121
+ end
122
+ end
123
+
124
+ context "adapter interface" do
125
+
126
+ let(:remote_path) { stub }
127
+ let(:local_path) { stub }
128
+ let(:file_path) { stub }
129
+ let(:scp) { stub }
130
+ let(:session) { stub(:scp => scp) }
131
+
132
+ before(:each) do
133
+ ssh.stub(:session).and_return(session)
134
+ end
135
+
136
+ context "#get" do
137
+ it "should get a remote path" do
138
+ scp.should_receive(:download!).with(remote_path, file_path)
139
+ ssh.get(remote_path, file_path)
140
+ end
141
+ end
142
+
143
+ context "#put_file" do
144
+ it "should send a file to remote" do
145
+ scp.should_receive(:upload!).with(file_path, remote_path)
146
+ ssh.put_file(file_path, remote_path)
147
+ end
148
+ end
149
+
150
+ context "#delete" do
151
+ it "should delete a remote path" do
152
+ ssh.should_receive(:exec!).with("rm -rf foo")
153
+ ssh.delete("foo")
154
+ end
155
+ end
156
+
157
+ context "directories management" do
158
+ let(:remote_path) { "remote_path" }
159
+ let(:exclude_list) { [] }
160
+
161
+ context "#get_directory" do
162
+ it "should get a remote directory" do
163
+ FileUtils.should_receive(:mkdir_p).with(local_path)
164
+ ssh.should_receive(:rsync).with(":remote_path", local_path, exclude_list)
165
+ ssh.get_directory(remote_path, local_path, exclude_list)
166
+ end
167
+ end
168
+
169
+ context "#put_directory" do
170
+ it "should send a directory to remote" do
171
+ ssh.should_receive(:rsync).with(local_path, ":remote_path", exclude_list)
172
+ ssh.put_directory(local_path, remote_path, exclude_list)
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: photocopier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-17 00:00:00.000000000 Z
12
+ date: 2013-03-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -132,6 +132,7 @@ extensions: []
132
132
  extra_rdoc_files: []
133
133
  files:
134
134
  - .gitignore
135
+ - .rvmrc
135
136
  - Gemfile
136
137
  - Gemfile.lock
137
138
  - LICENSE
@@ -143,6 +144,10 @@ files:
143
144
  - lib/photocopier/ssh.rb
144
145
  - lib/photocopier/version.rb
145
146
  - photocopier.gemspec
147
+ - spec/adapter_spec.rb
148
+ - spec/ftp_spec.rb
149
+ - spec/shared_examples_for_adapter.rb
150
+ - spec/ssh_spec.rb
146
151
  homepage: https://github.com/stefanoverna/photocopier
147
152
  licenses: []
148
153
  post_install_message:
@@ -168,4 +173,9 @@ signing_key:
168
173
  specification_version: 3
169
174
  summary: Photocopier provides FTP/SSH adapters to abstract away file and directory
170
175
  copying.
171
- test_files: []
176
+ test_files:
177
+ - spec/adapter_spec.rb
178
+ - spec/ftp_spec.rb
179
+ - spec/shared_examples_for_adapter.rb
180
+ - spec/ssh_spec.rb
181
+ has_rdoc: