tap 0.19.0 → 1.3.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.
Files changed (66) hide show
  1. data/History +100 -45
  2. data/MIT-LICENSE +1 -1
  3. data/README +95 -51
  4. data/bin/tap +11 -57
  5. data/bin/tapexe +84 -0
  6. data/doc/API +91 -139
  7. data/doc/Configuration +93 -0
  8. data/doc/Examples/Command Line +10 -42
  9. data/doc/Examples/Tapfile +124 -0
  10. data/doc/Ruby to Ruby +87 -0
  11. data/doc/Workflow Syntax +185 -0
  12. data/lib/tap.rb +74 -5
  13. data/lib/tap/app.rb +217 -310
  14. data/lib/tap/app/api.rb +44 -23
  15. data/lib/tap/app/queue.rb +11 -12
  16. data/lib/tap/app/stack.rb +4 -4
  17. data/lib/tap/declarations.rb +200 -0
  18. data/lib/tap/declarations/context.rb +31 -0
  19. data/lib/tap/declarations/description.rb +33 -0
  20. data/lib/tap/env.rb +133 -779
  21. data/lib/tap/env/cache.rb +87 -0
  22. data/lib/tap/env/constant.rb +94 -39
  23. data/lib/tap/env/path.rb +71 -0
  24. data/lib/tap/join.rb +42 -78
  25. data/lib/tap/joins/gate.rb +85 -0
  26. data/lib/tap/joins/switch.rb +4 -2
  27. data/lib/tap/joins/sync.rb +3 -3
  28. data/lib/tap/middleware.rb +5 -5
  29. data/lib/tap/middlewares/debugger.rb +18 -58
  30. data/lib/tap/parser.rb +115 -183
  31. data/lib/tap/root.rb +162 -239
  32. data/lib/tap/signal.rb +72 -0
  33. data/lib/tap/signals.rb +20 -2
  34. data/lib/tap/signals/class_methods.rb +38 -43
  35. data/lib/tap/signals/configure.rb +19 -0
  36. data/lib/tap/signals/help.rb +5 -7
  37. data/lib/tap/signals/load.rb +49 -0
  38. data/lib/tap/signals/module_methods.rb +1 -0
  39. data/lib/tap/task.rb +46 -275
  40. data/lib/tap/tasks/dump.rb +21 -16
  41. data/lib/tap/tasks/list.rb +184 -0
  42. data/lib/tap/tasks/load.rb +4 -4
  43. data/lib/tap/tasks/prompt.rb +128 -0
  44. data/lib/tap/tasks/signal.rb +42 -0
  45. data/lib/tap/tasks/singleton.rb +35 -0
  46. data/lib/tap/tasks/stream.rb +64 -0
  47. data/lib/tap/utils.rb +83 -0
  48. data/lib/tap/version.rb +2 -2
  49. data/lib/tap/workflow.rb +124 -0
  50. data/tap.yml +0 -0
  51. metadata +59 -24
  52. data/cmd/console.rb +0 -43
  53. data/cmd/manifest.rb +0 -118
  54. data/cmd/run.rb +0 -145
  55. data/doc/Examples/Workflow +0 -40
  56. data/lib/tap/app/node.rb +0 -29
  57. data/lib/tap/env/context.rb +0 -61
  58. data/lib/tap/env/gems.rb +0 -63
  59. data/lib/tap/env/manifest.rb +0 -179
  60. data/lib/tap/env/minimap.rb +0 -308
  61. data/lib/tap/intern.rb +0 -50
  62. data/lib/tap/joins.rb +0 -9
  63. data/lib/tap/prompt.rb +0 -36
  64. data/lib/tap/root/utils.rb +0 -220
  65. data/lib/tap/root/versions.rb +0 -138
  66. data/lib/tap/signals/signal.rb +0 -68
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # usage: tap [workflow] [--- tapfile] [-d-]
4
+ #
5
+ # workflow:
6
+ # [break] [left:]right [ARGS...] Constants are identified by matching the
7
+ # left and right paths; all subsequent args
8
+ # example: are passed to the constant. Breaks delimit
9
+ # tap:load -> Tap::Tasks::Load and the syntax repeats.
10
+ #
11
+ # breaks:
12
+ # - Delimiter, separates object argvs
13
+ # -- Delimits argvs and enques the next object
14
+ # -:[qai][.class] Sequence - joins previous and next objects
15
+ # -/obj[/sig] Signal - enques signal with argv
16
+ # --/obj[/sig] Signal - executes signal with argv
17
+ # -@ obj Enque - enques obj with argv
18
+ # -! obj Execute - executes obj with argv
19
+ # -. .- Escape begin/end
20
+ # --- End workflow
21
+ #
22
+ # env:
23
+ # TAP_GEMS Gem patterns to match and register
24
+ # TAP_PATH Directory paths to register
25
+ # TAPENV Signal files for env
26
+ # TAPRC Signal files for app
27
+ # TAPFILE Declaration files
28
+ #
29
+ # examples:
30
+ # tap load str -: dump A simple sequence workflow
31
+ # tap load str - dump - join 0 1 Manually build the join
32
+ # tap --/use debugger -d- Debugging utilities
33
+ # tap prompt Open a prompt
34
+ # tap list List resources
35
+ #
36
+
37
+ require 'tap'
38
+ require 'tap/parser'
39
+
40
+ begin
41
+ options = Tap.options
42
+
43
+ if ARGV[-1] == '-d-'
44
+ ARGV.pop
45
+ options[:debug] = 'true'
46
+ end
47
+
48
+ if ARGV == ['--help']
49
+ puts Lazydoc.usage(__FILE__)
50
+ puts "\nversion #{Tap::VERSION} -- #{Tap::WEBSITE}"
51
+ exit(0)
52
+ end
53
+
54
+ app = Tap.setup(options)
55
+ parser = Tap::Parser.new
56
+
57
+ loop do
58
+ break if ARGV.empty?
59
+ parser.parse!(ARGV)
60
+ parser.build_to(app)
61
+
62
+ break if ARGV.empty?
63
+ config_parser = ConfigParser.new(app.config,
64
+ :option_break => Tap::Parser::BREAK,
65
+ :keep_break => true,
66
+ :clear_config => false,
67
+ :add_defaults => false)
68
+ config_parser.add(app.class.configurations)
69
+
70
+ config_parser.scan(ARGV) do |file|
71
+ app.call('sig' => 'load', 'args' => [file])
72
+ end
73
+ end
74
+
75
+ app.run
76
+ rescue
77
+ if $DEBUG || options[:debug]
78
+ raise $!
79
+ end
80
+ $stderr.puts $!.message
81
+ exit(1)
82
+ end
83
+
84
+ exit(0)
data/doc/API CHANGED
@@ -1,70 +1,71 @@
1
- = Application Programming Interface
1
+ = Application Programming Interfaces
2
2
 
