tap 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +12 -0
- data/MIT-LICENSE +0 -2
- data/README +23 -32
- data/bin/rap +116 -0
- data/bin/tap +6 -9
- data/cgi/run.rb +67 -0
- data/cmd/console.rb +1 -1
- data/cmd/destroy.rb +4 -4
- data/cmd/generate.rb +4 -4
- data/cmd/manifest.rb +61 -53
- data/cmd/run.rb +8 -75
- data/doc/Class Reference +130 -121
- data/doc/Command Reference +76 -124
- data/doc/Syntax Reference +290 -0
- data/doc/Tutorial +305 -237
- data/lib/tap/app.rb +140 -467
- data/lib/tap/constants.rb +2 -2
- data/lib/tap/declarations.rb +211 -0
- data/lib/tap/env.rb +171 -193
- data/lib/tap/exe.rb +100 -21
- data/lib/tap/file_task.rb +3 -3
- data/lib/tap/generator/base.rb +1 -1
- data/lib/tap/generator/destroy.rb +10 -10
- data/lib/tap/generator/generate.rb +29 -18
- data/lib/tap/generator/generators/command/command_generator.rb +2 -2
- data/lib/tap/generator/generators/command/templates/command.erb +2 -2
- data/lib/tap/generator/generators/config/config_generator.rb +3 -3
- data/lib/tap/generator/generators/config/templates/doc.erb +1 -1
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +1 -1
- data/lib/tap/generator/generators/file_task/templates/task.erb +1 -1
- data/lib/tap/generator/generators/file_task/templates/test.erb +1 -1
- data/lib/tap/generator/generators/generator/generator_generator.rb +27 -0
- data/lib/tap/generator/generators/generator/templates/task.erb +27 -0
- data/lib/tap/generator/generators/root/root_generator.rb +13 -13
- data/lib/tap/generator/generators/root/templates/README +0 -0
- data/lib/tap/generator/generators/root/templates/Rakefile +2 -2
- data/lib/tap/generator/generators/root/templates/gemspec +4 -5
- data/lib/tap/generator/generators/root/templates/tapfile +11 -8
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
- data/lib/tap/generator/generators/task/task_generator.rb +1 -3
- data/lib/tap/generator/generators/task/templates/test.erb +1 -3
- data/lib/tap/patches/optparse/summarize.rb +62 -0
- data/lib/tap/root.rb +41 -29
- data/lib/tap/support/aggregator.rb +16 -3
- data/lib/tap/support/assignments.rb +10 -9
- data/lib/tap/support/audit.rb +58 -64
- data/lib/tap/support/class_configuration.rb +33 -44
- data/lib/tap/support/combinator.rb +125 -0
- data/lib/tap/support/configurable.rb +13 -14
- data/lib/tap/support/configurable_class.rb +21 -43
- data/lib/tap/support/configuration.rb +55 -9
- data/lib/tap/support/constant.rb +87 -13
- data/lib/tap/support/constant_manifest.rb +116 -0
- data/lib/tap/support/dependencies.rb +54 -0
- data/lib/tap/support/dependency.rb +44 -0
- data/lib/tap/support/executable.rb +247 -32
- data/lib/tap/support/executable_queue.rb +1 -1
- data/lib/tap/support/gems/rake.rb +29 -8
- data/lib/tap/support/gems.rb +10 -30
- data/lib/tap/support/instance_configuration.rb +29 -3
- data/lib/tap/support/intern.rb +46 -0
- data/lib/tap/support/join.rb +143 -0
- data/lib/tap/support/joins/fork.rb +19 -0
- data/lib/tap/support/joins/merge.rb +22 -0
- data/lib/tap/support/joins/sequence.rb +21 -0
- data/lib/tap/support/joins/switch.rb +25 -0
- data/lib/tap/support/joins/sync_merge.rb +63 -0
- data/lib/tap/support/joins.rb +15 -0
- data/lib/tap/support/lazy_attributes.rb +17 -2
- data/lib/tap/support/lazydoc/comment.rb +503 -0
- data/lib/tap/support/lazydoc/config.rb +17 -0
- data/lib/tap/support/lazydoc/definition.rb +36 -0
- data/lib/tap/support/lazydoc/document.rb +152 -0
- data/lib/tap/support/lazydoc/method.rb +24 -0
- data/lib/tap/support/lazydoc.rb +269 -343
- data/lib/tap/support/manifest.rb +121 -103
- data/lib/tap/support/minimap.rb +90 -0
- data/lib/tap/support/node.rb +56 -0
- data/lib/tap/support/parser.rb +436 -0
- data/lib/tap/support/schema.rb +359 -0
- data/lib/tap/support/shell_utils.rb +3 -5
- data/lib/tap/support/string_ext.rb +60 -0
- data/lib/tap/support/tdoc.rb +7 -2
- data/lib/tap/support/templater.rb +30 -16
- data/lib/tap/support/validation.rb +77 -8
- data/lib/tap/task.rb +431 -143
- data/lib/tap/tasks/dump.rb +15 -10
- data/lib/tap/tasks/load.rb +112 -0
- data/lib/tap/tasks/rake.rb +4 -41
- data/lib/tap/test/assertions.rb +38 -0
- data/lib/tap/test/env_vars.rb +1 -1
- data/lib/tap/test/extensions.rb +79 -0
- data/lib/tap/test/file_test.rb +420 -0
- data/lib/tap/test/file_test_class.rb +12 -0
- data/lib/tap/test/regexp_escape.rb +87 -0
- data/lib/tap/test/script_test.rb +46 -0
- data/lib/tap/test/script_tester.rb +115 -0
- data/lib/tap/test/subset_test.rb +260 -0
- data/lib/tap/test/subset_test_class.rb +99 -0
- data/lib/tap/test/{tap_methods.rb → tap_test.rb} +45 -43
- data/lib/tap/test/utils.rb +231 -0
- data/lib/tap/test.rb +53 -26
- data/lib/tap.rb +3 -20
- metadata +50 -27
- data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +0 -15
- data/lib/tap/patches/rake/rake_test_loader.rb +0 -8
- data/lib/tap/patches/rake/testtask.rb +0 -57
- data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -51
- data/lib/tap/patches/ruby19/parsedate.rb +0 -16
- data/lib/tap/support/batchable.rb +0 -47
- data/lib/tap/support/batchable_class.rb +0 -107
- data/lib/tap/support/command_line.rb +0 -98
- data/lib/tap/support/comment.rb +0 -270
- data/lib/tap/support/constant_utils.rb +0 -127
- data/lib/tap/support/declarations.rb +0 -111
- data/lib/tap/support/framework.rb +0 -83
- data/lib/tap/support/framework_class.rb +0 -180
- data/lib/tap/support/run_error.rb +0 -39
- data/lib/tap/support/summary.rb +0 -30
- data/lib/tap/test/file_methods.rb +0 -377
- data/lib/tap/test/script_methods/script_test.rb +0 -98
- data/lib/tap/test/script_methods.rb +0 -107
- data/lib/tap/test/subset_methods.rb +0 -420
- data/lib/tap/workflow.rb +0 -200
@@ -1,14 +1,15 @@
|
|
1
1
|
require 'tap/support/class_configuration'
|
2
2
|
require 'tap/support/validation'
|
3
3
|
require 'tap/support/lazy_attributes'
|
4
|
+
require 'tap/support/lazydoc/config'
|
4
5
|
|
5
6
|
module Tap
|
6
7
|
module Support
|
7
8
|
autoload(:Templater, 'tap/support/templater')
|
8
9
|
|
9
|
-
# ConfigurableClass
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# ConfigurableClass defines class methods used by a Configurable class to
|
11
|
+
# declare configurations. In addition to registering key-value pairs,
|
12
|
+
# these methods generate readers and writers, much like attr_accessor.
|
12
13
|
#
|
13
14
|
# class ConfigurableClass
|
14
15
|
# extend ConfigurableClass
|
@@ -21,31 +22,6 @@ module Tap
|
|
21
22
|
# c.respond_to?('one') # => true
|
22
23
|
# c.respond_to?('one=') # => true
|
23
24
|
#
|
24
|
-
# If a block is given, the block will be used to create the writer method
|
25
|
-
# for the config. Used in this manner, config defines a <tt>config_key=</tt> method
|
26
|
-
# wherein <tt>@config_key</tt> will be set to the return value of the block.
|
27
|
-
#
|
28
|
-
# class AnotherConfigurableClass
|
29
|
-
# extend ConfigurableClass
|
30
|
-
# config(:one, 'one') {|value| value.upcase }
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# ac = AnotherConfigurableClass.new
|
34
|
-
# ac.one = 'value'
|
35
|
-
# ac.one # => 'VALUE'
|
36
|
-
#
|
37
|
-
# The block has class-context in this case. To have instance-context, use the
|
38
|
-
# config_attr method which defines the writer method using the block directly.
|
39
|
-
#
|
40
|
-
# class YetAnotherConfigurableClass
|
41
|
-
# extend ConfigurableClass
|
42
|
-
# config_attr(:one, 'one') {|value| @one = value.reverse }
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# ac = YetAnotherConfigurableClass.new
|
46
|
-
# ac.one = 'value'
|
47
|
-
# ac.one # => 'eulav'
|
48
|
-
#
|
49
25
|
module ConfigurableClass
|
50
26
|
include Tap::Support::LazyAttributes
|
51
27
|
|
@@ -53,11 +29,11 @@ module Tap
|
|
53
29
|
attr_reader :configurations
|
54
30
|
|
55
31
|
# Sets the source_file for base and initializes base.configurations.
|
56
|
-
def self.extended(base)
|
32
|
+
def self.extended(base) # :nodoc:
|
57
33
|
caller.each_with_index do |line, index|
|
58
34
|
case line
|
59
35
|
when /\/configurable.rb/ then next
|
60
|
-
when
|
36
|
+
when Lazydoc::CALLER_REGEXP
|
61
37
|
base.instance_variable_set(:@source_file, File.expand_path($1))
|
62
38
|
break
|
63
39
|
end
|
@@ -69,9 +45,9 @@ module Tap
|
|
69
45
|
# When subclassed, the parent.configurations are duplicated and passed to
|
70
46
|
# the child class where they can be extended/modified without affecting
|
71
47
|
# the configurations of the parent class.
|
72
|
-
def inherited(child)
|
48
|
+
def inherited(child) # :nodoc:
|
73
49
|
unless child.instance_variable_defined?(:@source_file)
|
74
|
-
caller.first =~
|
50
|
+
caller.first =~ Lazydoc::CALLER_REGEXP
|
75
51
|
child.instance_variable_set(:@source_file, File.expand_path($1))
|
76
52
|
end
|
77
53
|
|
@@ -79,6 +55,7 @@ module Tap
|
|
79
55
|
super
|
80
56
|
end
|
81
57
|
|
58
|
+
# Returns the lazydoc for self.
|
82
59
|
def lazydoc(resolve=true)
|
83
60
|
Lazydoc.resolve_comments(configurations.code_comments) if resolve
|
84
61
|
super
|
@@ -87,8 +64,8 @@ module Tap
|
|
87
64
|
# Loads the contents of path as YAML. Returns an empty hash if the path
|
88
65
|
# is empty, does not exist, or is not a file.
|
89
66
|
def load_config(path)
|
90
|
-
|
91
|
-
|
67
|
+
# the last check prevents YAML from auto-loading itself for empty files
|
68
|
+
return {} if path == nil || !File.file?(path) || File.size(path) == 0
|
92
69
|
YAML.load_file(path) || {}
|
93
70
|
end
|
94
71
|
|
@@ -250,8 +227,8 @@ module Tap
|
|
250
227
|
caller.each do |line|
|
251
228
|
case line
|
252
229
|
when /in .config.$/ then next
|
253
|
-
when
|
254
|
-
options[:desc] = Lazydoc.register($1, $3.to_i - 1)
|
230
|
+
when Lazydoc::CALLER_REGEXP
|
231
|
+
options[:desc] = Lazydoc.register($1, $3.to_i - 1, Lazydoc::Config)
|
255
232
|
break
|
256
233
|
end
|
257
234
|
end if options[:desc] == nil
|
@@ -270,10 +247,10 @@ module Tap
|
|
270
247
|
# blocks, such as switch (Validation::SWITCH) and list
|
271
248
|
# (Validation::LIST).
|
272
249
|
def arg_type(block) # :nodoc:
|
273
|
-
case
|
274
|
-
when Validation::SWITCH then :switch
|
275
|
-
when Validation::FLAG then :flag
|
276
|
-
when Validation::LIST then :list
|
250
|
+
case
|
251
|
+
when block == Validation::SWITCH then :switch
|
252
|
+
when block == Validation::FLAG then :flag
|
253
|
+
when block == Validation::LIST then :list
|
277
254
|
else nil
|
278
255
|
end
|
279
256
|
end
|
@@ -282,12 +259,13 @@ module Tap
|
|
282
259
|
# blocks, such as switch (Validation::ARRAY) and list
|
283
260
|
# (Validation::HASH).
|
284
261
|
def arg_name(block) # :nodoc:
|
285
|
-
case
|
286
|
-
when Validation::ARRAY then "'[a, b, c]'"
|
287
|
-
when Validation::HASH then "'{one: 1, two: 2}'"
|
262
|
+
case
|
263
|
+
when block == Validation::ARRAY then "'[a, b, c]'"
|
264
|
+
when block == Validation::HASH then "'{one: 1, two: 2}'"
|
288
265
|
else nil
|
289
266
|
end
|
290
267
|
end
|
268
|
+
|
291
269
|
end
|
292
270
|
end
|
293
271
|
end
|
@@ -1,8 +1,12 @@
|
|
1
1
|
module Tap
|
2
2
|
module Support
|
3
|
+
|
4
|
+
# Represents a configuration declared by a Configurable class.
|
3
5
|
class Configuration
|
4
6
|
class << self
|
5
|
-
|
7
|
+
|
8
|
+
# Matches a short option
|
9
|
+
SHORT_OPTION = /^-[A-z]$/
|
6
10
|
|
7
11
|
# Turns the input string into a short-format option. Raises
|
8
12
|
# an error if the option does not match SHORT_REGEXP.
|
@@ -13,11 +17,12 @@ module Tap
|
|
13
17
|
def shortify(str)
|
14
18
|
str = str.to_s
|
15
19
|
str = "-#{str}" unless str[0] == ?-
|
16
|
-
raise "invalid short option: #{str}" unless str =~
|
20
|
+
raise "invalid short option: #{str}" unless str =~ SHORT_OPTION
|
17
21
|
str
|
18
22
|
end
|
19
|
-
|
20
|
-
|
23
|
+
|
24
|
+
# Matches a long option
|
25
|
+
LONG_OPTION = /^--(\[no-\])?([A-z][\w-]*)$/
|
21
26
|
|
22
27
|
# Turns the input string into a long-format option. Raises
|
23
28
|
# an error if the option does not match LONG_REGEXP.
|
@@ -33,7 +38,7 @@ module Tap
|
|
33
38
|
str = "--#{str}" unless str.index("--")
|
34
39
|
str.gsub!(/_/, '-') if hyphenize
|
35
40
|
|
36
|
-
raise "invalid long option: #{str}" unless str =~
|
41
|
+
raise "invalid long option: #{str}" unless str =~ LONG_OPTION
|
37
42
|
|
38
43
|
if switch_notation && $1.nil?
|
39
44
|
str = "--[no-]#{$2}"
|
@@ -43,12 +48,24 @@ module Tap
|
|
43
48
|
end
|
44
49
|
end
|
45
50
|
|
51
|
+
# The name of the configuration
|
46
52
|
attr_reader :name
|
53
|
+
|
54
|
+
# The reader method, by default name
|
47
55
|
attr_reader :reader
|
56
|
+
|
57
|
+
# The writer method, by default name=
|
48
58
|
attr_reader :writer
|
59
|
+
|
60
|
+
# True if the default value may be duplicated
|
49
61
|
attr_reader :duplicable
|
62
|
+
|
63
|
+
# An array of optional metadata for self
|
50
64
|
attr_reader :attributes
|
51
|
-
|
65
|
+
|
66
|
+
# Initializes a new Configuration with the specified name and default
|
67
|
+
# value. Options may specify an alternate reader/writer; any
|
68
|
+
# additional options are set as attributes.
|
52
69
|
def initialize(name, default=nil, options={})
|
53
70
|
@name = name
|
54
71
|
self.default = default
|
@@ -59,11 +76,12 @@ module Tap
|
|
59
76
|
end
|
60
77
|
|
61
78
|
# Sets the default value for self and determines if the
|
62
|
-
# default is duplicable
|
63
|
-
# Numeric, and
|
79
|
+
# default is duplicable. Non-duplicable values include
|
80
|
+
# nil, true, false, Symbol, Numeric, and any object that
|
81
|
+
# does not respond to dup.
|
64
82
|
def default=(value)
|
65
83
|
@duplicable = case value
|
66
|
-
when nil, true, false, Symbol, Numeric then false
|
84
|
+
when nil, true, false, Symbol, Numeric, Method then false
|
67
85
|
else value.respond_to?(:dup)
|
68
86
|
end
|
69
87
|
|
@@ -88,22 +106,29 @@ module Tap
|
|
88
106
|
@writer = value == nil ? value : value.to_sym
|
89
107
|
end
|
90
108
|
|
109
|
+
# The argument name for self: either attributes[:arg_name]
|
110
|
+
# or name.to_s.upcase
|
91
111
|
def arg_name
|
92
112
|
attributes[:arg_name] || name.to_s.upcase
|
93
113
|
end
|
94
114
|
|
115
|
+
# The argument type for self: either attributes[:arg_type]
|
116
|
+
# or :mandatory
|
95
117
|
def arg_type
|
96
118
|
attributes[:arg_type] || :mandatory
|
97
119
|
end
|
98
120
|
|
121
|
+
# The long version of name.
|
99
122
|
def long(switch_notation=false, hyphenize=true)
|
100
123
|
Configuration.longify(attributes[:long] || name.to_s, switch_notation, hyphenize)
|
101
124
|
end
|
102
125
|
|
126
|
+
# The short version of name.
|
103
127
|
def short
|
104
128
|
attributes[:short] ? Configuration.shortify(attributes[:short]) : nil
|
105
129
|
end
|
106
130
|
|
131
|
+
# The description for self: attributes[:desc]
|
107
132
|
def desc
|
108
133
|
attributes[:desc]
|
109
134
|
end
|
@@ -119,6 +144,27 @@ module Tap
|
|
119
144
|
self.default(false) == another.default(false)
|
120
145
|
end
|
121
146
|
|
147
|
+
# Returns self as an argv that can be used to register
|
148
|
+
# an option with OptionParser.
|
149
|
+
def to_optparse_argv
|
150
|
+
argtype = case arg_type
|
151
|
+
when :optional
|
152
|
+
"#{long} [#{arg_name}]"
|
153
|
+
when :switch
|
154
|
+
long(true)
|
155
|
+
when :flag
|
156
|
+
long
|
157
|
+
when :list
|
158
|
+
"#{long} a,b,c"
|
159
|
+
when :mandatory, nil
|
160
|
+
"#{long} #{arg_name}"
|
161
|
+
else
|
162
|
+
raise "unknown arg_type: #{arg_type}"
|
163
|
+
end
|
164
|
+
|
165
|
+
[short, argtype, desc].compact
|
166
|
+
end
|
167
|
+
|
122
168
|
end
|
123
169
|
end
|
124
170
|
end
|
data/lib/tap/support/constant.rb
CHANGED
@@ -1,18 +1,79 @@
|
|
1
|
-
require 'tap/support/
|
2
|
-
class String # :nodoc:
|
3
|
-
include Tap::Support::ConstantUtils
|
4
|
-
end
|
1
|
+
require 'tap/support/string_ext'
|
5
2
|
|
6
3
|
module Tap
|
7
4
|
module Support
|
5
|
+
|
6
|
+
# A Constant serves as a placeholder for an actual constant, sort of like
|
7
|
+
# autoload. Use the constantize method to retrieve the actual constant;
|
8
|
+
# if it doesn't exist, constantize requires require_path and tries again.
|
9
|
+
#
|
10
|
+
# Object.const_defined?(:Net) # => false
|
11
|
+
# $".include?('net/http') # => false
|
12
|
+
#
|
13
|
+
# http = Constant.new('Net::HTTP', 'net/http')
|
14
|
+
# http.constantize # => Net::HTTP
|
15
|
+
# $".include?('net/http') # => true
|
16
|
+
#
|
8
17
|
class Constant
|
18
|
+
class << self
|
19
|
+
|
20
|
+
# Tries to find a declared constant under base with the specified
|
21
|
+
# const_name. When a constant is missing, constantize yields
|
22
|
+
# the current base and any non-existant constant names the block,
|
23
|
+
# if given, or raises a NameError. The block is expected
|
24
|
+
# to return the proper constant.
|
25
|
+
#
|
26
|
+
# module ConstName; end
|
27
|
+
#
|
28
|
+
# Constant.constantize('ConstName') # => ConstName
|
29
|
+
# Constant.constantize('Non::Existant') { ConstName } # => ConstName
|
30
|
+
#
|
31
|
+
def constantize(const_name, base=Object) # :yields: base, missing_const_names
|
32
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ const_name
|
33
|
+
raise NameError, "#{const_name.inspect} is not a valid constant name!"
|
34
|
+
end
|
35
|
+
|
36
|
+
constants = $1.split(/::/)
|
37
|
+
while !constants.empty?
|
38
|
+
unless const_is_defined?(base, constants[0])
|
39
|
+
if block_given?
|
40
|
+
return yield(base, constants)
|
41
|
+
else
|
42
|
+
raise NameError.new("uninitialized constant #{const_name}", constants[0])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
base = base.const_get(constants.shift)
|
46
|
+
end
|
47
|
+
base
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# helper method. Determines if a constant named
|
53
|
+
# name is defined in const. The implementation
|
54
|
+
# (annoyingly) has to be different for ruby 1.9
|
55
|
+
# due to changes in the API.
|
56
|
+
case RUBY_VERSION
|
57
|
+
when /^1.9/
|
58
|
+
def const_is_defined?(const, name) # :nodoc:
|
59
|
+
const.const_defined?(name, false)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
def const_is_defined?(const, name) # :nodoc:
|
63
|
+
const.const_defined?(name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
9
67
|
|
10
|
-
# The
|
68
|
+
# The constant name
|
11
69
|
attr_reader :name
|
12
70
|
|
13
|
-
# The path to load to initialize
|
71
|
+
# The path to load to initialize a missing constant
|
14
72
|
attr_reader :require_path
|
15
|
-
|
73
|
+
|
74
|
+
# Initializes a new Constant with the specified constant
|
75
|
+
# name and require_path. The name should be a valid
|
76
|
+
# constant name.
|
16
77
|
def initialize(name, require_path=nil)
|
17
78
|
@name = name
|
18
79
|
@require_path = require_path
|
@@ -48,23 +109,36 @@ module Tap
|
|
48
109
|
@nesting_depth ||= nesting.split(/::/).length
|
49
110
|
end
|
50
111
|
|
51
|
-
# Returns the document for require_path
|
112
|
+
# Returns the Lazydoc document for require_path.
|
52
113
|
def document
|
53
|
-
require_path ?
|
114
|
+
require_path ? Lazydoc[require_path] : nil
|
54
115
|
end
|
55
116
|
|
117
|
+
# True if another is a Constant with the same name
|
118
|
+
# and require_path as self.
|
56
119
|
def ==(another)
|
57
120
|
another.kind_of?(Constant) &&
|
58
121
|
another.name == self.name &&
|
59
122
|
another.require_path == self.require_path
|
60
123
|
end
|
61
|
-
|
124
|
+
|
125
|
+
# Looks up and returns the constant indicated by name.
|
126
|
+
# If the constant cannot be found, the constantize
|
127
|
+
# requires require_path and tries again.
|
128
|
+
#
|
129
|
+
# Raises a NameError if the constant cannot be found.
|
62
130
|
def constantize
|
63
|
-
name
|
64
|
-
require require_path
|
65
|
-
|
131
|
+
Constant.constantize(name) do
|
132
|
+
require require_path if require_path
|
133
|
+
Constant.constantize(name)
|
66
134
|
end
|
67
135
|
end
|
136
|
+
|
137
|
+
# Returns a string like:
|
138
|
+
# "#<Tap::Support::Constant:object_id Const::Name (require_path)>"
|
139
|
+
def inspect
|
140
|
+
"#<#{self.class}:#{object_id} #{name}#{@require_path == nil ? "" : " (#{@require_path})"}>"
|
141
|
+
end
|
68
142
|
end
|
69
143
|
end
|
70
144
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'tap/support/manifest'
|
2
|
+
require 'tap/support/constant'
|
3
|
+
|
4
|
+
module Tap
|
5
|
+
module Support
|
6
|
+
|
7
|
+
# ConstantManifest builds a manifest of Constant entries using Lazydoc.
|
8
|
+
# The idea is that Lazydoc can find files with resouces of a specific type
|
9
|
+
# (ex tasks) and Constant can reference those resouces and load them as
|
10
|
+
# necessary. ConstantManifest registers paths so that they may be lazily
|
11
|
+
# scanned when searching for a specific resource.
|
12
|
+
class ConstantManifest < Support::Manifest
|
13
|
+
|
14
|
+
# The attribute identifying resources in a file
|
15
|
+
attr_reader :const_attr
|
16
|
+
|
17
|
+
# Registered [root, [paths]] pairs that will be searched
|
18
|
+
# for the const_attr
|
19
|
+
attr_reader :search_paths
|
20
|
+
|
21
|
+
# The current index of search_paths
|
22
|
+
attr_reader :search_path_index
|
23
|
+
|
24
|
+
# The current index of paths
|
25
|
+
attr_reader :path_index
|
26
|
+
|
27
|
+
# Initializes a new ConstantManifest
|
28
|
+
def initialize(const_attr)
|
29
|
+
@const_attr = const_attr
|
30
|
+
@search_paths = []
|
31
|
+
@search_path_index = 0
|
32
|
+
@path_index = 0
|
33
|
+
super([])
|
34
|
+
end
|
35
|
+
|
36
|
+
# Registers the files matching pattern under dir. Returns self.
|
37
|
+
def register(dir, pattern)
|
38
|
+
paths = Dir.glob(File.join(dir, pattern)).select {|file| File.file?(file) }
|
39
|
+
search_paths << [dir, paths.sort]
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Searches all paths for entries and adds them to self. Returns self.
|
44
|
+
def build
|
45
|
+
each {|entry| } unless built?
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# True if there are no more paths to search
|
50
|
+
# (ie search_path_index == search_paths.length)
|
51
|
+
def built?
|
52
|
+
search_path_index == search_paths.length
|
53
|
+
end
|
54
|
+
|
55
|
+
# Sets search_path_index and path_index to zero and clears entries.
|
56
|
+
# Returns self.
|
57
|
+
def reset
|
58
|
+
# Support::Lazydoc[path].resolved = false
|
59
|
+
@entries.clear
|
60
|
+
@search_path_index = 0
|
61
|
+
@path_index = 0
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
# Yields each entry to the block. Unless built?, each lazily
|
66
|
+
# iterates over search_paths to look for new entries.
|
67
|
+
def each
|
68
|
+
entries.each do |entry|
|
69
|
+
yield(entry)
|
70
|
+
end
|
71
|
+
|
72
|
+
search_paths[search_path_index, search_paths.length - search_path_index].each do |(path_root, paths)|
|
73
|
+
paths[path_index, paths.length - path_index].each do |path|
|
74
|
+
new_entries = resolve(path_root, path) - entries
|
75
|
+
entries.concat(new_entries)
|
76
|
+
|
77
|
+
@path_index += 1
|
78
|
+
new_entries.each {|entry| yield(entry) }
|
79
|
+
end
|
80
|
+
|
81
|
+
@search_path_index += 1
|
82
|
+
@path_index = 0
|
83
|
+
end unless built?
|
84
|
+
end
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
def minikey(const) # :nodoc:
|
89
|
+
const.path
|
90
|
+
end
|
91
|
+
|
92
|
+
# Scans path for constants having const_attr, and initializes Constant
|
93
|
+
# objects for each. If the document has no default_const_name set,
|
94
|
+
# resolve will set the default_const_name based on the relative
|
95
|
+
# filepath from path_root to path.
|
96
|
+
def resolve(path_root, path)
|
97
|
+
entries = []
|
98
|
+
if document = Lazydoc.scan_doc(path, const_attr)
|
99
|
+
if document.default_const_name.empty?
|
100
|
+
relative_path = Root.relative_filepath(path_root, path).chomp(File.extname(path))
|
101
|
+
document.default_const_name = relative_path.camelize
|
102
|
+
end
|
103
|
+
|
104
|
+
document.const_attrs.each_pair do |const_name, attrs|
|
105
|
+
if attrs.has_key?(const_attr)
|
106
|
+
entries << Constant.new(const_name, path)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
entries
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'tap/support/dependency'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Support
|
5
|
+
|
6
|
+
# Dependencies tracks Executable dependencies and results, and provides
|
7
|
+
# for thread-safe resolution of dependencies.
|
8
|
+
class Dependencies < Monitor
|
9
|
+
|
10
|
+
# Initializes a new Dependencies
|
11
|
+
def initialize
|
12
|
+
super
|
13
|
+
@resolve_stack = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Thread-safe registration of instance as a dependency. During
|
17
|
+
# registration, instance is extended with the Dependency module.
|
18
|
+
# Returns self.
|
19
|
+
def register(instance)
|
20
|
+
synchronize do
|
21
|
+
unless instance.kind_of?(Dependency)
|
22
|
+
instance.extend Dependency
|
23
|
+
end
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Thread-safe resolution of the instance. Resolve checks for
|
29
|
+
# circular dependencies, then yields control to the block,
|
30
|
+
# which is responsible for the actual resolution.
|
31
|
+
def resolve(instance)
|
32
|
+
synchronize do
|
33
|
+
if @resolve_stack.include?(instance)
|
34
|
+
raise CircularDependencyError.new(@resolve_stack)
|
35
|
+
end
|
36
|
+
|
37
|
+
# mark the results at the index to prevent
|
38
|
+
# infinite loops with circular dependencies
|
39
|
+
@resolve_stack.push instance
|
40
|
+
yield()
|
41
|
+
@resolve_stack.pop
|
42
|
+
end
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Raised when Dependencies#resolve detects a circular dependency.
|
47
|
+
class CircularDependencyError < StandardError
|
48
|
+
def initialize(resolve_stack)
|
49
|
+
super "circular dependency: [#{resolve_stack.join(', ')}]"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Tap
|
2
|
+
module Support
|
3
|
+
|
4
|
+
# Constrains an Executable to only _execute once, and provides several
|
5
|
+
# methods making the Executable behave like a Dependency.
|
6
|
+
module Dependency
|
7
|
+
|
8
|
+
# The audited result of self
|
9
|
+
attr_accessor :_result
|
10
|
+
|
11
|
+
def self.extended(base) # :nodoc:
|
12
|
+
base.instance_variable_set(:@_result, nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Conditional _execute; only calls _method_name if
|
16
|
+
# resolved? is false (thus assuring self will only
|
17
|
+
# be executed once).
|
18
|
+
#
|
19
|
+
# Returns _result.
|
20
|
+
def _execute(*args)
|
21
|
+
app.dependencies.resolve(self) do
|
22
|
+
@_result = super
|
23
|
+
end unless resolved?
|
24
|
+
_result
|
25
|
+
end
|
26
|
+
|
27
|
+
# Alias for _execute().
|
28
|
+
def resolve
|
29
|
+
_execute
|
30
|
+
end
|
31
|
+
|
32
|
+
# True if _result is non-nil.
|
33
|
+
def resolved?
|
34
|
+
@_result != nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Resets the dependency by setting _result to nil.
|
38
|
+
def reset
|
39
|
+
@_result = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|