tap 0.9.1 → 0.10.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 +37 -30
- data/MIT-LICENSE +1 -1
- data/README +92 -44
- data/bin/tap +62 -75
- data/cmd/console.rb +42 -0
- data/cmd/destroy.rb +16 -0
- data/cmd/generate.rb +16 -0
- data/cmd/run.rb +126 -0
- data/doc/Class Reference +362 -0
- data/doc/Command Reference +153 -0
- data/doc/Tutorial +237 -0
- data/lib/tap.rb +6 -45
- data/lib/tap/app.rb +126 -500
- data/lib/tap/constants.rb +2 -29
- data/lib/tap/env.rb +555 -250
- data/lib/tap/file_task.rb +60 -103
- data/lib/tap/generator/base.rb +109 -0
- data/lib/tap/generator/destroy.rb +37 -0
- data/lib/tap/generator/generate.rb +61 -0
- data/lib/tap/generator/generators/command/command_generator.rb +16 -12
- data/lib/tap/generator/generators/command/templates/command.erb +13 -19
- data/lib/tap/generator/generators/config/config_generator.rb +18 -27
- data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
- data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -11
- data/lib/tap/generator/generators/file_task/templates/file.txt +11 -2
- data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +24 -31
- data/lib/tap/generator/generators/file_task/templates/test.erb +18 -22
- data/lib/tap/generator/generators/root/root_generator.rb +45 -31
- data/lib/tap/generator/generators/root/templates/Rakefile +64 -41
- data/lib/tap/generator/generators/root/templates/gemspec +27 -0
- data/lib/tap/generator/generators/root/templates/tapfile +8 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
- data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
- data/lib/tap/generator/generators/task/task_generator.rb +21 -28
- data/lib/tap/generator/generators/task/templates/task.erb +13 -23
- data/lib/tap/generator/generators/task/templates/test.erb +15 -18
- data/lib/tap/generator/manifest.rb +14 -0
- data/lib/tap/patches/rake/rake_test_loader.rb +0 -0
- data/lib/tap/patches/rake/testtask.rb +0 -0
- data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -0
- data/lib/tap/patches/ruby19/parsedate.rb +0 -0
- data/lib/tap/root.rb +260 -21
- data/lib/tap/support/aggregator.rb +11 -11
- data/lib/tap/support/assignments.rb +172 -0
- data/lib/tap/support/audit.rb +20 -18
- data/lib/tap/support/batchable.rb +21 -10
- data/lib/tap/support/batchable_class.rb +107 -0
- data/lib/tap/support/class_configuration.rb +154 -239
- data/lib/tap/support/command_line.rb +97 -102
- data/lib/tap/support/comment.rb +270 -0
- data/lib/tap/support/configurable.rb +86 -65
- data/lib/tap/support/configurable_class.rb +296 -0
- data/lib/tap/support/configuration.rb +122 -0
- data/lib/tap/support/constant.rb +70 -0
- data/lib/tap/support/constant_utils.rb +127 -0
- data/lib/tap/support/declarations.rb +111 -0
- data/lib/tap/support/executable.rb +30 -17
- data/lib/tap/support/executable_queue.rb +0 -0
- data/lib/tap/support/framework.rb +71 -0
- data/lib/tap/support/framework_class.rb +199 -0
- data/lib/tap/support/instance_configuration.rb +147 -0
- data/lib/tap/support/lazydoc.rb +428 -0
- data/lib/tap/support/manifest.rb +89 -0
- data/lib/tap/support/run_error.rb +0 -0
- data/lib/tap/support/shell_utils.rb +33 -9
- data/lib/tap/support/summary.rb +30 -0
- data/lib/tap/support/tdoc.rb +339 -134
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -0
- data/lib/tap/support/templater.rb +180 -0
- data/lib/tap/support/validation.rb +409 -76
- data/lib/tap/support/versions.rb +5 -3
- data/lib/tap/task.rb +78 -174
- data/lib/tap/tasks/dump.rb +56 -0
- data/lib/tap/tasks/rake.rb +93 -0
- data/lib/tap/test.rb +3 -3
- data/lib/tap/test/env_vars.rb +2 -2
- data/lib/tap/test/file_methods.rb +19 -20
- data/lib/tap/test/script_methods.rb +144 -0
- data/lib/tap/test/subset_methods.rb +1 -1
- data/lib/tap/test/tap_methods.rb +28 -62
- data/lib/tap/workflow.rb +22 -39
- metadata +48 -179
- data/Basic Overview +0 -151
- data/Command Reference +0 -99
- data/Rakefile +0 -127
- data/Tutorial +0 -287
- data/lib/tap/cmd/console.rb +0 -31
- data/lib/tap/cmd/destroy.rb +0 -20
- data/lib/tap/cmd/generate.rb +0 -20
- data/lib/tap/cmd/run.rb +0 -151
- data/lib/tap/dump.rb +0 -57
- data/lib/tap/generator.rb +0 -91
- data/lib/tap/generator/generators/command/USAGE +0 -6
- data/lib/tap/generator/generators/config/USAGE +0 -21
- data/lib/tap/generator/generators/config/templates/config.erb +0 -1
- data/lib/tap/generator/generators/file_task/USAGE +0 -3
- data/lib/tap/generator/generators/file_task/templates/file.yml +0 -3
- data/lib/tap/generator/generators/generator/USAGE +0 -0
- data/lib/tap/generator/generators/generator/generator_generator.rb +0 -21
- data/lib/tap/generator/generators/generator/templates/generator.erb +0 -32
- data/lib/tap/generator/generators/generator/templates/usage.erb +0 -1
- data/lib/tap/generator/generators/root/USAGE +0 -0
- data/lib/tap/generator/generators/root/templates/ReadMe.txt +0 -0
- data/lib/tap/generator/generators/root/templates/tap.yml +0 -80
- data/lib/tap/generator/generators/task/USAGE +0 -3
- data/lib/tap/generator/generators/workflow/USAGE +0 -0
- data/lib/tap/generator/generators/workflow/templates/task.erb +0 -16
- data/lib/tap/generator/generators/workflow/templates/test.erb +0 -7
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +0 -6
- data/lib/tap/generator/options.rb +0 -26
- data/lib/tap/generator/usage.rb +0 -26
- data/lib/tap/support/batchable_methods.rb +0 -34
- data/lib/tap/support/command_line_methods.rb +0 -76
- data/lib/tap/support/configurable_methods.rb +0 -224
- data/lib/tap/support/logger.rb +0 -88
- data/lib/tap/support/rake.rb +0 -43
- data/lib/tap/support/tdoc/config_attr.rb +0 -362
- data/test/app/config/another/task.yml +0 -1
- data/test/app/config/batch.yml +0 -2
- data/test/app/config/empty.yml +0 -0
- data/test/app/config/erb.yml +0 -2
- data/test/app/config/some/task.yml +0 -1
- data/test/app/config/template.yml +0 -2
- data/test/app/config/version-0.1.yml +0 -1
- data/test/app/config/version.yml +0 -1
- data/test/app/lib/app_test_task.rb +0 -3
- data/test/app_test.rb +0 -1849
- data/test/env/test_configure/recurse_a.yml +0 -2
- data/test/env/test_configure/recurse_b.yml +0 -2
- data/test/env/test_configure/tap.yml +0 -23
- data/test/env/test_load_env_config/dir/tap.yml +0 -3
- data/test/env/test_load_env_config/recurse_a.yml +0 -2
- data/test/env/test_load_env_config/recurse_b.yml +0 -2
- data/test/env/test_load_env_config/tap.yml +0 -3
- data/test/env_test.rb +0 -198
- data/test/file_task/config/batch.yml +0 -2
- data/test/file_task/config/configured.yml +0 -1
- 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 +0 -1291
- data/test/root/alt_lib/alt_module.rb +0 -4
- data/test/root/file.txt +0 -0
- data/test/root/glob/one.txt +0 -0
- data/test/root/glob/two.txt +0 -0
- data/test/root/lib/absolute_alt_filepath.rb +0 -2
- data/test/root/lib/alternative_filepath.rb +0 -2
- data/test/root/lib/another_module.rb +0 -2
- data/test/root/lib/nested/some_module.rb +0 -4
- data/test/root/lib/no_module_included.rb +0 -0
- data/test/root/lib/some/module.rb +0 -4
- data/test/root/lib/some_class.rb +0 -2
- data/test/root/lib/some_module.rb +0 -3
- data/test/root/load_path/load_path_module.rb +0 -2
- data/test/root/load_path/skip_module.rb +0 -2
- data/test/root/mtime/older.txt +0 -0
- data/test/root/unload/full_path.rb +0 -2
- data/test/root/unload/loaded_by_nested.rb +0 -2
- data/test/root/unload/nested/nested_load.rb +0 -6
- data/test/root/unload/nested/nested_with_ext.rb +0 -4
- data/test/root/unload/nested/relative_path.rb +0 -4
- data/test/root/unload/older.rb +0 -2
- data/test/root/unload/unload_base.rb +0 -9
- 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 +0 -718
- data/test/support/aggregator_test.rb +0 -99
- data/test/support/audit_test.rb +0 -445
- data/test/support/batchable_test.rb +0 -74
- data/test/support/class_configuration_test.rb +0 -331
- data/test/support/command_line_test.rb +0 -58
- data/test/support/configurable/config/configured.yml +0 -2
- data/test/support/configurable_test.rb +0 -295
- data/test/support/executable_queue_test.rb +0 -103
- data/test/support/executable_test.rb +0 -38
- data/test/support/logger_test.rb +0 -31
- data/test/support/rake_test.rb +0 -37
- data/test/support/shell_utils_test.rb +0 -24
- data/test/support/tdoc_test.rb +0 -370
- data/test/support/validation_test.rb +0 -54
- data/test/support/versions_test.rb +0 -103
- data/test/tap_test_helper.rb +0 -57
- data/test/tap_test_suite.rb +0 -7
- data/test/task/config/batch.yml +0 -2
- data/test/task/config/batched.yml +0 -2
- data/test/task/config/configured.yml +0 -1
- data/test/task/config/example.yml +0 -1
- data/test/task_base_test.rb +0 -24
- data/test/task_syntax_test.rb +0 -300
- data/test/task_test.rb +0 -320
- data/test/test/env_vars_test.rb +0 -48
- data/test/test/file_methods/test_assert_files/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files/expected/two.txt +0 -1
- data/test/test/file_methods/test_assert_files/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +0 -1
- 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/file_methods_doc/test_sub/expected/one.txt +0 -1
- data/test/test/file_methods_doc/test_sub/expected/two.txt +0 -1
- data/test/test/file_methods_doc/test_sub/input/one.txt +0 -1
- data/test/test/file_methods_doc/test_sub/input/two.txt +0 -1
- data/test/test/file_methods_doc_test.rb +0 -29
- data/test/test/file_methods_test.rb +0 -275
- data/test/test/subset_methods_test.rb +0 -171
- data/test/test/tap_methods/test_assert_files/expected/task/name/a.txt +0 -1
- data/test/test/tap_methods/test_assert_files/expected/task/name/b.txt +0 -1
- data/test/test/tap_methods/test_assert_files/input/a.txt +0 -1
- data/test/test/tap_methods/test_assert_files/input/b.txt +0 -1
- data/test/test/tap_methods_test.rb +0 -399
- data/test/workflow_test.rb +0 -120
- data/vendor/rails_generator.rb +0 -56
- data/vendor/rails_generator/base.rb +0 -263
- data/vendor/rails_generator/commands.rb +0 -581
- data/vendor/rails_generator/generated_attribute.rb +0 -42
- data/vendor/rails_generator/lookup.rb +0 -209
- data/vendor/rails_generator/manifest.rb +0 -53
- data/vendor/rails_generator/options.rb +0 -143
- data/vendor/rails_generator/scripts.rb +0 -83
- data/vendor/rails_generator/scripts/destroy.rb +0 -7
- data/vendor/rails_generator/scripts/generate.rb +0 -7
- data/vendor/rails_generator/scripts/update.rb +0 -12
- data/vendor/rails_generator/simple_logger.rb +0 -46
- data/vendor/rails_generator/spec.rb +0 -44
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'tap/support/batchable'
|
|
2
|
+
require 'tap/support/executable'
|
|
3
|
+
require 'tap/support/framework_class'
|
|
4
|
+
|
|
5
|
+
module Tap
|
|
6
|
+
module Support
|
|
7
|
+
|
|
8
|
+
# Framework encapsulates the basic framework functionality (batching,
|
|
9
|
+
# configuration, documentation, etc) used by Task and Workflow. Note
|
|
10
|
+
# that Framework does NOT encapsulate the functionality needed to
|
|
11
|
+
# make a class useful in workflows, such as enq and on_complete.
|
|
12
|
+
module Framework
|
|
13
|
+
include Batchable
|
|
14
|
+
include Configurable
|
|
15
|
+
|
|
16
|
+
def self.included(mod)
|
|
17
|
+
mod.extend Support::BatchableClass
|
|
18
|
+
mod.extend Support::ConfigurableClass
|
|
19
|
+
mod.extend Support::FrameworkClass
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The application used to load config_file templates
|
|
23
|
+
# (and hence, to initialize batched objects).
|
|
24
|
+
attr_reader :app
|
|
25
|
+
|
|
26
|
+
# The name of self.
|
|
27
|
+
attr_accessor :name
|
|
28
|
+
|
|
29
|
+
# Initializes a new instance and associated batch objects. Batch
|
|
30
|
+
# objects will be initialized for each configuration template
|
|
31
|
+
# specified by app.each_config_template(config_file) where
|
|
32
|
+
# config_file = app.config_filepath(name).
|
|
33
|
+
def initialize(config={}, name=nil, app=App.instance)
|
|
34
|
+
super()
|
|
35
|
+
@app = app
|
|
36
|
+
@name = name || self.class.default_name
|
|
37
|
+
initialize_config(config)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Creates a new batched object and adds the object to batch. The batched object
|
|
41
|
+
# will be a duplicate of the current object but with a new name and/or
|
|
42
|
+
# configurations.
|
|
43
|
+
def initialize_batch_obj(overrides={}, name=nil)
|
|
44
|
+
obj = super().reconfigure(overrides)
|
|
45
|
+
obj.name = name if name
|
|
46
|
+
obj
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Logs the inputs to the application logger (via app.log)
|
|
50
|
+
def log(action, msg="", level=Logger::INFO)
|
|
51
|
+
# TODO - add a task identifier?
|
|
52
|
+
app.log(action, msg, level)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Raises a TerminateError if app.state == State::TERMINATE.
|
|
56
|
+
# check_terminate may be called at any time to provide a
|
|
57
|
+
# breakpoint in long-running processes.
|
|
58
|
+
def check_terminate
|
|
59
|
+
if app.state == App::State::TERMINATE
|
|
60
|
+
raise App::TerminateError.new
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns self.name
|
|
65
|
+
def to_s
|
|
66
|
+
name
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
require 'tap/support/command_line'
|
|
2
|
+
|
|
3
|
+
module Tap
|
|
4
|
+
module Support
|
|
5
|
+
|
|
6
|
+
# FrameworkClass encapsulates class methods related to Framework.
|
|
7
|
+
module FrameworkClass
|
|
8
|
+
|
|
9
|
+
# Returns the default name for the class: to_s.underscore
|
|
10
|
+
attr_accessor :default_name
|
|
11
|
+
|
|
12
|
+
def self.extended(base)
|
|
13
|
+
caller.each_with_index do |line, index|
|
|
14
|
+
case line
|
|
15
|
+
when /\/framework.rb/ then next
|
|
16
|
+
when /^(([A-z]:)?[^:]+):(\d+)/
|
|
17
|
+
base.instance_variable_set(:@source_file, File.expand_path($1))
|
|
18
|
+
break
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
base.instance_variable_set(:@default_name, base.to_s.underscore)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def inherited(child)
|
|
26
|
+
unless child.instance_variable_defined?(:@source_file)
|
|
27
|
+
caller.first =~ /^(([A-z]:)?[^:]+):(\d+)/
|
|
28
|
+
child.instance_variable_set(:@source_file, File.expand_path($1))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
child.instance_variable_set(:@default_name, child.to_s.underscore)
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def subclass(const_name, configs={}, options={}, &block)
|
|
36
|
+
# Generate the nesting module
|
|
37
|
+
current, constants = const_name.to_s.constants_split
|
|
38
|
+
raise ArgumentError, "#{current} is already defined!" if constants.empty?
|
|
39
|
+
|
|
40
|
+
subclass_const = constants.pop
|
|
41
|
+
constants.each {|const| current = current.const_set(const, Module.new)}
|
|
42
|
+
|
|
43
|
+
# Generate the subclass
|
|
44
|
+
subclass = Class.new(self)
|
|
45
|
+
configs = configs[0] if configs.kind_of?(Array) && configs.length == 1 && configs[0].kind_of?(Hash)
|
|
46
|
+
|
|
47
|
+
case configs
|
|
48
|
+
when Hash
|
|
49
|
+
subclass.send(:attr_accessor, *configs.keys)
|
|
50
|
+
configs.each_pair do |key, value|
|
|
51
|
+
subclass.configurations.add(key, value)
|
|
52
|
+
end
|
|
53
|
+
when Array
|
|
54
|
+
configs.each do |method, key, value, opts, config_block|
|
|
55
|
+
subclass.send(method, key, value, opts, &config_block)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
block_method = options[:block_method] || :process
|
|
60
|
+
subclass.send(:define_method, block_method, &block)
|
|
61
|
+
subclass.default_name = const_name
|
|
62
|
+
|
|
63
|
+
const_name = current == Object ? subclass_const : "#{current}::#{subclass_const}"
|
|
64
|
+
caller.each_with_index do |line, index|
|
|
65
|
+
case line
|
|
66
|
+
when /\/tap\/support\/declarations.rb/ then next
|
|
67
|
+
when /^(([A-z]:)?[^:]+):(\d+)/
|
|
68
|
+
subclass.source_file = File.expand_path($1)
|
|
69
|
+
subclass.lazydoc[const_name, false]['manifest'] = subclass.lazydoc.register($3.to_i - 1)
|
|
70
|
+
break
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
arity = options[:arity] || block.arity
|
|
75
|
+
comment = Comment.new
|
|
76
|
+
comment.subject = case
|
|
77
|
+
when arity > 0
|
|
78
|
+
Array.new(arity, "INPUT").join(' ')
|
|
79
|
+
when arity < 0
|
|
80
|
+
array = Array.new(-1 * arity - 1, "INPUT")
|
|
81
|
+
array << "INPUTS..."
|
|
82
|
+
array.join(' ')
|
|
83
|
+
else ""
|
|
84
|
+
end
|
|
85
|
+
subclass.lazydoc[const_name, false]['args'] ||= comment
|
|
86
|
+
|
|
87
|
+
# Set the subclass constant
|
|
88
|
+
current.const_set(subclass_const, subclass)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
DEFAULT_HELP_TEMPLATE = %Q{<%= task_class %><%= manifest.subject.to_s.strip.empty? ? '' : ' -- ' %><%= manifest.subject %>
|
|
92
|
+
|
|
93
|
+
<% unless manifest.empty? %>
|
|
94
|
+
<%= '-' * 80 %>
|
|
95
|
+
|
|
96
|
+
<% manifest.wrap(77, 2, nil).each do |line| %>
|
|
97
|
+
<%= line %>
|
|
98
|
+
<% end %>
|
|
99
|
+
<%= '-' * 80 %>
|
|
100
|
+
<% end %>
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
def instantiate(argv, app=Tap::App.instance) # => instance, argv
|
|
105
|
+
opts = OptionParser.new
|
|
106
|
+
|
|
107
|
+
# Add configurations
|
|
108
|
+
config = {}
|
|
109
|
+
unless configurations.empty?
|
|
110
|
+
opts.separator ""
|
|
111
|
+
opts.separator "configurations:"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
configurations.each do |receiver, key, configuration|
|
|
115
|
+
opts.on(*CommandLine.configv(configuration)) do |value|
|
|
116
|
+
config[key] = value
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Add options on_tail, giving priority to configurations
|
|
121
|
+
opts.separator ""
|
|
122
|
+
opts.separator "options:"
|
|
123
|
+
|
|
124
|
+
opts.on_tail("-h", "--help", "Print this help") do
|
|
125
|
+
args = lazydoc(true)[to_s]['args'] || Tap::Support::Comment.new
|
|
126
|
+
|
|
127
|
+
opts.banner = "#{help}usage: tap run -- #{to_s.underscore} #{args.subject}"
|
|
128
|
+
puts opts
|
|
129
|
+
exit
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Add option for name
|
|
133
|
+
name = default_name
|
|
134
|
+
opts.on_tail('--name NAME', /^[^-].*/, 'Specify a name') do |value|
|
|
135
|
+
name = value
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Add option to add args
|
|
139
|
+
use_args = []
|
|
140
|
+
opts.on_tail('--use FILE', /^[^-].*/, 'Loads inputs from file') do |value|
|
|
141
|
+
obj = YAML.load_file(value)
|
|
142
|
+
case obj
|
|
143
|
+
when Hash
|
|
144
|
+
obj.values.each do |array|
|
|
145
|
+
# error if value isn't an array
|
|
146
|
+
use_args.concat(array)
|
|
147
|
+
end
|
|
148
|
+
when Array
|
|
149
|
+
use_args.concat(obj)
|
|
150
|
+
else
|
|
151
|
+
use_args << obj
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
opts.parse!(argv)
|
|
156
|
+
obj = new({}, name, app)
|
|
157
|
+
|
|
158
|
+
path_configs = load_config(app.config_filepath(name))
|
|
159
|
+
if path_configs.kind_of?(Array)
|
|
160
|
+
path_configs.each_with_index do |path_config, i|
|
|
161
|
+
obj.initialize_batch_obj(path_config, "#{name}_#{i}") unless i == 0
|
|
162
|
+
end
|
|
163
|
+
path_configs = path_configs[0]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
[obj.reconfigure(path_configs).reconfigure(config), argv + use_args]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def lazydoc(resolve=false, args_method=:process)
|
|
170
|
+
if resolve
|
|
171
|
+
lazydoc = super(false)
|
|
172
|
+
lazydoc.resolve(nil, /^\s*def\s+#{args_method}(\((.*?)\))?/) do |comment, match|
|
|
173
|
+
comment.subject = match[2].to_s.split(',').collect do |arg|
|
|
174
|
+
arg = arg.strip.upcase
|
|
175
|
+
case arg
|
|
176
|
+
when /^&/ then nil
|
|
177
|
+
when /^\*/ then arg[1..-1] + "..."
|
|
178
|
+
else arg
|
|
179
|
+
end
|
|
180
|
+
end.join(', ')
|
|
181
|
+
|
|
182
|
+
lazydoc.default_attributes['args'] ||= comment
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
super(true)
|
|
186
|
+
else
|
|
187
|
+
super(false)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def help
|
|
192
|
+
Tap::Support::Templater.new(DEFAULT_HELP_TEMPLATE,
|
|
193
|
+
:task_class => self,
|
|
194
|
+
:manifest => lazydoc(true)[to_s]['manifest'] || Tap::Support::Comment.new
|
|
195
|
+
).build
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
module Tap
|
|
2
|
+
module Support
|
|
3
|
+
|
|
4
|
+
# InstanceConfiguration serves as a forwarding hash, where get and set operations
|
|
5
|
+
# for configurations are sent to instance methods rather than to an underlying data
|
|
6
|
+
# store.
|
|
7
|
+
#
|
|
8
|
+
# class Sample
|
|
9
|
+
# attr_accessor :key
|
|
10
|
+
# end
|
|
11
|
+
# sample = Sample.new
|
|
12
|
+
#
|
|
13
|
+
# class_config = ClassConfiguration.new(Sample)
|
|
14
|
+
# class_config.add(:key)
|
|
15
|
+
#
|
|
16
|
+
# config = InstanceConfiguration.new(class_config)
|
|
17
|
+
# config.bind(sample)
|
|
18
|
+
#
|
|
19
|
+
# sample.key = 'value'
|
|
20
|
+
# config[:key] # => 'value'
|
|
21
|
+
#
|
|
22
|
+
# config[:key] = 'another'
|
|
23
|
+
# sample.key # => 'another'
|
|
24
|
+
#
|
|
25
|
+
# Non-config keys are simply stored:
|
|
26
|
+
#
|
|
27
|
+
# config[:not_a_key] = 'value'
|
|
28
|
+
# config[:not_a_key] # => 'value'
|
|
29
|
+
#
|
|
30
|
+
# config.store # => {:not_a_key => 'value'}
|
|
31
|
+
# config.to_hash # => {:key => 'another', :not_a_key => 'value'}
|
|
32
|
+
#
|
|
33
|
+
class InstanceConfiguration
|
|
34
|
+
|
|
35
|
+
# The bound receiver
|
|
36
|
+
attr_reader :receiver
|
|
37
|
+
|
|
38
|
+
# The underlying data store for non-config keys
|
|
39
|
+
attr_reader :store
|
|
40
|
+
|
|
41
|
+
# The ClassConfiguration specifying config keys
|
|
42
|
+
attr_reader :class_config
|
|
43
|
+
|
|
44
|
+
def initialize(class_config, receiver=nil)
|
|
45
|
+
@receiver = receiver
|
|
46
|
+
@store = {}
|
|
47
|
+
@class_config = class_config
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Binds self to the specified receiver. Mapped keys are
|
|
51
|
+
# removed from store and sent to their writer method on
|
|
52
|
+
# receiver.
|
|
53
|
+
def bind(receiver)
|
|
54
|
+
raise ArgumentError.new("receiver cannot be nil") if receiver == nil
|
|
55
|
+
|
|
56
|
+
class_config.each_pair do |key, config|
|
|
57
|
+
receiver.send(config.writer, store.delete(key))
|
|
58
|
+
end
|
|
59
|
+
@receiver = receiver
|
|
60
|
+
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns true if self is bound to a receiver
|
|
65
|
+
def bound?
|
|
66
|
+
receiver != nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Unbinds self from the specified receiver. Mapped values
|
|
70
|
+
# are stored in store. Returns the unbound receiver.
|
|
71
|
+
def unbind
|
|
72
|
+
class_config.each_pair do |key, config|
|
|
73
|
+
store[key] = receiver.send(config.reader)
|
|
74
|
+
end
|
|
75
|
+
r = receiver
|
|
76
|
+
@receiver = nil
|
|
77
|
+
|
|
78
|
+
r
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Duplicates self, returning an unbound InstanceConfiguration.
|
|
82
|
+
def dup
|
|
83
|
+
duplicate = super()
|
|
84
|
+
duplicate.instance_variable_set(:@receiver, nil)
|
|
85
|
+
duplicate.instance_variable_set(:@store, @store.dup)
|
|
86
|
+
duplicate
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Associates the value the key. If bound? and the key
|
|
90
|
+
# is a class_config key, then the value will be forwarded
|
|
91
|
+
# to the class_config.writer method on the receiver.
|
|
92
|
+
def []=(key, value)
|
|
93
|
+
case
|
|
94
|
+
when bound? && config = class_config.map[key.to_sym]
|
|
95
|
+
receiver.send(config.writer, value)
|
|
96
|
+
else store[key] = value
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Retrieves the value corresponding to the key. If bound?
|
|
101
|
+
# and the key is a class_config key, then the value is
|
|
102
|
+
# obtained from the :key method on the receiver.
|
|
103
|
+
def [](key)
|
|
104
|
+
case
|
|
105
|
+
when bound? && config = class_config.map[key.to_sym]
|
|
106
|
+
receiver.send(config.reader)
|
|
107
|
+
else store[key]
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# True if the key is assigned in self.
|
|
112
|
+
def has_key?(key)
|
|
113
|
+
(bound? && class_config.key?(key)) || store.has_key?(key)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Calls block once for each key-value pair stored in self.
|
|
117
|
+
def each_pair # :yields: key, value
|
|
118
|
+
class_config.each_pair do |key, config|
|
|
119
|
+
yield(key, receiver.send(config.reader))
|
|
120
|
+
end if bound?
|
|
121
|
+
|
|
122
|
+
store.each_pair do |key, value|
|
|
123
|
+
yield(key, value)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Equal if the to_hash values of self and another are equal.
|
|
128
|
+
def ==(another)
|
|
129
|
+
to_hash == another.to_hash
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Returns self as a hash.
|
|
133
|
+
def to_hash
|
|
134
|
+
hash = store.dup
|
|
135
|
+
class_config.keys.each do |key|
|
|
136
|
+
hash[key] = self[key]
|
|
137
|
+
end if bound?
|
|
138
|
+
hash
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Overrides default inspect to show the to_hash values.
|
|
142
|
+
def inspect
|
|
143
|
+
"#<#{self.class}:#{object_id} to_hash=#{to_hash.inspect}>"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
require 'tap/support/comment'
|
|
2
|
+
|
|
3
|
+
module Tap
|
|
4
|
+
module Support
|
|
5
|
+
|
|
6
|
+
# Lazydoc scans source files to pull out documentation. Lazydoc can find two
|
|
7
|
+
# types of documentation, constant attributes and code comments.
|
|
8
|
+
#
|
|
9
|
+
# === Constant Attributes
|
|
10
|
+
#
|
|
11
|
+
# Constant attributes are designated the same as constants in Ruby, but with
|
|
12
|
+
# an extra 'key' constant that must consist of only lowercase letters and/or
|
|
13
|
+
# underscores. This format assures that attributes are sytactically invalid
|
|
14
|
+
# outside of comments.
|
|
15
|
+
#
|
|
16
|
+
# When Lazydoc finds an attribute it parses a Comment value where the subject
|
|
17
|
+
# is the remainder of the line, and comment lines are parsed down until a
|
|
18
|
+
# non-comment line, an end key, or a new attribute is reached.
|
|
19
|
+
#
|
|
20
|
+
# str = %Q{
|
|
21
|
+
# # Const::Name::key subject for key
|
|
22
|
+
# # comment for key
|
|
23
|
+
# # parsed until a non-comment line
|
|
24
|
+
#
|
|
25
|
+
# # Const::Name::another subject for another
|
|
26
|
+
# # comment for another
|
|
27
|
+
# # parsed to an end key
|
|
28
|
+
# # Const::Name::another-
|
|
29
|
+
# #
|
|
30
|
+
# # ignored comment
|
|
31
|
+
# }
|
|
32
|
+
#
|
|
33
|
+
# lazydoc = Lazydoc.new
|
|
34
|
+
# lazydoc.resolve(str)
|
|
35
|
+
#
|
|
36
|
+
# lazydoc.to_hash {|comment| [comment.subject, comment.to_s] }
|
|
37
|
+
# # => {'Const::Name' => {
|
|
38
|
+
# # 'key' => ['subject for key', 'comment for key parsed until a non-comment line'],
|
|
39
|
+
# # 'another' => ['subject for another', 'comment for another parsed to an end key']
|
|
40
|
+
# # }}
|
|
41
|
+
#
|
|
42
|
+
# A constant name does not need to be specified; when no constant name is
|
|
43
|
+
# specified, Lazydoc will store the key as a default for the document. To
|
|
44
|
+
# turn off attribute parsing for a section of documentation, use start/stop
|
|
45
|
+
# keys:
|
|
46
|
+
#
|
|
47
|
+
# str = %Q{
|
|
48
|
+
# # :::-
|
|
49
|
+
# # Const::Name::not_parsed
|
|
50
|
+
# # :::+
|
|
51
|
+
#
|
|
52
|
+
# # Const::Name::parsed subject
|
|
53
|
+
# }
|
|
54
|
+
#
|
|
55
|
+
# lazydoc = Lazydoc.new
|
|
56
|
+
# lazydoc.resolve(str)
|
|
57
|
+
# lazydoc.to_hash {|comment| comment.subject } # => {'Const::Name' => {'parsed' => 'subject'}}
|
|
58
|
+
#
|
|
59
|
+
# ==== startdoc
|
|
60
|
+
#
|
|
61
|
+
# Lazydoc is completely separate from RDoc, but the syntax of Lazydoc was developed
|
|
62
|
+
# with RDoc in mind. To hide attributes in one line, make use of the RDoc
|
|
63
|
+
# <tt>:startdoc:</tt> document modifier like this (spaces added to keep them in the
|
|
64
|
+
# example):
|
|
65
|
+
#
|
|
66
|
+
# # :start doc::Const::Name::one hidden in RDoc
|
|
67
|
+
# # * This line is visible in RDoc.
|
|
68
|
+
# # :start doc::Const::Name::one-
|
|
69
|
+
# #
|
|
70
|
+
# #--
|
|
71
|
+
# # Const::Name::two
|
|
72
|
+
# # You can hide attribute comments like this.
|
|
73
|
+
# # Const::Name::two-
|
|
74
|
+
# #++
|
|
75
|
+
# #
|
|
76
|
+
# # * This line is also visible in RDoc.
|
|
77
|
+
#
|
|
78
|
+
# Here is the same text, actually in RDoc:
|
|
79
|
+
#
|
|
80
|
+
# :startdoc::Const::Name::one hidden in RDoc
|
|
81
|
+
# * This line is visible in RDoc.
|
|
82
|
+
# :startdoc::Const::Name::one-
|
|
83
|
+
#
|
|
84
|
+
#--
|
|
85
|
+
# Const::Name::two
|
|
86
|
+
# You can hide attribute comments like this.
|
|
87
|
+
# Const::Name::two-
|
|
88
|
+
#++
|
|
89
|
+
#
|
|
90
|
+
# * This line is also visible in RDoc.
|
|
91
|
+
#
|
|
92
|
+
# === Code Comments
|
|
93
|
+
#
|
|
94
|
+
# Code comments are lines marked for parsing if and when a Lazydoc gets resolved.
|
|
95
|
+
# Unlike constant attributes, the line is the subject of a code comment and
|
|
96
|
+
# comment lines are parsed up from it (effectively mimicking the behavior of
|
|
97
|
+
# RDoc).
|
|
98
|
+
#
|
|
99
|
+
# str = %Q{
|
|
100
|
+
# # comment lines for
|
|
101
|
+
# # the method
|
|
102
|
+
# def method
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
# # as in RDoc, the comment can be
|
|
106
|
+
# # separated from the method
|
|
107
|
+
#
|
|
108
|
+
# def another_method
|
|
109
|
+
# end
|
|
110
|
+
# }
|
|
111
|
+
#
|
|
112
|
+
# lazydoc = Lazydoc.new
|
|
113
|
+
# lazydoc.register(3)
|
|
114
|
+
# lazydoc.register(9)
|
|
115
|
+
# lazydoc.resolve(str)
|
|
116
|
+
#
|
|
117
|
+
# lazydoc.code_comments.collect {|comment| [comment.subject, comment.to_s] }
|
|
118
|
+
# # => [
|
|
119
|
+
# # ['def method', 'comment lines for the method'],
|
|
120
|
+
# # ['def another_method', 'as in RDoc, the comment can be separated from the method']]
|
|
121
|
+
#
|
|
122
|
+
class Lazydoc
|
|
123
|
+
|
|
124
|
+
# A regexp matching an attribute start or end. For the match:
|
|
125
|
+
#
|
|
126
|
+
# $1:: const_name
|
|
127
|
+
# $3:: key
|
|
128
|
+
# $4:: end flag
|
|
129
|
+
#
|
|
130
|
+
ATTRIBUTE_REGEXP = /(::|([A-Z][A-z]*::)+)([a-z_]+)(-?)/
|
|
131
|
+
|
|
132
|
+
# A regexp matching constants.
|
|
133
|
+
CONSTANT_REGEXP = /(::|([A-Z][A-z]*::)+)/
|
|
134
|
+
|
|
135
|
+
class << self
|
|
136
|
+
|
|
137
|
+
# A hash of (source_file, lazydoc) pairs tracking the
|
|
138
|
+
# Lazydoc instance for the given source file.
|
|
139
|
+
def registry
|
|
140
|
+
@registry ||= []
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns the lazydoc in registry for the specified source file.
|
|
144
|
+
# If no such lazydoc exists, one will be created for it.
|
|
145
|
+
def [](source_file)
|
|
146
|
+
source_file = File.expand_path(source_file.to_s)
|
|
147
|
+
lazydoc = registry.find {|doc| doc.source_file == source_file }
|
|
148
|
+
if lazydoc == nil
|
|
149
|
+
lazydoc = new(source_file)
|
|
150
|
+
registry << lazydoc
|
|
151
|
+
end
|
|
152
|
+
lazydoc
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Register the specified line numbers to the lazydoc for source_file.
|
|
156
|
+
# Returns a CodeComment corresponding to the line.
|
|
157
|
+
def register(source_file, line_number)
|
|
158
|
+
Lazydoc[source_file].register(line_number)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Resolves all lazydocs which include the specified code comments.
|
|
162
|
+
def resolve(code_comments)
|
|
163
|
+
registry.each do |doc|
|
|
164
|
+
next if (code_comments & doc.code_comments).empty?
|
|
165
|
+
doc.resolve
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Scans the specified file for attributes keyed by key and stores
|
|
170
|
+
# the resulting comments in the corresponding lazydoc.
|
|
171
|
+
# Returns the lazydoc.
|
|
172
|
+
def scan_doc(source_file, key)
|
|
173
|
+
lazydoc = nil
|
|
174
|
+
scan(File.read(source_file), key) do |const_name, attr_key, comment|
|
|
175
|
+
lazydoc = self[source_file] unless lazydoc
|
|
176
|
+
lazydoc.attributes(const_name)[attr_key] = comment
|
|
177
|
+
end
|
|
178
|
+
lazydoc
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Scans the string or StringScanner for attributes matching the key;
|
|
182
|
+
# keys may be patterns, they are incorporated into a regexp. Yields
|
|
183
|
+
# each (const_name, key, value) triplet to the mandatory block and
|
|
184
|
+
# skips regions delimited by the stop and start keys <tt>:-</tt>
|
|
185
|
+
# and <tt>:+</tt>.
|
|
186
|
+
#
|
|
187
|
+
# str = %Q{
|
|
188
|
+
# Const::Name::key value
|
|
189
|
+
# ::alt alt_value
|
|
190
|
+
#
|
|
191
|
+
# Ignored::Attribute::not_matched value
|
|
192
|
+
# :::-
|
|
193
|
+
# Also::Ignored::key value
|
|
194
|
+
# :::+
|
|
195
|
+
# Another::key another value
|
|
196
|
+
# }
|
|
197
|
+
#
|
|
198
|
+
# results = []
|
|
199
|
+
# Lazydoc.scan(str, 'key|alt') do |const_name, key, value|
|
|
200
|
+
# results << [const_name, key, value]
|
|
201
|
+
# end
|
|
202
|
+
#
|
|
203
|
+
# results
|
|
204
|
+
# # => [
|
|
205
|
+
# # ['Const::Name', 'key', 'value'],
|
|
206
|
+
# # ['', 'alt', 'alt_value'],
|
|
207
|
+
# # ['Another', 'key', 'another value']]
|
|
208
|
+
#
|
|
209
|
+
# Returns the StringScanner used during scanning.
|
|
210
|
+
def scan(str, key) # :yields: const_name, key, value
|
|
211
|
+
scanner = case str
|
|
212
|
+
when StringScanner then str
|
|
213
|
+
when String then StringScanner.new(str)
|
|
214
|
+
else raise TypeError, "can't convert #{str.class} into StringScanner or String"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
regexp = /(#{key})([ \r\t-].*$|$)/
|
|
218
|
+
while !scanner.eos?
|
|
219
|
+
break if scanner.skip_until(CONSTANT_REGEXP) == nil
|
|
220
|
+
const_name = scanner[1]
|
|
221
|
+
|
|
222
|
+
case
|
|
223
|
+
when scanner.scan(regexp)
|
|
224
|
+
yield(const_name.chomp('::'), scanner[1], scanner[2].strip)
|
|
225
|
+
when scanner.scan(/:-/)
|
|
226
|
+
scanner.skip_until(/:\+/)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
scanner
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Parses constant attributes from the string or StringScanner. Yields
|
|
234
|
+
# each (const_name, key, comment) triplet to the mandatory block
|
|
235
|
+
# and skips regions delimited by the stop and start keys <tt>:-</tt>
|
|
236
|
+
# and <tt>:+</tt>.
|
|
237
|
+
#
|
|
238
|
+
# str = %Q{
|
|
239
|
+
# # Const::Name::key subject for key
|
|
240
|
+
# # comment for key
|
|
241
|
+
#
|
|
242
|
+
# # :::-
|
|
243
|
+
# # Ignored::key value
|
|
244
|
+
# # :::+
|
|
245
|
+
#
|
|
246
|
+
# # Ignored text before attribute ::another subject for another
|
|
247
|
+
# # comment for another
|
|
248
|
+
# }
|
|
249
|
+
#
|
|
250
|
+
# results = []
|
|
251
|
+
# Lazydoc.parse(str) do |const_name, key, comment|
|
|
252
|
+
# results << [const_name, key, comment.subject, comment.to_s]
|
|
253
|
+
# end
|
|
254
|
+
#
|
|
255
|
+
# results
|
|
256
|
+
# # => [
|
|
257
|
+
# # ['Const::Name', 'key', 'subject for key', 'comment for key'],
|
|
258
|
+
# # ['', 'another', 'subject for another', 'comment for another']]
|
|
259
|
+
#
|
|
260
|
+
# Returns the StringScanner used during scanning.
|
|
261
|
+
def parse(str) # :yields: const_name, key, comment
|
|
262
|
+
scanner = case str
|
|
263
|
+
when StringScanner then str
|
|
264
|
+
when String then StringScanner.new(str)
|
|
265
|
+
else raise TypeError, "can't convert #{str.class} into StringScanner or String"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
scan(scanner, '[a-z_]+') do |const_name, key, value|
|
|
269
|
+
comment = Comment.parse(scanner, false) do |line|
|
|
270
|
+
if line =~ /::/ && line =~ ATTRIBUTE_REGEXP
|
|
271
|
+
# rewind to capture the next attribute unless an end is specified.
|
|
272
|
+
scanner.unscan unless !$4.empty? && $1.chomp("::") == const_name && $3 == key
|
|
273
|
+
true
|
|
274
|
+
else false
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
comment.subject = value
|
|
278
|
+
yield(const_name, key, comment)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
include Enumerable
|
|
284
|
+
|
|
285
|
+
# The source file for self, used in resolving comments and
|
|
286
|
+
# attributes.
|
|
287
|
+
attr_reader :source_file
|
|
288
|
+
|
|
289
|
+
# An array of Comment objects identifying lines resolved or
|
|
290
|
+
# to-be-resolved for self.
|
|
291
|
+
attr_reader :code_comments
|
|
292
|
+
|
|
293
|
+
# A hash of (const_name, attributes) pairs tracking the constant
|
|
294
|
+
# attributes resolved or to-be-resolved for self. Attributes
|
|
295
|
+
# are hashes of (key, comment) pairs.
|
|
296
|
+
attr_reader :const_attrs
|
|
297
|
+
|
|
298
|
+
def initialize(source_file=nil)
|
|
299
|
+
self.source_file = source_file
|
|
300
|
+
@code_comments = []
|
|
301
|
+
@const_attrs = {}
|
|
302
|
+
@resolved = false
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Sets the source file for self. Expands the source file path if necessary.
|
|
306
|
+
def source_file=(source_file)
|
|
307
|
+
@source_file = source_file == nil ? nil : File.expand_path(source_file)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Returns the attributes for the specified const_name.
|
|
311
|
+
def attributes(const_name)
|
|
312
|
+
const_attrs[const_name] ||= {}
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Returns default document attributes (ie attributes(''))
|
|
316
|
+
def default_attributes
|
|
317
|
+
attributes('')
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Returns the attributes for const_name merged to default_attributes.
|
|
321
|
+
# Set merge_defaults to false to get just the attributes for const_name.
|
|
322
|
+
def [](const_name, merge_defaults=true)
|
|
323
|
+
merge_defaults ? default_attributes.merge(attributes(const_name)) : attributes(const_name)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Yields each (const_name, attributes) pair to the block; const_names where
|
|
327
|
+
# the attributes are empty are skipped.
|
|
328
|
+
def each
|
|
329
|
+
const_attrs.each_pair do |const_name, attrs|
|
|
330
|
+
yield(const_name, attrs) unless attrs.empty?
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Returns true if the attributes for const_name are not empty.
|
|
335
|
+
def has_const?(const_name)
|
|
336
|
+
const_attrs.each_pair do |constname, attrs|
|
|
337
|
+
next unless constname == const_name
|
|
338
|
+
return !attrs.empty?
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
false
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Returns an array of the constant names in self, for which
|
|
345
|
+
# the constant attributes are not empty.
|
|
346
|
+
def const_names
|
|
347
|
+
names = []
|
|
348
|
+
const_attrs.each_pair do |const_name, attrs|
|
|
349
|
+
names << const_name unless attrs.empty?
|
|
350
|
+
end
|
|
351
|
+
names
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Register the specified line number to self. Returns a
|
|
355
|
+
# Comment object corresponding to the line.
|
|
356
|
+
def register(line_number)
|
|
357
|
+
comment = code_comments.find {|c| c.line_number == line_number }
|
|
358
|
+
|
|
359
|
+
if comment == nil
|
|
360
|
+
comment = Comment.new(line_number)
|
|
361
|
+
code_comments << comment
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
comment
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Returns true if the code_comments for source_file are frozen.
|
|
368
|
+
def resolved?
|
|
369
|
+
@resolved
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
attr_writer :resolved
|
|
373
|
+
|
|
374
|
+
def resolve(str=nil, comment_regexp=nil) # :yields: comment, match
|
|
375
|
+
return(false) if resolved?
|
|
376
|
+
|
|
377
|
+
if str == nil
|
|
378
|
+
raise ArgumentError, "no source file specified" unless source_file && File.exists?(source_file)
|
|
379
|
+
str = File.read(source_file)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
Lazydoc.parse(str) do |const_name, key, comment|
|
|
383
|
+
attributes(const_name)[key] = comment
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
lines = str.split(/\r?\n/)
|
|
387
|
+
lines.each_with_index do |line, line_number|
|
|
388
|
+
next unless line =~ comment_regexp
|
|
389
|
+
comment = register(line_number)
|
|
390
|
+
yield(comment, $~) if block_given?
|
|
391
|
+
end unless comment_regexp == nil
|
|
392
|
+
|
|
393
|
+
code_comments.collect! do |comment|
|
|
394
|
+
line_number = comment.line_number
|
|
395
|
+
comment.subject = lines[line_number] if comment.subject == nil
|
|
396
|
+
|
|
397
|
+
# remove whitespace lines
|
|
398
|
+
line_number -= 1
|
|
399
|
+
while lines[line_number].strip.empty?
|
|
400
|
+
line_number -= 1
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# put together the comment
|
|
404
|
+
while line_number >= 0
|
|
405
|
+
break unless comment.prepend(lines[line_number])
|
|
406
|
+
line_number -= 1
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
comment
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
@resolved = true
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def to_hash
|
|
416
|
+
const_hash = {}
|
|
417
|
+
const_names.sort.each do |const_name|
|
|
418
|
+
attr_hash = {}
|
|
419
|
+
self[const_name, false].each_pair do |key, comment|
|
|
420
|
+
attr_hash[key] = (block_given? ? yield(comment) : comment)
|
|
421
|
+
end
|
|
422
|
+
const_hash[const_name] = attr_hash
|
|
423
|
+
end
|
|
424
|
+
const_hash
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|