tap 0.8.0 → 0.9.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.
- 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
|