tap 0.7.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|