ztk 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ *.log
1
2
  *.gem
2
3
  *.rbc
3
4
  .bundle
data/README.md CHANGED
@@ -18,9 +18,6 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
22
-
23
-
24
21
  ### Parallel
25
22
 
26
23
  Parallel Processing Class
@@ -38,3 +35,9 @@ Example:
38
35
  parallel.waitall
39
36
  parallel.results
40
37
  => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
38
+
39
+ ### Logger
40
+
41
+ Logging Class
42
+
43
+ This is a logging class based off the ruby core Logger class; but with very verbose logging information, adding PID, micro second timing to log messages. It favors passing messages via blocks in order to speed up execution when log messages do not need to be yielded.
data/bin/ztk ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pry"
4
+ require "ztk"
5
+
6
+ ##
7
+ #
8
+ # Welcome to the ZTK pry shell!
9
+ #
10
+ ##
11
+ binding.pry
data/lib/ztk.rb CHANGED
@@ -1,8 +1,29 @@
1
- require "ztk/version"
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary@jovelabs.com>
4
+ # Copyright: Copyright (c) Jove Labs
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
2
20
 
3
- require "ztk/logger"
4
- require "ztk/parallel"
21
+ require "ztk/version"
5
22
 
6
23
  module ZTK
7
- # Your code goes here...
24
+ class Error < StandardError; end
25
+
26
+ autoload :Logger, "ztk/logger"
27
+ autoload :Parallel, "ztk/parallel"
28
+ autoload :SSH, "ztk/ssh"
8
29
  end
@@ -1,42 +1,79 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary@jovelabs.com>
4
+ # Copyright: Copyright (c) Jove Labs
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+
1
21
  require "logger"
2
22
 
3
- class ZTK::Logger < ::Logger
4
- SEVERITIES = Severity.constants.inject([]) {|arr,c| arr[Severity.const_get(c)] = c; arr}
23
+ module ZTK
24
+ class Logger < ::Logger
5
25
 
6
- def initialize(filename)
7
- super(filename)
8
- set_log_level
9
- end
26
+ ################################################################################
27
+
28
+ SEVERITIES = Severity.constants.inject([]) {|arr,c| arr[Severity.const_get(c)] = c; arr}
10
29
 
11
- def parse_caller(at)
12
- if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
13
- file = Regexp.last_match[1]
14
- line = Regexp.last_match[2]
15
- method = Regexp.last_match[3]
16
- "#{File.basename(file)}:#{line}:#{method} | "
17
- else
18
- ""
30
+ ################################################################################
31
+
32
+ def initialize(*args)
33
+ super(*args)
34
+ set_log_level
19
35
  end
20
- end
21
36
 
22
- def add(severity, message = nil, progname = nil, &block)
23
- return if (@level > severity)
37
+ ################################################################################
24
38
 
25
- called_by = parse_caller(caller[1])
26
- msg = (block && block.call)
27
- return if (msg.nil? || msg.strip.empty?)
28
- message = [message, progname, msg].delete_if{|i| i == nil}.join(": ")
29
- message = "%19s.%06d+%05d|%5s|%s%s\n" % [Time.now.utc.strftime("%Y-%m-%d %H:%M:%S"), Time.now.utc.usec, Process.pid, SEVERITIES[severity], called_by, message]
39
+ def parse_caller(at)
40
+ if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
41
+ file = Regexp.last_match[1]
42
+ line = Regexp.last_match[2]
43
+ method = Regexp.last_match[3]
44
+ "#{File.basename(file)}:#{line}:#{method} | "
45
+ else
46
+ ""
47
+ end
48
+ end
30
49
 
31
- @logdev.write(message)
50
+ ################################################################################
32
51
 
33
- true
34
- end
52
+ def add(severity, message = nil, progname = nil, &block)
53
+ return if (@level > severity)
35
54
 
