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