kronk 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/kronk/cmd.rb CHANGED
@@ -6,7 +6,7 @@ class Kronk
6
6
  class Cmd
7
7
 
8
8
  ##
9
- # Start an IRB console with the given http response object.
9
+ # Start an IRB console with the given Kronk::Response object.
10
10
 
11
11
  def self.irb resp
12
12
  require 'irb'
@@ -18,11 +18,11 @@ class Kronk
18
18
  resp.body
19
19
  end
20
20
 
21
- puts "\nHTTP Response is in $http_response"
22
- puts "Response data is in $response\n\n"
21
+ $stdout.puts "\nHTTP Response is in $http_response"
22
+ $stdout.puts "Response data is in $response\n\n"
23
23
 
24
24
  IRB.start
25
- exit 1
25
+ false
26
26
  end
27
27
 
28
28
 
@@ -47,21 +47,6 @@ class Kronk
47
47
  end
48
48
 
49
49
 
50
- ##
51
- # Moves the old config file to the new directory structure.
52
-
53
- def self.move_config_file
54
- require 'fileutils'
55
-
56
- kronk_tmp_config = ".kronk.tmp"
57
- File.rename Kronk::CONFIG_DIR, kronk_tmp_config
58
-
59
- Dir.mkdir Kronk::CONFIG_DIR
60
-
61
- FileUtils.mv kronk_tmp_config, Kronk::DEFAULT_CONFIG_FILE
62
- end
63
-
64
-
65
50
  ##
66
51
  # Parse ARGV
67
52
 
