scout-gear 5.1.1 → 6.0.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +24 -12
  3. data/Rakefile +2 -0
  4. data/VERSION +1 -1
  5. data/bin/scout +2 -0
  6. data/lib/scout/exceptions.rb +14 -2
  7. data/lib/scout/log/color.rb +34 -10
  8. data/lib/scout/log/progress/report.rb +5 -4
  9. data/lib/scout/meta_extension.rb +4 -2
  10. data/lib/scout/misc/format.rb +16 -4
  11. data/lib/scout/misc/monitor.rb +41 -0
  12. data/lib/scout/misc.rb +1 -0
  13. data/lib/scout/open/stream.rb +31 -0
  14. data/lib/scout/path/find.rb +2 -1
  15. data/lib/scout/path.rb +1 -1
  16. data/lib/scout/persist/serialize.rb +15 -4
  17. data/lib/scout/resource/path.rb +5 -0
  18. data/lib/scout/resource/util.rb +48 -0
  19. data/lib/scout/resource.rb +2 -0
  20. data/lib/scout/semaphore.rb +148 -0
  21. data/lib/scout/simple_opt/doc.rb +26 -2
  22. data/lib/scout/work_queue/socket.rb +119 -0
  23. data/lib/scout/work_queue/worker.rb +54 -0
  24. data/lib/scout/work_queue.rb +86 -0
  25. data/lib/scout/workflow/definition.rb +8 -2
  26. data/lib/scout/workflow/documentation.rb +32 -26
  27. data/lib/scout/workflow/step/info.rb +13 -13
  28. data/lib/scout/workflow/step/load.rb +18 -0
  29. data/lib/scout/workflow/step.rb +40 -4
  30. data/lib/scout/workflow/task/inputs.rb +4 -2
  31. data/lib/scout/workflow/task.rb +15 -1
  32. data/lib/scout/workflow/usage.rb +96 -76
  33. data/lib/scout/workflow.rb +1 -0
  34. data/scout-gear.gemspec +25 -3
  35. data/scout_commands/workflow/info +29 -0
  36. data/scout_commands/workflow/list +27 -0
  37. data/scout_commands/workflow/task +32 -681
  38. data/scout_commands/workflow/task_old +706 -0
  39. data/share/color/color_names +507 -0
  40. data/share/color/diverging_colors.hex +12 -0
  41. data/test/scout/log/test_color.rb +0 -0
  42. data/test/scout/resource/test_util.rb +27 -0
  43. data/test/scout/simple_opt/test_doc.rb +16 -0
  44. data/test/scout/test_meta_extension.rb +9 -0
  45. data/test/scout/test_semaphore.rb +17 -0
  46. data/test/scout/test_work_queue.rb +93 -0
  47. data/test/scout/work_queue/test_socket.rb +46 -0
  48. data/test/scout/work_queue/test_worker.rb +99 -0
  49. data/test/scout/workflow/step/test_info.rb +17 -15
  50. data/test/scout/workflow/step/test_load.rb +65 -0
  51. data/test/scout/workflow/test_definition.rb +0 -0
  52. data/test/scout/workflow/test_documentation.rb +30 -0
  53. data/test/scout/workflow/test_task.rb +1 -0
  54. data/test/scout/workflow/test_usage.rb +12 -3
  55. metadata +24 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '070801d66b058318a5be03704b424158222038331dac4d7984fab415ca1680ee'
4
- data.tar.gz: e58bcd314c0fa55acb722d12f8e77e69ade25f182501823db9a14691eaf3a887
3
+ metadata.gz: 919678993bd756d6a67712023120e562c01a69942faf8ad23eb088f29559b1fa
4
+ data.tar.gz: a233c808dc9af64381196bce97c74727c9064c9b1a4c2a129b364a5d776b01e1
5
5
  SHA512:
