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
@@ -2,33 +2,34 @@ require 'tap/task'
2
2
 
3
3
  module Tap
4
4
  module Tasks
5
- # :startdoc::task the default dump task
5
+ # :startdoc::task dump data
6
6
  #
7
7
  # Dumps data to $stdout or a file output.
8
8
  #
9
- # % tap run -- dump content --output FILEPATH
9
+ # % tap dump content --output FILEPATH
10
10
  #
11
11
  # Dump faciliates normal redirection:
12
12
  #
13
- # % tap run -- load hello --: dump | more
14
- # hello
13
+ # % tap load 'goodnight moon' -: dump | more
14
+ # goodnight moon
15
15
  #
16
- # % tap run -- load hello --: dump 1> results.txt
16
+ # % tap load 'goodnight moon' -: dump 1> results.txt
17
17
  # % more results.txt
18
- # hello
18
+ # goodnight moon
19
19
  #
20
- # Note that dumps are appended to the file. Dump only accepts one object
21
- # at a time, so joins that produce an array (like sync) need to iterate
22
- # outputs to dump:
20
+ # Dump converts objects to strings using to_s:
23
21
  #
24
- # % tap run -- load hello -- load world -- dump --[0,1][2]i.sync
25
- # hello
26
- # world
22
+ # % tap load goodnight -- load moon - dump - sync 0,1 2
23
+ # ["goodnight", "moon"]
24
+ #
25
+ # % tap load goodnight -- load moon - dump - sync 0,1 2 -i
26
+ # goodnight
27
+ # moon
27
28
  #
28
29
  # :startdoc::task-
29
30
  #
30
- # Dump serves as a baseclass for more complicated dumps. A YAML dump
31
- # (see {tap-tasks}[http://tap.rubyforge.org/tap-tasks]) looks like this:
31
+ # Dump serves as a baseclass for more complicated dumps. A YAML dump (see
32
+ # {tap-tasks}[http://tap.rubyforge.org/tap-tasks]) looks like this:
32
33
  #
33
34
  # class Yaml < Tap::Tasks::Dump
34
35
  # def dump(obj, io)
@@ -39,7 +40,11 @@ module Tap
39
40
  class Dump < Tap::Task
40
41
  config :output, $stdout, &c.io(:<<, :puts, :print) # The dump target file
41
42
  config :overwrite, false, &c.flag # Overwrite the existing target
42
-
43
+
44
+ def call(input)
45
+ process(input)
46
+ end
47
+
43
48
  # The default process prints dump headers as specified in the config,
44
49
  # then append the audit value to io.
45
50
  def process(input)
@@ -48,7 +53,7 @@ module Tap
48
53
  end
49
54
  input
50
55
  end
51
-
56
+
52
57
  # Dumps the object to io, by default dump puts (not prints) obj.to_s.
53
58
  def dump(input, io)
54
59
  io.puts input.to_s