@@ -69,6 +54,7 @@ class Kronk
69
54
  options = {
70
55
  :auth => {},
71
56
  :no_body => false,
57
+ :player => {},
72
58
  :proxy => {},
73
59
  :uris => [],
74
60
  :with_headers => false
@@ -115,8 +101,10 @@ Parse and run diffs against data from live and cached http responses.
115
101
 
116
102
 
117
103
  opt.on('--completion', 'Print bash completion file path and exit') do
118
- file = File.join(File.dirname(__FILE__), "../script/kronk_completion")
119
- puts File.expand_path(file)
104
+ file = File.join(File.dirname(__FILE__),
105
+ "../../script/kronk_completion")
106
+
107
+ $stdout.puts File.expand_path(file)
120
108
  exit 2
121
109
  end
122
110
 
@@ -138,7 +126,7 @@ Parse and run diffs against data from live and cached http responses.
138
126
  end
139
127
 
140
128
 
141
- opt.on('-i', '--include [header1,header2]', Array,
129
+ opt.on('-i', '--include [HEADER1,HEADER2]', Array,
142
130
  'Include all or given headers in response') do |value|
143
131
  options[:with_headers] ||= []
144
132
 
@@ -148,10 +136,12 @@ Parse and run diffs against data from live and cached http responses.
148
136
  else
149
137
  options[:with_headers] = true
150
138
  end
139
+
140
+ options[:no_body] = false
151
141
  end
152
142
 
153
143
 
154
- opt.on('-I', '--head [header1,header2]', Array,
144
+ opt.on('-I', '--head [HEADER1,HEADER2]', Array,
155
145
  'Use all or given headers only in the response') do |value|
156
146
  options[:with_headers] ||= []
157
147
 
@@ -187,7 +177,7 @@ Parse and run diffs against data from live and cached http responses.
187
177
 
188
178
 
189
179
  opt.on('-P', '--parser STR', String,
190
- 'Override default parser') do |value|
180
+ 'Override default response body parser') do |value|
191
181
  options[:parser] = value
192
182
  end
193
183
 
@@ -202,13 +192,18 @@ Parse and run diffs against data from live and cached http responses.
202
192
  end
203
193
 
204
194
 
205
- opt.on('-r', '--require lib1,lib2', Array,
195
+ opt.on('-r', '--require LIB1,LIB2', Array,
206
196
  'Require a library or gem') do |value|
207
197
  options[:requires] ||= []
208
198
  options[:requires].concat value
209
199
  end
210
200
 
211
201
 
202
+ opt.on('--ruby', 'Use legacy Ruby renderer') do
203
+ Kronk.config[:render_lang] = 'ruby'
204
+ end
205
+
206
+
212
207
  opt.on('--struct', 'Run diff on the data structure') do
213
208
  options[:struct] = true
214
209
  end
@@ -219,6 +214,51 @@ Parse and run diffs against data from live and cached http responses.
219
214
  end
220
215
 
221
216
 
217
+ opt.separator <<-STR
218
+
219
+ Player Options:
220
+ STR
221
+
222
+ opt.on('-c', '--concurrency NUM', Integer,
223
+ 'Number of concurrent requests to make; default: 1') do |num|
224
+ options[:player][:concurrency] = num
225
+ end
226
+
227
+
228
+ opt.on('-n', '--number NUM', Integer,
229
+ 'Total number of requests to make') do |num|
230
+ options[:player][:number] = num
231
+ end
232
+
233
+
234
+ opt.on('-o', '--replay-out [FORMAT]',
235
+ 'Output format used by --replay; default: stream') do |output|
236
+ options[:player][:output] = output || :stream
237
+ end
238
+
239
+
240
+ opt.on('-p', '--replay [FILE]',
241
+ 'Replay the given file or STDIN against URIs') do |file|
242
+ options[:player][:io] = File.open(file, "r") if file
243
+ options[:player][:io] ||= $stdin if !$stdin.tty?
244
+ options[:player][:output] ||= :suite
245
+ end
246
+
247
+
248
+ opt.on('--benchmark [FILE]', 'Same as -p [FILE] -o benchmark') do |file|
249
+ options[:player][:io] = File.open(file, "r") if file
250
+ options[:player][:io] ||= $stdin if !$stdin.tty?
251
+ options[:player][:output] = :benchmark
252
+ end
253
+
254
+
255
+ opt.on('--stream [FILE]', 'Same as -p [FILE] -o stream') do |file|
256
+ options[:player][:io] = File.open(file, "r") if file
257
+ options[:player][:io] ||= $stdin if !$stdin.tty?
258
+ options[:player][:output] = :stream
259
+ end
260
+
261
+
222
262
  opt.separator <<-STR
223
263
 
224
264
  HTTP Options:
@@ -240,8 +280,8 @@ Parse and run diffs against data from live and cached http responses.
240
280
  'Header to pass to the server request') do |value|
241
281
  options[:headers] ||= {}
242
282
 
243
- key, value = value.split ": ", 2
244
- options[:headers][key] = value.strip
283
+ key, value = value.split /:\s*/, 2
284
+ options[:headers][key] = value.to_s.strip
245
285
  end
246
286
 
247
287
 
@@ -274,7 +314,7 @@ Parse and run diffs against data from live and cached http responses.
274
314
  end
275
315
 
276
316
 
277
- opt.on('--timeout INT', Integer,
317
+ opt.on('-t', '--timeout INT', Integer,
278
318
  'Timeout for http connection in seconds') do |value|
279
319
  Kronk.config[:timeout] = value
280
320
  end
@@ -314,7 +354,14 @@ Parse and run diffs against data from live and cached http responses.
314
354
 
315
355
  opts.parse! argv
316
356
 
317
- unless $stdin.tty?
357
+ unless options[:player].empty?
358
+ options[:player] = Player.new options[:player]
359
+ else
360
+ options.delete :player
361
+ end
362
+
363
+ if !$stdin.tty? && !(options[:player] && options[:player].input.io)
364
+ io = $stdin
318
365
  io = StringIO.new $stdin.read
319
366
  options[:uris] << io
320
367
  end
@@ -322,22 +369,22 @@ Parse and run diffs against data from live and cached http responses.
322
369
  options[:uris].concat argv
323
370
  options[:uris].slice!(2..-1)
324
371
 
325
- if options[:uris].empty? && File.file?(Kronk.config[:cache_file])
372
+ if options[:uris].empty? && File.file?(Kronk.config[:cache_file]) &&
373
+ options[:player].nil?
326
374
  verbose "No URI specified - using kronk cache"
327
375
  options[:uris] << Kronk.config[:cache_file]
328
376
  end
329
377
 
330
378
  argv.clear
331
379
 
332
- raise OptionParser::MissingArgument, "You must enter at least one URI" if
333
- options[:uris].empty?
380
+ raise "You must enter at least one URI" if options[:uris].empty?
334
381
 
335
382
  options
336
383
 
337
384
  rescue => e
338
- $stderr << "\nError: #{e.message}\n"
339
- $stderr << "See 'kronk --help' for usage\n\n"
340
- exit 1
385
+ error e.message, e.backtrace
386
+ $stderr.puts "See 'kronk --help' for usage\n\n"
387
+ exit 2
341
388
  end
342
389
 
343
390
 
@@ -354,15 +401,6 @@ Parse and run diffs against data from live and cached http responses.
354
401
  data_paths.each do |path|
355
402
  if path[0,1] == "-"
356
403
  (options[:ignore_data] ||= []) << path[1..-1]
357
-
358
- elsif path[0,2] == ":-"
359
- warn "The :path notation is deprecated, use path/.."
360
- (options[:ignore_data_with] ||= []) << path[2..-1]
361
-
362
- elsif path[0,1] == ":"
363
- warn "The :path notation is deprecated, use path/.."
364
- (options[:only_data_with] ||= []) << path[1..-1]
365
-
366
404
  else
367
405
  (options[:only_data] ||= []) << path
368
406
  end
@@ -395,17 +433,9 @@ Parse and run diffs against data from live and cached http responses.
395
433
 
396
434
  rescue Errno::ENOENT
397
435
  make_config_file
398
-
399
- $stderr << "\nNo config file was found.\n"
400
- $stderr << "Created default config in #{DEFAULT_CONFIG_FILE}\n"
401
- $stderr << "Edit file if necessary and try again.\n"
402
- exit 2
403
-
404
- rescue Errno::ENOTDIR
405
- move_config_file
406
-
407
- $stderr << "\nOld config file was moved to #{DEFAULT_CONFIG_FILE}\n"
408
- $stderr << "Edit file if necessary and try again.\n"
436
+ error "No config file was found.\n" +
437
+ "Created default config in #{DEFAULT_CONFIG_FILE}\n" +
438
+ "Edit file if necessary and try again.\n"
409
439
  exit 2
410
440
  end
411
441
 
@@ -413,7 +443,7 @@ Parse and run diffs against data from live and cached http responses.
413
443
 
414
444
  Kronk.load_cookie_jar
415
445
 
416
- load_requires options[:requires]
446
+ load_requires options.delete(:requires)
417
447
 
418
448
  at_exit do
419
449
  Kronk.save_cookie_jar
@@ -424,32 +454,115 @@ Parse and run diffs against data from live and cached http responses.
424
454
  exit 2
425
455
  end
426
456
 
457
+ uri1, uri2 = options.delete :uris
458
+ runner = options.delete(:player) || self
459
+
460
+ success =
461
+ if uri1 && uri2
462
+ runner.compare uri1, uri2, options
463
+ else
464
+ runner.request uri1, options
465
+ end
466
+
467
+ exit 1 unless success
468
+
469
+ rescue Kronk::Exception, Response::MissingParser, Errno::ECONNRESET => e
470
+ error e.message, e.backtrace
471
+ exit 2
472
+ end
473
+
474
+
475
+ ##
476
+ # Performs a Kronk compare and renders it to $stdout.
477
+
478
+ def self.compare uri1, uri2, options={}
479
+ kronk = Kronk.new options
480
+ kronk.compare uri1, uri2
481
+ render kronk, options
482
+ end
483
+
484
+
485
+ ##
486
+ # Performs a single Kronk request and renders it to $stdout.
487
+
488
+ def self.request uri, options={}
489
+ kronk = Kronk.new options
490
+ kronk.retrieve uri
491
+ render kronk, options
492
+ end
493
+
494
+
495
+ ##
496
+ # Renders the results of a Kronk compare or retrieve
497
+ # to $stdout.
498
+
499
+ def self.render kronk, options={}
500
+ cache_response kronk.response
501
+
502
+ if options[:irb]
503
+ irb kronk.response
504
+
505
+ elsif kronk.diff
506
+ render_diff kronk.diff
507
+
508
+ elsif kronk.response
509
+ render_response kronk.response, kronk.options
510
+ end
511
+ end
512
+
513
+
514
+ ##
515
+ # Renders a Diff instance to $stdout
427
516
 
428
- options[:cache_response] =
429
- Kronk.config[:cache_file] if Kronk.config[:cache_file]
517
+ def self.render_diff diff
518
+ $stdout.puts diff.formatted unless Kronk.config[:brief]
430
519
 
431
- uri1, uri2 = options.delete :uris
520
+ if Kronk.config[:verbose] || Kronk.config[:brief]
521
+ $stdout.puts "Found #{diff.count} diff(s)."
522
+ end
432
523
 
433
- if uri1 && uri2
434
- diff = Kronk.compare uri1, uri2, options
524
+ diff.count == 0
525
+ end
435
526
 
436
- puts "#{diff.formatted}\n" unless Kronk.config[:brief]
437
527
 
438
- if Kronk.config[:verbose] || Kronk.config[:brief]
439
- $stdout << "Found #{diff.count} diff(s).\n"
440
- end
528
+ ##
529
+ # Output a Kronk::Response instance. Returns true if response code
530
+ # is in the 200 range.
441
531
 
442
- exit 1 if diff.count > 0
532
+ def self.render_response response, options={}
533
+ str = response.stringify options
534
+ str = Diff.insert_line_nums str if Kronk.config[:show_lines]
535
+ $stdout.puts str
443
536
 
444
- else
445
- out = Kronk.retrieve_data_string uri1, options
446
- out = Diff.insert_line_nums out if Kronk.config[:show_lines]
447
- puts out
537
+ verbose "\nResp. Time: #{response.time.to_f}"
538
+
539
+ response.success?
540
+ end
541
+
542
+
543
+ ##
544
+ # Saves the raw http response to a cache file.
545
+
546
+ def self.cache_response resp, filepath=nil
547
+ filepath ||= Kronk.config[:cache_file]
548
+ return unless filepath
549
+
550
+ begin
551
+ File.open(filepath, "wb+") do |file|
552
+ file.write resp.raw
553
+ end
554
+ rescue => e
555
+ error "#{e.class}: #{e.message}"
448
556
  end
557
+ end
449
558
 
450
- rescue Request::Exception, Response::MissingParser, Errno::ECONNRESET => e
451
- $stderr << "\nError: #{e.message}\n"
452
- exit 2
559
+
560
+ ##
561
+ # Print and error string
562
+
563
+ def self.error str, more=nil
564
+ $stderr.puts "\nError: #{str}"
565
+ $stderr.puts more if Kronk.config[:verbose] && more
453
566
  end
454
567
 
455
568
 
@@ -473,7 +586,8 @@ Parse and run diffs against data from live and cached http responses.
473
586
  # Returns true if kronk is running on ruby for windows.
474
587
 
475
588
  def self.windows?
476
- !!(RUBY_PLATFORM.downcase =~ /mswin|mingw|cygwin/)
589
+ $RUBY_PLATFORM ||= RUBY_PLATFORM
590
+ !!($RUBY_PLATFORM.downcase =~ /mswin|mingw|cygwin/)
477
591
  end
478
592
  end
479
593
  end
@@ -0,0 +1,90 @@
1
+ class Kronk
2
+
3
+ # Generic Request exception.
4
+ class Exception < ::Exception; end
5
+
6
+ # Raised when the URI was not resolvable.
7
+ class NotFoundError < Exception; end
8
+
9
+ # Raised when HTTP times out.
10
+ class TimeoutError < Exception; end
11
+
12
+ # Raised when a missing (but non-mandatory) dependency can't be loaded.
13
+ class MissingDependency < Exception; end
14
+
15
+
16
+ # Config directory.
17
+ CONFIG_DIR = File.expand_path "~/.kronk"
18
+
19
+ # Default config file to load. Defaults to ~/.kronk.
20
+ DEFAULT_CONFIG_FILE = File.join CONFIG_DIR, "rc"
21
+
22
+ # Default cache file.
23
+ DEFAULT_CACHE_FILE = File.join CONFIG_DIR, "cache"
24
+
25
+ # Default cookies file.
26
+ DEFAULT_COOKIES_FILE = File.join CONFIG_DIR, "cookies"
27
+
28
+ # Default file with history of unique URIs. (Used for autocomplete)
29
+ DEFAULT_HISTORY_FILE = File.join CONFIG_DIR, "history"
30
+
31
+
32
+ # Default Content-Type header to parser mapping.
33
+ DEFAULT_CONTENT_TYPES = {
34
+ 'js' => 'JSON',
35
+ 'json' => 'JSON',
36
+ 'plist' => 'PlistParser',
37
+ 'xml' => 'XMLParser'
38
+ }
39
+
40
+
41
+ # Recursive Hash merge proc.
42
+ DEEP_MERGE =
43
+ proc do |key,v1,v2|
44
+ Hash === v1 && Hash === v2 ? v1.merge(v2,&DEEP_MERGE) : v2
45
+ end
46
+
47
+
48
+ # Aliases for various user-agents. Thanks Mechanize! :)
49
+ USER_AGENTS = {
50
+ 'kronk' =>
51
+ "Kronk/#{VERSION} (http://github.com/yaksnrainbows/kronk)",
52
+ 'iphone' =>
53
+ "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1C28 Safari/419.3",
54
+ 'linux_firefox' =>
55
+ "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.1) Gecko/20100122 firefox/3.6.1",
56
+ 'linux_mozilla' =>
57
+ "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4) Gecko/20030624",
58
+ 'mac_mozilla' =>
59
+ "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4a) Gecko/20030401",
60
+ 'linux_konqueror' =>
61
+ "Mozilla/5.0 (compatible; Konqueror/3; Linux)",
62
+ 'mac_firefox' =>
63
+ "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6",
64
+ 'mac_safari' =>
65
+ "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; de-at) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10",
66
+ 'win_ie6' =>
67
+ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
68
+ 'win_ie7' =>
69
+ "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
70
+ 'win_mozilla' =>
71
+ "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.4b) Gecko/20030516 Mozilla Firebird/0.6"
72
+ }
73
+
74
+
75
+ # Default config to use.
76
+ DEFAULT_CONFIG = {
77
+ :content_types => DEFAULT_CONTENT_TYPES.dup,
78
+ :cache_file => DEFAULT_CACHE_FILE,
79
+ :cookies_file => DEFAULT_COOKIES_FILE,
80
+ :default_host => "http://localhost:3000",
81
+ :diff_format => :ascii_diff,
82
+ :history_file => DEFAULT_HISTORY_FILE,
83
+ :indentation => 1,
84
+ :requires => [],
85
+ :show_lines => false,
86
+ :uri_options => {},
87
+ :use_cookies => true,
88
+ :user_agents => USER_AGENTS.dup
89
+ }
90
+ end