6
- metadata.gz: 81146847ebd0802456b42f152d2fe6e96d1a4ab434b20111afd2f9bd2bf7e1006d8ce2108ba22db78ae65e23ba7e69d6e4e28543aaa3c55a0482727b2472ed7c
7
- data.tar.gz: 59d1574833255c0336472a5297bb00e21319b044bca684b018512557de65cc8ff34091b2bc9adcd5f4758833793cb0b4a8092927835a328f76170fa8c60be115
6
+ metadata.gz: 842323af61a5c66c7bf4433cbc0d23e6484e562c88601c64ac7a47ea2c1e217e6f7b590bd7d613d62614ebb2ab228246a2ac150d4d263f9425d9c1a2b4333e65
7
+ data.tar.gz: 2a4a7d00279d0cbc3153d7b116405fb5d02f8656249b4e0bdf417d01599749686242fb30db183b2baff856e92dcd5c83317161e73bb5bb1dbc861ce10d0086de
data/.vimproject CHANGED
@@ -1,16 +1,19 @@
1
1
  scout-gear=/$PWD filter="*.rb *.yaml" {
2
2
  Rakefile
3
- bin=bin filter="*"{
3
+ bin=bin filter="*"{
4
4
  scout
5
- scout_rbbt
6
5
  }
7
6
  scout_commands=scout_commands filter="*"{
7
+ rbbt
8
+ alias
8
9
  find
9
10
  glob
10
11
  workflow=workflow{
12
+ task_old
11
13
  task
14
+ list
15
+ info
12
16
  }
13
- alias
14
17
  }
15
18
  lib=lib {
16
19
  scout-gear.rb
@@ -26,6 +29,7 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
26
29
  insist.rb
27
30
  digest.rb
28
31
  filesystem.rb
32
+ monitor.rb
29
33
  }
30
34
  indiferent_hash.rb
31
35
  indiferent_hash=indiferent_hash{
@@ -73,8 +77,9 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
73
77
  produce=produce{
74
78
  rake.rb
75
79
  }
76
- path.rb
77
80
  scout.rb
81
+ util.rb
82
+ path.rb
78
83
  }
79
84
  persist.rb
80
85
  persist=persist{
@@ -88,6 +93,7 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
88
93
  step.rb
89
94
  step=step{
90
95
  info.rb
96
+ load.rb
91
97
  }
92
98
  task.rb
93
99
  task=task{
@@ -97,6 +103,12 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
97
103
  usage.rb
98
104
  util.rb
99
105
  }
106
+ semaphore.rb
107
+ work_queue.rb
108
+ work_queue=work_queue{
109
+ socket.rb
110
+ worker.rb
111
+ }
100
112
  }
101
113
  }
102
114
  test=test {
@@ -218,16 +230,16 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
218
230
  concurrency=concurrency{
219
231
  processes.rb
220
232
  threads.rb
221
- processes=processes{
222
- socket.rb
223
- worker.rb
224
- }
225
- }
233
+ processes=processes{
234
+ socket.rb
235
+ worker.rb
236
+ }
237
+ }
226
238
 
227
- procpath.rb
239
+ procpath.rb
228
240
 
229
- migrate.rb
230
- }
241
+ migrate.rb
242
+ }
231
243
 
232
244
  monitor.rb
233
245
 
data/Rakefile CHANGED
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
+ ENV["BRANCH"] = 'main'
4
+
3
5
  require 'rubygems'
4
6
  require 'rake'
5
7
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.1.1
1
+ 6.0.0
data/bin/scout CHANGED
@@ -39,6 +39,8 @@ if dev_dir
39
39
  end
40
40
  end
41
41
 
42
+ Log.nocolor = true if ARGV.include? "--nocolor"
43
+
42
44
  require 'scout/simple_opt'
43
45
 
44
46
  options = SOPT.setup <<EOF
@@ -71,8 +71,20 @@ end
71
71
 
72
72
  class LockInterrupted < TryAgain; end
73
73
 
74
- #
75
- #class ClosedStream < StandardError; end
74
+ class ClosedStream < StandardError; end
75
+
76
+ class DoneProcessing < Exception
77
+ attr_accessor :pid
78
+ def initialize(pid = Process.pid)
79
+ @pid = pid
80
+ end
81
+
82
+ def message
83
+ "Done processing pid #{pid}"
84
+ end
85
+ end
86
+
87
+
76
88
  #class OpenGzipError < StandardError; end
77
89
  #
78
90
  #
@@ -2,6 +2,7 @@ require_relative 'color_class'
2
2
  require_relative '../indiferent_hash'
3
3
 
4
4
  require 'term/ansicolor'
5
+ require 'colorist'
5
6
 
6
7
  module Colorize
7
8
  def self.colors=(colors)
@@ -9,7 +10,13 @@ module Colorize
9
10
  end
10
11
 
