rbbt-util 5.4.1 → 5.5.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.
Files changed (45) hide show
  1. checksums.yaml +8 -8
  2. data/bin/rbbt_monitor.rb +8 -4
  3. data/lib/rbbt.rb +4 -11
  4. data/lib/rbbt/annotations.rb +4 -1
  5. data/lib/rbbt/association.rb +218 -157
  6. data/lib/rbbt/association/index.rb +92 -0
  7. data/lib/rbbt/association/item.rb +44 -0
  8. data/lib/rbbt/entity.rb +4 -0
  9. data/lib/rbbt/fix_width_table.rb +14 -9
  10. data/lib/rbbt/knowledge_base.rb +269 -0
  11. data/lib/rbbt/persist.rb +1 -1
  12. data/lib/rbbt/persist/tsv.rb +22 -2
  13. data/lib/rbbt/resource.rb +0 -1
  14. data/lib/rbbt/resource/path.rb +1 -1
  15. data/lib/rbbt/resource/util.rb +0 -1
  16. data/lib/rbbt/tsv.rb +15 -14
  17. data/lib/rbbt/tsv/accessor.rb +21 -16
  18. data/lib/rbbt/tsv/attach.rb +5 -5
  19. data/lib/rbbt/tsv/attach/util.rb +4 -2
  20. data/lib/rbbt/tsv/change_id.rb +67 -0
  21. data/lib/rbbt/tsv/index.rb +5 -3
  22. data/lib/rbbt/tsv/manipulate.rb +83 -37
  23. data/lib/rbbt/tsv/parser.rb +2 -1
  24. data/lib/rbbt/tsv/util.rb +2 -0
  25. data/lib/rbbt/util/cmd.rb +1 -2
  26. data/lib/rbbt/util/log.rb +42 -38
  27. data/lib/rbbt/util/misc.rb +134 -46
  28. data/lib/rbbt/util/open.rb +3 -17
  29. data/lib/rbbt/util/semaphore.rb +8 -2
  30. data/lib/rbbt/workflow.rb +31 -46
  31. data/lib/rbbt/workflow/accessor.rb +1 -1
  32. data/lib/rbbt/workflow/step.rb +5 -3
  33. data/share/rbbt_commands/workflow/server +1 -0
  34. data/share/rbbt_commands/workflow/task +12 -2
  35. data/test/rbbt/association/test_index.rb +36 -0
  36. data/test/rbbt/test_annotations.rb +5 -4
  37. data/test/rbbt/test_association.rb +40 -13
  38. data/test/rbbt/test_knowledge_base.rb +103 -0
  39. data/test/rbbt/test_workflow.rb +4 -2
  40. data/test/rbbt/tsv/test_change_id.rb +43 -0
  41. data/test/rbbt/tsv/test_index.rb +2 -1
  42. data/test/rbbt/tsv/test_manipulate.rb +51 -0
  43. data/test/rbbt/util/test_misc.rb +21 -1
  44. data/test/test_helper.rb +8 -4
  45. metadata +12 -86
data/lib/rbbt/persist.rb CHANGED
@@ -43,7 +43,7 @@ module Persist
43
43
 
44
44
  def self.persistence_path(file, persist_options = {}, options = {})
45
45
  persistence_file = Misc.process_options persist_options, :file
46
- return persistence_file if not persistence_file.nil?
46
+ return persistence_file unless persistence_file.nil?
47
47
 
48
48
  prefix = Misc.process_options persist_options, :prefix
49
49
 
@@ -1,4 +1,8 @@
1
- require 'tokyocabinet'
1
+ begin
2
+ require 'tokyocabinet'
3
+ rescue Exception
4
+ Log.warn "The tokyocabinet gem could not be loaded: persistence over TSV files will fail"
5
+ end
2
6
 
3
7
  module Persist
4
8
  TC_CONNECTIONS = {}
@@ -85,6 +89,20 @@ module Persist
85
89
  out(key)
86
90
  end
87
91
 
