blitz 0.1.12 → 0.1.13

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/Gemfile CHANGED
@@ -10,6 +10,7 @@ gem "json_pure", "~> 1.5.1"
10
10
  gem "hexy", "~> 0.1.1"
11
11
  gem 'rspec', '2.6.0'
12
12
  gem 'rspec-core', '2.6.4'
13
+ gem 'term-ansicolor', '1.0.5'
13
14
 
14
15
  # Add dependencies to develop your gem here.
15
16
  # Include everything needed to run rake, tests, features, etc.
@@ -26,6 +26,7 @@ GEM
26
26
  rspec-expectations (2.6.0)
27
27
  diff-lcs (~> 1.1.2)
28
28
  rspec-mocks (2.6.0)
29
+ term-ansicolor (1.0.5)
29
30
 
30
31
  PLATFORMS
31
32
  ruby
@@ -40,3 +41,4 @@ DEPENDENCIES
40
41
  rest-client (~> 1.6.1)
41
42
  rspec (= 2.6.0)
42
43
  rspec-core (= 2.6.4)
44
+ term-ansicolor (= 1.0.5)
data/Rakefile CHANGED
@@ -32,13 +32,6 @@ Jeweler::Tasks.new do |gem|
32
32
  end
33
33
  Jeweler::RubygemsDotOrgTasks.new
34
34
 
35
- require 'rake/testtask'
36
- Rake::TestTask.new(:test) do |test|
37
- test.libs << 'lib' << 'test'
38
- test.pattern = 'test/**/test_*.rb'
39
- test.verbose = true
40
- end
41
-
42
35
  desc "Run all the specs"
43
36
  task :spec do
44
37
  require 'rspec/core'
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{blitz}
8
- s.version = "0.1.12"
8
+ s.version = "0.1.13"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["pcapr"]
12
- s.date = %q{2011-10-05}
12
+ s.date = %q{2011-10-11}
13
13
  s.default_executable = %q{blitz}
14
14
  s.description = %q{Make load and performance testing a fun sport}
15
15
  s.email = %q{support@blitz.io}
@@ -64,6 +64,7 @@ Gem::Specification.new do |s|
64
64
  s.add_runtime_dependency(%q<hexy>, ["~> 0.1.1"])
65
65
  s.add_runtime_dependency(%q<rspec>, ["= 2.6.0"])
66
66
  s.add_runtime_dependency(%q<rspec-core>, ["= 2.6.4"])
67
+ s.add_runtime_dependency(%q<term-ansicolor>, ["= 1.0.5"])
67
68
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
68
69
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
69
70
  else
@@ -74,6 +75,7 @@ Gem::Specification.new do |s|
74
75
  s.add_dependency(%q<hexy>, ["~> 0.1.1"])
75
76
  s.add_dependency(%q<rspec>, ["= 2.6.0"])
76
77
  s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
78
+ s.add_dependency(%q<term-ansicolor>, ["= 1.0.5"])
77
79
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
78
80
  s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
79
81
  end
@@ -85,6 +87,7 @@ Gem::Specification.new do |s|
85
87
  s.add_dependency(%q<hexy>, ["~> 0.1.1"])
86
88
  s.add_dependency(%q<rspec>, ["= 2.6.0"])
87
89
  s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
90
+ s.add_dependency(%q<term-ansicolor>, ["= 1.0.5"])
88
91
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
89
92
  s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
90
93
  end
@@ -1,11 +1,12 @@
1
1
  require 'rubygems'
2
+ require 'term/ansicolor'
2
3
  require 'couchrest'
3
4
  require 'hexy'
4
5
  require 'pp'
5
6
 
6
7
  class Blitz # :nodoc:
7
8
  require 'blitz/helper'
8
- Version = "0.1.12"
9
+ Version = "0.1.13"
9
10
 
10
11
  extend Blitz::Helper
11
12
 
@@ -1,6 +1,8 @@
1
1
  class Blitz
2
2
  class Command
3
3
  class Curl < Command # :nodoc:
4
+ include Term::ANSIColor
5
+
4
6
  def cmd_help argv
5
7
  help
6
8
  end
@@ -38,17 +40,17 @@ class Curl < Command # :nodoc:
38
40
  if check['bytes']
