kronk 1.6.2 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/History.rdoc +29 -1
  2. data/Manifest.txt +6 -1
  3. data/README.rdoc +74 -28
  4. data/Rakefile +4 -3
  5. data/TODO.rdoc +7 -5
  6. data/bin/kronk +2 -11
  7. data/lib/kronk/async/em_ext.rb +34 -0
  8. data/lib/kronk/async/request.rb +73 -0
  9. data/lib/kronk/async/response.rb +70 -0
  10. data/lib/kronk/async.rb +118 -0
  11. data/lib/kronk/cmd.rb +111 -43
  12. data/lib/kronk/constants.rb +1 -0
  13. data/lib/kronk/core_ext.rb +1 -1
  14. data/lib/kronk/data_string.rb +251 -0
  15. data/lib/kronk/diff/output.rb +132 -100
  16. data/lib/kronk/diff.rb +20 -24
  17. data/lib/kronk/path/matcher.rb +8 -4
  18. data/lib/kronk/path/path_match.rb +48 -4
  19. data/lib/kronk/path/transaction.rb +74 -53
  20. data/lib/kronk/path.rb +11 -6
  21. data/lib/kronk/player/benchmark.rb +11 -12
  22. data/lib/kronk/player/input_reader.rb +40 -3
  23. data/lib/kronk/player/request_parser.rb +4 -1
  24. data/lib/kronk/player/stream.rb +2 -2
  25. data/lib/kronk/player/suite.rb +16 -9
  26. data/lib/kronk/player.rb +93 -143
  27. data/lib/kronk/queue_runner.rb +238 -0
  28. data/lib/kronk/request.rb +25 -20
  29. data/lib/kronk/response.rb +39 -10
  30. data/lib/kronk/test/assertions.rb +2 -2
  31. data/lib/kronk/test/helper_methods.rb +1 -1
  32. data/lib/kronk.rb +56 -24
  33. data/test/test_assertions.rb +4 -4
  34. data/test/test_cmd.rb +38 -10
  35. data/test/test_data_string.rb +242 -1
  36. data/test/test_diff.rb +8 -303
  37. data/test/test_helper.rb +1 -1
  38. data/test/test_kronk.rb +21 -28
  39. data/test/test_path.rb +29 -0
  40. data/test/test_path_match.rb +47 -2
  41. data/test/test_path_matcher.rb +42 -1
  42. data/test/test_player.rb +71 -72
  43. data/test/test_request.rb +31 -6
  44. data/test/test_request_parser.rb +7 -1
  45. data/test/test_response.rb +1 -1
  46. data/test/test_transaction.rb +78 -30
  47. metadata +64 -8
  48. data/lib/kronk/data_renderer.rb +0 -219
data/History.rdoc CHANGED
@@ -1,6 +1,34 @@
1
+ === 1.7.0 / 2011-10-09
2
+
3
+ * Major Enhancements:
4
+
5
+ * EventMachine backend support for Player.
6
+
7
+ * Enhancements:
8
+
9
+ * Full URL support for player input.
10
+
11
+ * Move and map path transaction support with matches and splats.
12
+
13
+ * Block support for player input and result.
14
+
15
+ * Request proxy :address key changed to :host.
16
+
17
+ * Many Player performance improvements, especially on Ubuntu.
18
+
19
+ * Bugfixes:
20
+
21
+ * Support relative URLs in redirects.
22
+
23
+ * More accurate data diff metadata output.
24
+
25
+ * Lots of Diff::Output and DataString cleanup.
26
+
27
+ * Assign URI options to redirects.
28
+
1
29
  === 1.6.2 / 2011-09-12
2
30
 
3
- * Bugfixes
31
+ * Bugfixes:
4
32
 
5
33
  * Handling of stream output with Diff#formatted is nil.
6
34
 
data/Manifest.txt CHANGED
@@ -5,10 +5,14 @@ README.rdoc
5
5
  Rakefile
6
6
  bin/kronk
7
7
  lib/kronk.rb
