stella 0.8.8.001 → 2.0.1.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/CHANGES.txt +9 -1
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +50 -0
  4. data/README.md +5 -79
  5. data/Rakefile +10 -7
  6. data/Rudyfile +1 -1
  7. data/TODO +31 -0
  8. data/VERSION.yml +3 -4
  9. data/bin/stella +23 -81
  10. data/certs/README.txt +17 -0
  11. data/certs/cacerts.pem +1529 -0
  12. data/certs/gd-class2-root.crt +24 -0
  13. data/certs/gd_bundle.crt +76 -0
  14. data/certs/gd_intermediate.crt +29 -0
  15. data/certs/startssl-ca.pem +44 -0
  16. data/certs/startssl-sub.class1.server.ca.pem +36 -0
  17. data/certs/stella-master.crt +1738 -0
  18. data/lib/stella.rb +191 -123
  19. data/lib/stella/cli.rb +47 -67
  20. data/lib/stella/client.rb +424 -360
  21. data/lib/stella/core_ext.rb +527 -0
  22. data/lib/stella/engine.rb +126 -419
  23. data/lib/stella/report.rb +391 -0
  24. data/lib/stella/testplan.rb +432 -306
  25. data/lib/stella/utils.rb +227 -2
  26. data/stella.gemspec +56 -55
  27. data/try/00_basics_try.rb +29 -0
  28. data/try/01_selectable_try.rb +25 -0
  29. data/try/09_utils_try.rb +67 -0
  30. data/try/10_stella_object_try.rb +49 -0
  31. data/try/40_report_try.rb +133 -0
  32. data/try/90_class_syntax_try.rb +13 -0
  33. data/try/emhttp.rb +62 -0
  34. data/try/rubyroute.rb +70 -0
  35. data/try/support/file.bmp +0 -0
  36. data/try/support/file.gif +0 -0
  37. data/try/support/file.ico +0 -0
  38. data/try/support/file.jpeg +0 -0
  39. data/try/support/file.jpg +0 -0
  40. data/try/support/file.png +0 -0
  41. data/try/traceviz.rb +60 -0
  42. data/vendor/httpclient-2.1.5.2/httpclient/session.rb +5 -2
  43. metadata +81 -53
  44. data/examples/cookies/plan.rb +0 -49
  45. data/examples/csvdata/plan.rb +0 -32
  46. data/examples/csvdata/search_terms.csv +0 -14
  47. data/examples/dynamic/plan.rb +0 -60
  48. data/examples/essentials/logo.png +0 -0
  49. data/examples/essentials/plan.rb +0 -248
  50. data/examples/essentials/search_terms.txt +0 -19
  51. data/examples/exceptions/plan.rb +0 -20
  52. data/examples/httpauth/plan.rb +0 -33
  53. data/examples/timeout/plan.rb +0 -18
  54. data/examples/variables/plan.rb +0 -41
  55. data/lib/stella/client/container.rb +0 -378
  56. data/lib/stella/common.rb +0 -363
  57. data/lib/stella/data.rb +0 -59
  58. data/lib/stella/data/http.rb +0 -189
  59. data/lib/stella/engine/functional.rb +0 -156
  60. data/lib/stella/engine/load.rb +0 -516
  61. data/lib/stella/guidelines.rb +0 -18
  62. data/lib/stella/logger.rb +0 -150
  63. data/lib/stella/utils/httputil.rb +0 -266
  64. data/try/01_numeric_mixins_tryouts.rb +0 -40
  65. data/try/12_digest_tryouts.rb +0 -42
  66. data/try/70_module_usage.rb +0 -21
  67. data/try/api/10_functional.rb +0 -20
  68. data/try/configs/failed_requests.rb +0 -31
  69. data/try/configs/global_sequential.rb +0 -18
  70. data/try/proofs/thread_queue.rb +0 -21