39
41
  bytes = '| ' + check['bytes']
40
42
  end
41
- error "#{check['code']} | #{check['url']} #{bytes}"
43
+ error "#{bold(red(check['code']))} | #{cyan(check['url'])} #{bytes}"
42
44
  end
43
45
  error ''
44
46
  end
45
47
  error "If your app is RESTfully built with sinatra or rails, simply add this route:"
46
48
  error ""
47
- error "get '/#{e.uuid}' do"
49
+ error "get '/#{cyan(e.uuid)}' do"
48
50
  error " '42'"
49
51
  error "end"
50
52
  error ""
51
- error "Once this is done, you can blitz #{e.host} all you want."
53
+ error "Once this is done, you can blitz #{cyan(e.host)} all you want."
52
54
  puts
53
55
  end
54
56
 
@@ -59,128 +61,137 @@ class Curl < Command # :nodoc:
59
61
  print_sprint_result args, result
60
62
  rescue ::Blitz::Curl::Error::Authorize => e
61
63
  authorize_error e
64
+ rescue ::Blitz::Curl::Error::Step => e
65
+ error "#{yellow(e.region)}: #{red(e.message)} in step #{e.step}"
66
+ puts
67
+ print_sprint_result args, e
62
68
  rescue ::Blitz::Curl::Error::Region => e
63
- error "#{e.region}: #{e.message}"
69
+ error = "#{yellow(e.region)}: #{red(e.message)}"
64
70
  rescue ::Blitz::Curl::Error => e
65
- error e.message
71
+ error red(e.message)
66
72
  end
67
73
  end
68
-
69
- def print_sprint_result args, result
70
- rtt = result.duration
74
+
75
+ def pretty_print_duration duration
76
+ rtt = duration
71
77
  if rtt < 1.0
72
78
  rtt = (rtt * 1000).floor.to_s + ' ms';
73
79
  else
74
80
  rtt = ("%.2f" % rtt) + ' sec';
75
81
  end
82
+ end
76
83
 
77
- puts "-" * 70
78
- msg "#{result.region}: Response time of #{rtt}"
79
- unless args['dump-header'] or args['verbose']
80
- msg "Try --verbose to see the request/response headers"
81
- end
82
- puts "-" * 70
83
- puts
84
-
85
- if args['dump-header'] or args['verbose']
86
- puts "> " + result.request.line
87
- result.request.headers.each_pair { |k, v| puts "> #{k}: #{v}\r\n" }
84
+ def print_sprint_result args, result
85
+ if result.respond_to? :duration
86
+ rtt = pretty_print_duration result.duration
87
+ msg "#{yellow(result.region)}: Transaction time #{green(rtt)}"
88
88
  puts
89
+ end
89
90
 
90
- content = result.request.content
91
- if not content.empty?
92
- if /^[[:print:]]+$/ =~ content
93
- puts content
94
- else
95
- puts Hexy.new(content).to_s
91
+ result.steps.each do |step|
92
+ req, res = step.request, step.response
93
+ if args['dump-header'] or args['verbose']
94
+ puts "> " + req.line
95
+ req.headers.each_pair { |k, v| puts "> #{k}: #{v}\r\n" }
96
+ puts
97
+
98
+ content = req.content
99
+ if not content.empty?
100
+ if /^[[:print:]]+$/ =~ content
101
+ puts content
102
+ else
103
+ puts Hexy.new(content).to_s
104
+ end
105
+ puts
96
106
  end
107
+
108
+ puts "< " + res.line
109
+ res.headers.each_pair { |k, v| puts "< #{k}: #{v}\r\n" }
97
110
  puts
98
- end
99
-
100
- puts "< " + result.response.line
101
- result.response.headers.each_pair { |k, v| puts "> #{k}: #{v}\r\n" }
102
- puts
103
- end
104
-
105
- content = result.response.content
106
- if not content.empty?
107
- if /^[[:print:]]+$/ =~ content
108
- puts content
111
+ content = res.content
112
+ if not content.empty?
113
+ if /^[[:print:]]+$/ =~ content
114
+ puts content
115
+ else
116
+ puts Hexy.new(content).to_s
117
+ end
118
+ end
109
119
  else
