epitools 0.1.11 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/epitools.gemspec +17 -8
- data/lib/epitools/browser.rb +213 -0
- data/lib/epitools/browser/browser_cache.rb +164 -0
- data/lib/epitools/browser/mechanize_progressbar.rb +53 -0
- data/lib/epitools/progressbar.rb +269 -0
- data/lib/epitools/sys.rb +398 -0
- data/spec/basetypes_spec.rb +4 -4
- data/spec/browser_spec.rb +14 -0
- data/spec/sys_spec.rb +64 -0
- metadata +19 -10
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/epitools.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{epitools}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["epitron"]
|
12
|
-
s.date = %q{2010-09-
|
12
|
+
s.date = %q{2010-09-28}
|
13
13
|
s.description = %q{Miscellaneous utility libraries to make my life easier.}
|
14
14
|
s.email = %q{chris@ill-logic.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,6 +26,9 @@ Gem::Specification.new do |s|
|
|
26
26
|
"epitools.gemspec",
|
27
27
|
"lib/epitools.rb",
|
28
28
|
"lib/epitools/basetypes.rb",
|
29
|
+
"lib/epitools/browser.rb",
|
30
|
+
"lib/epitools/browser/browser_cache.rb",
|
31
|
+
"lib/epitools/browser/mechanize_progressbar.rb",
|
29
32
|
"lib/epitools/hexdump.rb",
|
30
33
|
"lib/epitools/http.rb",
|
31
34
|
"lib/epitools/lcs.rb",
|
@@ -33,12 +36,15 @@ Gem::Specification.new do |s|
|
|
33
36
|
"lib/epitools/niceprint.rb",
|
34
37
|
"lib/epitools/permutations.rb",
|
35
38
|
"lib/epitools/pretty_backtrace.rb",
|
39
|
+
"lib/epitools/progressbar.rb",
|
36
40
|
"lib/epitools/rails.rb",
|
37
41
|
"lib/epitools/rash.rb",
|
38
42
|
"lib/epitools/ratio.rb",
|
39
43
|
"lib/epitools/string_to_proc.rb",
|
44
|
+
"lib/epitools/sys.rb",
|
40
45
|
"lib/epitools/zopen.rb",
|
41
46
|
"spec/basetypes_spec.rb",
|
47
|
+
"spec/browser_spec.rb",
|
42
48
|
"spec/lcs_spec.rb",
|
43
49
|
"spec/metaclass_spec.rb",
|
44
50
|
"spec/permutations_spec.rb",
|
@@ -46,6 +52,7 @@ Gem::Specification.new do |s|
|
|
46
52
|
"spec/ratio_spec.rb",
|
47
53
|
"spec/spec.opts",
|
48
54
|
"spec/spec_helper.rb",
|
55
|
+
"spec/sys_spec.rb",
|
49
56
|
"spec/zopen_spec.rb"
|
50
57
|
]
|
51
58
|
s.homepage = %q{http://github.com/epitron/epitools}
|
@@ -54,14 +61,16 @@ Gem::Specification.new do |s|
|
|
54
61
|
s.rubygems_version = %q{1.3.7}
|
55
62
|
s.summary = %q{NOT UTILS... METILS!}
|
56
63
|
s.test_files = [
|
57
|
-
"spec/
|
58
|
-
"spec/
|
59
|
-
"spec/
|
60
|
-
"spec/
|
64
|
+
"spec/sys_spec.rb",
|
65
|
+
"spec/permutations_spec.rb",
|
66
|
+
"spec/rash_spec.rb",
|
67
|
+
"spec/spec_helper.rb",
|
68
|
+
"spec/browser_spec.rb",
|
61
69
|
"spec/lcs_spec.rb",
|
70
|
+
"spec/basetypes_spec.rb",
|
71
|
+
"spec/ratio_spec.rb",
|
62
72
|
"spec/zopen_spec.rb",
|
63
|
-
"spec/
|
64
|
-
"spec/spec_helper.rb"
|
73
|
+
"spec/metaclass_spec.rb"
|
65
74
|
]
|
66
75
|
|
67
76
|
if s.respond_to? :specification_version then
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'uri'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
require 'epitools/browser/mechanize_progressbar'
|
6
|
+
|
7
|
+
# TODO: Make socksify optional (eg: if proxy is specified)
|
8
|
+
#require 'socksify'
|
9
|
+
|
10
|
+
# TODO: Put options here.
|
11
|
+
=begin
|
12
|
+
class BrowserOptions < OpenStruct
|
13
|
+
|
14
|
+
DEFAULTS = {
|
15
|
+
:delay => 1,
|
16
|
+
:delay_jitter => 0.2,
|
17
|
+
:use_cache => true,
|
18
|
+
:use_logs => false,
|
19
|
+
:cookie_file => "cookies.txt"
|
20
|
+
}
|
21
|
+
|
22
|
+
def initialize(extra_opts)
|
23
|
+
|
24
|
+
@opts = DEFAULTS.dup
|
25
|
+
|
26
|
+
for key, val in opts
|
27
|
+
if key.in? DEFAULTS
|
28
|
+
@opts[key] = val
|
29
|
+
else
|
30
|
+
raise "Unknown option: #{key}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
=end
|
37
|
+
|
38
|
+
#
|
39
|
+
# A mechanize class that emulates a web-browser, with cache and everything.
|
40
|
+
# Progress bars are enabled by default.
|
41
|
+
#
|
42
|
+
class Browser
|
43
|
+
|
44
|
+
attr_accessor :agent, :cache, :use_cache, :delay, :delay_jitter
|
45
|
+
|
46
|
+
#
|
47
|
+
# Default options:
|
48
|
+
# :delay => 1, # Sleep 1 second between gets
|
49
|
+
# :delay_jitter => 0.2, # Random deviation from delay
|
50
|
+
# :use_cache => true, # Cache all gets
|
51
|
+
# :use_logs => false, # Don't log the detailed transfer info
|
52
|
+
# :cookie_file => "cookies.txt" # Save cookies to file
|
53
|
+
#
|
54
|
+
def initialize(options={})
|
55
|
+
@last_get = Time.at(0)
|
56
|
+
@delay = options[:delay] || 1
|
57
|
+
@delay_jitter = options[:delay_jitter] || 0.2
|
58
|
+
@use_cache = options[:cache] || true
|
59
|
+
@use_logs = options[:logs] || false
|
60
|
+
@cookie_file = options[:cookiefile] || "cookies.txt"
|
61
|
+
|
62
|
+
# TODO: @progress, @user_agent, @logfile, @cache_file (default location: ~/.epitools?)
|
63
|
+
|
64
|
+
if options[:proxy]
|
65
|
+
host, port = options[:proxy].split(':')
|
66
|
+
TCPSocket::socks_server = host
|
67
|
+
TCPSocket::socks_port = port.to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
init_agent!
|
71
|
+
init_cache!
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def init_agent!
|
76
|
+
@agent = Mechanize.new do |a|
|
77
|
+
# ["Mechanize", "Mac Mozilla", "Linux Mozilla", "Windows IE 6", "iPhone", "Linux Konqueror", "Windows IE 7", "Mac FireFox", "Mac Safari", "Windows Mozilla"]
|
78
|
+
a.max_history = 10
|
79
|
+
a.user_agent_alias = "Windows IE 7"
|
80
|
+
a.log = Logger.new "mechanize.log" if @use_logs
|
81
|
+
end
|
82
|
+
|
83
|
+
load_cookies!
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def delay(override_delay=nil, override_jitter=nil)
|
88
|
+
elapsed = Time.now - @last_get
|
89
|
+
jitter = rand * (override_jitter || @delay_jitter)
|
90
|
+
amount = ( (override_delay || @delay) + jitter ) - elapsed
|
91
|
+
|
92
|
+
if amount > 0
|
93
|
+
puts " |_ sleeping for %0.3f seconds..." % amount
|
94
|
+
sleep amount
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def init_cache!
|
100
|
+
# TODO: Rescue "couldn't load" exception and disable caching
|
101
|
+
require 'epitools/browser/browser_cache'
|
102
|
+
@cache = CacheDB.new(agent) if @use_cache
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def relative?(url)
|
107
|
+
not url =~ %r{^https?://}
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def cache_put(page, url)
|
112
|
+
if page.is_a? Mechanize::Page and page.content_type =~ %r{^text/}
|
113
|
+
puts " |_ writing to cache"
|
114
|
+
cache.put(page, url, :overwrite=>true)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Retrieve an URL, and return a Mechanize::Page instance (which acts a
|
120
|
+
# bit like a Nokogiri::HTML::Document instance.)
|
121
|
+
#
|
122
|
+
# Options:
|
123
|
+
# :use_cache => true/false | read/write
|
124
|
+
# :read_cache => true/false | check cache before getting page
|
125
|
+
# :write_cache => true/false | write gotten pages to cache
|
126
|
+
#
|
127
|
+
def get(url, options={})
|
128
|
+
|
129
|
+
# TODO: Have a base-URL option
|
130
|
+
|
131
|
+
#if relative?(url)
|
132
|
+
# url = URI.join("http://base-url/", url).to_s
|
133
|
+
#end
|
134
|
+
|
135
|
+
# Determine the cache setting
|
136
|
+
options[:use_cache] ||= @use_cache
|
137
|
+
|
138
|
+
if options[:use_cache] == false
|
139
|
+
options[:read_cache] = false
|
140
|
+
options[:write_cache] = false
|
141
|
+
end
|
142
|
+
|
143
|
+
options[:read_cache] = true if options[:read_cache].nil?
|
144
|
+
options[:write_cache] = true if options[:write_cache].nil?
|
145
|
+
|
146
|
+
read_cache = options[:read_cache] && cache.include?(url)
|
147
|
+
write_cache = options[:write_cache]
|
148
|
+
|
149
|
+
puts
|
150
|
+
puts "[ #{url.inspect} (read_cache=#{options[:read_cache]}, write_cache=#{options[:write_cache]}) ]"
|
151
|
+
|
152
|
+
delay unless read_cache
|
153
|
+
|
154
|
+
begin
|
155
|
+
|
156
|
+
if read_cache
|
157
|
+
page = cache.get(url)
|
158
|
+
if page.nil?
|
159
|
+
puts " |_ CACHE FAIL! Re-getting page."
|
160
|
+
page = get(url, false)
|
161
|
+
end
|
162
|
+
puts " |_ cached (#{page.content_type})"
|
163
|
+
else
|
164
|
+
page = agent.get url
|
165
|
+
@last_get = Time.now
|
166
|
+
end
|
167
|
+
|
168
|
+
cache_put(page, url) if write_cache and not read_cache
|
169
|
+
|
170
|
+
puts
|
171
|
+
|
172
|
+
rescue Net::HTTPBadResponse, Errno::ECONNRESET, SocketError, Timeout::Error, SOCKSError => e
|
173
|
+
puts " |_ ERROR: #{e.inspect} -- retrying"
|
174
|
+
delay(5)
|
175
|
+
retry
|
176
|
+
=begin
|
177
|
+
rescue Mechanize::ResponseCodeError => e
|
178
|
+
|
179
|
+
case e.response_code
|
180
|
+
when "401" #=> Net::HTTPUnauthorized
|
181
|
+
p e
|
182
|
+
login!
|
183
|
+
page = get(url)
|
184
|
+
puts
|
185
|
+
when "404"
|
186
|
+
p e
|
187
|
+
raise e
|
188
|
+
when "503"
|
189
|
+
puts " |_ ERROR: #{e.inspect} -- retrying"
|
190
|
+
delay(5)
|
191
|
+
retry
|
192
|
+
else
|
193
|
+
raise e
|
194
|
+
end
|
195
|
+
=end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
page
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def load_cookies!
|
205
|
+
agent.cookie_jar.load @cookie_file if File.exists? @cookie_file
|
206
|
+
end
|
207
|
+
|
208
|
+
def save_cookies!
|
209
|
+
agent.cookie_jar.save_as @cookie_file
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
@@ -0,0 +1,164 @@
|
|
1
|
+
#require 'mechanize'
|
2
|
+
require 'sqlite3'
|
3
|
+
|
4
|
+
class CacheDB
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :db, :agent
|
9
|
+
|
10
|
+
def initialize(agent, filename="browsercache.db")
|
11
|
+
@agent = agent
|
12
|
+
@filename = filename
|
13
|
+
|
14
|
+
@db = SQLite3::Database.new(filename)
|
15
|
+
@db.busy_timeout(50)
|
16
|
+
|
17
|
+
create_tables
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"#<CacheDB filename=#{@filename.inspect}, count=#{count}, size=#{File.size @filename} bytes>"
|
22
|
+
end
|
23
|
+
|
24
|
+
def count
|
25
|
+
db.execute("SELECT COUNT(1) FROM cache").first.first.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :size, :count
|
29
|
+
|
30
|
+
def put(page, original_url=nil, options={})
|
31
|
+
raise "Invalid page" unless [:body, :content_type, :uri].all?{|m| page.respond_to? m }
|
32
|
+
|
33
|
+
url = page.uri.to_s
|
34
|
+
|
35
|
+
if url != original_url
|
36
|
+
# redirect original_url to url
|
37
|
+
expire(original_url) if options[:overwrite]
|
38
|
+
db.execute(
|
39
|
+
"INSERT INTO cache VALUES ( ?, ?, ?, ? )",
|
40
|
+
original_url,
|
41
|
+
page.content_type,
|
42
|
+
nil,
|
43
|
+
url
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
compressed_body = Zlib::Deflate.deflate(page.body)
|
48
|
+
expire(url) if options[:overwrite]
|
49
|
+
db.execute(
|
50
|
+
"INSERT INTO cache VALUES ( ?, ?, ?, ? )",
|
51
|
+
url,
|
52
|
+
page.content_type,
|
53
|
+
SQLite3::Blob.new( compressed_body ),
|
54
|
+
nil
|
55
|
+
)
|
56
|
+
|
57
|
+
true
|
58
|
+
|
59
|
+
rescue SQLite3::SQLException => e
|
60
|
+
p [:exception, e]
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
def row_to_page(row)
|
65
|
+
url, content_type, compressed_body, redirect = row
|
66
|
+
|
67
|
+
if redirect
|
68
|
+
get(redirect)
|
69
|
+
else
|
70
|
+
body = Zlib::Inflate.inflate(compressed_body)
|
71
|
+
|
72
|
+
WWW::Mechanize::Page.new(
|
73
|
+
URI.parse(url),
|
74
|
+
{'content-type'=>content_type},
|
75
|
+
body,
|
76
|
+
nil,
|
77
|
+
agent
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def pages_via_sql(*args, &block)
|
83
|
+
if block_given?
|
84
|
+
db.execute(*args) do |row|
|
85
|
+
yield row_to_page(row)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
db.execute(*args).map{|row| row_to_page(row) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def grep(pattern, &block)
|
93
|
+
pages_via_sql("SELECT * FROM cache WHERE url like '%#{pattern}%'", &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
def get(url)
|
97
|
+
pages = pages_via_sql("SELECT * FROM cache WHERE url = ?", url.to_s)
|
98
|
+
|
99
|
+
if pages.any?
|
100
|
+
pages.first
|
101
|
+
else
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def includes?(url)
|
107
|
+
db.execute("SELECT url FROM cache WHERE url = ?", url.to_s).any?
|
108
|
+
end
|
109
|
+
|
110
|
+
alias_method :include?, :includes?
|
111
|
+
|
112
|
+
def urls(pattern=nil)
|
113
|
+
if pattern
|
114
|
+
rows = db.execute("SELECT url FROM cache WHERE url LIKE '%#{pattern}%'")
|
115
|
+
else
|
116
|
+
rows = db.execute('SELECT url FROM cache')
|
117
|
+
end
|
118
|
+
rows.map{|row| row.first}
|
119
|
+
end
|
120
|
+
|
121
|
+
def clear(pattern=nil)
|
122
|
+
if pattern
|
123
|
+
db.execute("DELETE FROM cache WHERE url LIKE '%#{pattern}%'")
|
124
|
+
else
|
125
|
+
db.execute("DELETE FROM cache")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def each(&block)
|
130
|
+
pages_via_sql("SELECT * FROM cache", &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
def each_url
|
134
|
+
db.execute("SELECT url FROM cache") do |row|
|
135
|
+
yield row.first
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def expire(url)
|
140
|
+
db.execute("DELETE FROM cache WHERE url = ?", url)
|
141
|
+
end
|
142
|
+
|
143
|
+
def recreate_tables
|
144
|
+
drop_tables rescue nil
|
145
|
+
create_tables
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def list_tables
|
151
|
+
db.execute("SELECT name FROM SQLITE_MASTER WHERE type='table'")
|
152
|
+
end
|
153
|
+
|
154
|
+
def create_tables
|
155
|
+
db.execute("CREATE TABLE IF NOT EXISTS cache ( url varchar(2048), content_type varchar(255), body blob, redirect varchar(2048) )")
|
156
|
+
db.execute("CREATE UNIQUE INDEX IF NOT EXISTS url_index ON cache ( url )")
|
157
|
+
end
|
158
|
+
|
159
|
+
def drop_tables
|
160
|
+
db.execute("DROP TABLE cache")
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'epitools/progressbar'
|
2
|
+
|
3
|
+
class Mechanize
|
4
|
+
class Chain
|
5
|
+
class ResponseReader
|
6
|
+
include Mechanize::Handler
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
@response = response
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle(ctx, params)
|
13
|
+
params[:response] = @response
|
14
|
+
body = StringIO.new
|
15
|
+
total = 0
|
16
|
+
|
17
|
+
if @response.respond_to? :content_type
|
18
|
+
pbar = ProgressBar.new(" |_ #{@response.content_type}", @response.content_length)
|
19
|
+
else
|
20
|
+
pbar = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
@response.read_body { |part|
|
24
|
+
total += part.length
|
25
|
+
body.write(part)
|
26
|
+
|
27
|
+
pbar.set(total) if pbar
|
28
|
+
Mechanize.log.debug("Read #{total} bytes") if Mechanize.log
|
29
|
+
}
|
30
|
+
|
31
|
+
pbar.finish if pbar
|
32
|
+
|
33
|
+
body.rewind
|
34
|
+
|
35
|
+
res_klass = Net::HTTPResponse::CODE_TO_OBJ[@response.code.to_s]
|
36
|
+
raise ResponseCodeError.new(@response) unless res_klass
|
37
|
+
|
38
|
+
# Net::HTTP ignores EOFError if Content-length is given, so we emulate it here.
|
39
|
+
unless res_klass <= Net::HTTPRedirection
|
40
|
+
raise EOFError if (!params[:request].is_a?(Net::HTTP::Head)) && @response.content_length() && @response.content_length() != total
|
41
|
+
end
|
42
|
+
|
43
|
+
@response.each_header { |k,v|
|
44
|
+
Mechanize.log.debug("response-header: #{ k } => #{ v }")
|
45
|
+
} if Mechanize.log
|
46
|
+
|
47
|
+
params[:response_body] = body
|
48
|
+
params[:res_klass] = res_klass
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
#
|
2
|
+
# Ruby/ProgressBar - a text progress bar library
|
3
|
+
#
|
4
|
+
# Copyright (C) 2001-2005 Satoru Takabayashi <satoru@namazu.org>
|
5
|
+
# Copyright (C) 2010 Chris Gahan <chris@ill-logic.com>
|
6
|
+
# All rights reserved.
|
7
|
+
# This is free software with ABSOLUTELY NO WARRANTY.
|
8
|
+
#
|
9
|
+
# You can redistribute it and/or modify it under the terms
|
10
|
+
# of Ruby's license.
|
11
|
+
#
|
12
|
+
|
13
|
+
class ProgressBar
|
14
|
+
VERSION = "0.10"
|
15
|
+
|
16
|
+
def initialize (title, total=nil, out = STDERR)
|
17
|
+
@title = title
|
18
|
+
@total = total
|
19
|
+
@out = out
|
20
|
+
@terminal_width = 80
|
21
|
+
@bar_mark = "."
|
22
|
+
@current = 0
|
23
|
+
@previous = 0
|
24
|
+
@finished_p = false
|
25
|
+
@start_time = Time.now
|
26
|
+
@previous_time = @start_time
|
27
|
+
@title_width = 18
|
28
|
+
#@format = "%-#{@title_width}s %3d%% %s %s"
|
29
|
+
if @total
|
30
|
+
#@format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
|
31
|
+
@format_arguments = [:title, :percentage, :stat_for_file_transfer]
|
32
|
+
else
|
33
|
+
@format_arguments = [:title, :stat_for_file_transfer]
|
34
|
+
end
|
35
|
+
|
36
|
+
clear
|
37
|
+
show
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :title
|
41
|
+
attr_reader :current
|
42
|
+
attr_reader :total
|
43
|
+
attr_accessor :start_time
|
44
|
+
|
45
|
+
private
|
46
|
+
def fmt_bar
|
47
|
+
bar_width = do_percentage * @terminal_width / 100
|
48
|
+
sprintf("|%s%s|",
|
49
|
+
@bar_mark * bar_width,
|
50
|
+
" " * (@terminal_width - bar_width))
|
51
|
+
end
|
52
|
+
|
53
|
+
def fmt_percentage
|
54
|
+
"%3d%%" % do_percentage
|
55
|
+
end
|
56
|
+
|
57
|
+
def fmt_stat
|
58
|
+
if @finished_p then elapsed else eta end
|
59
|
+
end
|
60
|
+
|
61
|
+
def fmt_stat_for_file_transfer
|
62
|
+
if !@total or @finished_p then
|
63
|
+
sprintf("%s %s %s", bytes, transfer_rate, elapsed)
|
64
|
+
else
|
65
|
+
sprintf("%s %s %s", bytes, transfer_rate, eta)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def fmt_title
|
70
|
+
title = (@title[0,(@title_width - 1)] + ":")
|
71
|
+
@total ? "%-#{@title_width}s" % title : title
|
72
|
+
end
|
73
|
+
|
74
|
+
def convert_bytes(bytes)
|
75
|
+
if bytes < 1024
|
76
|
+
sprintf("%6dB", bytes)
|
77
|
+
elsif bytes < 1024 * 1000 # 1000kb
|
78
|
+
sprintf("%5.1fKB", bytes.to_f / 1024)
|
79
|
+
elsif bytes < 1024 * 1024 * 1000 # 1000mb
|
80
|
+
sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
|
81
|
+
else
|
82
|
+
sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def transfer_rate
|
87
|
+
bytes_per_second = @current.to_f / (Time.now - @start_time)
|
88
|
+
sprintf("%s/s", convert_bytes(bytes_per_second))
|
89
|
+
end
|
90
|
+
|
91
|
+
def bytes
|
92
|
+
convert_bytes(@current)
|
93
|
+
end
|
94
|
+
|
95
|
+
def format_time(t)
|
96
|
+
t = t.to_i
|
97
|
+
sec = t % 60
|
98
|
+
min = (t / 60) % 60
|
99
|
+
hour = t / 3600
|
100
|
+
sprintf("%02d:%02d:%02d", hour, min, sec);
|
101
|
+
end
|
102
|
+
|
103
|
+
# ETA stands for Estimated Time of Arrival.
|
104
|
+
def eta
|
105
|
+
if @current == 0
|
106
|
+
"ETA: --:--:--"
|
107
|
+
else
|
108
|
+
elapsed = Time.now - @start_time
|
109
|
+
eta = elapsed * @total / @current - elapsed;
|
110
|
+
sprintf("ETA: %s", format_time(eta))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def elapsed
|
115
|
+
elapsed = Time.now - @start_time
|
116
|
+
sprintf("Time: %s", format_time(elapsed))
|
117
|
+
end
|
118
|
+
|
119
|
+
def eol
|
120
|
+
if @finished_p then "\n" else "\r" end
|
121
|
+
end
|
122
|
+
|
123
|
+
def do_percentage
|
124
|
+
if @total.zero?
|
125
|
+
100
|
126
|
+
else
|
127
|
+
@current * 100 / @total
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def get_width
|
132
|
+
# FIXME: I don't know how portable it is.
|
133
|
+
default_width = 80
|
134
|
+
begin
|
135
|
+
tiocgwinsz = 0x5413
|
136
|
+
data = [0, 0, 0, 0].pack("SSSS")
|
137
|
+
if @out.ioctl(tiocgwinsz, data) >= 0 then
|
138
|
+
rows, cols, xpixels, ypixels = data.unpack("SSSS")
|
139
|
+
if cols >= 0 then cols else default_width end
|
140
|
+
else
|
141
|
+
default_width
|
142
|
+
end
|
143
|
+
rescue Exception
|
144
|
+
default_width
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def show
|
149
|
+
arguments = @format_arguments.map {|method|
|
150
|
+
send("fmt_#{method}")
|
151
|
+
}
|
152
|
+
#line = sprintf(@format, *arguments)
|
153
|
+
line = arguments.join(" ")
|
154
|
+
|
155
|
+
# width = get_width
|
156
|
+
# if line.length == width - 1
|
157
|
+
# @out.print(line + eol)
|
158
|
+
# @out.flush
|
159
|
+
# elsif line.length >= width
|
160
|
+
# @terminal_width = [@terminal_width - (line.length - width + 1), 0].max
|
161
|
+
# if @terminal_width == 0 then @out.print(line + eol) else show end
|
162
|
+
# else # line.length < width - 1
|
163
|
+
# @terminal_width += width - line.length + 1
|
164
|
+
# show
|
165
|
+
# end
|
166
|
+
@out.print(line + eol)
|
167
|
+
@out.flush
|
168
|
+
|
169
|
+
@previous_time = Time.now
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def show_if_needed
|
174
|
+
|
175
|
+
if @total
|
176
|
+
|
177
|
+
if @total.zero?
|
178
|
+
cur_percentage = 100
|
179
|
+
prev_percentage = 0
|
180
|
+
else
|
181
|
+
cur_percentage = (@current * 100 / @total).to_i
|
182
|
+
prev_percentage = (@previous * 100 / @total).to_i
|
183
|
+
end
|
184
|
+
|
185
|
+
# Use "!=" instead of ">" to support negative changes
|
186
|
+
if cur_percentage != prev_percentage ||
|
187
|
+
Time.now - @previous_time >= 1 || @finished_p
|
188
|
+
show
|
189
|
+
end
|
190
|
+
|
191
|
+
else
|
192
|
+
|
193
|
+
if Time.now - @previous_time >= 1 || @finished_p
|
194
|
+
show
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
public
|
202
|
+
def clear
|
203
|
+
@out.print "\r"
|
204
|
+
@out.print(" " * (get_width - 1))
|
205
|
+
@out.print "\r"
|
206
|
+
end
|
207
|
+
|
208
|
+
def finish
|
209
|
+
@current = @total if @total
|
210
|
+
@finished_p = true
|
211
|
+
show
|
212
|
+
end
|
213
|
+
|
214
|
+
def finished?
|
215
|
+
@finished_p
|
216
|
+
end
|
217
|
+
|
218
|
+
def file_transfer_mode
|
219
|
+
@format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
|
220
|
+
end
|
221
|
+
|
222
|
+
def format=(format)
|
223
|
+
@format = format
|
224
|
+
end
|
225
|
+
|
226
|
+
def format_arguments=(arguments)
|
227
|
+
@format_arguments = arguments
|
228
|
+
end
|
229
|
+
|
230
|
+
def halt
|
231
|
+
@finished_p = true
|
232
|
+
show
|
233
|
+
end
|
234
|
+
|
235
|
+
def inc(step = 1)
|
236
|
+
@current += step
|
237
|
+
@current = @total if @total and @current > @total
|
238
|
+
show_if_needed
|
239
|
+
@previous = @current
|
240
|
+
end
|
241
|
+
|
242
|
+
def set(count)
|
243
|
+
if @total and (count < 0 || count > @total)
|
244
|
+
raise "invalid count: #{count} (total: #{@total})"
|
245
|
+
end
|
246
|
+
@current = count
|
247
|
+
show_if_needed
|
248
|
+
@previous = @current
|
249
|
+
end
|
250
|
+
|
251
|
+
def inspect
|
252
|
+
"#<ProgressBar:#{@current}/#{@total}>"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class ReversedProgressBar < ProgressBar
|
257
|
+
def do_percentage
|
258
|
+
100 - super
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
if $0 == __FILE__
|
263
|
+
pb = ProgressBar.new("no_total")
|
264
|
+
5.times do
|
265
|
+
pb.inc 4000
|
266
|
+
sleep 1
|
267
|
+
end
|
268
|
+
pb.finish
|
269
|
+
end
|
data/lib/epitools/sys.rb
ADDED
@@ -0,0 +1,398 @@
|
|
1
|
+
#
|
2
|
+
# Cross-platform operating system functions.
|
3
|
+
# Includes: process listing, platform detection, etc.
|
4
|
+
#
|
5
|
+
module Sys
|
6
|
+
|
7
|
+
#-----------------------------------------------------------------------------
|
8
|
+
|
9
|
+
PS_FIELD_TABLE = [
|
10
|
+
[:pid, :to_i],
|
11
|
+
[:pcpu, :to_f],
|
12
|
+
[:pmem, :to_f],
|
13
|
+
[:stat, :to_s],
|
14
|
+
[:rss, :to_i],
|
15
|
+
[:vsz, :to_i],
|
16
|
+
[:user, :to_s],
|
17
|
+
[:majflt, :to_i],
|
18
|
+
[:minflt, :to_i],
|
19
|
+
[:command,:to_s],
|
20
|
+
]
|
21
|
+
|
22
|
+
PS_FIELDS = PS_FIELD_TABLE.map { |name, func| name }
|
23
|
+
PS_FIELD_TRANSFORMS = Hash[ *PS_FIELD_TABLE.flatten ]
|
24
|
+
|
25
|
+
class ProcessNotFound < Exception; end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Contains all the information that PS can report about a process for
|
29
|
+
# the current platform.
|
30
|
+
#
|
31
|
+
# The following attribute accessor methods are available:
|
32
|
+
#
|
33
|
+
# pid (integer)
|
34
|
+
# pcpu (float)
|
35
|
+
# pmem (float)
|
36
|
+
# stat (string)
|
37
|
+
# rss (integer)
|
38
|
+
# vsz (integer)
|
39
|
+
# user (string)
|
40
|
+
# majflt (integer)
|
41
|
+
# minflt (integer)
|
42
|
+
# command (string)
|
43
|
+
# state (array of symbols; see DARWIN_STATES or LINUX_STATES)
|
44
|
+
#
|
45
|
+
class ProcessInfo < Struct.new(*PS_FIELDS+[:state])
|
46
|
+
|
47
|
+
DARWIN_STATES = {
|
48
|
+
"R"=>:running,
|
49
|
+
"S"=>:sleeping,
|
50
|
+
"I"=>:idle,
|
51
|
+
"T"=>:stopped,
|
52
|
+
"U"=>:wait,
|
53
|
+
"Z"=>:zombie,
|
54
|
+
"W"=>:swapped,
|
55
|
+
|
56
|
+
"s"=>:session_leader,
|
57
|
+
"X"=>:debugging,
|
58
|
+
"E"=>:exiting,
|
59
|
+
"<"=>:high_priority,
|
60
|
+
"N"=>:low_priority,
|
61
|
+
"+"=>:foreground,
|
62
|
+
"L"=>:locked_pages,
|
63
|
+
}
|
64
|
+
|
65
|
+
LINUX_STATES = {
|
66
|
+
"R"=>:running,
|
67
|
+
"S"=>:sleeping,
|
68
|
+
"T"=>:stopped,
|
69
|
+
"D"=>:wait,
|
70
|
+
"Z"=>:zombie,
|
71
|
+
"W"=>:swapped,
|
72
|
+
"X"=>:dead,
|
73
|
+
|
74
|
+
"s"=>:session_leader,
|
75
|
+
"<"=>:high_priority,
|
76
|
+
"N"=>:low_priority,
|
77
|
+
"+"=>:foreground,
|
78
|
+
"L"=>:locked_pages,
|
79
|
+
"l"=>:multithreaded,
|
80
|
+
}
|
81
|
+
|
82
|
+
def initialize(*args)
|
83
|
+
@dead = false
|
84
|
+
args << stat_to_state(args[PS_FIELDS.index(:stat)])
|
85
|
+
super(*args)
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Convert all the process information to a hash.
|
90
|
+
#
|
91
|
+
def to_hash
|
92
|
+
Hash[ *members.zip(values).flatten(1) ]
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Send the TERM signal to this process.
|
97
|
+
#
|
98
|
+
def kill!(signal="TERM")
|
99
|
+
puts "Killing #{pid} (#{signal})"
|
100
|
+
Process.kill(signal, pid)
|
101
|
+
# TODO: handle exception Errno::ESRCH (no such process)
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Has this process been killed?
|
106
|
+
#
|
107
|
+
def dead?
|
108
|
+
@dead ||= Sys.pid(pid).empty?
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Refresh this process' statistics.
|
113
|
+
#
|
114
|
+
def refresh
|
115
|
+
processes = Sys.ps(pid)
|
116
|
+
|
117
|
+
if processes.empty?
|
118
|
+
@dead = true
|
119
|
+
raise ProcessNotFound
|
120
|
+
end
|
121
|
+
|
122
|
+
updated_process = processes.first
|
123
|
+
members.each { |member| self[member] = updated_process[member] }
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def stat_to_state(str)
|
130
|
+
states = case Sys.os
|
131
|
+
when "Linux" then LINUX_STATES
|
132
|
+
when "Darwin" then DARWIN_STATES
|
133
|
+
else raise "Unsupported platform: #{Sys.os}"
|
134
|
+
end
|
135
|
+
|
136
|
+
str.scan(/./).map { |char| states[char] }.compact
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
#-----------------------------------------------------------------------------
|
142
|
+
|
143
|
+
#
|
144
|
+
# List all (or specified) processes, and return ProcessInfo objects.
|
145
|
+
# (Takes an optional list of pids as arguments.)
|
146
|
+
#
|
147
|
+
def self.ps(*pids)
|
148
|
+
options = PS_FIELDS.join(',')
|
149
|
+
|
150
|
+
if pids.any?
|
151
|
+
command = "ps -p #{pids.join(',')} -o #{options}"
|
152
|
+
else
|
153
|
+
command = "ps ax -o #{options}"
|
154
|
+
end
|
155
|
+
|
156
|
+
lines = `#{command}`.to_a
|
157
|
+
|
158
|
+
lines[1..-1].map do |line|
|
159
|
+
fields = line.split
|
160
|
+
if fields.size > PS_FIELDS.size
|
161
|
+
fields = fields[0..PS_FIELDS.size-2] + [fields[PS_FIELDS.size-1..-1].join(" ")]
|
162
|
+
end
|
163
|
+
|
164
|
+
fields = PS_FIELDS.zip(fields).map { |name, value| value.send(PS_FIELD_TRANSFORMS[name]) }
|
165
|
+
|
166
|
+
ProcessInfo.new(*fields)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
#-----------------------------------------------------------------------------
|
171
|
+
|
172
|
+
#
|
173
|
+
# Return the current operating system: Darwin, Linux, or Windows.
|
174
|
+
#
|
175
|
+
def self.os
|
176
|
+
return @os if @os
|
177
|
+
|
178
|
+
require 'rbconfig'
|
179
|
+
host_os = Config::CONFIG['host_os']
|
180
|
+
case host_os
|
181
|
+
when /darwin/
|
182
|
+
@os = "Darwin"
|
183
|
+
when /linux/
|
184
|
+
@os = "Linux"
|
185
|
+
when /mingw|mswin/
|
186
|
+
@os = 'Windows'
|
187
|
+
else
|
188
|
+
raise "Unknown OS: #{host_os.inspect}"
|
189
|
+
end
|
190
|
+
|
191
|
+
@os
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# Is this Linux?
|
196
|
+
#
|
197
|
+
def self.linux?
|
198
|
+
os == "Linux"
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Is this Windows?
|
203
|
+
#
|
204
|
+
def self.windows?
|
205
|
+
os == "Windows"
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# Is this Darwin?
|
210
|
+
#
|
211
|
+
def self.darwin?
|
212
|
+
os == "Darwin"
|
213
|
+
end
|
214
|
+
|
215
|
+
#
|
216
|
+
# Is this a Mac? (aka. Darwin?)
|
217
|
+
#
|
218
|
+
def self.mac?; darwin?; end
|
219
|
+
|
220
|
+
#-----------------------------------------------------------------------------
|
221
|
+
|
222
|
+
#
|
223
|
+
# Trap signals!
|
224
|
+
#
|
225
|
+
# usage: trap("EXIT", "HUP", "ETC", :ignore=>["VTALRM"]) { |signal| puts "Got #{signal}!" }
|
226
|
+
# (Execute Signal.list to see what's available.)
|
227
|
+
#
|
228
|
+
# No paramters defaults to all signals except VTALRM, CHLD, CLD, and EXIT.
|
229
|
+
#
|
230
|
+
def self.trap(*args, &block)
|
231
|
+
options = if args.last.is_a?(Hash) then args.pop else Hash.new end
|
232
|
+
args = [args].flatten
|
233
|
+
signals = if args.any? then args else Signal.list.keys end
|
234
|
+
|
235
|
+
ignore = %w[ VTALRM CHLD CLD EXIT ] unless ignore = options[:ignore]
|
236
|
+
ignore = [ignore] unless ignore.is_a? Array
|
237
|
+
|
238
|
+
signals = signals - ignore
|
239
|
+
|
240
|
+
signals.each do |signal|
|
241
|
+
p [:sig, signal]
|
242
|
+
Signal.trap(signal) { yield signal }
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
#-----------------------------------------------------------------------------
|
247
|
+
|
248
|
+
#
|
249
|
+
# A metaprogramming helper that allows you to write platform-specific methods
|
250
|
+
# which the user can call with one name. Here's how to use it:
|
251
|
+
#
|
252
|
+
# Define these methods:
|
253
|
+
# reboot_linux, reboot_darwin, reboot_windows
|
254
|
+
#
|
255
|
+
# Call the magic method:
|
256
|
+
# cross_platform_method(:reboot)
|
257
|
+
#
|
258
|
+
# Now the user can execute "reboot" on any platform!
|
259
|
+
#
|
260
|
+
# (Note: If you didn't create a method for a specific platform, then you'll get
|
261
|
+
# NoMethodError exception when the "reboot" method is called on that platform.)
|
262
|
+
#
|
263
|
+
def self.cross_platform_method(name)
|
264
|
+
platform_method_name = "#{name}_#{os.downcase}"
|
265
|
+
metaclass.instance_eval do
|
266
|
+
define_method(name) do |*args|
|
267
|
+
begin
|
268
|
+
self.send(platform_method_name, *args)
|
269
|
+
rescue NoMethodError
|
270
|
+
raise NotImplementedError.new("#{name} is not yet supported on this platform.")
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
#-----------------------------------------------------------------------------
|
277
|
+
|
278
|
+
cross_platform_method :interfaces
|
279
|
+
|
280
|
+
#
|
281
|
+
# Darwin: Return a hash of (device, IP address) pairs.
|
282
|
+
#
|
283
|
+
# eg: {"en0"=>"192.168.1.101"}
|
284
|
+
#
|
285
|
+
def self.interfaces_darwin
|
286
|
+
sections = `ifconfig`.split(/^(?=[^\t])/)
|
287
|
+
sections_with_relevant_ip = sections.select {|i| i =~ /inet/ }
|
288
|
+
|
289
|
+
device_ips = {}
|
290
|
+
sections_with_relevant_ip.each do |section|
|
291
|
+
device = section[/[^:]+/]
|
292
|
+
ip = section[/inet ([^ ]+)/, 1]
|
293
|
+
device_ips[device] = ip
|
294
|
+
end
|
295
|
+
|
296
|
+
device_ips
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# Linux: Return a hash of (device, IP address) pairs.
|
301
|
+
#
|
302
|
+
# eg: {"eth0"=>"192.168.1.101"}
|
303
|
+
#
|
304
|
+
def self.interfaces_linux
|
305
|
+
sections = `ifconfig`.split(/^(?=Link encap:Ethernet)/)
|
306
|
+
sections_with_relevant_ip = sections.select {|i| i =~ /inet/ }
|
307
|
+
|
308
|
+
device_ips = {}
|
309
|
+
sections_with_relevant_ip.each do |section|
|
310
|
+
device = section[/([\w\d]+)\s+Link encap:Ethernet/, 1]
|
311
|
+
ip = section[/inet addr:([^\s]+)/, 1]
|
312
|
+
device_ips[device] = ip
|
313
|
+
end
|
314
|
+
|
315
|
+
device_ips
|
316
|
+
end
|
317
|
+
|
318
|
+
#-----------------------------------------------------------------------------
|
319
|
+
|
320
|
+
cross_platform_method :browser_open
|
321
|
+
|
322
|
+
#
|
323
|
+
# Linux: Open an URL in the default browser (using "gnome-open").
|
324
|
+
#
|
325
|
+
def browser_open_linux(url)
|
326
|
+
system("gnome-open", url)
|
327
|
+
end
|
328
|
+
|
329
|
+
#
|
330
|
+
# Darwin: Open the webpage in a new chrome tab.
|
331
|
+
#
|
332
|
+
def browser_open_darwin(url)
|
333
|
+
system("open", "-a", "chrome", url)
|
334
|
+
end
|
335
|
+
|
336
|
+
#-----------------------------------------------------------------------------
|
337
|
+
|
338
|
+
cross_platform_method :memstat
|
339
|
+
|
340
|
+
def self.memstat_linux
|
341
|
+
#$ free
|
342
|
+
# total used free shared buffers cached
|
343
|
+
#Mem: 4124380 3388548 735832 0 561888 968004
|
344
|
+
#-/+ buffers/cache: 1858656 2265724
|
345
|
+
#Swap: 2104504 166724 1937780
|
346
|
+
|
347
|
+
#$ vmstat
|
348
|
+
raise "Not implemented"
|
349
|
+
end
|
350
|
+
|
351
|
+
def self.memstat_darwin
|
352
|
+
#$ vm_stat
|
353
|
+
#Mach Virtual Memory Statistics: (page size of 4096 bytes)
|
354
|
+
#Pages free: 198367.
|
355
|
+
#Pages active: 109319.
|
356
|
+
#Pages inactive: 61946.
|
357
|
+
#Pages speculative: 18674.
|
358
|
+
#Pages wired down: 70207.
|
359
|
+
#"Translation faults": 158788687.
|
360
|
+
#Pages copy-on-write: 17206973.
|
361
|
+
#Pages zero filled: 54584525.
|
362
|
+
#Pages reactivated: 8768.
|
363
|
+
#Pageins: 176076.
|
364
|
+
#Pageouts: 3757.
|
365
|
+
#Object cache: 16 hits of 255782 lookups (0% hit rate)
|
366
|
+
|
367
|
+
#$ iostat
|
368
|
+
raise "Not implemented"
|
369
|
+
end
|
370
|
+
|
371
|
+
def self.temperatures
|
372
|
+
#/Applications/Utilities/TemperatureMonitor.app/Contents/MacOS/tempmonitor -a -l
|
373
|
+
#CPU Core 1: 28 C
|
374
|
+
#CPU Core 2: 28 C
|
375
|
+
#SMART Disk Hitachi HTS543216L9SA02 (090831FBE200VCGH3D5F): 40 C
|
376
|
+
#SMC CPU A DIODE: 41 C
|
377
|
+
#SMC CPU A HEAT SINK: 42 C
|
378
|
+
#SMC DRIVE BAY 1: 41 C
|
379
|
+
#SMC NORTHBRIDGE POS 1: 46 C
|
380
|
+
#SMC WLAN CARD: 45 C
|
381
|
+
raise "Not implemented"
|
382
|
+
end
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
if $0 == __FILE__
|
387
|
+
require 'pp'
|
388
|
+
procs = Sys.ps
|
389
|
+
p [:processes, procs.size]
|
390
|
+
# some = procs[0..3]
|
391
|
+
# some.each{|ps| pp ps}
|
392
|
+
# some.first.kill!
|
393
|
+
# pp some.first.to_hash
|
394
|
+
# p [:total_cpu, procs.map{|ps| ps.pcpu}.sum]
|
395
|
+
# p [:total_mem, procs.map{|ps| ps.pmem}.sum]
|
396
|
+
|
397
|
+
pp Sys.interfaces
|
398
|
+
end
|
data/spec/basetypes_spec.rb
CHANGED
@@ -7,10 +7,10 @@ describe Object do
|
|
7
7
|
defined?(Enum).should_not == nil
|
8
8
|
end
|
9
9
|
|
10
|
-
it "enums" do
|
11
|
-
|
12
|
-
|
13
|
-
end
|
10
|
+
#it "enums" do
|
11
|
+
# generator = enum { |y| y.yield 1 }
|
12
|
+
# generator.next.should == 1
|
13
|
+
#end
|
14
14
|
|
15
15
|
it "in?" do
|
16
16
|
5.in?([1,2,3,4,5,6]).should == true
|
data/spec/sys_spec.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'epitools/sys'
|
2
|
+
|
3
|
+
describe Sys::ProcessInfo do
|
4
|
+
|
5
|
+
specify "checks OS" do
|
6
|
+
proc { Sys.os }.should_not raise_error
|
7
|
+
proc { Sys.linux? }.should_not raise_error
|
8
|
+
proc { Sys.mac? }.should_not raise_error
|
9
|
+
proc { Sys.darwin? }.should_not raise_error
|
10
|
+
|
11
|
+
%w[Linux Windows Darwin].include?(Sys.os).should == true
|
12
|
+
|
13
|
+
( (Sys.linux? and not Sys.mac?) or (Sys.mac? and not Sys.linux?) ).should == true
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
specify "list all processes" do
|
18
|
+
# procs = Sys.ps
|
19
|
+
#
|
20
|
+
# procs.first.state.is_a?(Array).should == true
|
21
|
+
#
|
22
|
+
# pids = procs.map{ |process| process.pid }
|
23
|
+
#
|
24
|
+
# p2s = Hash[ *Sys.ps(*pids).map { |process| [process.pid, process] }.flatten ]
|
25
|
+
# matches = 0
|
26
|
+
# procs.each do |p1|
|
27
|
+
# if p2 = p2s[p1.pid]
|
28
|
+
# matches += 1
|
29
|
+
# p1.command.should == p2.command
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# matches.should > 1
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
specify "refresh processes" do
|
38
|
+
|
39
|
+
# STDOUT.sync = true
|
40
|
+
#
|
41
|
+
# procs = Sys.ps
|
42
|
+
# procs.shuffle!
|
43
|
+
# procs.each do |process|
|
44
|
+
# proc do
|
45
|
+
# begin
|
46
|
+
# process.refresh
|
47
|
+
# print "."
|
48
|
+
# rescue Sys::ProcessNotFound
|
49
|
+
# end
|
50
|
+
# end.should_not raise_error
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# puts
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
specify "cross-platform method" do
|
59
|
+
Sys.interfaces.should_not be_nil
|
60
|
+
Sys.cross_platform_method(:cross_platform_test)
|
61
|
+
proc{ Sys.cross_platform_test }.should raise_error
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epitools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- epitron
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-09-
|
18
|
+
date: 2010-09-28 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -53,6 +53,9 @@ files:
|
|
53
53
|
- epitools.gemspec
|
54
54
|
- lib/epitools.rb
|
55
55
|
- lib/epitools/basetypes.rb
|
56
|
+
- lib/epitools/browser.rb
|
57
|
+
- lib/epitools/browser/browser_cache.rb
|
58
|
+
- lib/epitools/browser/mechanize_progressbar.rb
|
56
59
|
- lib/epitools/hexdump.rb
|
57
60
|
- lib/epitools/http.rb
|
58
61
|
- lib/epitools/lcs.rb
|
@@ -60,12 +63,15 @@ files:
|
|
60
63
|
- lib/epitools/niceprint.rb
|
61
64
|
- lib/epitools/permutations.rb
|
62
65
|
- lib/epitools/pretty_backtrace.rb
|
66
|
+
- lib/epitools/progressbar.rb
|
63
67
|
- lib/epitools/rails.rb
|
64
68
|
- lib/epitools/rash.rb
|
65
69
|
- lib/epitools/ratio.rb
|
66
70
|
- lib/epitools/string_to_proc.rb
|
71
|
+
- lib/epitools/sys.rb
|
67
72
|
- lib/epitools/zopen.rb
|
68
73
|
- spec/basetypes_spec.rb
|
74
|
+
- spec/browser_spec.rb
|
69
75
|
- spec/lcs_spec.rb
|
70
76
|
- spec/metaclass_spec.rb
|
71
77
|
- spec/permutations_spec.rb
|
@@ -73,6 +79,7 @@ files:
|
|
73
79
|
- spec/ratio_spec.rb
|
74
80
|
- spec/spec.opts
|
75
81
|
- spec/spec_helper.rb
|
82
|
+
- spec/sys_spec.rb
|
76
83
|
- spec/zopen_spec.rb
|
77
84
|
has_rdoc: true
|
78
85
|
homepage: http://github.com/epitron/epitools
|
@@ -109,11 +116,13 @@ signing_key:
|
|
109
116
|
specification_version: 3
|
110
117
|
summary: NOT UTILS... METILS!
|
111
118
|
test_files:
|
119
|
+
- spec/sys_spec.rb
|
120
|
+
- spec/permutations_spec.rb
|
112
121
|
- spec/rash_spec.rb
|
113
|
-
- spec/
|
114
|
-
- spec/
|
115
|
-
- spec/basetypes_spec.rb
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
- spec/browser_spec.rb
|
116
124
|
- spec/lcs_spec.rb
|
125
|
+
- spec/basetypes_spec.rb
|
126
|
+
- spec/ratio_spec.rb
|
117
127
|
- spec/zopen_spec.rb
|
118
|
-
- spec/
|
119
|
-
- spec/spec_helper.rb
|
128
|
+
- spec/metaclass_spec.rb
|