11
12
  def self.colors
12
- @colors ||= IndiferentHash.setup({green: "#00cd00" , red: "#cd0000" , yellow: "#ffd700" })
13
+ @colors ||= IndiferentHash.setup(Hash[<<-EOF.split("\n").collect{|l| l.split(" ")}])
14
+ green #00cd00
15
+ red #cd0000
16
+ yellow #ffd700
17
+ blue #0000cd
18
+ path blue
19
+ EOF
13
20
  end
14
21
 
15
22
  def self.diverging_colors=(colors)
@@ -33,7 +40,6 @@ module Colorize
33
40
  EOF
34
41
  end
35
42
 
36
-
37
43
  def self.from_name(color)
38
44
  return color if color =~ /^#?[0-9A-F]+$/i
39
45
  return colors[color.to_s] if colors.include?(color.to_s)
@@ -52,7 +58,7 @@ module Colorize
52
58
  when 'blue'
53
59
  colors["RoyalBlue"]
54
60
  else
55
- colors[color.to_s] || color
61
+ colors[color.to_s] || color.to_s
56
62
  end
57
63
  end
58
64
 
@@ -134,8 +140,21 @@ module Log
134
140
  WHITE, DARK, GREEN, YELLOW, RED = Color::SOLARIZED.values_at :base0, :base00, :green, :yellow, :magenta
135
141
 
136
142
  SEVERITY_COLOR = [reset, cyan, green, magenta, blue, yellow, red] #.collect{|e| "\033[#{e}"}
143
+ CONCEPT_COLORS = IndiferentHash.setup({
144
+ :title => magenta,
145
+ :path => blue,
146
+ :input => blue,
147
+ :value => green,
148
+ :integer => green,
149
+ :negative => red,
150
+ :float => green,
151
+ :waiting => yellow,
152
+ :started => blue,
153
+ :done => green,
154
+ :error => red,
155
+ })
137
156
  HIGHLIGHT = "\033[1m"
138
-
157
+
139
158
  def self.uncolor(str)
140
159
  "" << Term::ANSIColor.uncolor(str)
141
160
  end
@@ -144,15 +163,20 @@ module Log
144
163
  reset
145
164
  end
146
165
 
147
- def self.color(severity, str = nil, reset = false)
166
+ def self.color(color, str = nil, reset = false)
148
167
  return str.dup || "" if nocolor
149
- color = reset ? Term::ANSIColor.reset : ""
150
- color << SEVERITY_COLOR[severity] if Integer === severity
151
- color << Term::ANSIColor.send(severity) if Symbol === severity and Term::ANSIColor.respond_to? severity
168
+
169
+ color = SEVERITY_COLOR[color] if Integer === color
170
+ color = CONCEPT_COLORS[color] if CONCEPT_COLORS.include?(color)
171
+ color = Term::ANSIColor.send(color) if Symbol === color and Term::ANSIColor.respond_to?(color)
172
+
173
+ return str if Symbol === color
174
+ color_str = reset ? Term::ANSIColor.reset : ""
175
+ color_str << color
152
176
  if str.nil?
153
- color
177
+ color_str
154
178
  else
155
- color + str.to_s + self.color(0)
179
+ color_str + str.to_s + Term::ANSIColor.reset
156
180
  end
157
181
  end
158
182
 
@@ -195,15 +195,16 @@ module Log
195
195
  def done(io = STDERR)
196
196
  done_msg = Log.color(:magenta, "· ") << Log.color(:green, "done")
197
197
  if @start
198
- ellapsed = (Time.now - @start).to_i
198
+ ellapsed = (Time.now - @start)
199
199
  else
200
200
  ellapsed = 0
201
201
  end
202
- ellapsed = [ellapsed/3600, ellapsed/60 % 60, ellapsed % 60].map{|t| "%02i" % t }.join(':')
203
- done_msg << " " << Log.color(:blue, (@ticks).to_s) << " #{bytes ? 'bytes' : 'items'} in " << Log.color(:green, ellapsed)
202
+ ellapsed_str = [ellapsed/3600, ellapsed/60 % 60, ellapsed % 60].map{|t| "%02i" % t }.join(':')
203
+ done_msg << " " << Log.color(:blue, (@ticks).to_s) << " #{bytes ? 'bytes' : 'items'} in " << Log.color(:green, ellapsed_str)
204
204
  @last_count = 0