110
- puts Hexy.new(content).to_s
120
+ puts "> " + req.method + ' ' + req.url
121
+ if res
122
+ text = "< " + res.status.to_s + ' ' + res.message
123
+ if step.duration
124
+ text << ' in ' + green(pretty_print_duration(step.duration))
125
+ end
126
+ puts text
127
+ end
128
+ puts
111
129
  end
112
130
  end
113
131
  end
114
132
 
115
133
  def rush args
116
134
  continue = true
135
+ last_index = nil
117
136
  begin
118
137
  [ 'INT', 'STOP', 'HUP' ].each do |s|
119
138
  trap(s) { continue = false }
120
139
  end
121
140
  job = ::Blitz::Curl::Rush.queue args
122
- border = "+-----------+----------+----------+------------+--------------+--------+----------+\n"
123
- msg "rushing from #{job.region}..."
124
- header = true
141
+ msg "rushing from #{yellow(job.region)}..."
142
+ puts
125
143
  job.result do |result|
126
- if header
127
- header = nil
128
- msg border
129
- msg "| time (s) | users | hits/sec | kbytes/sec | latency (ms) | errors | timeouts |\n"
130
- msg border
144
+ print_rush_result args, result, last_index
145
+ if not result.timeline.empty?
146
+ last_index = result.timeline.size
131
147
  end
132
- print_rush_result result
133
- sleep 1.0 if not continue
148
+ sleep 2.0 if not continue
134
149
  continue
135
150
  end
136
- msg border
137
151
  puts
138
- msg "[aborted]" if not continue
152
+ msg "[#{red('aborted')}]" if not continue
139
153
  rescue ::Blitz::Curl::Error::Authorize => e
140
154
  authorize_error e
141
155
  rescue ::Blitz::Curl::Error::Region => e
142
- error "#{e.region}: #{e.message}"
156
+ error "#{yellow(e.region)}: #{red(e.message)}"
143
157
  rescue ::Blitz::Curl::Error => e
144
- error e.message
158
+ error red(e.message)
145
159
  end
146
160
  end
147
161
 
148
- def print_rush_result result
149
- recent = result.timeline[-1]
150
- kilobytes_per_second = 0
151
- hits_per_second = 0
152
- if result.timeline.size > 1
153
- last = result.timeline[-2]
154
- elapsed = recent.timestamp - last.timestamp
155
- hits_per_second = ( recent.hits - last.hits ) / ( recent.timestamp - last.timestamp )
156
- kilobytes_per_second = ( ( recent.rxbytes + recent.txbytes ) - ( last.rxbytes + last.txbytes ) / elapsed ) / 1024
157
- else
158
- hits_per_second = recent.hits/recent.timestamp
159
- kilobytes_per_second = ( ( recent.rxbytes + recent.txbytes )/recent.timestamp ) / 1024
162
+ def print_rush_result args, result, last_index
163
+ if last_index.nil?
164
+ print yellow("%6s " % "Time")
165
+ print "%6s " % "Users"
166
+ print green("%8s " % "Hits")
167
+ print magenta("%8s " % "Timeouts")
168
+ print red("%8s " % "Errors")
169
+ print green("%8s " % "Hits/s")
170
+ print "%s" % "Mbps"
171
+ puts
160
172
  end
161
173
 
162
- output = ['']
163
- output << "%10u " % recent.timestamp
164
- output << "%9u " % recent.volume
165
- output << "%9u " % hits_per_second
166
- output << "%11u " % kilobytes_per_second
167
-
168
- duration = recent.duration * 1000
169
- if duration >= 0
170
- output << "%13u " % duration
171
- else
172
- output << "%13s " % 'no data'
174
+ if last_index and result.timeline.size == last_index
175
+ return
173
176
  end
174
177
 
