bahuvrihi-tap 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/History +69 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +119 -0
  4. data/bin/tap +114 -0
  5. data/cmd/console.rb +42 -0
  6. data/cmd/destroy.rb +16 -0
  7. data/cmd/generate.rb +16 -0
  8. data/cmd/run.rb +126 -0
  9. data/doc/Class Reference +362 -0
  10. data/doc/Command Reference +153 -0
  11. data/doc/Tutorial +237 -0
  12. data/lib/tap.rb +32 -0
  13. data/lib/tap/app.rb +720 -0
  14. data/lib/tap/constants.rb +8 -0
  15. data/lib/tap/env.rb +640 -0
  16. data/lib/tap/file_task.rb +547 -0
  17. data/lib/tap/generator/base.rb +109 -0
  18. data/lib/tap/generator/destroy.rb +37 -0
  19. data/lib/tap/generator/generate.rb +61 -0
  20. data/lib/tap/generator/generators/command/command_generator.rb +21 -0
  21. data/lib/tap/generator/generators/command/templates/command.erb +32 -0
  22. data/lib/tap/generator/generators/config/config_generator.rb +26 -0
  23. data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
  24. data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
  25. data/lib/tap/generator/generators/file_task/file_task_generator.rb +27 -0
  26. data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
  27. data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
  28. data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
  29. data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
  30. data/lib/tap/generator/generators/root/root_generator.rb +55 -0
  31. data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
  32. data/lib/tap/generator/generators/root/templates/gemspec +27 -0
  33. data/lib/tap/generator/generators/root/templates/tapfile +8 -0
  34. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
  36. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +27 -0
  38. data/lib/tap/generator/generators/task/templates/task.erb +14 -0
  39. data/lib/tap/generator/generators/task/templates/test.erb +21 -0
  40. data/lib/tap/generator/manifest.rb +14 -0
  41. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  42. data/lib/tap/patches/rake/testtask.rb +55 -0
  43. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  44. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  45. data/lib/tap/root.rb +581 -0
  46. data/lib/tap/support/aggregator.rb +55 -0
  47. data/lib/tap/support/assignments.rb +172 -0
  48. data/lib/tap/support/audit.rb +418 -0
  49. data/lib/tap/support/batchable.rb +47 -0
  50. data/lib/tap/support/batchable_class.rb +107 -0
  51. data/lib/tap/support/class_configuration.rb +194 -0
  52. data/lib/tap/support/command_line.rb +98 -0
  53. data/lib/tap/support/comment.rb +270 -0
  54. data/lib/tap/support/configurable.rb +114 -0
  55. data/lib/tap/support/configurable_class.rb +296 -0
  56. data/lib/tap/support/configuration.rb +122 -0
  57. data/lib/tap/support/constant.rb +70 -0
  58. data/lib/tap/support/constant_utils.rb +127 -0
  59. data/lib/tap/support/declarations.rb +111 -0
  60. data/lib/tap/support/executable.rb +111 -0
  61. data/lib/tap/support/executable_queue.rb +82 -0
  62. data/lib/tap/support/framework.rb +71 -0
  63. data/lib/tap/support/framework_class.rb +199 -0
  64. data/lib/tap/support/instance_configuration.rb +147 -0
  65. data/lib/tap/support/lazydoc.rb +428 -0
  66. data/lib/tap/support/manifest.rb +89 -0
  67. data/lib/tap/support/run_error.rb +39 -0
  68. data/lib/tap/support/shell_utils.rb +71 -0
  69. data/lib/tap/support/summary.rb +30 -0
  70. data/lib/tap/support/tdoc.rb +404 -0
  71. data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
  72. data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
  73. data/lib/tap/support/templater.rb +180 -0
  74. data/lib/tap/support/validation.rb +410 -0
  75. data/lib/tap/support/versions.rb +97 -0
  76. data/lib/tap/task.rb +259 -0
  77. data/lib/tap/tasks/dump.rb +56 -0
  78. data/lib/tap/tasks/rake.rb +93 -0
  79. data/lib/tap/test.rb +37 -0
  80. data/lib/tap/test/env_vars.rb +29 -0
  81. data/lib/tap/test/file_methods.rb +377 -0
  82. data/lib/tap/test/script_methods.rb +144 -0
  83. data/lib/tap/test/subset_methods.rb +420 -0
  84. data/lib/tap/test/tap_methods.rb +237 -0
  85. data/lib/tap/workflow.rb +187 -0
  86. metadata +145 -0
@@ -0,0 +1,82 @@
1
+ module Tap
2
+ module Support
3
+
4
+ # ExecutableQueue allows thread-safe enqueing and dequeing of
5
+ # Executable methods and inputs for execution.
6
+ class ExecutableQueue
7
+ include MonitorMixin
8
+
9
+ # Creates a new ExecutableQueue
10
+ def initialize
11
+ # required for MonitorMixin
12
+ super()
13
+ @queue = []
14
+ end
15
+
16
+ # Clears all methods and inputs. Returns the existing queue as an array.
17
+ def clear
18
+ synchronize do
19
+ current = self.queue
20
+ self.queue = []
21
+ current
22
+ end
23
+ end
24
+
25
+ # Returns the number of enqueued methods
26
+ def size
27
+ queue.length
28
+ end
29
+
30
+ # True if no methods are enqueued
31
+ def empty?
32
+ queue.empty?
33
+ end
34
+
35
+ # Enqueues the method and inputs. Raises an error if the
36
+ # method is not an Executable.
37
+ def enq(method, inputs)
38
+ synchronize do
39
+ check_method(method)
40
+ queue.push [method, inputs]
41
+ end
42
+ end
43
+
44
+ # Enqueues the method and inputs, but to the top of the queue.
45
+ # Raises an error if the method is not an Executable.
46
+ def unshift(method, inputs)
47
+ synchronize do
48
+ check_method(method)
49
+ queue.unshift [method, inputs]
50
+ end
51
+ end
52
+
53
+ # Dequeues the next method and inputs as an array like
54
+ # [method, inputs]. Returns nil if the queue is empty.
55
+ def deq
56
+ synchronize { queue.shift }
57
+ end
58
+
59
+ def concat(array)
60
+ synchronize do
61
+ array.each do |method, inputs|
62
+ enq(method, inputs)
63
+ end
64
+ end
65
+ end
66
+
67
+ # Converts self to an array.
68
+ def to_a
69
+ queue.dup
70
+ end
71
+
72
+ protected
73
+
74
+ attr_accessor :queue
75
+
76
+ # Checks if the input method is extended with Executable
77
+ def check_method(method) # :nodoc:
78
+ raise "not Executable: #{method}" unless method.kind_of?(Executable)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -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