8
+ lib/kronk/async.rb
9
+ lib/kronk/async/em_ext.rb
10
+ lib/kronk/async/request.rb
11
+ lib/kronk/async/response.rb
8
12
  lib/kronk/cmd.rb
9
13
  lib/kronk/constants.rb
10
14
  lib/kronk/core_ext.rb
11
- lib/kronk/data_renderer.rb
15
+ lib/kronk/data_string.rb
12
16
  lib/kronk/diff.rb
13
17
  lib/kronk/diff/ascii_format.rb
14
18
  lib/kronk/diff/color_format.rb
@@ -25,6 +29,7 @@ lib/kronk/player/request_parser.rb
25
29
  lib/kronk/player/suite.rb
26
30
  lib/kronk/player/stream.rb
27
31
  lib/kronk/plist_parser.rb
32
+ lib/kronk/queue_runner.rb
28
33
  lib/kronk/request.rb
29
34
  lib/kronk/response.rb
30
35
  lib/kronk/test.rb
data/README.rdoc CHANGED
@@ -11,10 +11,14 @@ Kronk was made possible by the sponsoring of AT&T Interactive.
11
11
 
12
12
  * Parse and display or diff data from http response body and/or headers.
13
13
 
14
- * Include or exclude specific data points with file-glob-like paths.
14
+ * Include, exclude or map specific data points with file-glob-like paths.
15
+
16
+ * Query and logfile playback with custom output.
15
17
 
16
18
  * Supports json, rails-ish xml, and plist.
17
19
 
20
+ * URI-specific configuration.
21
+
18
22
  * Support for custom data parsers and diff formatters.
19
23
 
20
24
  * Launch IRB console with the retrieved response data.
@@ -25,12 +29,8 @@ Kronk was made possible by the sponsoring of AT&T Interactive.
25
29
 
26
30
  * Easy-to-read output (color or ascii, line numbers, etc).
27
31
 
28
- * URI-specific configuration.
29
-
30
32
  * Helper methods for test suites.
31
33
 
32
- * Query and logfile playback with custom output.
33
-
34
34
  * Yes, it works on Windows.
35
35
 
36
36
  == SYNOPSIS:
@@ -79,12 +79,12 @@ Kronk pulls it's config from $HOME/.kronk/rc and supports the following:
79
79
 
80
80
  Set the file to save the last http response retrieved in:
81
81
 
82
- :cache_file: ~/.kronk_cache
82
+ cache_file: ~/.kronk_cache
83
83
 
84
84
  Content types to match to a parser. Keys are used as a part of
85
85
  a regexp and values are evaluated by const_get.
86
86
 
87
- :content_types:
87
+ content_types:
88
88
  xml: XMLParser
89
89
  plist: PlistParser
90
90
  json: JSON
@@ -92,23 +92,23 @@ a regexp and values are evaluated by const_get.
92
92
  Number of lines of context to use for diff. Full diff is returned when
93
93
  set to false:
94
94
 
95
- :context: 5 # show 5 lines before and after diff
96
- :context: false # show the full file
95
+ context: 5 # show 5 lines before and after diff
96
+ context: false # show the full file
97
97
 
98
- How to format the diff output. Supports the "special" values
99
- :ascii_diff and :color_diff or any string that will correctly
98
+ How to format the diff output. Supports the special values
99
+ 'ascii' and 'color' or any string that will correctly
100
100
  resolve to a constant:
101
101
 
102
- :diff_format: :ascii_diff
102
+ diff_format: ascii
103
103
 
104
104
  Adding User-Agent aliases is useful and simple!
105
105
 
106
- :user_agents:
106
+ user_agents:
107
107
  win_ie9: Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.0)
108
108
 
109
109
  Disabling cookies altogether may be done as so:
110
110
 
111
- :use_cookies: false
111
+ use_cookies: false
112
112
 
113
113
  Setting URI-specific config options - useful if you always need to send
114
114
  headers or use one-off configs. Each URI option key is dropped in a regexp