@@ -0,0 +1,184 @@
1
+ require 'tap/tasks/dump'
2
+
3
+ module Tap
4
+ module Tasks
5
+ # :startdoc::task list resources
6
+ #
7
+ # Prints a list of resources registered with the application env. Any of
8
+ # the resources may be used in a workflow. A list of filters may be used
9
+ # to limit the output; each is converted to a regexp and can match any
10
+ # part of the resource (path, class, desc).
11
+ #
12
+ # % tap list join gate
13
+ # join:
14
+ # gate # collects results before the join
15
+ #
16
+ # The configurations can be used to switch the resource description. By
17
+ # default env only lists resources registered as a task, join, or
18
+ # middleware.
19
+ #
20
+ # % tap list join gate --class --full
21
+ # join:
22
+ # /tap/joins/gate # Tap::Joins::Gate
23
+ #
24
+ class List < Dump
25
+
26
+ config :all, false, :short => :a, &c.flag # Shows all types
27
+ config :types, ['task', 'join', 'middleware'],
28
+ :long => :type,
29
+ :short => :t,
30
+ :reader => false,
31
+ &c.list(&c.string) # List types to show
32
+
33
+ config :full, false, :short => :f, &c.flag # Show full paths
34
+ config :path, false, :short => :p, &c.flag # Show require path
35
+ config :clas, false, :long => :class,
36
+ :short => :c, &c.flag # Show class
37
+
38
+ def call(input)
39
+ process manifest(*input).join("\n")
40
+ end
41
+
42
+ def basis
43
+ app.env.constants
44
+ end
45
+
46
+ def types
47
+ return @types unless all
48
+
49
+ types = []
50
+ app.env.constants.each do |constant|
51
+ types.concat constant.types.keys
52
+ end
53
+ types.uniq!
54
+ types.sort!
55
+ types
56
+ end
57
+
58
+ def manifest(*filters)
59
+ constants = filter(basis, filters)
60
+
61
+ paths = full ? fullmap(constants) : minimap(constants)
62
+ constants = constants.sort_by {|constant| paths[constant] }
63
+
64
+ descriptions = {}
65
+ selected_paths = []
66
+ selected_types = types
67
+
68
+ selected_types.each do |type|
69
+ lines = []
70
+ constants.each do |constant|
71
+ next unless constant.types.include?(type)
72
+
73
+ path = paths[constant]
74
+ selected_paths << path
75
+ lines << [path, describe(constant, type)]
76
+ end
77
+
78
+ descriptions[type] = lines unless lines.empty?
79
+ end
80
+
81
+ format = " %-#{max_width(selected_paths)}s # %s"
82
+
83
+ lines = []
84
+ selected_types.each do |type|
85
+ next unless descriptions.has_key?(type)
86
+
87
+ lines << "#{type}:"
88
+ descriptions[type].each do |description|
89
+ lines << (format % description)
90
+ end
91
+ end
92
+
93
+ if lines.empty?
94
+ lines << "(no constants match criteria)"
95
+ end
96
+
97
+ lines
98
+ end
99
+
100
+ def filter(constants, filters)
101
+ return constants if filters.empty?
102
+
103
+ filters.collect! {|filter| Regexp.new(filter) }
104
+ constants = constants.select do |constant|
105
+ filters.all? do |filter|
106
+ constant.path =~ filter
107
+ end
108
+ end
109
+ end
110
+
111
+ def fullmap(constants)
112
+ paths = {}
113
+ constants.each {|constant| paths[constant] = constant.path }
114
+ paths
115
+ end
116
+
117
+ def minimap(constants)
118
+ paths = {}
119
+ constants.each do |constant|
120
+ paths[constant] = split(constant.path)
121
+ end
122
+
123
+ minimap = {}
124
+ queue = constants.dup
125
+ while !queue.empty?
126
+ next_queue = []
127
+ queue.each do |constant|
128
+ path = paths[constant].shift
129
+
130
+ if current = minimap[path]
131
+ next_queue << current unless current == :skip
132
+ next_queue << constant
133
+ minimap[path] = :skip
134
+ else
135
+ minimap[path] = constant
136
+ end
137
+ end
138
+
139
+ queue = next_queue
140
+ end
141
+
142
+ minimap.delete_if {|path, constant| constant == :skip }.invert
143
+ end
144
+
145
+ def split(path)
146
+ splits = []
147
+ current = nil
148
+ path.split('/').reverse_each do |split|
149
+ current = current ? File.join(split, current) : split
150
+ splits << current
151
+ end
152
+ splits
153
+ end
154
+
155
+ def describe(constant, type)
156
+ case
157
+ when clas
158
+ constant.const_name
159
+
160
+ when path
161
+ require_paths = constant.require_paths
162
+ require_paths = require_paths.collect do |path|
163
+ File.join(load_path(path), path)
164
+ end if full
165
+ require_paths.join(',')
166
+
167
+ else
168
+ constant.types[type]
169
+ end
170
+ end
171
+
172
+ def load_path(path)
173
+ $:.find do |load_path|
174
+ File.exists?(File.join(load_path, path))
175
+ end || '?'
176
+ end
177
+
178
+ def max_width(paths)
179
+ max = paths.collect {|path| path.length }.max
180
+ max.nil? || max < 20 ? 20 : max
181
+ end
182
+ end
183
+ end
184
+ end
@@ -3,20 +3,20 @@ require 'stringio'
3
3
 
4
4
  module Tap
5
5
  module Tasks
6
- # :startdoc::task the default load task
6
+ # :startdoc::task load data
7
7
  #
8
8
  # Loads data from $stdin. String data may be passed directly. Load
9
9
  # is typically used as a gateway to other tasks.
10
10
  #
11
- # % tap run -- load string --: dump
11
+ # % tap load string -: dump
12
12
  # string
13
13
  #
14
14
  # Load facilitates normal redirection:
15
15
  #
16
- # % echo goodnight moon | tap run -- load --: dump
16
+ # % echo goodnight moon | tap load -: dump
17
17
  # goodnight moon
18
18
  #
19
- # % tap run -- load --: dump < somefile.txt
19
+ # % tap load -: dump < somefile.txt
20
20
  # contents of somefile
21
21
  #
22
22
  # :startdoc::task-