3
- Applications require the following methods for nodes, joins, and middleware.
4
- Tap provides modules and base classes that implement these APIs.
3
+ All of the objects used by tap have two APIs -- one regarding the specific
4
+ type of object (ex a task, a join, or middleware), and another regarding how
5
+ applications find and use the objects. These APIs allow users to make custom
6
+ objects.
5
7
 
6
- === {Node}[link:classes/Tap/App/Node.html] ({Task}[link:classes/Tap/Task.html])
8
+ == Object Interface
7
9
 
8
- call(*inputs) # any return is allowed
9
- joins() # returns an array of joins, or nil (optional)
10
+ Applications require the following methods for tasks, joins, and middleware.
11
+ Tap provides base classes that implement these APIs. Objects satisfying the
12
+ task, join, or middleware API are referred to as workflow objects.
10
13
 
11
- The signature for call defines the arguments that must be enqued to the node
12
- or passed to the node via a join. All signature constructs are allowed
13
- including multiple arguments, default arguments and splats (blocks are
14
- technically allowed but will never receive a value).
14
+ === {Task}[link:classes/Tap/Task.html]
15
15
 
16
- The optional joins method specifies an array of joins to be called by a
17
- running application when the node completes. Each join is called in order. An
18
- empty array specifies the default application joins should be called; nil
19
- specifies that no joins should be called. No joins will be called when joins
20
- is left undefined.
16
+ call(input) # any return is allowed
17
+ joins() # returns an array of joins, or nil (optional)
18
+
19
+ The call method takes an object and returns an object. There are no formal
20
+ constraints for the input/output objects. The optional joins method returns an
21
+ array of joins to be called when the task completes, or nil if no joins are to
22
+ be called. Each join is called in order.
21
23
 