36
- def set_log_level(level=nil)
37
- defined?(Rails) and (default = (Rails.env.production? ? "INFO" : "DEBUG")) or (default = "INFO")
38
- log_level = (ENV['LOG_LEVEL'] || level || default)
39
- self.level = ZTK::Logger.const_get(log_level.to_s.upcase)
40
- end
55
+ called_by = parse_caller(caller[1])
56
+ msg = (block && block.call)
57
+ return if (msg.nil? || msg.strip.empty?)
58
+ message = [message, progname, msg].delete_if{|i| i == nil}.join(": ")
59
+ message = "%19s.%06d+%05d|%5s|%s%s\n" % [Time.now.utc.strftime("%Y-%m-%d %H:%M:%S"), Time.now.utc.usec, Process.pid, SEVERITIES[severity], called_by, message]
60
+
61
+ @logdev.write(message)
41
62
 
63
+ true
64
+ end
65
+
66
+ ################################################################################
67
+
68
+ def set_log_level(level=nil)
69
+ defined?(Rails) and (default = (Rails.env.production? ? "INFO" : "DEBUG")) or (default = "INFO")
70
+ log_level = (ENV['LOG_LEVEL'] || level || default)
71
+ self.level = ZTK::Logger.const_get(log_level.to_s.upcase)
72
+ end
73
+
74
+ ################################################################################
75
+
76
+ end
42
77
  end
78
+
79
+ ################################################################################
@@ -1,81 +1,120 @@
1
- class ZTK::Parallel
2
- attr_accessor :results
3
-
4
- def initialize(options={})
5
- GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary@jovelabs.com>
4
+ # Copyright: Copyright (c) Jove Labs
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+
21
+ module ZTK
22
+ class Parallel
23
+
24
+ ################################################################################
25
+
26
+ attr_accessor :results
27
+
28
+ ################################################################################
29
+
30
+ def initialize(options={})
31
+ GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
32
+
33
+ options.reverse_merge!(
34
+ :max_forks => `grep -c processor /proc/cpuinfo`.strip.to_i,
35
+ :one_shot => false
36
+ )
37
+
38
+ @max_forks = options[:max_forks]
39
+ @one_shot = options[:one_shot]
40
+
41
+ @forks = Array.new
42
+ @results = Array.new
43
+ end
6
44
 
7
- options.reverse_merge!(
8
- :max_forks => `grep -c processor /proc/cpuinfo`.strip.to_i,
9
- :one_shot => false
10
- )
45
+ ################################################################################
11
46
 
12
- @max_forks = options[:max_forks]
13
- @one_shot = options[:one_shot]
47
+ def process
48
+ pid = nil
49
+ return pid if (@forks.count >= @max_forks)
14
50
 
15
- @forks = Array.new
16
- @results = Array.new
17
- end
51
+ child_reader, parent_writer = IO.pipe
52
+ parent_reader, child_writer = IO.pipe
18
53
 
19
- def process
20
- pid = nil
21
- return pid if (@forks.count >= @max_forks)
54
+ ActiveRecord::Base.connection.disconnect!
55
+ pid = Process.fork do
56
+ ActiveRecord::Base.establish_connection
22
57
 
23
- child_reader, parent_writer = IO.pipe
24
- parent_reader, child_writer = IO.pipe
25
-
26
- ActiveRecord::Base.connection.disconnect!
27
- pid = Process.fork do
28
- ActiveRecord::Base.establish_connection
58
+ parent_writer.close
59
+ parent_reader.close
29
60
 
30
- parent_writer.close
31
- parent_reader.close
61
+ if (data = yield).present?
62
+ child_writer.write(Base64.encode64(Marshal.dump(data)))
63
+ end
32
64
 
33
- if (data = yield).present?
34
- child_writer.write(Base64.encode64(Marshal.dump(data)))
65
+ child_reader.close
66
+ child_writer.close
67
+ Process.exit!(0)
35
68
  end
