tap 0.7.9
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/MIT-LICENSE +21 -0
- data/README +71 -0
- data/Rakefile +117 -0
- data/bin/tap +63 -0
- data/lib/tap.rb +15 -0
- data/lib/tap/app.rb +739 -0
- data/lib/tap/file_task.rb +354 -0
- data/lib/tap/generator.rb +29 -0
- data/lib/tap/generator/generators/config/USAGE +0 -0
- data/lib/tap/generator/generators/config/config_generator.rb +23 -0
- data/lib/tap/generator/generators/config/templates/config.erb +2 -0
- data/lib/tap/generator/generators/file_task/USAGE +0 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +21 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +27 -0
- data/lib/tap/generator/generators/file_task/templates/test.erb +12 -0
- data/lib/tap/generator/generators/root/USAGE +0 -0
- data/lib/tap/generator/generators/root/root_generator.rb +36 -0
- data/lib/tap/generator/generators/root/templates/Rakefile +48 -0
- data/lib/tap/generator/generators/root/templates/app.yml +19 -0
- data/lib/tap/generator/generators/root/templates/config/process_tap_request.yml +4 -0
- data/lib/tap/generator/generators/root/templates/lib/process_tap_request.rb +26 -0
- data/lib/tap/generator/generators/root/templates/public/images/nav.jpg +0 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/color.css +57 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/layout.css +108 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/normalize.css +40 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/typography.css +21 -0
- data/lib/tap/generator/generators/root/templates/server/config/environment.rb +60 -0
- data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake +5 -0
- data/lib/tap/generator/generators/root/templates/server/test/test_helper.rb +53 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +4 -0
- data/lib/tap/generator/generators/task/USAGE +0 -0
- data/lib/tap/generator/generators/task/task_generator.rb +21 -0
- data/lib/tap/generator/generators/task/templates/task.erb +21 -0
- data/lib/tap/generator/generators/task/templates/test.erb +29 -0
- data/lib/tap/generator/generators/workflow/USAGE +0 -0
- data/lib/tap/generator/generators/workflow/templates/task.erb +16 -0
- data/lib/tap/generator/generators/workflow/templates/test.erb +7 -0
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +21 -0
- data/lib/tap/generator/options.rb +26 -0
- data/lib/tap/generator/usage.rb +26 -0
- data/lib/tap/root.rb +275 -0
- data/lib/tap/script/console.rb +7 -0
- data/lib/tap/script/destroy.rb +8 -0
- data/lib/tap/script/generate.rb +8 -0
- data/lib/tap/script/run.rb +111 -0
- data/lib/tap/script/server.rb +12 -0
- data/lib/tap/support/audit.rb +415 -0
- data/lib/tap/support/batch_queue.rb +165 -0
- data/lib/tap/support/combinator.rb +114 -0
- data/lib/tap/support/logger.rb +91 -0
- data/lib/tap/support/rap.rb +38 -0
- data/lib/tap/support/run_error.rb +20 -0
- data/lib/tap/support/template.rb +81 -0
- data/lib/tap/support/templater.rb +155 -0
- data/lib/tap/support/versions.rb +63 -0
- data/lib/tap/task.rb +448 -0
- data/lib/tap/test.rb +320 -0
- data/lib/tap/test/env_vars.rb +16 -0
- data/lib/tap/test/inference_methods.rb +298 -0
- data/lib/tap/test/subset_methods.rb +260 -0
- data/lib/tap/version.rb +3 -0
- data/lib/tap/workflow.rb +73 -0
- data/test/app/config/addition_template.yml +6 -0
- data/test/app/config/batch.yml +2 -0
- data/test/app/config/empty.yml +0 -0
- data/test/app/config/erb.yml +1 -0
- data/test/app/config/template.yml +6 -0
- data/test/app/config/version-0.1.yml +1 -0
- data/test/app/config/version.yml +1 -0
- data/test/app/lib/app_test_task.rb +2 -0
- data/test/app_class_test.rb +33 -0
- data/test/app_test.rb +1372 -0
- data/test/file_task/config/batch.yml +2 -0
- data/test/file_task/config/configured.yml +1 -0
- data/test/file_task/old_file_one.txt +0 -0
- data/test/file_task/old_file_two.txt +0 -0
- data/test/file_task_test.rb +1041 -0
- data/test/root/alt_lib/alt_module.rb +4 -0
- data/test/root/lib/absolute_alt_filepath.rb +2 -0
- data/test/root/lib/alternative_filepath.rb +2 -0
- data/test/root/lib/another_module.rb +2 -0
- data/test/root/lib/nested/some_module.rb +4 -0
- data/test/root/lib/no_module_included.rb +0 -0
- data/test/root/lib/some/module.rb +4 -0
- data/test/root/lib/some_class.rb +2 -0
- data/test/root/lib/some_module.rb +3 -0
- data/test/root/load_path/load_path_module.rb +2 -0
- data/test/root/load_path/skip_module.rb +2 -0
- data/test/root/mtime/older.txt +0 -0
- data/test/root/unload/full_path.rb +2 -0
- data/test/root/unload/loaded_by_nested.rb +2 -0
- data/test/root/unload/nested/nested_load.rb +6 -0
- data/test/root/unload/nested/nested_with_ext.rb +4 -0
- data/test/root/unload/nested/relative_path.rb +4 -0
- data/test/root/unload/older.rb +2 -0
- data/test/root/unload/unload_base.rb +9 -0
- data/test/root/versions/another.yml +0 -0
- data/test/root/versions/file-0.1.2.yml +0 -0
- data/test/root/versions/file-0.1.yml +0 -0
- data/test/root/versions/file.yml +0 -0
- data/test/root_test.rb +483 -0
- data/test/support/audit_test.rb +449 -0
- data/test/support/batch_queue_test.rb +320 -0
- data/test/support/combinator_test.rb +249 -0
- data/test/support/logger_test.rb +31 -0
- data/test/support/template_test.rb +122 -0
- data/test/support/templater/erb.txt +2 -0
- data/test/support/templater/erb.yml +2 -0
- data/test/support/templater/somefile.txt +2 -0
- data/test/support/templater_test.rb +192 -0
- data/test/support/versions_test.rb +71 -0
- data/test/tap_test_helper.rb +4 -0
- data/test/tap_test_suite.rb +4 -0
- data/test/task/config/batch.yml +2 -0
- data/test/task/config/batched.yml +2 -0
- data/test/task/config/configured.yml +1 -0
- data/test/task/config/example.yml +1 -0
- data/test/task/config/overriding.yml +2 -0
- data/test/task/config/task_with_config.yml +1 -0
- data/test/task/config/template.yml +4 -0
- data/test/task_class_test.rb +118 -0
- data/test/task_execute_test.rb +233 -0
- data/test/task_test.rb +424 -0
- data/test/test/inference_methods/test_assert_expected/expected/file.txt +1 -0
- data/test/test/inference_methods/test_assert_expected/expected/folder/file.txt +1 -0
- data/test/test/inference_methods/test_assert_expected/input/file.txt +1 -0
- data/test/test/inference_methods/test_assert_expected/input/folder/file.txt +1 -0
- data/test/test/inference_methods/test_assert_files_exist/input/input_1.txt +0 -0
- data/test/test/inference_methods/test_assert_files_exist/input/input_2.txt +0 -0
- data/test/test/inference_methods/test_file_compare/expected/output_1.txt +3 -0
- data/test/test/inference_methods/test_file_compare/expected/output_2.txt +1 -0
- data/test/test/inference_methods/test_file_compare/input/input_1.txt +3 -0
- data/test/test/inference_methods/test_file_compare/input/input_2.txt +3 -0
- data/test/test/inference_methods/test_infer_glob/expected/file.yml +0 -0
- data/test/test/inference_methods/test_infer_glob/expected/file_1.txt +0 -0
- data/test/test/inference_methods/test_infer_glob/expected/file_2.txt +0 -0
- data/test/test/inference_methods/test_yml_compare/expected/output_1.yml +6 -0
- data/test/test/inference_methods/test_yml_compare/expected/output_2.yml +6 -0
- data/test/test/inference_methods/test_yml_compare/input/input_1.yml +4 -0
- data/test/test/inference_methods/test_yml_compare/input/input_2.yml +4 -0
- data/test/test/inference_methods_test.rb +311 -0
- data/test/test/subset_methods_test.rb +115 -0
- data/test/test_test.rb +233 -0
- data/test/workflow_test.rb +108 -0
- metadata +274 -0
data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# This clears the built-in prerequisite 'db:test:prepare' from all the test tasks, allowing
|
|
2
|
+
# rails to run without a database (updated from "Rails Recipies":http://media.pragprog.com/titles/fr_rr/NoDatabase.pdf)
|
|
3
|
+
['test:units', 'test:functionals', 'test:recent', 'test:integration', 'test:uncommitted'].each do |name|
|
|
4
|
+
Rake::Task[name].prerequisites.clear
|
|
5
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
ENV["RAILS_ENV" ] = "test"
|
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../config/environment" )
|
|
3
|
+
|
|
4
|
+
#################
|
|
5
|
+
# Here I've copied the contents of 'test_help' so I could remove the dependencies
|
|
6
|
+
# causing rails to raise errors if running without a database.
|
|
7
|
+
#
|
|
8
|
+
# require 'test_help'
|
|
9
|
+
require_dependency 'application'
|
|
10
|
+
|
|
11
|
+
# Make double-sure the RAILS_ENV is set to test,
|
|
12
|
+
# so fixtures are loaded to the right database
|
|
13
|
+
silence_warnings { RAILS_ENV = "test" }
|
|
14
|
+
|
|
15
|
+
require 'test/unit'
|
|
16
|
+
#require 'active_record/fixtures'
|
|
17
|
+
require 'action_controller/test_process'
|
|
18
|
+
require 'action_controller/integration'
|
|
19
|
+
#require 'action_web_service/test_invoke'
|
|
20
|
+
require 'breakpoint'
|
|
21
|
+
|
|
22
|
+
#Test::Unit::TestCase.fixture_path = RAILS_ROOT + "/test/fixtures/"
|
|
23
|
+
#ActionController::IntegrationTest.fixture_path = Test::Unit::TestCase.fixture_path
|
|
24
|
+
|
|
25
|
+
#def create_fixtures(*table_names)
|
|
26
|
+
# Fixtures.create_fixtures(RAILS_ROOT + "/test/fixtures", table_names)
|
|
27
|
+
#end
|
|
28
|
+
#################
|
|
29
|
+
|
|
30
|
+
class Test::Unit::TestCase
|
|
31
|
+
# Transactional fixtures accelerate your tests by wrapping each test method
|
|
32
|
+
# in a transaction that's rolled back on completion. This ensures that the
|
|
33
|
+
# test database remains unchanged so your fixtures don't have to be reloaded
|
|
34
|
+
# between every test method. Fewer database queries means faster tests.
|
|
35
|
+
#
|
|
36
|
+
# Read Mike Clark's excellent walkthrough at
|
|
37
|
+
# http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
|
|
38
|
+
#
|
|
39
|
+
# Every Active Record database supports transactions except MyISAM tables
|
|
40
|
+
# in MySQL. Turn off transactional fixtures in this case; however, if you
|
|
41
|
+
# don't care one way or the other, switching from MyISAM to InnoDB tables
|
|
42
|
+
# is recommended.
|
|
43
|
+
#self.use_transactional_fixtures = true
|
|
44
|
+
|
|
45
|
+
# Instantiated fixtures are slow, but give you @david where otherwise you
|
|
46
|
+
# would need people(:david). If you don't want to migrate your existing
|
|
47
|
+
# test cases which use the @david style and don't mind the speed hit (each
|
|
48
|
+
# instantiated fixtures translates to a database query per test method),
|
|
49
|
+
# then set this back to true.
|
|
50
|
+
#self.use_instantiated_fixtures = false
|
|
51
|
+
|
|
52
|
+
# Add more helper methods to be used by all tests here...
|
|
53
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Tap::Generator::Generators
|
|
2
|
+
class TaskGenerator < Rails::Generator::NamedBase # :nodoc:
|
|
3
|
+
def initialize(*args)
|
|
4
|
+
super(*args)
|
|
5
|
+
@destination_root = Tap::App.instance[:root]
|
|
6
|
+
@app = Tap::App.instance
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def manifest
|
|
10
|
+
record do |m|
|
|
11
|
+
lib_path = @app.relative_filepath(:root, @app[:lib])
|
|
12
|
+
m.directory File.join(lib_path, class_path)
|
|
13
|
+
m.template "task.erb", File.join(lib_path, class_name.underscore + ".rb")
|
|
14
|
+
|
|
15
|
+
test_path = @app.relative_filepath(:root, @app[:test])
|
|
16
|
+
m.directory File.join(test_path, class_path)
|
|
17
|
+
m.template "test.erb", File.join(test_path, class_name.underscore + "_test.rb")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# == Documentation
|
|
2
|
+
#
|
|
3
|
+
# === Command Line Usage
|
|
4
|
+
# Replace with your command line usage instructions
|
|
5
|
+
#
|
|
6
|
+
class <%= class_name %> < Tap::Task
|
|
7
|
+
|
|
8
|
+
def process(input)
|
|
9
|
+
# The process logic goes here.
|
|
10
|
+
|
|
11
|
+
# You can access configurations as symbols within the config hash
|
|
12
|
+
message = "#{config[:greeting]} #{input}!"
|
|
13
|
+
|
|
14
|
+
# Use log to record information
|
|
15
|
+
log :message, message
|
|
16
|
+
|
|
17
|
+
# The return of process is the task output
|
|
18
|
+
message
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), '<%= '../' * class_nesting_depth %>tap_test_helper.rb')
|
|
2
|
+
require '<%= class_path.empty? ? file_name : File.join(class_path, file_name) %>'
|
|
3
|
+
|
|
4
|
+
class <%= class_name %>Test < Test::Unit::TestCase
|
|
5
|
+
acts_as_tap_test
|
|
6
|
+
|
|
7
|
+
def test_greeting
|
|
8
|
+
t = <%= class_name %>.new nil, :greeting => 'hello'
|
|
9
|
+
|
|
10
|
+
with_options(:quiet => true) do
|
|
11
|
+
t.execute("from me", "to you")
|
|
12
|
+
|
|
13
|
+
assert_equal({:greeting => 'hello'}, t.config)
|
|
14
|
+
assert_outputs(t => ["hello from me!", "hello to you!"])
|
|
15
|
+
|
|
16
|
+
# check the audit trail for each input
|
|
17
|
+
assert_audits(t.results,
|
|
18
|
+
0 => [[nil, 'from me'], [t, 'hello from me!']],
|
|
19
|
+
1 => [[nil, 'to you'], [t, 'hello to you!']])
|
|
20
|
+
|
|
21
|
+
# an alternate way of checking audits
|
|
22
|
+
check_audit(t.results[1]) do
|
|
23
|
+
'to you'._from nil
|
|
24
|
+
'hello to you!'._from t
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# == Documentation
|
|
2
|
+
#
|
|
3
|
+
# === Command Line Usage
|
|
4
|
+
# Replace with your command line usage instructions
|
|
5
|
+
#
|
|
6
|
+
class <%= class_name %> < Tap::Workflow
|
|
7
|
+
protected
|
|
8
|
+
|
|
9
|
+
def workflow
|
|
10
|
+
# Define the workflow entry and exit points,
|
|
11
|
+
# as well as the workflow logic.
|
|
12
|
+
self.entry_point = Task.new
|
|
13
|
+
|
|
14
|
+
# app.sequence(entry_point, 'another/task')
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Tap::Generator::Generators
|
|
2
|
+
class WorkflowGenerator < Rails::Generator::NamedBase # :nodoc:
|
|
3
|
+
def initialize(*args)
|
|
4
|
+
super(*args)
|
|
5
|
+
@destination_root = Tap::App.instance[:root]
|
|
6
|
+
@app = Tap::App.instance
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def manifest
|
|
10
|
+
record do |m|
|
|
11
|
+
lib_path = @app.relative_filepath(:root, @app[:lib])
|
|
12
|
+
m.directory File.join(lib_path, class_path)
|
|
13
|
+
m.template "task.erb", File.join(lib_path, class_name.underscore + ".rb")
|
|
14
|
+
|
|
15
|
+
test_path = @app.relative_filepath(:root, @app[:test])
|
|
16
|
+
m.directory File.join(test_path, class_path)
|
|
17
|
+
m.template "test.erb", File.join(test_path, class_name.underscore + "_test.rb")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Tap
|
|
2
|
+
module Generator
|
|
3
|
+
module Options # :nodoc:
|
|
4
|
+
protected
|
|
5
|
+
|
|
6
|
+
# Adapted from code in 'rails/rails_generator/options.rb'
|
|
7
|
+
def add_general_options!(opt)
|
|
8
|
+
opt.separator ''
|
|
9
|
+
opt.separator 'General Options:'
|
|
10
|
+
|
|
11
|
+
opt.on('-h', '--help', 'Show this help message and quit.') { |v| options[:help] = v }
|
|
12
|
+
opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v }
|
|
13
|
+
opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force }
|
|
14
|
+
opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip }
|
|
15
|
+
opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v }
|
|
16
|
+
opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v }
|
|
17
|
+
opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do
|
|
18
|
+
options[:svn] = `svn status`.inject({}) do |opt, e|
|
|
19
|
+
opt[e.chomp[7..-1]] = true
|
|
20
|
+
opt
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'tap/generator/options'
|
|
2
|
+
|
|
3
|
+
module Tap
|
|
4
|
+
module Generator
|
|
5
|
+
module Usage # :nodoc:
|
|
6
|
+
include Options
|
|
7
|
+
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
# Adapted from code in 'rails/rails_generator/scripts.rb'
|
|
11
|
+
def usage_message
|
|
12
|
+
usage = "\nInstalled Generators\n"
|
|
13
|
+
Rails::Generator::Base.sources.each do |source|
|
|
14
|
+
label = source.label.to_s.capitalize
|
|
15
|
+
names = source.names
|
|
16
|
+
usage << " #{label}: #{names.join(', ')}\n" unless names.empty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
usage << <<end_blurb
|
|
20
|
+
|
|
21
|
+
end_blurb
|
|
22
|
+
return usage
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/tap/root.rb
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'tap/support/versions'
|
|
3
|
+
|
|
4
|
+
# string.rb is required for camelizing and underscoring strings
|
|
5
|
+
# requiring just this file rather than the full gem results in
|
|
6
|
+
# noticably faster startup
|
|
7
|
+
require 'active_support/core_ext/string'
|
|
8
|
+
|
|
9
|
+
module Tap
|
|
10
|
+
|
|
11
|
+
# Most applications that utilize files allow the definition of a root directory,
|
|
12
|
+
# allowing you to place the application directory wherever you like. Root takes
|
|
13
|
+
# this concept further, allowing you to define subdirectories, as well as manipulate
|
|
14
|
+
# files and filepaths within these directories. For example:
|
|
15
|
+
#
|
|
16
|
+
# root = Root.new 'root_dir', :input => 'in', :output => 'out'
|
|
17
|
+
#
|
|
18
|
+
# # work with directories
|
|
19
|
+
# root[:in] # => 'root_dir/in'
|
|
20
|
+
# root[:out] # => 'root_dir/out'
|
|
21
|
+
# root[:implicit] # => 'root_dir/implicit'
|
|
22
|
+
#
|
|
23
|
+
# # work with filepaths
|
|
24
|
+
# fp = root.filepath(:in, 'path/to/file.txt') # => 'root_dir/in/path/to/file.txt'
|
|
25
|
+
# root.relative_filepath(:in, fp) # => 'path/to/file.txt'
|
|
26
|
+
# root.translate(fp, :in, :out) # => 'root_dir/out/path/to/file.txt'
|
|
27
|
+
#
|
|
28
|
+
# # version filepaths
|
|
29
|
+
# root.version('path/to/config.yml', 1.0) # => 'path/to/config-1.0.yml'
|
|
30
|
+
# root.increment('path/to/config-1.0.yml', 0.1) # => 'path/to/config-1.1.yml'
|
|
31
|
+
# root.deversion('path/to/config-1.1.yml') # => ['path/to/config.yml', "1.1"]
|
|
32
|
+
#
|
|
33
|
+
# # work with modules...
|
|
34
|
+
# mp = root.module_filepath(:lib, 'Some::Module') # => 'root_dir/lib/some/module.rb'
|
|
35
|
+
#
|
|
36
|
+
# # loading them...
|
|
37
|
+
# Object.const_defined?(:Some) # => false
|
|
38
|
+
# root.lookup_module('Some::Module') # => Some::Module
|
|
39
|
+
# Some.const_defined?(:Module) # => true
|
|
40
|
+
#
|
|
41
|
+
# # and unloading them!
|
|
42
|
+
# root.unload_modules # => ["Some::Module"]
|
|
43
|
+
# Object.const_defined?(:Some) # => false
|
|
44
|
+
#
|
|
45
|
+
# === Notes
|
|
46
|
+
#
|
|
47
|
+
# Root internally stores paths for both relative directories and absolute paths in a
|
|
48
|
+
# hash for quick lookup using the dir keys. Root keeps a separate hash for the
|
|
49
|
+
# original directories used to create the paths, but this is not necessary for
|
|
50
|
+
# absolute paths. An absolute path can be distinguished from a directory by having
|
|
51
|
+
# an entry in paths but not directories.
|
|
52
|
+
class Root
|
|
53
|
+
class << self
|
|
54
|
+
include Support::Versions
|
|
55
|
+
|
|
56
|
+
# Returns the filepath of path relative to dir. If strict=true (the default) then
|
|
57
|
+
# relative_filepath raises an error if the path is not relative to dir.
|
|
58
|
+
#
|
|
59
|
+
# Root.relative_filepath('dir', "dir/path/to/file.txt") # => "path/to/file.txt"
|
|
60
|
+
#
|
|
61
|
+
def relative_filepath(dir, path, strict=true)
|
|
62
|
+
dir = File.expand_path(dir)
|
|
63
|
+
expanded = File.expand_path(path)
|
|
64
|
+
|
|
65
|
+
unless expanded[0...dir.length] == dir
|
|
66
|
+
raise "\n#{expanded}\nis not relative to:\n#{dir}" if strict
|
|
67
|
+
return path
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# use dir.length + 1 to remove a leading '/'. If dir.length + 1 >= expanded.length
|
|
71
|
+
# as in: relative_filepath('/path', '/path') then the first arg returns nil, and an
|
|
72
|
+
# empty string is returned
|
|
73
|
+
expanded[(dir.length+1)..-1] || ""
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Lists all unique paths matching the input glob patterns.
|
|
77
|
+
def glob(*patterns)
|
|
78
|
+
patterns.collect do |pattern|
|
|
79
|
+
Dir.glob(pattern)
|
|
80
|
+
end.flatten.uniq
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Lists all unique versions of path matching the glob version patterns. If no patterns
|
|
84
|
+
# are specified, then all versions of path will be returned.
|
|
85
|
+
def vglob(path, *vpatterns)
|
|
86
|
+
vpatterns << "*" if vpatterns.empty?
|
|
87
|
+
vpatterns.collect do |vpattern|
|
|
88
|
+
results = Dir.glob(version(path, vpattern))
|
|
89
|
+
|
|
90
|
+
# extra work to include the default version path for any version
|
|
91
|
+
results << path if vpattern == "*" && File.exists?(path)
|
|
92
|
+
results
|
|
93
|
+
end.flatten.uniq
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
#def package(target, *filepaths)
|
|
99
|
+
# options = filepaths.pop if filepaths.last.kind_of?(Hash)
|
|
100
|
+
#end
|
|
101
|
+
|
|
102
|
+
# glob for dirs, remove if empty. removes dir if empty on complete
|
|
103
|
+
# OR
|
|
104
|
+
# remove files older than a certain date
|
|
105
|
+
# remove all files
|
|
106
|
+
#
|
|
107
|
+
#def cleanup(dir)
|
|
108
|
+
#end
|
|
109
|
+
|
|
110
|
+
# iotask?
|
|
111
|
+
def parse_by_line(path, options={}, &block)
|
|
112
|
+
str = read(path, options.merge(:as => :txt))
|
|
113
|
+
lines = str.split(/\r?\n/)
|
|
114
|
+
lines.each do |line|
|
|
115
|
+
# don't yield empty lines because they split to [] (ie [nil, nil] for the block inputs)
|
|
116
|
+
yield(line.split(/#/, 2)) unless line.empty?
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
include Support::Versions
|
|
122
|
+
attr_reader :root, :directories, :paths
|
|
123
|
+
|
|
124
|
+
# Creates a new Root with the given root, directories, and absolute
|
|
125
|
+
# paths. By default root is the current working directory and no
|
|
126
|
+
# directories or absolute paths are specified.
|
|
127
|
+
def initialize(root=Dir.pwd, directories={}, absolute_paths={})
|
|
128
|
+
assign_paths(root, directories, absolute_paths)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Sets the root directory. All paths are reassigned accordingly.
|
|
132
|
+
def root=(path)
|
|
133
|
+
assign_paths(path, directories, absolute_paths)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Sets the directories to those provided. 'root' and :root are reserved directory
|
|
137
|
+
# keys and cannot be set using this method (use root= instead).
|
|
138
|
+
def directories=(dirs)
|
|
139
|
+
assign_paths(root, dirs, absolute_paths)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Sets the absolute paths to those provided. 'root' and :root are reserved directory
|
|
143
|
+
# keys and cannot be set using this method (use root= instead).
|
|
144
|
+
def absolute_paths=(paths)
|
|
145
|
+
assign_paths(root, directories, paths)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Returns the absolute paths registered with the Root.
|
|
149
|
+
def absolute_paths
|
|
150
|
+
abs_paths = {}
|
|
151
|
+
paths.each do |dir, path|
|
|
152
|
+
abs_paths[dir] = path unless directories.include?(dir) || dir.to_s == 'root'
|
|
153
|
+
end
|
|
154
|
+
abs_paths
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Sets the path to the specified directory, relative to the root directory.
|
|
158
|
+
# The root directory cannot be set with this method (use root= instead).
|
|
159
|
+
# Absolute filepaths can be set by specifying the absolute parameter.
|
|
160
|
+
#
|
|
161
|
+
# root[:dir] = 'path/to/dir'
|
|
162
|
+
# root[:root] # => 'root_dir'
|
|
163
|
+
# root[:dir] # => 'root_dir/path/to/dir'
|
|
164
|
+
#
|
|
165
|
+
# root[:abs, true] = '/abs/path/to/dir'
|
|
166
|
+
# root[:abs] # => '/abs/path/to/dir'
|
|
167
|
+
#
|
|
168
|
+
# Note - the syntax for setting an absolute filepath requires an odd use []=.
|
|
169
|
+
# In fact the method recieves the arguments (:dir, true, '/abs/path/to/dir')
|
|
170
|
+
# rather than (:dir, '/abs/path/to/dir', true), meaning that internally path
|
|
171
|
+
# and absolute are switched when setting an absolute filepath.
|
|
172
|
+
def []=(dir, path, absolute=false)
|
|
173
|
+
raise ArgumentError, "The directory key '#{dir}' is reserved." if dir.to_s == 'root'
|
|
174
|
+
|
|
175
|
+
# switch the paths if absolute was provided
|
|
176
|
+
unless absolute == false
|
|
177
|
+
switch = path
|
|
178
|
+
path = absolute
|
|
179
|
+
absolute = switch
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
case
|
|
183
|
+
when path.nil?
|
|
184
|
+
@directories.delete(dir)
|
|
185
|
+
@paths.delete(dir)
|
|
186
|
+
when absolute
|
|
187
|
+
@directories.delete(dir)
|
|
188
|
+
@paths[dir] = path
|
|
189
|
+
else
|
|
190
|
+
@directories[dir] = path
|
|
191
|
+
@paths[dir] = File.join(root, path)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Returns the path to the specified directory. If the path has not been set,
|
|
196
|
+
# then the path is inferred to be 'root/dir'.
|
|
197
|
+
def [](dir)
|
|
198
|
+
self.paths[dir] || File.join(root, dir.to_s)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Constructs filepaths relative to the path of the specfied directory.
|
|
202
|
+
def filepath(dir, *filename)
|
|
203
|
+
File.join(self[dir], *filename)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Retrieves filepath relative to the path of the specified directory.
|
|
207
|
+
def relative_filepath(dir, filepath, strict=true)
|
|
208
|
+
Root.relative_filepath(self[dir], filepath, strict)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Generates a target filepath translated from the input directory to the output directory.
|
|
212
|
+
#
|
|
213
|
+
# translate("/input/dir/path/to/input.txt") # => "/output/dir/path/to/input.txt"
|
|
214
|
+
def translate(filepath, input_dir, output_dir)
|
|
215
|
+
filepath(output_dir, relative_filepath(input_dir, filepath))
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Lists all files in the specified dir matching the input patterns. Patterns should be valid inputs
|
|
219
|
+
# for +Dir.glob+. If no patterns are specified, lists all files/folders matching '**/*'.
|
|
220
|
+
def glob(dir, *patterns)
|
|
221
|
+
patterns << "**/*" if patterns.empty?
|
|
222
|
+
patterns.collect! {|pattern| filepath(dir, pattern)}
|
|
223
|
+
Root.glob(*patterns)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Lists all versions of filename in the specified dir matching the version patterns.
|
|
227
|
+
# If no patterns are specified, then all versions of filename will be returned.
|
|
228
|
+
def vglob(dir, filename, *vpatterns)
|
|
229
|
+
Root.vglob(filepath(dir, filename), *vpatterns)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Opens the file specfied by dir and filename, then passes the open file to the input block
|
|
233
|
+
def open(dir, filename, mode_string="r", &block)
|
|
234
|
+
File.open(filepath(dir, filename), mode_string, &block)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Reads the specififed file
|
|
238
|
+
def read(dir, filename)
|
|
239
|
+
File.read( filepath(dir, filename) )
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
#
|
|
243
|
+
def parse_by_line(dir, filename, options={}, &block)
|
|
244
|
+
Root.parse_by_line( filepath(dir, filename), options, &block)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# The timestamp of the most recently modified file under dir. Returns nil
|
|
248
|
+
# if none of the listed files are relative to dir.
|
|
249
|
+
def mtime(dir, *filenames)
|
|
250
|
+
filenames.collect do |fname|
|
|
251
|
+
fname = relative_filepath(dir, fname, false)
|
|
252
|
+
path = filepath(dir, fname)
|
|
253
|
+
|
|
254
|
+
File.exists?(path) ? File.mtime(path) : nil
|
|
255
|
+
end.compact.max
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Makes all the directories specified in paths. Parent directories are created as needed.
|
|
259
|
+
def mkpaths
|
|
260
|
+
paths.values.each { |dir| FileUtils.mkdir_p(dir) }
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def assign_paths(root, directories, absolute_paths)
|
|
266
|
+
@root = root
|
|
267
|
+
@directories = {}
|
|
268
|
+
@paths = {'root' => root, :root => root}
|
|
269
|
+
|
|
270
|
+
directories.each_pair {|dir, path| self[dir] = path }
|
|
271
|
+
absolute_paths.each_pair {|dir, path| self[dir, true] = path }
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
end
|
|
275
|
+
end
|