kronk 1.1.0 → 1.1.1

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/History.rdoc CHANGED
@@ -1,3 +1,17 @@
1
+ === 1.1.1 / 2010-12-22
2
+
3
+ * Enhancements:
4
+
5
+ * Added diff-like brief option.
6
+
7
+ * Added support for loading alternate Kronk configs from the command line.
8
+
9
+ * Bugfixes:
10
+
11
+ * Changed uri options config to allow matching of multiple keys.
12
+
13
+ * Implementing smart merging of uri options with command line options.
14
+
1
15
  === 1.1.0 / 2010-12-12
2
16
 
3
17
  * Enhancements:
data/README.rdoc CHANGED
@@ -198,7 +198,7 @@ are supported:
198
198
 
199
199
  * | effectively works as an "OR" character, matches /^val1|val2$/
200
200
 
201
- * = is used to match values and may be used in conjuction with a key or not.
201
+ * \= is used to match values and may be used in conjuction with a key or not.
202
202
 
203
203
  * Parentheses are ok to use if in conjunction with other special characters
204
204
  for a given path item, such as: /path(1|2)
data/Rakefile CHANGED
@@ -17,6 +17,7 @@ Hoe.spec 'kronk' do
17
17
  self.extra_deps << ['i18n', '~>0.5']
18
18
  self.extra_deps << ['activesupport', '>=2.0.0']
19
19
  self.extra_deps << ['cookiejar', '~>0.3.0']
20
+ self.extra_deps << ['rack', '~>1.0']
20
21
 
21
22
  self.extra_dev_deps << ['mocha', '~>0.9.10']
22
23
  end
data/lib/kronk.rb CHANGED
@@ -3,6 +3,7 @@ require 'plist'
3
3
  require 'json'
4
4
  require 'nokogiri'
5
5
  require 'cookiejar'
6
+ require 'rack'
6
7
 
7
8
  # Support for new and old versions of ActiveSupport
8
9
  begin
@@ -19,7 +20,7 @@ require 'yaml'
19
20
  class Kronk
20
21
 
21
22
  # This gem's version.
22
- VERSION = '1.1.0'
23
+ VERSION = '1.1.1'
23
24
 
24
25
 
25
26
  require 'kronk/data_set'
@@ -175,15 +176,64 @@ class Kronk
175
176
 
176
177
 
177
178
  ##
178
- # Returns config-defined options for a given uri.
179
- # Returns empty Hash if none found.
179
+ # Returns merged config-defined options for a given uri.
180
+ # Values in cmd_opts take precedence.
181
+ # Returns cmd_opts Hash if none found.
180
182
 