92
+ def write_and_read
93
+ lock_filename = Persist.persistence_path(persistence_path, {:dir => TSV.lock_dir})
94
+ Misc.lock(lock_filename) do
95
+ write if @closed or not write?
96
+ res = begin
97
+ yield
98
+ ensure
99
+ read
100
+ end
101
+ res
102
+ end
103
+ end
104
+
105
+
88
106
  def write_and_close
89
107
  lock_filename = Persist.persistence_path(persistence_path, {:dir => TSV.lock_dir})
90
108
  Misc.lock(lock_filename) do
@@ -148,10 +166,12 @@ module Persist
148
166
  filename ||= case
149
167
  when Path === source
150
168
  source
151
- when source.respond_to?(:filename)
169
+ when (source.respond_to?(:filename) and source.filename)
152
170
  source.filename
153
171
  when source.respond_to?(:cmd)
154
172
  "CMD-#{Misc.digest(source.cmd)}"
173
+ when TSV === source
174
+ "TSV[#{Misc.digest Misc.fingerprint(source)}]"
155
175
  else
156
176
  source.object_id.to_s
157
177
  end
data/lib/rbbt/resource.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'rbbt/util/open'
2
2
  require 'rbbt/util/log'
3
- require 'rbbt/util/chain_methods'
4
3
  require 'rbbt/resource/path'
5
4
 
6
5
  module Resource
@@ -1,5 +1,5 @@
1
1
  require 'rbbt/resource/util'
2
- require 'rbbt/tsv'
2
+ require 'yaml'
3
3
 
4
4
  module Path
5
5
  attr_accessor :resource, :pkgdir
@@ -1,4 +1,3 @@
1
-
2
1
  module Path
3
2
 
4
3
  def self.caller_lib_dir(file = nil, relative_to = 'lib')
data/lib/rbbt/tsv.rb CHANGED
@@ -1,10 +1,9 @@
1
- require 'yaml'
1
+ require 'rbbt/persist'
2
+ require 'rbbt/persist/tsv'
2
3
 
4
+ require 'rbbt/util/log'
3
5
  require 'rbbt/util/misc'
4
6
  require 'rbbt/util/named_array'
5
- require 'rbbt/util/log'
6
-
7
- require 'rbbt/persist'
8
7
 
9
8
  require 'rbbt/tsv/util'
10
9
  require 'rbbt/tsv/serializers'
@@ -36,6 +35,8 @@ module TSV
36
35
  hash.send("#{ entry }=", options[entry.to_sym]) if options.include? entry.to_sym
37
36
  end
38
37
 
38
+ hash.unnamed = options[:unnamed]
39
+
39
40
  hash
40
41
  end
41
42
 
@@ -95,17 +96,19 @@ module TSV
95
96
  stream.open do |f|
96
97
  Parser.new f, options
97
98
  end
98
- when (String === stream and stream.length < 300 and Open.exists? stream)
99
+ when (String === stream and stream.length < 300 and Open.exists? stream or Open.remote? stream)
99
100
  Open.open(stream) do |f|
100
101
  Parser.new f, options
101
102
  end
102
103
  else
104
+ filename = stream.respond_to?(:filename) ? stream.filename : Misc.fingerprint(stream)
105
+ Log.debug("Parsing header of open stream: #{filename}")
103
106
  Parser.new stream, options
104
107
  end
105
108
  end
106
109
 
107
110
  def self.parse(stream, data, options = {})
108
- monitor, grep, invert_grep = Misc.process_options options, :monitor, :grep, :invert_grep
111
+ monitor, grep, invert_grep, head = Misc.process_options options, :monitor, :grep, :invert_grep, :head
109
112
 
110
113
  parser = Parser.new stream, options
111
114
 
@@ -117,19 +120,14 @@ module TSV
117
120
 
118
121
  line = parser.rescue_first_line
119
122
 
120
- if TokyoCabinet::HDB === data and parser.straight
123
+ if TokyoCabinet::HDB === data and parser.straight and
121
124
  data.close
122
- pos = stream.pos if stream.respond_to? :pos
123
125
  begin
