rssh 0.0.1

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.
@@ -0,0 +1,10 @@
1
+ require 'rake_ext'
2
+
3
+ project(
4
+ name: "rssh",
5
+ version: "0.0.1",
6
+ summary: "ruby ssh and io",
7
+
8
+ author: "Alexey Petrushin",
9
+ homepage: "http://github.com/alexeypetrushin/rssh"
10
+ )
@@ -0,0 +1,11 @@
1
+ class Ssh
2
+ attr_reader :config
3
+
4
+
5
+ def setstat(path, options)
6
+ remote do |sh, sftp|
7
+ sftp.setstat! path, options
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,18 @@
1
+ raise 'ruby 1.9.2 or higher required!' if RUBY_VERSION < '1.9.2'
2
+
3
+ require 'rsh/gems'
4
+
5
+ require 'open3'
6
+
7
+ require 'net/ssh'
8
+ require 'net/sftp'
9
+
10
+ %w(
11
+ support
12
+
13
+ drivers/abstract
14
+ drivers/local
15
+ drivers/ssh
16
+
17
+ box
18
+ ).each{|f| require "rsh/#{f}"}
@@ -0,0 +1,145 @@
1
+ module Rsh
2
+ class Box
3
+ attr_accessor :options
4
+
5
+ def initialize options = {}
6
+ @options = options
7
+ options[:host] ||= 'localhost'
8
+ end
9
+
10
+ def driver
11
+ unless @driver
12
+ klass = options[:host] == 'localhost' ? Drivers::Local : Drivers::Ssh
13
+ @driver = klass.new options
14
+ end
15
+ @driver
16
+ end
17
+
18
+ def local_driver
19
+ @local_driver ||= Drivers::Local.new
20
+ end
21
+
22
+ def bulk &b
23
+ driver.bulk &b
24
+ end
25
+
26
+ def upload_file from_local_path, to_remote_path, options = {}
27
+ bulk do
28
+ raise "file '#{from_local_path}' not exists!" unless local_driver.file_exist? from_local_path
29
+ if driver.file_exist?(to_remote_path)
30
+ if options[:override]
31
+ driver.remove_file to_remote_path
32
+ else
33
+ raise "file '#{to_remote_path}' already exists!"
34
+ end
35
+ end
36
+ driver.upload_file from_local_path, to_remote_path
37
+ end
38
+ end
39
+
40
+ def download_file from_remote_path, to_local_path, options = {}
41
+ bulk do
42
+ raise "file #{from_remote_path} not exists!" unless driver.file_exist?(from_remote_path)
43
+ if local_driver.file_exist? to_local_path
44
+ if options[:override]
45
+ local_driver.remove_file to_local_path
46
+ else
47
+ raise "file #{to_local_path} already exists!"
48
+ end
49
+ end
50
+ driver.download_file from_remote_path, to_local_path
51
+ end
52
+ end
53
+
54
+ def remove_file remote_file_path
55
+ bulk do
56
+ raise "file #{remote_file_path} not exists!" unless driver.file_exist? remote_file_path
57
+ driver.remove_file remote_file_path
58
+ end
59
+ end
60
+
61
+ def exist? remote_path
62
+ driver.exist? remote_path
63
+ end
64
+
65
+ def file_exist? remote_file_path
66
+ driver.file_exist? remote_file_path
67
+ end
68
+
69
+ def remove_directory remote_directory_path
70
+ bulk do
71
+ raise "directory #{remote_directory_path} not exists!" unless driver.directory_exist? remote_directory_path
72
+ driver.remove_directory remote_directory_path
73
+ end
74
+ end
75
+
76
+ def create_directory remote_path, options = {}
77
+ bulk do
78
+ if driver.directory_exist?(remote_path)
79
+ if options[:override]
80
+ driver.remove_directory remote_path
81
+ else
82
+ raise "directory '#{remote_path}' already exists!"
83
+ end
84
+ end
85
+ driver.create_directory remote_path
86
+ end
87
+ end
88
+
89
+ def directory_exist? remote_path
90
+ driver.directory_exist? remote_path
91
+ end
92
+
93
+ def upload_directory from_local_path, to_remote_path, options = {}
94
+ bulk do
95
+ raise "directory '#{from_local_path}' not exists!" unless local_driver.directory_exist? from_local_path
96
+ if driver.directory_exist?(to_remote_path)
97
+ if options[:override]
98
+ driver.remove_directory to_remote_path
99
+ else
100
+ raise "directory '#{to_remote_path}' already exists!"
101
+ end
102
+ end
103
+ driver.upload_directory from_local_path, to_remote_path
104
+ end
105
+ end
106
+
107
+ def download_directory from_remote_path, to_local_path, options = {}
108
+ bulk do
109
+ raise "directory #{from_remote_path} not exists!" unless driver.directory_exist?(from_remote_path)
110
+ if local_driver.directory_exist? to_local_path
111
+ if options[:override]
112
+ local_driver.remove_directory to_local_path
113
+ else
114
+ raise "directory #{to_local_path} already exists!"
115
+ end
116
+ end
117
+ driver.download_directory from_remote_path, to_local_path
118
+ end
119
+ end
120
+
121
+ def with_tmp_dir &block
122
+ bulk do
123
+ tmp_dir = driver.generate_tmp_dir_name
124
+ begin
125
+ remove_directory tmp_dir if directory_exist? tmp_dir
126
+ create_directory tmp_dir
127
+ block.call
128
+ ensure
129
+ remove_directory tmp_dir if directory_exist? tmp_dir
130
+ end
131
+ end
132
+ end
133
+
134
+ def bash cmd
135
+ code, stdout, stderr = driver.exec cmd
136
+ raise "can't execute '#{cmd}'!" unless code == 0
137
+ return stdout, stderr
138
+ end
139
+
140
+ protected
141
+ def method_missing m, *a, &b
142
+ driver.send m, *a, &b
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,19 @@
1
+ module Rsh
2
+ module Drivers
3
+ class Abstract
4
+ attr_reader :options
5
+
6
+ def initialize options = {}
7
+ @options = options
8
+ end
9
+
10
+ def bulk &b
11
+ b.call
12
+ end
13
+
14
+ def generate_tmp_dir_name
15
+ "/tmp/ssh_tmp_dir_#{rand(10**6)}"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ module Rsh
2
+ module Drivers
3
+ class Local < Abstract
4
+ def bulk &b
5
+ b.call
6
+ end
7
+
8
+ def upload_file from_local_path, to_remote_path
9
+ FileUtils.copy from_local_path, to_remote_path
10
+ end
11
+
12
+ def download_file from_remote_path, to_local_path
13
+ FileUtils.copy from_remote_path, to_local_path
14
+ end
15
+
16
+ def exist? remote_file_path
17
+ File.exist? remote_file_path
18
+ end
19
+
20
+ alias_method :directory_exist?, :exist?
21
+ alias_method :file_exist?, :exist?
22
+
23
+ def remove_file remote_file_path
24
+ File.delete remote_file_path
25
+ end
26
+
27
+ def create_directory path
28
+ Dir.mkdir path
29
+ end
30
+
31
+ def remove_directory path
32
+ FileUtils.rm_r path
33
+ end
34
+
35
+ def upload_directory from_local_path, to_remote_path
36
+ FileUtils.cp_r from_local_path, to_remote_path
37
+ end
38
+
39
+ def download_directory from_remote_path, to_local_path
40
+ FileUtils.cp_r from_remote_path, to_local_path
41
+ end
42
+
43
+ def exec command
44
+ code, stdout, stderr = Open3.popen3 command do |stdin, stdout, stderr, waitth|
45
+ [waitth.value.to_i, stdout.read, stderr.read]
46
+ end
47
+
48
+ return code, stdout, stderr
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,145 @@
1
+ module Rsh
2
+ module Drivers
3
+ class Ssh < Abstract
4
+ def initialize options = {}
5
+ super
6
+ raise "ssh options not provided!" unless options[:ssh]
7
+ raise "invalid ssh options!" unless options[:ssh].is_a?(Hash)
8
+ end
9
+
10
+ def upload_file from_local_path, to_remote_path
11
+ remote do |ssh, sftp|
12
+ sftp.upload! from_local_path, to_remote_path
13
+ end
14
+ end
15
+
16
+ def download_file from_remote_path, to_local_path
17
+ File.open to_local_path, "w" do |out|
18
+ remote do |ssh, sftp|
19
+ sftp.download! from_remote_path, out #, :recursive => true
20
+ end
21
+ end
22
+ end
23
+
24
+ def exist? remote_file_path
25
+ remote do |ssh, sftp|
26
+ begin
27
+ fattrs = sftp.stat! remote_file_path
28
+ fattrs.directory? or fattrs.file? or fattrs.symlink?
29
+ rescue Net::SFTP::StatusException
30
+ false
31
+ end
32
+ end
33
+ end
34
+ alias_method :directory_exist?, :exist?
35
+ alias_method :file_exist?, :exist?
36
+
37
+ def remove_file remote_file_path
38
+ remote do |ssh, sftp|
39
+ sftp.remove! remote_file_path
40
+ end
41
+ end
42
+
43
+ def exec command
44
+ remote do |ssh, sftp|
45
+ # somehow net-ssh doesn't executes ~/.profile, so we need to execute it manually
46
+ # command = ". ~/.profile && #{command}"
47
+
48
+ stdout, stderr, code, signal = hacked_exec! ssh, command
49
+
50
+ return code, stdout, stderr
51
+ end
52
+ end
53
+
54
+ def open_connection
55
+ ssh_options = self.options[:ssh].clone
56
+ host = options[:host] || raise('host not provided!')
57
+ user = ssh_options.delete(:user) || raise('user not provied!')
58
+ @ssh = Net::SSH.start(host, user, ssh_options)
59
+ @sftp = @ssh.sftp.connect
60
+ end
61
+
62
+ def close_connection
63
+ @ssh.close
64
+ # @sftp.close not needed
65
+ @ssh, @sftp = nil
66
+ end
67
+
68
+ def bulk &block
69
+ remote &block
70
+ end
71
+
72
+ def create_directory path
73
+ remote do |ssh, sftp|
74
+ sftp.mkdir! path
75
+ # exec "mkdir #{path}"
76
+ end
77
+ end
78
+
79
+ def remove_directory path
80
+ exec "rm -r #{path}"
81
+ end
82
+
83
+ def upload_directory from_local_path, to_remote_path
84
+ remote do |ssh, sftp|
85
+ sftp.upload! from_local_path, to_remote_path
86
+ end
87
+ end
88
+
89
+ def download_directory from_remote_path, to_local_path
90
+ remote do |ssh, sftp|
91
+ sftp.download! from_remote_path, to_local_path, :recursive => true
92
+ end
93
+ end
94
+
95
+ protected
96
+ # taken from here http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library/3386375#3386375
97
+ def hacked_exec!(ssh, command, &block)
98
+ stdout_data = ""
99
+ stderr_data = ""
100
+ exit_code = nil
101
+ exit_signal = nil
102
+
103
+ channel = ssh.open_channel do |channel|
104
+ channel.exec(command) do |ch, success|
105
+ raise "could not execute command: #{command.inspect}" unless success
106
+
107
+ channel.on_data{|ch2, data| stdout_data << data}
108
+ channel.on_extended_data{|ch2, type, data| stderr_data << data}
109
+ channel.on_request("exit-status"){|ch,data| exit_code = data.read_long}
110
+ channel.on_request("exit-signal"){|ch, data| exit_signal = data.read_long}
111
+ end
112
+ end
113
+
114
+ channel.wait
115
+
116
+ [stdout_data, stderr_data, exit_code, exit_signal]
117
+ end
118
+
119
+ def remote(&block)
120
+ if @ssh
121
+ block.call @ssh, @sftp
122
+ else
123
+ # Rails.logger.info "Connecting to remote Hadoop #{options[:user]}@#{options[:host]}"
124
+ begin
125
+ open_connection
126
+ block.call @ssh, @sftp
127
+ ensure
128
+ close_connection
129
+ end
130
+
131
+ # Net::SSH.start options[:host], options[:user], :config => true do |ssh|
132
+ # ssh.sftp.connect do |sftp|
133
+ # begin
134
+ # @ssh, @sftp = ssh, sftp
135
+ # block.call @ssh, @sftp
136
+ # ensure
137
+ # @ssh, @sftp = nil
138
+ # end
139
+ # end
140
+ # end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,2 @@
1
+ gem 'net-ssh'
2
+ gem 'net-sftp'
@@ -0,0 +1,30 @@
1
+ require 'fileutils'
2
+ require 'net/ssh'
3
+ require 'net/sftp'
4
+
5
+ # class File
6
+ # class << self
7
+ # def ensure_dir_exist directory, options = {}
8
+ # options = options.symbolize_keys
9
+ # clean = options.delete :clean
10
+ # raise "unsupported options #{options.keys}!" unless options.empty?
11
+ #
12
+ # FileUtils.rm_r directory, :force => true if clean and File.exist?(directory)
13
+ # FileUtils.mkdir_p directory
14
+ # end
15
+ #
16
+ # def copy_dir_content from, to
17
+ # Dir.glob "#{from}/*" do |item|
18
+ # FileUtils.cp_r item, to
19
+ # end
20
+ # end
21
+ #
22
+ # def copy_dir from, to
23
+ # FileUtils.cp_r from, to
24
+ # end
25
+ #
26
+ # def remove_dir dir
27
+ # FileUtils.rm_r dir
28
+ # end
29
+ # end
30
+ # end
@@ -0,0 +1,4 @@
1
+ TODO
2
+ - introduce Entity/Dir/File (the same as in Rush)
3
+ - allow to move files between any Boxes, not only between local and remote.
4
+ - add support for moving dirs.
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'abstract driver' do
4
+ dir = "#{File.dirname __FILE__}/abstract_driver"
5
+ with_tmp_spec_dir dir, before: :each
6
+
7
+ describe "io" do
8
+ before :each do
9
+ @local_dir = spec_dir
10
+ @remote_dir = @driver.generate_tmp_dir_name
11
+
12
+ @driver.remove_directory @remote_dir if @driver.directory_exist? @remote_dir
13
+ @driver.create_directory @remote_dir
14
+ end
15
+
16
+ after :each do
17
+ @driver.remove_directory @remote_dir if @driver.directory_exist? @remote_dir
18
+ end
19
+
20
+ describe "files" do
21
+ before :each do
22
+ @local_file = "#{@local_dir}/local_file"
23
+ @check_file = "#{@local_dir}/check_file"
24
+ @remote_file = "#{@remote_dir}/remote_file"
25
+ end
26
+
27
+ it "file_exist?" do
28
+ @driver.file_exist?(@remote_file).should be_false
29
+ @driver.upload_file(@local_file, @remote_file)
30
+ @driver.file_exist?(@remote_file).should be_true
31
+ end
32
+
33
+ it "upload & download file" do
34
+ @driver.upload_file(@local_file, @remote_file)
35
+ @driver.file_exist?(@remote_file).should be_true
36
+
37
+ @driver.download_file(@remote_file, @check_file)
38
+ File.read(@local_file).should == File.read(@check_file)
39
+ end
40
+
41
+ it "remove_file" do
42
+ @driver.upload_file(@local_file, @remote_file)
43
+ @driver.file_exist?(@remote_file).should be_true
44
+ @driver.remove_file(@remote_file)
45
+ @driver.file_exist?(@remote_file).should be_false
46
+ end
47
+ end
48
+
49
+ describe 'directories' do
50
+ before :each do
51
+ @from_local, @remote_path, @to_local = "#{@local_dir}/dir", "#{@remote_dir}/upload", "#{@local_dir}/download"
52
+ end
53
+
54
+ it "directory_exist?, create_directory, remove_directory" do
55
+ dir = "#{@remote_dir}/some_dir"
56
+ @driver.directory_exist?(dir).should be_false
57
+ @driver.create_directory(dir)
58
+ @driver.directory_exist?(dir).should be_true
59
+ @driver.remove_directory(dir)
60
+ @driver.directory_exist?(dir).should be_false
61
+ end
62
+
63
+ it "upload_directory & download_directory" do
64
+ upload_path_check = "#{@remote_path}/dir2/file"
65
+ @driver.file_exist?(upload_path_check).should be_false
66
+ @driver.upload_directory(@from_local, @remote_path)
67
+ @driver.file_exist?(upload_path_check).should be_true
68
+
69
+ download_path_check = "#{@to_local}/dir2/file"
70
+ File.exist?(download_path_check).should be_false
71
+ @driver.download_directory(@remote_path, @to_local)
72
+ File.exist?(download_path_check).should be_true
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "shell" do
78
+ it 'exec' do
79
+ @driver.exec("echo 'ok'").should == [0, "ok\n", ""]
80
+ end
81
+ end
82
+ end
File without changes
@@ -0,0 +1 @@
1
+ some content
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rsh::Box do
4
+ with_tmp_spec_dir before: :each
5
+
6
+ before :each do
7
+ @box = Rsh::Box.new
8
+
9
+ @local_dir = spec_dir
10
+ @remote_dir = @box.generate_tmp_dir_name
11
+
12
+ @box.remove_directory @remote_dir if @box.directory_exist? @remote_dir
13
+ @box.create_directory @remote_dir
14
+ end
15
+
16
+ after :each do
17
+ @box.remove_directory @remote_dir if @box.directory_exist? @remote_dir
18
+ end
19
+
20
+ describe "io" do
21
+ describe "files" do
22
+ before :each do
23
+ @local_file = "#{@local_dir}/local_file"
24
+ @check_file = "#{@local_dir}/check_file"
25
+ @remote_file = "#{@remote_dir}/remote_file"
26
+ end
27
+
28
+ it "file_exist?" do
29
+ @box.file_exist?(@remote_file).should be_false
30
+ @box.upload_file(@local_file, @remote_file)
31
+ @box.file_exist?(@remote_file).should be_true
32
+ end
33
+
34
+ it "upload_file" do
35
+ @box.upload_file(@local_file, @remote_file)
36
+ @box.file_exist?(@remote_file).should be_true
37
+
38
+ lambda{@box.upload_file(@local_file, @remote_file)}.should raise_error(/exists/)
39
+
40
+ # upload with override
41
+ @box.upload_file(@local_file, @remote_file, override: true)
42
+ @box.file_exist?(@remote_file).should be_true
43
+ end
44
+
45
+ it "download_file" do
46
+ lambda{@box.download_file(@remote_file, @check_file)}.should raise_error(/not exists/)
47
+ @box.upload_file(@local_file, @remote_file)
48
+ @box.download_file(@remote_file, @check_file)
49
+ File.read(@local_file).should == File.read(@check_file)
50
+ end
51
+
52
+ it "remove_file" do
53
+ lambda{@box.remove_file(@remote_file)}.should raise_error(/not exists/)
54
+ @box.upload_file(@local_file, @remote_file)
55
+ @box.file_exist?(@remote_file).should be_true
56
+ @box.remove_file(@remote_file)
57
+ @box.file_exist?(@remote_file).should be_false
58
+ end
59
+ end
60
+
61
+ describe 'directories' do
62
+ before :each do
63
+ @from_local, @remote_path, @to_local = "#{@local_dir}/dir", "#{@remote_dir}/upload", "#{@local_dir}/download"
64
+ end
65
+
66
+ it "directory_exist?" do
67
+ @box.file_exist?(@remote_path).should be_false
68
+ @box.upload_directory(@from_local, @remote_path)
69
+ @box.file_exist?(@remote_path).should be_true
70
+ end
71
+
72
+ it "upload_directory" do
73
+ @box.upload_directory(@from_local, @remote_path)
74
+ @box.directory_exist?(@remote_path).should be_true
75
+
76
+ lambda{@box.upload_directory(@from_local, @remote_path)}.should raise_error(/exists/)
77
+
78
+ # upload with override
79
+ @box.upload_directory(@from_local, @remote_path, override: true)
80
+ @box.directory_exist?(@remote_path).should be_true
81
+ end
82
+
83
+ it "download_directory" do
84
+ lambda{@box.download_directory(@remote_path, @to_local)}.should raise_error(/not exists/)
85
+ @box.upload_directory(@from_local, @remote_path)
86
+ @box.download_directory(@remote_path, @to_local)
87
+ File.exist?("#{@to_local}/dir2/file").should be_true
88
+ end
89
+
90
+ it "remove_directory" do
91
+ lambda{@box.remove_directory(@remote_path)}.should raise_error(/not exists/)
92
+ @box.upload_directory(@from_local, @remote_path)
93
+ @box.directory_exist?(@remote_path).should be_true
94
+ @box.remove_directory(@remote_path)
95
+ @box.directory_exist?(@remote_path).should be_false
96
+ end
97
+ end
98
+ end
99
+
100
+ describe "shell" do
101
+ it 'exec' do
102
+ @box.bash("echo 'ok'").should == ["ok\n", ""]
103
+ end
104
+ end
105
+ end
File without changes
@@ -0,0 +1 @@
1
+ some content
@@ -0,0 +1,4 @@
1
+ :ssh:
2
+ :host: some_host.com
3
+ :user: some_user
4
+ :password: some_password
@@ -0,0 +1,5 @@
1
+ :remote_driver:
2
+ :host: universal.4ire.net
3
+ :ssh:
4
+ :user: root
5
+ :password: privateprivate
@@ -0,0 +1,9 @@
1
+ require 'abstract_driver'
2
+
3
+ describe Rsh::Drivers::Local do
4
+ it_should_behave_like "abstract driver"
5
+
6
+ before :each do
7
+ @driver = Rsh::Drivers::Local.new
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ require 'rspec_ext'
2
+ require 'rsh'
3
+
4
+ rspec do
5
+ def config
6
+ dir = File.dirname __FILE__
7
+ config_file_path = "#{dir}/config.yml"
8
+ raise "no config '#{config_file_path}'!" unless File.exist? config_file_path
9
+ @config ||= YAML.load_file config_file_path
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ require 'abstract_driver'
2
+ require 'yaml'
3
+
4
+ describe Rsh::Drivers::Ssh do
5
+ it_should_behave_like "abstract driver"
6
+
7
+ before :each do
8
+ @driver = Rsh::Drivers::Ssh.new config[:remote_driver]
9
+ @driver.open_connection
10
+ end
11
+
12
+ after :each do
13
+ @driver.close_connection
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rssh
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Alexey Petrushin
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-30 00:00:00 +03:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email:
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - Rakefile
31
+ - readme.md
32
+ - lib/old/ssh.rb
33
+ - lib/rsh/box.rb
34
+ - lib/rsh/drivers/abstract.rb
35
+ - lib/rsh/drivers/local.rb
36
+ - lib/rsh/drivers/ssh.rb
37
+ - lib/rsh/gems.rb
38
+ - lib/rsh/support.rb
39
+ - lib/rsh.rb
40
+ - spec/abstract_driver/dir/dir2/file
41
+ - spec/abstract_driver/local_file
42
+ - spec/abstract_driver.rb
43
+ - spec/box_spec/dir/dir2/file
44
+ - spec/box_spec/local_file
45
+ - spec/box_spec.rb
46
+ - spec/config.example.yml
47
+ - spec/config.yml
48
+ - spec/local_driver_spec.rb
49
+ - spec/spec_helper.rb
50
+ - spec/ssh_driver_spec.rb
51
+ has_rdoc: true
52
+ homepage: http://github.com/alexeypetrushin/rssh
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.7
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: ruby ssh and io
83
+ test_files: []
84
+