205
205
  @last_time = @start
206
- done_msg << " - " << thr_msg
206
+ thr = ellapsed > 0 ? (@ticks / ellapsed).to_i.to_s : 0
207
+ done_msg << " - " << Log.color(:blue, thr) << " per second"
207
208
  done_msg << Log.color(:magenta, " · " << desc)
208
209
  print(io, Log.up_lines(@depth) << done_msg << Log.down_lines(@depth))
209
210
 
@@ -24,8 +24,10 @@ module MetaExtension
24
24
  return if attrs.nil? || attrs.empty?
25
25
 
26
26
  if rest.length == 1 && Hash === (rlast = rest.last) &&
27
- ! (rlkey = rlast.keys.first).nil? &&
28
- attrs.include?(rlkey.to_sym)
27
+ ((! (rlkey = rlast.keys.first).nil? && attrs.include?(rlkey.to_sym)) ||
28
+ (! attrs.length != 1 ))
29
+
30
+
29
31
 
30
32
  pairs = rlast
31
33
  else
@@ -25,7 +25,14 @@ module Misc
25
25
  str
26
26
  end
27
27
 
28
- def self.format_paragraph(text, size = 80, indent = 0, offset = 0)
28
+
29
+ MAX_WIDTH = 100
30
+ def self.format_paragraph(text, size = nil, indent = nil, offset = nil)
31
+ size ||= Log.tty_size || MAX_WIDTH
32
+ size = MAX_WIDTH if size > MAX_WIDTH
33
+ indent ||= 0
34
+ offset ||= 0
35
+
29
36
  i = 0
30
37
  size = size + offset + indent
31
38
  re = /((?:\n\s*\n\s*)|(?:\n\s*(?=\*)))/
@@ -55,7 +62,10 @@ module Misc
55
62
  end*""
56
63
  end
57
64
 
58
- def self.format_definition_list_item(dt, dd, size = 80, indent = 20, color = :yellow)
65
+ def self.format_definition_list_item(dt, dd, indent = nil, size = nil, color = :yellow)
66
+ size ||= Log.tty_size || MAX_WIDTH
67
+ size = MAX_WIDTH if size > MAX_WIDTH
68
+ indent ||= size / 3
59
69
  dd = "" if dd.nil?
60
70
  dt = Log.color color, dt if color
61
71
  dt = dt.to_s unless dd.empty?
@@ -73,10 +83,12 @@ module Misc
73
83
  text
74
84
  end
75
85
 
76
- def self.format_definition_list(defs, size = 80, indent = 20, color = :yellow, sep = "\n\n")
86
+ def self.format_definition_list(defs, indent = nil, size = nil, color = :yellow, sep = "\n\n")
87
+ size ||= Log.tty_size || MAX_WIDTH
88
+ indent ||= 30
77
89
  entries = []
78
90
  defs.each do |dt,dd|
79
- text = format_definition_list_item(dt,dd,size,indent,color)
91
+ text = format_definition_list_item(dt,dd,indent, size,color)
80
92
  entries << text
81
93
  end
82
94
  entries * sep
@@ -0,0 +1,41 @@
1
+ module Misc
2
+ def self.benchmark(repeats = 1, message = nil)
3
+ require 'benchmark'
4
+ res = nil
5
+ begin
6
+ measure = Benchmark.measure do
7
+ repeats.times do
8
+ res = yield
9
+ end
10
+ end
11
+ if message
12
+ puts "#{message }: #{ repeats } repeats"
13
+ else
14
+ puts "Benchmark for #{ repeats } repeats"
15
+ end
16
+ puts measure
17
+ rescue Exception
18
+ puts "Benchmark aborted"
19
+ raise $!
20
+ end
21
+ res
22
+ end
23
+
24
+ def self.profile(options = {})
25
+ require 'ruby-prof'
26
+ profiler = RubyProf::Profile.new
27
+ profiler.start
28
+ begin
29
+ res = yield
30
+ rescue Exception
31
+ puts "Profiling aborted"
32
+ raise $!
33
+ ensure
34
+ result = profiler.stop
35
+ printer = RubyProf::FlatPrinter.new(result)
36
+ printer.print(STDOUT, options)
37
+ end
38
+
39
+ res
40
+ end
41
+ end
data/lib/scout/misc.rb CHANGED
@@ -2,6 +2,7 @@ require_relative 'misc/format'
2
2
  require_relative 'misc/insist'