126
+ CMD.cmd('tchmgr', :log => false)
127
+ FileUtils.mkdir_p File.dirname(data.persistence_path)
124
128
  CMD.cmd("tchmgr importtsv '#{data.persistence_path}'", :in => stream, :log => false, :dont_close_in => true)
125
129
  rescue
126
130
  Log.debug("tchmgr importtsv failed for: #{data.persistence_path}")
127
- Log.debug($!.message)
128
- if stream.respond_to? :seek
129
- stream.seek pos
130
- else
131
- #raise "tchmgr import failed and cannot restore stream"
132
- end
133
131
  end
134
132
  data.write
135
133
  end
@@ -170,6 +168,7 @@ module TSV
170
168
  progress_monitor = nil
171
169
  end
172
170
 
171
+ line_num = 1
173
172
  while not line.nil?
174
173
  begin
175
174
  progress_monitor.tick(stream.pos) if progress_monitor
@@ -183,6 +182,8 @@ module TSV
183
182
  values = parser.cast_values values if parser.cast?
184
183
  parser.add_to_data data, key, values
185
184
  line = stream.gets
185
+ line_num += 1
186
+ raise Parser::END_PARSING if head and line_num > head.to_i
186
187
  rescue Parser::SKIP_LINE
187
188
  begin
188
189
  line = stream.gets
@@ -1,4 +1,3 @@
1
- #require 'rbbt/util/chain_methods'
2
1
  require 'yaml'
3
2
  module TSV
4
3
  #extend ChainMethods
@@ -35,7 +34,6 @@ module TSV
35
34
  return entity if entity.nil?
36
35
  return entity unless defined? Entity
37
36
  entity = entity if options.delete :dup_array
38
- entity_templates
39
37
  if (template = entity_templates[field])
40
38
  entity = template.annotate(entity.frozen? ? entity.dup : entity)
41
39
  entity.extend AnnotatedArray if Array === entity
@@ -263,7 +261,7 @@ module TSV
263
261
  end
264
262
  when :list, :flat
265
263
  through :key, field do |key, fields|
266
- elems << [key, fields]
264
+ elems << [key, fields.first]
267
265
  end
268
266
  when :double
269
267
  through :key, field do |key, fields|
@@ -481,19 +479,22 @@ end
481
479
 
482
480
 
483
481
  def all_fields
482
+ return nil if key_field.nil? or fields.nil?
484
483
  [key_field] + fields
485
484
  end
486
485
 
487
486
  def values_to_s(values)
488
- case
489
- when (values.nil? and fields.nil?)
490
- "\n"
491
- when (values.nil? and not fields.nil?)
492
- "\t" << ([""] * fields.length) * "\t" << "\n"
493
- when (not Array === values)
494
- "\t" << values.to_s << "\n"
495
- else
487
+ case values
488
+ when nil
489
+ if fields.nil? or fields.empty?
490
+ "\n"
491
+ else
492
+ "\t" << ([""] * fields.length) * "\t" << "\n"
493
+ end
494
+ when Array
496
495
  "\t" << values.collect{|v| Array === v ? v * "|" : v} * "\t" << "\n"
496
+ else
497
+ "\t" << values.to_s << "\n"
497
498
  end
498
499
  end
499
500
 
@@ -541,14 +542,18 @@ end
541
542
  end
542
543
 
543
544
  def summary
544
- <<-EOF
545
- Key field = #{key_field}
546
- Fields = #{fields * ", "}
545
+ with_unnamed do
546
+ <<-EOF
547
+ Key field = #{key_field || "*No key field*"}
548
+ Fields = #{fields ? Misc.fingerprint(fields) : "*No field info*"}
547
549
  Type = #{type}
550
+ Size = #{size}
551
+ namespace = #{namespace}
548
552
  Example:
549
- - #{key = keys.first}: #{self[key].inspect}
553
+ - #{key = keys.first}: #{Misc.fingerprint self[key] }
550
554
 
551
- EOF
555
+ EOF
556
+ end
552
557
  end
553
558
 
