kronk 1.2.5 → 1.3.0
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/.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
|