kronk 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,146 @@
1
+ class Kronk
2
+
3
+ ##
4
+ # Creates ordered data strings for rendering to the output.
5
+
6
+ module DataRenderer
7
+
8
+ ##
9
+ # Returns a ruby data string that is diff-able, meaning sorted by
10
+ # Hash keys when available.
11
+
12
+ def self.ruby data, struct_only=false
13
+ ordered_data_string data, struct_only do |type, obj|
14
+ case type
15
+ when :key_assign then " =>"
16
+ when :key then obj.inspect
17
+ when :value then obj.inspect
18
+ when :struct
19
+ (obj == true || obj == false) ? "Boolean" : obj.class
20
+ end
21
+ end
22
+ end
23
+
24
+ ##
25
+ # Returns a json data string that is diff-able, meaning sorted by
26
+ # Hash keys when available.
27
+
28
+ def self.json data, struct_only=false
29
+ ordered_data_string data, struct_only do |type, obj|
30
+ case type
31
+ when :key_assign then ":"
32
+ when :key
33
+ (Symbol === obj ? obj.inspect : obj.to_s).to_json
34
+ when :value
35
+ (Symbol === obj ? obj.inspect : obj).to_json
36
+ when :struct
37
+ ((obj == true || obj == false) ? "Boolean" : obj.class).to_json
38
+ end
39
+ end
40
+ end
41
+
42
+
43
+ def self.ordered_data_string data, struct_only=false, indent=nil, &block
44
+ i_width = Kronk.config[:indentation] || 1
45
+ indent ||= 0
46
+ indent += i_width
47
+
48
+ case data
49
+
50
+ when Hash
51
+ return "{}" if data.empty?
52
+
53
+ output = "{\n"
54
+
55
+ sorted_keys = sort_any data.keys
56
+
57
+ data_values =
58
+ sorted_keys.map do |key|
59
+ value = data[key]
60
+ pad = " " * indent
61
+ subdata = ordered_data_string value, struct_only, indent, &block
62
+ "#{pad}#{ yield(:key, key) }#{ yield(:key_assign) } #{subdata}"
63
+ end
64
+
65
+ output << data_values.join(",\n") << "\n"
66
+ output << "#{" " * (indent - i_width)}}"
67
+
68
+ when Array
69
+ return "[]" if data.empty?
70
+
71
+ output = "[\n"
72
+
73
+ data_values =
74
+ data.map do |value|
75
+ pad = " " * indent
76
+ "#{pad}#{ordered_data_string value, struct_only, indent, &block}"
77
+ end
78
+
79
+ output << data_values.join(",\n") << "\n"
80
+ output << "#{" " * (indent - i_width)}]"
81
+
82
+ else
83
+ struct_only ? yield(:struct, data) : yield(:value, data)
84
+ end
85
+ end
86
+
87
+
88
+
89
+ ##
90
+ # Sorts an array of any combination of string, integer, or symbols.
91
+
92
+ def self.sort_any arr
93
+ i = 1
94
+ until i >= arr.length
95
+ j = i-1
96
+ val = arr[i]
97
+ prev_val = arr[j]
98
+
99
+ loop do
100
+ if smaller?(val, arr[j])
101
+ arr[j+1] = arr[j]
102
+ j = j - 1
103
+ break if j < 0
104
+
105
+ else
106
+ break
107
+ end
108
+ end
109
+
110
+ arr[j+1] = val
111
+
112
+ i = i.next
113
+ end
114
+
115
+ arr
116
+ end
117
+
118
+
119
+ ##
120
+ # Compares Numerics, Strings, and Symbols and returns true if the left
121
+ # side is 'smaller' than the right side.
122
+
123
+ def self.smaller? left, right
124
+ case left
125
+ when Numeric
126
+ case right
127
+ when Numeric then right > left
128
+ else true
129
+ end
130
+
131
+ when Symbol
132
+ case right
133
+ when Numeric then false
134
+ when Symbol then right.to_s > left.to_s
135
+ else true
136
+ end
137
+
138
+ when String
139
+ case right
140
+ when String then right > left
141
+ else false
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
data/lib/kronk/diff.rb CHANGED
@@ -19,99 +19,11 @@ class Kronk
19
19
  # Returns a data string that is diff-able, meaning sorted by
20
20
  # Hash keys when available.
21
21
 
22
- def self.ordered_data_string data, struct_only=false, indent=0
23
- case data
24
-
25
- when Hash
26
- output = "{\n"
27
-
28
- sorted_keys = sort_any data.keys
29
-
30
- data_values =
31
- sorted_keys.map do |key|
32
- value = data[key]
33
- pad = " " * indent
34
- subdata = ordered_data_string value, struct_only, indent + 1
35
- "#{pad}#{key.inspect} => #{subdata}"
36
- end
37
-
38
- output << data_values.join(",\n") << "\n" unless data_values.empty?
39
- output << "#{" " * indent}}"
40
-
41
- when Array
42
- output = "[\n"
43
-
44
- data_values =
45
- data.map do |value|
46
- pad = " " * indent
47
- "#{pad}#{ordered_data_string value, struct_only, indent + 1}"
48
- end
49
-
50
- output << data_values.join(",\n") << "\n" unless data_values.empty?
51
- output << "#{" " * indent}]"
52
-
22
+ def self.ordered_data_string data, struct_only=false
23
+ case Kronk.config[:render_lang].to_s
24
+ when 'ruby' then DataRenderer.ruby(data, struct_only)
53
25
  else