554
559
  def to_hash
@@ -144,9 +144,9 @@ module TSV
144
144
  in_namespace = options[:in_namespace]
145
145
 
146
146
  unless TSV === other
147
- other_identifier_files = other.identifier_files if other.respond_to? :identifier_files
147
+ other_identifier_file = other.identifier_files.first if other.respond_to? :identifier_files
148
148
  other = TSV.open(other, :persist => options[:persist_input] == true)
149
- other.identifiers = other_identifier_files
149
+ other.identifiers = other_identifier_file
150
150
  end
151
151
 
152
152
  fields = other.fields - [key_field].concat(self.fields) if fields.nil? or fields == :all
@@ -157,10 +157,10 @@ module TSV
157
157
  end
158
158
 
159
159
  other_filename = other.respond_to?(:filename) ? other.filename : other.inspect
160
- Log.low("Attaching fields:#{fields.inspect} from #{other_filename}.")
160
+ Log.low("Attaching fields:#{Misc.fingerprint fields } from #{other_filename}.")
161
161
 
162
162
  case
163
- when key_field == other.key_field
163
+ when key_field == other.key_field
164
164
  Log.debug "Attachment with same key: #{other.key_field}"
165
165
  attach_same_key other, fields
166
166
  when (not in_namespace and self.fields.include?(other.key_field))
@@ -175,7 +175,7 @@ module TSV
175
175
  Log.debug "Attachment with index: #{other.key_field}"
176
176
  attach_index other, index, fields
177
177
  end
178
- Log.debug("Attachment of fields:#{fields.inspect} from #{other.filename.inspect} finished.")
178
+ Log.debug("Attachment of fields:#{Misc.fingerprint fields } from #{other.filename.inspect} finished.")
179
179
 
180
180
  self
181
181
  end
@@ -36,7 +36,7 @@ module TSV
36
36
  if type == :double
37
37
  self[key] = current.concat [[]] * fields.length
38
38
  else
39
- self[key] = current.concat [""] * fields.length
39
+ self[key] = current.concat [nil] * fields.length
40
40
  end
41
41
  end
42
42
  end
@@ -46,6 +46,8 @@ module TSV
46
46
  self.type = :list if self.type == :single
47
47
 
48
48
  self.fields = self.fields.concat fields
49
+
50
+ self
49
51
  end
50
52
 
51
53
  def attach_source_key(other, source, options = {})
@@ -169,7 +171,7 @@ module TSV
169
171
  if type == :double
170
172
  all_new_values = [[[]] * field_positions.length]
171
173
  else
172
- all_new_values = [[""] * field_positions.length]
174
+ all_new_values = [[nil] * field_positions.length]
173
175
  end
174
176
  end
175
177
 
@@ -0,0 +1,67 @@
1
+ require 'rbbt/tsv'
2
+
3
+ module TSV
4
+ def self.change_key(tsv, format, options = {})
5
+ options = Misc.add_defaults options, :persist => false, :identifiers => tsv.identifiers
6
+
7
+ identifiers, persist_input = Misc.process_options options, :identifiers, :persist_input
8
+
9
+ if not tsv.fields.include? format
10
+ tsv = tsv.annotate(Hash[*tsv.keys.zip(tsv.values.collect{|l| l.dup}).flatten(1)])
11
+
12
+ orig_type = tsv.type
13
+ tsv = tsv.to_double if orig_type != :double
14
+
15
+ tsv = tsv.attach identifiers, :fields => [format], :persist_input => true
16
+ tsv = tsv.reorder(format, tsv.fields - [format])
17
+
18
+ tsv = tsv.to_flat if orig_type == :flat
19
+
20
+ tsv
21
+ else
22
+ tsv.reorder(format)
23
+ end
24
+ end
25
+
26
+ def change_key(*args)
27
+ TSV.change_key(self, *args)
28
+ end
29
+
30
+ def self.swap_id(tsv, field, format, options = {})
31
+ options = Misc.add_defaults options, :persist => false, :identifiers => tsv.identifiers
32
+
33
+ identifiers, persist_input = Misc.process_options options, :identifiers, :persist
34
+
35
+ index = identifiers.index :target => format, :fields => [field], :persist => persist_input
36
+
37
+ orig_type = tsv.type
38
+ tsv = tsv.to_double if orig_type != :double
39
+
40
+ pos = tsv.fields.index field
41
+ tsv.with_unnamed do
42
+ if tsv.type == :list or tsv.type == :single
43
+ tsv.through do |k,v|
44
+ v[pos] = index[v[pos]]
45
+ tsv[k] = v
46
+ end
47
+ else
48
+ tsv.through do |k,v|
49
+ v[pos] = index.values_at(*v[pos])
50
+ tsv[k] = v
51
+ end
52
+ end
53
+
54
+ tsv.fields = tsv.fields.collect{|f| f == field ? format : f}
55
+ end
56
+
57
+ tsv = tsv.to_flat if orig_type == :flat
58
+
59
+ tsv
60
+ end
61
+
62
+ def swap_id(*args)
63
+ TSV.swap_id(self, *args)
64
+ end
65
+
66
+
67
+ end
@@ -1,9 +1,11 @@
1
- require 'rbbt/util/misc'
2
1
  require 'rbbt/fix_width_table'
