stella 0.3.2
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 +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
|