175
- output << "%7u " % recent.errors
176
- output << "%9u " % recent.timeouts
177
- output << ''
178
-
179
- if recent.volume > 0
180
- $stdout.print output.join('|')
181
- $stdout.print "\n"
178
+ last = result.timeline[-2]
179
+ curr = result.timeline[-1]
180
+ print yellow("%5.1fs " % curr.timestamp)
181
+ print "%6d " % curr.volume
182
+ print green("%8d " % curr.hits)
183
+ print magenta("%8d " % curr.timeouts)
184
+ print red("%8d " % curr.errors)
185
+
186
+ if last
187
+ elapsed = curr.timestamp - last.timestamp
188
+ mbps = ((curr.txbytes + curr.rxbytes) - (last.txbytes + last.rxbytes))/elapsed/1024.0/1024.0
189
+ htps = (curr.hits - last.hits)/elapsed
190
+ print green(" %7.2f " % htps)
191
+ print "%.2f" % mbps
182
192
  end
183
- $stdout.flush
193
+
194
+ print "\n"
184
195
  end
185
196
 
186
197
  def help
@@ -217,169 +228,173 @@ class Curl < Command # :nodoc:
217
228
  end
218
229
 
219
230
  def parse_cli argv
220
- hash = Hash.new
221
- hash['text'] = argv.join(' ')
222
-
231
+ hash = { 'steps' => [] }
232
+
223
233
  while not argv.empty?
224
- break if argv.first[0,1] != '-'
234
+ hash['steps'] << Hash.new
235
+ step = hash['steps'].last
236
+
237
+ while not argv.empty?
238
+ break if argv.first[0,1] != '-'
225
239
 
226
- k = argv.shift
227
- if [ '-A', '--user-agent' ].member? k
228
- hash['user-agent'] = shift(k, argv)
229
- next
230
- end
240
+ k = argv.shift
241
+ if [ '-A', '--user-agent' ].member? k
242
+ step['user-agent'] = shift(k, argv)
243
+ next
244
+ end
231
245
 
232
- if [ '-b', '--cookie' ].member? k
233
- # TODO: support cookie jars
234
- hash['cookies'] ||= []
235
- hash['cookies'] << shift(k, argv)
236
- next
237
- end
246
+ if [ '-b', '--cookie' ].member? k
247
+ step['cookies'] ||= []
248
+ step['cookies'] << shift(k, argv)
249
+ next
250
+ end
238
251
 
239
- if [ '-d', '--data' ].member? k
240
- hash['content'] ||= Hash.new
241
- hash['content']['data'] ||= []
242
- v = shift(k, argv)
243
- v = File.read v[1..-1] if v =~ /^@/
244
- hash['content']['data'] << v
245
- next
246
- end
252
+ if [ '-d', '--data' ].member? k
253
+ step['content'] ||= Hash.new
254
+ step['content']['data'] ||= []
255
+ v = shift(k, argv)
256
+ v = File.read v[1..-1] if v =~ /^@/
257
+ step['content']['data'] << v
258
+ next
259
+ end
247
260
 
248
- if [ '-D', '--dump-header' ].member? k
249
- hash['dump-header'] = shift(k, argv)
250
- next
251
- end
261
+ if [ '-D', '--dump-header' ].member? k
262
+ hash['dump-header'] = shift(k, argv)
263
+ next
264
+ end
252
265
 
253
- if [ '-e', '--referer'].member? k
254
- hash['referer'] = shift(k, argv)
255
- next
256
- end
266
+ if [ '-e', '--referer'].member? k
267
+ step['referer'] = shift(k, argv)
268
+ next
269
+ end
257
270
 
258
- if [ '-h', '--help' ].member? k
259
- hash['help'] = true
260
- next
261
- end
271
+ if [ '-h', '--help' ].member? k
272
+ hash['help'] = true
273
+ next
274
+ end
262
275
 
263
- if [ '-H', '--header' ].member? k
264
- hash['headers'] ||= []
265
- hash['headers'].push shift(k, argv)
266
- next
267
- end
276
+ if [ '-H', '--header' ].member? k
277
+ step['headers'] ||= []
278
+ step['headers'].push shift(k, argv)
279
+ next
280
+ end
268
281
 
