tap 0.7.9 → 0.8.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/History +28 -0
- data/MIT-LICENSE +1 -1
- data/README +71 -43
- data/Rakefile +81 -64
- data/Tutorial +235 -0
- data/bin/tap +80 -44
- data/lib/tap.rb +41 -12
- data/lib/tap/app.rb +243 -246
- data/lib/tap/file_task.rb +357 -118
- data/lib/tap/generator.rb +88 -29
- data/lib/tap/generator/generators/config/config_generator.rb +4 -2
- data/lib/tap/generator/generators/config/templates/config.erb +1 -2
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +3 -18
- data/lib/tap/generator/generators/file_task/templates/task.erb +22 -15
- data/lib/tap/generator/generators/file_task/templates/test.erb +13 -2
- data/{test/test/inference_methods/test_assert_files_exist/input/input_1.txt → lib/tap/generator/generators/generator/USAGE} +0 -0
- data/lib/tap/generator/generators/generator/generator_generator.rb +21 -0
- data/lib/tap/generator/generators/generator/templates/generator.erb +23 -0
- data/lib/tap/generator/generators/generator/templates/usage.erb +1 -0
- data/{test/test/inference_methods/test_assert_files_exist/input/input_2.txt → lib/tap/generator/generators/package/USAGE} +0 -0
- data/lib/tap/generator/generators/package/package_generator.rb +38 -0
- data/lib/tap/generator/generators/package/templates/package.erb +186 -0
- data/lib/tap/generator/generators/root/root_generator.rb +14 -9
- data/lib/tap/generator/generators/root/templates/Rakefile +20 -14
- data/{test/test/inference_methods/test_infer_glob/expected/file.yml → lib/tap/generator/generators/root/templates/ReadMe.txt} +0 -0
- data/lib/tap/generator/generators/root/templates/tap.yml +82 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -1
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +2 -1
- data/{test/test/inference_methods/test_infer_glob/expected/file_1.txt → lib/tap/generator/generators/script/USAGE} +0 -0
- data/lib/tap/generator/generators/script/script_generator.rb +17 -0
- data/lib/tap/generator/generators/script/templates/script.erb +42 -0
- data/lib/tap/generator/generators/task/task_generator.rb +1 -1
- data/lib/tap/generator/generators/task/templates/task.erb +24 -16
- data/lib/tap/generator/generators/task/templates/test.erb +13 -17
- data/lib/tap/generator/generators/workflow/templates/task.erb +10 -10
- data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +3 -18
- data/lib/tap/root.rb +108 -146
- data/lib/tap/script.rb +362 -0
- data/lib/tap/script/console.rb +28 -0
- data/lib/tap/script/destroy.rb +13 -1
- data/lib/tap/script/generate.rb +13 -1
- data/lib/tap/script/run.rb +100 -57
- data/lib/tap/support/batch_queue.rb +0 -3
- data/lib/tap/support/logger.rb +6 -3
- data/lib/tap/support/rake.rb +54 -0
- data/lib/tap/support/task_configuration.rb +169 -0
- data/lib/tap/support/tdoc.rb +198 -0
- data/lib/tap/support/tdoc/config_attr.rb +338 -0
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
- data/lib/tap/support/versions.rb +33 -1
- data/lib/tap/task.rb +339 -227
- data/lib/tap/test.rb +86 -128
- data/lib/tap/test/env_vars.rb +16 -5
- data/lib/tap/test/file_methods.rb +373 -0
- data/lib/tap/test/subset_methods.rb +299 -180
- data/lib/tap/version.rb +2 -1
- data/lib/tap/workflow.rb +2 -0
- data/test/app/lib/app_test_task.rb +1 -0
- data/test/app_test.rb +327 -83
- data/test/check/binding_eval.rb +23 -0
- data/test/check/define_method_check.rb +22 -0
- data/test/check/dependencies_check.rb +175 -0
- data/test/check/inheritance_check.rb +22 -0
- data/test/file_task_test.rb +524 -291
- data/test/{test/inference_methods/test_infer_glob/expected/file_2.txt → root/glob/one.txt} +0 -0
- data/test/root/glob/two.txt +0 -0
- data/test/root_test.rb +330 -262
- data/test/script_test.rb +194 -0
- data/test/support/audit_test.rb +5 -2
- data/test/support/combinator_test.rb +10 -10
- data/test/support/rake_test.rb +35 -0
- data/test/support/task_configuration_test.rb +272 -0
- data/test/support/tdoc_test.rb +363 -0
- data/test/support/templater_test.rb +2 -2
- data/test/support/versions_test.rb +32 -0
- data/test/tap_test_helper.rb +39 -0
- data/test/task_base_test.rb +115 -0
- data/test/task_class_test.rb +56 -4
- data/test/task_execute_test.rb +29 -0
- data/test/task_test.rb +89 -70
- data/test/test/env_vars_test.rb +48 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/folder/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/folder/file.txt +0 -0
- 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_assert_output_files_equal/expected/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/expected/two.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/two.txt +1 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_2.txt +0 -0
- 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_method_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_2.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_2.yml +0 -0
- data/test/test/file_methods_test.rb +204 -0
- data/test/test/subset_methods_test.rb +93 -33
- data/test/test/test_assert_expected_result_files/expected/task/name/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/expected/task/name/b.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/b.txt +1 -0
- data/test/test/test_file_task_test/expected/one.txt +1 -0
- data/test/test/test_file_task_test/expected/two.txt +1 -0
- data/test/test/test_file_task_test/input/one.txt +1 -0
- data/test/test/test_file_task_test/input/two.txt +1 -0
- data/test/test_test.rb +143 -3
- data/test/workflow_test.rb +2 -0
- data/vendor/rails_generator.rb +56 -0
- data/vendor/rails_generator/base.rb +263 -0
- data/vendor/rails_generator/commands.rb +581 -0
- data/vendor/rails_generator/generated_attribute.rb +42 -0
- data/vendor/rails_generator/lookup.rb +209 -0
- data/vendor/rails_generator/manifest.rb +53 -0
- data/vendor/rails_generator/options.rb +143 -0
- data/vendor/rails_generator/scripts.rb +83 -0
- data/vendor/rails_generator/scripts/destroy.rb +7 -0
- data/vendor/rails_generator/scripts/generate.rb +7 -0
- data/vendor/rails_generator/scripts/update.rb +12 -0
- data/vendor/rails_generator/simple_logger.rb +46 -0
- data/vendor/rails_generator/spec.rb +44 -0
- metadata +180 -196
- data/lib/tap/generator/generators/root/templates/app.yml +0 -19
- data/lib/tap/generator/generators/root/templates/config/process_tap_request.yml +0 -4
- data/lib/tap/generator/generators/root/templates/lib/process_tap_request.rb +0 -26
- data/lib/tap/generator/generators/root/templates/public/images/nav.jpg +0 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/color.css +0 -57
- data/lib/tap/generator/generators/root/templates/public/stylesheets/layout.css +0 -108
- data/lib/tap/generator/generators/root/templates/public/stylesheets/normalize.css +0 -40
- data/lib/tap/generator/generators/root/templates/public/stylesheets/typography.css +0 -21
- data/lib/tap/generator/generators/root/templates/server/config/environment.rb +0 -60
- data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake +0 -5
- data/lib/tap/generator/generators/root/templates/server/test/test_helper.rb +0 -53
- data/lib/tap/script/server.rb +0 -12
- data/lib/tap/support/rap.rb +0 -38
- data/lib/tap/test/inference_methods.rb +0 -298
- data/test/task/config/task_with_config.yml +0 -1
- data/test/test/inference_methods_test.rb +0 -311
data/lib/tap/support/logger.rb
CHANGED
|
@@ -2,20 +2,23 @@ require 'logger'
|
|
|
2
2
|
|
|
3
3
|
module Tap
|
|
4
4
|
module Support
|
|
5
|
+
|
|
6
|
+
# == UNDER CONSTRUCTION
|
|
5
7
|
# Logger provides methods to extend Logger objects.
|
|
6
8
|
#
|
|
7
9
|
# The output format is designed to look nice in a command prompt, but provide useful information
|
|
8
10
|
# if directed at a log file. The Logger output format is like:
|
|
9
11
|
#
|
|
10
|
-
# I[datetime]
|
|
11
|
-
|
|
12
|
+
# I[datetime] action message
|
|
13
|
+
#--
|
|
12
14
|
# The first letter ('I' - INFO) indicates the severity of the message, next comes the datetime of the
|
|
13
15
|
# log, the type of log message, then the message itself. Specify the log type and message like:
|
|
14
16
|
#
|
|
15
17
|
# logger.type "message"
|
|
16
18
|
#
|
|
17
19
|
# Typed logging occurs through +method_missing+. Any type is possible so long as the logger doesn't
|
|
18
|
-
#
|
|
20
|
+
# already have a method of the input type.
|
|
21
|
+
#++
|
|
19
22
|
module Logger
|
|
20
23
|
Format = " %s[%s] %18s %s\n"
|
|
21
24
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
|
|
3
|
+
module Rake # :nodoc:
|
|
4
|
+
# Modifies Rake::Task to behave like a Tap::Task.
|
|
5
|
+
class Task # :nodoc:
|
|
6
|
+
alias_method(:tap_original_execute, :execute)
|
|
7
|
+
alias_method(:tap_original_invoke, :invoke)
|
|
8
|
+
alias_method(:tap_original_initialize, :initialize)
|
|
9
|
+
|
|
10
|
+
def initialize(*args)
|
|
11
|
+
tap_original_initialize(*args)
|
|
12
|
+
self.extend Tap::Task::Base
|
|
13
|
+
self.extend InstanceMethods
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def invoke
|
|
17
|
+
@lock.synchronize do
|
|
18
|
+
if application.options.trace
|
|
19
|
+
puts "** Invoke #{name} #{format_trace_flags}"
|
|
20
|
+
end
|
|
21
|
+
return if @already_invoked
|
|
22
|
+
@already_invoked = true
|
|
23
|
+
invoke_prerequisites
|
|
24
|
+
tap_original_execute if needed?
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module InstanceMethods # :nodoc:
|
|
29
|
+
|
|
30
|
+
def iterate=(input)
|
|
31
|
+
raise "iterate cannot be set to true for Rake tasks." if input == true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
|
|
36
|
+
def on_execute(audited_inputs)
|
|
37
|
+
invoke
|
|
38
|
+
audited_inputs._record(self, nil)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module Tap
|
|
45
|
+
module Support
|
|
46
|
+
# == UNDER CONSTRUCTION
|
|
47
|
+
module Rake
|
|
48
|
+
|
|
49
|
+
def task(td, config=nil)
|
|
50
|
+
Object::Rake.application.lookup(td) || super
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
module Tap
|
|
2
|
+
module Support
|
|
3
|
+
# == UNDER CONSTRUCTION
|
|
4
|
+
#
|
|
5
|
+
# TaskConfiguration holds the class configurations defined in a Tap::Task.
|
|
6
|
+
# The configurations are stored as an array of declarations like:
|
|
7
|
+
# [name, default, msg, declaration_class]. In addition, TaskConfiguration
|
|
8
|
+
# collapse the array of declarations into a hash, which acts as the default
|
|
9
|
+
# task configuration.
|
|
10
|
+
#
|
|
11
|
+
# Storing metadata about the configurations, such as the declaration_class,
|
|
12
|
+
# allows the creation of more user-friendly configuration files and facilitates
|
|
13
|
+
# incorporation into command-line applications.
|
|
14
|
+
#
|
|
15
|
+
# In general, users will not have to interact with TaskConfigurations directly.
|
|
16
|
+
#
|
|
17
|
+
# == Example
|
|
18
|
+
#
|
|
19
|
+
# class BaseTask < Tap::Task
|
|
20
|
+
# class_configurations [:one, 1]
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# BaseTask.configurations.hash # => {:one => 1}
|
|
24
|
+
#
|
|
25
|
+
# class SubTask < BaseTask
|
|
26
|
+
# class_configurations(
|
|
27
|
+
# [:one, 'one', "the first configuration"],
|
|
28
|
+
# [:two, 'two', "the second configuration"])
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# SubTask.configurations.hash # => {:one => 'one', :two => 'two'}
|
|
32
|
+
#
|
|
33
|
+
# Now you can see how the comments and declaring classes get used in the
|
|
34
|
+
# configuration files. Note that configuration keys are stringified
|
|
35
|
+
# for clarity (this is ok -- they will be symbolized when loaded by a
|
|
36
|
+
# task).
|
|
37
|
+
#
|
|
38
|
+
# [BaseTask.configurations.format_yaml]
|
|
39
|
+
# # BaseTask configuration
|
|
40
|
+
# one: 1
|
|
41
|
+
#
|
|
42
|
+
# [SubTask.configurations.format_yaml]
|
|
43
|
+
# # BaseTask configuration
|
|
44
|
+
# one: one # the first configuration
|
|
45
|
+
#
|
|
46
|
+
# # SubTask configuration
|
|
47
|
+
# two: two # the second configuration
|
|
48
|
+
#
|
|
49
|
+
class TaskConfiguration
|
|
50
|
+
attr_reader :declarations, :default, :attributes
|
|
51
|
+
|
|
52
|
+
include Enumerable
|
|
53
|
+
|
|
54
|
+
def initialize
|
|
55
|
+
@declarations = {}
|
|
56
|
+
@default = {}
|
|
57
|
+
@attributes = {}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dup
|
|
61
|
+
# ensure the duplication does not pass along
|
|
62
|
+
# new references to the same objects
|
|
63
|
+
another = TaskConfiguration.new
|
|
64
|
+
another.declarations = self.declarations.dup
|
|
65
|
+
another.default = self.default.dup
|
|
66
|
+
another.attributes = self.attributes.dup
|
|
67
|
+
another
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def empty?
|
|
71
|
+
@declaration.empty?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def declared?(key)
|
|
75
|
+
default.has_key?(key.to_sym)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def declare(key, declaration_class)
|
|
79
|
+
key = key.to_sym
|
|
80
|
+
|
|
81
|
+
# Check if the key is already declared...
|
|
82
|
+
return if declared?(key)
|
|
83
|
+
|
|
84
|
+
# Add a declaration array if no declaration have been
|
|
85
|
+
# created for the class and append the key
|
|
86
|
+
(declarations[declaration_class] ||= [declarations.length]) << key
|
|
87
|
+
default[key] = nil
|
|
88
|
+
attributes[key] = {}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def set(key, value, attributes={})
|
|
92
|
+
key = key.to_sym
|
|
93
|
+
raise "configurations cannot be set until they are declared" unless declared?(key)
|
|
94
|
+
|
|
95
|
+
default[key] = value
|
|
96
|
+
self.attributes[key].merge!(attributes)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def each # :yields: declaration_class, key, default, attributes
|
|
100
|
+
declarations.each_pair do |declaration_class, keys|
|
|
101
|
+
keys[1..-1].each do |key|
|
|
102
|
+
yield(declaration_class, key, default[key], attributes[key])
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Nicely formats the configurations into yaml with messages and
|
|
108
|
+
# declaration class divisions.
|
|
109
|
+
def format_yaml
|
|
110
|
+
lines = []
|
|
111
|
+
declarations.each_pair do |declaration_class, keys|
|
|
112
|
+
lines << "# #{declaration_class} configuration#{keys.length > 2 ? 's' : ''}"
|
|
113
|
+
|
|
114
|
+
class_name = declaration_class.to_s
|
|
115
|
+
class_path = class_name.underscore
|
|
116
|
+
source_file = Dependencies.search_for_file(class_path)
|
|
117
|
+
|
|
118
|
+
Tap::Support::TDoc.document(source_file)
|
|
119
|
+
class_doc = Tap::Support::TDoc[declaration_class]
|
|
120
|
+
configurations = (class_doc == nil ? [] : class_doc.configurations)
|
|
121
|
+
|
|
122
|
+
keys[1..-1].each do |key|
|
|
123
|
+
value = default[key]
|
|
124
|
+
attribs = attributes[key]
|
|
125
|
+
message = ""
|
|
126
|
+
configurations.each do |config|
|
|
127
|
+
if config.name == key.to_s
|
|
128
|
+
message = config.comment
|
|
129
|
+
break
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# yaml adds a header and a final newline which should be removed:
|
|
134
|
+
# {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
|
|
135
|
+
# {'key' => 'value'}.to_yaml[5...-1] # => "key: value"
|
|
136
|
+
yaml = {key.to_s => value}.to_yaml[5...-1]
|
|
137
|
+
#yaml = "##{yaml}" if value.nil?
|
|
138
|
+
|
|
139
|
+
lines << case
|
|
140
|
+
when message == nil || message.empty?
|
|
141
|
+
# if there is no message, simply add the yaml
|
|
142
|
+
yaml
|
|
143
|
+
when yaml !~ /\r?\n/ && message !~ /\r?\n/ && yaml.length < 20 && message.length < 30
|
|
144
|
+
# shorthand ONLY if the config and message can be expressed in a single line
|
|
145
|
+
message = message.gsub(/^#\s*/, "")
|
|
146
|
+
"%-20s # %s" % [yaml, message]
|
|
147
|
+
else
|
|
148
|
+
# comment out new lines and add the message
|
|
149
|
+
message = message.gsub(/\n#\s*/, "\n# ")
|
|
150
|
+
lines << "# #{message}"
|
|
151
|
+
yaml
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# add a spacer line
|
|
156
|
+
lines << ""
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
lines.join("\n")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
protected
|
|
164
|
+
|
|
165
|
+
attr_writer :declarations, :default, :attributes
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
require 'tap/support/tdoc/config_attr'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
|
|
4
|
+
module Tap
|
|
5
|
+
module Support
|
|
6
|
+
|
|
7
|
+
# == Overview
|
|
8
|
+
# TDoc hooks into and extends RDoc to make task documentation available for command line
|
|
9
|
+
# applications as well as for inclusion in RDoc html. In particular, TDoc makes available
|
|
10
|
+
# documentation for Task configurations, when they are present. TDoc provides an extension
|
|
11
|
+
# to the standard RDoc HTMLGenerator and template.
|
|
12
|
+
#
|
|
13
|
+
# == Usage
|
|
14
|
+
# To generate task documentation with configuration information, TDoc must be loaded and
|
|
15
|
+
# the appropriate flags passed to rdoc . Essentially what you want is:
|
|
16
|
+
#
|
|
17
|
+
# % rdoc --fmt tdoc --template tap/support/tdoc/tdoc_html_template [file_names....]
|
|
18
|
+
#
|
|
19
|
+
# Unfortunately, there is no way to load or require a file into the rdoc utility directly; the
|
|
20
|
+
# above code causes an 'Invalid output formatter' error. However, TDoc is easy to utilize
|
|
21
|
+
# from a Rake::RDocTask:
|
|
22
|
+
#
|
|
23
|
+
# require 'rake'
|
|
24
|
+
# require 'rake/rdoctask'
|
|
25
|
+
#
|
|
26
|
+
# desc 'Generate documentation.'
|
|
27
|
+
# Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
28
|
+
# require 'tap/support/tdoc'
|
|
29
|
+
# rdoc.template = 'tap/support/tdoc/tdoc_html_template'
|
|
30
|
+
# rdoc.options << '--fmt' << 'tdoc'
|
|
31
|
+
#
|
|
32
|
+
# # specify whatever else you need
|
|
33
|
+
# # rdoc.rdoc_files.include(...)
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# Now execute the rake task like:
|
|
37
|
+
#
|
|
38
|
+
# % rake rdoc
|
|
39
|
+
#
|
|
40
|
+
# TDoc may also be utilized programatically, but you should be aware that RDoc in Ruby
|
|
41
|
+
# can raise errors and/or cause namespace conflicts (see below).
|
|
42
|
+
#
|
|
43
|
+
# == Implementation
|
|
44
|
+
# RDoc is a beast to utilize in a non-standard way. One way to make RDoc parse unexpected
|
|
45
|
+
# flags like 'config_accessor' or the 'c' config specifier is to use the '--accessor' option
|
|
46
|
+
# (see 'rdoc --help' or the RDoc documentation for more details).
|
|
47
|
+
#
|
|
48
|
+
# TDoc hooks into the '--accessor' parsing process to pull out configuration attributes and
|
|
49
|
+
# format them into their own Configuration section on an RDoc html page. When 'tdoc' is
|
|
50
|
+
# specified as an rdoc option, TDoc in effect sets accessor flags for all the standard Task
|
|
51
|
+
# configuration methods, and then extends the RDoc::RubyParser handle these specially.
|
|
52
|
+
#
|
|
53
|
+
# If 'tdoc' is not specified as the rdoc format, TDoc does not affect the RDoc output.
|
|
54
|
+
# Similarly, the configuration attributes will not appear in the output unless you specify a
|
|
55
|
+
# template that utilizes them.
|
|
56
|
+
#
|
|
57
|
+
# == Namespace conflicts
|
|
58
|
+
# RDoc creates a namespace conflict with other libraries that define RubyToken and RubyLex
|
|
59
|
+
# in the Object namespace (the prime example being IRB). TDoc checks for such a conflict
|
|
60
|
+
# and redfines the RDoc RubyToken and RubyLex within the RDoc namespace. Essentially:
|
|
61
|
+
#
|
|
62
|
+
# RubyToken => RDoc::RubyToken
|
|
63
|
+
# RubyLex => RDoc::RubyLex
|
|
64
|
+
#
|
|
65
|
+
# The redefinition should not affect the existing RubyToken and RubyLex constants, but if
|
|
66
|
+
# you directly use the RDoc versions after loading TDoc, you should be aware that they must
|
|
67
|
+
# be accessed through the new constants. Unfortunatley the trick is not seamless. The RDoc
|
|
68
|
+
# RubyLex makes a few calls to the RubyLex class method 'debug?'... these will be issued to
|
|
69
|
+
# the existing RubyLex method and not RDoc::RubyLex.debug?
|
|
70
|
+
#
|
|
71
|
+
# In addition, because of the RubyLex calls, the RDoc::RubyLex cannot be fully hidden when
|
|
72
|
+
# TDoc is loaded before the conflicting RubyLex; you cannot load TDoc before loading IRB
|
|
73
|
+
# without raising warnings. I hope to submit a patch for RDoc to stop this nonsense in the
|
|
74
|
+
# future.
|
|
75
|
+
#
|
|
76
|
+
# On the plus side, you can now access/use RDoc within irb by requiring 'tap/support/tdoc'.
|
|
77
|
+
#
|
|
78
|
+
class TDoc
|
|
79
|
+
include Singleton
|
|
80
|
+
|
|
81
|
+
attr_accessor :stats, :options, :documented_files, :load_paths
|
|
82
|
+
|
|
83
|
+
def initialize
|
|
84
|
+
reinitialize
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def reinitialize(argv=['--fmt', 'tdoc', '--quiet'])
|
|
88
|
+
@documented_files = []
|
|
89
|
+
@tl = RDoc::TopLevel::reset
|
|
90
|
+
@stats = RDoc::Stats.new
|
|
91
|
+
@options = Options.instance
|
|
92
|
+
@options.parse(argv, RDoc::RDoc::GENERATORS)
|
|
93
|
+
@load_paths = $:
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class << self
|
|
97
|
+
|
|
98
|
+
def search_for_files(path_suffix)
|
|
99
|
+
# modified from 'activesupport/dependencies'
|
|
100
|
+
path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb'
|
|
101
|
+
|
|
102
|
+
files = instance.load_paths.collect do |root|
|
|
103
|
+
path = File.join(root, path_suffix)
|
|
104
|
+
File.file?(path) ? path : nil
|
|
105
|
+
end.compact
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def search_for_source_files(klass)
|
|
109
|
+
source_files = search_for_files(klass.to_s.underscore)
|
|
110
|
+
while klass.superclass.kind_of?(Tap::Task)
|
|
111
|
+
klass = klass.superclass
|
|
112
|
+
break if klass.configurations.empty?
|
|
113
|
+
source_files.concat(search_for_files(klass.to_s.underscore))
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
source_files.uniq
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def document(*filepaths)
|
|
120
|
+
filepaths.each do |filepath|
|
|
121
|
+
next if filepath == nil || instance.documented_files.include?(filepath) || !File.exists?(filepath)
|
|
122
|
+
|
|
123
|
+
tl = RDoc::TopLevel.new(filepath)
|
|
124
|
+
parser = RDoc::RubyParser.new(tl, filepath, File.read(filepath), instance.options, instance.stats)
|
|
125
|
+
parser.scan
|
|
126
|
+
instance.documented_files << filepath
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def find_class_or_module_named(name)
|
|
131
|
+
RDoc::TopLevel.all_classes_and_modules.each do |c|
|
|
132
|
+
res = c.find_class_or_module_named(name)
|
|
133
|
+
return res if res
|
|
134
|
+
end
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def [](klass)
|
|
139
|
+
name = klass.to_s
|
|
140
|
+
res = find_class_or_module_named(name)
|
|
141
|
+
|
|
142
|
+
# If no result was found, try to document a sourcefile
|
|
143
|
+
# from the standard filepath and search again
|
|
144
|
+
if res == nil
|
|
145
|
+
source_files = klass.respond_to?(:source_files) ? klass.source_files : []
|
|
146
|
+
source_files = search_for_source_files(klass) if source_files.empty?
|
|
147
|
+
|
|
148
|
+
unless source_files.empty?
|
|
149
|
+
document(*source_files)
|
|
150
|
+
res = find_class_or_module_named(name)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
res
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def usage(program_file, sections=[], keep_section_headers=false)
|
|
158
|
+
comment = []
|
|
159
|
+
File.open(program_file) do |file|
|
|
160
|
+
while line = file.gets
|
|
161
|
+
case line
|
|
162
|
+
when /^\s*$/
|
|
163
|
+
# skip leading blank lines
|
|
164
|
+
comment.empty? ? next : break
|
|
165
|
+
when /^\s*# ?(.*)/m
|
|
166
|
+
comment << $1
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
unless sections.empty?
|
|
172
|
+
sections_hash = {}
|
|
173
|
+
current_section = nil
|
|
174
|
+
comment.each do |line|
|
|
175
|
+
case line
|
|
176
|
+
when /^s*=+(.*)/
|
|
177
|
+
current_section = []
|
|
178
|
+
current_section << line if keep_section_headers
|
|
179
|
+
sections_hash[$1.strip] = current_section
|
|
180
|
+
else
|
|
181
|
+
current_section << line unless current_section.nil?
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
comment = []
|
|
186
|
+
sections.each do |section|
|
|
187
|
+
next unless sections_hash.has_key?(section)
|
|
188
|
+
comment.concat(sections_hash[section])
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
comment.join('')
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|