bahuvrihi-tap 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +69 -0
- data/MIT-LICENSE +21 -0
- data/README +119 -0
- data/bin/tap +114 -0
- data/cmd/console.rb +42 -0
- data/cmd/destroy.rb +16 -0
- data/cmd/generate.rb +16 -0
- data/cmd/run.rb +126 -0
- data/doc/Class Reference +362 -0
- data/doc/Command Reference +153 -0
- data/doc/Tutorial +237 -0
- data/lib/tap.rb +32 -0
- data/lib/tap/app.rb +720 -0
- data/lib/tap/constants.rb +8 -0
- data/lib/tap/env.rb +640 -0
- data/lib/tap/file_task.rb +547 -0
- data/lib/tap/generator/base.rb +109 -0
- data/lib/tap/generator/destroy.rb +37 -0
- data/lib/tap/generator/generate.rb +61 -0
- data/lib/tap/generator/generators/command/command_generator.rb +21 -0
- data/lib/tap/generator/generators/command/templates/command.erb +32 -0
- data/lib/tap/generator/generators/config/config_generator.rb +26 -0
- data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
- data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +27 -0
- data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
- data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
- data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
- data/lib/tap/generator/generators/root/root_generator.rb +55 -0
- data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
- data/lib/tap/generator/generators/root/templates/gemspec +27 -0
- data/lib/tap/generator/generators/root/templates/tapfile +8 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
- data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
- data/lib/tap/generator/generators/task/task_generator.rb +27 -0
- data/lib/tap/generator/generators/task/templates/task.erb +14 -0
- data/lib/tap/generator/generators/task/templates/test.erb +21 -0
- data/lib/tap/generator/manifest.rb +14 -0
- data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
- data/lib/tap/patches/rake/testtask.rb +55 -0
- data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
- data/lib/tap/patches/ruby19/parsedate.rb +16 -0
- data/lib/tap/root.rb +581 -0
- data/lib/tap/support/aggregator.rb +55 -0
- data/lib/tap/support/assignments.rb +172 -0
- data/lib/tap/support/audit.rb +418 -0
- data/lib/tap/support/batchable.rb +47 -0
- data/lib/tap/support/batchable_class.rb +107 -0
- data/lib/tap/support/class_configuration.rb +194 -0
- data/lib/tap/support/command_line.rb +98 -0
- data/lib/tap/support/comment.rb +270 -0
- data/lib/tap/support/configurable.rb +114 -0
- data/lib/tap/support/configurable_class.rb +296 -0
- data/lib/tap/support/configuration.rb +122 -0
- data/lib/tap/support/constant.rb +70 -0
- data/lib/tap/support/constant_utils.rb +127 -0
- data/lib/tap/support/declarations.rb +111 -0
- data/lib/tap/support/executable.rb +111 -0
- data/lib/tap/support/executable_queue.rb +82 -0
- data/lib/tap/support/framework.rb +71 -0
- data/lib/tap/support/framework_class.rb +199 -0
- data/lib/tap/support/instance_configuration.rb +147 -0
- data/lib/tap/support/lazydoc.rb +428 -0
- data/lib/tap/support/manifest.rb +89 -0
- data/lib/tap/support/run_error.rb +39 -0
- data/lib/tap/support/shell_utils.rb +71 -0
- data/lib/tap/support/summary.rb +30 -0
- data/lib/tap/support/tdoc.rb +404 -0
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
- data/lib/tap/support/templater.rb +180 -0
- data/lib/tap/support/validation.rb +410 -0
- data/lib/tap/support/versions.rb +97 -0
- data/lib/tap/task.rb +259 -0
- data/lib/tap/tasks/dump.rb +56 -0
- data/lib/tap/tasks/rake.rb +93 -0
- data/lib/tap/test.rb +37 -0
- data/lib/tap/test/env_vars.rb +29 -0
- data/lib/tap/test/file_methods.rb +377 -0
- data/lib/tap/test/script_methods.rb +144 -0
- data/lib/tap/test/subset_methods.rb +420 -0
- data/lib/tap/test/tap_methods.rb +237 -0
- data/lib/tap/workflow.rb +187 -0
- metadata +145 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
require 'tap/constants'
|
7
|
+
require 'tap/patches/rake/testtask.rb'
|
8
|
+
|
9
|
+
#
|
10
|
+
# Gem specification
|
11
|
+
#
|
12
|
+
|
13
|
+
def gemspec
|
14
|
+
data = File.read('<%= project_name %>.gemspec')
|
15
|
+
spec = nil
|
16
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
17
|
+
spec
|
18
|
+
end
|
19
|
+
|
20
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
21
|
+
pkg.need_tar = true
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Prints the gemspec manifest.'
|
25
|
+
task :print_manifest do
|
26
|
+
# collect files from the gemspec, labeling
|
27
|
+
# with true or false corresponding to the
|
28
|
+
# file existing or not
|
29
|
+
files = gemspec.files.inject({}) do |files, file|
|
30
|
+
files[File.expand_path(file)] = [File.exists?(file), file]
|
31
|
+
files
|
32
|
+
end
|
33
|
+
|
34
|
+
# gather non-rdoc/pkg files for the project
|
35
|
+
# and add to the files list if they are not
|
36
|
+
# included already (marking by the absence
|
37
|
+
# of a label)
|
38
|
+
Dir.glob("**/*").each do |file|
|
39
|
+
next if file =~ /^(rdoc|pkg)/ || File.directory?(file)
|
40
|
+
|
41
|
+
path = File.expand_path(file)
|
42
|
+
files[path] = ["", file] unless files.has_key?(path)
|
43
|
+
end
|
44
|
+
|
45
|
+
# sort and output the results
|
46
|
+
files.values.sort_by {|exists, file| file }.each do |entry|
|
47
|
+
puts "%-5s : %s" % entry
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Documentation tasks
|
53
|
+
#
|
54
|
+
|
55
|
+
desc 'Generate documentation.'
|
56
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
57
|
+
spec = gemspec
|
58
|
+
|
59
|
+
rdoc.rdoc_dir = 'rdoc'
|
60
|
+
rdoc.title = '<%= project_name %>'
|
61
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
62
|
+
rdoc.rdoc_files.include( spec.extra_rdoc_files )
|
63
|
+
rdoc.rdoc_files.include( spec.files.select {|file| file =~ /^lib.*\.rb$/} )
|
64
|
+
|
65
|
+
# Using Tdoc to template your Rdoc will result in configurations being
|
66
|
+
# listed with documentation in a subsection following attributes. Not
|
67
|
+
# necessary, but nice.
|
68
|
+
require 'tap/support/tdoc'
|
69
|
+
rdoc.template = 'tap/support/tdoc/tdoc_html_template'
|
70
|
+
rdoc.options << '--fmt' << 'tdoc'
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Test tasks
|
75
|
+
#
|
76
|
+
|
77
|
+
desc 'Default: Run tests.'
|
78
|
+
task :default => :test
|
79
|
+
|
80
|
+
desc 'Run tests.'
|
81
|
+
Rake::TestTask.new(:test) do |t|
|
82
|
+
t.test_files = Dir.glob( File.join('test', ENV['pattern'] || '**/*_test.rb') )
|
83
|
+
t.verbose = true
|
84
|
+
t.warning = true
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "<%= project_name %>"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
#s.author = "Your Name Here"
|
5
|
+
#s.email = "your.email@pubfactory.edu"
|
6
|
+
#s.homepage = "http://rubyforge.org/projects/<%= project_name %>/"
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = "<%= project_name %> task library"
|
9
|
+
s.require_path = "lib"
|
10
|
+
s.test_file = "test/tap_test_suite.rb"
|
11
|
+
#s.rubyforge_project = "<%= project_name %>"
|
12
|
+
#s.has_rdoc = true
|
13
|
+
s.add_dependency("tap", "~> <%= Tap::VERSION %>")
|
14
|
+
|
15
|
+
# list extra rdoc files like README here.
|
16
|
+
s.extra_rdoc_files = %W{
|
17
|
+
}
|
18
|
+
|
19
|
+
# list the files you want to include here. you can
|
20
|
+
# check this manifest using 'rake :print_manifest'
|
21
|
+
s.files = %W{
|
22
|
+
tapfile.rb
|
23
|
+
test/tap_test_helper.rb
|
24
|
+
test/tap_test_suite.rb
|
25
|
+
test/tapfile_test.rb
|
26
|
+
}
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/tap_test_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/../tapfile.rb'
|
3
|
+
|
4
|
+
class TapfileTest < Test::Unit::TestCase
|
5
|
+
acts_as_tap_test
|
6
|
+
|
7
|
+
def test_goodnight
|
8
|
+
task = Goodnight.new :message => "goodnight"
|
9
|
+
|
10
|
+
# a simple test
|
11
|
+
assert_equal({:message => 'goodnight'}, task.config)
|
12
|
+
assert_equal "goodnight moon", task.process("moon")
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Tap::Generator::Generators
|
2
|
+
|
3
|
+
# :startdoc::generator a task and test
|
4
|
+
#
|
5
|
+
# Generates a new Tap::Task and an associated test file.
|
6
|
+
class TaskGenerator < Tap::Generator::Base
|
7
|
+
|
8
|
+
config :test, true, &c.switch # Generates the task without test files.
|
9
|
+
|
10
|
+
def manifest(m, const_name)
|
11
|
+
const = Constant.new(const_name.camelize)
|
12
|
+
|
13
|
+
task_path = app.filepath('lib', "#{const.path}.rb")
|
14
|
+
m.directory File.dirname(task_path)
|
15
|
+
m.template task_path, "task.erb", :const => const
|
16
|
+
|
17
|
+
if test
|
18
|
+
test_path = app.filepath('test', "#{const.path}_test.rb")
|
19
|
+
m.directory File.dirname(test_path)
|
20
|
+
m.template test_path, "test.erb", :const => const
|
21
|
+
end
|
22
|
+
|
23
|
+
const
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<% redirect do |target| %># <%= const.name %>::manifest <replace with manifest summary>
|
2
|
+
# <replace with command line description>
|
3
|
+
|
4
|
+
# <%= const.const_name %> Documentation
|
5
|
+
class <%= const.const_name %> < Tap::Task
|
6
|
+
|
7
|
+
# <config file documentation>
|
8
|
+
config :message, 'goodnight' # a sample config
|
9
|
+
|
10
|
+
def process(name)
|
11
|
+
log message, name
|
12
|
+
"#{message} #{name}"
|
13
|
+
end
|
14
|
+
end <% module_nest(const.nesting, ' ') { target } end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '<%= '../' * const.nesting_depth %>tap_test_helper.rb')
|
2
|
+
require '<%= const.path %>'
|
3
|
+
|
4
|
+
class <%= const.name %>Test < Test::Unit::TestCase
|
5
|
+
acts_as_tap_test
|
6
|
+
|
7
|
+
def test_<%= const.basename %>
|
8
|
+
task = <%= const.name %>.new :message => "goodnight"
|
9
|
+
|
10
|
+
# a simple test
|
11
|
+
assert_equal({:message => 'goodnight'}, task.config)
|
12
|
+
assert_equal "goodnight moon", task.process("moon")
|
13
|
+
|
14
|
+
# a more complex test
|
15
|
+
task.enq("moon")
|
16
|
+
app.run
|
17
|
+
|
18
|
+
assert_equal ["goodnight moon"], app.results(task)
|
19
|
+
assert_audit_equal ExpAudit[[nil, "moon"], [task, "goodnight moon"]], app._results(task)[0]
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# this is the same code as in rake/rake_test_loader.rb
|
2
|
+
# except it duplicates ARGV before iterating over it.
|
3
|
+
#
|
4
|
+
# This prevents an error in Ruby 1.9 when one of the
|
5
|
+
# loaded files attempts to modify ARGV. In that case
|
6
|
+
# you get an error like: 'can't modify array during
|
7
|
+
# iteration (RuntimeError)'
|
8
|
+
ARGV.dup.each { |f| load f unless f =~ /^-/ }
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# NO idea why this prevents an error with @ruby_opts=nil,
|
2
|
+
# or even how @ruby_opts could be nil, on ruby 1.9 with
|
3
|
+
# rake test and tdoc. It does, though.
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
module Rake # :nodoc:
|
7
|
+
|
8
|
+
class TestTask < TaskLib # :nodoc:
|
9
|
+
|
10
|
+
# Patch for TestTask#define in 'rake\testtask.rb'
|
11
|
+
#
|
12
|
+
# This patch lets you specify Windows-style paths in lib
|
13
|
+
# (ie with spaces and slashes) and to do something like:
|
14
|
+
#
|
15
|
+
# Rake::TestTask.new(:test) do |t|
|
16
|
+
# t.libs = $: << 'lib'
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Using this patch you can specify additional load paths
|
20
|
+
# for the test from the command line using --lib-dir
|
21
|
+
#
|
22
|
+
def define
|
23
|
+
lib_opt = @libs.collect {|f| "-I\"#{File.expand_path(f)}\""}.join(' ')
|
24
|
+
desc "Run tests" + (@name==:test ? "" : " for #{@name}")
|
25
|
+
task @name do
|
26
|
+
run_code = ''
|
27
|
+
RakeFileUtils.verbose(@verbose) do
|
28
|
+
run_code =
|
29
|
+
case @loader
|
30
|
+
when :direct
|
31
|
+
"-e 'ARGV.each{|f| load f}'"
|
32
|
+
when :testrb
|
33
|
+
"-S testrb #{fix}"
|
34
|
+
when :rake
|
35
|
+
rake_loader
|
36
|
+
end
|
37
|
+
@ruby_opts.unshift(lib_opt)
|
38
|
+
@ruby_opts.unshift( "-w" ) if @warning
|
39
|
+
ruby @ruby_opts.join(" ") +
|
40
|
+
" \"#{run_code}\" " +
|
41
|
+
file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
|
42
|
+
" #{option_list}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Loads in the patched rake_test_loader to avoid the ARGV
|
49
|
+
# modification error, which arises within TDoc.
|
50
|
+
def rake_loader # :nodoc:
|
51
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'rake_test_loader.rb'))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Test
|
2
|
+
module Unit
|
3
|
+
module Util # :nodoc:
|
4
|
+
module BacktraceFilter # :nodoc:
|
5
|
+
|
6
|
+
if method_defined?(:filter_backtrace)
|
7
|
+
alias :tap_original_filter_backtrace :filter_backtrace
|
8
|
+
end
|
9
|
+
|
10
|
+
# This is a slightly-modified version of the default BacktraceFilter
|
11
|
+
# provided in the Ruby 1.9 distribution. It solves the issue documented
|
12
|
+
# below, and hopefully will not be necessary when Ruby 1.9 is stable.
|
13
|
+
#
|
14
|
+
def filter_backtrace(backtrace, prefix=nil)
|
15
|
+
return ["No backtrace"] unless(backtrace)
|
16
|
+
split_p = if(prefix)
|
17
|
+
prefix.split(TESTUNIT_FILE_SEPARATORS)
|
18
|
+
else
|
19
|
+
TESTUNIT_PREFIX
|
20
|
+
end
|
21
|
+
match = proc do |e|
|
22
|
+
split_e = e.split(TESTUNIT_FILE_SEPARATORS)[0, split_p.size]
|
23
|
+
next false unless(split_e[0..-2] == split_p[0..-2])
|
24
|
+
split_e[-1].sub(TESTUNIT_RB_FILE, '') == split_p[-1]
|
25
|
+
end
|
26
|
+
|
27
|
+
# The Ruby 1.9 issue is that sometimes backtrace is a String
|
28
|
+
# and String is no longer Enumerable (hence it doesn't respond
|
29
|
+
# respond to detect). Arrayify to solve.
|
30
|
+
backtrace = [backtrace] unless backtrace.kind_of?(Array)
|
31
|
+
|
32
|
+
return backtrace unless(backtrace.detect(&match))
|
33
|
+
found_prefix = false
|
34
|
+
new_backtrace = backtrace.reverse.reject do |e|
|
35
|
+
if(match[e])
|
36
|
+
found_prefix = true
|
37
|
+
true
|
38
|
+
elsif(found_prefix)
|
39
|
+
false
|
40
|
+
else
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end.reverse
|
44
|
+
new_backtrace = (new_backtrace.empty? ? backtrace : new_backtrace)
|
45
|
+
new_backtrace = new_backtrace.reject(&match)
|
46
|
+
new_backtrace.empty? ? backtrace : new_backtrace
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'date/format'
|
2
|
+
|
3
|
+
# Taken directly from the Ruby 1.8.6 standard library. Ruby 1.9 has apparently
|
4
|
+
# eliminated this class, but it is currently (2008-02-08) required by ActiveSupport.
|
5
|
+
#
|
6
|
+
# Making this file available to ActiveSupport allows testing under Ruby 1.9,
|
7
|
+
# but this patch should be unnecessary when ActiveSupport upgrades and becomes
|
8
|
+
# compatible with Ruby 1.9
|
9
|
+
module ParseDate # :nodoc:
|
10
|
+
def parsedate(str, comp=false)
|
11
|
+
Date._parse(str, comp).
|
12
|
+
values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday)
|
13
|
+
end
|
14
|
+
|
15
|
+
module_function :parsedate
|
16
|
+
end
|
data/lib/tap/root.rb
ADDED
@@ -0,0 +1,581 @@
|
|
1
|
+
require 'tap/support/versions'
|
2
|
+
require 'tap/support/configurable'
|
3
|
+
autoload(:FileUtils, 'fileutils')
|
4
|
+
|
5
|
+
module Tap
|
6
|
+
|
7
|
+
# Root allows you to define a root directory and alias subdirectories, so that
|
8
|
+
# you can conceptualize what filepaths you need without predefining the full
|
9
|
+
# filepaths. Root also simplifies operations on filepaths.
|
10
|
+
#
|
11
|
+
# # define a root directory with aliased subdirectories
|
12
|
+
# r = Root.new '/root_dir', :input => 'in', :output => 'out'
|
13
|
+
#
|
14
|
+
# # work with directories
|
15
|
+
# r[:input] # => '/root_dir/in'
|
16
|
+
# r[:output] # => '/root_dir/out'
|
17
|
+
# r['implicit'] # => '/root_dir/implicit'
|
18
|
+
#
|
19
|
+
# # expanded paths are returned unchanged
|
20
|
+
# r[File.expand_path('expanded')] # => File.expand_path('expanded')
|
21
|
+
#
|
22
|
+
# # work with filepaths
|
23
|
+
# fp = r.filepath(:input, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
|
24
|
+
# r.relative_filepath(:input, fp) # => 'path/to/file.txt'
|
25
|
+
# r.translate(fp, :input, :output) # => '/root_dir/out/path/to/file.txt'
|
26
|
+
#
|
27
|
+
# # version filepaths
|
28
|
+
# r.version('path/to/config.yml', 1.0) # => 'path/to/config-1.0.yml'
|
29
|
+
# r.increment('path/to/config-1.0.yml', 0.1) # => 'path/to/config-1.1.yml'
|
30
|
+
# r.deversion('path/to/config-1.1.yml') # => ['path/to/config.yml', "1.1"]
|
31
|
+
#
|
32
|
+
# # absolute paths can also be aliased
|
33
|
+
# r[:abs, true] = "/absolute/path"
|
34
|
+
# r.filepath(:abs, "to", "file.txt") # => '/absolute/path/to/file.txt'
|
35
|
+
#
|
36
|
+
# By default, Roots are initialized to the present working directory (Dir.pwd).
|
37
|
+
# As in the 'implicit' example, Root infers a path relative to the root directory
|
38
|
+
# whenever it needs to resolve an alias that is not explicitly set. The only
|
39
|
+
# exceptions to this are fully expanded paths. These are returned unchanged.
|
40
|
+
#
|
41
|
+
# === Implementation Notes
|
42
|
+
#
|
43
|
+
# Internally Root stores expanded paths all aliased paths in the 'paths' hash.
|
44
|
+
# Expanding paths ensures they remain constant even when the present working
|
45
|
+
# directory (Dir.pwd) changes.
|
46
|
+
#
|
47
|
+
# Root keeps a separate 'directories' hash mapping aliases to their subdirectory paths.
|
48
|
+
# This hash allow reassignment if and when the root directory changes. By contrast,
|
49
|
+
# there is no separate data structure storing the absolute paths. An absolute path
|
50
|
+
# thus has an alias in 'paths' but not 'directories', whereas subdirectory paths
|
51
|
+
# have aliases in both.
|
52
|
+
#
|
53
|
+
# These features may be important to note when subclassing Root:
|
54
|
+
# - root and all filepaths in 'paths' are expanded
|
55
|
+
# - subdirectory paths are stored in 'directories'
|
56
|
+
# - absolute paths are present in 'paths' but not in 'directories'
|
57
|
+
#
|
58
|
+
class Root
|
59
|
+
# Regexp to match a windows-style root filepath.
|
60
|
+
WIN_ROOT_PATTERN = /^[A-z]:\//
|
61
|
+
|
62
|
+
class << self
|
63
|
+
include Support::Versions
|
64
|
+
|
65
|
+
# Returns the filepath of path relative to dir. Both dir and path are
|
66
|
+
# expanded before the relative filepath is determined. Returns nil if
|
67
|
+
# the path is not relative to dir.
|
68
|
+
#
|
69
|
+
# Root.relative_filepath('dir', "dir/path/to/file.txt") # => "path/to/file.txt"
|
70
|
+
#
|
71
|
+
def relative_filepath(dir, path, dir_string=Dir.pwd)
|
72
|
+
expanded_dir = File.expand_path(dir, dir_string)
|
73
|
+
expanded_path = File.expand_path(path, dir_string)
|
74
|
+
|
75
|
+
return nil unless expanded_path.index(expanded_dir) == 0
|
76
|
+
|
77
|
+
# use dir.length + 1 to remove a leading '/'. If dir.length + 1 >= expanded.length
|
78
|
+
# as in: relative_filepath('/path', '/path') then the first arg returns nil, and an
|
79
|
+
# empty string is returned
|
80
|
+
expanded_path[( expanded_dir.chomp("/").length + 1)..-1] || ""
|
81
|
+
end
|
82
|
+
|
83
|
+
# Lists all unique paths matching the input glob patterns.
|
84
|
+
def glob(*patterns)
|
85
|
+
patterns.collect do |pattern|
|
86
|
+
Dir.glob(pattern)
|
87
|
+
end.flatten.uniq
|
88
|
+
end
|
89
|
+
|
90
|
+
# Lists all unique versions of path matching the glob version patterns.
|
91
|
+
# If no patterns are specified, then all versions of path will be returned.
|
92
|
+
def vglob(path, *vpatterns)
|
93
|
+
vpatterns << "*" if vpatterns.empty?
|
94
|
+
vpatterns.collect do |vpattern|
|
95
|
+
results = Dir.glob(version(path, vpattern))
|
96
|
+
|
97
|
+
# extra work to include the default version path for any version
|
98
|
+
results << path if vpattern == "*" && File.exists?(path)
|
99
|
+
results
|
100
|
+
end.flatten.uniq
|
101
|
+
end
|
102
|
+
|
103
|
+
# Path suffix glob. Globs along the base paths for
|
104
|
+
# paths that match the specified suffix pattern.
|
105
|
+
def sglob(suffix_pattern, *base_paths)
|
106
|
+
base_paths.collect do |base|
|
107
|
+
base = File.expand_path(base)
|
108
|
+
Dir.glob(File.join(base, suffix_pattern))
|
109
|
+
end.flatten.uniq
|
110
|
+
end
|
111
|
+
|
112
|
+
# Executes the block in the specified directory. Makes the directory, if
|
113
|
+
# necessary when mkdir is specified. Otherwise, indir raises an error
|
114
|
+
# for non-existant directories, as well as non-directory inputs.
|
115
|
+
def indir(dir, mkdir=false)
|
116
|
+
unless File.directory?(dir)
|
117
|
+
if !File.exists?(dir) && mkdir
|
118
|
+
FileUtils.mkdir_p(dir)
|
119
|
+
else
|
120
|
+
raise "not a directory: #{dir}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
pwd = Dir.pwd
|
125
|
+
begin
|
126
|
+
Dir.chdir(dir)
|
127
|
+
yield
|
128
|
+
ensure
|
129
|
+
Dir.chdir(pwd)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# The path root type indicating windows, *nix, or some unknown
|
134
|
+
# style of filepaths (:win, :nix, :unknown).
|
135
|
+
def path_root_type
|
136
|
+
@path_root_type ||= case
|
137
|
+
when RUBY_PLATFORM =~ /mswin/ && File.expand_path(".") =~ WIN_ROOT_PATTERN then :win
|
138
|
+
when File.expand_path(".")[0] == ?/ then :nix
|
139
|
+
else :unknown
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns true if the input path appears to be an expanded path,
|
144
|
+
# based on Root.path_root_type.
|
145
|
+
#
|
146
|
+
# If root_type == :win returns true if the path matches
|
147
|
+
# WIN_ROOT_PATTERN.
|
148
|
+
#
|
149
|
+
# Root.expanded_path?('C:/path') # => true
|
150
|
+
# Root.expanded_path?('c:/path') # => true
|
151
|
+
# Root.expanded_path?('D:/path') # => true
|
152
|
+
# Root.expanded_path?('path') # => false
|
153
|
+
#
|
154
|
+
# If root_type == :nix, then expanded? returns true if
|
155
|
+
# the path begins with '/'.
|
156
|
+
#
|
157
|
+
# Root.expanded_path?('/path') # => true
|
158
|
+
# Root.expanded_path?('path') # => false
|
159
|
+
#
|
160
|
+
# Otherwise expanded_path? always returns nil.
|
161
|
+
def expanded_path?(path, root_type=path_root_type)
|
162
|
+
case root_type
|
163
|
+
when :win
|
164
|
+
path =~ WIN_ROOT_PATTERN ? true : false
|
165
|
+
when :nix
|
166
|
+
path[0] == ?/
|
167
|
+
else
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Minimizes a set of paths to the set of shortest basepaths that unqiuely
|
173
|
+
# identify the paths. The path extension and versions are removed from
|
174
|
+
# the basepath if possible. For example:
|
175
|
+
#
|
176
|
+
# Tap::Root.minimize ['path/to/a.rb', 'path/to/b.rb']
|
177
|
+
# # => ['a', 'b']
|
178
|
+
#
|
179
|
+
# Tap::Root.minimize ['path/to/a-0.1.0.rb', 'path/to/b-0.1.0.rb']
|
180
|
+
# # => ['a', 'b']
|
181
|
+
#
|
182
|
+
# Tap::Root.minimize ['path/to/file.rb', 'path/to/file.txt']
|
183
|
+
# # => ['file.rb', 'file.txt']
|
184
|
+
#
|
185
|
+
# Tap::Root.minimize ['path-0.1/to/file.rb', 'path-0.2/to/file.rb']
|
186
|
+
# # => ['path-0.1/to/file', 'path-0.2/to/file']
|
187
|
+
#
|
188
|
+
# Minimized paths that carry their extension will always carry
|
189
|
+
# their version as well, but the converse is not true; paths
|
190
|
+
# can be minimized to carry just the version and not the path
|
191
|
+
# extension.
|
192
|
+
#
|
193
|
+
# Tap::Root.minimize ['path/to/a-0.1.0.rb', 'path/to/a-0.1.0.txt']
|
194
|
+
# # => ['a-0.1.0.rb', 'a-0.1.0.txt']
|
195
|
+
#
|
196
|
+
# Tap::Root.minimize ['path/to/a-0.1.0.rb', 'path/to/a-0.2.0.rb']
|
197
|
+
# # => ['a-0.1.0', 'a-0.2.0']
|
198
|
+
#
|
199
|
+
# If a block is given, each (path, mini-path) pair will be passed
|
200
|
+
# to it after minimization.
|
201
|
+
def minimize(paths) # :yields: path, mini_path
|
202
|
+
unless block_given?
|
203
|
+
mini_paths = []
|
204
|
+
minimize(paths) {|p, mp| mini_paths << mp }
|
205
|
+
return mini_paths
|
206
|
+
end
|
207
|
+
|
208
|
+
splits = paths.uniq.collect do |path|
|
209
|
+
extname = File.extname(path)
|
210
|
+
extname = '' if extname =~ /^\.\d+$/
|
211
|
+
base = File.basename(path.chomp(extname))
|
212
|
+
version = base =~ /(-\d+(\.\d+)*)$/ ? $1 : ''
|
213
|
+
|
214
|
+
[dirname_or_array(path), base.chomp(version), extname, version, false, path]
|
215
|
+
end
|
216
|
+
|
217
|
+
while !splits.empty?
|
218
|
+
index = 0
|
219
|
+
splits = splits.collect do |(dir, base, extname, version, flagged, path)|
|
220
|
+
index += 1
|
221
|
+
case
|
222
|
+
when !flagged && just_one?(splits, index, base)
|
223
|
+
|
224
|
+
# found just one
|
225
|
+
yield(path, base)
|
226
|
+
nil
|
227
|
+
when dir.kind_of?(Array)
|
228
|
+
|
229
|
+
# no more path segments to use, try to add
|
230
|
+
# back version and extname
|
231
|
+
if dir.empty?
|
232
|
+
dir << File.dirname(base)
|
233
|
+
base = File.basename(base)
|
234
|
+
end
|
235
|
+
|
236
|
+
case
|
237
|
+
when !version.empty?
|
238
|
+
# add back version (occurs first)
|
239
|
+
[dir, "#{base}#{version}", extname, '', false, path]
|
240
|
+
|
241
|
+
when !extname.empty?
|
242
|
+
|
243
|
+
# add back extension (occurs second)
|
244
|
+
[dir, "#{base}#{extname}", '', version, false, path]
|
245
|
+
else
|
246
|
+
|
247
|
+
# nothing more to distinguish... path is minimized (occurs third)
|
248
|
+
yield(path, min_join(dir[0], base))
|
249
|
+
nil
|
250
|
+
end
|
251
|
+
else
|
252
|
+
|
253
|
+
# shift path segment. dirname_or_array returns an
|
254
|
+
# array if this is the last path segment to shift.
|
255
|
+
[dirname_or_array(dir), min_join(File.basename(dir), base), extname, version, false, path]
|
256
|
+
end
|
257
|
+
end.compact
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Returns true if the mini_path matches path. Matching logic
|
262
|
+
# reverses that of minimize:
|
263
|
+
# * a match occurs when path ends with mini_path
|
264
|
+
# * if mini_path doesn't specify an extension, then mini_path
|
265
|
+
# must only match path up to the path extension
|
266
|
+
# * if mini_path doesn't specify a version, then mini_path
|
267
|
+
# must only match path up to the path basename (minus the
|
268
|
+
# version and extname)
|
269
|
+
#
|
270
|
+
# For example:
|
271
|
+
#
|
272
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.rb', 'file') # => true
|
273
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.rb', 'dir/file') # => true
|
274
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.rb', 'file-0.1.0') # => true
|
275
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.rb', 'file-0.1.0.rb') # => true
|
276
|
+
#
|
277
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.rb', 'file.rb') # => false
|
278
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.rb', 'file-0.2.0') # => false
|
279
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.rb', 'another') # => false
|
280
|
+
#
|
281
|
+
# In matching, partial basenames are not allowed but partial directories
|
282
|
+
# are allowed. Hence:
|
283
|
+
#
|
284
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.txt', 'file') # => true
|
285
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.txt', 'ile') # => false
|
286
|
+
# Tap::Root.minimal_match?('dir/file-0.1.0.txt', 'r/file') # => true
|
287
|
+
#
|
288
|
+
def minimal_match?(path, mini_path)
|
289
|
+
extname = File.extname(mini_path)
|
290
|
+
extname = '' if extname =~ /^\.\d+$/
|
291
|
+
version = mini_path =~ /(-\d+(\.\d+)*)#{extname}$/ ? $1 : ''
|
292
|
+
|
293
|
+
match_path = case
|
294
|
+
when !extname.empty?
|
295
|
+
# force full match
|
296
|
+
path
|
297
|
+
when !version.empty?
|
298
|
+
# match up to version
|
299
|
+
path.chomp(File.extname(path))
|
300
|
+
else
|
301
|
+
# match up base
|
302
|
+
path.chomp(File.extname(path)).sub(/(-\d+(\.\d+)*)$/, '')
|
303
|
+
end
|
304
|
+
|
305
|
+
# key ends with pattern AND basenames of each are equal...
|
306
|
+
# the last check ensures that a full path segment has
|
307
|
+
# been specified
|
308
|
+
match_path[-mini_path.length, mini_path.length] == mini_path && File.basename(match_path) == File.basename(mini_path)
|
309
|
+
end
|
310
|
+
|
311
|
+
# Returns the path segments for the given path, splitting along the path
|
312
|
+
# divider. Root paths are always represented by a string, if only an
|
313
|
+
# empty string.
|
314
|
+
#
|
315
|
+
# os divider example
|
316
|
+
# windows '\' Root.split('C:\path\to\file') # => ["C:", "path", "to", "file"]
|
317
|
+
# *nix '/' Root.split('/path/to/file') # => ["", "path", "to", "file"]
|
318
|
+
#
|
319
|
+
# The path is always expanded relative to the expand_dir; so '.' and '..' are
|
320
|
+
# resolved. However, unless expand_path == true, only the segments relative
|
321
|
+
# to the expand_dir are returned.
|
322
|
+
#
|
323
|
+
# On windows (note that expanding paths allows the use of slashes or backslashes):
|
324
|
+
#
|
325
|
+
# Dir.pwd # => 'C:/'
|
326
|
+
# Root.split('path\to\..\.\to\file') # => ["C:", "path", "to", "file"]
|
327
|
+
# Root.split('path/to/.././to/file', false) # => ["path", "to", "file"]
|
328
|
+
#
|
329
|
+
# On *nix (or more generally systems with '/' roots):
|
330
|
+
#
|
331
|
+
# Dir.pwd # => '/'
|
332
|
+
# Root.split('path/to/.././to/file') # => ["", "path", "to", "file"]
|
333
|
+
# Root.split('path/to/.././to/file', false) # => ["path", "to", "file"]
|
334
|
+
#
|
335
|
+
def split(path, expand_path=true, expand_dir=Dir.pwd)
|
336
|
+
path = if expand_path
|
337
|
+
File.expand_path(path, expand_dir)
|
338
|
+
else
|
339
|
+
# normalize the path by expanding it, then
|
340
|
+
# work back to the relative filepath as needed
|
341
|
+
expanded_dir = File.expand_path(expand_dir)
|
342
|
+
expanded_path = File.expand_path(path, expand_dir)
|
343
|
+
expanded_path.index(expanded_dir) != 0 ? expanded_path : Tap::Root.relative_filepath(expanded_dir, expanded_path)
|
344
|
+
end
|
345
|
+
|
346
|
+
segments = path.scan(/[^\/]+/)
|
347
|
+
|
348
|
+
# add back the root filepath as needed on *nix
|
349
|
+
segments.unshift "" if path[0] == ?/
|
350
|
+
segments
|
351
|
+
end
|
352
|
+
|
353
|
+
private
|
354
|
+
|
355
|
+
# utility method for minimize -- joins the
|
356
|
+
# dir and path, preventing results like:
|
357
|
+
#
|
358
|
+
# "./path"
|
359
|
+
# "//path"
|
360
|
+
def min_join(dir, path) # :nodoc:
|
361
|
+
case dir
|
362
|
+
when "." then path
|
363
|
+
when "/" then "/#{path}"
|
364
|
+
else "#{dir}/#{path}"
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
# utility method for minimize -- returns the
|
369
|
+
# dirname of path, or an array if the dirname
|
370
|
+
# is effectively empty.
|
371
|
+
def dirname_or_array(path) # :nodoc:
|
372
|
+
dir = File.dirname(path)
|
373
|
+
case dir
|
374
|
+
when path, '.' then []
|
375
|
+
else dir
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
# utility method for minimize -- determines if there
|
380
|
+
# is just one of the base in splits, while flagging
|
381
|
+
# all matching entries.
|
382
|
+
def just_one?(splits, index, base) # :nodoc:
|
383
|
+
just_one = true
|
384
|
+
index.upto(splits.length-1) do |i|
|
385
|
+
if splits[i][1] == base
|
386
|
+
splits[i][4] = true
|
387
|
+
just_one = false
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
just_one
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|
395
|
+
|
396
|
+
include Support::Versions
|
397
|
+
include Support::Configurable
|
398
|
+
|
399
|
+
# The root directory.
|
400
|
+
config_attr(:root, '.', :writer => false)
|
401
|
+
|
402
|
+
# A hash of (alias, relative path) pairs for aliased subdirectories.
|
403
|
+
config_attr(:directories, {}, :writer => false)
|
404
|
+
|
405
|
+
# A hash of (alias, relative path) pairs for aliased absolute paths.
|
406
|
+
config_attr(:absolute_paths, {}, :reader => false, :writer => false)
|
407
|
+
|
408
|
+
# A hash of (alias, expanded path) pairs for aliased subdirectories and absolute paths.
|
409
|
+
attr_reader :paths
|
410
|
+
|
411
|
+
# The filesystem root, inferred from self.root
|
412
|
+
# (ex '/' on *nix or something like 'C:/' on Windows).
|
413
|
+
attr_reader :path_root
|
414
|
+
|
415
|
+
# Creates a new Root with the given root directory, aliased directories
|
416
|
+
# and absolute paths. By default root is the present working directory
|
417
|
+
# and no aliased directories or absolute paths are specified.
|
418
|
+
def initialize(root=Dir.pwd, directories={}, absolute_paths={})
|
419
|
+
assign_paths(root, directories, absolute_paths)
|
420
|
+
@config = self.class.configurations.instance_config(self)
|
421
|
+
end
|
422
|
+
|
423
|
+
# Sets the root directory. All paths are reassigned accordingly.
|
424
|
+
def root=(path)
|
425
|
+
assign_paths(path, directories, absolute_paths)
|
426
|
+
end
|
427
|
+
|
428
|
+
# Sets the directories to those provided. 'root' and :root are reserved
|
429
|
+
# and cannot be set using this method (use root= instead).
|
430
|
+
#
|
431
|
+
# r['alt'] # => File.join(r.root, 'alt')
|
432
|
+
# r.directories = {'alt' => 'dir'}
|
433
|
+
# r['alt'] # => File.join(r.root, 'dir')
|
434
|
+
def directories=(dirs)
|
435
|
+
assign_paths(root, dirs, absolute_paths)
|
436
|
+
end
|
437
|
+
|
438
|
+
# Sets the absolute paths to those provided. 'root' and :root are reserved
|
439
|
+
# directory keys and cannot be set using this method (use root= instead).
|
440
|
+
#
|
441
|
+
# r['abs'] # => File.join(r.root, 'abs')
|
442
|
+
# r.absolute_paths = {'abs' => '/path/to/dir'}
|
443
|
+
# r['abs'] # => '/path/to/dir'
|
444
|
+
def absolute_paths=(paths)
|
445
|
+
assign_paths(root, directories, paths)
|
446
|
+
end
|
447
|
+
|
448
|
+
# Returns the absolute paths registered with self.
|
449
|
+
def absolute_paths
|
450
|
+
abs_paths = {}
|
451
|
+
paths.each do |da, path|
|
452
|
+
abs_paths[da] = path unless directories.include?(da) || da.to_s == 'root'
|
453
|
+
end
|
454
|
+
abs_paths
|
455
|
+
end
|
456
|
+
|
457
|
+
# Sets an alias for the subdirectory relative to the root directory.
|
458
|
+
# The aliases 'root' and :root cannot be set with this method
|
459
|
+
# (use root= instead). Absolute filepaths can be set using the
|
460
|
+
# second syntax.
|
461
|
+
#
|
462
|
+
# r = Root.new '/root_dir'
|
463
|
+
# r[:dir] = 'path/to/dir'
|
464
|
+
# r[:dir] # => '/root_dir/path/to/dir'
|
465
|
+
#
|
466
|
+
# r[:abs, true] = '/abs/path/to/dir'
|
467
|
+
# r[:abs] # => '/abs/path/to/dir'
|
468
|
+
#
|
469
|
+
#--
|
470
|
+
# Implementation Notes:
|
471
|
+
# The syntax for setting an absolute filepath requires an odd use []=.
|
472
|
+
# In fact the method recieves the arguments (:dir, true, '/abs/path/to/dir')
|
473
|
+
# rather than (:dir, '/abs/path/to/dir', true), meaning that internally path
|
474
|
+
# and absolute are switched when setting an absolute filepath.
|
475
|
+
#++
|
476
|
+
def []=(dir, path, absolute=false)
|
477
|
+
raise ArgumentError, "The directory key '#{dir}' is reserved." if dir.to_s == 'root'
|
478
|
+
|
479
|
+
# switch the paths if absolute was provided
|
480
|
+
unless absolute == false
|
481
|
+
switch = path
|
482
|
+
path = absolute
|
483
|
+
absolute = switch
|
484
|
+
end
|
485
|
+
|
486
|
+
case
|
487
|
+
when path.nil?
|
488
|
+
@directories.delete(dir)
|
489
|
+
@paths.delete(dir)
|
490
|
+
when absolute
|
491
|
+
@directories.delete(dir)
|
492
|
+
@paths[dir] = File.expand_path(path)
|
493
|
+
else
|
494
|
+
@directories[dir] = path
|
495
|
+
@paths[dir] = File.expand_path(File.join(root, path))
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
# Returns the expanded path for the specified alias. If the alias
|
500
|
+
# has not been set, then the path is inferred to be 'root/dir' unless
|
501
|
+
# the path is relative to path_root. These paths are returned
|
502
|
+
# directly.
|
503
|
+
#
|
504
|
+
# r = Root.new '/root_dir', :dir => 'path/to/dir'
|
505
|
+
# r[:dir] # => '/root_dir/path/to/dir'
|
506
|
+
#
|
507
|
+
# r.path_root # => '/'
|
508
|
+
# r['relative/path'] # => '/root_dir/relative/path'
|
509
|
+
# r['/expanded/path'] # => '/expanded/path'
|
510
|
+
#
|
511
|
+
def [](dir)
|
512
|
+
path = self.paths[dir]
|
513
|
+
return path unless path == nil
|
514
|
+
|
515
|
+
dir = dir.to_s
|
516
|
+
Root.expanded_path?(dir) ? dir : File.expand_path(File.join(root, dir))
|
517
|
+
end
|
518
|
+
|
519
|
+
# Constructs expanded filepaths relative to the path of the specified alias.
|
520
|
+
def filepath(dir, *filename)
|
521
|
+
# TODO - consider filename.compact so nils will not raise errors
|
522
|
+
File.expand_path(File.join(self[dir], *filename))
|
523
|
+
end
|
524
|
+
|
525
|
+
# Retrieves the filepath relative to the path of the specified alias.
|
526
|
+
def relative_filepath(dir, filepath)
|
527
|
+
Root.relative_filepath(self[dir], filepath)
|
528
|
+
end
|
529
|
+
|
530
|
+
# Generates a target filepath translated from the aliased input dir to
|
531
|
+
# the aliased output dir. Raises an error if the filepath is not relative
|
532
|
+
# to the aliased input dir.
|
533
|
+
#
|
534
|
+
# fp = r.filepath(:in, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
|
535
|
+
# r.translate(fp, :in, :out) # => '/root_dir/out/path/to/file.txt'
|
536
|
+
def translate(filepath, input_dir, output_dir)
|
537
|
+
unless relative_path = relative_filepath(input_dir, filepath)
|
538
|
+
raise "\n#{filepath}\nis not relative to:\n#{input_dir}"
|
539
|
+
end
|
540
|
+
filepath(output_dir, relative_path)
|
541
|
+
end
|
542
|
+
|
543
|
+
# Lists all files in the aliased dir matching the input patterns. Patterns
|
544
|
+
# should be valid inputs for +Dir.glob+. If no patterns are specified, lists
|
545
|
+
# all files/folders matching '**/*'.
|
546
|
+
def glob(dir, *patterns)
|
547
|
+
patterns << "**/*" if patterns.empty?
|
548
|
+
patterns.collect! {|pattern| filepath(dir, pattern)}
|
549
|
+
Root.glob(*patterns)
|
550
|
+
end
|
551
|
+
|
552
|
+
# Lists all versions of filename in the aliased dir matching the version patterns.
|
553
|
+
# If no patterns are specified, then all versions of filename will be returned.
|
554
|
+
def vglob(dir, filename, *vpatterns)
|
555
|
+
Root.vglob(filepath(dir, filename), *vpatterns)
|
556
|
+
end
|
557
|
+
|
558
|
+
# Executes the provided block in the specified directory using Root.indir.
|
559
|
+
def indir(dir, mkdir=false)
|
560
|
+
Root.indir(self[dir], mkdir) { yield }
|
561
|
+
end
|
562
|
+
|
563
|
+
private
|
564
|
+
|
565
|
+
# reassigns all paths with the input root, directories, and absolute_paths
|
566
|
+
def assign_paths(root, directories, absolute_paths)
|
567
|
+
@root = File.expand_path(root)
|
568
|
+
@directories = {}
|
569
|
+
@paths = {'root' => @root, :root => @root}
|
570
|
+
|
571
|
+
@path_root = File.dirname(@root)
|
572
|
+
while @path_root != (parent = File.dirname(@path_root))
|
573
|
+
@path_root = parent
|
574
|
+
end
|
575
|
+
|
576
|
+
directories.each_pair {|dir, path| self[dir] = path }
|
577
|
+
absolute_paths.each_pair {|dir, path| self[dir, true] = path }
|
578
|
+
end
|
579
|
+
|
580
|
+
end
|
581
|
+
end
|