22
24
  === {Join}[link:classes/Tap/Join.html]
23
25
 
24
- call(result) # any return is allowed
26
+ call(output) # any return is allowed
25
27
 
26
- The call method receives the result of input nodes. The result of call is not
27
- used; call must internally performing the join actions.
28
+ The call method receives the output of a task. The result of call is not used;
29
+ call internally performs the join actions.
28
30
 
29
31
  === {Middleware}[link:classes/Tap/Middleware.html]
30
32
 
31
33
  Middleware.new(stack, *args) # returns an instance of middleware
32
- call(node, inputs=[]) # any return is allowed
34
+ call(node, input) # return is the task output
33
35
  stack() # returns the original stack
34
36
 
35
- Middleware wraps the execution of nodes. Nodes and inputs are passed to the
36
- middleware during execution; the middleware is responsible for processing the
37
- node or passing it into the stack using the same call API. By default the base
38
- stack invokes call on the node with the inputs. Joins are performed after the
39
- middleware returns.
37
+ Middleware wraps the execution of tasks. Tasks and inputs are passed to the
38
+ middleware during execution. Joins are performed with the call output.
39
+
40
+ == {Application Interface}[link:classes/Tap/App/Api.html]
41
+
42
+ Tap defines an interface allowing applications to create and manage objects
43
+ using signals. The application interface is distinct from the workflow APIs,
44
+ although typically one will be implemented on top of the other. Objects
45
+ satisfying the application interface are referred to as application objects.
40
46
 
41
- == Application Interface
47
+ The application interface allows instantiation of a class from a hash and
48
+ serialization of an instance back into a hash. The hash is referred to as a
49
+ specification and must be serializable as {JSON}[http://json.org/] (meaning
50
+ the hash must consist of simple object types: numbers, strings, hashes, and
51
+ arrays).
42
52
 
43
- In addition to the APIs for individual workflow objects, Tap defines an
44
- application interface allowing objects to be created and modified using
45
- signals sent to an application (think HTTP to a web app). Signals are simple
46
- hash constructs and typically form the basis for user interfaces. The
47
- application interface is distinct from the object APIs, although it is typical
48
- to implement the application interface on top of an object API.
53
+ The application interface consists of two methods:
49
54
 
