scout-gear 5.1.1 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
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