@@ -116,40 +116,40 @@ to match against the given URIs so this also works for local files.
116
116
  See Kronk::Request.retrieve and Kronk::Response#selective_data
117
117
  for all options supported:
118
118
 
119
- :uri_options:
119
+ uri_options:
120
120
  example.com:
121
- :parser: XMLParser
122
- :http_method: POST
121
+ parser: XMLParser
122
+ http_method: POST
123
123
 
124
- :follow_redirects: true
124
+ follow_redirects: true
125
125
 
126
- :ignore_data:
126
+ ignore_data:
127
127
  - path/to/data/point
128
128
 
129
- :headers:
129
+ headers:
130
130
  X-Something: custom header value
131
131
 
132
- :query:
132
+ query:
133
133
  offset: 1
134
134
  limit: 10
135
135
 
136
136
  Require specific files or gems in the ruby runtime:
137
137
 
138
- :requires:
138
+ requires:
139
139
  - kronk-my_custom_parser
140
140
  - kronk-my_custom_diff_formatter
141
141
 
142
142
  Show line numbers in the output:
143
143
 
144
- :show_lines: true
144
+ show_lines: true
145
145
 
146
146
  Assign a default host to use when none is specified:
147
147
 
148
- :default_host: http://localhost:3000
148
+ default_host: http://localhost:3000
149
149
 
150
150
  Set the number of spaces for indentation:
151
151
 
152
- :indentation: 2
152
+ indentation: 2
153
153
 
154
154
  === Bash Completion:
155
155
 
@@ -158,10 +158,15 @@ Bash completion is available by sourcing the file returned by:
158
158
  $ kronk --completion
159
159
  [gem path]/script/kronk_completion
160
160
 
161
- == DATA TRAVERSING:
161
+ == DATA MANIPULATION:
162
162
 
163
163
  One of Kronk's most powerful features is its ability to segregate data.
164
- From the command line, this is done by passing data paths after '--':
164
+ From the command line, this is done by passing data paths after '--'
165
+
166
+ === Selecting and Deleting:
167
+
168
+ The first kind of data transformation is the ability to select and delete
169
+ data by path:
165
170
 
166
171
  $ kronk http://host.com -- data/path1 data/path2/1/child
167
172
 
@@ -240,6 +245,37 @@ end of the path in the following fashion:
240
245
  }
241
246
  }
242
247
 
248
+ === Mapping and Moving:
249
+
250
+ In Kronk, mapping equates to selecting and renaming a given path in an empty
251
+ data structure. Moving refers to deleting the path value and placing it
252
+ back in the original data structure at the new path.
253
+
254
+ # Mapping is done with the > operator:
255
+ $ kronk host.com -- data/path3 "data/path(1|2)>moved%1"
256
+
257
+ {
258
+ "data": {
259
+ "path3": "value3"
260
+ }
261
+ "moved1": "value1",
262
+ "moved2": "value2"
263
+ }
264
+
265
+
266
+ # Moving is done with the >> operator:
267
+ $ kronk host.com -- "data/path(1|2)>moved%1"
268
+
269
+ {
270
+ "data": {
271
+ "path3": "value3",
272
+ "path4": "value4"
273
+ }
274
+ "moved1": "value1",
275
+ "moved2": "value2"
276
+ }
277
+
278
+ === Special Characters:
243
279
 
244
280
  There are additionally a variety of wildcard and special characters that
245
281
  are supported:
@@ -263,7 +299,17 @@ are supported:
263
299
 
264
300
  * \\ escapes any special path character
265
301
 
266
- Check out Kronk::Path and Kronk::DataSet for more details on data traversing.
302
+ * > is used to exclusively map one path to another
303
+
304
+ * >> moves a path to another within the original data structure
305
+
306
+ * %NUM is used in the target path to reference a given match
307
+
308
+ * %% is used to reference splats from the original path and assign it in the
309
+ target path
310
+
311
+ Check out Kronk::Path and Kronk::Path::Transaction for more details
312
+ on data manipulation.
267
313
 
