photocopier 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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: