tap 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Basic Overview +151 -0
- data/Command Reference +99 -0
- data/History +24 -0
- data/MIT-LICENSE +1 -1
- data/README +29 -57
- data/Rakefile +30 -37
- data/Tutorial +243 -191
- data/bin/tap +66 -35
- data/lib/tap.rb +47 -29
- data/lib/tap/app.rb +700 -342
- data/lib/tap/{script → cmd}/console.rb +0 -0
- data/lib/tap/{script → cmd}/destroy.rb +0 -0
- data/lib/tap/{script → cmd}/generate.rb +0 -0
- data/lib/tap/cmd/run.rb +156 -0
- data/lib/tap/constants.rb +4 -0
- data/lib/tap/dump.rb +57 -0
- data/lib/tap/env.rb +316 -0
- data/lib/tap/file_task.rb +106 -109
- data/lib/tap/generator.rb +4 -1
- data/lib/tap/generator/generators/command/USAGE +6 -0
- data/lib/tap/generator/generators/command/command_generator.rb +17 -0
- data/lib/tap/generator/generators/{script/templates/script.erb → command/templates/command.erb} +10 -10
- data/lib/tap/generator/generators/config/USAGE +21 -0
- data/lib/tap/generator/generators/config/config_generator.rb +17 -7
- data/lib/tap/generator/generators/file_task/USAGE +3 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -0
- data/lib/tap/generator/generators/file_task/templates/file.txt +2 -0
- data/lib/tap/generator/generators/file_task/templates/file.yml +3 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +26 -20
- data/lib/tap/generator/generators/file_task/templates/test.erb +20 -10
- data/lib/tap/generator/generators/generator/generator_generator.rb +1 -1
- data/lib/tap/generator/generators/generator/templates/generator.erb +21 -12
- data/lib/tap/generator/generators/root/templates/Rakefile +33 -24
- data/lib/tap/generator/generators/root/templates/tap.yml +28 -31
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +1 -0
- data/lib/tap/generator/generators/task/USAGE +3 -0
- data/lib/tap/generator/generators/task/task_generator.rb +18 -5
- data/lib/tap/generator/generators/task/templates/task.erb +7 -12
- data/lib/tap/generator/generators/task/templates/test.erb +10 -11
- data/lib/tap/generator/generators/workflow/templates/task.erb +1 -1
- data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
- data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
- data/lib/tap/patches/rake/testtask.rb +55 -0
- data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
- data/lib/tap/patches/ruby19/parsedate.rb +16 -0
- data/lib/tap/root.rb +172 -67
- data/lib/tap/script.rb +70 -336
- data/lib/tap/support/aggregator.rb +55 -0
- data/lib/tap/support/audit.rb +281 -280
- data/lib/tap/support/batchable.rb +59 -0
- data/lib/tap/support/class_configuration.rb +279 -0
- data/lib/tap/support/configurable.rb +92 -0
- data/lib/tap/support/configurable_methods.rb +296 -0
- data/lib/tap/support/executable.rb +98 -0
- data/lib/tap/support/executable_queue.rb +82 -0
- data/lib/tap/support/logger.rb +9 -15
- data/lib/tap/support/rake.rb +43 -54
- data/lib/tap/support/run_error.rb +32 -13
- data/lib/tap/support/shell_utils.rb +47 -0
- data/lib/tap/support/tdoc.rb +9 -8
- data/lib/tap/support/tdoc/config_attr.rb +40 -16
- data/lib/tap/support/validation.rb +77 -0
- data/lib/tap/support/versions.rb +36 -36
- data/lib/tap/task.rb +276 -482
- data/lib/tap/test.rb +20 -261
- data/lib/tap/test/env_vars.rb +7 -5
- data/lib/tap/test/file_methods.rb +126 -121
- data/lib/tap/test/subset_methods.rb +86 -45
- data/lib/tap/test/tap_methods.rb +271 -0
- data/lib/tap/workflow.rb +174 -46
- data/test/app/config/another/task.yml +1 -0
- data/test/app/config/erb.yml +2 -1
- data/test/app/config/some/task.yml +1 -0
- data/test/app/config/template.yml +2 -6
- data/test/app_test.rb +1241 -1008
- data/test/env/test_configure/recurse_a.yml +2 -0
- data/test/env/test_configure/recurse_b.yml +2 -0
- data/test/env/test_configure/tap.yml +23 -0
- data/test/env/test_load_env_config/dir/tap.yml +3 -0
- data/test/env/test_load_env_config/recurse_a.yml +2 -0
- data/test/env/test_load_env_config/recurse_b.yml +2 -0
- data/test/env/test_load_env_config/tap.yml +3 -0
- data/test/env_test.rb +198 -0
- data/test/file_task_test.rb +70 -53
- data/{lib/tap/generator/generators/package/USAGE → test/root/file.txt} +0 -0
- data/test/root_test.rb +621 -454
- data/test/script_test.rb +38 -174
- data/test/support/aggregator_test.rb +99 -0
- data/test/support/audit_test.rb +409 -416
- data/test/support/batchable_test.rb +74 -0
- data/test/support/{task_configuration_test.rb → class_configuration_test.rb} +106 -47
- data/test/{task/config/overriding.yml → support/configurable/config/configured.yml} +0 -0
- data/test/support/configurable_test.rb +295 -0
- data/test/support/executable_queue_test.rb +103 -0
- data/test/support/executable_test.rb +38 -0
- data/test/support/logger_test.rb +17 -17
- data/test/support/rake_test.rb +4 -2
- data/test/support/shell_utils_test.rb +24 -0
- data/test/support/tdoc_test.rb +265 -258
- data/test/support/validation_test.rb +54 -0
- data/test/support/versions_test.rb +38 -38
- data/test/tap_test_helper.rb +19 -5
- data/test/tap_test_suite.rb +5 -2
- data/test/task_base_test.rb +13 -104
- data/test/task_syntax_test.rb +300 -0
- data/test/task_test.rb +258 -381
- data/test/test/env_vars_test.rb +40 -40
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/one.txt +0 -0
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/two.txt +0 -0
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/one.txt +0 -0
- data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/two.txt +0 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/one.txt +0 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/two.txt +0 -0
- data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +1 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_different_content}/expected/two.txt +0 -0
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +1 -0
- data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_missing_expected_file}/expected/one.txt +0 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +1 -0
- data/test/test/file_methods_doc/test_sub/expected/one.txt +1 -0
- data/test/test/file_methods_doc/test_sub/expected/two.txt +1 -0
- data/test/test/file_methods_doc/test_sub/input/one.txt +1 -0
- data/test/test/file_methods_doc/test_sub/input/two.txt +1 -0
- data/test/test/file_methods_doc_test.rb +29 -0
- data/test/test/file_methods_test.rb +214 -143
- data/test/test/subset_methods_test.rb +111 -115
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/a.txt +0 -0
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/b.txt +0 -0
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/a.txt +0 -0
- data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/b.txt +0 -0
- data/test/test/tap_methods_test.rb +399 -0
- data/test/workflow_test.rb +101 -91
- metadata +86 -70
- data/lib/tap/generator/generators/package/package_generator.rb +0 -38
- data/lib/tap/generator/generators/package/templates/package.erb +0 -186
- data/lib/tap/generator/generators/script/USAGE +0 -0
- data/lib/tap/generator/generators/script/script_generator.rb +0 -17
- data/lib/tap/script/run.rb +0 -154
- data/lib/tap/support/batch_queue.rb +0 -162
- data/lib/tap/support/combinator.rb +0 -114
- data/lib/tap/support/task_configuration.rb +0 -169
- data/lib/tap/support/template.rb +0 -81
- data/lib/tap/support/templater.rb +0 -155
- data/lib/tap/version.rb +0 -4
- data/test/app/config/addition_template.yml +0 -6
- data/test/app_class_test.rb +0 -33
- data/test/check/binding_eval.rb +0 -23
- data/test/check/define_method_check.rb +0 -22
- data/test/check/dependencies_check.rb +0 -175
- data/test/check/inheritance_check.rb +0 -22
- data/test/support/batch_queue_test.rb +0 -320
- data/test/support/combinator_test.rb +0 -249
- data/test/support/template_test.rb +0 -122
- data/test/support/templater/erb.txt +0 -2
- data/test/support/templater/erb.yml +0 -2
- data/test/support/templater/somefile.txt +0 -2
- data/test/support/templater_test.rb +0 -192
- data/test/task/config/template.yml +0 -4
- data/test/task_class_test.rb +0 -170
- data/test/task_execute_test.rb +0 -262
- data/test/test/file_methods/test_assert_expected/expected/file.txt +0 -1
- data/test/test/file_methods/test_assert_expected/expected/folder/file.txt +0 -1
- data/test/test/file_methods/test_assert_expected/input/file.txt +0 -1
- data/test/test/file_methods/test_assert_expected/input/folder/file.txt +0 -1
- data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
- data/test/test/file_methods/test_file_compare/expected/output_1.txt +0 -3
- data/test/test/file_methods/test_file_compare/expected/output_2.txt +0 -1
- data/test/test/file_methods/test_file_compare/input/input_1.txt +0 -3
- data/test/test/file_methods/test_file_compare/input/input_2.txt +0 -3
- data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
- data/test/test/file_methods/test_yml_compare/expected/output_1.yml +0 -6
- data/test/test/file_methods/test_yml_compare/expected/output_2.yml +0 -6
- data/test/test/file_methods/test_yml_compare/input/input_1.yml +0 -4
- data/test/test/file_methods/test_yml_compare/input/input_2.yml +0 -4
- data/test/test_test.rb +0 -373
|
File without changes
|
|
File without changes
|
|
File without changes
|
data/lib/tap/cmd/run.rb
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# = Usage
|
|
2
|
+
# tap run {options} -- {task options} task INPUTS...
|
|
3
|
+
#
|
|
4
|
+
# examples:
|
|
5
|
+
# tap run --help Prints this help
|
|
6
|
+
# tap run -- task --help Prints help for task
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'tap/script'
|
|
10
|
+
env = Tap::Env.instance
|
|
11
|
+
app = Tap::App.instance
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# handle options
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
opts = [
|
|
18
|
+
['--help', '-h', GetoptLong::NO_ARGUMENT, "Print this help"],
|
|
19
|
+
['--debug', '-d', GetoptLong::NO_ARGUMENT, "Trace execution and debug"],
|
|
20
|
+
['--force', '-f', GetoptLong::NO_ARGUMENT, "Force execution at checkpoints"],
|
|
21
|
+
['--quiet', '-q', GetoptLong::NO_ARGUMENT, "Suppress logging"]]
|
|
22
|
+
|
|
23
|
+
Tap::Script.handle_options(*opts) do |opt, value|
|
|
24
|
+
case opt
|
|
25
|
+
when '--help'
|
|
26
|
+
puts Tap::Script.usage(__FILE__, "Usage", :keep_headers => false)
|
|
27
|
+
puts
|
|
28
|
+
puts "Options:"
|
|
29
|
+
puts Tap::Script.usage_options(opts)
|
|
30
|
+
exit
|
|
31
|
+
|
|
32
|
+
when '--quiet', '--force', '--debug'
|
|
33
|
+
# simply track these have been set
|
|
34
|
+
opt =~ /^-+(\w+)/
|
|
35
|
+
app.options.send("#{$1}=", true)
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# handle options for each specified task
|
|
42
|
+
#
|
|
43
|
+
rounds = Tap::Script.split_argv(ARGV).collect do |argv|
|
|
44
|
+
argv.each do |args|
|
|
45
|
+
ARGV.clear
|
|
46
|
+
ARGV.concat(args)
|
|
47
|
+
|
|
48
|
+
td = Tap::Script.next_arg(ARGV)
|
|
49
|
+
case td
|
|
50
|
+
when 'rake', nil
|
|
51
|
+
# remove --help as this will print the Rake help
|
|
52
|
+
ARGV.delete_if do |arg|
|
|
53
|
+
if arg == "--help"
|
|
54
|
+
env.log(:warn, "ignoring --help config for rake command")
|
|
55
|
+
true
|
|
56
|
+
else
|
|
57
|
+
false
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
next if ARGV.empty?
|
|
61
|
+
|
|
62
|
+
rake = env.rake_setup
|
|
63
|
+
|
|
64
|
+
# takes the place of rake.top_level
|
|
65
|
+
if rake.options.show_tasks
|
|
66
|
+
rake.display_tasks_and_comments
|
|
67
|
+
exit
|
|
68
|
+
elsif rake.options.show_prereqs
|
|
69
|
+
rake.display_prerequisites
|
|
70
|
+
exit
|
|
71
|
+
else
|
|
72
|
+
rake.top_level_tasks.each do |task_name|
|
|
73
|
+
app.task(task_name).enq
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
else
|
|
78
|
+
begin
|
|
79
|
+
# attempt lookup the task class
|
|
80
|
+
task_class = app.task_class(td)
|
|
81
|
+
rescue(Tap::App::LookupError)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# unless a Tap::Task was found, treat the
|
|
85
|
+
# args as a specification for Rake.
|
|
86
|
+
if task_class == nil || !task_class.include?(Tap::Support::Configurable)
|
|
87
|
+
args.unshift('rake')
|
|
88
|
+
redo
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# now let the class handle the argv
|
|
92
|
+
ARGV.collect! {|str| Tap::Script.parse_yaml(str) }
|
|
93
|
+
ARGV.unshift(td)
|
|
94
|
+
task_class.argv_enq(app)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
app.queue.clear
|
|
99
|
+
end
|
|
100
|
+
ARGV.clear
|
|
101
|
+
|
|
102
|
+
rounds.delete_if {|round| round.empty? }
|
|
103
|
+
if rounds.empty?
|
|
104
|
+
puts "no task specified"
|
|
105
|
+
exit
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#
|
|
109
|
+
# set signals
|
|
110
|
+
#
|
|
111
|
+
|
|
112
|
+
# info signal -- Note: some systems do
|
|
113
|
+
# not support the INFO signal
|
|
114
|
+
# (windows, fedora, at least)
|
|
115
|
+
signals = Signal.list.keys
|
|
116
|
+
if signals.include?("INFO")
|
|
117
|
+
Signal.trap("INFO") do
|
|
118
|
+
puts app.info
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
puts "ctl-i prints information"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# interuption signal
|
|
125
|
+
if signals.include?("INT")
|
|
126
|
+
Signal.trap("INT") do
|
|
127
|
+
puts " interrupted!"
|
|
128
|
+
# prompt for decision
|
|
129
|
+
while true
|
|
130
|
+
print "stop, terminate, or resume? (s/t/r):"
|
|
131
|
+
case gets.strip
|
|
132
|
+
when /s(top)?/i
|
|
133
|
+
app.stop
|
|
134
|
+
break
|
|
135
|
+
when /t(erminate)?/i
|
|
136
|
+
app.terminate
|
|
137
|
+
break
|
|
138
|
+
when /r(esume)?/i
|
|
139
|
+
break
|
|
140
|
+
else
|
|
141
|
+
puts "unexpected response..."
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
puts "ctl-c interupts execution"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#
|
|
150
|
+
# enque tasks and run!
|
|
151
|
+
#
|
|
152
|
+
puts "beginning run..."
|
|
153
|
+
rounds.each_with_index do |queue, i|
|
|
154
|
+
app.queue.concat(queue)
|
|
155
|
+
app.run
|
|
156
|
+
end
|
data/lib/tap/dump.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Tap
|
|
2
|
+
# == Description
|
|
3
|
+
# A primitive dump task (still in development) to print
|
|
4
|
+
# application results to a file or IO. The results are
|
|
5
|
+
# printed in a tap-readable format allowing the use of
|
|
6
|
+
# dumped results as inputs to other tasks.
|
|
7
|
+
#
|
|
8
|
+
# Usually dump is used as the final task in a round of
|
|
9
|
+
# tasks executed through 'tap run'.
|
|
10
|
+
# === Usage
|
|
11
|
+
# % tap run -- [your tasks] --+ dump filepath
|
|
12
|
+
#
|
|
13
|
+
# If no filepath is specified, the results are printed
|
|
14
|
+
# to stdout.
|
|
15
|
+
#
|
|
16
|
+
class Dump < Tap::FileTask
|
|
17
|
+
|
|
18
|
+
config :datetime_format, '%Y-%m-%d %H:%M:%S'
|
|
19
|
+
config :print_audit, true
|
|
20
|
+
config :print_date, true
|
|
21
|
+
|
|
22
|
+
#config :overwrite
|
|
23
|
+
|
|
24
|
+
def process(target=$stdout)
|
|
25
|
+
case target
|
|
26
|
+
when IO then dump_to(target)
|
|
27
|
+
else
|
|
28
|
+
log_basename(:dump, target)
|
|
29
|
+
prepare(target)
|
|
30
|
+
File.open(target, "wb") {|file| dump_to(file) }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def dump_to(io)
|
|
35
|
+
trails = []
|
|
36
|
+
results = {}
|
|
37
|
+
app.aggregator.to_hash.each_pair do |src, _results|
|
|
38
|
+
name = src.respond_to?(:name) ? src.name : ''
|
|
39
|
+
|
|
40
|
+
results["#{name} (#{src.object_id})"] = _results.collect {|_audit| _audit._current }
|
|
41
|
+
_results.each {|_audit| trails << _audit._to_s }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if print_audit
|
|
45
|
+
io.puts "# audit:"
|
|
46
|
+
trails.each {|trail| io.puts "# #{trail.gsub("\n", "\n# ")}"}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if print_date
|
|
50
|
+
io.puts "# date: #{Time.now.strftime(datetime_format)}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
YAML::dump(results, io)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/tap/env.rb
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
require 'tap/root'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
autoload(:PP, "pp")
|
|
4
|
+
|
|
5
|
+
module Tap
|
|
6
|
+
|
|
7
|
+
# == Under Construction
|
|
8
|
+
#
|
|
9
|
+
# Env manages configuration of the Tap execution environment, including the
|
|
10
|
+
# specification of gems that should be available through the tap command.
|
|
11
|
+
class Env
|
|
12
|
+
|
|
13
|
+
# A variety of configuration loading/handling methods for use in
|
|
14
|
+
# conjuction with Tap::Env, to aid in configuring the running
|
|
15
|
+
# environment for Tap.
|
|
16
|
+
module Configuration
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# Templates the input filepath using ERB then loads it as YAML.
|
|
20
|
+
# Returns an empty hash if the file doesn't exist, or loads to
|
|
21
|
+
# nil or false (as for an empty file). Raises an error if the
|
|
22
|
+
# filepath doesn't load to a hash.
|
|
23
|
+
def read_config(filepath)
|
|
24
|
+
return {} if !File.exists?(filepath) || File.directory?(filepath)
|
|
25
|
+
|
|
26
|
+
input = ERB.new(File.read(filepath)).result
|
|
27
|
+
config = YAML.load(input)
|
|
28
|
+
|
|
29
|
+
case config
|
|
30
|
+
when Hash then config
|
|
31
|
+
when nil, false then {}
|
|
32
|
+
else
|
|
33
|
+
raise "expected hash from config file: #{filepath}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Partitions a configuration hash into environment, execution,
|
|
38
|
+
# and application configurations, as determined by ENV_CONFIG_KEYS
|
|
39
|
+
# and EXE_CONFIG_KEYS. All non-env, non-exe configurations are
|
|
40
|
+
# considered application configurations.
|
|
41
|
+
def partition_configs(hash, *sets)
|
|
42
|
+
partitions = Array.new(sets.length + 1) { Hash.new }
|
|
43
|
+
|
|
44
|
+
hash.each_pair do |key, value|
|
|
45
|
+
index = 0
|
|
46
|
+
sets.each do |keys|
|
|
47
|
+
break if keys.include?(key)
|
|
48
|
+
index += 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
partitions[index][key] = value
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
partitions
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Joins the input configuration hashes, concatenating
|
|
58
|
+
# values for matching keys. Values will be made into
|
|
59
|
+
# arrays if they are not so already; duplicate values
|
|
60
|
+
# are removed from the result on a key-per-key basis.
|
|
61
|
+
def join_configs(*configs)
|
|
62
|
+
merge = {}
|
|
63
|
+
configs.each do |hash|
|
|
64
|
+
hash.each_pair do |key, values|
|
|
65
|
+
values = [values] unless values.kind_of?(Array)
|
|
66
|
+
(merge[key] ||= []).concat(values)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
merge.values.each {|values| values.uniq! }
|
|
70
|
+
merge
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
include Configuration
|
|
75
|
+
include Singleton
|
|
76
|
+
|
|
77
|
+
DEFAULT_CONFIG_FILE = "tap.yml"
|
|
78
|
+
|
|
79
|
+
# Currently these are ALWAYS included.
|
|
80
|
+
DEFAULT_CONFIG = {
|
|
81
|
+
"load_paths" => ["lib"],
|
|
82
|
+
"load_once_paths" => [],
|
|
83
|
+
"config_paths" => [],
|
|
84
|
+
"command_paths" => ["cmd"],
|
|
85
|
+
"gems" => [],
|
|
86
|
+
"generator_paths" => ["lib/generators"]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
attr_reader :config
|
|
90
|
+
attr_accessor :logger
|
|
91
|
+
|
|
92
|
+
def initialize
|
|
93
|
+
@config = nil
|
|
94
|
+
@logger = nil
|
|
95
|
+
# @on_handle_unknown_configs = nil
|
|
96
|
+
reset
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def debug_setup
|
|
100
|
+
$DEBUG = true
|
|
101
|
+
logger.level = Logger::DEBUG
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def rails_setup(app=Tap::App.instance)
|
|
105
|
+
Object.const_set('RAILS_ROOT', app.root)
|
|
106
|
+
Object.const_set('RAILS_DEFAULT_LOGGER', app.logger)
|
|
107
|
+
Dependencies.log_activity = app.debug?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def rake_setup(argv=ARGV, app=Tap::App.instance)
|
|
111
|
+
Tap::Support.autoload(:Rake, 'tap/support/rake')
|
|
112
|
+
|
|
113
|
+
# setup
|
|
114
|
+
app.extend Tap::Support::Rake
|
|
115
|
+
rake = Rake.application
|
|
116
|
+
options = rake.options
|
|
117
|
+
|
|
118
|
+
# merge options down from app
|
|
119
|
+
app.options.marshal_dump.each_pair do |key, value|
|
|
120
|
+
options.send("#{key}=", value)
|
|
121
|
+
end
|
|
122
|
+
options.silent = true
|
|
123
|
+
|
|
124
|
+
# run as if from command line using argv
|
|
125
|
+
current_argv = ARGV.dup
|
|
126
|
+
begin
|
|
127
|
+
ARGV.concat(argv)
|
|
128
|
+
|
|
129
|
+
# now follow the same protocol as
|
|
130
|
+
# in run, handling options
|
|
131
|
+
rake.init
|
|
132
|
+
rake.load_rakefile
|
|
133
|
+
ensure
|
|
134
|
+
ARGV.clear
|
|
135
|
+
ARGV.concat(current_argv)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
rake
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Resets Env. Load paths (load_paths and load_once_paths) are
|
|
142
|
+
# not reset unless dependencies==true; in which case Dependencies
|
|
143
|
+
# are cleared before load paths are cleared. The load paths added
|
|
144
|
+
# to $LOAD_PATH are not cleared.
|
|
145
|
+
#
|
|
146
|
+
# Generally not recommended.
|
|
147
|
+
def reset
|
|
148
|
+
unless @config == nil
|
|
149
|
+
$LOAD_PATH.delete_if {|path| config['load_paths'].include?(path) }
|
|
150
|
+
|
|
151
|
+
Dependencies.clear
|
|
152
|
+
Dependencies.load_paths.delete_if {|path| config['load_paths'].include?(path) }
|
|
153
|
+
Dependencies.load_once_paths.delete_if {|path| config['load_once_paths'].include?(path) }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
@config = {}
|
|
157
|
+
DEFAULT_CONFIG.keys.each do |key|
|
|
158
|
+
@config[key] = []
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Logs the action and message at the input level (default INFO).
|
|
163
|
+
# Logging is suppressed if no logger is set.
|
|
164
|
+
def log(action, msg="", level=Logger::INFO)
|
|
165
|
+
logger.add(level, msg, action.to_s) if logger
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Configures the specified App using the configurations in config_file.
|
|
169
|
+
# Loading of environement configurations occcurs via load_env_config;
|
|
170
|
+
# all environment paths are resolved using the app, after the app has
|
|
171
|
+
# been configured.
|
|
172
|
+
|
|
173
|
+
# Loads environment configurations from the specified path. If a directory
|
|
174
|
+
# is given as path, then the DEFAULT_CONFIG_FILE relative to that location
|
|
175
|
+
# will be loaded. The loading cycle recurses as specified by the configurations.
|
|
176
|
+
#
|
|
177
|
+
# Configuration paths are expanded relative to the parent directory
|
|
178
|
+
# of the loaded file. Raises an error if non-env configuration are
|
|
179
|
+
# found (as determined by Tap::Env::Configurtion::ENV_CONFIG_KEYS).
|
|
180
|
+
def load_config(path, root=Tap::Root.new, &block)
|
|
181
|
+
path = File.join(path, DEFAULT_CONFIG_FILE) if File.directory?(path)
|
|
182
|
+
path = File.expand_path(path)
|
|
183
|
+
|
|
184
|
+
# prevent infinite looping
|
|
185
|
+
config_paths = config['config_paths']
|
|
186
|
+
return false if config_paths.include?(path)
|
|
187
|
+
|
|
188
|
+
# load config
|
|
189
|
+
log(:load_config, path, Logger::DEBUG)
|
|
190
|
+
config_paths << path
|
|
191
|
+
|
|
192
|
+
config = read_config(path)
|
|
193
|
+
config['root'] = File.dirname(path) unless config['root']
|
|
194
|
+
|
|
195
|
+
configure(config, root) do |configured_root, other_configs|
|
|
196
|
+
if block_given?
|
|
197
|
+
yield(configured_root, path, other_configs)
|
|
198
|
+
else
|
|
199
|
+
log(:warn, "ignoring configs: #{path} (#{other_configs.keys.join(',')})", Logger::WARN)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def configure(config, root=Tap::Root.new, &block)
|
|
206
|
+
root_configs, env_configs, other_configs = partition_configs(config, ['root', 'directories', 'absolute_paths'], DEFAULT_CONFIG.keys)
|
|
207
|
+
env_configs = join_configs(DEFAULT_CONFIG, env_configs)
|
|
208
|
+
|
|
209
|
+
# assign root configs
|
|
210
|
+
root.send(:assign_paths,
|
|
211
|
+
root_configs['root'] || root.root,
|
|
212
|
+
root_configs['directories'] || root.directories,
|
|
213
|
+
root_configs['absolute_paths'] || root.absolute_paths)
|
|
214
|
+
|
|
215
|
+
# handle unknown configs (handle before setting
|
|
216
|
+
# env configs in case the configs modify root)
|
|
217
|
+
unless other_configs.empty?
|
|
218
|
+
if block_given?
|
|
219
|
+
yield(root, other_configs)
|
|
220
|
+
else
|
|
221
|
+
log(:warn, "ignoring configs: (#{other_configs.keys.join(',')})", Logger::WARN)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# load gems and configurations
|
|
226
|
+
gem_paths = env_configs.delete('gems').collect do |gem_name|
|
|
227
|
+
full_gem_path(gem_name)
|
|
228
|
+
end
|
|
229
|
+
config_paths = env_configs.delete('config_paths') + gem_paths
|
|
230
|
+
config_paths.each {|path| load_config(root[path], &block) }
|
|
231
|
+
|
|
232
|
+
# assign env configs
|
|
233
|
+
env_configs.each_pair do |key, value|
|
|
234
|
+
case key
|
|
235
|
+
when 'load_paths'
|
|
236
|
+
assign_paths(root, value, self.config[key], $LOAD_PATH, Dependencies.load_paths)
|
|
237
|
+
when 'load_once_paths'
|
|
238
|
+
assign_paths(root, value, self.config[key], Dependencies.load_once_paths)
|
|
239
|
+
when /_paths$/
|
|
240
|
+
assign_paths(root, value, self.config[key])
|
|
241
|
+
else
|
|
242
|
+
handle_unknown_config(key, value)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
true
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Loads env configurations from a gem, specifically from
|
|
250
|
+
# gemspec.full_gem_path. A gem version can be specified
|
|
251
|
+
# in the name, like 'gem >= 1.2'.
|
|
252
|
+
def full_gem_path(gem_name)
|
|
253
|
+
# figure the version of the gem, by default >= 0.0.0
|
|
254
|
+
gem_name =~ /^([^<=>]*)(.*)$/
|
|
255
|
+
name, version = $1, $2
|
|
256
|
+
version = ">= 0.0.0" if version.empty?
|
|
257
|
+
|
|
258
|
+
# load the gem and get the spec
|
|
259
|
+
gem(name, version)
|
|
260
|
+
spec = Gem.loaded_specs[name]
|
|
261
|
+
|
|
262
|
+
if spec == nil
|
|
263
|
+
log(:warn, "unknown gem: #{gem_name}", Logger::WARN)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
spec.full_gem_path
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def load_gem(gem_name)
|
|
270
|
+
load_config(full_gem_path(gem_name))
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Searches for and returns all .rb files under each of the command_paths
|
|
274
|
+
# as well as the default tap commands. Commands with conflicting names
|
|
275
|
+
# raise an error; however, user commands are allowed to override the
|
|
276
|
+
# default tap commands and will NOT raise an error.
|
|
277
|
+
def commands
|
|
278
|
+
commands = {}
|
|
279
|
+
config['command_paths'].each do |path|
|
|
280
|
+
pattern = File.join(path, "**/*.rb")
|
|
281
|
+
|
|
282
|
+
Dir.glob(pattern).each do |file|
|
|
283
|
+
cmd = Tap::App.relative_filepath(path, file).chomp(".rb")
|
|
284
|
+
raise "command name confict: #{cmd}" if commands.include?(cmd)
|
|
285
|
+
commands[cmd] = file
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# allow all other scripts to override default scripts
|
|
290
|
+
# (hence do this second)
|
|
291
|
+
tap_command_dir = File.expand_path(File.join( File.dirname(__FILE__), "cmd"))
|
|
292
|
+
Dir.glob( tap_command_dir + "/**/*.rb" ).each do |file|
|
|
293
|
+
cmd = Tap::App.relative_filepath(tap_command_dir, file).chomp(".rb")
|
|
294
|
+
commands[cmd] = file unless commands.include?(cmd)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
commands
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
protected
|
|
301
|
+
|
|
302
|
+
def assign_paths(root, paths, *targets)
|
|
303
|
+
paths = paths.collect {|path| root[path]}
|
|
304
|
+
targets.each do |array|
|
|
305
|
+
paths.reverse_each do |path|
|
|
306
|
+
array.unshift(path)
|
|
307
|
+
end
|
|
308
|
+
array.uniq!
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def handle_unknown_config(key, value)
|
|
313
|
+
raise "unknown env config: #{key}"
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|