69
+ ActiveRecord::Base.establish_connection
36
70
 
37
71
  child_reader.close
38
72
  child_writer.close
39
- Process.exit!(0)
73
+
74
+ fork = {:reader => parent_reader, :writer => parent_writer, :pid => pid}
75
+ @forks << fork
76
+
77
+ pid
40
78
  end
41
- ActiveRecord::Base.establish_connection
42
79
 
43
- child_reader.close
44
- child_writer.close
80
+ ################################################################################
45
81
 
46
- fork = {:reader => parent_reader, :writer => parent_writer, :pid => pid}
47
- @forks << fork
82
+ def wait
83
+ pid, status = (Process.wait2(-1, Process::WNOHANG) rescue nil)
84
+ if pid.present? && status.present?
85
+ if (fork = @forks.select{ |f| f[:pid] == pid }.first).present?
86
+ data = (Marshal.load(Base64.decode64(fork[:reader].read.to_s)) rescue nil)
87
+ @results.push(data) if (data.present? && !@one_shot)
48
88
 
49
- pid
50
- end
89
+ fork[:reader].close
90
+ fork[:writer].close
51
91
 
52
- def wait
53
- pid, status = (Process.wait2(-1, Process::WNOHANG) rescue nil)
54
- if pid.present? && status.present?
55
- if (fork = @forks.select{ |f| f[:pid] == pid }.first).present?
56
- data = (Marshal.load(Base64.decode64(fork[:reader].read.to_s)) rescue nil)
57
- @results.push(data) if (data.present? && !@one_shot)
92
+ @forks -= [fork]
93
+ return [pid, status, data]
94
+ end
95
+ end
96
+ nil
97
+ end
58
98
 
59
- fork[:reader].close
60
- fork[:writer].close
99
+ ################################################################################
61
100
 
62
- @forks -= [fork]
63
- return [pid, status, data]
101
+ def waitall
102
+ results = Array.new
103
+ while @forks.count > 0
104
+ results << wait
64
105
  end
106
+ results
65
107
  end
66
- nil
67
- end
68
108
 
69
- def waitall
70
- results = Array.new
71
- while @forks.count > 0
72
- results << wait
109
+ ################################################################################
110
+
111
+ def count
112
+ @forks.count
73
113
  end
74
- results
75
- end
76
114
 
77
- def count
78
- @forks.count
79
- end
115
+ ################################################################################
80
116
 
117
+ end
81
118
  end
