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.
@@ -19,22 +19,6 @@ class Kronk
19
19
  end
20
20
 
21
21
 
22
- ##
23
- # Retrieves the data at the given path array.
24
-
25
- def data_at_path path
26
- self.class.data_at_path @data, path
27
- end
28
-
29
-
30
- ##
31
- # Checks if data is available at the given path.
32
-
33
- def data_at_path? path
34
- self.class.data_at_path? @data, path
35
- end
36
-
37
-
38
22
  ##
39
23
  # Modify the data object by passing inclusive or exclusive data paths.
40
24
  # Supports the following options:
@@ -85,6 +69,10 @@ class Kronk
85
69
  new_curr_data[key] = curr_data[key]
86
70
  break
87
71
 
72
+ elsif path.length == 1 && affect_parent
73
+ new_data = curr_data
74
+ break
75
+
88
76
  else
89
77
  new_curr_data[key] ||= curr_data[key].class.new
90
78
  new_curr_data = new_curr_data[key]
@@ -106,6 +94,8 @@ class Kronk
106
94
  find_data data_path do |obj, k, path|
107
95
 
108
96
  if affect_parent && data_at_path?(path)
97
+ @data = @data.class.new and return if path.length == 1
98
+
109
99
  parent_data = data_at_path path[0..-3]
110
100
  del_method = Array === parent_data ? :delete_at : :delete
111
101
 
@@ -140,23 +130,16 @@ class Kronk
140
130
  # # Returns an Array of grand-children key/value pairs
141
131
  # # where the value is 'invalid' or blank
142
132
 
143
- def find_data data_path, curr_path=nil, &block
144
- self.class.find_data @data, data_path, curr_path, &block
145
- end
146
-
147
-
148
- ##
149
- # See DataSet#find_data
150
-
151
- def self.find_data data, data_path, curr_path=nil, &block
133
+ def find_data data_path, curr_path=nil, data=nil, &block
152
134
  curr_path ||= []
135
+ data ||= @data
153
136
 
154
137
  key, value, rec, data_path = parse_data_path data_path
155
138
 
156
139
  yield_data_points data, key, value, rec, curr_path do |d, k, p|
157
140
 
158
141
  if data_path
159
- find_data d[k], data_path, p, &block
142
+ find_data data_path, p, d[k], &block
160
143
  else
161
144
  yield d, k, p
162
145
  end
@@ -167,8 +150,8 @@ class Kronk
167
150
  ##
168
151
  # Checks if data is available at the given path.
169
152
 
170
- def self.data_at_path? data, path
171
- data_at_path(data, path)
153
+ def data_at_path? path
154
+ data_at_path path
172
155
  true
173
156
 
174
157
  rescue NoMethodError, TypeError
@@ -179,8 +162,8 @@ class Kronk
179
162
  ##
180
163
  # Retrieve the data at the given path array location.
181
164
 
182
- def self.data_at_path data, path
183
- curr = data
165
+ def data_at_path path
166
+ curr = @data
184
167
  path.each do |p|
185
168
  raise TypeError, "Expected instance of Array or Hash" unless
186
169
  Array === curr || Hash === curr
@@ -198,7 +181,7 @@ class Kronk
198
181
  # - Recursive matching
199
182
  # - New data path value
200
183
 
201
- def self.parse_data_path data_path
184
+ def parse_data_path data_path
202
185
  data_path = data_path.dup
203
186
  key = nil
204
187
  value = nil
@@ -236,17 +219,22 @@ class Kronk
236
219
 
237
220
 
238
221
  ##
239
- # Decide whether to make path item a regex or not.
222
+ # Decide whether to make path item a regex, range, array, or string.
240
223
 
241
- def self.parse_path_item str
224
+ def parse_path_item str
242
225
  if str =~ /(^|[^\\])([\*\?\|])/
243
226
  str.gsub!(/(^|[^\\])(\*|\?)/, '\1.\2')
