tap 0.11.1 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +35 -1
- data/MIT-LICENSE +1 -1
- data/README +16 -15
- data/bin/tap +1 -1
- data/cmd/console.rb +4 -3
- data/cmd/manifest.rb +2 -2
- data/cmd/run.rb +12 -15
- data/doc/Class Reference +120 -117
- data/doc/Command Reference +27 -27
- data/doc/Syntax Reference +55 -111
- data/doc/Tutorial +69 -26
- data/lib/tap.rb +3 -8
- data/lib/tap/app.rb +122 -146
- data/lib/tap/constants.rb +2 -2
- data/lib/tap/env.rb +178 -252
- data/lib/tap/exe.rb +67 -30
- data/lib/tap/file_task.rb +224 -411
- data/lib/tap/generator/arguments.rb +13 -0
- data/lib/tap/generator/base.rb +112 -30
- data/lib/tap/generator/destroy.rb +36 -13
- data/lib/tap/generator/generate.rb +69 -48
- data/lib/tap/generator/generators/command/templates/command.erb +3 -3
- data/lib/tap/generator/generators/config/config_generator.rb +82 -10
- data/lib/tap/generator/generators/generator/generator_generator.rb +16 -6
- data/lib/tap/generator/generators/generator/templates/task.erb +2 -2
- data/lib/tap/generator/generators/generator/templates/test.erb +26 -0
- data/lib/tap/generator/generators/root/root_generator.rb +24 -13
- data/lib/tap/generator/generators/root/templates/Rakefile +4 -4
- data/lib/tap/generator/generators/root/templates/{tapfile → Rapfile} +6 -6
- data/lib/tap/generator/generators/root/templates/gemspec +0 -1
- data/lib/tap/generator/generators/task/task_generator.rb +3 -3
- data/lib/tap/generator/generators/task/templates/test.erb +1 -1
- data/lib/tap/generator/manifest.rb +7 -1
- data/lib/tap/generator/preview.rb +76 -0
- data/lib/tap/root.rb +222 -156
- data/lib/tap/spec.rb +41 -0
- data/lib/tap/support/aggregator.rb +25 -28
- data/lib/tap/support/audit.rb +278 -357
- data/lib/tap/support/constant.rb +2 -1
- data/lib/tap/support/constant_manifest.rb +28 -25
- data/lib/tap/support/dependency.rb +1 -1
- data/lib/tap/support/executable.rb +52 -183
- data/lib/tap/support/executable_queue.rb +50 -20
- data/lib/tap/support/gems.rb +1 -1
- data/lib/tap/support/intern.rb +0 -6
- data/lib/tap/support/join.rb +49 -83
- data/lib/tap/support/joins.rb +0 -3
- data/lib/tap/support/joins/switch.rb +13 -11
- data/lib/tap/support/joins/sync_merge.rb +25 -50
- data/lib/tap/support/manifest.rb +1 -0
- data/lib/tap/support/node.rb +140 -20
- data/lib/tap/support/parser.rb +56 -42
- data/lib/tap/support/schema.rb +183 -157
- data/lib/tap/support/templater.rb +9 -1
- data/lib/tap/support/versions.rb +39 -0
- data/lib/tap/task.rb +150 -177
- data/lib/tap/tasks/dump.rb +4 -4
- data/lib/tap/tasks/load.rb +29 -29
- data/lib/tap/test.rb +66 -53
- data/lib/tap/test/env_vars.rb +3 -3
- data/lib/tap/test/extensions.rb +11 -17
- data/lib/tap/test/file_test.rb +74 -132
- data/lib/tap/test/file_test_class.rb +4 -1
- data/lib/tap/test/regexp_escape.rb +2 -2
- data/lib/tap/test/script_test.rb +2 -2
- data/lib/tap/test/subset_test.rb +6 -6
- data/lib/tap/test/tap_test.rb +28 -154
- metadata +30 -51
- data/bin/rap +0 -118
- data/cgi/run.rb +0 -97
- data/lib/tap/declarations.rb +0 -229
- data/lib/tap/generator/generators/config/templates/doc.erb +0 -12
- data/lib/tap/generator/generators/config/templates/nodoc.erb +0 -8
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +0 -27
- data/lib/tap/generator/generators/file_task/templates/file.txt +0 -11
- data/lib/tap/generator/generators/file_task/templates/result.yml +0 -6
- data/lib/tap/generator/generators/file_task/templates/task.erb +0 -33
- data/lib/tap/generator/generators/file_task/templates/test.erb +0 -29
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +0 -5
- data/lib/tap/patches/optparse/summarize.rb +0 -62
- data/lib/tap/support/assignments.rb +0 -173
- data/lib/tap/support/class_configuration.rb +0 -182
- data/lib/tap/support/combinator.rb +0 -125
- data/lib/tap/support/configurable.rb +0 -113
- data/lib/tap/support/configurable_class.rb +0 -271
- data/lib/tap/support/configuration.rb +0 -170
- data/lib/tap/support/gems/rake.rb +0 -111
- data/lib/tap/support/instance_configuration.rb +0 -173
- data/lib/tap/support/joins/fork.rb +0 -19
- data/lib/tap/support/joins/merge.rb +0 -22
- data/lib/tap/support/joins/sequence.rb +0 -21
- data/lib/tap/support/lazy_attributes.rb +0 -45
- data/lib/tap/support/lazydoc.rb +0 -386
- data/lib/tap/support/lazydoc/comment.rb +0 -503
- data/lib/tap/support/lazydoc/config.rb +0 -17
- data/lib/tap/support/lazydoc/definition.rb +0 -36
- data/lib/tap/support/lazydoc/document.rb +0 -152
- data/lib/tap/support/lazydoc/method.rb +0 -24
- data/lib/tap/support/tdoc.rb +0 -409
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -38
- data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -42
- data/lib/tap/support/validation.rb +0 -479
- data/lib/tap/tasks/rake.rb +0 -57
@@ -5,23 +5,33 @@ module Tap::Generator::Generators
|
|
5
5
|
# Generates a new generator.
|
6
6
|
class GeneratorGenerator < Tap::Generator::Base
|
7
7
|
|
8
|
+
config :test, true, &c.switch # specifies creation of a test file
|
9
|
+
|
8
10
|
def manifest(m, const_name)
|
9
|
-
const = Constant.new(const_name.camelize)
|
10
|
-
dir=
|
11
|
+
const = Tap::Support::Constant.new(const_name.camelize)
|
12
|
+
dir = app.filepath('lib', const.path)
|
11
13
|
|
12
14
|
# make the directory
|
13
|
-
m.directory
|
15
|
+
m.directory dir
|
14
16
|
|
15
17
|
# make the generator
|
16
|
-
m.template app.filepath(dir, const.basename
|
18
|
+
m.template app.filepath(dir, "#{const.basename}_generator.rb"), "task.erb", :const => const
|
17
19
|
|
18
20
|
# make the templates directory
|
19
21
|
m.directory app.filepath(dir, 'templates')
|
22
|
+
|
23
|
+
# make a template file
|
24
|
+
# (note it's easier to do this as a file since erb is
|
25
|
+
# added, and would have to be escaped in a template)
|
20
26
|
m.file app.filepath(dir, 'templates', 'template_file.erb') do |file|
|
21
|
-
file
|
22
|
-
file.puts "key: <%= key %>"
|
27
|
+
file << "# A sample template file.\nkey: <%= key %>\n"
|
23
28
|
end
|
24
29
|
|
30
|
+
if test
|
31
|
+
test_path = app.filepath('test', "#{const.path}_generator_test.rb")
|
32
|
+
m.directory File.dirname(test_path)
|
33
|
+
m.template test_path, "test.erb", :const => const
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
27
37
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# <replace with command line description>
|
3
3
|
|
4
4
|
# <%= const.const_name %> Documentation
|
5
|
-
class <%= const.const_name %> < Tap::Generator::Base
|
5
|
+
class <%= const.const_name %>Generator < Tap::Generator::Base
|
6
6
|
|
7
7
|
config :key, 'value' # a sample config
|
8
8
|
|
@@ -21,7 +21,7 @@ class <%= const.const_name %> < Tap::Generator::Base
|
|
21
21
|
# template a file in the templates directory using ERB.
|
22
22
|
# The (key, value) pairs will be available in the
|
23
23
|
# template as local variables.
|
24
|
-
m.template "<%= const.const_name.underscore %>_file.txt", "template_file.erb",
|
24
|
+
m.template "<%= const.const_name.underscore %>_file.txt", "template_file.erb", config.to_hash
|
25
25
|
|
26
26
|
end
|
27
27
|
end <% module_nest(const.nesting, ' ') { target } end %>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '<%= '../' * const.nesting_depth %>tap_test_helper.rb')
|
2
|
+
require '<%= const.path %>'
|
3
|
+
require 'tap/generator/preview.rb'
|
4
|
+
|
5
|
+
class <%= const.name %>Test < Test::Unit::TestCase
|
6
|
+
|
7
|
+
# Preview fakes out a generator for testing
|
8
|
+
Preview = Tap::Generator::Preview
|
9
|
+
|
10
|
+
acts_as_tap_test
|
11
|
+
|
12
|
+
def test_<%= const.basename %>
|
13
|
+
g = <%= const.name %>.new.extend Preview
|
14
|
+
|
15
|
+
# check the files and directories
|
16
|
+
assert_equal %w{
|
17
|
+
<%= const.const_name.underscore %>_file.txt
|
18
|
+
}, g.process
|
19
|
+
|
20
|
+
# check the content as necessary
|
21
|
+
assert_equal %q{
|
22
|
+
# A sample template file.
|
23
|
+
key: value
|
24
|
+
}, "\n" + g.builds['<%= const.const_name.underscore %>_file.txt']
|
25
|
+
end
|
26
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'tap/
|
1
|
+
require 'tap/generator/base'
|
2
2
|
|
3
3
|
module Tap::Generator::Generators
|
4
4
|
|
@@ -21,7 +21,7 @@ module Tap::Generator::Generators
|
|
21
21
|
class RootGenerator < Tap::Generator::Base
|
22
22
|
|
23
23
|
config :config_file, true, &c.switch # create a tap.yml file
|
24
|
-
config :
|
24
|
+
config :rapfile, false, &c.switch # create a rapfile
|
25
25
|
|
26
26
|
# ::args ROOT, PROJECT_NAME=basename(ROOT)
|
27
27
|
def manifest(m, root, project_name=nil)
|
@@ -30,32 +30,43 @@ module Tap::Generator::Generators
|
|
30
30
|
|
31
31
|
m.directory r.root
|
32
32
|
m.directory r['lib']
|
33
|
+
m.directory r['test']
|
33
34
|
|
34
35
|
template_files do |source, target|
|
35
36
|
case
|
36
37
|
when File.directory?(source)
|
37
38
|
m.directory r[target]
|
38
39
|
next
|
39
|
-
when
|
40
|
-
m.template r[project_name + '.gemspec'], source, :project_name => project_name, :
|
40
|
+
when source =~ /gemspec$/
|
41
|
+
m.template r[project_name + '.gemspec'], source, :project_name => project_name, :config_file => config_file
|
41
42
|
next
|
42
|
-
when
|
43
|
-
next unless
|
43
|
+
when source =~ /Rapfile$/
|
44
|
+
next unless rapfile
|
44
45
|
end
|
45
46
|
|
46
47
|
m.template r[target], source, :project_name => project_name
|
47
48
|
end
|
48
49
|
|
49
50
|
m.file(r['tap.yml']) do |file|
|
50
|
-
|
51
|
-
|
51
|
+
Configurable::Utils.dump(Tap::App.configurations, file) do |key, delegate|
|
52
|
+
default = delegate.default
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
54
|
+
# get the description
|
55
|
+
desc = delegate.attributes[:desc]
|
56
|
+
doc = desc.to_s
|
57
|
+
doc = desc.comment if doc.empty?
|
58
|
+
|
59
|
+
# wrap as lines
|
60
|
+
lines = Lazydoc::Utils.wrap(doc, 50).collect {|line| "# #{line}"}
|
61
|
+
lines << "" unless lines.empty?
|
62
|
+
|
63
|
+
# setup formatting
|
64
|
+
leader = key == 'root' || default == nil ? '# ' : ''
|
65
|
+
config = {key => default}.to_yaml[5..-1]
|
66
|
+
"#{lines.join("\n")}#{leader}#{config.strip}\n\n"
|
67
|
+
end
|
58
68
|
end if config_file
|
59
69
|
end
|
70
|
+
|
60
71
|
end
|
61
72
|
end
|
@@ -62,12 +62,12 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
62
62
|
rdoc.rdoc_files.include( spec.extra_rdoc_files )
|
63
63
|
rdoc.rdoc_files.include( spec.files.select {|file| file =~ /^lib.*\.rb$/} )
|
64
64
|
|
65
|
-
# Using
|
65
|
+
# Using CDoc to template your RDoc will result in configurations being
|
66
66
|
# listed with documentation in a subsection following attributes. Not
|
67
67
|
# necessary, but nice.
|
68
|
-
require '
|
69
|
-
rdoc.template = '
|
70
|
-
rdoc.options << '--fmt' << '
|
68
|
+
require 'cdoc'
|
69
|
+
rdoc.template = 'cdoc/cdoc_html_template'
|
70
|
+
rdoc.options << '--fmt' << 'cdoc'
|
71
71
|
end
|
72
72
|
|
73
73
|
#
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'tap/declarations'
|
2
2
|
|
3
3
|
module <%= project_name.camelize %>
|
4
|
-
extend
|
5
|
-
|
6
|
-
# ::desc your basic goodnight moon task
|
7
|
-
# Says goodnight with a configurable message.
|
8
|
-
task(:goodnight, :obj, :message => 'goodnight') do |task, args|
|
9
|
-
puts "#{task.message} #{args.obj}"
|
4
|
+
extend Rap::Declarations
|
5
|
+
|
6
|
+
# ::desc your basic goodnight moon task
|
7
|
+
# Says goodnight with a configurable message.
|
8
|
+
task(:goodnight, :obj, :message => 'goodnight') do |task, args|
|
9
|
+
puts "#{task.message} #{args.obj}"
|
10
10
|
end
|
11
11
|
end
|
@@ -5,15 +5,15 @@ module Tap::Generator::Generators
|
|
5
5
|
# Generates a new Tap::Task and an associated test file.
|
6
6
|
class TaskGenerator < Tap::Generator::Base
|
7
7
|
|
8
|
-
config :test, true, &c.switch #
|
8
|
+
config :test, true, &c.switch # specifies creation of a test file
|
9
9
|
|
10
10
|
def manifest(m, const_name)
|
11
|
-
const = Constant.new(const_name.camelize)
|
11
|
+
const = Tap::Support::Constant.new(const_name.camelize)
|
12
12
|
|
13
13
|
task_path = app.filepath('lib', "#{const.path}.rb")
|
14
14
|
m.directory File.dirname(task_path)
|
15
15
|
m.template task_path, "task.erb", :const => const
|
16
|
-
|
16
|
+
|
17
17
|
if test
|
18
18
|
test_path = app.filepath('test', "#{const.path}_test.rb")
|
19
19
|
m.directory File.dirname(test_path)
|
@@ -14,6 +14,6 @@ class <%= const.name %>Test < Test::Unit::TestCase
|
|
14
14
|
# a more complex test
|
15
15
|
task.execute("moon")
|
16
16
|
assert_equal ["goodnight moon"], app.results(task)
|
17
|
-
assert_audit_equal
|
17
|
+
assert_audit_equal [[nil, "moon"], [task, "goodnight moon"]], app._results(task)[0]
|
18
18
|
end
|
19
19
|
end
|
@@ -1,11 +1,17 @@
|
|
1
1
|
module Tap
|
2
2
|
module Generator
|
3
|
+
|
4
|
+
# Manifest records methods called upon it using method_missing. These
|
5
|
+
# actions are replayed on a generator in order (for generate) or in
|
6
|
+
# reverse order (for destroy).
|
3
7
|
class Manifest
|
8
|
+
|
9
|
+
# Makes a new Manifest. Method calls on self are recorded to actions.
|
4
10
|
def initialize(actions)
|
5
11
|
@actions = actions
|
6
12
|
end
|
7
13
|
|
8
|
-
#
|
14
|
+
# Records an action.
|
9
15
|
def method_missing(action, *args, &block)
|
10
16
|
@actions << [action, args, block]
|
11
17
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Generator
|
5
|
+
|
6
|
+
# Preview is a testing module designed so that process will return an array
|
7
|
+
# of relative filepaths for the created files/directories (which are easy
|
8
|
+
# to specify in a test). Preview also collects the content of created files
|
9
|
+
# to be tested as needed.
|
10
|
+
#
|
11
|
+
# class Sample < Tap::Generator::Base
|
12
|
+
# def manifest(m)
|
13
|
+
# dir = app.filepath(:root, 'dir')
|
14
|
+
#
|
15
|
+
# m.directory dir
|
16
|
+
# m.file(File.join(dir, 'file.txt')) {|io| io << "content"}
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# These assertions will pass:
|
21
|
+
#
|
22
|
+
# s = Sample.new.extend Preview
|
23
|
+
# assert_equal %w{
|
24
|
+
# dir
|
25
|
+
# dir/file.txt
|
26
|
+
# }, s.process
|
27
|
+
#
|
28
|
+
# assert_equal "content", s.preview['dir/file.txt']
|
29
|
+
#
|
30
|
+
# Note that relative filepaths are determined from app.root for the
|
31
|
+
# instance; in tests like the one above, it may be prudent to reset
|
32
|
+
# the Tap::App.instance like so:
|
33
|
+
#
|
34
|
+
# def setup
|
35
|
+
# Tap::App.instance = Tap::App.new
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
module Preview
|
39
|
+
|
40
|
+
# A hash of (relative_path, content) pairs representing
|
41
|
+
# content built to files.
|
42
|
+
attr_accessor :preview
|
43
|
+
|
44
|
+
def self.extended(base) # :nodoc:
|
45
|
+
base.instance_variable_set(:@preview, {})
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the path of path, relative to app.root. If path
|
49
|
+
# is app.root, '.' will be returned.
|
50
|
+
def relative_path(path)
|
51
|
+
path = app.relative_filepath(:root, path) || path
|
52
|
+
path.empty? ? "." : path
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the relative path of the target.
|
56
|
+
def directory(target, options={})
|
57
|
+
relative_path(target)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the relative path of the target. If a block is given,
|
61
|
+
# the block will be called with a StringIO and the results stored
|
62
|
+
# in builds.
|
63
|
+
def file(target, options={})
|
64
|
+
target = relative_path(target)
|
65
|
+
|
66
|
+
if block_given?
|
67
|
+
io = StringIO.new
|
68
|
+
yield(io)
|
69
|
+
preview[target] = io.string
|
70
|
+
end
|
71
|
+
|
72
|
+
target
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/tap/root.rb
CHANGED
@@ -1,59 +1,58 @@
|
|
1
|
+
require 'configurable'
|
1
2
|
require 'tap/support/versions'
|
2
|
-
require 'tap/support/configurable'
|
3
3
|
autoload(:FileUtils, 'fileutils')
|
4
4
|
|
5
5
|
module Tap
|
6
6
|
|
7
|
-
# Root allows you to define a root directory and alias
|
8
|
-
# you can conceptualize what filepaths you need without predefining the
|
9
|
-
# filepaths. Root also simplifies operations on filepaths.
|
7
|
+
# Root allows you to define a root directory and alias relative paths, so
|
8
|
+
# that you can conceptualize what filepaths you need without predefining the
|
9
|
+
# full filepaths. Root also simplifies operations on filepaths.
|
10
10
|
#
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# # define a root directory with aliased relative paths
|
12
|
+
# r = Root.new '/root_dir', :input => 'in', :output => 'out'
|
13
13
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
14
|
+
# # work with aliases
|
15
|
+
# r[:input] # => '/root_dir/in'
|
16
|
+
# r[:output] # => '/root_dir/out'
|
17
|
+
# r['implicit'] # => '/root_dir/implicit'
|
18
18
|
#
|
19
|
-
#
|
20
|
-
#
|
19
|
+
# # expanded paths are returned unchanged
|
20
|
+
# r[File.expand_path('expanded')] # => File.expand_path('expanded')
|
21
21
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
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
26
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
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
31
|
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
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
35
|
#
|
36
|
-
# By default, Roots are initialized to the present working directory
|
37
|
-
#
|
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.
|
36
|
+
# By default, Roots are initialized to the present working directory
|
37
|
+
# (Dir.pwd).
|
40
38
|
#
|
39
|
+
#--
|
41
40
|
# === Implementation Notes
|
42
41
|
#
|
43
|
-
# Internally Root
|
44
|
-
# Expanding paths ensures they remain constant even when the present working
|
42
|
+
# Internally Root expands and stores all aliased paths in the 'paths' hash.
|
43
|
+
# Expanding paths ensures they remain constant even when the present working
|
45
44
|
# directory (Dir.pwd) changes.
|
46
45
|
#
|
47
|
-
# Root keeps a separate '
|
48
|
-
# This hash allow reassignment if and when the root directory
|
49
|
-
# there is no separate data structure storing the
|
50
|
-
# thus has an alias in 'paths' but not
|
51
|
-
# have aliases in both.
|
46
|
+
# Root keeps a separate 'relative_paths' hash mapping aliases to their
|
47
|
+
# relative paths. This hash allow reassignment if and when the root directory
|
48
|
+
# changes. By contrast, there is no separate data structure storing the
|
49
|
+
# absolute paths. An absolute path thus has an alias in 'paths' but not
|
50
|
+
# 'relative_paths', whereas relative paths have aliases in both.
|
52
51
|
#
|
53
52
|
# These features may be important to note when subclassing Root:
|
54
53
|
# - root and all filepaths in 'paths' are expanded
|
55
|
-
# -
|
56
|
-
# - absolute paths are present in 'paths' but not in '
|
54
|
+
# - relative paths are stored in 'relative_paths'
|
55
|
+
# - absolute paths are present in 'paths' but not in 'relative_paths'
|
57
56
|
#
|
58
57
|
class Root
|
59
58
|
# Regexp to match a windows-style root filepath.
|
@@ -77,12 +76,12 @@ module Tap
|
|
77
76
|
# use dir.length + 1 to remove a leading '/'. If dir.length + 1 >= expanded.length
|
78
77
|
# as in: relative_filepath('/path', '/path') then the first arg returns nil, and an
|
79
78
|
# empty string is returned
|
80
|
-
expanded_path[(
|
79
|
+
expanded_path[(expanded_dir.chomp("/").length + 1)..-1] || ""
|
81
80
|
end
|
82
81
|
|
83
|
-
# Generates a target filepath translated from the source_dir to
|
84
|
-
#
|
85
|
-
#
|
82
|
+
# Generates a target filepath translated from the source_dir to the
|
83
|
+
# target_dir. Raises an error if the filepath is not relative to the
|
84
|
+
# source_dir.
|
86
85
|
#
|
87
86
|
# Root.translate("/path/to/file.txt", "/path", "/another/path") # => '/another/path/to/file.txt'
|
88
87
|
#
|
@@ -92,6 +91,16 @@ module Tap
|
|
92
91
|
end
|
93
92
|
File.join(target_dir, relative_path)
|
94
93
|
end
|
94
|
+
|
95
|
+
# Returns the path, exchanging the extension with extname. Extname may
|
96
|
+
# optionally omit the leading period.
|
97
|
+
#
|
98
|
+
# Root.exchange('path/to/file.txt', '.html') # => 'path/to/file.html'
|
99
|
+
# Root.exchange('path/to/file.txt', 'rb') # => 'path/to/file.rb'
|
100
|
+
#
|
101
|
+
def exchange(path, extname)
|
102
|
+
"#{path.chomp(File.extname(path))}#{extname[0] == ?. ? '' : '.'}#{extname}"
|
103
|
+
end
|
95
104
|
|
96
105
|
# Lists all unique paths matching the input glob patterns.
|
97
106
|
def glob(*patterns)
|
@@ -100,8 +109,8 @@ module Tap
|
|
100
109
|
end.flatten.uniq
|
101
110
|
end
|
102
111
|
|
103
|
-
# Lists all unique versions of path matching the glob version patterns.
|
104
|
-
#
|
112
|
+
# Lists all unique versions of path matching the glob version patterns. If
|
113
|
+
# no patterns are specified, then all versions of path will be returned.
|
105
114
|
def vglob(path, *vpatterns)
|
106
115
|
vpatterns << "*" if vpatterns.empty?
|
107
116
|
vpatterns.collect do |vpattern|
|
@@ -113,8 +122,8 @@ module Tap
|
|
113
122
|
end.flatten.uniq
|
114
123
|
end
|
115
124
|
|
116
|
-
# Path suffix glob. Globs along the base paths for
|
117
|
-
#
|
125
|
+
# Path suffix glob. Globs along the base paths for paths that match the
|
126
|
+
# specified suffix pattern.
|
118
127
|
def sglob(suffix_pattern, *base_paths)
|
119
128
|
base_paths.collect do |base|
|
120
129
|
base = File.expand_path(base)
|
@@ -122,9 +131,9 @@ module Tap
|
|
122
131
|
end.flatten.uniq
|
123
132
|
end
|
124
133
|
|
125
|
-
# Like Dir.chdir but makes the directory, if necessary, when
|
126
|
-
#
|
127
|
-
#
|
134
|
+
# Like Dir.chdir but makes the directory, if necessary, when mkdir is
|
135
|
+
# specified. chdir raises an error for non-existant directories, as well
|
136
|
+
# as non-directory inputs.
|
128
137
|
def chdir(dir, mkdir=false, &block)
|
129
138
|
dir = File.expand_path(dir)
|
130
139
|
|
@@ -139,8 +148,20 @@ module Tap
|
|
139
148
|
Dir.chdir(dir, &block)
|
140
149
|
end
|
141
150
|
|
142
|
-
#
|
143
|
-
#
|
151
|
+
# Prepares the input path by making the parent directory for path. If a
|
152
|
+
# block is given, a file is created at path and passed to it; in this
|
153
|
+
# way files with non-existant parent directories are readily made.
|
154
|
+
#
|
155
|
+
# Returns path.
|
156
|
+
def prepare(path, &block)
|
157
|
+
dirname = File.dirname(path)
|
158
|
+
FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
|
159
|
+
File.open(path, "w", &block) if block_given?
|
160
|
+
path
|
161
|
+
end
|
162
|
+
|
163
|
+
# The path root type indicating windows, *nix, or some unknown style of
|
164
|
+
# filepaths (:win, :nix, :unknown).
|
144
165
|
def path_root_type
|
145
166
|
@path_root_type ||= case
|
146
167
|
when RUBY_PLATFORM =~ /mswin/ && File.expand_path(".") =~ WIN_ROOT_PATTERN then :win
|
@@ -149,25 +170,24 @@ module Tap
|
|
149
170
|
end
|
150
171
|
end
|
151
172
|
|
152
|
-
# Returns true if the input path appears to be an expanded path,
|
153
|
-
#
|
173
|
+
# Returns true if the input path appears to be an expanded path, based on
|
174
|
+
# Root.path_root_type.
|
154
175
|
#
|
155
|
-
# If root_type == :win returns true if the path matches
|
156
|
-
# WIN_ROOT_PATTERN.
|
176
|
+
# If root_type == :win returns true if the path matches WIN_ROOT_PATTERN.
|
157
177
|
#
|
158
|
-
# Root.
|
159
|
-
# Root.
|
160
|
-
# Root.
|
161
|
-
# Root.
|
178
|
+
# Root.expanded?('C:/path') # => true
|
179
|
+
# Root.expanded?('c:/path') # => true
|
180
|
+
# Root.expanded?('D:/path') # => true
|
181
|
+
# Root.expanded?('path') # => false
|
162
182
|
#
|
163
|
-
# If root_type == :nix, then expanded? returns true if
|
164
|
-
#
|
183
|
+
# If root_type == :nix, then expanded? returns true if the path begins
|
184
|
+
# with '/'.
|
165
185
|
#
|
166
|
-
# Root.
|
167
|
-
# Root.
|
186
|
+
# Root.expanded?('/path') # => true
|
187
|
+
# Root.expanded?('path') # => false
|
168
188
|
#
|
169
|
-
# Otherwise
|
170
|
-
def
|
189
|
+
# Otherwise expanded? always returns nil.
|
190
|
+
def expanded?(path, root_type=path_root_type)
|
171
191
|
case root_type
|
172
192
|
when :win
|
173
193
|
path =~ WIN_ROOT_PATTERN ? true : false
|
@@ -178,6 +198,17 @@ module Tap
|
|
178
198
|
end
|
179
199
|
end
|
180
200
|
|
201
|
+
# Trivial indicates when a path does not have content to load. Returns
|
202
|
+
# true if the file at path is empty, non-existant, a directory, or nil.
|
203
|
+
def trivial?(path)
|
204
|
+
path == nil || !File.file?(path) || File.size(path) == 0
|
205
|
+
end
|
206
|
+
|
207
|
+
# Empty returns true when dir is an existing directory that has no files.
|
208
|
+
def empty?(dir)
|
209
|
+
File.directory?(dir) && (Dir.entries(dir) - ['.', '..']).empty?
|
210
|
+
end
|
211
|
+
|
181
212
|
# Minimizes a set of paths to the set of shortest basepaths that unqiuely
|
182
213
|
# identify the paths. The path extension and versions are removed from
|
183
214
|
# the basepath if possible. For example:
|
@@ -267,8 +298,9 @@ module Tap
|
|
267
298
|
end
|
268
299
|
end
|
269
300
|
|
270
|
-
# Returns true if the mini_path matches path. Matching logic
|
271
|
-
#
|
301
|
+
# Returns true if the mini_path matches path. Matching logic reverses
|
302
|
+
# that of minimize:
|
303
|
+
#
|
272
304
|
# * a match occurs when path ends with mini_path
|
273
305
|
# * if mini_path doesn't specify an extension, then mini_path
|
274
306
|
# must only match path up to the path extension
|
@@ -324,11 +356,12 @@ module Tap
|
|
324
356
|
# windows '\' Root.split('C:\path\to\file') # => ["C:", "path", "to", "file"]
|
325
357
|
# *nix '/' Root.split('/path/to/file') # => ["", "path", "to", "file"]
|
326
358
|
#
|
327
|
-
# The path is always expanded relative to the expand_dir; so '.' and
|
328
|
-
# resolved. However, unless expand_path == true, only the
|
329
|
-
# to the expand_dir are returned.
|
359
|
+
# The path is always expanded relative to the expand_dir; so '.' and
|
360
|
+
# '..' are resolved. However, unless expand_path == true, only the
|
361
|
+
# segments relative to the expand_dir are returned.
|
330
362
|
#
|
331
|
-
# On windows (note that expanding paths allows the use of slashes or
|
363
|
+
# On windows (note that expanding paths allows the use of slashes or
|
364
|
+
# backslashes):
|
332
365
|
#
|
333
366
|
# Dir.pwd # => 'C:/'
|
334
367
|
# Root.split('path\to\..\.\to\file') # => ["C:", "path", "to", "file"]
|
@@ -365,6 +398,7 @@ module Tap
|
|
365
398
|
#
|
366
399
|
# "./path"
|
367
400
|
# "//path"
|
401
|
+
#
|
368
402
|
def min_join(dir, path) # :nodoc:
|
369
403
|
case dir
|
370
404
|
when "." then path
|
@@ -407,89 +441,99 @@ module Tap
|
|
407
441
|
end
|
408
442
|
|
409
443
|
end
|
410
|
-
|
444
|
+
|
445
|
+
include Configurable
|
411
446
|
include Support::Versions
|
412
|
-
|
413
|
-
|
447
|
+
|
414
448
|
# The root directory.
|
415
449
|
config_attr(:root, '.', :writer => false)
|
416
450
|
|
417
|
-
# A hash of (alias, relative path) pairs for aliased
|
418
|
-
|
451
|
+
# A hash of (alias, relative path) pairs for aliased paths relative
|
452
|
+
# to root.
|
453
|
+
config_attr(:relative_paths, {}, :writer => false)
|
419
454
|
|
420
455
|
# A hash of (alias, relative path) pairs for aliased absolute paths.
|
421
456
|
config_attr(:absolute_paths, {}, :reader => false, :writer => false)
|
422
457
|
|
423
|
-
# A hash of (alias, expanded path) pairs for
|
458
|
+
# A hash of (alias, expanded path) pairs for expanded relative and
|
459
|
+
# absolute paths.
|
424
460
|
attr_reader :paths
|
425
461
|
|
426
462
|
# The filesystem root, inferred from self.root
|
427
463
|
# (ex '/' on *nix or something like 'C:/' on Windows).
|
428
464
|
attr_reader :path_root
|
429
465
|
|
430
|
-
# Creates a new Root with the given root directory, aliased
|
466
|
+
# Creates a new Root with the given root directory, aliased relative paths
|
431
467
|
# and absolute paths. By default root is the present working directory
|
432
|
-
# and no aliased
|
433
|
-
def initialize(root=Dir.pwd,
|
434
|
-
assign_paths(root,
|
435
|
-
@config = self.class.configurations
|
468
|
+
# and no aliased relative or absolute paths are specified.
|
469
|
+
def initialize(root=Dir.pwd, relative_paths={}, absolute_paths={})
|
470
|
+
assign_paths(root, relative_paths, absolute_paths)
|
471
|
+
@config = DelegateHash.new(self.class.configurations, {}, self)
|
436
472
|
end
|
437
473
|
|
438
474
|
# Sets the root directory. All paths are reassigned accordingly.
|
439
475
|
def root=(path)
|
440
|
-
assign_paths(path,
|
476
|
+
assign_paths(path, relative_paths, absolute_paths)
|
441
477
|
end
|
442
478
|
|
443
|
-
# Sets the
|
444
|
-
# and cannot be set using this method (use root= instead).
|
479
|
+
# Sets the relative_paths to those provided. 'root' and :root are reserved
|
480
|
+
# aliases and cannot be set using this method (use root= instead).
|
445
481
|
#
|
446
|
-
#
|
447
|
-
#
|
448
|
-
#
|
449
|
-
|
450
|
-
|
482
|
+
# r = Tap::Root.new
|
483
|
+
# r['alt'] # => File.join(r.root, 'alt')
|
484
|
+
# r.relative_paths = {'alt' => 'dir'}
|
485
|
+
# r['alt'] # => File.join(r.root, 'dir')
|
486
|
+
#
|
487
|
+
def relative_paths=(paths)
|
488
|
+
paths = Validation::HASH[paths]
|
489
|
+
assign_paths(root, paths, absolute_paths)
|
451
490
|
end
|
452
491
|
|
453
492
|
# Sets the absolute paths to those provided. 'root' and :root are reserved
|
454
|
-
#
|
493
|
+
# aliases and cannot be set using this method (use root= instead).
|
494
|
+
#
|
495
|
+
# r = Tap::Root.new
|
496
|
+
# r['abs'] # => File.join(r.root, 'abs')
|
497
|
+
# r.absolute_paths = {'abs' => '/path/to/dir'}
|
498
|
+
# r['abs'] # => '/path/to/dir'
|
455
499
|
#
|
456
|
-
# r['abs'] # => File.join(r.root, 'abs')
|
457
|
-
# r.absolute_paths = {'abs' => '/path/to/dir'}
|
458
|
-
# r['abs'] # => '/path/to/dir'
|
459
500
|
def absolute_paths=(paths)
|
460
|
-
|
501
|
+
paths = Validation::HASH[paths]
|
502
|
+
assign_paths(root, relative_paths, paths)
|
461
503
|
end
|
462
504
|
|
463
505
|
# Returns the absolute paths registered with self.
|
464
506
|
def absolute_paths
|
465
507
|
abs_paths = {}
|
466
|
-
paths.each do |
|
467
|
-
|
508
|
+
paths.each do |als, path|
|
509
|
+
unless relative_paths.include?(als) || als.to_s == 'root'
|
510
|
+
abs_paths[als] = path
|
511
|
+
end
|
468
512
|
end
|
469
513
|
abs_paths
|
470
514
|
end
|
471
515
|
|
472
|
-
# Sets an alias for the
|
473
|
-
#
|
474
|
-
#
|
475
|
-
# second syntax.
|
516
|
+
# Sets an alias for the path relative to the root directory. The aliases
|
517
|
+
# 'root' and :root cannot be set with this method (use root= instead).
|
518
|
+
# Absolute filepaths can be set using the second syntax.
|
476
519
|
#
|
477
|
-
#
|
478
|
-
#
|
479
|
-
#
|
520
|
+
# r = Root.new '/root_dir'
|
521
|
+
# r[:dir] = 'path/to/dir'
|
522
|
+
# r[:dir] # => '/root_dir/path/to/dir'
|
480
523
|
#
|
481
|
-
#
|
482
|
-
#
|
524
|
+
# r[:abs, true] = '/abs/path/to/dir'
|
525
|
+
# r[:abs] # => '/abs/path/to/dir'
|
483
526
|
#
|
484
527
|
#--
|
485
|
-
# Implementation
|
528
|
+
# Implementation Note:
|
529
|
+
#
|
486
530
|
# The syntax for setting an absolute filepath requires an odd use []=.
|
487
531
|
# In fact the method recieves the arguments (:dir, true, '/abs/path/to/dir')
|
488
532
|
# rather than (:dir, '/abs/path/to/dir', true), meaning that internally path
|
489
533
|
# and absolute are switched when setting an absolute filepath.
|
490
|
-
|
491
|
-
def []=(
|
492
|
-
raise ArgumentError, "
|
534
|
+
#
|
535
|
+
def []=(als, path, absolute=false)
|
536
|
+
raise ArgumentError, "the alias #{als.inspect} is reserved" if als.to_s == 'root'
|
493
537
|
|
494
538
|
# switch the paths if absolute was provided
|
495
539
|
unless absolute == false
|
@@ -500,84 +544,106 @@ module Tap
|
|
500
544
|
|
501
545
|
case
|
502
546
|
when path.nil?
|
503
|
-
@
|
504
|
-
@paths.delete(
|
547
|
+
@relative_paths.delete(als)
|
548
|
+
@paths.delete(als)
|
505
549
|
when absolute
|
506
|
-
@
|
507
|
-
@paths[
|
550
|
+
@relative_paths.delete(als)
|
551
|
+
@paths[als] = File.expand_path(path)
|
508
552
|
else
|
509
|
-
@
|
510
|
-
@paths[
|
553
|
+
@relative_paths[als] = path
|
554
|
+
@paths[als] = File.expand_path(File.join(root, path))
|
511
555
|
end
|
512
556
|
end
|
513
557
|
|
514
|
-
# Returns the expanded path for the specified alias. If the alias
|
515
|
-
#
|
516
|
-
#
|
517
|
-
# directly.
|
558
|
+
# Returns the expanded path for the specified alias. If the alias has not
|
559
|
+
# been set, then the path is inferred to be 'root/als'. Expanded paths
|
560
|
+
# are returned directly.
|
518
561
|
#
|
519
|
-
#
|
520
|
-
#
|
562
|
+
# r = Root.new '/root_dir', :dir => 'path/to/dir'
|
563
|
+
# r[:dir] # => '/root_dir/path/to/dir'
|
521
564
|
#
|
522
|
-
#
|
523
|
-
#
|
524
|
-
#
|
565
|
+
# r.path_root # => '/'
|
566
|
+
# r['relative/path'] # => '/root_dir/relative/path'
|
567
|
+
# r['/expanded/path'] # => '/expanded/path'
|
525
568
|
#
|
526
|
-
def [](
|
527
|
-
path = self.paths[
|
569
|
+
def [](als)
|
570
|
+
path = self.paths[als]
|
528
571
|
return path unless path == nil
|
529
572
|
|
530
|
-
|
531
|
-
Root.
|
573
|
+
als = als.to_s
|
574
|
+
Root.expanded?(als) ? als : File.expand_path(File.join(root, als))
|
532
575
|
end
|
533
576
|
|
534
|
-
#
|
535
|
-
|
536
|
-
|
537
|
-
File.expand_path(File.join(self[
|
577
|
+
# Resolves the specified alias, joins the paths together, and expands the
|
578
|
+
# resulting filepath.
|
579
|
+
def filepath(als, *paths)
|
580
|
+
File.expand_path(File.join(self[als], *paths))
|
538
581
|
end
|
539
582
|
|
540
583
|
# Retrieves the filepath relative to the path of the specified alias.
|
541
|
-
def relative_filepath(
|
542
|
-
Root.relative_filepath(self[
|
584
|
+
def relative_filepath(als, path)
|
585
|
+
Root.relative_filepath(self[als], path)
|
586
|
+
end
|
587
|
+
|
588
|
+
# Same as filepath but raises an error if the result is not a subpath of
|
589
|
+
# the aliased directory.
|
590
|
+
def subpath(als, *paths)
|
591
|
+
dir = self[als]
|
592
|
+
path = filepath(als, *paths)
|
593
|
+
|
594
|
+
if path.rindex(dir, 0) != 0
|
595
|
+
raise "not a subpath: #{path} (#{dir})"
|
596
|
+
end
|
597
|
+
|
598
|
+
path
|
543
599
|
end
|
544
600
|
|
545
|
-
# Generates a
|
546
|
-
#
|
547
|
-
# to the
|
601
|
+
# Generates a filepath translated from the aliased source dir to the
|
602
|
+
# aliased target dir. Raises an error if the filepath is not relative
|
603
|
+
# to the source dir.
|
548
604
|
#
|
549
|
-
#
|
550
|
-
#
|
551
|
-
|
552
|
-
|
605
|
+
# r = Tap::Root.new '/root_dir'
|
606
|
+
# path = r.filepath(:in, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
|
607
|
+
# r.translate(path, :in, :out) # => '/root_dir/out/path/to/file.txt'
|
608
|
+
#
|
609
|
+
def translate(path, source_als, target_als)
|
610
|
+
Root.translate(path, self[source_als], self[target_als])
|
553
611
|
end
|
554
612
|
|
555
|
-
# Lists all files
|
556
|
-
# should
|
557
|
-
#
|
558
|
-
|
613
|
+
# Lists all files along the aliased path matching the input patterns.
|
614
|
+
# Patterns should join with the aliased path make valid globs for
|
615
|
+
# Dir.glob. If no patterns are specified, glob returns all paths
|
616
|
+
# matching 'als/**/*'.
|
617
|
+
def glob(als, *patterns)
|
559
618
|
patterns << "**/*" if patterns.empty?
|
560
|
-
patterns.collect! {|pattern| filepath(
|
619
|
+
patterns.collect! {|pattern| filepath(als, pattern)}
|
561
620
|
Root.glob(*patterns)
|
562
621
|
end
|
563
622
|
|
564
|
-
# Lists all versions of
|
565
|
-
# If no patterns are specified, then all versions of
|
566
|
-
|
567
|
-
|
623
|
+
# Lists all versions of path in the aliased dir matching the version
|
624
|
+
# patterns. If no patterns are specified, then all versions of path
|
625
|
+
# will be returned.
|
626
|
+
def vglob(als, path, *vpatterns)
|
627
|
+
Root.vglob(filepath(als, path), *vpatterns)
|
628
|
+
end
|
629
|
+
|
630
|
+
# Changes pwd to the specified directory using Root.chdir.
|
631
|
+
def chdir(als, mkdir=false, &block)
|
632
|
+
Root.chdir(self[als], mkdir, &block)
|
568
633
|
end
|
569
634
|
|
570
|
-
#
|
571
|
-
|
572
|
-
|
635
|
+
# Constructs a path from the inputs (using filepath) and prepares it using
|
636
|
+
# Root.prepare. Returns the path.
|
637
|
+
def prepare(als, *paths, &block)
|
638
|
+
Root.prepare(filepath(als, *paths), &block)
|
573
639
|
end
|
574
640
|
|
575
641
|
private
|
576
642
|
|
577
|
-
# reassigns all paths with the input root,
|
578
|
-
def assign_paths(root,
|
643
|
+
# reassigns all paths with the input root, relative_paths, and absolute_paths
|
644
|
+
def assign_paths(root, relative_paths, absolute_paths)
|
579
645
|
@root = File.expand_path(root)
|
580
|
-
@
|
646
|
+
@relative_paths = {}
|
581
647
|
@paths = {'root' => @root, :root => @root}
|
582
648
|
|
583
649
|
@path_root = File.dirname(@root)
|
@@ -585,7 +651,7 @@ module Tap
|
|
585
651
|
@path_root = parent
|
586
652
|
end
|
587
653
|
|
588
|
-
|
654
|
+
relative_paths.each_pair {|dir, path| self[dir] = path }
|
589
655
|
absolute_paths.each_pair {|dir, path| self[dir, true] = path }
|
590
656
|
end
|
591
657
|
|