2
+ require 'rbbt/util/misc'
3
+
4
+ require 'rbbt/persist'
5
+ require 'rbbt/persist/tsv'
6
+
3
7
  require 'rbbt/tsv/manipulate'
4
8
  require 'rbbt/tsv/filter'
5
- require 'rbbt/persist/tsv'
6
- require 'rbbt/persist'
7
9
 
8
10
  module TSV
9
11
 
@@ -1,7 +1,4 @@
1
1
  require 'progress-bar'
2
- require 'rbbt/persist'
3
- require 'rbbt/tsv/util'
4
- require 'set'
5
2
 
6
3
  module TSV
7
4
 
@@ -59,6 +56,7 @@ module TSV
59
56
  [key] : values[field] }.flatten
60
57
  ]
61
58
  end
59
+
62
60
  def initialize(key_field, fields, new_key_field, new_fields, type, uniq)
63
61
  @new_key_field = TSV.identify_field(key_field, fields, new_key_field)
64
62
 
@@ -235,28 +233,55 @@ module TSV
235
233
  persist_options[:prefix] = "Reorder"
236
234
 
237
235
  Persist.persist_tsv self, self.filename, {:key_field => new_key_field, :fields => new_fields}, persist_options do |data|
236
+ if data.respond_to? :persistence_path
237
+ real_data = data
238
+ data = {}
239
+ end
238
240
 
239
241
  with_unnamed do
240
- new_key_field_name, new_field_names = through new_key_field, new_fields, uniq, zipped do |key, value|
241
- if data.include?(key) and not zipped
242
- case type
243
- when :double
244
- data[key] = data[key].zip(value).collect do |old_list, new_list| old_list + new_list end
245
- when :flat
242
+ if zipped or (type != :double and type != :flat)
243
+ new_key_field_name, new_field_names = through new_key_field, new_fields, uniq, zipped do |key, value|
244
+ data[key] = value.clone if Array === value
245
+ end
246
+ else
247
+ case type
248
+ when :double
249
+ new_key_field_name, new_field_names = through new_key_field, new_fields, uniq, zipped do |key, value|
250
+ if data[key].nil?
251
+ #data[key] = value.collect{|v| v.dup}
252
+ data[key] = value.collect{|v| v.dup}
253
+ else
254
+ current = data[key]
255
+ value.each_with_index do |v, i|
256
+ current[i].concat v
257
+ end
258
+ data[key] = current if data.respond_to? :tokyocabinet_class
259
+ end
260
+ end
261
+ when :flat
262
+ new_key_field_name, new_field_names = through new_key_field, new_fields, uniq, zipped do |key, value|
263
+ data[key] ||= []
246
264
  data[key].concat value
247
265
  end
248
- else
249
- data[key] = value.dup
250
266
  end