@@ -0,0 +1,128 @@
1
+ require 'tap/tasks/stream'
2
+ require 'readline'
3
+
4
+ module Tap
5
+ module Tasks
6
+ # :startdoc::task open a prompt
7
+ #
8
+ # Prompt reads signals from the input until a signal that returns the app
9
+ # is reached (ex run/stop) or the source io is closed.
10
+ #
11
+ # % tap prompt
12
+ # /set 0 load
13
+ # /set 1 dump
14
+ # /build join 0 1
15
+ # /enq 0 'goodnight moon'
16
+ # /run
17
+ # goodnight moon
18
+ #
19
+ # Prompts can be registered to a control signal (ex INT) so that that a
20
+ # running app may be interrupted, interrogated, or modified. This infinite
21
+ # loop can be stopped using ctl-C and a prompt.
22
+ #
23
+ # % tap dump '.' - join 0 0 -q - prompt --on INT
24
+ # .
25
+ # .
26
+ # .
27
+ # (ctl-C)
28
+ # /stop
29
+ #
30
+ class Prompt < Stream
31
+ include Tap::Utils
32
+
33
+ config :prompt, '/', &c.string_or_nil # The prompt sequence
34
+ config :terminal, $stdout, &c.io_or_nil # The terminal IO
35
+ config :variable, '', &c.string_or_nil # Assign to variable in app
36
+ config :on, nil, &c.string_or_nil # Register to a SIG
37
+
38
+ def initialize(*args)
39
+ super
40
+ trap(on) if on
41
+ end
42
+
43
+ # Traps interrupt the normal flow of the program and so I assume thread
44
+ # safety is an issue (ex if the INT occurs during an enque and a signal
45
+ # specifies another enque). A safer way to go is to enque the prompt...
46
+ # when the prompt is executed the app won't be be doing anything else so
47
+ # thread safety shouldn't be an issue.
48
+ def trap(sig)
49
+ ::Signal.trap(sig) do
50
+ puts
51
+ puts "Interrupt! Signals from an interruption are not thread-safe."
52
+
53
+ call_prompt = true
54
+ 3.times do
55
+ print "Wait for thread-safe break? (y/n): "
56
+
57
+ case gets.strip
58
+ when /^y(es)?$/i
59
+ puts "waiting for break..."
60
+ app.pq(self, [])
61
+ call_prompt = false
62
+ break
63
+
64
+ when /^no?$/i
65
+ break
66
+ end
67
+ end
68
+
69
+ if call_prompt
70
+ call([])
71
+ end
72
+ end
73
+ end
74
+
75
+ def signal(sig)
76
+ lambda do |spec|
77
+ app.build('class' => sig, 'spec' => spec) do |obj, args|
78
+ obj.call(args)
79
+ end
80
+ end
81
+ end
82
+
83
+ def process(io=$stdin)
84
+ app.set(variable, self) if variable
85
+
86
+ result = super(io)
87
+ unless file || result.nil? || result == app
88
+ open_io(terminal) do |terminal|
89
+ terminal.puts result
90
+ end
91
+ end
92
+
93
+ app.set(variable, nil) if variable
94
+ result
95
+ end
96
+
97
+ def load(io)
98
+ line = readline(io)
99
+ return nil if line.empty?
100
+
101
+ begin
102
+ sig, *args = shellsplit(line)
103
+ app.call('sig' => sig, 'args' => args)
104
+ rescue
105
+ $!
106
+ end
107
+ end
108
+
109
+ def readline(io)
110
+ if io == $stdin && terminal == $stdout
111
+ return Readline.readline(prompt, true)
112
+ end
113
+
114
+ if prompt && !file
115
+ open_io(terminal) do |terminal|
116
+ terminal.print prompt
117
+ end
118
+ end
119
+
120
+ io.eof? ? '' : io.readline.strip!
121
+ end
122
+
123
+ def complete?(io, result)
124
+ result == app
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,42 @@
1
+ require 'tap/task'
2
+
3
+ module Tap
4
+ module Tasks
5
+ # ::task signal via a task
6
+ class Signal < Tap::Task
7
+ class << self
8
+ def build(spec={}, app=Tap::App.current)
9
+ new(spec['sig'], spec['config'], app)
10
+ end
11
+
12
+ def convert_to_spec(parser, args)
13
+ if args.empty?
14
+ raise "no signal specified"
15
+ end
16
+
17
+ {
18
+ 'config' => parser.nested_config,
19
+ 'sig' => args.shift
20
+ }
21
+ end
22
+ end
23
+
24
+ attr_accessor :sig
25
+
26
+ def initialize(sig, config={}, app=Tap::App.current)
27
+ super(config, app)
28
+ @sig = sig
29
+ end
30
+
31
+ def process(*args)
32
+ app.signal(sig).call(args)
33
+ end
34
+
35
+ def to_spec
36
+ spec = super
37
+ spec['sig'] = sig
38
+ spec
39
+ end
40
+ end
41
+ end
42
+ end