119
+
120
+ ################################################################################
@@ -0,0 +1,191 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary@jovelabs.com>
4
+ # Copyright: Copyright (c) Jove Labs
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+
21
+ module ZTK
22
+ class SSHError < Error; end
23
+ class SSH
24
+
25
+ ################################################################################
26
+
27
+ attr_accessor :stdout, :stderr, :stdin, :config
28
+
29
+ ################################################################################
30
+
31
+ def initialize(logger=$logger, stdout=$stdout, stderr=$stderr, stdin=$stdin)
32
+ @logger, @stdout, @stderr, @stdin = logger, stdout, stderr, stdin
33
+
34
+ @logger.sync = true if @logger.respond_to?(:sync=)
35
+ @stdout.sync = true if @stdout.respond_to?(:sync=)
36
+ @stderr.sync = true if @stderr.respond_to?(:sync=)
37
+ @stdin.sync = true if @stdin.respond_to?(:sync=)
38
+
39
+ @config = Hash.new(nil)
40
+ end
41
+
42
+ ################################################################################
43
+
44
+ def console
45
+ @logger and @logger.debug { "config(#{@config.inspect})" }
46
+
47
+ command = [ "ssh" ]
48
+ command << [ "-q" ]
49
+ command << [ "-o", "UserKnownHostsFile=/dev/null" ]
50
+ command << [ "-o", "StrictHostKeyChecking=no" ]
51
+ command << [ "-o", "KeepAlive=yes" ]
52
+ command << [ "-o", "ServerAliveInterval=60" ]
53
+ command << [ "-i", @config[:identity_file] ] if @config[:identity_file]
54
+ command << [ "-o", "ProxyCommand=\"#{proxy_command}\"" ] if @config[:proxy]
55
+ command << "#{@config[:ssh_user]}@#{@config[:host]}"
56
+ command = command.flatten.compact.join(" ")
57
+ @logger and @logger.info { "command(#{command})" }
58
+ Kernel.exec(command)
59
+ end
60
+
61
+ ################################################################################
62
+
63
+ def exec(command, options={})
64
+ @ssh ||= Net::SSH.start(@config[:host], @config[:ssh_user], ssh_options)
65
+
66
+ options = { :silence => false }.merge(options)
67
+ silence = options[:silence]
68
+ output = ""
69
+
70
+ @logger and @logger.debug { "config(#{@config.inspect})" }
71
+ @logger and @logger.debug { "options(#{options.inspect})" }
72
+ @logger and @logger.info { "command(#{command})" }
73
+ channel = @ssh.open_channel do |chan|
74
+ @logger and @logger.debug { "channel opened" }
75
+ chan.exec(command) do |ch, success|
76
+ raise SSHError, "Could not execute '#{command}'." unless success
77
+
78
+ ch.on_data do |c, data|
79
+ output += data
80
+ @logger and @logger.debug { data.chomp.strip }
81
+ @stdout.print(data) if !silence
82
+ end
83
+
84
+ ch.on_extended_data do |c, type, data|
85
+ output += data
86
+ @logger and @logger.debug { data.chomp.strip }
87
+ @stderr.print(data) if !silence
88
+ end
89
+
90
+ end
91
+ end
92
+ channel.wait
93
+ @logger and @logger.debug { "channel closed" }
94
+
95
+ output
96
+ end
97
+
98
+ ################################################################################
99
+
100
+ def upload(local, remote)
101
+ @sftp ||= Net::SFTP.start(@config[:host], @config[:ssh_user], ssh_options)
102
+
103
+ @logger and @logger.debug { "config(#{@config.inspect})" }
104
+ @logger and @logger.info { "parameters(#{local},#{remote})" }
105
+ @sftp.upload!(local.to_s, remote.to_s) do |event, uploader, *args|
106
+ case event
107
+ when :open
108
+ @logger and @logger.info { "upload(#{args[0].local} -> #{args[0].remote})" }
109
+ when :close
110
+ @logger and @logger.debug { "close(#{args[0].remote})" }
111
+ when :mkdir
112
+ @logger and @logger.debug { "mkdir(#{args[0]})" }
113
+ when :put
114
+ @logger and @logger.debug { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
115
+ when :finish
116
+ @logger and @logger.info { "finish" }
117
+ end
118
+ end
119
+ end
120
+
121
+ ################################################################################
122
+
123
+ def download(remote, local)
124
+ @sftp ||= Net::SFTP.start(@config[:host], @config[:ssh_user], ssh_options)
125
+
126
+ @logger and @logger.debug { "config(#{@config.inspect})" }
127
+ @logger and @logger.info { "parameters(#{remote},#{local})" }
128
+ @sftp.download!(remote.to_s, local.to_s) do |event, downloader, *args|
129
+ case event
130
+ when :open
131
+ @logger and @logger.info { "download(#{args[0].remote} -> #{args[0].local})" }
132
+ when :close
133
+ @logger and @logger.debug { "close(#{args[0].local})" }
134
+ when :mkdir
135
+ @logger and @logger.debug { "mkdir(#{args[0]})" }
136
+ when :get
137
+ @logger and @logger.debug { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
138
+ when :finish
139
+ @logger and @logger.info { "finish" }
140
+ end
141
+ end
142
+ end
143
+
144
+
145
+ ################################################################################
146
+ private
147
+ ################################################################################
148
+
149
+ def proxy_command
150
+ @logger and @logger.debug { "config(#{@config.inspect})" }
151
+
152
+ if !@config[:identity_file]
153
+ message = "You must specify an identity file in order to SSH proxy."
154
+ @logger and @logger.fatal { message }
155
+ raise SSHError, message
156
+ end
157
+
158
+ command = ["ssh"]
159
+ command << [ "-q" ]
160
+ command << [ "-o", "UserKnownHostsFile=/dev/null" ]
161
+ command << [ "-o", "StrictHostKeyChecking=no" ]
162
+ command << [ "-o", "KeepAlive=yes" ]
163
+ command << [ "-o", "ServerAliveInterval=60" ]
164
+ command << [ "-i", @config[:proxy_identity_file] ] if @config[:proxy_identity_file]
165
+ command << "#{@config[:proxy_ssh_user]}@#{@config[:proxy_host]}"
166
+ command << "nc %h %p"
167
+ command = command.flatten.compact.join(" ")
168
+ @logger and @logger.debug { "command(#{command})" }
169
+ command
170
+ end
171
+
172
+ ################################################################################
173
+
174
+ def ssh_options
175
+ @logger and @logger.debug { "config(#{@config.inspect})" }
176
+ options = {}
177
+ options.merge!(:password => @config[:ssh_password]) if @config[:ssh_password]
178
+ options.merge!(:keys => @config[:identity_file]) if @config[:identity_file]
179
+ options.merge!(:timeout => @config[:timeout]) if @config[:timeout]
180
+ options.merge!(:user_known_hosts_file => '/dev/null') if !@config[:host_key_verify]
181
+ options.merge!(:proxy => Net::SSH::Proxy::Command.new(proxy_command)) if @config[:proxy]
182
+ @logger and @logger.debug { "options(#{options.inspect})" }
183
+ options
184
+ end
185
+
186
+ ################################################################################
187
+
188
+ end
189
+ end
190
+
191
+ ################################################################################
@@ -1,3 +1,23 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary@jovelabs.com>
4
+ # Copyright: Copyright (c) Jove Labs
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+
1
21
  module ZTK
2
- VERSION = "0.0.2" unless const_defined?(:VERSION)
22
+ VERSION = "0.0.3" unless const_defined?(:VERSION)
3
23
  end
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
6
6
  gem.email = ["zachary@jovelabs.com"]
7
7
  gem.description = %q{Zachary's Toolkit}
8
8
  gem.summary = %q{Contains various helper classes and utility methods I find I regularly need.}
9
- gem.homepage = "http://www.github.com/jovelabs"
9
+ gem.homepage = "https://github.com/jovelabs/ztk"
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
12
12
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -14,4 +14,8 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "ztk"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = ZTK::VERSION
17
+
18
+ gem.add_dependency("net-ssh")
19
+ gem.add_dependency("net-sftp")
20
+ gem.add_development_dependency("pry")
17
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ztk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,61 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-24 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2012-08-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-ssh
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: net-sftp
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: pry
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
14
62
  description: Zachary's Toolkit
15
63
  email:
16
64
  - zachary@jovelabs.com
17
- executables: []
65
+ executables:
66
+ - ztk
18
67
  extensions: []
19
68
  extra_rdoc_files: []
20
69
  files:
@@ -24,12 +73,14 @@ files:
24
73
  - LICENSE
25
74
  - README.md
26
75
  - Rakefile
76
+ - bin/ztk
27
77
  - lib/ztk.rb
28
78
  - lib/ztk/logger.rb
29
79
  - lib/ztk/parallel.rb
80
+ - lib/ztk/ssh.rb
30
81
  - lib/ztk/version.rb
31
82
  - ztk.gemspec
32
- homepage: http://www.github.com/jovelabs
83
+ homepage: https://github.com/jovelabs/ztk
33
84
  licenses: []
34
85
  post_install_message:
35
86
  rdoc_options: []