bahuvrihi-tap 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +69 -0
- data/MIT-LICENSE +21 -0
- data/README +119 -0
- data/bin/tap +114 -0
- 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 +32 -0
- data/lib/tap/app.rb +720 -0
- data/lib/tap/constants.rb +8 -0
- data/lib/tap/env.rb +640 -0
- data/lib/tap/file_task.rb +547 -0
- 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 +21 -0
- data/lib/tap/generator/generators/command/templates/command.erb +32 -0
- data/lib/tap/generator/generators/config/config_generator.rb +26 -0
- 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 +27 -0
- data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
- data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
- data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
- data/lib/tap/generator/generators/root/root_generator.rb +55 -0
- data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
- 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 +3 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
- data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
- data/lib/tap/generator/generators/task/task_generator.rb +27 -0
- data/lib/tap/generator/generators/task/templates/task.erb +14 -0
- data/lib/tap/generator/generators/task/templates/test.erb +21 -0
- data/lib/tap/generator/manifest.rb +14 -0
- data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
- data/lib/tap/patches/rake/testtask.rb +55 -0
- data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
- data/lib/tap/patches/ruby19/parsedate.rb +16 -0
- data/lib/tap/root.rb +581 -0
- data/lib/tap/support/aggregator.rb +55 -0
- data/lib/tap/support/assignments.rb +172 -0
- data/lib/tap/support/audit.rb +418 -0
- data/lib/tap/support/batchable.rb +47 -0
- data/lib/tap/support/batchable_class.rb +107 -0
- data/lib/tap/support/class_configuration.rb +194 -0
- data/lib/tap/support/command_line.rb +98 -0
- data/lib/tap/support/comment.rb +270 -0
- data/lib/tap/support/configurable.rb +114 -0
- 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 +111 -0
- data/lib/tap/support/executable_queue.rb +82 -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 +39 -0
- data/lib/tap/support/shell_utils.rb +71 -0
- data/lib/tap/support/summary.rb +30 -0
- data/lib/tap/support/tdoc.rb +404 -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/templater.rb +180 -0
- data/lib/tap/support/validation.rb +410 -0
- data/lib/tap/support/versions.rb +97 -0
- data/lib/tap/task.rb +259 -0
- data/lib/tap/tasks/dump.rb +56 -0
- data/lib/tap/tasks/rake.rb +93 -0
- data/lib/tap/test.rb +37 -0
- data/lib/tap/test/env_vars.rb +29 -0
- data/lib/tap/test/file_methods.rb +377 -0
- data/lib/tap/test/script_methods.rb +144 -0
- data/lib/tap/test/subset_methods.rb +420 -0
- data/lib/tap/test/tap_methods.rb +237 -0
- data/lib/tap/workflow.rb +187 -0
- metadata +145 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
module Tap
|
2
|
+
module Support
|
3
|
+
class Configuration
|
4
|
+
class << self
|
5
|
+
SHORT_REGEXP = /^-[A-z]$/
|
6
|
+
|
7
|
+
# Turns the input string into a short-format option. Raises
|
8
|
+
# an error if the option does not match SHORT_REGEXP.
|
9
|
+
#
|
10
|
+
# Configuration.shortify("-o") # => '-o'
|
11
|
+
# Configuration.shortify(:o) # => '-o'
|
12
|
+
#
|
13
|
+
def shortify(str)
|
14
|
+
str = str.to_s
|
15
|
+
str = "-#{str}" unless str[0] == ?-
|
16
|
+
raise "invalid short option: #{str}" unless str =~ SHORT_REGEXP
|
17
|
+
str
|
18
|
+
end
|
19
|
+
|
20
|
+
LONG_REGEXP = /^--(\[no-\])?([A-z][\w-]*)$/
|
21
|
+
|
22
|
+
# Turns the input string into a long-format option. Raises
|
23
|
+
# an error if the option does not match LONG_REGEXP.
|
24
|
+
#
|
25
|
+
# Configuration.longify("--opt") # => '--opt'
|
26
|
+
# Configuration.longify(:opt) # => '--opt'
|
27
|
+
# Configuration.longify(:opt, true) # => '--[no-]opt'
|
28
|
+
# Configuration.longify(:opt_ion) # => '--opt-ion'
|
29
|
+
# Configuration.longify(:opt_ion, false, false) # => '--opt_ion'
|
30
|
+
#
|
31
|
+
def longify(str, switch_notation=false, hyphenize=true)
|
32
|
+
str = str.to_s
|
33
|
+
str = "--#{str}" unless str.index("--")
|
34
|
+
str.gsub!(/_/, '-') if hyphenize
|
35
|
+
|
36
|
+
raise "invalid long option: #{str}" unless str =~ LONG_REGEXP
|
37
|
+
|
38
|
+
if switch_notation && $1.nil?
|
39
|
+
str = "--[no-]#{$2}"
|
40
|
+
end
|
41
|
+
|
42
|
+
str
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :name
|
47
|
+
attr_reader :reader
|
48
|
+
attr_reader :writer
|
49
|
+
attr_reader :duplicable
|
50
|
+
attr_reader :attributes
|
51
|
+
|
52
|
+
def initialize(name, default=nil, options={})
|
53
|
+
@name = name
|
54
|
+
self.default = default
|
55
|
+
|
56
|
+
self.reader = options.delete(:reader) || name
|
57
|
+
self.writer = options.delete(:writer) || "#{name}="
|
58
|
+
@attributes = options
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sets the default value for self and determines if the
|
62
|
+
# default is duplicable (ie not nil, true, false, Symbol,
|
63
|
+
# Numeric, and responds_to?(:dup)).
|
64
|
+
def default=(value)
|
65
|
+
@duplicable = case value
|
66
|
+
when nil, true, false, Symbol, Numeric then false
|
67
|
+
else value.respond_to?(:dup)
|
68
|
+
end
|
69
|
+
|
70
|
+
@default = value.freeze
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the default value, or a duplicate of the default
|
74
|
+
# value if specified and the default value is duplicable.
|
75
|
+
def default(duplicate=true)
|
76
|
+
duplicate && duplicable ? @default.dup : @default
|
77
|
+
end
|
78
|
+
|
79
|
+
# Sets the reader for self. The reader is symbolized.
|
80
|
+
def reader=(value)
|
81
|
+
@reader = value.to_sym
|
82
|
+
end
|
83
|
+
|
84
|
+
# Sets the writer for self. The writer is symbolized.
|
85
|
+
def writer=(value)
|
86
|
+
@writer = value.to_sym
|
87
|
+
end
|
88
|
+
|
89
|
+
def arg_name
|
90
|
+
attributes[:arg_name] || name.to_s.upcase
|
91
|
+
end
|
92
|
+
|
93
|
+
def arg_type
|
94
|
+
attributes[:arg_type] || :mandatory
|
95
|
+
end
|
96
|
+
|
97
|
+
def long(switch_notation=false, hyphenize=true)
|
98
|
+
Configuration.longify(attributes[:long] || name.to_s, switch_notation, hyphenize)
|
99
|
+
end
|
100
|
+
|
101
|
+
def short
|
102
|
+
attributes[:short] ? Configuration.shortify(attributes[:short]) : nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def desc
|
106
|
+
attributes[:desc]
|
107
|
+
end
|
108
|
+
|
109
|
+
# True if another is a kind of Configuration with the same name,
|
110
|
+
# default value, reader and writer; other attributes are NOT
|
111
|
+
# taken into account.
|
112
|
+
def ==(another)
|
113
|
+
another.kind_of?(Configuration) &&
|
114
|
+
self.name == another.name &&
|
115
|
+
self.reader == another.reader &&
|
116
|
+
self.writer == another.writer &&
|
117
|
+
self.default(false) == another.default(false)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'tap/support/constant_utils'
|
2
|
+
class String # :nodoc:
|
3
|
+
include Tap::Support::ConstantUtils
|
4
|
+
end
|
5
|
+
|
6
|
+
module Tap
|
7
|
+
module Support
|
8
|
+
class Constant
|
9
|
+
|
10
|
+
# The camelized name for self.
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# The path to load to initialize the constant name.
|
14
|
+
attr_reader :require_path
|
15
|
+
|
16
|
+
def initialize(name, require_path=nil)
|
17
|
+
@name = name
|
18
|
+
@require_path = require_path
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the underscored name.
|
22
|
+
def path
|
23
|
+
@path ||= name.underscore
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the basename of path.
|
27
|
+
def basename
|
28
|
+
@basename ||= File.basename(path)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the path, minus the basename of path.
|
32
|
+
def dirname
|
33
|
+
@dirname ||= (dirname = File.dirname(path)) == "." ? "" : dirname
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the name of the constant, minus nesting.
|
37
|
+
def const_name
|
38
|
+
@const_name ||= (name =~ /.*::(.*)$/ ? $1 : name)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns an array of the nesting constants of name.
|
42
|
+
def nesting
|
43
|
+
@nesting ||= (name =~ /(.*)::.*$/ ? $1 : '')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the number of constants in nesting.
|
47
|
+
def nesting_depth
|
48
|
+
@nesting_depth ||= nesting.split(/::/).length
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the document for require_path, if set, or nil otherwise.
|
52
|
+
def document
|
53
|
+
require_path ? Support::Lazydoc[require_path] : nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def ==(another)
|
57
|
+
another.kind_of?(Constant) &&
|
58
|
+
another.name == self.name &&
|
59
|
+
another.require_path == self.require_path
|
60
|
+
end
|
61
|
+
|
62
|
+
def constantize
|
63
|
+
name.try_constantize do |const_name|
|
64
|
+
require require_path
|
65
|
+
name.constantize
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Tap
|
2
|
+
module Support
|
3
|
+
|
4
|
+
# ConstantUtils provides methods for transforming strings into constants.
|
5
|
+
# Several methods are directly taken from or based heavily on the
|
6
|
+
# ActiveSupport {Inflections}[http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/String/Inflections.html]
|
7
|
+
# module and should not cause conflicts if ActiveSupport is loaded
|
8
|
+
# alongside Tap.
|
9
|
+
#
|
10
|
+
# ActiveSupport is distributed with an MIT-LICENSE:
|
11
|
+
#
|
12
|
+
# Copyright (c) 2004-2008 David Heinemeier Hansson
|
13
|
+
#
|
14
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
15
|
+
# associated documentation files (the "Software"), to deal in the Software without restriction,
|
16
|
+
# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
17
|
+
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
18
|
+
# subject to the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial
|
21
|
+
# portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
24
|
+
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
25
|
+
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
26
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
27
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
28
|
+
#
|
29
|
+
module ConstantUtils
|
30
|
+
|
31
|
+
# camelize converts self to UpperCamelCase. If the argument to
|
32
|
+
# camelize is set to :lower then camelize produces lowerCamelCase.
|
33
|
+
# camelize will also convert '/' to '::' which is useful for
|
34
|
+
# converting paths to namespaces.
|
35
|
+
def camelize(first_letter = :upper)
|
36
|
+
case first_letter
|
37
|
+
when :upper then self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
38
|
+
when :lower then self.first + camelize[1..-1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# The reverse of camelize. Makes an underscored, lowercase form
|
43
|
+
# from self. underscore will also change '::' to '/' to convert
|
44
|
+
# namespaces to paths.
|
45
|
+
def underscore
|
46
|
+
self.gsub(/::/, '/').
|
47
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
48
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
49
|
+
tr("-", "_").
|
50
|
+
downcase
|
51
|
+
end
|
52
|
+
|
53
|
+
# constantize tries to find a declared constant with the name specified
|
54
|
+
# by self. It raises a NameError when the name is not in CamelCase
|
55
|
+
# or is not initialized.
|
56
|
+
def constantize
|
57
|
+
case RUBY_VERSION
|
58
|
+
when /^1.9/
|
59
|
+
|
60
|
+
# a check is necessary to maintain the 1.8 behavior
|
61
|
+
# in 1.9, where ancestor constants may be returned
|
62
|
+
# by a direct evaluation
|
63
|
+
const_name.split("::").inject(Object) do |current, const|
|
64
|
+
const = const.to_sym
|
65
|
+
|
66
|
+
current.const_get(const).tap do |c|
|
67
|
+
unless current.const_defined?(const, false)
|
68
|
+
raise NameError.new("uninitialized constant #{const_name}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
else
|
74
|
+
Object.module_eval("::#{const_name}", __FILE__, __LINE__)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Tries to constantize self; if a NameError is raised, try_constantize
|
79
|
+
# passes control to the block. Control is only passed if the NameError
|
80
|
+
# is for one of the constants in self.
|
81
|
+
def try_constantize
|
82
|
+
begin
|
83
|
+
constantize
|
84
|
+
rescue(NameError)
|
85
|
+
error_name = $!.name.to_s
|
86
|
+
missing_const = const_name.split(/::/).inject(Object) do |current, const|
|
87
|
+
if current.const_defined?(const)
|
88
|
+
current.const_get(const)
|
89
|
+
else
|
90
|
+
break(const)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# check that the error_name is the first missing constant
|
95
|
+
raise $! unless missing_const == error_name
|
96
|
+
yield(const_name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def constants_split
|
101
|
+
camel_cased_word = camelize
|
102
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
|
103
|
+
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
|
104
|
+
end
|
105
|
+
|
106
|
+
constants = $1.split(/::/)
|
107
|
+
current = Object
|
108
|
+
while !constants.empty?
|
109
|
+
break unless current.const_defined?(constants[0])
|
110
|
+
current = current.const_get(constants.shift)
|
111
|
+
end
|
112
|
+
|
113
|
+
[current, constants]
|
114
|
+
end
|
115
|
+
|
116
|
+
protected
|
117
|
+
|
118
|
+
def const_name
|
119
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
|
120
|
+
raise NameError, "#{inspect} is not a valid constant name!"
|
121
|
+
end
|
122
|
+
$1
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Tap
|
2
|
+
module Support
|
3
|
+
module Declarations
|
4
|
+
def self.set_declaration_base(base)
|
5
|
+
# TODO -- warn if base is Object -- conflict with Rake
|
6
|
+
declaration_base = base.to_s
|
7
|
+
declaration_base = "" if ["Object", "Tap"].include?(declaration_base)
|
8
|
+
|
9
|
+
base.instance_variable_set(:@tap_declaration_base, declaration_base.underscore)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
set_declaration_base(base)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.extended(base)
|
17
|
+
set_declaration_base(base)
|
18
|
+
end
|
19
|
+
|
20
|
+
def tasc(name, configs={}, options={}, &block)
|
21
|
+
Tap::Task.subclass(nest(name), configs, options, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def task(name, configs={}, options={}, &block)
|
25
|
+
options[:arity] = arity(block)
|
26
|
+
tasc(name, configs, options, &task_block(block)).new
|
27
|
+
end
|
28
|
+
|
29
|
+
def file_tasc(name, configs={}, options={}, &block)
|
30
|
+
Tap::FileTask.subclass(nest(name), configs, options, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def file_task(name, configs={}, options={}, &block)
|
34
|
+
options[:arity] = arity(block)
|
35
|
+
file_tasc(nest(name), configs, options, &task_block(block)).new
|
36
|
+
end
|
37
|
+
|
38
|
+
def worcflow(name, configs={}, options={}, &block)
|
39
|
+
Tap::Workflow.subclass(nest(name), configs, options, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def workflow(name, configs={}, options={}, &block)
|
43
|
+
options[:arity] = arity(block)
|
44
|
+
worcflow(name, configs, options, &task_block(block)).new
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def config(key, value=nil, options={}, &block)
|
50
|
+
caller.each_with_index do |line, index|
|
51
|
+
case line
|
52
|
+
when /^(([A-z]:)?[^:]+):(\d+)/
|
53
|
+
options[:desc] = Support::Lazydoc.register($1, $3.to_i - 1)
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end if options[:desc] == nil
|
57
|
+
|
58
|
+
[:config, key, value, options, block]
|
59
|
+
end
|
60
|
+
|
61
|
+
def config_attr(key, value=nil, options={}, &block)
|
62
|
+
caller.each_with_index do |line, index|
|
63
|
+
case line
|
64
|
+
when /^(([A-z]:)?[^:]+):(\d+)/
|
65
|
+
options[:desc] = Support::Lazydoc.register($1, $3.to_i - 1)
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end if options[:desc] == nil
|
69
|
+
|
70
|
+
[:config_attr, key, value, options, block]
|
71
|
+
end
|
72
|
+
|
73
|
+
def c
|
74
|
+
Support::Validation
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def nest(name)
|
80
|
+
# use self if self is a Module or Class,
|
81
|
+
# or self.class if self is an instance.
|
82
|
+
File.join((self.kind_of?(Module) ? self : self.class).instance_variable_get(:@tap_declaration_base), name.to_s)
|
83
|
+
end
|
84
|
+
|
85
|
+
def arity(block)
|
86
|
+
arity = block.arity
|
87
|
+
|
88
|
+
case
|
89
|
+
when arity > 0 then arity -= 1
|
90
|
+
when arity < 0 then arity += 1
|
91
|
+
end
|
92
|
+
|
93
|
+
arity
|
94
|
+
end
|
95
|
+
|
96
|
+
def task_block(block)
|
97
|
+
lambda do |*inputs|
|
98
|
+
inputs.unshift(self)
|
99
|
+
|
100
|
+
arity = block.arity
|
101
|
+
n = inputs.length
|
102
|
+
unless n == arity || (arity < 0 && (-1-n) <= arity)
|
103
|
+
raise ArgumentError.new("wrong number of arguments (#{n} for #{arity})")
|
104
|
+
end
|
105
|
+
|
106
|
+
block.call(*inputs)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'tap/support/audit'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Support
|
5
|
+
# Executable wraps methods to make them executable by App. Methods are
|
6
|
+
# wrapped by extending the object that receives them; the easiest way
|
7
|
+
# to make an object executable is to use Object#_method.
|
8
|
+
module Executable
|
9
|
+
|
10
|
+
# The method called when an Executable is executed via _execute
|
11
|
+
attr_reader :_method_name
|
12
|
+
|
13
|
+
# Indicates whether or not to execute in multithread mode.
|
14
|
+
attr_accessor :multithread
|
15
|
+
|
16
|
+
# Stores the on complete block.
|
17
|
+
attr_reader :on_complete_block
|
18
|
+
|
19
|
+
public
|
20
|
+
|
21
|
+
# Extends obj with Executable and sets up all required variables. The
|
22
|
+
# specified method will be called on _execute.
|
23
|
+
def self.initialize(obj, method_name, multithread=false, &on_complete_block)
|
24
|
+
obj.extend Executable
|
25
|
+
obj.instance_variable_set(:@_method_name, method_name)
|
26
|
+
obj.instance_variable_set(:@multithread, multithread)
|
27
|
+
obj.instance_variable_set(:@on_complete_block, on_complete_block)
|
28
|
+
obj
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sets a block to receive the results of _execute. Raises an error
|
32
|
+
# if an on_complete block is already set. Override an existing
|
33
|
+
# on_complete block by specifying override = true.
|
34
|
+
#
|
35
|
+
# Note the block recieves an audited result and not
|
36
|
+
# the result itself (see Audit for more information).
|
37
|
+
def on_complete(override=false, &block) # :yields: _result
|
38
|
+
unless on_complete_block == nil || override
|
39
|
+
raise "on_complete_block already set: #{self}"
|
40
|
+
end
|
41
|
+
@on_complete_block = block
|
42
|
+
end
|
43
|
+
|
44
|
+
# Auditing method call. Executes _method_name for self, but audits
|
45
|
+
# the result. Sends the audited result to the on_complete_block if set.
|
46
|
+
#
|
47
|
+
# Audits are initialized in the follwing manner:
|
48
|
+
# no inputs:: create a new, empty Audit. The first value of the audit
|
49
|
+
# will be the result of call
|
50
|
+
# one input:: forks the input if it is an audit, otherwise initializes
|
51
|
+
# a new audit using the input
|
52
|
+
# multiple inputs:: merges the inputs into a new Audit.
|
53
|
+
#
|
54
|
+
def _execute(*inputs)
|
55
|
+
audit = case inputs.length
|
56
|
+
when 0 then Audit.new
|
57
|
+
when 1
|
58
|
+
audit = inputs.first
|
59
|
+
if audit.kind_of?(Audit)
|
60
|
+
inputs = [audit._current]
|
61
|
+
audit._fork
|
62
|
+
else
|
63
|
+
Audit.new(audit)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
sources = []
|
67
|
+
inputs.collect! do |input|
|
68
|
+
if input.kind_of?(Audit)
|
69
|
+
sources << input._fork
|
70
|
+
input._current
|
71
|
+
else
|
72
|
+
sources << nil
|
73
|
+
input
|
74
|
+
end
|
75
|
+
end
|
76
|
+
Audit.new(inputs, sources)
|
77
|
+
end
|
78
|
+
|
79
|
+
audit._record(self, send(_method_name, *inputs))
|
80
|
+
on_complete_block.call(audit) if on_complete_block
|
81
|
+
|
82
|
+
audit
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Tap extends Object with <tt>_method</tt> to generate executable methods
|
89
|
+
# that can be enqued by Tap::App and incorporated into workflows.
|
90
|
+
#
|
91
|
+
# array = []
|
92
|
+
# push_to_array = array._method(:push)
|
93
|
+
#
|
94
|
+
# task = Tap::Task.new
|
95
|
+
# task.app.sequence(task, push_to_array)
|
96
|
+
#
|
97
|
+
# task.enq(1).enq(2,3)
|
98
|
+
# task.app.run
|
99
|
+
#
|
100
|
+
# array # => [[1],[2,3]]
|
101
|
+
#
|
102
|
+
class Object
|
103
|
+
|
104
|
+
# Initializes a Tap::Support::Executable using the Method returned by
|
105
|
+
# Object#method(method_name), setting multithread and the on_complete
|
106
|
+
# block as specified. Returns nil if Object#method returns nil.
|
107
|
+
def _method(method_name, multithread=false, &on_complete_block) # :yields: _result
|
108
|
+
return nil unless m = method(method_name)
|
109
|
+
Tap::Support::Executable.initialize(m, :call, multithread, &on_complete_block)
|
110
|
+
end
|
111
|
+
end
|