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.
- data/CHANGES.txt +9 -1
- data/Gemfile +19 -0
- data/Gemfile.lock +50 -0
- data/README.md +5 -79
- data/Rakefile +10 -7
- data/Rudyfile +1 -1
- data/TODO +31 -0
- data/VERSION.yml +3 -4
- data/bin/stella +23 -81
- data/certs/README.txt +17 -0
- data/certs/cacerts.pem +1529 -0
- data/certs/gd-class2-root.crt +24 -0
- data/certs/gd_bundle.crt +76 -0
- data/certs/gd_intermediate.crt +29 -0
- data/certs/startssl-ca.pem +44 -0
- data/certs/startssl-sub.class1.server.ca.pem +36 -0
- data/certs/stella-master.crt +1738 -0
- data/lib/stella.rb +191 -123
- data/lib/stella/cli.rb +47 -67
- data/lib/stella/client.rb +424 -360
- data/lib/stella/core_ext.rb +527 -0
- data/lib/stella/engine.rb +126 -419
- data/lib/stella/report.rb +391 -0
- data/lib/stella/testplan.rb +432 -306
- data/lib/stella/utils.rb +227 -2
- data/stella.gemspec +56 -55
- data/try/00_basics_try.rb +29 -0
- data/try/01_selectable_try.rb +25 -0
- data/try/09_utils_try.rb +67 -0
- data/try/10_stella_object_try.rb +49 -0
- data/try/40_report_try.rb +133 -0
- data/try/90_class_syntax_try.rb +13 -0
- data/try/emhttp.rb +62 -0
- data/try/rubyroute.rb +70 -0
- data/try/support/file.bmp +0 -0
- data/try/support/file.gif +0 -0
- data/try/support/file.ico +0 -0
- data/try/support/file.jpeg +0 -0
- data/try/support/file.jpg +0 -0
- data/try/support/file.png +0 -0
- data/try/traceviz.rb +60 -0
- data/vendor/httpclient-2.1.5.2/httpclient/session.rb +5 -2
- metadata +81 -53
- data/examples/cookies/plan.rb +0 -49
- data/examples/csvdata/plan.rb +0 -32
- data/examples/csvdata/search_terms.csv +0 -14
- data/examples/dynamic/plan.rb +0 -60
- data/examples/essentials/logo.png +0 -0
- data/examples/essentials/plan.rb +0 -248
- data/examples/essentials/search_terms.txt +0 -19
- data/examples/exceptions/plan.rb +0 -20
- data/examples/httpauth/plan.rb +0 -33
- data/examples/timeout/plan.rb +0 -18
- data/examples/variables/plan.rb +0 -41
- data/lib/stella/client/container.rb +0 -378
- data/lib/stella/common.rb +0 -363
- data/lib/stella/data.rb +0 -59
- data/lib/stella/data/http.rb +0 -189
- data/lib/stella/engine/functional.rb +0 -156
- data/lib/stella/engine/load.rb +0 -516
- data/lib/stella/guidelines.rb +0 -18
- data/lib/stella/logger.rb +0 -150
- data/lib/stella/utils/httputil.rb +0 -266
- data/try/01_numeric_mixins_tryouts.rb +0 -40
- data/try/12_digest_tryouts.rb +0 -42
- data/try/70_module_usage.rb +0 -21
- data/try/api/10_functional.rb +0 -20
- data/try/configs/failed_requests.rb +0 -31
- data/try/configs/global_sequential.rb +0 -18
- 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\-_=%&@\?\.\/]+(\/\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
|