3
3
  require_relative 'misc/digest'
4
4
  require_relative 'misc/filesystem'
5
+ require_relative 'misc/monitor'
5
6
 
6
7
  module Misc
7
8
  end
@@ -68,6 +68,7 @@ module Open
68
68
  block.call if block_given?
69
69
 
70
70
  Log.high "Done consuming stream #{Log.fingerprint io} into #{into_path || into}"
71
+ c
71
72
  rescue Aborted
72
73
  Log.high "Consume stream Aborted #{Log.fingerprint io} into #{into_path || into}"
73
74
  io.abort $! if io.respond_to? :abort
@@ -369,4 +370,34 @@ module Open
369
370
  def self.tee_stream(stream)
370
371
  tee_stream_thread(stream)
371
372
  end
373
+
374
+ def self.read_stream(stream, size)
375
+ str = nil
376
+ Thread.pass while IO.select([stream],nil,nil,1).nil?
377
+ while not str = stream.read(size)
378
+ IO.select([stream],nil,nil,1)
379
+ Thread.pass
380
+ raise ClosedStream if stream.eof?
381
+ end
382
+
383
+ while str.length < size
384
+ raise ClosedStream if stream.eof?
385
+ IO.select([stream],nil,nil,1)
386
+ if new = stream.read(size-str.length)
387
+ str << new
388
+ end
389
+ end
390
+ str
391
+ end
392
+
393
+ def self.read_stream(stream, size)
394
+ str = ""
395
+ while str.length < size
396
+ missing = size - str.length
397
+ more = stream.read(missing)
398
+ str << more
399
+ end
400
+ str
401
+ end
402
+
372
403
  end
@@ -123,7 +123,7 @@ module Path
123
123
 
124
124
  def follow(map_name = :default, annotate = true)
125
125
  IndiferentHash.setup(path_maps)
126
- map = path_maps[map_name]
126
+ map = path_maps[map_name] || Path.path_maps[map_name]
127
127
  raise "Map not found #{Log.fingerprint map_name} not in #{Log.fingerprint path_maps.keys}" if map.nil?
128
128
  while Symbol === map
129
129
  map_name = map
@@ -163,4 +163,5 @@ module Path
163
163
  .collect{|where| find(where) }
164
164
  .select{|file| file.exist? }.uniq
165
165
  end
166
+
166
167
  end
data/lib/scout/path.rb CHANGED
@@ -24,7 +24,7 @@ module Path
24
24
  end
25
25
 
26
26
  def path_maps
27
- @path_maps ||= Path.path_maps
27
+ @path_maps ||= Path.path_maps.dup
28
28
  end
29
29
 
30
30
  def join(subpath, prevpath = nil)
@@ -3,6 +3,7 @@ require_relative 'open'
3
3
 
4
4
  module Persist
5
5
  TRUE_STRINGS = Set.new ["true", "True", "TRUE", "t", "T", "1", "yes", "Yes", "YES", "y", "Y", "ON", "on"] unless defined? TRUE_STRINGS
6
+ SERIALIZER = :json
6
7
 
7
8
  class << self
8
9
  attr_accessor :save_drivers, :load_drivers
@@ -15,6 +16,8 @@ module Persist
15
16
  end
16
17
 
17
18
  def self.serialize(content, type)
19
+ type = type.to_sym if String === type
20
+ type = SERIALIZER if type == :serializer
18
21
  case type
19
22
  when nil, :string, :integer, :float, :boolean, :file, :path
20
23
  if IO === content || StringIO === content
@@ -28,7 +31,7 @@ module Persist
28
31
  content.to_yaml
29
32
  when :json
30
33
  content.to_json
31
- when :marshal, :serializer
34
+ when :marshal
32
35
  Marshal.dump(content)
33
36
  else
34
37
  if m = type.to_s.match(/(.*)_array/)
@@ -41,6 +44,8 @@ module Persist
41
44
  end
42
45
 
43
46
  def self.deserialize(serialized, type)
47
+ type = type.to_sym if String === type
48
+ type = SERIALIZER if type == :serializer
44
49
  case type
45
50
  when nil, :string, :file, :stream
46
51
  serialized
@@ -58,7 +63,7 @@ module Persist
58
63
  YAML.parse(serialized)