@@ -0,0 +1,527 @@
1
+ #encoding: utf-8
2
+
3
+ $KCODE = "u" if RUBY_VERSION =~ /^1.8/
4
+
5
+
6
+ # A hash with indifferent access and magic predicates.
7
+ #
8
+ # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
9
+ #
10
+ # hash[:foo] #=> 'bar'
11
+ # hash['foo'] #=> 'bar'
12
+ # hash.foo? #=> true
13
+ #
14
+ class HashWithIndifferentAccess < ::Hash #:nodoc:
15
+
16
+ def initialize(hash={})
17
+ super()
18
+ hash.each do |key, value|
19
+ self[convert_key(key)] = value
20
+ end
21
+ end
22
+
23
+ def [](key)
24
+ super(convert_key(key))
25
+ end
26
+
27
+ def []=(key, value)
28
+ super(convert_key(key), value)
29
+ end
30
+
31
+ def delete(key)
32
+ super(convert_key(key))
33
+ end
34
+
35
+ def values_at(*indices)
36
+ indices.collect { |key| self[convert_key(key)] }
37
+ end
38
+
39
+ def merge(other)
40
+ dup.merge!(other)
41
+ end
42
+
43
+ def merge!(other)
44
+ other.each do |key, value|
45
+ self[convert_key(key)] = value
46
+ end
47
+ self
48
+ end
49
+
50
+ protected
51
+
52
+ def convert_key(key)
53
+ key.is_a?(Symbol) ? key.to_s : key
54
+ end
55
+
56
+ # Magic predicates. For instance:
57
+ #
58
+ # options.force? # => !!options['force']
59
+ # options.shebang # => "/usr/lib/local/ruby"
60
+ # options.test_framework?(:rspec) # => options[:test_framework] == :rspec
61
+ #
62
+ def method_missing(method, *args, &block)
63
+ method = method.to_s
64
+ if method =~ /^(\w+)\?$/
65
+ if args.empty?
66
+ !!self[$1]
67
+ else
68
+ self[$1] == args.first
69
+ end
70
+ else
71
+ self[method]
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ class Symbol
78
+ def downcase
79
+ self.to_s.downcase.to_sym
80
+ end
81
+ def upcase
82
+ self.to_s.upcase.to_sym
83
+ end
84
+ unless method_defined?(:empty?)
85
+ def empty?
86
+ self.to_s.empty?
87
+ end
88
+ end
89
+ end
90
+
91
+ # Fix for eventmachine in Ruby 1.9
92
+ class Thread
93
+ unless method_defined? :kill!
94
+ def kill!(*args) kill( *args) end
95
+ end
96
+ end
97
+
98
+
99
+ # Assumes Time::Units and Numeric mixins are available.
100
+ class String
101
+ def in_seconds
102
+ # "60m" => ["60", "m"]
103
+ q,u = self.scan(/([\d\.]+)([s,m,h])?/).flatten
104
+ q &&= q.to_f and u ||= 's'
105
+ q &&= q.in_seconds(u)
106
+ end
107
+ end
108
+
109
+
110
+ class String
111
+ def encode_fix(enc="UTF-8")
112
+ if RUBY_VERSION >= "1.9"
113
+ begin
114
+ encode!(enc, :undef => :replace, :invalid => :replace, :replace => '?')
115
+ rescue Encoding::CompatibilityError
116
+ BS.info "String#encode_fix: resorting to US-ASCII"
117
+ encode!("US-ASCII", :undef => :replace, :invalid => :replace, :replace => '?')
118
+ end
119
+ end
120
+ self
121
+ end
122
+ def plural(int=1)
123
+ int > 1 || int.zero? ? "#{self}s" : self
124
+ end
125
+ def shorten(len=50)
126
+ return self if size <= len
127
+ self[0..len] + "..."
128
+ end
129
+ def to_file(filename, mode, chmod=0744)
130
+ mode = (mode == :append) ? 'a' : 'w'
131
+ f = File.open(filename,mode)
132
+ f.puts self
133
+ f.close
134
+ raise "Provided chmod is not a Fixnum (#{chmod})" unless chmod.is_a?(Fixnum)
135
+ File.chmod(chmod, filename)
136
+ end
137
+
138
+ # via: http://www.est1985.nl/design/2-design/96-linkify-urls-in-ruby-on-rails
139
+ def linkify!
140
+ self.gsub!(/\b((https?:\/\/|ftps?:\/\/|mailto:|www\.|status\.)([A-Za-z0-9\-_=%&amp;@\?\.\/]+(\/\s)?))\b/) {
141
+ match = $1
142
+ tail = $3
143
+ case match
144
+ when /^(www|status)/ then "<a href=\"http://#{match.strip}\">#{match}</a>"
145
+ when /^mailto/ then "<a href=\"#{match.strip}\">#{tail}</a>"
146
+ else "<a href=\"#{match.strip}\">#{match}</a>"
147
+ end
148
+ }
149
+ self
150
+ end
151
+
152
+ def linkify
153
+ self.dup.linkify!
154
+ end
155
+
156
+ end
157
+
158
+
159
+ unless defined?(Time::Units)
160
+ class Time
161
+ module Units
162
+ PER_MICROSECOND = 0.000001.freeze
163
+ PER_MILLISECOND = 0.001.freeze
164
+ PER_MINUTE = 60.0.freeze
165
+ PER_HOUR = 3600.0.freeze
166
+ PER_DAY = 86400.0.freeze
167
+
168
+ def microseconds() seconds * PER_MICROSECOND end
169
+ def milliseconds() seconds * PER_MILLISECOND end
170
+ def seconds() self end
171
+ def minutes() seconds * PER_MINUTE end
172
+ def hours() seconds * PER_HOUR end
173
+ def days() seconds * PER_DAY end
174
+ def weeks() seconds * PER_DAY * 7 end
175
+ def years() seconds * PER_DAY * 365 end
176
+
177
+ def in_years() seconds / PER_DAY / 365 end
178
+ def in_weeks() seconds / PER_DAY / 7 end
179
+ def in_days() seconds / PER_DAY end
180
+ def in_hours() seconds / PER_HOUR end
181
+ def in_minutes() seconds / PER_MINUTE end
182
+ def in_milliseconds() seconds / PER_MILLISECOND end
183
+ def in_microseconds() seconds / PER_MICROSECOND end
184
+
185
+ def in_time
186
+ Time.at(self).utc
187
+ end
188
+
189
+ def in_seconds(u=nil)
190
+ case u.to_s
191
+ when /\A(y)|(years?)\z/
192
+ years
193
+ when /\A(w)|(weeks?)\z/
194
+ weeks
195
+ when /\A(d)|(days?)\z/
196
+ days
197
+ when /\A(h)|(hours?)\z/
198
+ hours
199
+ when /\A(m)|(minutes?)\z/
200
+ minutes
201
+ when /\A(ms)|(milliseconds?)\z/
202
+ milliseconds
203
+ when /\A(us)|(microseconds?)|(μs)\z/
204
+ microseconds
205
+ else
206
+ self
207
+ end
208
+ end
209
+
210
+
211
+ ## JRuby doesn't like using instance_methods.select here.
212
+ ## It could be a bug or something quirky with Attic
213
+ ## (although it works in 1.8 and 1.9). The error:
214
+ ##
215
+ ## lib/attic.rb:32:in `select': yield called out of block (LocalJumpError)
216
+ ## lib/stella/mixins/numeric.rb:24
217
+ ##
218
+ ## Create singular methods, like hour and day.
219
+ # instance_methods.select.each do |plural|
220
+ # singular = plural.to_s.chop
221
+ # alias_method singular, plural
222
+ # end
223
+
224
+ alias_method :ms, :milliseconds
225
+ alias_method :'μs', :microseconds
226
+ alias_method :second, :seconds
227
+ alias_method :minute, :minutes
228
+ alias_method :hour, :hours
229
+ alias_method :day, :days
230
+ alias_method :week, :weeks
231
+ alias_method :year, :years
232
+
233
+ end
234
+ end
235
+
236
+ class Numeric
237
+ include Time::Units
238
+
239
+ def to_ms
240
+ (self*1000.to_f)
241
+ end
242
+
243
+ # TODO: Use 1024?
244
+ def to_bytes
245
+ args = case self.abs.to_i
246
+ when (1000)..(1000**2)
247
+ '%3.2f%s' % [(self / 1000.to_f).to_s, 'KB']
248
+ when (1000**2)..(1000**3)
249
+ '%3.2f%s' % [(self / (1000**2).to_f).to_s, 'MB']
250
+ when (1000**3)..(1000**4)
251
+ '%3.2f%s' % [(self / (1000**3).to_f).to_s, 'GB']
252
+ when (1000**4)..(1000**6)
253
+ '%3.2f%s' % [(self / (1000**4).to_f).to_s, 'TB']
254
+ else
255
+ [self.to_i, 'B'].join
256
+ end
257
+ end
258
+ end
259
+ end
260
+
261
+
262
+ # A simple class for really fast serialized timing data.
263
+ # NOTE: We're storing the serialized data directly to a
264
+ # redis sorted set so it's important that each chunk of
265
+ # data in unique. Also, it's possible to grab the stamp
266
+ # from the zrange using :with_scores.
267
+ # NOTE2: We bypass Storable's #to_csv and #from_csv for
268
+ # speed. TODO: test speed difference.
269
+ class MetricsPack < Storable
270
+ unless defined?(MetricsPack::METRICS)
271
+ METRICS = [:rt, :sc, :sr, :fb, :lb, :rscs, :rshs, :rqcs, :rqhs]
272
+ TALLIES = [:n, :errors]
273
+ end
274
+ field :stamp => Float
275
+ field :uid => String
276
+ field :n => Integer
277
+ field :rt => Float
278
+ field :sc => Float
279
+ field :sr => Float
280
+ field :fb => Float
281
+ field :lb => Float
282
+ field :rqhs => Integer
283
+ field :rqcs => Integer
284
+ field :rshs => Integer
285
+ field :rscs => Integer
286
+ field :score => Float
287
+ field :errors => Integer
288
+ field :rtsd => Float # response time stdev
289
+ def initialize(stamp=nil, uid=nil, n=nil)
290
+ @stamp, @uid, @n = stamp, uid, n
291
+ @stamp &&= @stamp.utc.to_i if Time === @stamp
292
+ self.class.field_names.each do |fname|
293
+ self.send("#{fname}=", 0) unless fname == :id || self.send(fname)
294
+ end
295
+ @score ||= 1.0
296
+ @errors ||= 0
297
+ end
298
+ def kind
299
+ :metric
300
+ end
301
+
302
+ # should be in the same order as the fields are defined (i.e. MetricsPack.field_names)
303
+ def update(*args)
304
+ field_names.each_with_index do |field,idx|
305
+ val = args[idx]
306
+ val = case field_types[field].to_s
307
+ when 'Float'
308
+ val.to_f
309
+ when 'Integer'
310
+ val.to_i
311
+ else
312
+ val
313
+ end
314
+ send("#{field}=", val)
315
+ end
316
+ self
317
+ end
318
+
319
+ def pack
320
+ to_s
321
+ end
322
+
323
+ # @stamp => 1281355304 (2010-08-09-12-01-44)
324
+ # quantize_stamp(1.day) => 1281312000 (2010-08-09)
325
+ # quantize_stamp(1.hour) => 1281355200 (2010-08-09-12)
326
+ # quantize_stamp(1.minute) => 1281355260 (2010-08-09-12-01)
327
+ def quantize_stamp(quantum)
328
+ @stamp - (@stamp % quantum)
329
+ end
330
+ def to_a
331
+ field_names.collect { |field|
332
+ v = send(field)
333
+ if v.nil?
334
+ field_types[field] == String ? 'unknown' : 0.0
335
+ else
336
+ field_types[field] == Float ? v.fineround : v
337
+ end
338
+ }
339
+ end
340
+ def to_s
341
+ to_a.join(',')
342
+ end
343
+ def to_csv
344
+ to_s
345
+ end
346
+ def self.from_csv(str)
347
+ unpack str
348
+ end
349
+ def self.unpack(str)
350
+ return ArgumentError, "Cannot unpack nil" if str.nil?
351
+ a = str.split(',')
352
+ me = new
353
+ me.update *a
354
+ end
355
+ def self.from_json(str)
356
+ unpack(str)
357
+ end
358
+ def self.metric?(guess)
359
+ METRICS.member?(guess.to_s.to_sym)
360
+ end
361
+ end
362
+
363
+
364
+ #############################
365
+ # Statistics Module for Ruby
366
+ # (C) Derrick Pallas
367
+ #
368
+ # Authors: Derrick Pallas
369
+ # Website: http://derrick.pallas.us/ruby-stats/
370
+ # License: Academic Free License 3.0
371
+ # Version: 2007-10-01b
372
+ #
373
+
374
+ class Numeric
375
+ def square ; self * self ; end
376
+ def fineround(len=6.0)
377
+ v = (self * (10.0**len)).round / (10.0**len)
378
+ v.zero? ? 0 : v
379
+ end
380
+ end
381
+
382
+ class Array
383
+ def sum ; self.inject(0){|a,x| next if x.nil? || a.nil?; x+a} ; end
384
+ def mean; self.sum.to_f/self.size ; end
385
+ def median
386
+ case self.size % 2
387
+ when 0 then self.sort[self.size/2-1,2].mean
388
+ when 1 then self.sort[self.size/2].to_f
389
+ end if self.size > 0
390
+ end
391
+ def histogram ; self.sort.inject({}){|a,x|a[x]=a[x].to_i+1;a} ; end
392
+ def mode
393
+ map = self.histogram
394
+ max = map.values.max
395
+ map.keys.select{|x|map[x]==max}
396
+ end
397
+ def squares ; self.inject(0){|a,x|x.square+a} ; end
398
+ def variance ; self.squares.to_f/self.size - self.mean.square; end
399
+ def deviation ; Math::sqrt( self.variance ) ; end
400
+ alias_method :sd, :deviation
401
+ def permute ; self.dup.permute! ; end
402
+ def permute!
403
+ (1...self.size).each do |i| ; j=rand(i+1)
404
+ self[i],self[j] = self[j],self[i] if i!=j
405
+ end;self
406
+ end
407
+ def sample n=1 ; (0...n).collect{ self[rand(self.size)] } ; end
408
+
409
+ def random
410
+ self[rand(self.size)]
411
+ end
412
+ def percentile(perc)
413
+ self.sort[percentile_index(perc)]
414
+ end
415
+ def percentile_index(perc)
416
+ (perc * self.length).ceil - 1
417
+ end
418
+ end
419
+
420
+
421
+ class Array
422
+ def dump(format)
423
+ respond_to?(:"to_#{format}") ? send(:"to_#{format}") : raise("Unknown format: #{format}")
424
+ end
425
+
426
+ def to_json
427
+ Yajl::Encoder.encode(self)
428
+ end
429
+ def self.from_json(str)
430
+ Yajl::Parser.parse(str, :check_utf8 => false)
431
+ end
432
+ end
433
+
434
+ class Float
435
+
436
+ # Returns true if a float has a fractional part; i.e. <tt>f == f.to_i</tt>
437
+ def fractional_part?
438
+ fractional_part != 0.0
439
+ end
440
+
441
+ # Returns the fractional part of a float. For example, <tt>(6.67).fractional_part == 0.67</tt>
442
+ def fractional_part
443
+ (self - self.truncate).abs
444
+ end
445
+
446
+ end
447
+
448
+
449
+ class Hash
450
+
451
+ unless method_defined?(:to_json)
452
+ def to_json(*args)
453
+ Yajl::Encoder.encode(self)
454
+ end
455
+ end
456
+
457
+ # Courtesy of Julien Genestoux
458
+ def flatten
459
+ params = {}
460
+ stack = []
461
+
462
+ each do |k, v|
463
+ if v.is_a?(Hash)
464
+ stack << [k,v]
465
+ elsif v.is_a?(Array)
466
+ stack << [k,Hash.from_array(v)]
467
+ else
468
+ params[k] = v
469
+ end
470
+ end
471
+
472
+ stack.each do |parent, hash|
473
+ hash.each do |k, v|
474
+ if v.is_a?(Hash)
475
+ stack << ["#{parent}[#{k}]", v]
476
+ else
477
+ params["#{parent}[#{k}]"] = v
478
+ end
479
+ end
480
+ end
481
+
482
+ params
483
+ end
484
+
485
+ def dump(format)
486
+ respond_to?(:"to_#{format}") ? send(:"to_#{format}") : raise("Unknown format")
487
+ end
488
+
489
+ # Courtesy of Julien Genestoux
490
+ # See: http://stackoverflow.com/questions/798710/how-to-turn-a-ruby-hash-into-http-params
491
+ # NOTE: conflicts w/ HTTParty 0.7.3 when named "to_params"
492
+ def to_http_params
493
+ params = ''
494
+ stack = []
495
+
496
+ each do |k, v|
497
+ if v.is_a?(Hash)
498
+ stack << [k,v]
499
+ elsif v.is_a?(Array)
500
+ stack << [k,Hash.from_array(v)]
501
+ else
502
+ params << "#{k}=#{v}&"
503
+ end
504
+ end
505
+
506
+ stack.each do |parent, hash|
507
+ hash.each do |k, v|
508
+ if v.is_a?(Hash)
509
+ stack << ["#{parent}[#{k}]", URI::Escape.escape(v)]
510
+ else
511
+ params << "#{parent}[#{k}]=#{URI::Escape.escape(v)}&"
512
+ end
513
+ end
514
+ end
515
+
516
+ params.chop!
517
+ params
518
+ end
519
+ def self.from_array(array = [])
520
+ h = Hash.new
521
+ array.size.times do |t|
522
+ h[t] = array[t]
523
+ end
524
+ h
525
+ end
526
+
527
+ end