stella 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/{README.txt → README.textile} +63 -40
- data/Rakefile +7 -5
- data/bin/stella +1 -1
- data/bin/stella.bat +12 -0
- data/lib/pcaplet.rb +180 -0
- data/lib/stella/adapter/ab.rb +57 -33
- data/lib/stella/adapter/base.rb +11 -1
- data/lib/stella/adapter/httperf.rb +13 -10
- data/lib/stella/adapter/pcap_watcher.rb +221 -0
- data/lib/stella/adapter/proxy_watcher.rb +76 -0
- data/lib/stella/adapter/siege.rb +28 -11
- data/lib/stella/cli/agents.rb +2 -2
- data/lib/stella/cli/base.rb +37 -1
- data/lib/stella/cli/localtest.rb +1 -2
- data/lib/stella/cli/sysinfo.rb +17 -0
- data/lib/stella/cli/watch.rb +278 -0
- data/lib/stella/cli.rb +23 -11
- data/lib/stella/command/base.rb +1 -10
- data/lib/stella/command/localtest.rb +43 -23
- data/lib/stella/data/domain.rb +75 -0
- data/lib/stella/data/http.rb +124 -0
- data/lib/stella/logger.rb +16 -5
- data/lib/stella/storable.rb +4 -2
- data/lib/stella/support.rb +71 -0
- data/lib/stella/sysinfo.rb +247 -0
- data/lib/stella/test/base.rb +5 -1
- data/lib/stella/test/definition.rb +1 -1
- data/lib/stella/test/run/summary.rb +14 -4
- data/lib/stella/text/resource.rb +0 -1
- data/lib/stella.rb +28 -10
- data/lib/utils/domainutil.rb +47 -0
- data/lib/utils/fileutil.rb +22 -3
- data/lib/utils/httputil.rb +184 -128
- data/lib/utils/mathutil.rb +20 -7
- data/lib/win32/Console/ANSI.rb +305 -0
- data/lib/win32/Console.rb +970 -0
- data/spec/show-agents_spec.rb +0 -0
- data/support/kvm.h +91 -0
- data/support/ruby-pcap-takuma-notes.txt +19 -0
- data/support/ruby-pcap-takuma-patch.txt +30 -0
- data/support/text/en.yaml +26 -3
- data/vendor/frylock/README.textile +72 -0
- data/vendor/frylock/bin/example +170 -0
- data/vendor/frylock/frylock.gemspec +18 -0
- data/vendor/frylock/lib/frylock/exceptions.rb +24 -0
- data/vendor/frylock/lib/frylock.rb +232 -0
- data/vendor/frylock/test/command_test.rb +33 -0
- data/vendor/hitimes-0.4.0/HISTORY +28 -0
- data/vendor/hitimes-0.4.0/LICENSE.txt +19 -0
- data/vendor/hitimes-0.4.0/README +80 -0
- data/vendor/hitimes-0.4.0/Rakefile +63 -0
- data/vendor/hitimes-0.4.0/examples/benchmarks.rb +86 -0
- data/vendor/hitimes-0.4.0/examples/stats.rb +29 -0
- data/vendor/hitimes-0.4.0/ext/extconf.rb +15 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_ext.c +21 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_instant_clock_gettime.c +20 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_instant_osx.c +16 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_instant_windows.c +27 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_interval.c +340 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_interval.h +73 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_stats.c +242 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_stats.h +30 -0
- data/vendor/hitimes-0.4.0/ext/rbconfig-mingw.rb +178 -0
- data/vendor/hitimes-0.4.0/ext/rbconfig.rb +178 -0
- data/vendor/hitimes-0.4.0/gemspec.rb +54 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/mutexed_stats.rb +23 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/paths.rb +54 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/stats.rb +29 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/timer.rb +223 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/version.rb +42 -0
- data/vendor/hitimes-0.4.0/lib/hitimes.rb +24 -0
- data/vendor/hitimes-0.4.0/spec/interval_spec.rb +115 -0
- data/vendor/hitimes-0.4.0/spec/mutex_stats_spec.rb +34 -0
- data/vendor/hitimes-0.4.0/spec/paths_spec.rb +14 -0
- data/vendor/hitimes-0.4.0/spec/spec_helper.rb +6 -0
- data/vendor/hitimes-0.4.0/spec/stats_spec.rb +72 -0
- data/vendor/hitimes-0.4.0/spec/timer_spec.rb +105 -0
- data/vendor/hitimes-0.4.0/spec/version_spec.rb +27 -0
- data/vendor/hitimes-0.4.0/tasks/announce.rake +39 -0
- data/vendor/hitimes-0.4.0/tasks/config.rb +107 -0
- data/vendor/hitimes-0.4.0/tasks/distribution.rake +53 -0
- data/vendor/hitimes-0.4.0/tasks/documentation.rake +33 -0
- data/vendor/hitimes-0.4.0/tasks/extension.rake +64 -0
- data/vendor/hitimes-0.4.0/tasks/rspec.rake +31 -0
- data/vendor/hitimes-0.4.0/tasks/rubyforge.rake +52 -0
- data/vendor/hitimes-0.4.0/tasks/utils.rb +80 -0
- data/vendor/useragent/lib/user_agent.rb +1 -1
- metadata +87 -8
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
require 'net/dns/packet'
|
3
|
+
|
4
|
+
module DomainUtil
|
5
|
+
|
6
|
+
def DomainUtil.parse_domain_request(data=[])
|
7
|
+
return unless data && !data.empty?
|
8
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
9
|
+
data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
|
10
|
+
|
11
|
+
dns_data = Net::DNS::Packet.parse( data.join($/) )
|
12
|
+
return unless dns_data.header.query?
|
13
|
+
domain_name = dns_data.question[0].qName
|
14
|
+
return dns_data, domain_name, dns_data.header
|
15
|
+
end
|
16
|
+
|
17
|
+
def DomainUtil.parse_domain_response(data=[])
|
18
|
+
return unless data && !data.empty?
|
19
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
20
|
+
data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
|
21
|
+
|
22
|
+
# This is the heavy lifting.
|
23
|
+
dns_data = Net::DNS::Packet.parse( data.join($/) )
|
24
|
+
|
25
|
+
# We don't want queries or empty answers
|
26
|
+
return if dns_data.header.query? || dns_data.answer.nil? || dns_data.answer.empty?
|
27
|
+
|
28
|
+
domain_name = dns_data.answer[0].name
|
29
|
+
|
30
|
+
# Empty the lists if they are already populated
|
31
|
+
addresses = []
|
32
|
+
cnames = []
|
33
|
+
|
34
|
+
# Store the CNAMEs associated to this domain. Can be empty.
|
35
|
+
dns_data.each_cname do |cname|
|
36
|
+
cnames << cname.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
# Store the IP address for this domain. If empty, the lookup was unsuccessful.
|
40
|
+
dns_data.each_address do |ip|
|
41
|
+
addresses << ip.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
return dns_data, domain_name, dns_data.header, addresses, cnames
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/utils/fileutil.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'fileutils'
|
2
2
|
|
3
3
|
module FileUtil
|
4
4
|
|
@@ -50,10 +50,29 @@ module FileUtil
|
|
50
50
|
File.chmod(0600, path)
|
51
51
|
end
|
52
52
|
|
53
|
-
def FileUtil.
|
53
|
+
def FileUtil.create_file(filepath, perm='w', file_perms=nil, force=false)
|
54
|
+
raise Exception.new("File #{filepath} already exists!") if File.exists?(filepath) && !force
|
55
|
+
|
56
|
+
newfile = File.new(filepath, perm)
|
57
|
+
begin
|
58
|
+
if file_perms && File.exists?(file_perms)
|
59
|
+
File.chown(File.stat(file_perms).uid.to_i, File.stat(file_perms).gid.to_i, filepath)
|
60
|
+
end
|
61
|
+
rescue NotImplementedError => ex
|
62
|
+
end
|
63
|
+
|
64
|
+
newfile
|
65
|
+
end
|
66
|
+
|
67
|
+
def FileUtil.create_dir(dirpath, dir_perms=nil)
|
54
68
|
return if File.directory?(dirpath)
|
55
69
|
|
56
70
|
#STDERR.puts "Creating #{ dirpath }"
|
57
|
-
|
71
|
+
FileUtils.makedirs(dirpath)
|
72
|
+
|
73
|
+
if dir_perms && File.exists?(dir_perms)
|
74
|
+
File.chown(File.stat(dir_perms).uid.to_i, File.stat(dir_perms).gid.to_i, dirpath)
|
75
|
+
end
|
76
|
+
|
58
77
|
end
|
59
78
|
end
|
data/lib/utils/httputil.rb
CHANGED
@@ -1,172 +1,210 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
require 'uri'
|
3
3
|
require 'timeout'
|
4
|
+
require 'net/http'
|
4
5
|
|
5
6
|
module HTTPUtil
|
6
7
|
VALID_METHODS = %w{GET HEAD POST PUT DELETE}
|
7
8
|
@@timeout = 20
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
# Takes a string. See WEBrick::parse_header(string).
|
11
|
+
def HTTPUtil.parse_header(raw)
|
12
|
+
header = Hash.new([].freeze)
|
13
|
+
raw.each_line do |line|
|
14
|
+
case line
|
15
|
+
when /\A(.+?):\s+(.+)\z/om
|
16
|
+
name, value = $1, $2
|
17
|
+
name = name.tr('-', '_').to_sym
|
18
|
+
value.strip!
|
19
|
+
|
20
|
+
header[name] = [] unless header.has_key?(name)
|
21
|
+
header[name] << value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
header
|
15
25
|
end
|
16
26
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#STDERR.puts " AFTER: " << uri_str
|
27
|
+
# Takes a string or array. See parse_header_body for further info.
|
28
|
+
# Returns +method+, +http_version+, +uri+, +header+, +body+
|
29
|
+
def HTTPUtil.parse_http_request(data, host=:unknown, port=80)
|
30
|
+
return unless data && !data.empty?
|
31
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
32
|
+
data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
|
33
|
+
request_line = data.shift # i.e. GET /path HTTP/1.1
|
34
|
+
method, path, http_version = nil
|
26
35
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
if (scheme)
|
36
|
-
uri_clean << uri.scheme.to_s + '://'
|
36
|
+
# With WEBrick and other proxies, the entire URI is included in HTTP requests.
|
37
|
+
# i.e. GET http://stellaaahhhh.com/streetcar.png HTTP/1.1
|
38
|
+
# The parser is expecting just the absolute path.
|
39
|
+
if request_line =~ /^(\S+)\s+(http:\/\/.+)\s+(HTTP.+)?/mo
|
40
|
+
uri = URI.parse($2)
|
41
|
+
request_line = "#{$1} #{uri.request_uri} #{$3}"
|
42
|
+
host = uri.host
|
37
43
|
end
|
38
44
|
|
45
|
+
if request_line =~ /^(\S+)\s+(\S+)(?:\s+HTTP\/(\d+\.\d+))?/mo
|
46
|
+
method = $1
|
47
|
+
http_version = $3 # Comes before $2 b/c the split resets the numbered vars
|
48
|
+
path, query_string = $2.split('?')
|
39
49
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
|
62
|
-
if (!uri.query.nil? && !uri.path.empty?)
|
63
|
-
uri_clean << "?" << uri.query
|
50
|
+
# We only process the header and body data when we know we're
|
51
|
+
# starting from the beginning of a request string. We don't
|
52
|
+
# want no partials.
|
53
|
+
header, body = HTTPUtil.parse_header_body(data)
|
54
|
+
query = HTTPUtil.parse_query(method, query_string)
|
55
|
+
|
56
|
+
|
57
|
+
# TODO: Parse username/password
|
58
|
+
uri = URI::HTTP.build({
|
59
|
+
:scheme => 'http',
|
60
|
+
:host => header[:Host][0] || host.to_s,
|
61
|
+
:port => port,
|
62
|
+
:path => path,
|
63
|
+
:query => query_string
|
64
|
+
})
|
65
|
+
|
66
|
+
else
|
67
|
+
rl = request_line.sub(/\x0d?\x0a\z/o, '')
|
68
|
+
raise "Bad Request-Line `#{rl}'."
|
64
69
|
end
|
65
70
|
|
66
|
-
|
67
|
-
#STDERR.puts "OUT: " << uri_clean
|
68
|
-
|
69
|
-
uri_clean
|
71
|
+
return method, http_version, uri, header, body
|
70
72
|
end
|
71
73
|
|
72
|
-
def HTTPUtil.fetch_content(uri, limit = 10)
|
73
|
-
res = self.fetch(uri,limit)
|
74
|
-
return (res) ? res.body : ""
|
75
|
-
end
|
76
74
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
# Takes a string or array. See parse_header_body for further info.
|
76
|
+
# Returns +status+, +http_version+, +message+, +header+, +body+
|
77
|
+
def HTTPUtil.parse_http_response(data=[])
|
78
|
+
return unless data && !data.empty?
|
79
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
80
|
+
data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
|
81
|
+
status_line = data.shift # ie. HTTP/1.1 200 OK
|
82
|
+
http_version, status, message = nil
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
if status_line =~ /^HTTP\/(\d.+?)(\s+(\d\d\d)\s+(.+))?$/mo
|
85
|
+
http_version = $1
|
86
|
+
status = $2
|
87
|
+
message = $4
|
87
88
|
|
89
|
+
header, body, query = HTTPUtil.parse_header_body(data)
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
-
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
92
|
-
else
|
93
|
-
STDERR.puts "Not found: " << uri.to_s
|
94
|
-
end
|
95
|
-
end
|
96
|
-
rescue TimeoutError
|
97
|
-
STDERR.puts "Net::HTTP timed out for " << uri.to_s
|
98
|
-
return
|
99
|
-
rescue => ex
|
100
|
-
STDERR.puts "Error: #{ex.message}"
|
91
|
+
else
|
92
|
+
raise "Bad Response-Line `#{status_line}'."
|
101
93
|
end
|
102
|
-
|
94
|
+
|
95
|
+
return status, http_version, message, header, body
|
103
96
|
end
|
104
97
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
98
|
+
# Process everything after the first line of an HTTP request or response:
|
99
|
+
# GET / HTTP/1.1
|
100
|
+
# HTTP/1.1 200 OK
|
101
|
+
# etc...
|
102
|
+
# Used by parse_http_request and parse_http_response but can be used separately.
|
103
|
+
# Takes a string or array of strings. A string should be formatted like an HTTP
|
104
|
+
# request or response. If a body is present it should be separated by two newlines.
|
105
|
+
# An array of string should contain an empty or nil element between the header
|
106
|
+
# and body content. This will happen naturally if the raw lines were split by
|
107
|
+
# a single line terminator. (i.e. /\n/ rather than /\n\n/)
|
108
|
+
# Returns header (hash), body (string)
|
109
|
+
def HTTPUtil.parse_header_body(data=[])
|
110
|
+
header, body = {}, nil
|
111
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
112
|
+
data.shift while (data[0].nil? || data[0].empty?) # Remove leading empties
|
109
113
|
|
110
|
-
|
114
|
+
return header, body unless data && !data.empty?
|
111
115
|
|
112
|
-
|
113
|
-
timeout(@@timeout) do
|
114
|
-
response = Net::HTTP.post_form(uri, params)
|
115
|
-
|
116
|
-
case response
|
117
|
-
when Net::HTTPSuccess then response
|
118
|
-
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
119
|
-
else
|
120
|
-
STDERR.puts "Error for " << uri.to_s
|
121
|
-
STDERR.puts response.body
|
122
|
-
end
|
123
|
-
end
|
124
|
-
rescue TimeoutError
|
125
|
-
STDERR.puts "Net::HTTP timed out for " << uri.to_s
|
126
|
-
return
|
127
|
-
end
|
116
|
+
#puts data.to_yaml
|
128
117
|
|
118
|
+
# Skip that first line if it exists
|
119
|
+
data.shift if data[0].match(/\AHTTP|GET|POST|DELETE|PUT|HEAD/mo)
|
129
120
|
|
121
|
+
header_lines = []
|
122
|
+
header_lines << data.shift while (!data[0].nil? && !data[0].empty?)
|
123
|
+
header = HTTPUtil::parse_header(header_lines.join($/))
|
130
124
|
|
125
|
+
# We omit the blank line that delimits the header from the body
|
126
|
+
body = data[1..-1].join($/) unless data.empty?
|
127
|
+
|
128
|
+
return header, body
|
131
129
|
end
|
132
130
|
|
133
|
-
def HTTPUtil.
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
131
|
+
def HTTPUtil.parse_query(request_method, query_string, content_type='', body='')
|
132
|
+
query = Hash.new([].freeze)
|
133
|
+
|
134
|
+
if request_method == "GET" || request_method == "HEAD"
|
135
|
+
query = HTTPUtil::parse_query_from_string(query_string)
|
136
|
+
elsif content_type =~ /^application\/x-www-form-urlencoded/
|
137
|
+
query = HTTPUtil::parse_query_from_string(body)
|
138
|
+
elsif content_type =~ /^multipart\/form-data; boundary=(.+)/
|
139
|
+
boundary = $1.tr('"', '')
|
140
|
+
query = HTTPUtil::parse_form_data(body, boundary)
|
140
141
|
else
|
141
|
-
|
142
|
+
query
|
142
143
|
end
|
143
|
-
|
144
|
-
|
145
|
-
STDERR.puts "Problem: " + e.message
|
146
|
-
false
|
147
|
-
end
|
148
|
-
|
144
|
+
|
145
|
+
query
|
149
146
|
end
|
150
147
|
|
151
|
-
def HTTPUtil.
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
148
|
+
def HTTPUtil.validate_method(meth='GET')
|
149
|
+
(VALID_METHODS.member? meth.upcase) ? meth : VALID_METHODS[0]
|
150
|
+
end
|
151
|
+
|
152
|
+
# Parses a query string by breaking it up at the '&'
|
153
|
+
# and ';' characters. You can also use this to parse
|
154
|
+
# cookies by changing the characters used in the second
|
155
|
+
# parameter (which defaults to '&;'.
|
156
|
+
# Stolen from Mongrel
|
157
|
+
def HTTPUtil.parse_query_from_string(qs, d = '&;')
|
158
|
+
params = {}
|
159
|
+
(qs||'').split(/[#{d}] */n).inject(params) { |h,p|
|
160
|
+
k, v=unescape(p).split('=',2)
|
161
|
+
next unless k
|
162
|
+
k = k.tr('-', '_').to_sym
|
163
|
+
if cur = params[k]
|
164
|
+
if cur.class == Array
|
165
|
+
params[k] << v
|
166
|
+
else
|
167
|
+
params[k] = [cur, v]
|
168
|
+
end
|
158
169
|
else
|
159
|
-
params[
|
170
|
+
params[k] = v
|
160
171
|
end
|
161
|
-
|
172
|
+
}
|
162
173
|
|
163
|
-
params
|
174
|
+
return params
|
164
175
|
end
|
176
|
+
|
165
177
|
|
166
|
-
|
167
|
-
|
178
|
+
|
179
|
+
# Based on WEBrick::HTTPutils::parse_form_data
|
180
|
+
def HTTPUtil.parse_form_data(io, boundary)
|
181
|
+
boundary_regexp = /\A--#{boundary}(--)?#{$/}\z/
|
182
|
+
form_data = Hash.new
|
183
|
+
return form_data unless io
|
184
|
+
data = nil
|
185
|
+
io.each_line{|line|
|
186
|
+
if boundary_regexp =~ line
|
187
|
+
if data
|
188
|
+
data.chop!
|
189
|
+
key = data.name.tr('-', '_').to_sym
|
190
|
+
if form_data.has_key?(key)
|
191
|
+
form_data[key].append_data(data)
|
192
|
+
else
|
193
|
+
form_data[key] = data
|
194
|
+
end
|
195
|
+
end
|
196
|
+
data = FormData.new
|
197
|
+
next
|
198
|
+
else
|
199
|
+
if data
|
200
|
+
data << line
|
201
|
+
end
|
202
|
+
end
|
203
|
+
}
|
204
|
+
return form_data
|
168
205
|
end
|
169
206
|
|
207
|
+
|
170
208
|
# Extend the basic query string parser provided by the cgi module.
|
171
209
|
# converts single valued params (the most common case) to
|
172
210
|
# objects instead of arrays
|
@@ -178,7 +216,6 @@ require 'timeout'
|
|
178
216
|
# hash of parameters, contains arrays for multivalued parameters
|
179
217
|
# (multiselect, checkboxes , etc)
|
180
218
|
# If no query string is provided (nil or "") returns an empty hash.
|
181
|
-
|
182
219
|
def HTTPUtil.query_to_hash(query_string)
|
183
220
|
return {} unless query_string
|
184
221
|
|
@@ -205,6 +242,25 @@ require 'timeout'
|
|
205
242
|
#return pairs.join(";")
|
206
243
|
end
|
207
244
|
|
245
|
+
|
246
|
+
|
247
|
+
# Performs URI escaping so that you can construct proper
|
248
|
+
# query strings faster. Use this rather than the cgi.rb
|
249
|
+
# version since it's faster. (Stolen from Mongrel/Camping).
|
250
|
+
def HTTPUtil.escape(s)
|
251
|
+
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
252
|
+
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
253
|
+
}.tr(' ', '+')
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
# Unescapes a URI escaped string. (Stolen from Mongrel/Camping).
|
258
|
+
def HTTPUtil.unescape(s)
|
259
|
+
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
|
260
|
+
[$1.delete('%')].pack('H*')
|
261
|
+
}
|
262
|
+
end
|
263
|
+
|
208
264
|
|
209
265
|
end
|
210
266
|
|
data/lib/utils/mathutil.rb
CHANGED
@@ -6,6 +6,7 @@ module MathUtil
|
|
6
6
|
n = 0
|
7
7
|
mean = 0.0
|
8
8
|
s = 0.0
|
9
|
+
|
9
10
|
population.each { |x|
|
10
11
|
n = n + 1
|
11
12
|
delta = (x - mean).to_f
|
@@ -13,7 +14,9 @@ module MathUtil
|
|
13
14
|
s = (s + delta * (x - mean)).to_f
|
14
15
|
}
|
15
16
|
|
16
|
-
|
17
|
+
s / n
|
18
|
+
rescue => ex
|
19
|
+
0.0
|
17
20
|
end
|
18
21
|
|
19
22
|
# calculate the standard deviation of a population
|
@@ -43,7 +46,8 @@ module Enumerable
|
|
43
46
|
# Sum of all the elements of the Enumerable
|
44
47
|
|
45
48
|
def sum
|
46
|
-
return
|
49
|
+
return 0 if !self || self.empty?
|
50
|
+
self.inject(0) { |acc, i| acc.to_f + i.to_f }
|
47
51
|
end
|
48
52
|
|
49
53
|
##
|
@@ -52,7 +56,10 @@ module Enumerable
|
|
52
56
|
# The Enumerable must respond to #length
|
53
57
|
|
54
58
|
def average
|
55
|
-
|
59
|
+
return 0 unless self
|
60
|
+
self.sum / self.length.to_f
|
61
|
+
rescue => ex
|
62
|
+
0.0
|
56
63
|
end
|
57
64
|
|
58
65
|
##
|
@@ -61,9 +68,12 @@ module Enumerable
|
|
61
68
|
# The Enumerable must respond to #length
|
62
69
|
|
63
70
|
def sample_variance
|
64
|
-
|
65
|
-
|
66
|
-
|
71
|
+
return 0 unless self
|
72
|
+
avg = self.average
|
73
|
+
sum = self.sum
|
74
|
+
(1 / self.length.to_f * sum)
|
75
|
+
rescue => ex
|
76
|
+
0.0
|
67
77
|
end
|
68
78
|
|
69
79
|
##
|
@@ -72,7 +82,10 @@ module Enumerable
|
|
72
82
|
# The Enumerable must respond to #length
|
73
83
|
|
74
84
|
def standard_deviation
|
75
|
-
|
85
|
+
return 0 unless self
|
86
|
+
Math.sqrt(self.sample_variance)
|
87
|
+
rescue => ex
|
88
|
+
0.0
|
76
89
|
end
|
77
90
|
|
78
91
|
end
|