54
- return data.inspect unless struct_only
55
- return "Boolean" if data == true || data == false
56
- data.class
57
- end
58
- end
59
-
60
-
61
- ##
62
- # Sorts an array of any combination of string, integer, or symbols.
63
-
64
- def self.sort_any arr
65
- i = 1
66
- until i >= arr.length
67
- j = i-1
68
- val = arr[i]
69
- prev_val = arr[j]
70
-
71
- loop do
72
- if smaller?(val, arr[j])
73
- arr[j+1] = arr[j]
74
- j = j - 1
75
- break if j < 0
76
-
77
- else
78
- break
79
- end
80
- end
81
-
82
- arr[j+1] = val
83
-
84
- i = i.next
85
- end
86
-
87
- arr
88
- end
89
-
90
-
91
- ##
92
- # Compares Numerics, Strings, and Symbols and returns true if the left
93
- # side is 'smaller' than the right side.
94
-
95
- def self.smaller? left, right
96
- case left
97
- when Numeric
98
- case right
99
- when Numeric then right > left
100
- else true
101
- end
102
-
103
- when Symbol
104
- case right
105
- when Numeric then false
106
- when Symbol then right.to_s > left.to_s
107
- else true
108
- end
109
-
110
- when String
111
- case right
112
- when String then right > left
113
- else false
114
- end
26
+ DataRenderer.json(data, struct_only)
115
27
  end
116
28
  end
117
29
 
@@ -94,6 +94,7 @@ class Kronk::Path::Transaction
94
94
 
95
95
 
96
96
  def transaction_select data, *data_paths # :nodoc:
97
+ data_paths = data_paths.compact
97
98
  return data if data_paths.empty?
98
99
 
99
100
  new_data = Hash.new
@@ -129,6 +130,7 @@ class Kronk::Path::Transaction
129
130
 
130
131
 
131
132
  def transaction_delete data, *data_paths # :nodoc:
133
+ data_paths = data_paths.compact
132
134
  return data if data_paths.empty?
133
135
 
134
136
  new_data = data.dup
