stella 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +135 -0
- data/Rakefile +100 -0
- data/bin/stella +12 -0
- data/lib/stella.rb +58 -0
- data/lib/stella/adapter/ab.rb +303 -0
- data/lib/stella/adapter/base.rb +87 -0
- data/lib/stella/adapter/httperf.rb +296 -0
- data/lib/stella/adapter/siege.rb +321 -0
- data/lib/stella/cli.rb +291 -0
- data/lib/stella/cli/agents.rb +73 -0
- data/lib/stella/cli/base.rb +19 -0
- data/lib/stella/cli/language.rb +18 -0
- data/lib/stella/cli/localtest.rb +80 -0
- data/lib/stella/command/base.rb +111 -0
- data/lib/stella/command/localtest.rb +339 -0
- data/lib/stella/logger.rb +63 -0
- data/lib/stella/response.rb +82 -0
- data/lib/stella/storable.rb +116 -0
- data/lib/stella/support.rb +106 -0
- data/lib/stella/test/base.rb +34 -0
- data/lib/stella/test/definition.rb +79 -0
- data/lib/stella/test/run/summary.rb +50 -0
- data/lib/stella/test/summary.rb +82 -0
- data/lib/stella/text.rb +64 -0
- data/lib/stella/text/resource.rb +39 -0
- data/lib/utils/crypto-key.rb +84 -0
- data/lib/utils/escape.rb +302 -0
- data/lib/utils/fileutil.rb +59 -0
- data/lib/utils/httputil.rb +210 -0
- data/lib/utils/mathutil.rb +78 -0
- data/lib/utils/textgraph.rb +267 -0
- data/lib/utils/timerutil.rb +58 -0
- data/support/text/en.yaml +54 -0
- data/support/text/nl.yaml +1 -0
- data/support/useragents.txt +75 -0
- data/vendor/useragent/MIT-LICENSE +20 -0
- data/vendor/useragent/README +21 -0
- data/vendor/useragent/init.rb +1 -0
- data/vendor/useragent/lib/user_agent.rb +83 -0
- data/vendor/useragent/lib/user_agent/browsers.rb +24 -0
- data/vendor/useragent/lib/user_agent/browsers/all.rb +69 -0
- data/vendor/useragent/lib/user_agent/browsers/gecko.rb +43 -0
- data/vendor/useragent/lib/user_agent/browsers/internet_explorer.rb +40 -0
- data/vendor/useragent/lib/user_agent/browsers/opera.rb +49 -0
- data/vendor/useragent/lib/user_agent/browsers/webkit.rb +94 -0
- data/vendor/useragent/lib/user_agent/comparable.rb +25 -0
- data/vendor/useragent/lib/user_agent/operating_systems.rb +19 -0
- data/vendor/useragent/spec/browsers/gecko_user_agent_spec.rb +209 -0
- data/vendor/useragent/spec/browsers/internet_explorer_user_agent_spec.rb +99 -0
- data/vendor/useragent/spec/browsers/opera_user_agent_spec.rb +59 -0
- data/vendor/useragent/spec/browsers/other_user_agent_spec.rb +19 -0
- data/vendor/useragent/spec/browsers/webkit_user_agent_spec.rb +373 -0
- data/vendor/useragent/spec/spec_helper.rb +1 -0
- data/vendor/useragent/spec/user_agent_spec.rb +331 -0
- data/vendor/useragent/useragent.gemspec +12 -0
- metadata +139 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'ftools'
|
2
|
+
|
3
|
+
module FileUtil
|
4
|
+
|
5
|
+
def FileUtil.read_file(path)
|
6
|
+
FileUtil.read_file_to_array(path).join('')
|
7
|
+
end
|
8
|
+
|
9
|
+
def FileUtil.read_file_to_array(path)
|
10
|
+
contents = []
|
11
|
+
return contents unless File.exists?(path)
|
12
|
+
|
13
|
+
open(path, 'r') do |l|
|
14
|
+
contents = l.readlines
|
15
|
+
end
|
16
|
+
|
17
|
+
contents
|
18
|
+
end
|
19
|
+
def FileUtil.read_binary_file(path)
|
20
|
+
contents = ''
|
21
|
+
return contents unless File.exists?(path)
|
22
|
+
|
23
|
+
open(path, 'rb') do |l|
|
24
|
+
while (!l.eof?)
|
25
|
+
contents << l.read(4096)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
contents
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
def FileUtil.write_file(path, content, flush=true)
|
35
|
+
FileUtil.write_or_append_file('w', path, content, flush)
|
36
|
+
end
|
37
|
+
|
38
|
+
def FileUtil.append_file(path, content, flush=true)
|
39
|
+
FileUtil.write_or_append_file('a', path, content, flush)
|
40
|
+
end
|
41
|
+
|
42
|
+
def FileUtil.write_or_append_file(write_or_append, path, content = '', flush = true)
|
43
|
+
#STDERR.puts "Writing to #{ path }..."
|
44
|
+
FileUtil.create_dir(File.dirname(path))
|
45
|
+
|
46
|
+
open(path, write_or_append) do |f|
|
47
|
+
f.puts content
|
48
|
+
f.flush if flush;
|
49
|
+
end
|
50
|
+
File.chmod(0600, path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def FileUtil.create_dir(dirpath)
|
54
|
+
return if File.directory?(dirpath)
|
55
|
+
|
56
|
+
#STDERR.puts "Creating #{ dirpath }"
|
57
|
+
File.makedirs(dirpath)
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module HTTPUtil
|
6
|
+
VALID_METHODS = %w{GET HEAD POST PUT DELETE}
|
7
|
+
@@timeout = 20
|
8
|
+
|
9
|
+
def HTTPUtil.hostname(tmp_uri)
|
10
|
+
return if tmp_uri.empty?
|
11
|
+
uri = URI.parse(tmp_uri) if tmp_uri.is_a? String
|
12
|
+
|
13
|
+
#STDERR.puts "Hostname for #{ uri.port }"
|
14
|
+
uri.host
|
15
|
+
end
|
16
|
+
|
17
|
+
# Normalize all URIs before they are used for anything else
|
18
|
+
def HTTPUtil.normalize(uri_str, scheme = true)
|
19
|
+
|
20
|
+
|
21
|
+
#STDERR.puts " BEFORE: " << uri_str
|
22
|
+
if (!uri_str.index(/^https?:\/\//))
|
23
|
+
uri_str = 'http://' << uri_str
|
24
|
+
end
|
25
|
+
#STDERR.puts " AFTER: " << uri_str
|
26
|
+
|
27
|
+
uri_str.gsub!(/\s/, '%20')
|
28
|
+
|
29
|
+
uri = URI.parse(uri_str)
|
30
|
+
|
31
|
+
uri_clean = ""
|
32
|
+
|
33
|
+
# TODO: use URI.to_s instead of manually creating the string
|
34
|
+
|
35
|
+
if (scheme)
|
36
|
+
uri_clean << uri.scheme.to_s + '://'
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
if (!uri.userinfo.nil?)
|
41
|
+
uri_clean << uri.userinfo.to_s
|
42
|
+
uri_clean << '@'
|
43
|
+
end
|
44
|
+
|
45
|
+
#uri.host.gsub!(/^www\./, '')
|
46
|
+
|
47
|
+
uri_clean << uri.host.to_s
|
48
|
+
|
49
|
+
if (!uri.port.nil? && uri.port != 80 && uri.port != 443)
|
50
|
+
uri_clean << ':' + uri.port.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
if (!uri.path.nil? && !uri.path.empty?)
|
56
|
+
uri_clean << uri.path
|
57
|
+
elsif
|
58
|
+
uri_clean << '/'
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
if (!uri.query.nil? && !uri.path.empty?)
|
63
|
+
uri_clean << "?" << uri.query
|
64
|
+
end
|
65
|
+
|
66
|
+
#STDERR.puts "IN: " << uri_str
|
67
|
+
#STDERR.puts "OUT: " << uri_clean
|
68
|
+
|
69
|
+
uri_clean
|
70
|
+
end
|
71
|
+
|
72
|
+
def HTTPUtil.fetch_content(uri, limit = 10)
|
73
|
+
res = self.fetch(uri,limit)
|
74
|
+
return (res) ? res.body : ""
|
75
|
+
end
|
76
|
+
|
77
|
+
def HTTPUtil.fetch(uri, limit = 10)
|
78
|
+
|
79
|
+
# You should choose better exception.
|
80
|
+
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
81
|
+
STDERR.puts "URL: #{uri.to_s}"
|
82
|
+
uri = URI.parse(uri) if uri.is_a? String
|
83
|
+
|
84
|
+
begin
|
85
|
+
timeout(@@timeout) do
|
86
|
+
response = Net::HTTP.get_response(uri)
|
87
|
+
|
88
|
+
|
89
|
+
case response
|
90
|
+
when Net::HTTPSuccess then response
|
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}"
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
def HTTPUtil.post(uri, params = {}, limit = 10)
|
106
|
+
|
107
|
+
# You should choose better exception.
|
108
|
+
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
109
|
+
|
110
|
+
uri = URI.parse(uri) if uri.is_a? String
|
111
|
+
|
112
|
+
begin
|
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
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def HTTPUtil.exists(uri)
|
134
|
+
|
135
|
+
begin
|
136
|
+
response = fetch(uri)
|
137
|
+
case response
|
138
|
+
when Net::HTTPSuccess then true
|
139
|
+
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
140
|
+
else
|
141
|
+
false
|
142
|
+
end
|
143
|
+
|
144
|
+
rescue Exception => e
|
145
|
+
STDERR.puts "Problem: " + e.message
|
146
|
+
false
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
def HTTPUtil.parse_query(query)
|
152
|
+
params = Hash.new([].freeze)
|
153
|
+
|
154
|
+
query.split(/[&;]/n).each do |pairs|
|
155
|
+
key, value = pairs.split('=',2).collect{|v| URI.unescape(v) }
|
156
|
+
if params.has_key?(key)
|
157
|
+
params[key].push(value)
|
158
|
+
else
|
159
|
+
params[key] = [value]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
params
|
164
|
+
end
|
165
|
+
|
166
|
+
def HTTPUtil.validate_method(meth='GET')
|
167
|
+
meth = (VALID_METHODS.member? meth.upcase) ? meth : VALID_METHODS[0]
|
168
|
+
end
|
169
|
+
|
170
|
+
# Extend the basic query string parser provided by the cgi module.
|
171
|
+
# converts single valued params (the most common case) to
|
172
|
+
# objects instead of arrays
|
173
|
+
#
|
174
|
+
# Input:
|
175
|
+
# the query string
|
176
|
+
#
|
177
|
+
# Output:
|
178
|
+
# hash of parameters, contains arrays for multivalued parameters
|
179
|
+
# (multiselect, checkboxes , etc)
|
180
|
+
# If no query string is provided (nil or "") returns an empty hash.
|
181
|
+
|
182
|
+
def HTTPUtil.query_to_hash(query_string)
|
183
|
+
return {} unless query_string
|
184
|
+
|
185
|
+
query_parameters = HTTPUtil.parse_query(query_string)
|
186
|
+
|
187
|
+
query_parameters.each { |key, val|
|
188
|
+
# replace the array with an object
|
189
|
+
query_parameters[key] = val[0] if 1 == val.length
|
190
|
+
}
|
191
|
+
|
192
|
+
# set default value to nil! cgi sets this to []
|
193
|
+
query_parameters.default = nil
|
194
|
+
|
195
|
+
return query_parameters
|
196
|
+
end
|
197
|
+
|
198
|
+
def HTTPUtil.hash_to_query(parameters)
|
199
|
+
return '' unless parameters
|
200
|
+
pairs = []
|
201
|
+
parameters.each do |param, value|
|
202
|
+
pairs << "#{param}=#{URI.escape(value.to_s)}"
|
203
|
+
end
|
204
|
+
return pairs.join('&')
|
205
|
+
#return pairs.join(";")
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
end
|
210
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module MathUtil
|
4
|
+
|
5
|
+
def self.variance(population)
|
6
|
+
n = 0
|
7
|
+
mean = 0.0
|
8
|
+
s = 0.0
|
9
|
+
population.each { |x|
|
10
|
+
n = n + 1
|
11
|
+
delta = (x - mean).to_f
|
12
|
+
mean = (mean + (delta / n)).to_f
|
13
|
+
s = (s + delta * (x - mean)).to_f
|
14
|
+
}
|
15
|
+
|
16
|
+
return s / n
|
17
|
+
end
|
18
|
+
|
19
|
+
# calculate the standard deviation of a population
|
20
|
+
# accepts: an array, the population
|
21
|
+
# returns: the standard deviation
|
22
|
+
def self.standard_deviation(population)
|
23
|
+
Math.sqrt(variance(population))
|
24
|
+
end
|
25
|
+
|
26
|
+
# enforce_limit
|
27
|
+
#
|
28
|
+
# Enforce a minimum and maximum value
|
29
|
+
def self.enforce_limit(val,min,max)
|
30
|
+
val = min if val < min
|
31
|
+
val = max if val > max
|
32
|
+
val
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
module Enumerable
|
41
|
+
|
42
|
+
##
|
43
|
+
# Sum of all the elements of the Enumerable
|
44
|
+
|
45
|
+
def sum
|
46
|
+
return self.inject(0) { |acc, i| acc + i }
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Average of all the elements of the Enumerable
|
51
|
+
#
|
52
|
+
# The Enumerable must respond to #length
|
53
|
+
|
54
|
+
def average
|
55
|
+
return self.sum / self.length.to_f
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Sample variance of all the elements of the Enumerable
|
60
|
+
#
|
61
|
+
# The Enumerable must respond to #length
|
62
|
+
|
63
|
+
def sample_variance
|
64
|
+
avg = self.average
|
65
|
+
sum = self.inject(0) { |acc, i| acc + (i - avg) ** 2 }
|
66
|
+
return (1 / self.length.to_f * sum)
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Standard deviation of all the elements of the Enumerable
|
71
|
+
#
|
72
|
+
# The Enumerable must respond to #length
|
73
|
+
|
74
|
+
def standard_deviation
|
75
|
+
return Math.sqrt(self.sample_variance)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
# A port of Text::Graph, which generates pretty ascii bar graphs from
|
2
|
+
# numeric datasets, like
|
3
|
+
#
|
4
|
+
# aaaa : (1)
|
5
|
+
# bb :..* (22)
|
6
|
+
# ccc :...* (43)
|
7
|
+
# dddddd :.....* (500)
|
8
|
+
# ee :......*(1000)
|
9
|
+
# f :.....* (300)
|
10
|
+
# ghi :...* (50)
|
11
|
+
#
|
12
|
+
# It accepts data in the following forms (see the 'extract' method):
|
13
|
+
#
|
14
|
+
# # { label => value, label => value, ... }
|
15
|
+
# # { :values => { label => value, ...} }
|
16
|
+
# # { :values => [values] }
|
17
|
+
# # {:values => { label => value, label => value }, :labels => [...]}
|
18
|
+
# # {:values => [values], :labels => [labels]}
|
19
|
+
# # [ [label, value], [label, value], ...]
|
20
|
+
# # [[values], [labels]]
|
21
|
+
#
|
22
|
+
# Numeric parameters:
|
23
|
+
# :minval
|
24
|
+
# :maxval
|
25
|
+
# :maxlen
|
26
|
+
#
|
27
|
+
# Boolean parameters:
|
28
|
+
# :log # logarithmic scale
|
29
|
+
# :right # label justification
|
30
|
+
#
|
31
|
+
# Display parameters:
|
32
|
+
# :marker
|
33
|
+
# :fill
|
34
|
+
# :separator
|
35
|
+
# :style # {:bar|:line} - sets default values for marker and fill
|
36
|
+
# :showval # numeric value after bar
|
37
|
+
|
38
|
+
# Text::Graph
|
39
|
+
# Port of Wade Johnson's Text::Graph for perl
|
40
|
+
# http://search.cpan.org/src/GWADEJ/Text-Graph-0.23/Graph.pm
|
41
|
+
#
|
42
|
+
# Author: Martin DeMello <martindeme...@gmail.com>
|
43
|
+
|
44
|
+
module Enumerable
|
45
|
+
def minmax
|
46
|
+
min = 1.0/0
|
47
|
+
max = -1.0/0
|
48
|
+
each {|i|
|
49
|
+
min = i if i < min
|
50
|
+
max = i if i > max
|
51
|
+
}
|
52
|
+
[min, max]
|
53
|
+
end
|
54
|
+
|
55
|
+
def map_with_index
|
56
|
+
a = []
|
57
|
+
each_with_index {|e, i| a << yield(e,i)}
|
58
|
+
a
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class TextGraph
|
63
|
+
include Math
|
64
|
+
|
65
|
+
def initialize(data, params = {})
|
66
|
+
@data = extract(data)
|
67
|
+
@params = {:style => (params[:style] || :bar)}
|
68
|
+
apply_style(@params[:style])
|
69
|
+
@params.update(params)
|
70
|
+
@params[:separator] ||= " :"
|
71
|
+
end
|
72
|
+
|
73
|
+
def update_params(par)
|
74
|
+
apply_style(par[:style]) if par[:style]
|
75
|
+
@params.update(par)
|
76
|
+
end
|
77
|
+
|
78
|
+
def apply_style(style)
|
79
|
+
if style == :bar
|
80
|
+
@params[:marker] = "*"
|
81
|
+
@params[:fill] = "*"
|
82
|
+
elsif style == :line
|
83
|
+
@params[:marker] = '*'
|
84
|
+
@params[:fill] = ' '
|
85
|
+
else
|
86
|
+
raise "Invalid style #{style}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def extract(data)
|
91
|
+
if data.is_a? Array
|
92
|
+
if data.length == 2 and data[0].is_a? Array and data[1].is_a? Array
|
93
|
+
# [[values], [labels]]
|
94
|
+
a = {}
|
95
|
+
a[:values] = data[0]
|
96
|
+
a[:labels] = data[1]
|
97
|
+
data = a
|
98
|
+
else
|
99
|
+
# [ [label, value], [label, value], ...]
|
100
|
+
a = {:values => [], :labels => []}
|
101
|
+
data.each {|i,j| a[:labels] << i; a[:values] << j}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if (data.length == 2) and data[:values] and data[:labels]
|
106
|
+
if data[:values].is_a? Array
|
107
|
+
# {:values => [values], :labels => [labels]}
|
108
|
+
# do nothing
|
109
|
+
elsif data[:values].is_a? Hash
|
110
|
+
# {:values => { label => value, label => value }, :labels => [...]}
|
111
|
+
a = data[:labels].map {|i| data[:values][i]}
|
112
|
+
data[:values] = a
|
113
|
+
else
|
114
|
+
raise "Invalid valueset"
|
115
|
+
end
|
116
|
+
elsif (data.length == 1) and data[:values]
|
117
|
+
if data[:values].is_a? Array
|
118
|
+
# { :values => [values] }
|
119
|
+
data[:labels] = data[:values].map {""}
|
120
|
+
elsif data[:values].is_a? Hash
|
121
|
+
# { :values => { label => value, ...} }
|
122
|
+
data[:labels] = data[:values].keys.sort_by {|i| i.to_s}
|
123
|
+
data[:values] = data[:labels].map {|i| data[:values][i]}
|
124
|
+
else
|
125
|
+
raise "Invalid valueset"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
# { label => value, label => value, ... }
|
129
|
+
a = {}
|
130
|
+
a[:labels] = data.keys.sort_by {|i| i.to_s}
|
131
|
+
a[:values] = a[:labels].map {|i| data[i]}
|
132
|
+
data = a
|
133
|
+
end
|
134
|
+
data[:labels].map! {|i| i.to_s}
|
135
|
+
data
|
136
|
+
end
|
137
|
+
|
138
|
+
def make_within(val, min, max)
|
139
|
+
(val < min) ? min : (val > max ? max : val)
|
140
|
+
end
|
141
|
+
|
142
|
+
def makebar(val, m, f)
|
143
|
+
val = (val + 0.5).to_i
|
144
|
+
(val > 0) ? (f*(val - 1) + m) : ""
|
145
|
+
end
|
146
|
+
|
147
|
+
def fmt_labels (right, labels)
|
148
|
+
len = labels.map {|i| i.length}.max
|
149
|
+
just = right ? :rjust : :ljust
|
150
|
+
labels.map {|i| i.send(just, len)}
|
151
|
+
end
|
152
|
+
|
153
|
+
def make_labelled_lines(data)
|
154
|
+
labels = fmt_labels(@params[:right], data[:labels])
|
155
|
+
lines = histogram(data)
|
156
|
+
lines.zip(labels).map {|line, label| label + @params[:separator] + line}
|
157
|
+
end
|
158
|
+
|
159
|
+
def histogram(data)
|
160
|
+
values = data[:values].dup
|
161
|
+
minval, maxval, maxlen =
|
162
|
+
@params[:minval], @params[:maxval], @params[:maxlen]
|
163
|
+
|
164
|
+
if @params[:log]
|
165
|
+
values.map! {|i| log(i)}
|
166
|
+
minval = log(minval) rescue 1 if minval
|
167
|
+
maxval = log(maxval) rescue 1 if maxval
|
168
|
+
end
|
169
|
+
|
170
|
+
min, max = values.minmax
|
171
|
+
minval ||= min
|
172
|
+
maxval ||= max
|
173
|
+
maxl = maxval - minval + 1
|
174
|
+
maxlen ||= maxl
|
175
|
+
scale = maxlen*1.0/maxl
|
176
|
+
values = values.map {|i|
|
177
|
+
j = make_within(i, minval, maxval) - minval
|
178
|
+
makebar(j*scale, @params[:marker], @params[:fill])
|
179
|
+
}
|
180
|
+
|
181
|
+
if(@params[:showval])
|
182
|
+
values = values.map_with_index {|v, i|
|
183
|
+
v.ljust(maxlen) + "(#{data[:values][i]})"
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
values
|
188
|
+
end
|
189
|
+
|
190
|
+
def to_s
|
191
|
+
make_labelled_lines(@data).join("\n") + "\n"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
if __FILE__ == $0
|
196
|
+
|
197
|
+
a = TextGraph.new({
|
198
|
+
:values => [100,142,43,54,111,62,52],
|
199
|
+
:labels => %w(aaaa bb ccc dddddd ee f ghi)
|
200
|
+
})
|
201
|
+
|
202
|
+
puts a
|
203
|
+
|
204
|
+
# aaaa :
|
205
|
+
# bb :*
|
206
|
+
# ccc :***
|
207
|
+
# dddddd :****
|
208
|
+
# ee :*********
|
209
|
+
# f :**
|
210
|
+
# ghi :****
|
211
|
+
|
212
|
+
puts "-------------------------------------------------------------"
|
213
|
+
|
214
|
+
a.update_params(:style => :line, :right => true, :showval => true)
|
215
|
+
puts a
|
216
|
+
|
217
|
+
# aaaa : (1)
|
218
|
+
# bb :* (2)
|
219
|
+
# ccc : * (4)
|
220
|
+
# dddddd : * (5)
|
221
|
+
# ee : * (10)
|
222
|
+
# f : * (3)
|
223
|
+
# ghi : * (5)
|
224
|
+
|
225
|
+
puts "-------------------------------------------------------------"
|
226
|
+
|
227
|
+
b = TextGraph.new({ :a=>1, :b=>5, :c=>20, :d=>10, :e=>17 }, {:maxlen => 10})
|
228
|
+
puts b
|
229
|
+
|
230
|
+
# a :
|
231
|
+
# b :**
|
232
|
+
# c :**********
|
233
|
+
# d :*****
|
234
|
+
# e :********
|
235
|
+
|
236
|
+
puts "-------------------------------------------------------------"
|
237
|
+
|
238
|
+
c = TextGraph.new({:values => { :a=>1, :b=>5, :c=>20, :d=>10, :e=>17 },
|
239
|
+
:labels => [:a, :c, :d]},
|
240
|
+
{:minval => 0, :maxval => 15, :showval => true})
|
241
|
+
puts c
|
242
|
+
|
243
|
+
# a :* (1)
|
244
|
+
# c :*************** (20)
|
245
|
+
# d :********** (10)
|
246
|
+
|
247
|
+
puts "-------------------------------------------------------------"
|
248
|
+
|
249
|
+
d = TextGraph.new([[10,22,43,500,1000,300,50], %w(aaaa bb ccc dddddd ee f ghi)],
|
250
|
+
{ :style => :line,
|
251
|
+
:right => true, # right-justify labels
|
252
|
+
:fill => '.', # change fill-marker
|
253
|
+
:log => false, # logarithmic graph
|
254
|
+
:showval => true # show actual values
|
255
|
+
}
|
256
|
+
)
|
257
|
+
puts d
|
258
|
+
|
259
|
+
# aaaa : (1)
|
260
|
+
# bb :..* (22)
|
261
|
+
# ccc :...* (43)
|
262
|
+
# dddddd :.....* (500)
|
263
|
+
# ee :......*(1000)
|
264
|
+
# f :.....* (300)
|
265
|
+
# ghi :...* (50)
|
266
|
+
|
267
|
+
end
|