kronk 1.2.5 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/History.rdoc +30 -2
- data/Manifest.txt +14 -0
- data/README.rdoc +5 -7
- data/Rakefile +88 -4
- data/bin/kronk +2 -1
- data/bin/yzma +13 -0
- data/lib/kronk.rb +112 -430
- data/lib/kronk/cmd.rb +469 -0
- data/lib/kronk/data_set.rb +38 -44
- data/lib/kronk/diff.rb +105 -112
- data/lib/kronk/diff/ascii_format.rb +35 -0
- data/lib/kronk/diff/color_format.rb +49 -0
- data/lib/kronk/request.rb +6 -6
- data/lib/kronk/test.rb +15 -0
- data/lib/kronk/test/assertions.rb +97 -0
- data/lib/kronk/test/core_ext.rb +65 -0
- data/lib/kronk/test/helper_methods.rb +86 -0
- data/lib/yzma.rb +174 -0
- data/lib/yzma/randomizer.rb +54 -0
- data/lib/yzma/report.rb +47 -0
- data/test/test_assertions.rb +93 -0
- data/test/test_core_ext.rb +74 -0
- data/test/test_data_set.rb +41 -32
- data/test/test_diff.rb +50 -24
- data/test/test_helper_methods.rb +177 -0
- data/test/test_kronk.rb +3 -1
- metadata +36 -19
data/lib/kronk/cmd.rb
ADDED
@@ -0,0 +1,469 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
##
|
4
|
+
# Command line interface.
|
5
|
+
|
6
|
+
class Cmd
|
7
|
+
|
8
|
+
##
|
9
|
+
# Start an IRB console with the given http response object.
|
10
|
+
|
11
|
+
def self.irb resp
|
12
|
+
require 'irb'
|
13
|
+
|
14
|
+
$http_response = resp
|
15
|
+
$response = begin
|
16
|
+
resp.parsed_body
|
17
|
+
rescue Response::MissingParser
|
18
|
+
resp.body
|
19
|
+
end
|
20
|
+
|
21
|
+
puts "\nHTTP Response is in $http_response"
|
22
|
+
puts "Response data is in $response\n\n"
|
23
|
+
|
24
|
+
IRB.start
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
##
|
30
|
+
# Load the config-based requires.
|
31
|
+
|
32
|
+
def self.load_requires more_requires=nil
|
33
|
+
return unless Kronk.config[:requires] || more_requires
|
34
|
+
(Kronk.config[:requires] | more_requires.to_a).each{|lib| require lib }
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
##
|
39
|
+
# Creates the default config file at the given path.
|
40
|
+
|
41
|
+
def self.make_config_file
|
42
|
+
Dir.mkdir Kronk::CONFIG_DIR unless File.directory? Kronk::CONFIG_DIR
|
43
|
+
|
44
|
+
File.open Kronk::DEFAULT_CONFIG_FILE, "w+" do |file|
|
45
|
+
file << Kronk::DEFAULT_CONFIG.to_yaml
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
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
|
+
##
|
66
|
+
# Parse ARGV
|
67
|
+
|
68
|
+
def self.parse_args argv
|
69
|
+
options = {
|
70
|
+
:auth => {},
|
71
|
+
:no_body => false,
|
72
|
+
:proxy => {},
|
73
|
+
:uris => [],
|
74
|
+
:with_headers => false
|
75
|
+
}
|
76
|
+
|
77
|
+
options = parse_data_path_args options, argv
|
78
|
+
|
79
|
+
opts = OptionParser.new do |opt|
|
80
|
+
opt.program_name = File.basename $0
|
81
|
+
opt.version = Kronk::VERSION
|
82
|
+
opt.release = nil
|
83
|
+
|
84
|
+
opt.banner = <<-STR
|
85
|
+
|
86
|
+
#{opt.program_name} #{opt.version}
|
87
|
+
|
88
|
+
Parse and run diffs against data from live and cached http responses.
|
89
|
+
|
90
|
+
Usage:
|
91
|
+
#{opt.program_name} --help
|
92
|
+
#{opt.program_name} --version
|
93
|
+
#{opt.program_name} uri1 [uri2] [options...] [-- data-paths]
|
94
|
+
|
95
|
+
Examples:
|
96
|
+
#{opt.program_name} http://example.com/A
|
97
|
+
#{opt.program_name} http://example.com/B --prev --raw
|
98
|
+
#{opt.program_name} http://example.com/B.xml local/file/B.json
|
99
|
+
#{opt.program_name} file1.json file2.json -- **/key1=val1 -root/key?
|
100
|
+
|
101
|
+
Arguments after -- will be used to focus the diff on specific data points.
|
102
|
+
If the data paths start with a '-' the matched data points will be removed.
|
103
|
+
If the data paths start with a ":" the parent of the matched data is used.
|
104
|
+
The ':' and '-' modifiers may be used together in that order (':-').
|
105
|
+
|
106
|
+
Options:
|
107
|
+
STR
|
108
|
+
|
109
|
+
opt.on('--ascii', 'Return ascii formatted diff') do
|
110
|
+
Kronk.config[:diff_format] = :ascii_diff
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
opt.on('--color', 'Return color formatted diff') do
|
115
|
+
Kronk.config[:diff_format] = :color_diff
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
opt.on('--completion', 'Print bash completion file path and exit') do
|
120
|
+
file = File.join(File.dirname(__FILE__), "../script/kronk_completion")
|
121
|
+
puts File.expand_path(file)
|
122
|
+
exit 2
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
opt.on('--config STR', String,
|
127
|
+
'Load the given Kronk config file') do |value|
|
128
|
+
Kronk.load_config value
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
opt.on('-q', '--brief', 'Output only whether URI responses differ') do
|
133
|
+
Kronk.config[:brief] = true
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
opt.on('--format STR', String,
|
138
|
+
'Use a custom diff formatter') do |value|
|
139
|
+
Kronk.config[:diff_format] = value
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
opt.on('-i', '--include [header1,header2]', Array,
|
144
|
+
'Include all or given headers in response') do |value|
|
145
|
+
options[:with_headers] ||= []
|
146
|
+
|
147
|
+
if value
|
148
|
+
options[:with_headers].concat value if
|
149
|
+
Array === options[:with_headers]
|
150
|
+
else
|
151
|
+
options[:with_headers] = true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
opt.on('-I', '--head [header1,header2]', Array,
|
157
|
+
'Use all or given headers only in the response') do |value|
|
158
|
+
options[:with_headers] ||= []
|
159
|
+
|
160
|
+
if value
|
161
|
+
options[:with_headers].concat value if
|
162
|
+
Array === options[:with_headers]
|
163
|
+
else
|
164
|
+
options[:with_headers] = true
|
165
|
+
end
|
166
|
+
|
167
|
+
options[:no_body] = true
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
opt.on('--irb', 'Start an IRB console') do
|
172
|
+
options[:irb] = true
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
opt.on('-l', '--lines', 'Show line numbers') do
|
177
|
+
Kronk.config[:show_lines] = true
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
opt.on('--no-opts', 'Turn off config URI options') do
|
182
|
+
Kronk.config[:no_uri_options] = true
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
opt.on('-P', '--parser STR', String,
|
187
|
+
'Override default parser') do |value|
|
188
|
+
options[:parser] = value
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
opt.on('--prev', 'Use last response to diff against') do
|
193
|
+
options[:uris].unshift :cache
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
opt.on('-R', '--raw', 'Run diff on the raw data returned') do
|
198
|
+
options[:raw] = true
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
opt.on('-r', '--require lib1,lib2', Array,
|
203
|
+
'Require a library or gem') do |value|
|
204
|
+
options[:requires] ||= []
|
205
|
+
options[:requires].concat value
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
opt.on('--struct', 'Run diff on the data structure') do
|
210
|
+
options[:struct] = true
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
opt.on('-V', '--verbose', 'Make the operation more talkative') do
|
215
|
+
Kronk.config[:verbose] = true
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
opt.separator <<-STR
|
220
|
+
|
221
|
+
HTTP Options:
|
222
|
+
STR
|
223
|
+
|
224
|
+
opt.on('--clear-cookies', 'Delete all saved cookies') do
|
225
|
+
Kronk.clear_cookies!
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
opt.on('-d', '--data STR', String,
|
230
|
+
'Post data with the request') do |value|
|
231
|
+
options[:data] = value
|
232
|
+
options[:http_method] ||= 'POST'
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
opt.on('-H', '--header STR', String,
|
237
|
+
'Header to pass to the server request') do |value|
|
238
|
+
options[:headers] ||= {}
|
239
|
+
|
240
|
+
key, value = value.split ": ", 2
|
241
|
+
options[:headers][key] = value.strip
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
opt.on('-A', '--user-agent STR', String,
|
246
|
+
'User-Agent to send to server or a valid alias') do |value|
|
247
|
+
options[:user_agent] = value
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
opt.on('-L', '--location [NUM]', Integer,
|
252
|
+
'Follow the location header always or num times') do |value|
|
253
|
+
options[:follow_redirects] = value || true
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
opt.on('--no-cookies', 'Don\'t use cookies for this session') do
|
258
|
+
options[:no_cookies] = true
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
opt.on('-?', '--query STR', String,
|
263
|
+
'Append query to URLs') do |value|
|
264
|
+
options[:query] = value
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
opt.on('--suff STR', String,
|
269
|
+
'Add common path items to the end of each URL') do |value|
|
270
|
+
options[:uri_suffix] = value
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
opt.on('--timeout INT', Integer,
|
275
|
+
'Timeout for http connection in seconds') do |value|
|
276
|
+
Kronk.config[:timeout] = value
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
opt.on('-U', '--proxy-user STR', String,
|
281
|
+
'Set proxy user and/or password: usr[:pass]') do |value|
|
282
|
+
options[:proxy][:username], options[:proxy][:password] =
|
283
|
+
value.split ":", 2
|
284
|
+
|
285
|
+
options[:proxy][:password] ||= query_password "Proxy password:"
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
opt.on('-u', '--user STR', String,
|
290
|
+
'Set server auth user and/or password: usr[:pass]') do |value|
|
291
|
+
options[:auth][:username], options[:auth][:password] =
|
292
|
+
value.split ":", 2
|
293
|
+
|
294
|
+
options[:auth][:password] ||= query_password "Server password:"
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
opt.on('-X', '--request STR', String,
|
299
|
+
'The request method to use') do |value|
|
300
|
+
options[:http_method] = value
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
opt.on('-x', '--proxy STR', String,
|
305
|
+
'Use HTTP proxy on given port: host[:port]') do |value|
|
306
|
+
options[:proxy][:address], options[:proxy][:port] = value.split ":", 2
|
307
|
+
end
|
308
|
+
|
309
|
+
opt.separator nil
|
310
|
+
end
|
311
|
+
|
312
|
+
opts.parse! argv
|
313
|
+
|
314
|
+
unless $stdin.tty?
|
315
|
+
io = StringIO.new $stdin.read
|
316
|
+
options[:uris] << io
|
317
|
+
end
|
318
|
+
|
319
|
+
options[:uris].concat argv
|
320
|
+
options[:uris].slice!(2..-1)
|
321
|
+
|
322
|
+
argv.clear
|
323
|
+
|
324
|
+
raise OptionParser::MissingArgument, "You must enter at least one URI" if
|
325
|
+
options[:uris].empty?
|
326
|
+
|
327
|
+
options
|
328
|
+
|
329
|
+
rescue => e
|
330
|
+
$stderr << "\nError: #{e.message}\n"
|
331
|
+
$stderr << "See 'kronk --help' for usage\n\n"
|
332
|
+
exit 1
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
##
|
337
|
+
# Searches ARGV and returns data paths to add or exclude in the diff.
|
338
|
+
# Returns the array [only_paths, except_paths]
|
339
|
+
|
340
|
+
def self.parse_data_path_args options, argv
|
341
|
+
return options unless argv.include? "--"
|
342
|
+
|
343
|
+
data_paths = argv.slice! argv.index("--")..-1
|
344
|
+
data_paths.shift
|
345
|
+
|
346
|
+
data_paths.each do |path|
|
347
|
+
if path[0,1] == "-"
|
348
|
+
(options[:ignore_data] ||= []) << path[1..-1]
|
349
|
+
|
350
|
+
elsif path[0,2] == ":-"
|
351
|
+
(options[:ignore_data_with] ||= []) << path[2..-1]
|
352
|
+
|
353
|
+
elsif path[0,1] == ":"
|
354
|
+
(options[:only_data_with] ||= []) << path[1..-1]
|
355
|
+
|
356
|
+
else
|
357
|
+
(options[:only_data] ||= []) << path
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
options
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
##
|
366
|
+
# Ask the user for a password from stdin the command line.
|
367
|
+
|
368
|
+
def self.query_password str=nil
|
369
|
+
$stderr << "#{(str || "Password:")} "
|
370
|
+
system "stty -echo"
|
371
|
+
password = $stdin.gets.chomp
|
372
|
+
ensure
|
373
|
+
system "stty echo"
|
374
|
+
$stderr << "\n"
|
375
|
+
password
|
376
|
+
end
|
377
|
+
|
378
|
+
|
379
|
+
##
|
380
|
+
# Runs the kronk command with the given terminal args.
|
381
|
+
|
382
|
+
def self.run argv=ARGV
|
383
|
+
|
384
|
+
options = parse_args argv
|
385
|
+
|
386
|
+
begin
|
387
|
+
Kronk.load_config
|
388
|
+
|
389
|
+
rescue Errno::ENOENT
|
390
|
+
make_config_file
|
391
|
+
|
392
|
+
$stderr << "\nNo config file was found.\n"
|
393
|
+
$stderr << "Created default config in #{DEFAULT_CONFIG_FILE}\n"
|
394
|
+
$stderr << "Edit file if necessary and try again.\n"
|
395
|
+
exit 2
|
396
|
+
|
397
|
+
rescue Errno::ENOTDIR
|
398
|
+
move_config_file
|
399
|
+
|
400
|
+
$stderr << "\nOld config file was moved to #{DEFAULT_CONFIG_FILE}\n"
|
401
|
+
$stderr << "Edit file if necessary and try again.\n"
|
402
|
+
exit 2
|
403
|
+
end
|
404
|
+
|
405
|
+
Kronk.load_cookie_jar
|
406
|
+
|
407
|
+
load_requires options[:requires]
|
408
|
+
|
409
|
+
at_exit do
|
410
|
+
Kronk.save_cookie_jar
|
411
|
+
Kronk.save_history
|
412
|
+
end
|
413
|
+
|
414
|
+
trap 'INT' do
|
415
|
+
exit 2
|
416
|
+
end
|
417
|
+
|
418
|
+
|
419
|
+
options[:cache_response] =
|
420
|
+
Kronk.config[:cache_file] if Kronk.config[:cache_file]
|
421
|
+
|
422
|
+
uri1, uri2 = options.delete :uris
|
423
|
+
|
424
|
+
if uri1 && uri2
|
425
|
+
diff = Kronk.compare uri1, uri2, options
|
426
|
+
puts "#{diff.formatted}\n" unless Kronk.config[:brief]
|
427
|
+
|
428
|
+
if Kronk.config[:verbose] || Kronk.config[:brief]
|
429
|
+
$stdout << "Found #{diff.count} diff(s).\n"
|
430
|
+
end
|
431
|
+
|
432
|
+
exit 1 if diff.count > 0
|
433
|
+
|
434
|
+
else
|
435
|
+
out = Kronk.retrieve_data_string uri1, options
|
436
|
+
out = Diff.insert_line_nums out if Kronk.config[:show_lines]
|
437
|
+
puts out
|
438
|
+
end
|
439
|
+
|
440
|
+
rescue Request::Exception, Response::MissingParser, Errno::ECONNRESET => e
|
441
|
+
$stderr << "\nError: #{e.message}\n"
|
442
|
+
exit 2
|
443
|
+
end
|
444
|
+
|
445
|
+
|
446
|
+
##
|
447
|
+
# Print string only if verbose
|
448
|
+
|
449
|
+
def self.verbose str
|
450
|
+
$stdout << "#{str}\n" if Kronk.config[:verbose]
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
##
|
455
|
+
# Write a warning to stderr.
|
456
|
+
|
457
|
+
def self.warn str
|
458
|
+
$stderr << "Warning: #{str}\n"
|
459
|
+
end
|
460
|
+
|
461
|
+
|
462
|
+
##
|
463
|
+
# Returns true if kronk is running on ruby for windows.
|
464
|
+
|
465
|
+
def self.windows?
|
466
|
+
!!(RUBY_PLATFORM.downcase =~ /mswin|mingw|cygwin/)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|