269
- if [ '-p', '--pattern' ].member? k
270
- v = shift(k, argv)
271
- if not /^(\d+)-(\d+):(\d+)$/ =~ v
272
- raise Test::Unit::AssertionFailedError, "invalid ramp pattern"
282
+ if [ '-p', '--pattern' ].member? k
283
+ v = shift(k, argv)
284
+ v.split(',').each do |vt|
285
+ unless /^(\d+)-(\d+):(\d+)$/ =~ vt
286
+ raise Test::Unit::AssertionFailedError, "invalid ramp pattern"
287
+ end
288
+ hash['pattern'] ||= { 'iterations' => 1, 'intervals' => [] }
289
+ hash['pattern']['intervals'] << {
290
+ 'iterations' => 1,
291
+ 'start' => $1.to_i,
292
+ 'end' => $2.to_i,
293
+ 'duration' => $3.to_i
294
+ }
295
+ end
296
+ next
273
297
  end
274
- hash['pattern'] = {
275
- 'iterations' => 1,
276
- 'intervals' => [{
277
- 'iterations' => 1,
278
- 'start' => $1.to_i,
279
- 'end' => $2.to_i,
280
- 'duration' => $3.to_i
281
- }]
282
- }
283
- next
284
- end
285
298
 
286
- if [ '-r', '--region' ].member? k
287
- v = shift(k, argv)
288
- assert_match(/^california|virginia|singapore|ireland|japan$/, v, 'region must be one of california, virginia, singapore, japan or ireland')
289
- hash['region'] = v
290
- next
291
- end
299
+ if [ '-r', '--region' ].member? k
300
+ v = shift(k, argv)
301
+ assert_match(/^california|virginia|singapore|ireland|japan$/, v, 'region must be one of california, virginia, singapore, japan or ireland')
302
+ hash['region'] = v
303
+ next
304
+ end
292
305
 
293
- if [ '-s', '--status' ].member? k
294
- hash['status'] = shift(k, argv).to_i
295
- next
296
- end
306
+ if [ '-s', '--status' ].member? k
307
+ step['status'] = shift(k, argv).to_i
308
+ next
309
+ end
297
310
 
298
- if [ '-T', '--timeout' ].member? k
299
- hash['timeout'] = shift(k, argv).to_i
300
- next
301
- end
311
+ if [ '-T', '--timeout' ].member? k
312
+ step['timeout'] = shift(k, argv).to_i
313
+ next
314
+ end
302
315
 
303
- if [ '-u', '--user' ].member? k
304
- hash['user'] = shift(k, argv)
305
- next
306
- end
316
+ if [ '-u', '--user' ].member? k
317
+ step['user'] = shift(k, argv)
318
+ next
319
+ end
307
320
 
308
- if [ '-X', '--request' ].member? k
309
- hash['request'] = shift(k, argv)
310
- next
311
- end
321
+ if [ '-X', '--request' ].member? k
322
+ step['request'] = shift(k, argv)
323
+ next
324
+ end
312
325
 
