tap 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Basic Overview +151 -0
- data/Command Reference +99 -0
- data/History +24 -0
- data/MIT-LICENSE +1 -1
- data/README +29 -57
- data/Rakefile +30 -37
- data/Tutorial +243 -191
- data/bin/tap +66 -35
- data/lib/tap.rb +47 -29
- data/lib/tap/app.rb +700 -342
- data/lib/tap/{script → cmd}/console.rb +0 -0
- data/lib/tap/{script → cmd}/destroy.rb +0 -0
- data/lib/tap/{script → cmd}/generate.rb +0 -0
- data/lib/tap/cmd/run.rb +156 -0
- data/lib/tap/constants.rb +4 -0
- data/lib/tap/dump.rb +57 -0
- data/lib/tap/env.rb +316 -0
- data/lib/tap/file_task.rb +106 -109
- data/lib/tap/generator.rb +4 -1
- data/lib/tap/generator/generators/command/USAGE +6 -0
- data/lib/tap/generator/generators/command/command_generator.rb +17 -0
- data/lib/tap/generator/generators/{script/templates/script.erb → command/templates/command.erb} +10 -10
- data/lib/tap/generator/generators/config/USAGE +21 -0
- data/lib/tap/generator/generators/config/config_generator.rb +17 -7
- data/lib/tap/generator/generators/file_task/USAGE +3 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -0
- data/lib/tap/generator/generators/file_task/templates/file.txt +2 -0
- data/lib/tap/generator/generators/file_task/templates/file.yml +3 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +26 -20
- data/lib/tap/generator/generators/file_task/templates/test.erb +20 -10
- data/lib/tap/generator/generators/generator/generator_generator.rb +1 -1
- data/lib/tap/generator/generators/generator/templates/generator.erb +21 -12
- data/lib/tap/generator/generators/root/templates/Rakefile +33 -24
- data/lib/tap/generator/generators/root/templates/tap.yml +28 -31
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +1 -0
- data/lib/tap/generator/generators/task/USAGE +3 -0
- data/lib/tap/generator/generators/task/task_generator.rb +18 -5
- data/lib/tap/generator/generators/task/templates/task.erb +7 -12
- data/lib/tap/generator/generators/task/templates/test.erb +10 -11
- data/lib/tap/generator/generators/workflow/templates/task.erb +1 -1
- data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
- 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 +172 -67
- data/lib/tap/script.rb +70 -336
- data/lib/tap/support/aggregator.rb +55 -0
- data/lib/tap/support/audit.rb +281 -280
- data/lib/tap/support/batchable.rb +59 -0
- data/lib/tap/support/class_configuration.rb +279 -0
- data/lib/tap/support/configurable.rb +92 -0
- data/lib/tap/support/configurable_methods.rb +296 -0
- data/lib/tap/support/executable.rb +98 -0
- data/lib/tap/support/executable_queue.rb +82 -0
- data/lib/tap/support/logger.rb +9 -15
- data/lib/tap/support/rake.rb +43 -54
- data/lib/tap/support/run_error.rb +32 -13
- data/lib/tap/support/shell_utils.rb +47 -0
- data/lib/tap/support/tdoc.rb +9 -8
- data/lib/tap/support/tdoc/config_attr.rb +40 -16
- data/lib/tap/support/validation.rb +77 -0
- data/lib/tap/support/versions.rb +36 -36
- data/lib/tap/task.rb +276 -482
- data/lib/tap/test.rb +20 -261
- data/lib/tap/test/env_vars.rb +7 -5
- data/lib/tap/test/file_methods.rb +126 -121
- data/lib/tap/test/subset_methods.rb +86 -45
- data/lib/tap/test/tap_methods.rb +271 -0
- data/lib/tap/workflow.rb +174 -46
- data/test/app/config/another/task.yml +1 -0
- data/test/app/config/erb.yml +2 -1
- data/test/app/config/some/task.yml +1 -0
- data/test/app/config/template.yml +2 -6
- data/test/app_test.rb +1241 -1008
- data/test/env/test_configure/recurse_a.yml +2 -0
- data/test/env/test_configure/recurse_b.yml +2 -0
- data/test/env/test_configure/tap.yml +23 -0
- data/test/env/test_load_env_config/dir/tap.yml +3 -0
- data/test/env/test_load_env_config/recurse_a.yml +2 -0
- data/test/env/test_load_env_config/recurse_b.yml +2 -0
- data/test/env/test_load_env_config/tap.yml +3 -0
- data/test/env_test.rb +198 -0
- data/test/file_task_test.rb +70 -53
- data/{lib/tap/generator/generators/package/USAGE → test/root/file.txt} +0 -0
- data/test/root_test.rb +621 -454
- data/test/script_test.rb +38 -174
- data/test/support/aggregator_test.rb +99 -0
- data/test/support/audit_test.rb +409 -416
- data/test/support/batchable_test.rb +74 -0
- data/test/support/{task_configuration_test.rb → class_configuration_test.rb} +106 -47
- data/test/{task/config/overriding.yml → support/configurable/config/configured.yml} +0 -0
- data/test/support/configurable_test.rb +295 -0
- data/test/support/executable_queue_test.rb +103 -0
- data/test/support/executable_test.rb +38 -0
- data/test/support/logger_test.rb +17 -17
- data/test/support/rake_test.rb +4 -2
- data/test/support/shell_utils_test.rb +24 -0
- data/test/support/tdoc_test.rb +265 -258
- data/test/support/validation_test.rb +54 -0
- data/test/support/versions_test.rb +38 -38
- data/test/tap_test_helper.rb +19 -5
- data/test/tap_test_suite.rb +5 -2
- data/test/task_base_test.rb +13 -104
- data/test/task_syntax_test.rb +300 -0
- data/test/task_test.rb +258 -381
- data/test/test/env_vars_test.rb +40 -40
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/one.txt +0 -0
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/two.txt +0 -0
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/one.txt +0 -0
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/two.txt +0 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/one.txt +0 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/two.txt +0 -0
- data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +1 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_different_content}/expected/two.txt +0 -0
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +1 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_missing_expected_file}/expected/one.txt +0 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +1 -0
- data/test/test/file_methods_doc/test_sub/expected/one.txt +1 -0
- data/test/test/file_methods_doc/test_sub/expected/two.txt +1 -0
- data/test/test/file_methods_doc/test_sub/input/one.txt +1 -0
- data/test/test/file_methods_doc/test_sub/input/two.txt +1 -0
- data/test/test/file_methods_doc_test.rb +29 -0
- data/test/test/file_methods_test.rb +214 -143
- data/test/test/subset_methods_test.rb +111 -115
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/a.txt +0 -0
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/b.txt +0 -0
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/a.txt +0 -0
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/b.txt +0 -0
- data/test/test/tap_methods_test.rb +399 -0
- data/test/workflow_test.rb +101 -91
- metadata +86 -70
- data/lib/tap/generator/generators/package/package_generator.rb +0 -38
- data/lib/tap/generator/generators/package/templates/package.erb +0 -186
- data/lib/tap/generator/generators/script/USAGE +0 -0
- data/lib/tap/generator/generators/script/script_generator.rb +0 -17
- data/lib/tap/script/run.rb +0 -154
- data/lib/tap/support/batch_queue.rb +0 -162
- data/lib/tap/support/combinator.rb +0 -114
- data/lib/tap/support/task_configuration.rb +0 -169
- data/lib/tap/support/template.rb +0 -81
- data/lib/tap/support/templater.rb +0 -155
- data/lib/tap/version.rb +0 -4
- data/test/app/config/addition_template.yml +0 -6
- data/test/app_class_test.rb +0 -33
- data/test/check/binding_eval.rb +0 -23
- data/test/check/define_method_check.rb +0 -22
- data/test/check/dependencies_check.rb +0 -175
- data/test/check/inheritance_check.rb +0 -22
- data/test/support/batch_queue_test.rb +0 -320
- data/test/support/combinator_test.rb +0 -249
- data/test/support/template_test.rb +0 -122
- data/test/support/templater/erb.txt +0 -2
- data/test/support/templater/erb.yml +0 -2
- data/test/support/templater/somefile.txt +0 -2
- data/test/support/templater_test.rb +0 -192
- data/test/task/config/template.yml +0 -4
- data/test/task_class_test.rb +0 -170
- data/test/task_execute_test.rb +0 -262
- data/test/test/file_methods/test_assert_expected/expected/file.txt +0 -1
- data/test/test/file_methods/test_assert_expected/expected/folder/file.txt +0 -1
- data/test/test/file_methods/test_assert_expected/input/file.txt +0 -1
- data/test/test/file_methods/test_assert_expected/input/folder/file.txt +0 -1
- data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
- data/test/test/file_methods/test_file_compare/expected/output_1.txt +0 -3
- data/test/test/file_methods/test_file_compare/expected/output_2.txt +0 -1
- data/test/test/file_methods/test_file_compare/input/input_1.txt +0 -3
- data/test/test/file_methods/test_file_compare/input/input_2.txt +0 -3
- data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
- data/test/test/file_methods/test_yml_compare/expected/output_1.yml +0 -6
- data/test/test/file_methods/test_yml_compare/expected/output_2.yml +0 -6
- data/test/test/file_methods/test_yml_compare/input/input_1.yml +0 -4
- data/test/test/file_methods/test_yml_compare/input/input_2.yml +0 -4
- data/test/test_test.rb +0 -373
@@ -1,24 +1,23 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '<%= '../' * class_nesting_depth %>tap_test_helper.rb')
|
2
2
|
require '<%= class_path.empty? ? file_name : File.join(class_path, file_name) %>'
|
3
3
|
|
4
|
-
class <%=
|
4
|
+
class <%= class_name %>Test < Test::Unit::TestCase
|
5
5
|
acts_as_tap_test
|
6
6
|
|
7
7
|
def test_<%= file_name %>
|
8
|
-
t = <%= class_name %>.new nil, :
|
8
|
+
t = <%= class_name %>.new nil, :key => 'value'
|
9
9
|
|
10
10
|
# specify application options
|
11
|
-
with_options(:quiet => true) do
|
11
|
+
with_options(:quiet => true, :debug => true) do
|
12
12
|
|
13
13
|
# run the task with some inputs
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
t.enq("one")
|
15
|
+
app.run
|
16
|
+
|
17
|
+
# check the configuration and outputs
|
18
|
+
assert_equal({:key => 'value'}, t.config)
|
19
|
+
assert_audit_equal ExpAudit[[nil, "one"], [t, "one was processed with value"]], app._results(t).first
|
20
|
+
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
@@ -9,7 +9,7 @@ class <%= class_name_without_nesting %> < Tap::Workflow
|
|
9
9
|
def workflow
|
10
10
|
# Define the workflow entry and exit points,
|
11
11
|
# as well as the workflow logic.
|
12
|
-
self.entry_point = Task.new
|
12
|
+
self.entry_point = Tap::Task.new
|
13
13
|
|
14
14
|
# app.sequence(entry_point, 'another/task')
|
15
15
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '<%= '../' * class_nesting_depth %>tap_test_helper.rb')
|
2
2
|
require '<%= class_path.empty? ? file_name : File.join(class_path, file_name) %>'
|
3
3
|
|
4
|
-
class <%=
|
4
|
+
class <%= class_name %>Test < Test::Unit::TestCase
|
5
5
|
acts_as_tap_test
|
6
6
|
|
7
7
|
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
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tap/support/versions'
|
2
|
+
autoload(:FileUtils, 'fileutils')
|
2
3
|
|
3
4
|
module Tap
|
4
5
|
|
@@ -10,14 +11,17 @@ module Tap
|
|
10
11
|
# r = Root.new '/root_dir', :input => 'in', :output => 'out'
|
11
12
|
#
|
12
13
|
# # work with directories
|
13
|
-
# r[:
|
14
|
-
# r[:
|
15
|
-
# r[
|
14
|
+
# r[:input] # => '/root_dir/in'
|
15
|
+
# r[:output] # => '/root_dir/out'
|
16
|
+
# r['implicit'] # => '/root_dir/implicit'
|
17
|
+
#
|
18
|
+
# # expanded paths are returned unchanged
|
19
|
+
# r[File.expand_path('expanded')] # => File.expand_path('expanded')
|
16
20
|
#
|
17
21
|
# # work with filepaths
|
18
|
-
# fp = r.filepath(:
|
19
|
-
# r.relative_filepath(:
|
20
|
-
# r.translate(fp, :
|
22
|
+
# fp = r.filepath(:input, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
|
23
|
+
# r.relative_filepath(:input, fp) # => 'path/to/file.txt'
|
24
|
+
# r.translate(fp, :input, :output) # => '/root_dir/out/path/to/file.txt'
|
21
25
|
#
|
22
26
|
# # version filepaths
|
23
27
|
# r.version('path/to/config.yml', 1.0) # => 'path/to/config-1.0.yml'
|
@@ -29,50 +33,52 @@ module Tap
|
|
29
33
|
# r.filepath(:abs, "to", "file.txt") # => '/absolute/path/to/file.txt'
|
30
34
|
#
|
31
35
|
# By default, Roots are initialized to the present working directory (Dir.pwd).
|
32
|
-
# As in the '
|
33
|
-
#
|
36
|
+
# As in the 'implicit' example, Root infers a path relative to the root directory
|
37
|
+
# whenever it needs to resolve an alias that is not explicitly set. The only
|
38
|
+
# exceptions to this are fully expanded paths. These are returned unchanged.
|
34
39
|
#
|
35
40
|
# === Implementation Notes
|
36
41
|
#
|
37
|
-
# Internally Root stores expanded paths
|
38
|
-
#
|
39
|
-
#
|
42
|
+
# Internally Root stores expanded paths all aliased paths in the 'paths' hash.
|
43
|
+
# Expanding paths ensures they remain constant even when the present working
|
44
|
+
# directory (Dir.pwd) changes.
|
40
45
|
#
|
41
|
-
# Root keeps a separate hash mapping aliases to
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# aliases
|
46
|
+
# Root keeps a separate 'directories' hash mapping aliases to their subdirectory paths.
|
47
|
+
# This hash allow reassignment if and when the root directory changes. By contrast,
|
48
|
+
# there is no separate data structure storing the absolute paths. An absolute path
|
49
|
+
# thus has an alias in 'paths' but not 'directories', whereas subdirectory paths
|
50
|
+
# have aliases in both.
|
46
51
|
#
|
47
52
|
# These features may be important to note when subclassing Root:
|
48
53
|
# - root and all filepaths in 'paths' are expanded
|
49
|
-
# -
|
54
|
+
# - subdirectory paths are stored in 'directories'
|
50
55
|
# - absolute paths are present in 'paths' but not in 'directories'
|
51
56
|
#
|
52
|
-
class Root
|
57
|
+
class Root
|
58
|
+
# Regexp to match a windows-style root filepath.
|
59
|
+
WIN_ROOT_PATTERN = /^[A-z]:\//
|
60
|
+
|
53
61
|
class << self
|
54
62
|
include Support::Versions
|
55
63
|
|
56
64
|
# Returns the filepath of path relative to dir. Both dir and path are
|
57
|
-
# expanded before the relative filepath is determined.
|
58
|
-
#
|
59
|
-
# not relative to dir. Otherwise, the expanded path will be returned.
|
65
|
+
# expanded before the relative filepath is determined. An error is
|
66
|
+
# raised if the path is not relative to dir.
|
60
67
|
#
|
61
68
|
# Root.relative_filepath('dir', "dir/path/to/file.txt") # => "path/to/file.txt"
|
62
69
|
#
|
63
|
-
def relative_filepath(dir, path,
|
64
|
-
|
65
|
-
|
70
|
+
def relative_filepath(dir, path, dir_string=Dir.pwd)
|
71
|
+
expanded_dir = File.expand_path(dir, dir_string)
|
72
|
+
expanded_path = File.expand_path(path, dir_string)
|
66
73
|
|
67
|
-
unless
|
68
|
-
raise "\n#{
|
69
|
-
return expanded
|
74
|
+
unless expanded_path.index(expanded_dir) == 0
|
75
|
+
raise "\n#{expanded_path}\nis not relative to:\n#{expanded_dir}"
|
70
76
|
end
|
71
77
|
|
72
78
|
# use dir.length + 1 to remove a leading '/'. If dir.length + 1 >= expanded.length
|
73
79
|
# as in: relative_filepath('/path', '/path') then the first arg returns nil, and an
|
74
80
|
# empty string is returned
|
75
|
-
|
81
|
+
expanded_path[( expanded_dir.chomp("/").length + 1)..-1] || ""
|
76
82
|
end
|
77
83
|
|
78
84
|
# Lists all unique paths matching the input glob patterns.
|
@@ -94,14 +100,86 @@ module Tap
|
|
94
100
|
results
|
95
101
|
end.flatten.uniq
|
96
102
|
end
|
103
|
+
|
104
|
+
# Executes the block in the specified directory. Makes the directory, if
|
105
|
+
# necessary when mkdir is specified. Otherwise, indir raises an error
|
106
|
+
# for non-existant directories, as well as non-directory inputs.
|
107
|
+
def indir(dir, mkdir=false)
|
108
|
+
unless File.directory?(dir)
|
109
|
+
if !File.exists?(dir) && mkdir
|
110
|
+
FileUtils.mkdir_p(dir)
|
111
|
+
else
|
112
|
+
raise "non a directory: #{dir}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
pwd = Dir.pwd
|
117
|
+
begin
|
118
|
+
Dir.chdir(dir)
|
119
|
+
yield
|
120
|
+
ensure
|
121
|
+
Dir.chdir(pwd)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# The path root type indicating windows, *nix, or some unknown
|
126
|
+
# style of filepaths (:win, :nix, :unknown).
|
127
|
+
def path_root_type
|
128
|
+
@path_root_type ||= case
|
129
|
+
when RUBY_PLATFORM =~ /mswin/ && File.expand_path(".") =~ WIN_ROOT_PATTERN then :win
|
130
|
+
when File.expand_path(".")[0] == ?/ then :nix
|
131
|
+
else :unknown
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns true if the input path appears to be an expanded path,
|
136
|
+
# based on Root.path_root_type.
|
137
|
+
#
|
138
|
+
# If root_type == :win returns true if the path matches
|
139
|
+
# WIN_ROOT_PATTERN.
|
140
|
+
#
|
141
|
+
# Root.expanded_path?('C:/path') # => true
|
142
|
+
# Root.expanded_path?('c:/path') # => true
|
143
|
+
# Root.expanded_path?('D:/path') # => true
|
144
|
+
# Root.expanded_path?('path') # => false
|
145
|
+
#
|
146
|
+
# If root_type == :nix, then expanded? returns true if
|
147
|
+
# the path begins with '/'.
|
148
|
+
#
|
149
|
+
# Root.expanded_path?('/path') # => true
|
150
|
+
# Root.expanded_path?('path') # => false
|
151
|
+
#
|
152
|
+
# Otherwise expanded_path? always returns nil.
|
153
|
+
def expanded_path?(path, root_type=path_root_type)
|
154
|
+
case root_type
|
155
|
+
when :win
|
156
|
+
path =~ WIN_ROOT_PATTERN ? true : false
|
157
|
+
when :nix
|
158
|
+
path[0] == ?/
|
159
|
+
else
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
end
|
97
163
|
end
|
98
164
|
|
99
165
|
include Support::Versions
|
100
|
-
attr_reader :root, :directories, :paths
|
101
166
|
|
102
|
-
#
|
103
|
-
|
104
|
-
|
167
|
+
# The root directory
|
168
|
+
attr_reader :root
|
169
|
+
|
170
|
+
# A hash of (alias, relative path) pairs for aliased subdirectories
|
171
|
+
attr_reader :directories
|
172
|
+
|
173
|
+
# A hash of (alias, expanded path) pairs for aliased subdirectories and absolute paths.
|
174
|
+
attr_reader :paths
|
175
|
+
|
176
|
+
# The filesystem root, inferred from self.root
|
177
|
+
# (ex '/' on *nix or something like 'C:/' on Windows).
|
178
|
+
attr_reader :path_root
|
179
|
+
|
180
|
+
# Creates a new Root with the given root directory, aliased directories
|
181
|
+
# and absolute paths. By default root is the present working directory
|
182
|
+
# and no aliased directories or absolute paths are specified.
|
105
183
|
def initialize(root=Dir.pwd, directories={}, absolute_paths={})
|
106
184
|
assign_paths(root, directories, absolute_paths)
|
107
185
|
end
|
@@ -112,13 +190,21 @@ module Tap
|
|
112
190
|
end
|
113
191
|
|
114
192
|
# Sets the directories to those provided. 'root' and :root are reserved
|
115
|
-
#
|
193
|
+
# and cannot be set using this method (use root= instead).
|
194
|
+
#
|
195
|
+
# r['alt'] # => File.join(r.root, 'alt')
|
196
|
+
# r.directories = {'alt' => 'dir'}
|
197
|
+
# r['alt'] # => File.join(r.root, 'dir')
|
116
198
|
def directories=(dirs)
|
117
199
|
assign_paths(root, dirs, absolute_paths)
|
118
200
|
end
|
119
201
|
|
120
202
|
# Sets the absolute paths to those provided. 'root' and :root are reserved
|
121
203
|
# directory keys and cannot be set using this method (use root= instead).
|
204
|
+
#
|
205
|
+
# r['abs'] # => File.join(r.root, 'abs')
|
206
|
+
# r.absolute_paths = {'abs' => '/path/to/dir'}
|
207
|
+
# r['abs'] # => '/path/to/dir'
|
122
208
|
def absolute_paths=(paths)
|
123
209
|
assign_paths(root, directories, paths)
|
124
210
|
end
|
@@ -132,9 +218,10 @@ module Tap
|
|
132
218
|
abs_paths
|
133
219
|
end
|
134
220
|
|
135
|
-
# Sets
|
136
|
-
# The root
|
137
|
-
# Absolute filepaths can be set using the
|
221
|
+
# Sets an alias for the subdirectory relative to the root directory.
|
222
|
+
# The aliases 'root' and :root cannot be set with this method
|
223
|
+
# (use root= instead). Absolute filepaths can be set using the
|
224
|
+
# second syntax.
|
138
225
|
#
|
139
226
|
# r = Root.new '/root_dir'
|
140
227
|
# r[:dir] = 'path/to/dir'
|
@@ -144,13 +231,14 @@ module Tap
|
|
144
231
|
# r[:abs] # => '/abs/path/to/dir'
|
145
232
|
#
|
146
233
|
#--
|
234
|
+
# Implementation Notes:
|
147
235
|
# The syntax for setting an absolute filepath requires an odd use []=.
|
148
236
|
# In fact the method recieves the arguments (:dir, true, '/abs/path/to/dir')
|
149
237
|
# rather than (:dir, '/abs/path/to/dir', true), meaning that internally path
|
150
238
|
# and absolute are switched when setting an absolute filepath.
|
151
239
|
#++
|
152
|
-
def []=(
|
153
|
-
raise ArgumentError, "The directory key '#{
|
240
|
+
def []=(dir, path, absolute=false)
|
241
|
+
raise ArgumentError, "The directory key '#{dir}' is reserved." if dir.to_s == 'root'
|
154
242
|
|
155
243
|
# switch the paths if absolute was provided
|
156
244
|
unless absolute == false
|
@@ -158,42 +246,49 @@ module Tap
|
|
158
246
|
path = absolute
|
159
247
|
absolute = switch
|
160
248
|
end
|
161
|
-
|
249
|
+
|
162
250
|
case
|
163
251
|
when path.nil?
|
164
|
-
@directories.delete(
|
165
|
-
@paths.delete(
|
252
|
+
@directories.delete(dir)
|
253
|
+
@paths.delete(dir)
|
166
254
|
when absolute
|
167
|
-
@directories.delete(
|
168
|
-
@paths[
|
255
|
+
@directories.delete(dir)
|
256
|
+
@paths[dir] = File.expand_path(path)
|
169
257
|
else
|
170
|
-
@directories[
|
171
|
-
@paths[
|
172
|
-
end
|
258
|
+
@directories[dir] = path
|
259
|
+
@paths[dir] = File.expand_path(File.join(root, path))
|
260
|
+
end
|
173
261
|
end
|
174
262
|
|
175
263
|
# Returns the expanded path for the specified alias. If the alias
|
176
|
-
# has not been set, then the path is inferred to be 'root/
|
264
|
+
# has not been set, then the path is inferred to be 'root/dir' unless
|
265
|
+
# the path is relative to path_root. These paths are returned
|
266
|
+
# directly.
|
177
267
|
#
|
178
|
-
# r = Root.new '/root_dir'
|
179
|
-
# r[:dir]
|
180
|
-
# r[:dir] # => '/root_dir/path/to/dir'
|
268
|
+
# r = Root.new '/root_dir', :dir => 'path/to/dir'
|
269
|
+
# r[:dir] # => '/root_dir/path/to/dir'
|
181
270
|
#
|
182
|
-
# r
|
183
|
-
# r[
|
271
|
+
# r.path_root # => '/'
|
272
|
+
# r['relative/path'] # => '/root_dir/relative/path'
|
273
|
+
# r['/expanded/path'] # => '/expanded/path'
|
184
274
|
#
|
185
|
-
def [](
|
186
|
-
self.paths[
|
275
|
+
def [](dir)
|
276
|
+
path = self.paths[dir]
|
277
|
+
return path unless path == nil
|
278
|
+
|
279
|
+
dir = dir.to_s
|
280
|
+
Root.expanded_path?(dir) ? dir : File.expand_path(File.join(root, dir))
|
187
281
|
end
|
188
282
|
|
189
283
|
# Constructs expanded filepaths relative to the path of the specified alias.
|
190
|
-
def filepath(
|
191
|
-
|
284
|
+
def filepath(dir, *filename)
|
285
|
+
# TODO - consider filename.compact so nils will not raise errors
|
286
|
+
File.expand_path(File.join(self[dir], *filename))
|
192
287
|
end
|
193
288
|
|
194
289
|
# Retrieves the filepath relative to the path of the specified alias.
|
195
|
-
def relative_filepath(
|
196
|
-
Root.relative_filepath(self[
|
290
|
+
def relative_filepath(dir, filepath)
|
291
|
+
Root.relative_filepath(self[dir], filepath)
|
197
292
|
end
|
198
293
|
|
199
294
|
# Generates a target filepath translated from the aliased input dir to
|
@@ -202,23 +297,28 @@ module Tap
|
|
202
297
|
#
|
203
298
|
# fp = r.filepath(:in, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
|
204
299
|
# r.translate(fp, :in, :out) # => '/root_dir/out/path/to/file.txt'
|
205
|
-
def translate(filepath,
|
206
|
-
filepath(
|
300
|
+
def translate(filepath, input_dir, output_dir)
|
301
|
+
filepath(output_dir, relative_filepath(input_dir, filepath))
|
207
302
|
end
|
208
303
|
|
209
304
|
# Lists all files in the aliased dir matching the input patterns. Patterns
|
210
305
|
# should be valid inputs for +Dir.glob+. If no patterns are specified, lists
|
211
306
|
# all files/folders matching '**/*'.
|
212
|
-
def glob(
|
307
|
+
def glob(dir, *patterns)
|
213
308
|
patterns << "**/*" if patterns.empty?
|
214
|
-
patterns.collect! {|pattern| filepath(
|
309
|
+
patterns.collect! {|pattern| filepath(dir, pattern)}
|
215
310
|
Root.glob(*patterns)
|
216
311
|
end
|
217
312
|
|
218
313
|
# Lists all versions of filename in the aliased dir matching the version patterns.
|
219
314
|
# If no patterns are specified, then all versions of filename will be returned.
|
220
|
-
def vglob(
|
221
|
-
Root.vglob(filepath(
|
315
|
+
def vglob(dir, filename, *vpatterns)
|
316
|
+
Root.vglob(filepath(dir, filename), *vpatterns)
|
317
|
+
end
|
318
|
+
|
319
|
+
# Executes the provided block in the specified directory using Root.indir.
|
320
|
+
def indir(dir, mkdir=false)
|
321
|
+
Root.indir(self[dir], mkdir) { yield }
|
222
322
|
end
|
223
323
|
|
224
324
|
private
|
@@ -228,9 +328,14 @@ module Tap
|
|
228
328
|
@root = File.expand_path(root)
|
229
329
|
@directories = {}
|
230
330
|
@paths = {'root' => @root, :root => @root}
|
231
|
-
|
232
|
-
|
233
|
-
|
331
|
+
|
332
|
+
@path_root = File.dirname(@root)
|
333
|
+
while @path_root != (parent = File.dirname(@path_root))
|
334
|
+
@path_root = parent
|
335
|
+
end
|
336
|
+
|
337
|
+
directories.each_pair {|dir, path| self[dir] = path }
|
338
|
+
absolute_paths.each_pair {|dir, path| self[dir, true] = path }
|
234
339
|
end
|
235
340
|
|
236
341
|
end
|