251
267
  end
252
268
 
269
+ if real_data and real_data.respond_to? :persistence_path
270
+ real_data.serializer = type if real_data.respond_to? :serializer
271
+ real_data.merge!(data)
272
+ data = real_data
273
+ end
274
+
253
275
  data.extend TSV unless TSV === data
254
276
  data.key_field = new_key_field_name
255
277
  data.fields = new_field_names
256
278
  data.filename = filename
257
279
  data.namespace = namespace
258
280
  data.entity_options = entity_options
259
- data.entity_templates = entity_templates
281
+ data.entity_templates = {}
282
+ data.fields.each do |field|
283
+ data.entity_templates[field] = entity_templates[field] if entity_templates.include? field
284
+ end
260
285
  data.type = zipped ? :list : type
261
286
  end
262
287
  end
@@ -289,7 +314,7 @@ module TSV
289
314
  elems.sort_by{|k,v| v}.collect{|k,v| k}
290
315
  end
291
316
 
292
- def select(method = nil, &block)
317
+ def select(method = nil, invert = false, &block)
293
318
  new = TSV.setup({}, :key_field => key_field, :fields => fields, :type => type, :filename => filename, :identifiers => identifiers)
294
319
 
295
320
  new.key_field = key_field
@@ -303,7 +328,7 @@ module TSV
303
328
  case
304
329
  when (method.nil? and block_given?)
305
330
  through do |key, values|
306
- new[key] = values if yield key, values
331
+ new[key] = values if invert ^ (yield key, values)
307
332
  end
308
333
  when Array === method
309
334
  method = Set.new method
@@ -311,22 +336,22 @@ module TSV
311
336
  case type
312
337
  when :single
313
338
  through do |key, value|
314
- new[key] = value if method.include? key or method.include? value
339
+ new[key] = value if invert ^ (method.include? key or method.include? value)
315
340
  end
316
341
  when :list, :flat
317
342
  through do |key, values|
318
- new[key] = values if method.include? key or (method & values).any?
343
+ new[key] = values if invert ^ (method.include? key or (method & values).any?)
319
344
  end
320
345
  else
321
346
  through do |key, values|
322
- new[key] = values if method.include? key or (method & values.flatten).any?
347
+ new[key] = values if invert ^ (method.include? key or (method & values.flatten).any?)
323
348
  end
324
349
  end
325
350
  end
326
351
  when Regexp === method
327
352
  with_unnamed do
328
353
  through do |key, values|
329
- new[key] = values if [key,values].flatten.select{|v| v =~ method}.any?
354
+ new[key] = values if invert ^ ([key,values].flatten.select{|v| v =~ method}.any?)
330
355
  end
331
356
  end
332
357
  when String === method
@@ -337,18 +362,18 @@ module TSV
337
362
  case
338
363
  when (method == key_field or method == :key)
339
364
  through do |key, values|
340
- new[key] = values if yield(key)
365
+ new[key] = values if invert ^ (yield(key))
341
366
  end
342
367
  when (type == :single or type == :flat)
343
368
  through do |key, value|
344
- new[key] = value if yield(value)
369
+ new[key] = value if invert ^ (yield(value))
345
370
  end
346
371
  else
347
372
  pos = identify_field method
348
373
  raise "Field #{ method } not identified. Available: #{ fields * ", " }" if pos.nil?
349
374
 
350
375
  through do |key, values|
351
- new[key] = values if yield(values[pos])
376
+ new[key] = values if invert ^ (yield(values[pos]))
352
377
  end
353
378
  end
354
379
  end
@@ -357,28 +382,26 @@ module TSV
357
382
  case
358
383
  when (method == key_field or method == :key)
359
384
  through do |key, values|
360
- new[key] = values if yield(key, key)
385
+ new[key] = values if invert ^ (yield(key, key))
361
386
  end
362
387
  when (type == :single or type == :flat)
363
388
  through do |key, value|
364
- new[key] = value if yield(key, value)
389
+ new[key] = value if invert ^ (yield(key, value))
365
390
  end