244
- str = /^(#{str})$/
227
+ /^(#{str})$/i
228
+
229
+ elsif str =~ %r{^(\-?\d+)(\.{2,3})(\-?\d+)$}
230
+ Range.new $1.to_i, $3.to_i, ($2 == "...")
231
+
232
+ elsif str =~ %r{^(\-?\d+),(\-?\d+)$}
233
+ Range.new $1.to_i, ($1.to_i + $2.to_i), true
234
+
245
235
  else
246
- str.gsub! "\\", ""
236
+ str.gsub "\\", ""
247
237
  end
248
-
249
- str
250
238
  end
251
239
 
252
240
 
@@ -254,7 +242,7 @@ class Kronk
254
242
  # Yield data object and key, if a specific key or value matches
255
243
  # the given data.
256
244
 
257
- def self.yield_data_points data, mkey, mvalue=nil,
245
+ def yield_data_points data, mkey, mvalue=nil,
258
246
  recursive=false, path=nil, &block
259
247
 
260
248
  return unless Hash === data || Array === data
@@ -266,8 +254,6 @@ class Kronk
266
254
  found = match_data_item(mkey, key) &&
267
255
  match_data_item(mvalue, value)
268
256
 
269
- #puts "Found: #{data.inspect} #{mkey.inspect} -> #{key.inspect}" if found
270
-
271
257
  yield data, key, curr_path if found
272
258
  yield_data_points data[key], mkey, mvalue, true, curr_path, &block if
273
259
  recursive
@@ -278,15 +264,23 @@ class Kronk
278
264
  ##
279
265
  # Check if data key or value is a match for nested data searches.
280
266
 
281
- def self.match_data_item item1, item2
267
+ def match_data_item item1, item2
282
268
  return if !item1.nil? && (Array === item2 || Hash === item2)
283
269
 
284
- if Regexp === item1
270
+ if item1.class == item2.class
271
+ item1 == item2
272
+
273
+ elsif Regexp === item1
285
274
  item2.to_s =~ item1
275
+
276
+ elsif Range === item1
277
+ item1.include? item2.to_i
278
+
286
279
  elsif item1.nil?
287
280
  true
281
+
288
282
  else
289
- item2.to_s == item1.to_s
283
+ item2.to_s.downcase == item1.to_s.downcase
290
284
  end
291
285
  end
292
286
 
@@ -294,7 +288,7 @@ class Kronk
294
288
  ##
295
289
  # Universal iterator for Hash and Array objects.
296
290
 
297
- def self.each_data_item data, &block
291
+ def each_data_item data, &block
298
292
  case data
299
293
 
300
294
  when Hash
@@ -1,87 +1,11 @@
1
1
  class Kronk
2
2
 
3
-
4
3
  ##
5
4
  # Creates simple diffs as formatted strings or arrays, from two strings or
6
5
  # data objects.
7
6
 
8
7
  class Diff
9
8
 
10
- ##
11
- # Format diff with ascii
12
-
13
- class AsciiFormat
14
-
15
- def self.lines line_nums, col_width
16
- out =
17
- [*line_nums].map do |lnum|
18
- lnum.to_s.rjust col_width
19
- end.join "|"
20
-
21
- "#{out} "
22
- end
23
-
24
-
25
- def self.deleted str
26
- "- #{str}"
27
- end
28
-
29
-
30
- def self.added str
31
- "+ #{str}"
32
- end
33
-
34
-
35
- def self.common str
36
- " #{str}"
37
- end
38
- end
39
-
40
-
41
- ##
42
- # Format diff with ascii
43
-
44
- class ColorFormat
45
-
46
- def self.require_win_color
47
- begin
48
- require 'Win32/Console/ANSI'
49
- rescue LoadError
50
- puts "Warning: You must gem install win32console to use color"
51
- end
52
- end
53
-
54
-
55
- def self.lines line_nums, col_width
56
- require_win_color if Kronk.windows?
57
-
58
- out =
59
- [*line_nums].map do |lnum|
60
- lnum.to_s.rjust col_width
61
- end.join "\033[32m"
62
-
63
- "\033[7;31m#{out}\033[0m "
64
- end
65
-
66
-
67
- def self.deleted str
68
- require_win_color if Kronk.windows?
69
- "\033[31m#{str}\033[0m"
70
- end
71
-
72
-
73
- def self.added str
74
- require_win_color if Kronk.windows?
75
- "\033[32m#{str}\033[0m"
76
- end
77
-
78
-
79
- def self.common str
80
- str
81
- end
82
- end
83
-
84
-
85
9
  ##
86
10
  # Creates a new diff from two data objects.
87
11
 
@@ -184,69 +108,127 @@ class Kronk
184
108
 
185
109
  def create_diff
186
110
  diff_ary = []
187
- sub_diff = nil
188
111
 
189
112
  arr1 = @str1.split @char
190
113
  arr2 = @str2.split @char
191
114
 
192
- until arr1.empty? && arr2.empty?
193
- item1, item2 = arr1.shift, arr2.shift
115
+ common_list = find_common arr1, arr2
194
116
 
195
- if item1 == item2
196
- if sub_diff
197
- diff_ary << sub_diff
198
- sub_diff = nil
199
- end
117
+ return [[arr1, arr2]] if common_list.empty?
200
118
 
201
- diff_ary << item1
202
- next
203
- end
119
+ last_i1 = 0
120
+ last_i2 = 0
204
121
 
205
- match1 = arr1.index item2
206
- match2 = arr2.index item1
122
+ common_list.each do |c|
123
+ next unless c
207
124
 
125
+ left = arr1[last_i1...c[1]]
126
+ right = arr2[last_i2...c[2]]
208
127
 
209
- if use_left?(match1, match2)
210
- diff_ary.concat diff_match(item1, match1, arr1, 0, sub_diff)
211
- sub_diff = nil
128
+ # add diffs
129
+ diff_ary << [left, right] unless left.empty? && right.empty?
212
130
 
213
- elsif match2
214
- diff_ary.concat diff_match(item2, match2, arr2, 1, sub_diff)
215
- sub_diff = nil
131
+ # add common
132
+ diff_ary.concat arr1[c[1], c[0]]
216
133
 
217
- elsif !item1.nil? || !item2.nil?
218
- sub_diff ||= [[],[]]
219
- sub_diff[0] << item1 if item1
220
- sub_diff[1] << item2 if item2
221
- end
134
+ last_i1 = c[1] + c[0]
135
+ last_i2 = c[2] + c[0]
222
136
  end
223
137
 
224
- diff_ary << sub_diff if sub_diff
138
+ left = arr1[last_i1..-1]
139
+ right = arr2[last_i2..-1]
140
+
141
+ diff_ary << [left, right] unless left.empty? && right.empty?
225
142
 
226
143
  diff_ary
227
144
  end
228
145
 
229
146
 
230
147
  ##
231
- # Check if the index on the left should be used.
148
+ # Recursively finds common sequences between two arrays and returns
149
+ # them in the order they occur as an array of arrays:
150
+ # find_common arr1, arr2
151
+ # #=> [[size, arr1_index, arr2_index], [size, arr1_index, arr2_index],...]
152
+
153
+ def find_common arr1, arr2
154
+ used1 = []
155
+ used2 = []
156
+
157
+ common = []
232
158
 
233
- def use_left? index1, index2
234
- index1 && (index2.nil? || index1 < index2)
159
+ common_sequences(arr1, arr2) do |seq|
160
+ next if used1[seq[1]] || used2[seq[2]]
161
+
162
+ next if used1[seq[1], seq[0]].to_a.index(true) ||
163
+ used2[seq[2], seq[0]].to_a.index(true)
164
+
165
+ next if used1[seq[1]+seq[0]..-1].to_a.nitems !=
166
+ used2[seq[2]+seq[0]..-1].to_a.nitems
167
+
168
+
169
+ used1.fill(true, seq[1], seq[0])
170
+ used2.fill(true, seq[2], seq[0])
171
+
172
+ common[seq[1]] = seq
173
+ end
174
+
175
+ common
235
176
  end
236
177
 
237
178
 
238
179
  ##
239
- # Create a diff from a match.
180
+ # Returns all common sequences between to arrays ordered by sequence length
181
+ # according to the following format:
182
+ # [[[len1, ix, iy], [len1, ix, iy]],[[len2, ix, iy]]]
183
+ # # e.g.
184
+ # [nil,[[1,2,3],[1,2,5]],nil,[[3,4,5],[3,6,9]]
185
+
186
+ def common_sequences arr1, arr2, &block
187
+ sequences = []
188
+
189
+ arr2_map = {}
190
+ arr2.each_with_index do |line, j|
191
+ arr2_map[line] ||= []
192
+ arr2_map[line] << j
193
+ end
194
+
195
+ arr1.each_with_index do |line, i|
196
+ next unless arr2_map[line]
240
197
 
241
- def diff_match item, match, arr, side, sub_diff
242
- sub_diff ||= [[],[]]
198
+ arr2_map[line].each do |j|
199
+ line1 = line
200
+ line2 = arr2[j]
243
201
 
244
- index = match - 1
245
- added = [item]
246
- added.concat arr.slice!(0..index) if index >= 0
202
+ k = i
203
+ start_j = j
247
204
 
248
- sub_diff[side].concat(added)
249
- [sub_diff, arr.shift]
205
+ while line1 && line1 == line2 && k < arr1.length
206
+ k += 1
207
+ j += 1
208
+
209
+ line1 = arr1[k]
210
+ line2 = arr2[j]
211
+ end
212
+
213
+ len = j - start_j
214
+
215
+ sequences[len] ||= []
216
+ sequences[len] << [len, i, start_j]
217
+ end
218
+ end
219
+
220
+ yield_sequences sequences, &block if block_given?
221
+
222
+ sequences
223
+ end
224
+
225
+
226
+ def yield_sequences sequences, dist=0, &block
227
+ while sequences.length > dist
228
+ item = sequences.pop
229
+ next unless item
230
+ item.each &block
231
+ end
250
232
  end
251
233
 
252
234
 
@@ -329,3 +311,14 @@ class Kronk
329
311
  end
330
312
  end
331
313
  end
314
+
315
+
316
+ # For Ruby 1.9
317
+
318
+ unless [].respond_to? :nitems
319
+ class Array
320
+ def nitems
321
+ self.compact.length
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,35 @@
1
+ class Kronk
2
+
3
+ class Diff
4
+
5
+ ##
6
+ # Format diff with ascii
7
+
8
+ class AsciiFormat
9
+
10
+ def self.lines line_nums, col_width
11
+ out =
12
+ [*line_nums].map do |lnum|
13
+ lnum.to_s.rjust col_width
14
+ end.join "|"
15
+
16
+ "#{out} "
17
+ end
18
+
19
+
20
+ def self.deleted str
21
+ "- #{str}"
22
+ end
23
+
24
+
25
+ def self.added str
26
+ "+ #{str}"
27
+ end
28
+
29
+
30
+ def self.common str
31
+ " #{str}"
32
+ end
33
+ end
34
+ end
35
+ end