50
- Tap::App::Api implements the application interface in a general way and is the
51
- baseclass for Tap::Task, Tap::Join, and Tap::Middleware. The basic idea is to
52
- allow instantiation of a class from a hash and serialization of an instance
53
- back into a hash. The hash is referred to as a specification and must be
54
- serializable as {JSON}[http://json.org/], basically meaning the hash must
55
- consist of simple object types: numbers, strings, hashes, and arrays.
55
+ Const.build(spec, app) # returns an instance of self
56
+ to_spec # returns a spec
56
57
 
57
- The application interface consists of two methods, build and to_spec:
58
+ As an example:
58
59
 
59
- class Stub
60
+ class Example
60
61
  class << self
61
- # Build takes a specification hash and returns an instance of self.
62
+ # Build takes a specification and returns an instance of self.
62
63
  # The spec must be serializable as JSON.
63
- def build(spec={}, app=Tap::App.instance)
64
+ def build(spec={}, app=Tap::App.current)
64
65
  end
65
66
  end
66
67
 
67
- # Takes no inputs and returns a specification hash that, when built,
68
+ # Takes no inputs and returns a specification that, when built,
68
69
  # returns an object like self.
69
70
  #
70
71
  # obj.class.build(obj.to_spec) # => returns an object like obj
@@ -74,22 +75,29 @@ The application interface consists of two methods, build and to_spec:
74
75
  end
75
76
  end
76
77
 
77
- The application API reserves several additional methods that do not need to be
78
- implemented but add functionality for specific, common use cases. If they are
79
- present they must adhere to these specifications.
78
+ The application interface reserves several additional methods that do not need
79
+ to be implemented but add functionality for specific, common use cases. These
80
+ are:
81
+
82
+ Const.parse(argv, app) # returns an instance of self, cannot modify ARGV
83
+ Const.parse!(argv, app) # same as parse but can modify ARGV
84
+ signal(sig) # returns an object responding to call
85
+ associations # returns an array like [refs, brefs]
86
+
87
+ If present they must do the following:
80
88
 
81
- # Optional methods #
82
- class Stub
89
+ class Example
83
90
  class << self
84
- # Parse takes an argument vector (an array, usually from the command
85
- # line) and returns an instance of self and any remaining arguments
86
- # in an array like [instance, args]. The remaining arguments may be
87
- # nil. Parse cannot modify argv.
88
- def parse(argv=ARGV, app=Tap::App.instance)
91
+ # Takes an argument vector (an array, usually from the command line)
92
+ # and returns an instance of self. If a block is given, parse
93
+ # yields the instance and remaining args and returns block result.
94
+ #
95
+ # Parse should not modify argv.
96
+ def parse(argv=ARGV, app=Tap::App.current)
89
97
  end
90
98
 
91
99
  # Same as parse, but able to modify argv.
92
- def parse!(argv=ARGV, app=Tap::App.instance)
100
+ def parse!(argv=ARGV, app=Tap::App.current)
93
101
  end
94
102
  end
95
103
 
@@ -98,18 +106,17 @@ present they must adhere to these specifications.
98
106
  def signal(sig)
99
107
  end
100
108
 
101
- # Returns a nested array of workflow objects associated with self (ex
102
- # input/output nodes for a join). The array should be structured like
103
- # [refs, brefs], where refs are references to objects that must be built
104
- # BEFORE self and brefs are back-references to objects that must be built
105
- # AFTER self.
109
+ # Returns a nested array of application objects associated with self.
110
+ # The array should be structured like [refs, brefs], where refs are
111
+ # references to objects that must be built BEFORE self and brefs are
112
+ # back-references to objects that must be built AFTER self.
106
113
  #
107
- # For example, nodes must be built before joins. As such, the associations
108
- # method for a node returns a brefs for each of its joins. Similarly, joins
109
- # must be built after nodes and hence the associations method for a join
110
- # returns refs to their input and output nodes:
114
+ # For example, tasks must be built before joins. As such, the associations
115
+ # method for a task returns a brefs for each of its joins. Similarly, joins
116
+ # must be built after tasks and hence the associations method for a join
117
+ # returns refs to their input and output tasks:
111
118
  #
112
- # node.associations # => [nil, join]
119
+ # task.associations # => [nil, join]
113
120
  # join.associations # => [inputs + outputs, nil]
114
121
  #
115
122
  # Nil is a valid return for associations, indicating no associations.
@@ -118,7 +125,7 @@ present they must adhere to these specifications.
118
125
  end
119
126
 
120
127
  The parse methods are used for building objects from interfaces that provide
121
- an array of inputs (ex the command line) rather a hash; without them objects
128
+ an array of inputs rather a hash (ex the command line); without them objects
122
129
  are effectively excluded from use within these interfaces.
123
130
 
124
131
  Signals can be used to interact with specific objects from a user interface
@@ -128,96 +135,41 @@ cannot receive signals.
128
135
  The associations method is used to order complex builds and is described in
129
136
  more detail below.
130
137
 
131
- === Spec References
138
+ === Object References
132
139
 
133
- Specifications often require references to other resources, as when a join
134
- refers to input and output nodes. These references are normally specified as
135
- variables that, unlike the resource itself, are easily serializable as JSON
136
- and may be used in multiple places. Apps are constructed to do this easily via
137
- the +obj+ and +var+ methods.
140
+ Specifications often require references to other application objects, as when
141
+ a join refers to input and output tasks. These references are normally
142
+ specified as variables that, unlike the object itself, are serializable as
143
+ JSON. Apps manage variables via the +obj+ and +var+ methods.
138
144
 
139
- As an example, consider the Sample class that references some other
140
- application object:
145
+ As an example:
141
146
 
142
- class Sample
143
- def initialize(object)
144
- @object = object
147
+ class A
148
+ def initialize(b)
149
+ @b = b
145
150
  end
146
151
 
147
152
  def to_spec
148
- {'key' => app.var(@object)} # store a variable into the spec
153
+ {'b' => app.var(@b)} # store a variable into the spec
149
154
  end
150
155
 
151
156
  def associations
152
- [[@object], nil] # establish a build order
157
+ [[@b], nil] # establish a build order
153
158
  end
154
159
 
155
160
  class << self
156
- def build(spec={}, app=Tap::App.instance)
157
- object = app.obj(spec['key']) # retrieve an object referenced by the spec
158
- new(object)
161
+ def build(spec={}, app=Tap::App.current)
162
+ b = app.obj(spec['b']) # retrieve an object referenced by the spec
163
+ new(b)
159
164
  end
160
165
  end
161
166
  end
162
167
 
163
- Using this technique the spec will have a serializable variable representing
164
- the object and the app will be able to properly schematize and rebuild the
165
- instance and all its references. Apps use the associations array to determine
166
- the correct build order for the references. In the example the @object
167
- reference must be built before the Sample instance and correspondingly the
168
- associations method returns @object in the 'ref' array.
168
+ The to_spec method records a variable identifying @b, rather than @b itself,
169
+ which allows the spec to be properly serialized. Likewise the build method
170
+ de-references the variable to retrieve b when initializing a new instance; the
171
+ associations array is used to ensure that b is built by the time A.build tries
172
+ to de-reference the variable.
169
173
 
170
174
  Note that only references to objects implementing the application interface
171
- may be stored this way; references to objects that do not implement the
172
- application interface must be serialized and deserialized by the build/to_spec
173
- methods internally.
174
-
175
- === Resource Identifiers
176
-
177
- Tap discovers application resources using resource identifiers (ie constant
178
- attributes, see {Lazydoc}[http://tap.rubyforge.org/lazydoc]). Resources
179
- identified in this way can be automatically loaded by the Tap::Env. If no
180
- identifiers are specified for a resource, the user must manually load the
181
- resource files.
182
-
183
- As an example, this identifies the Sample constant as an 'example' resource.
184
-
185
- [lib/file.rb]
186
-
187
- # Sample::example summary
188
- class Sample
189
- end
190
-
191
- A resource can be identified by zero or more identifiers. Typically all
192
- identifiers will be put in the same file as the class, but this does not have
193
- to be the case; applications automatically require all files that identify a
194
- resource. The order in which the files are required is indeterminate and it is
195
- up to the user to ensure consistency. For example:
196
-
197
- [a.rb]
198
- # Sample::a
199
- class Sample
200
- end
201
-
202
- [b.rb]
203
- require 'a'
204
-
205
- # Sample::b
206
- class Sample
207
- end
208
-
209
- Here the require statement ensures a.rb is always required before b.rb. Note
210
- that consistency is automatic when all identifiers are in the same file (and
211
- thus only one file is required).
212
-
213
- The constant name will be inferred from file path if no constant name is
214
- specified. This is the most compact form for identifying a resource:
215
-
216
- [lib/sample.rb]
217
-
218
- # ::example summary
219
- class Sample
220
- end
221
-
222
- In this case no constant name is specified, so 'Sample' is inferred from
223
- 'sample.rb'. Constant names are determined from the path using camelization.
175
+ should be stored this way.
@@ -0,0 +1,93 @@
1
+ = Configuration
2
+
3
+ Configuring tap is a matter of setting ENV variables that tell the executable
4
+ what to make available to workflows. Each of the ENV variables can be treated
5
+ like PATH, where multiple paths may be joined by a colon. The config files
6
+ specified in the ENV are handled in order as listed, before the command line
7
+ workflow is parsed.
8
+
9
+ === TAP_GEMS (default '.')
10
+
11
+ Specifies globs of gems to automatically load into the environment. Versions
12
+ can be specified as normal, separated by a comma. Use an empty string to
13
+ specify no gems.
14
+
15
+ % gem install tap-tasks
16
+ % TAP_GEMS=. tap inspect a b c
17
+ ["a", "b", "c"]
18
+ % TAP_GEMS=tap-ta* tap inspect a b c
19
+ ["a", "b", "c"]
20
+ % TAP_GEMS='tap-tasks, >= 0.6.0' tap inspect a b c
21
+ ["a", "b", "c"]
22
+ % TAP_GEMS=nomatch tap inspect a b c
23
+ unresolvable constant: "inspect"
24
+ % TAP_GEMS= tap inspect a b c
25
+ unresolvable constant: "inspect"
26
+
27
+ Note that all matching gems will be activated when tap launches.
28
+
29
+ === TAP_PATH (default '.')
30
+
31
+ Specifies directories to be scanned and registered with the tap env. All files
32
+ matching TAP_PATH/lib/**/*.rb will be scanned for constants; TAP_PATH itself
33
+ will be registered as a path in env. If TAP_PATH/tap.yml exists, it will be
34
+ loaded as a map of paths.
35
+
36
+ As a shorthand, just know that any constants under the lib directory of
37
+ TAP_PATH will be discovered.
38
+
39
+ [dir/lib/goodnight.rb]
40
+ require 'tap/task'
41
+
42
+ # ::task
43
+ class Goodnight < Tap::Task
44
+ def process(input)
45
+ puts "goodnight #{input}"
46
+ end
47
+ end
48
+
49
+ % tap goodnight moon
50
+ unresolvable constant: "goodnight"
51
+ % TAP_PATH=dir tap goodnight moon
52
+ goodnight moon
53
+
54
+ === TAPENV (default 'tapenv')
55
+
56
+ Specifies signal files to be loaded in the env signaling context. These files
57
+ can be used to manually adjust an environment by setting/unsetting constants
58
+ and resource paths.
59
+
60
+ [tapenv]
61
+ unset Tap::Tasks::Dump
62
+
63
+ % tap load a -: dump
64
+ unresolvable constant: "dump"
65
+
66
+ === TAPRC (default '~/.taprc:taprc')
67
+
68
+ Specifies signal files to be loaded in the app signaling context. These files
69
+ can be used to manually build workflows, or configure the app.
70
+
71
+ [taprc]
72
+ set loader load
73
+ set dumper dump
74
+
75
+ % tap - join loader dumper -/enq loader 'goodnight moon'
76
+ goodnight moon
77
+
78
+ === TAPFILE (default 'tapfile')
79
+
80
+ Specifies ruby files that will be executed in the app context (ie using the
81
+ binding of the app instance). Tapfiles can be used to declare tasks, typically
82
+ using the Tap::Declarations module, or to manually setup workflows.
83
+
84
+ [tapfile]
85
+ require 'tap/declarations'
86
+ extend Tap::Declarations
87
+
88
+ task :goodnight do |config, args|
89
+ "Goodnight #{args}!"
90
+ end
91
+
92
+ % tap goodnight Moon -: dump
93
+ Goodnight Moon!