366
391
  else
367
392
  pos = identify_field method
368
393
  through do |key, values|
369
- new[key] = values if yield(key, values[pos])
394
+ new[key] = values if invert ^ (yield(key, values[pos]))
370
395
  end
371
396
  end
372
397
 
373
398
  end
374
-
375
399
  end
376
400
 
377
-
378
401
  else
379
402
  with_unnamed do
380
403
  through do |key, values|
381
- new[key] = values if [key,values].flatten.select{|v| v == method}.any?
404
+ new[key] = values if invert ^ ([key,values].flatten.select{|v| v == method}.any?)
382
405
  end
383
406
  end
384
407
  end
@@ -389,7 +412,7 @@ module TSV
389
412
  when (Array === method and (key == :key or key_field == key))
390
413
  with_unnamed do
391
414
  method.each{|key|
392
- new[key] = self[key] if self.include? key
415
+ new[key] = self[key] if invert ^ (self.include? key)
393
416
  }
394
417
  end
395
418
  when Array === method
@@ -398,45 +421,68 @@ module TSV
398
421
  case type
399
422
  when :single
400
423
  through :key, key do |key, value|
401
- new[key] = self[key] if method.include? value
424
+ new[key] = self[key] if invert ^ (method.include? value)
402
425
  end
403
426
  when :list
404
427
  through :key, key do |key, values|
405
- new[key] = self[key] if method.include? values.first
428
+ new[key] = self[key] if invert ^ (method.include? values.first)
406
429
  end
407
430
  when :flat #untested
408
431
  through :key, key do |key, values|
409
- new[key] = self[key] if (method & values.flatten).any?
432
+ new[key] = self[key] if invert ^ ((method & values.flatten).any?)
410
433
  end
411
434
  else
412
435
  through :key, key do |key, values|
413
- new[key] = self[key] if (method & values.flatten).any?
436
+ new[key] = self[key] if invert ^ ((method & values.flatten).any?)
414
437
  end
415
438
  end
416
439
  end
440
+
417
441
  when Regexp === method
418
442
  with_unnamed do
419
443
  through :key, key do |key, values|
420
444
  values = [values] if type == :single
421
- new[key] = self[key] if values.flatten.select{|v| v =~ method}.any?
445
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| v =~ method}.any?)
422
446
  end
423
447
  end
448
+
449
+ when (String === method and method =~ /name:(.*)/)
450
+ name = $1
451
+ if name.strip =~ /^\/(.*)\/$/
452
+ regexp = Regexp.new $1
453
+ through :key, key do |key, values|
454
+ values = [values] if type == :single
455
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| v.name =~ regexp}.any?)
456
+ end
457
+ else
458
+ through :key, key do |key, values|
459
+ values = [values] if type == :single
460
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| v.name == name}.any?)
461
+ end
462
+ end
463
+
424
464
  when String === method
425
465
  with_unnamed do
426
466
  through :key, key do |key, values|
427
467
  values = [values] if type == :single
428
- new[key] = self[key] if values.flatten.select{|v| v == method}.any?
468
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| v == method}.any?)
469
+ end
470
+ end
471
+
472
+ when Fixnum === method
473
+ with_unnamed do
474
+ through :key, key do |key, values|
475
+ new[key] = self[key] if invert ^ (values.flatten.length >= method)
429
476
  end
430
477
  end
431
478
  when Proc === method
432
479
  with_unnamed do
433
480
  through :key, key do |key, values|
434
481
  values = [values] if type == :single
435
- new[key] = self[key] if values.flatten.select{|v| method.call(v)}.any?
482
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| method.call(v)}.any?)
436
483
  end
437
484
  end
438
485
  end
439
-
440
486
  end
441
487
 
442
488
  new
@@ -538,7 +584,7 @@ module TSV
538
584
  values = [new_values]
539
585
  when values.nil?
540
586
  values = [nil] * fields.length + [new_values]
541
- when NamedArray === values
587
+ when Array === values
542
588
  values += [new_values]
543
589
  else
544
590
  values << new_values