181
- def self.options_for_uri uri
182
- config[:uri_options].each do |key, options|
183
- return options if uri == key || uri =~ %r{#{key}}
183
+ def self.merge_options_for_uri uri, cmd_opts={}
184
+ out_opts = Hash.new.merge cmd_opts
185
+
186
+ config[:uri_options].each do |matcher, options|
187
+ next unless (uri == matcher || uri =~ %r{#{matcher}}) && Hash === options
188
+
189
+ options.each do |key, val|
190
+ if !out_opts[key]
191
+ out_opts[key] = val
192
+ next
193
+ end
194
+
195
+
196
+ case key
197
+
198
+ # Hash or uri query String
199
+ when :data, :query
200
+ val = Rack::Utils.parse_nested_query val if String === val
201
+
202
+ out_opts[key] = Rack::Utils.parse_nested_query out_opts[key] if
203
+ String === out_opts[key]
204
+
205
+ out_opts[key] = val.merge out_opts[key], &DataSet::DEEP_MERGE
206
+
207
+ # Hashes
208
+ when :headers, :auth
209
+ out_opts[key] = val.merge out_opts[key]
210
+
211
+ # Proxy hash or String
212
+ when :proxy
213
+ if Hash === val && Hash === out_opts[key]
214
+ out_opts[key] = val.merge out_opts[key]
215
+
216
+ elsif Hash === val && String === out_opts[key]
217
+ val[:address] = out_opts[key]
218
+ out_opts[key] = val
219
+
220
+ elsif String === val && Hash === out_opts[key]
221
+ out_opts[key][:address] ||= val
222
+ end
223
+
224
+ # Response headers - Boolean, String, or Array
225
+ when :with_headers
226
+ next if out_opts[key] == true || out_opts[key] && val == true
227
+ out_opts[key] = [*out_opts[key]] | [*val]
228
+
229
+ # String or Array
230
+ when :only_data, :only_data_with, :ignore_data, :ignore_data_with
231
+ out_opts[key] = [*out_opts[key]] | [*val]
232
+ end
233
+ end
184
234
  end
185
235
 
186
- Hash.new
236
+ out_opts
187
237
  end
188
238
 
189
239
 
@@ -218,8 +268,8 @@ class Kronk
218
268
 
219
269
  def self.save_cookie_jar file=nil
220
270
  file ||= config[:cookies_file]
221
- File.open(file, "w") do |file|
222
- file.write @cookie_jar.to_yaml
271
+ File.open(file, "w") do |f|
272
+ f.write @cookie_jar.to_yaml
223
273
  end
224
274
  end
225
275
 
@@ -255,9 +305,11 @@ class Kronk
255
305
  # :auth:: Hash - must contain :username and :password; defaults to nil
256
306
  # :proxy:: Hash/String - http proxy to use; defaults to nil
257
307
  # :only_data:: String/Array - extracts the data from given data paths
308
+ # :only_data_with:: String/Array - extracts the data from given parent paths
258
309
  # :ignore_data:: String/Array - defines which data points to exclude
310
+ # :ignore_data_with:: String/Array - defines which parent data to exclude
259
311
  # :with_headers:: Bool/String/Array - defines which headers to include
260
- # :parser:: Object - The parser to use for the body; default nil
312
+ # :parser:: Object/String - The parser to use for the body; default nil
261
313
  # :raw:: Bool - run diff on raw strings
262
314
  #
263
315
  # Returns a diff object.
@@ -275,7 +327,7 @@ class Kronk
275
327
  # See Kronk.compare for supported options.
276
328
 
277
329
  def self.retrieve_data_string query, options={}
278
- options = options.merge options_for_uri(query)
330
+ options = merge_options_for_uri query, options
279
331
 
280
332
  resp = Request.retrieve query, options
281
333
 
@@ -306,7 +358,7 @@ class Kronk
306
358
  $stderr << "\nNo config file was found.\n\n"
307
359
  $stderr << "Created default config in #{DEFAULT_CONFIG_FILE}\n"
308
360
  $stderr << "Edit file if necessary and try again.\n"
309
- exit 1
361
+ exit 2
310
362
  end
311
363
 
312
364
  load_cookie_jar
@@ -325,8 +377,13 @@ class Kronk
325
377
 
326
378
  if uri1 && uri2
327
379
  diff = compare uri1, uri2, options
328
- puts diff.formatted
329
- verbose "\n\nFound #{diff.count} diff(s).\n"
380
+ puts "#{diff.formatted}\n" unless config[:brief]
381
+
382
+ if config[:verbose] || config[:brief]
383
+ $stdout << "Found #{diff.count} diff(s).\n"
384
+ end
385
+
386
+ exit 1 if diff.count > 0
330
387
 
331
388
  else
332
389
  out = retrieve_data_string uri1, options
@@ -399,6 +456,17 @@ Kronk runs diffs against data from live and cached http responses.
399
456
  end
400
457
 
401
458
 
459
+ opt.on('--config STR', String,
460
+ 'Load the given Kronk config file') do |value|
461
+ load_config value
462
+ end
463
+
464
+
465
+ opt.on('-q', '--brief', 'Output only whether URI responses differ') do
466
+ config[:brief] = true
467
+ end
468
+
469
+
402
470
  opt.on('--format STR', String,
403
471
  'Use a custom diff formatter') do |value|
404
472
  config[:diff_format] = value
@@ -5,6 +5,13 @@ class Kronk
5
5
 
6
6
  class DataSet
7
7
 
8
+ # Deep merge proc for recursive Hash merging.
9
+ DEEP_MERGE =
10
+ proc do |key,v1,v2|
11
+ Hash === v1 && Hash === v2 ? v1.merge(v2,&DEEP_MERGE) : v2
12
+ end
13
+
14
+
8
15
  attr_accessor :data
9
16
 
10
17
  def initialize data
@@ -31,9 +38,9 @@ class Kronk
31
38
  ##
32
39
  # Modify the data object by passing inclusive or exclusive data paths.
33
40
  # Supports the following options:
34
- # :only_data:: String/Array - keep data with that matches the paths
41
+ # :only_data:: String/Array - keep data that matches the paths
35
42
  # :only_data_with:: String/Array - keep data with a matched child
36
- # :ignore_data:: String/Array - remove data with that matches the paths
43
+ # :ignore_data:: String/Array - remove data that matches the paths
37
44
  # :ignore_data_with:: String/Array - remove data with a matched child
38
45
  #
39
46
  # Note: the data is processed in the following order:
data/lib/kronk/diff.rb CHANGED
@@ -235,7 +235,10 @@ class Kronk
235
235
 
236
236
  ##
237
237
  # Returns a formatted output as a string.
238
- # Custom formats may be achieved by passing a block.
238
+ # Supported options are:
239
+ # :join_char:: String - The string used to join lines; default "\n"
240
+ # :show_lines:: Boolean - Insert line numbers or not; default @show_lines
241
+ # :formatter:: Object - The formatter to use; default @formatter
239
242
 
240
243
  def formatted options={}
241
244
  options = {
@@ -47,9 +47,9 @@ class TestDataSet < Test::Unit::TestCase
47
47
  end
48
48
 
49
49
 
50
- def test_modify_only_data
51
- data = @dataset_mock.modify :only_data => "subs/1"
52
- assert_equal({"subs" => [nil, "b"]}, data)
50
+ def test_modify_only_data_with
51
+ data = @dataset_mock.modify :only_data_with => "subs/1"
52
+ assert_equal({"subs" => ["a", "b"]}, data)
53
53
  end
54
54
 
55
55
 
data/test/test_kronk.rb CHANGED
@@ -119,17 +119,180 @@ class TestKronk < Test::Unit::TestCase
119
119
  end
120
120
 
121
121
 
122
- def test_options_for_uri
123
- old_uri_opts = Kronk.config[:uri_options].dup
124
- Kronk.config[:uri_options] = {
125
- 'example' => 'options1',
126
- 'example.com' => 'options2'
122
+ def test_merge_options_for_uri
123
+ with_uri_options do
124
+ assert_equal mock_uri_options['example'],
125
+ Kronk.merge_options_for_uri("http://example.com/path")
126
+
127
+ assert_equal Hash.new,
128
+ Kronk.merge_options_for_uri("http://thing.com/path")
129
+ end
130
+ end
131
+
132
+
133
+ def test_merge_options_for_uri_query
134
+ data = {
135
+ "add" => "this",
136
+ "foo" => {
137
+ "bar1" => "one",
138
+ "bar2" => 2,
139
+ "bar3" => "three"},
140
+ "key"=>"otherval"}
141
+
142
+ expected = {:query => data, :data => data}
143
+
144
+ with_uri_options do
145
+ new_data = {
146
+ "add" => "this",
147
+ "foo" => {'bar2' => 2, 'bar3' => "three"},
148
+ "key" => "otherval"
149
+ }
150
+
151
+ %w{uri_query hash_query}.each do |qtype|
152
+ opts = Kronk.merge_options_for_uri("http://#{qtype}.com",
153
+ :query => data, :data => data)
154
+
155
+ assert_equal expected, opts
156
+ end
157
+
158
+ opts = Kronk.merge_options_for_uri("http://uri_query.com")
159
+ assert_equal mock_uri_options['uri_query'], opts
160
+ end
161
+ end
162
+
163
+
164
+ def test_merge_options_for_uri_headers
165
+ with_uri_options do
166
+ opts = Kronk.merge_options_for_uri("http://headers.example.com",
167
+ :headers => {'hdr2' => 2, 'hdr3' => 3})
168
+
169
+ expected = {
170
+ :headers => {
171
+ 'hdr1' => 'one',
172
+ 'hdr2' => 2,
173
+ 'hdr3' => 3
174
+ },
175
+ :parser => "XMLParser"
176
+ }
177
+
178
+ assert_equal expected, opts
179
+ end
180
+ end
181
+
182
+
183
+ def test_merge_options_for_uri_auth
184
+ with_uri_options do
185
+ opts = Kronk.merge_options_for_uri("http://auth.example.com",
186
+ :auth => {:username => "bob"})
187
+
188
+ expected = {
189
+ :auth => {
190
+ :username => "bob",
191
+ :password => "pass"
192
+ },
193
+ :parser => "XMLParser"
194
+ }
195
+
196
+ assert_equal expected, opts
197
+ end
198
+ end
199
+
200
+
201
+ def test_merge_options_for_uri_proxy
202
+ with_uri_options do
203
+ expected = {
204
+ :proxy => {
205
+ :address => "proxy.com",
206
+ :port => 1234,
207
+ :username => "user",
208
+ :password => "pass"
209
+ }
210
+ }
211
+
212
+ opts = Kronk.merge_options_for_uri("http://proxy.com",
213
+ :proxy => "proxy.com")
214
+
215
+ assert_equal expected, opts
216
+
217
+ opts = Kronk.merge_options_for_uri("http://proxy.com",
218
+ :proxy => {:address => "proxy.com"})
219
+
220
+ assert_equal expected, opts
221
+ end
222
+ end
223
+
224
+
225
+ def test_merge_options_for_uri_str_proxy
226
+ with_uri_options do
227
+ expected = {
228
+ :proxy => {
229
+ :address => "someproxy.com",
230
+ :username => "user",
231
+ :password => "pass"
232
+ }
233
+ }
234
+
235
+ opts = Kronk.merge_options_for_uri("http://strprox.com",
236
+ :proxy => {:username => "user", :password => "pass"})
237
+
238
+ assert_equal expected, opts
239
+
240
+ opts = Kronk.merge_options_for_uri("http://strprox.com",
241
+ :proxy => "proxy.com")
242
+
243
+ assert_equal "proxy.com", opts[:proxy]
244
+ end
245
+ end
246
+
247
+
248
+ def test_merge_options_for_uri_with_headers
249
+ with_uri_options do
250
+ %w{withhdrs withstrhdrs withtruehdrs}.each do |type|
251
+ opts = Kronk.merge_options_for_uri "http://#{type}.com",
252
+ :with_headers => true
253
+
254
+ assert_equal true, opts[:with_headers]
255
+ end
256
+ end
257
+ end
258
+
259
+
260
+ def test_merge_options_for_uri_with_headers_arr
261
+ with_uri_options do
262
+ %w{withhdrs withstrhdrs}.each do |type|
263
+ opts = Kronk.merge_options_for_uri "http://#{type}.com",
264
+ :with_headers => %w{hdr2 hdr3}
265
+
266
+ assert_equal %w{hdr1 hdr2 hdr3}.sort, opts[:with_headers].sort
267
+ end
268
+
269
+ opts = Kronk.merge_options_for_uri "http://withtruehdrs.com",
270
+ :with_headers => %w{hdr2 hdr3}
271
+
272
+ assert_equal %w{hdr2 hdr3}, opts[:with_headers]
273
+ end
274
+ end
275
+
276
+
277
+ def test_merge_options_for_uri_data_paths
278
+ expected = {
279
+ :only_data => %w{path1 path2 path3},
280
+ :only_data_with => %w{only1 only2},
281
+ :ignore_data => "ign1",
282
+ :ignore_data_with => %w{ign2 ign3}
127
283
  }
128
284
 
129
- assert_equal 'options1', Kronk.options_for_uri("http://example.com/path")
130
- assert_equal Hash.new, Kronk.options_for_uri("http://thing.com/path")
285
+ with_uri_options do
286
+ opts = Kronk.merge_options_for_uri "http://focus_data.com",
287
+ :only_data => %w{path2 path3},
288
+ :only_data_with => "only2",
289
+ :ignore_data_with => %w{ign2 ign3}
131
290
 
132
- Kronk.config[:uri_options] = old_uri_opts
291
+ opts[:only_data].sort!
292
+ opts[:only_data_with].sort!
293
+
294
+ assert_equal expected, opts
295
+ end
133
296
  end
134
297
 
135
298
 
@@ -326,4 +489,80 @@ STR
326
489
 
327
490
  assert_equal %w{this is --argv}, argv
328
491
  end
492
+
493
+
494
+ private
495
+
496
+ def mock_uri_options
497
+ {
498
+ 'example' => {
499
+ :parser => "XMLParser"
500
+ },
501
+ 'example.com' => {
502
+ :parser => "PlistParser"
503
+ },
504
+ 'uri_query' => {
505
+ :query => "foo[bar1]=one&foo[bar2]=two&key=val",
506
+ :data => "foo[bar1]=one&foo[bar2]=two&key=val"
507
+ },
508
+ 'hash_query' => {
509
+ :query => {
510
+ 'foo' => {'bar1'=>'one', 'bar2'=>'two'},
511
+ 'key' => "val"
512
+ },
513
+ :data => {
514
+ 'foo' => {'bar1'=>'one', 'bar2'=>'two'},
515
+ 'key' => "val"
516
+ }
517
+ },
518
+ 'headers' => {
519
+ :headers => {
520
+ 'hdr1' => 'one',
521
+ 'hdr2' => 'two'
522
+ }
523
+ },
524
+ 'auth' => {
525
+ :auth => {
526
+ :username => "user",
527
+ :password => "pass"
528
+ }
529
+ },
530
+ 'proxy' => {
531
+ :proxy => {
532
+ :username => "user",
533
+ :password => "pass",
534
+ :address => "someproxy.com",
535
+ :port => 1234
536
+ }
537
+ },
538
+ 'strprox' => {
539
+ :proxy => "someproxy.com"
540
+ },
541
+ 'withhdrs' => {
542
+ :with_headers => %w{hdr1 hdr2 hdr3}
543
+ },
544
+ 'withstrhdrs' => {
545
+ :with_headers => "hdr1"
546
+ },
547
+ 'withtruehdrs' => {
548
+ :with_headers => true
549
+ },
550
+ 'focus_data' => {
551
+ :only_data => %w{path1 path2},
552
+ :only_data_with => "only1",
553
+ :ignore_data => "ign1"
554
+ }
555
+ }
556
+ end
557
+
558
+
559
+ def with_uri_options
560
+ old_uri_opts = Kronk.config[:uri_options].dup
561
+ Kronk.config[:uri_options] = mock_uri_options
562
+
563
+ yield if block_given?
564
+
565
+ ensure
566
+ Kronk.config[:uri_options] = old_uri_opts
567
+ end
329
568
  end
@@ -258,7 +258,7 @@ class TestResponse < Test::Unit::TestCase
258
258
  end
259
259
 
260
260
 
261
- def test_selective_data_multiple_only_data
261
+ def test_selective_data_multiple_ignore_data
262
262
  expected = JSON.parse @json_resp.body
263
263
  expected['business'].delete 'id'
264
264
  expected.delete 'request_id'
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kronk
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
5
4
  prerelease: false
6
5
  segments:
7
6
  - 1
8
7
  - 1
9
- - 0
10
- version: 1.1.0
8
+ - 1
9
+ version: 1.1.1
11
10
  platform: ruby
12
11
  authors:
13
12
  - Jeremie Castagna
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-12-12 00:00:00 -08:00
17
+ date: 2010-12-22 00:00:00 -08:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ~>
28
27
  - !ruby/object:Gem::Version
29
- hash: 3
30
28
  segments:
31
29
  - 3
32
30
  - 1
@@ -42,7 +40,6 @@ dependencies:
42
40
  requirements:
43
41
  - - ~>
44
42
  - !ruby/object:Gem::Version
45
- hash: 11
46
43
  segments:
47
44
  - 1
48
45
  - 2
@@ -57,7 +54,6 @@ dependencies:
57
54
  requirements:
58
55
  - - ~>
59
56
  - !ruby/object:Gem::Version
60
- hash: 9
61
57
  segments:
62
58
  - 1
63
59
  - 3
@@ -72,7 +68,6 @@ dependencies:
72
68
  requirements:
73
69
  - - ~>
74
70
  - !ruby/object:Gem::Version
75
- hash: 1
76
71
  segments:
77
72
  - 0
78
73
  - 5
@@ -87,7 +82,6 @@ dependencies:
87
82
  requirements:
88
83
  - - ">="
89
84
  - !ruby/object:Gem::Version
90
- hash: 15
91
85
  segments:
92
86
  - 2
93
87
  - 0
@@ -103,7 +97,6 @@ dependencies:
103
97
  requirements:
104
98
  - - ~>
105
99
  - !ruby/object:Gem::Version
106
- hash: 19
107
100
  segments:
108
101
  - 0
109
102
  - 3
@@ -112,37 +105,64 @@ dependencies:
112
105
  type: :runtime
113
106
  version_requirements: *id006
114
107
  - !ruby/object:Gem::Dependency
115
- name: mocha
108
+ name: rack
116
109
  prerelease: false
117
110
  requirement: &id007 !ruby/object:Gem::Requirement
118
111
  none: false
119
112
  requirements:
120
113
  - - ~>
121
114
  - !ruby/object:Gem::Version
122
- hash: 47
115
+ segments:
116
+ - 1
117
+ - 0
118
+ version: "1.0"
119
+ type: :runtime
120
+ version_requirements: *id007
121
+ - !ruby/object:Gem::Dependency
122
+ name: rubyforge
123
+ prerelease: false
124
+ requirement: &id008 !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ segments:
130
+ - 2
131
+ - 0
132
+ - 4
133
+ version: 2.0.4
134
+ type: :development
135
+ version_requirements: *id008
136
+ - !ruby/object:Gem::Dependency
137
+ name: mocha
138
+ prerelease: false
139
+ requirement: &id009 !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ~>
143
+ - !ruby/object:Gem::Version
123
144
  segments:
124
145
  - 0
125
146
  - 9
126
147
  - 10
127
148
  version: 0.9.10
128
149
  type: :development
129
- version_requirements: *id007
150
+ version_requirements: *id009
130
151
  - !ruby/object:Gem::Dependency
131
152
  name: hoe
132
153
  prerelease: false
133
- requirement: &id008 !ruby/object:Gem::Requirement
154
+ requirement: &id010 !ruby/object:Gem::Requirement
134
155
  none: false
135
156
  requirements:
136
157
  - - ">="
137
158
  - !ruby/object:Gem::Version
138
- hash: 47
139
159
  segments:
140
160
  - 2
141
- - 8
142
- - 0
143
- version: 2.8.0
161
+ - 6
162
+ - 2
163
+ version: 2.6.2
144
164
  type: :development
145
- version_requirements: *id008
165
+ version_requirements: *id010
146
166
  description: |-
147
167
  Kronk runs diffs against data from live and cached http responses.
148
168
  Kronk was made possible by the sponsoring of AT&T Interactive.
@@ -198,7 +218,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
198
218
  requirements:
199
219
  - - ">="
200
220
  - !ruby/object:Gem::Version
201
- hash: 3
221
+ hash: -1939027826967097857
202
222
  segments:
203
223
  - 0
204
224
  version: "0"
@@ -207,7 +227,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
227
  requirements:
208
228
  - - ">="
209
229
  - !ruby/object:Gem::Version
210
- hash: 3
211
230
  segments:
212
231
  - 0
213
232
  version: "0"