@@ -0,0 +1,233 @@
1
+ class Kronk
2
+
3
+ ##
4
+ # The Player class is used for running multiple requests and comparisons and
5
+ # providing useful output through Player::Output classes.
6
+ # Kronk includes a Suite (test-like) output, a Stream (chunked) output,
7
+ # and a Benchmark output.
8
+
9
+ class Player
10
+
11
+ attr_accessor :number, :concurrency, :queue, :count, :input,
12
+ :output, :mutex, :threads, :reader_thread
13
+
14
+ ##
15
+ # Create a new Player for batch diff or response validation.
16
+ # Supported options are:
17
+ # :concurrency:: Fixnum - The maximum number of concurrent requests to make
18
+ # :number:: Fixnum - The number of requests to make
19
+ # :io:: IO - The IO instance to read from
20
+ # :output:: Class - The output class to use (see Player::Output)
21
+ # :parser:: Class - The IO parser to use.
22
+
23
+ def initialize opts={}
24
+ @number = opts[:number]
25
+ @concurrency = opts[:concurrency]
26
+ @concurrency = 1 if !@concurrency || @concurrency <= 0
27
+ self.output_from opts[:output] || Suite
28
+
29
+ @count = 0
30
+ @queue = []
31
+ @threads = []
32
+ @input = InputReader.new opts[:io], opts[:parser]
33
+ @last_req = nil
34
+
35
+ @mutex = Mutex.new
36
+ end
37
+
38
+
39
+ ##
40
+ # The kind of output to use. Typically Player::Suite or Player::Stream.
41
+ # Takes an output class or a string that represents a class constant.
42
+
43
+ def output_from new_output
44
+ return @output = new_output.new(self) if Class === new_output
45
+
46
+ klass =
47
+ case new_output.to_s
48
+ when /^(Player::)?benchmark$/i then Benchmark
49
+ when /^(Player::)?stream$/i then Stream
50
+ when /^(Player::)?suite$/i then Suite
51
+ else
52
+ Kronk.find_const new_output
53
+ end
54
+
55
+ @output = klass.new self if klass
56
+ end
57
+
58
+
59
+ ##
60
+ # Adds kronk request hash options to queue.
61
+ # See Kronk#compare for supported options.
62
+
63
+ def queue_req kronk_opts
64
+ @queue << kronk_opts
65
+ end
66
+
67
+
68
+ ##
69
+ # Populate the queue by reading from the given IO instance and
70
+ # parsing it into kronk options.
71
+ #
72
+ # Default parser is RequestParser. See InputReader for parser requirements.
73
+
74
+ def from_io io, parser=nil
75
+ @input.io = io
76
+ @input.parser = parser if parser
77
+ @input
78
+ end
79
+
80
+
81
+ ##
82
+ # Process the queue to compare two uris.
83
+ # If options are given, they are merged into every request.
84
+
85
+ def compare uri1, uri2, opts={}
86
+ return Cmd.compare uri1, uri2, @queue.shift.merge(opts) if single_request?
87
+
88
+ process_queue do |kronk_opts|
89
+ process_compare uri1, uri2, kronk_opts.merge(opts)
90
+ end
91
+ end
92
+
93
+
94
+ ##
95
+ # Process the queue to request uris.
96
+ # If options are given, they are merged into every request.
97
+
98
+ def request uri, opts={}
99
+ return Cmd.request(uri, @queue.shift.merge(opts)) if single_request?
100
+
101
+ process_queue do |kronk_opts|
102
+ process_request uri, kronk_opts.merge(opts)
103
+ end
104
+ end
105
+
106
+
107
+ ##
108
+ # Check if we're only processing a single case.
109
+ # If so, yield a single item and return immediately.
110
+
111
+ def single_request?
112
+ @queue << next_request if @queue.empty? && (!@number || @number <= 1)
113
+ @queue.length == 1 && @input.eof?
114
+ end
115
+
116
+
117
+ ##
118
+ # Start processing the queue and reading from IO if available.
119
+ # Calls Output#start method and returns the value of Output#completed
120
+ # once processing is finished.
121
+ #
122
+ # Yields queue item until queue and io (if available) are empty and the
123
+ # totaly number of requests to run is met (if number is set).
124
+
125
+ def process_queue
126
+ @reader_thread = try_fill_queue
127
+
128
+ trap 'INT' do
129
+ @threads.each{|t| t.kill}
130
+ @threads.clear
131
+ @reader_thread.kill
132
+ output_results
133
+ exit 2
134
+ end
135
+
136
+ @output.start
137
+ @count = 0
138
+
139
+ until finished?
140
+ @threads.delete_if{|t| !t.alive? }
141
+ next if @threads.length >= @concurrency || @queue.empty?
142
+
143
+ @threads << Thread.new(@queue.shift) do |kronk_opts|
144
+ yield kronk_opts if block_given?
145
+ end
146
+
147
+ @count += 1
148
+ end
149
+
150
+ @threads.each{|t| t.join}
151
+ @threads.clear
152
+
153
+ @reader_thread.kill
154
+
155
+ output_results
156
+ end
157
+
158
+
159
+ ##
160
+ # Attempt to fill the queue by reading from the IO instance.
161
+ # Starts a new thread and returns the thread instance.
162
+
163
+ def try_fill_queue
164
+ Thread.new do
165
+ loop do
166
+ break if !@number && @input.eof?
167
+ next if @queue.length >= @concurrency * 2
168
+
169
+ max_new = @concurrency * 2 - @queue.length
170
+
171
+ max_new.times do
172
+ @queue << next_request
173
+ break if !@number && @input.eof?
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+
180
+ ##
181
+ # Gets the next request to perform and always returns a Hash.
182
+ # Tries from input first, then from the last item in the queue.
183
+ # If both fail, returns an empty Hash.
184
+
185
+ def next_request
186
+ @last_req = @input.get_next || @queue.last || @last_req || Hash.new
187
+ end
188
+
189
+
190
+ ##
191
+ # Returns true if processing queue should be stopped, otherwise false.
192
+
193
+ def finished?
194
+ (@number && @count >= @number) || @queue.empty? &&
195
+ @input.eof? && @count > 0 && !@reader_thread.alive?
196
+ end
197
+
198
+
199
+ ##
200
+ # Process and output the results.
201
+ # Calls Output#completed method.
202
+
203
+ def output_results
204
+ @output.completed
205
+ end
206
+
207
+
208
+ ##
209
+ # Run a single compare and call the Output#result or Output#error method.
210
+
211
+ def process_compare uri1, uri2, opts={}
212
+ kronk = Kronk.new opts
213
+ kronk.compare uri1, uri2
214
+ @output.result kronk, @mutex
215
+
216
+ rescue Kronk::Exception, Response::MissingParser, Errno::ECONNRESET => e
217
+ @output.error e, kronk, @mutex
218
+ end
219
+
220
+
221
+ ##
222
+ # Run a single request and call the Output#result or Output#error method.
223
+
224
+ def process_request uri, opts={}
225
+ kronk = Kronk.new opts
226
+ kronk.retrieve uri
227
+ @output.result kronk, @mutex
228
+
229
+ rescue Kronk::Exception, Response::MissingParser, Errno::ECONNRESET => e
230
+ @output.error e, kronk, @mutex
231
+ end
232
+ end
233
+ end