268
314
  Note: Bash may try to parse your data paths, especially if they start with
269
315
  wildcards or if they contain the pipe "|" character, so you may need to put
data/Rakefile CHANGED
@@ -25,9 +25,10 @@ Hoe.spec 'kronk' do
25
25
  self.extra_deps << ['json', '~>1.5']
26
26
  self.extra_deps << ['cookiejar', '~>0.3.0']
27
27
 
28
- self.extra_dev_deps << ['plist', '~>3.1.0']
29
- self.extra_dev_deps << ['nokogiri', '~>1.4']
30
- self.extra_dev_deps << ['mocha', '~>0.9.12']
28
+ self.extra_dev_deps << ['plist', '~>3.1.0']
29
+ self.extra_dev_deps << ['nokogiri', '~>1.4']
30
+ self.extra_dev_deps << ['mocha', '~>0.9.12']
31
+ self.extra_dev_deps << ['em-http-request', '~>1.0.0']
31
32
  end
32
33
 
33
34
 
data/TODO.rdoc CHANGED
@@ -1,18 +1,20 @@
1
1
  = TODO
2
2
 
3
- == Path
3
+ * Investigate Kronk console.
4
4
 
5
- * Support move and map for path transactions.
5
+ == Done
6
6
 
7
- == Player
7
+ * Investigate the use of EM for QueueRunner.
8
+
9
+ * Refactor Kronk::Player into a Player and a QueueRunner.
10
+
11
+ * Support move and map for path transactions.
8
12
 
9
13
  * Read full URIs for player input but strip off the protocol and host.
10
14
 
11
15
  * When using player and no host is given, support reading a full URI for
12
16
  player input.
13
17
 
14
- == Done
15
-
16
18
  * Support data diffing for arrays (including data struct).
17
19
 
18
20
  * Allow for showing diffed sections of a diff only.
data/bin/kronk CHANGED
@@ -1,15 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $: << File.join(File.dirname(__FILE__), "../lib") if $0 =~ %r{bin/kronk$}
4
-
5
- begin
6
- require 'kronk'
7
-
8
- rescue LoadError => e
9
- raise unless e.message =~ %r{no such file to load -- kronk}
10
-
11
- $: << File.join(File.dirname(__FILE__), "../lib")
12
- require 'kronk'
13
- end
3
+ $: << File.join(File.dirname(__FILE__), "../lib") if $0 == "bin/kronk"
14
4
 
5
+ require 'kronk'
15
6
  Kronk::Cmd.run