59
64
  when :json
60
65
  JSON.parse(serialized)
61
- when :marshal, :serializer
66
+ when :marshal
62
67
  Marshal.load(serialized)
63
68
  else
64
69
  if m = type.to_s.match(/(.*)_array/)
@@ -73,6 +78,10 @@ module Persist
73
78
 
74
79
  MEMORY = {}
75
80
  def self.save(content, file, type = :serializer)
81
+ type = :serializer if type.nil?
82
+ type = type.to_sym if String === type
83
+ type = SERIALIZER if type == :serializer
84
+ type = MEMORY if type == :memory
76
85
  return if content.nil?
77
86
  type = MEMORY if type == :memory
78
87
  type = :serializer if type.nil?
@@ -97,16 +106,18 @@ module Persist
97
106
  ConcurrentStream.setup copy, :threads => t, :filename => file, :autojoin => true
98
107
  else
99
108
  serialized = serialize(content, type)
100
- Open.sensible_write(file, serialized)
109
+ Open.sensible_write(file, serialized, :force => true)
101
110
  content
102
111
  end
103
112
  end
104
113
 
105
114
  def self.load(file, type = :serializer)
106
115
  file = file.find if Path === file
116
+ type = :serializer if type.nil?
117
+ type = type.to_sym if String === type
118
+ type = SERIALIZER if type == :serializer
107
119
  type = MEMORY if type == :memory
108
120
  return unless Hash === type || Open.exist?(file)
109
- type = :serializer if type.nil?
110
121
 
111
122
  Log.debug "Load #{Log.fingerprint type} on #{file}"
112
123
  if load_drivers[type]
@@ -1,4 +1,9 @@
1
1
  module Path
2
+ def relocate
3
+ return self if Open.exists?(self)
4
+ Resource.relocate(self)
5
+ end
6
+
2
7
  def open(*args, &block)
3
8
  produce
4
9
  Open.open(self, *args, &block)
@@ -0,0 +1,48 @@
1
+ module Resource
2
+ def identify(path)
3
+ return path unless path.start_with?("/")
4
+ path_maps = path.path_maps || self.path_maps || Path.path_maps
5
+ path = File.expand_path(path)
6
+ path += "/" if File.directory?(path)
7
+
8
+ map_order ||= (path_maps.keys & Path.basic_map_order) + (path_maps.keys - Path.basic_map_order)
9
+ map_order -= [:current, "current"]
10
+ map_order << :current
11
+
12
+ choices = []
13
+ map_order.uniq.each do |name|
14
+ pattern = path_maps[name]
15
+ pattern = path_maps[pattern] while Symbol === pattern
16
+ next if pattern.nil?
17
+
18
+ pattern = pattern.sub('{PWD}', Dir.pwd)
19
+ if String === pattern and pattern.include?('{')
20
+ regexp = "^" + pattern
21
+ .gsub(/{(TOPLEVEL)}/,'(?<\1>[^/]+)')
22
+ .gsub(/{([^}]+)}/,'(?<\1>[^/]+)?') +
23
+ "(?:/(?<REST>.*))?/?$"
24
+ if m = path.match(regexp)
25
+ if ! m.named_captures.include?("PKGDIR") || m["PKGDIR"] == self.pkgdir
26
+ unlocated = %w(TOPLEVEL SUBPATH PATH REST).collect{|c|
27
+ m.named_captures.include?(c) ? m[c] : nil
28
+ }.compact * "/"
29
+ unlocated.gsub!(/\/+/,'/')
30
+ unlocated[self.subdir] = "" if self.subdir
31
+ choices << self.annotate(unlocated)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ Path.setup(choices.sort_by{|s| s.length }.first, self, nil, path_maps)
38
+ end
39
+
40
+ def self.relocate(path)
41
+ return path if Open.exists?(path)
42
+ resource = path.pkgdir if Path === path
43
+ resource = Scout unless Resource === resource
44
+ unlocated = resource.identify path
45
+ unlocated.find
46
+ end
47
+ end
48
+
@@ -1,6 +1,8 @@
1
1
  require_relative 'log'
2
2
  require_relative 'path'
3
3
  require_relative 'resource/produce'
4
+ require_relative 'resource/path'
5
+ require_relative 'resource/util'
4
6
 
5
7
  module Resource
6
8
  extend MetaExtension