313
- if /-v:(\S+)/ =~ k or /--variable:(\S+)/ =~ k
314
- variable_name = $1
315
- variable_parameter = shift(k, argv)
316
-
317
- assert_match(/^[a-zA-Z][a-zA-Z0-9]*$/, variable_name, "variable name must be alphanumeric: #{variable_name}")
318
-
319
- hash['variables'] ||= Hash.new
320
-
321
- parameter_hash = {}
322
- if variable_parameter.match(/^(list)?\[([^\]]+)\]$/)
323
- parameter_hash['type'] = 'list'
324
- parameter_hash['entries'] = $2.split(',')
325
- elsif variable_parameter.match(/^(a|alpha)$/)
326
- parameter_hash['type'] = 'alpha'
327
- elsif variable_parameter.match(/^(a|alpha)\[(\d+),(\d+)(,(\d+))??\]$/)
328
- parameter_hash['type'] = 'alpha'
329
- parameter_hash['min'] = $2.to_i
330
- parameter_hash['max'] = $3.to_i
331
- parameter_hash['count'] = $5 ? $5.to_i : 1000
332
- elsif variable_parameter.match(/^(n|number)$/)
333
- parameter_hash['type'] = 'number'
334
- elsif variable_parameter.match(/^(n|number)\[(-?\d+),(-?\d+)(,(\d+))?\]$/)
335
- parameter_hash['type'] = 'number'
336
- parameter_hash['min'] = $2.to_i
337
- parameter_hash['max'] = $3.to_i
338
- parameter_hash['count'] = $5 ? $5.to_i : 1000
339
- elsif variable_parameter.match(/^(u|udid)$/)
340
- parameter_hash['type'] = 'udid'
341
- else
342
- raise ArgumentError, "Invalid variable parameter to #{variable_name}: #{variable_parameter}"
326
+ if /-v:(\S+)/ =~ k or /--variable:(\S+)/ =~ k
327
+ vname = $1
328
+ vargs = shift(k, argv)
329
+
330
+ assert_match /^[a-zA-Z][a-zA-Z0-9]*$/, vname, "variable name must be alphanumeric: #{vname}"
331
+
332
+ step['variables'] ||= Hash.new
333
+ vhash = step['variables'][vname] = Hash.new
334
+ if vargs.match /^(list)?\[([^\]]+)\]$/
335
+ vhash['type'] = 'list'
336
+ vhash['entries'] = $2.split(',')
337
+ elsif vargs.match /^(a|alpha)$/
338
+ vhash['type'] = 'alpha'
339
+ elsif vargs.match /^(a|alpha)\[(\d+),(\d+)(,(\d+))??\]$/
340
+ vhash['type'] = 'alpha'
341
+ vhash['min'] = $2.to_i
342
+ vhash['max'] = $3.to_i
343
+ vhash['count'] = $5 ? $5.to_i : 1000
344
+ elsif vargs.match /^(n|number)$/
345
+ vhash['type'] = 'number'
346
+ elsif vargs.match /^(n|number)\[(-?\d+),(-?\d+)(,(\d+))?\]$/
347
+ vhash['type'] = 'number'
348
+ vhash['min'] = $2.to_i
349
+ vhash['max'] = $3.to_i
350
+ vhash['count'] = $5 ? $5.to_i : 1000
351
+ elsif vargs.match /^(u|udid)$/
352
+ vhash['type'] = 'udid'
353
+ else
354
+ raise ArgumentError, "Invalid variable args for #{vname}: #{vargs}"
355
+ end
356
+ next
343
357
  end
344
358
 
345
- hash['variables'][variable_name] = parameter_hash
346
- next
347
- end
359
+ if [ '-V', '--verbose' ].member? k
360
+ hash['verbose'] = true
361
+ next
362
+ end
348
363
 
349
- if [ '-V', '--verbose' ].member? k
350
- hash['verbose'] = true
351
- next
352
- end
364
+ if [ '-1', '--tlsv1' ].member? k
365
+ step['ssl'] = 'tlsv1'
366
+ next
367
+ end
353
368
 
354
- if [ '-1', '--tlsv1' ].member? k
355
- hash['ssl'] = 'tlsv1'
356
- next
357
- end
369
+ if [ '-2', '--sslv2' ].member? k
370
+ step['ssl'] = 'sslv2'
371
+ next
372
+ end
358
373
 
359
- if [ '-2', '--sslv2' ].member? k
360
- hash['ssl'] = 'sslv2'
361
- next
362
- end
374
+ if [ '-3', '--sslv3' ].member? k
375
+ step['ssl'] = 'sslv3'
376
+ next
377
+ end
363
378
 
364
- if [ '-3', '--sslv3' ].member? k
365
- hash['ssl'] = 'sslv3'
366
- next
379
+ raise ArgumentError, "Unknown option #{k}"
367
380
  end
368
381
 
369
- raise ArgumentError, "Unknown option #{k}"
382
+ if step.member? 'content'
383
+ data_size = step['content']['data'].inject(0) { |m, v| m + v.size }
384
+ assert(data_size < 10*1024, "POST content must be < 10K")
385
+ end
386
+
387
+ break if hash['help']
388
+
389
+ url = argv.shift
390
+ raise ArgumentError, "no URL specified!" if not url
391
+ step['url'] = url
370
392
  end
371
-
393
+
372
394
  if not hash['help']
373
- url = argv.shift
374
- if not url
395
+ if hash['steps'].empty?
375
396
  raise ArgumentError, "no URL specified!"
376
397
  end
377
- hash['url'] = url
378
- end
379
-
380
- if hash.member? 'content'
381
- data_size = hash['content']['data'].inject(0) { |m, v| m + v.size }
382
- assert(data_size < 10*1024, "POST content must be < 10K")
383
398
  end
384
399
 
385
400
  hash