@@ -0,0 +1,34 @@
1
+ require 'em-http-request'
2
+
3
+ module EventMachine
4
+ class HttpClient
5
+ attr_accessor :raw_response
6
+
7
+ alias em_parse_response_header parse_response_header
8
+
9
+ def parse_response_header header, version, status
10
+ out = em_parse_response_header header, version, status
11
+
12
+ rheader = @response_header
13
+
14
+ @raw_response = "HTTP/#{rheader.http_version} "
15
+ @raw_response << "#{rheader.status} #{rheader.http_reason}\r\n"
16
+
17
+ header.each do |key, val|
18
+ @raw_response << "#{key}: #{val}\r\n"
19
+ end
20
+
21
+ @raw_response << "\r\n"
22
+
23
+ out
24
+ end
25
+
26
+
27
+ alias em_on_decoded_body_data on_decoded_body_data
28
+
29
+ def on_decoded_body_data data
30
+ @raw_response << data
31
+ em_on_decoded_body_data data
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,73 @@
1
+ class Kronk
2
+ class Request
3
+
4
+ class EMError < Kronk::Exception; end
5
+
6
+ ##
7
+ # Retrieve this requests' response asynchronously with em-http-request.
8
+ # Returns a EM::HttpConnection instance.
9
+ #
10
+ # Passing a block will yield a Kronk::Response instance and/or
11
+ # an Exception instance if an error was caught.
12
+ #
13
+ # req = Request.new "example.com"
14
+ # em_req = req.retrieve_async do |kronk_response, err|
15
+ # # do something with Kronk::Response instance here
16
+ # end
17
+ #
18
+ # em_req.callback { ... }
19
+ # em_req.error { ... }
20
+
21
+ def retrieve_async &block
22
+ header_opts = @headers.dup
23
+
24
+ if @auth && !@auth.empty?
25
+ header_opts['Authorization'] ||= []
26
+ header_opts['Authorization'][0] = @auth[:username] if @auth[:username]
27
+ header_opts['Authorization'][1] = @auth[:password] if @auth[:password]
28
+ end
29
+
30
+ conn = async_http
31
+
32
+ start_time = Time.now
33
+ req = conn.setup_request @http_method,
34
+ :head => header_opts, :body => @body, &block
35
+
36
+ req.callback do |resp|
37
+ elapsed_time = Time.now - start_time
38
+ @response = Response.new resp.raw_response, nil, self
39
+ @response.time = elapsed_time
40
+ yield @response, nil
41
+ end if block_given?
42
+
43
+ req.errback do |c|
44
+ err = c.error ?
45
+ EMError.new(c.error) :
46
+ Kronk::NotFoundError.new("#{@uri} could not be found")
47
+
48
+ yield nil, err
49
+ end
50
+
51
+ req
52
+ end
53
+
54
+
55
+ ##
56
+ # Return an EM::HttpRequest instance.
57
+
58
+ def async_http
59
+ unless @proxy.empty?
60
+ proxy_opts = @proxy.dup
61
+ proxy_opts[:authorization] = [
62
+ proxy_opts.delete(:username),
63
+ proxy_opts.delete(:password)
64
+ ] if proxy_opts[:username] || proxy_opts[:password]
65
+ end
66
+
67
+ EventMachine::HttpRequest.new @uri,
68
+ :connect_timeout => @timeout,
69
+ :inactivity_timeout => @timeout,
70
+ :proxy => proxy_opts
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,70 @@
1
+ class Kronk
2
+ class Response
3
+
4
+ class AsyncHandler < EM::Connection
5
+ attr_accessor :buffer
6
+
7
+ def initialize req
8
+ @buffer = ""
9
+ @callback = nil
10
+ @_req = req
11
+ @_res = nil
12
+ end
13
+
14
+
15
+ def callback &block
16
+ @callback = block
17
+ end
18
+
19
+
20
+ def receive_data str
21
+ @buffer << str
22
+ end
23
+
24
+
25
+ def unbind
26
+ return unless @callback
27
+
28
+ @_res = Kronk::Response.new @buffer, nil, @_req
29
+ err = Kronk::Request::EMError.new "IO read error" if error?
30
+
31
+ @callback.call @_res, err
32
+
33
+ rescue => e
34
+ @callback.call @_res, e
35
+ end
36
+ end
37
+
38
+
39
+ ##
40
+ # Response.new with asynchronous IO input.
41
+ # Returns an EM::Connection subclass (AsyncHandler) instance.
42
+ #
43
+ # Passing a block will yield a Kronk::Response instance and/or
44
+ # an Exception instance if an error was caught.
45
+ #
46
+ # conn = Response.from_async_io do |resp, err|
47
+ # # do something with Kronk::Response instance here
48
+ # end
49
+
50
+ def self.from_async_io io, req=nil, &block
51
+ conn = EM.attach io, AsyncHandler, req
52
+ conn.comm_inactivity_timeout = 2
53
+ conn.callback(&block)
54
+ conn
55
+ end
56
+
57
+
58
+ ##
59
+ # Follow the redirect and return a new Response instance.
60
+ # Returns nil if not redirect-able.
61
+ #
62
+ # Passing a block will yield a Kronk::Response instance and/or
63
+ # an Exception instance if an error was caught.
64
+
65
+ def follow_redirect_async opts={}, &block
66
+ return if !redirect?
67
+ Request.new(self.location, opts).retrieve_async(&block)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,118 @@
1
+ require 'kronk'
2
+ require 'kronk/async/em_ext'
3
+ require 'kronk/async/request'
4
+ require 'kronk/async/response'
5
+
6
+ class Kronk
7
+
8
+ ##
9
+ # Returns an EM::MultiRequest instance from a url, file, or IO pair.
10
+ # Calls the given block with a Kronk::Diff object on completion or error.
11
+ # Assigns @response, @responses, @diff. Must be called from an EM loop.
12
+ #
13
+ # kronk.compare_async uri1, uri2 do |diff, err|
14
+ # # handle diff, responses, or error
15
+ # end
16
+
17
+ def compare_async uri1, uri2
18
+ multi = EM::MultiRequest.new
19
+
20
+ str1 = str2 = ""
21
+ res1 = res2 = nil
22
+ err1 = err2 = nil
23
+
24
+ conn1 = request_async uri1 do |res, err|
25
+ err1 = err and next if err
26
+ res1 = res
27
+ str1 = res.stringify
28
+ end
29
+
30
+ conn2 = request_async uri2 do |res, err|
31
+ err2 = err and next if err
32
+ res2 = res
33
+ str2 = res.stringify
34
+ end
35
+
36
+ multi.add :left, conn1
37
+ multi.add :right, conn2
38
+
39
+ multi.callback do
40
+ next yield(nil, (err1 || err2)) if err1 || err2
41
+
42
+ @responses = [res1, res2]
43
+ @response = res2
44
+
45
+ opts = {:labels => [res1.uri, res2.uri]}.merge @options
46
+ @diff = Diff.new str1, str2, opts
47
+
48
+ yield @diff
49
+ end
50
+
51
+ multi
52
+
53
+ rescue => e
54
+ yield nil, e
55
+ end
56
+
57
+
58
+ ##
59
+ # Returns an EventMachine Connection instance from a url, file, or IO.
60
+ # Calls the given block with a Kronk::Response object on completion or error.
61
+ # Assigns @response, @responses, @diff. Must be called from an EM loop.
62
+ #
63
+ # kronk.request_async uri do |resp, err|
64
+ # # handle response or error
65
+ # end
66
+
67
+ def request_async uri
68
+ options = Kronk.config[:no_uri_options] ? @options : options_for_uri(uri)
69
+
70
+ rdir = options[:follow_redirects]
71
+
72
+ handler = Proc.new do |resp, err|
73
+ next yield(resp, err) if err
74
+
75
+ resp.parser = options[:parser] if options[:parser]
76
+ resp.stringify_opts = options
77
+
78
+ if resp.redirect? && (rdir == true || Fixnum === rdir && rdir > 0)
79
+ Cmd.verbose "Following redirect to #{resp.location}"
80
+
81
+ rdir = rdir - 1 if Fixnum === rdir
82
+ opts = options_for_uri resp.location
83
+ resp.follow_redirect_async(opts, &handler)
84
+
85
+ else
86
+ @responses = [resp]
87
+ @response = resp
88
+ @diff = nil
89
+
90
+ yield resp if block_given?
91
+
92
+ resp
93
+ end
94
+ end
95
+
96
+ if IO === uri
97
+ Cmd.verbose "Reading IO #{uri}"
98
+ Response.from_async_io(uri, &handler)
99
+
100
+ elsif StringIO === uri
101
+ Cmd.verbose "Reading IO #{uri}"
102
+ handler.call Response.new(uri)
103
+
104
+ elsif File.file? uri.to_s
105
+ Cmd.verbose "Reading file: #{uri}\n"
106
+ handler.call Response.read_file(uri)
107
+
108
+ else
109
+ req = Request.new uri, options
110
+ Cmd.verbose "Retrieving URL: #{req.uri}\n"
111
+ conn = req.retrieve_async(&handler)
112
+ conn.callback{ Kronk.history << uri }
113
+ end
114
+
115
+ rescue => e
116
+ yield nil, e
117
+ end
118
+ end