tap 0.18.0 → 0.19.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.
data/History CHANGED
@@ -1,3 +1,24 @@
1
+ == 0.19.0 / 2009-12-05
2
+
3
+ Significant rework of Tap internals.
4
+
5
+ * extracted task/join/middleware code to make Api base class
6
+ * some changes to semantics of Minimap; entry_to_minikey is
7
+ now entry_to_path. Likewise entries now specify their own
8
+ path using entry.path rather than entry.minikey
9
+ * added Signals implemented with the Dsl pattern
10
+
11
+ == 0.18.1
12
+
13
+ * updated API to allow nodes that do not respond to joins
14
+ * fixed bug in reassigning Sync joins
15
+ * added quick-queue option to run
16
+ * added short options to app configs
17
+ * added blocks to app.log
18
+ * added --help flag for joins, middleware
19
+ * removed recurrent loading hooks from load
20
+ (see tap-tasks:tap/tasks/stream)
21
+
1
22
  == 0.18.0 / 2009-06-17
2
23
 
3
24
  Several updates to simplify Tap. Most significantly,
@@ -1,19 +1,21 @@
1
1
  Copyright (c) 2006-2009, Regents of the University of Colorado.
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
- software and associated documentation files (the "Software"), to deal in the Software
5
- without restriction, including without limitation the rights to use, copy, modify, merge,
6
- publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
- to whom the Software is furnished to do so, subject to the following conditions:
3
+ Copyright (c) 2009, Simon Chiang.
8
4
 
9
- The above copyright notice and this permission notice shall be included in all copies or
10
- substantial portions of the Software.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
11
 
12
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
16
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19
- OTHER DEALINGS IN THE SOFTWARE.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README CHANGED
@@ -14,15 +14,10 @@ Tap is the core of the
14
14
  {Tap-Suite}[http://tap.rubyforge.org/tap-suite/index.html]
15
15
  which provides a standard library of
16
16
  {tasks}[http://tap.rubyforge.org/tap-tasks/index.html],
17
- {generators}[http://tap.rubyforge.org/tap-gen/index.html],
18
- {test utilities}[http://tap.rubyforge.org/tap-test/index.html],
19
- and a
20
- {server}[http://tap.rubyforge.org/tap-server/index.html]
21
- to execute workflows via HTTP.
17
+ {generators}[http://tap.rubyforge.org/tap-gen/index.html], and
18
+ {test utilities}[http://tap.rubyforge.org/tap-test/index.html].
22
19
 
23
- * {Tutorial}[http://tap.rubyforge.org/tap-suite/files/doc/Tutorial.html], {API}[link:files/doc/API.html], {Structure}[link:files/doc/Class%20Reference.html]
24
20
  * Website[http://tap.rubyforge.org]
25
- * Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/9908-tap-task-application/tickets]
26
21
  * Github[http://github.com/bahuvrihi/tap/tree/master]
27
22
  * {Google Group}[http://groups.google.com/group/ruby-on-tap]
28
23
 
@@ -51,7 +46,7 @@ Tap automatically discovers tasks.
51
46
  dump # the default dump task
52
47
  load # the default load task
53
48
 
54
- And generates documentation.
49
+ And generates command line documentation.
55
50
 
56
51
  % tap run -- goodnight --help
57
52
  Goodnight -- your basic goodnight moon task
@@ -65,24 +60,24 @@ And generates documentation.
65
60
 
66
61
  options:
67
62
  --help Print this help
68
- --name NAME Specifies the task name
63
+ --enque Manually enques self
69
64
  --config FILE Specifies a config file
70
65
 
71
- Tasks are immediately available for use in workflows.
66
+ Tasks are immediately available for use in workflows, and may be configured as
67
+ if they were individual executables. This workflow joins the goodnight task to
68
+ a dump task, which prints the message to stdout.
72
69
 
73
70
  % tap run -- goodnight moon --: dump
74
71
  goodnight moon
75
72
 
76
- Tasks may be configured as if they were individual executables.
77
-
78
73
  % tap run -- goodnight world --message hello --: dump
79
74
  hello world
80
75
 
81
76
  === Workflow Syntax
82
77
 
83
78
  Workflows are specified on the command line using argument vectors separated
84
- by option breaks. The vectors define the tasks and modifications to the breaks
85
- specify joins.
79
+ by option breaks (ie '--'). The argument vectors define the tasks while
80
+ modifications to the breaks specify joins.
86
81
 
87
82
  A simple sequence.
88
83
 
@@ -106,30 +101,18 @@ A merge (note that dump receives the inputs in serial).
106
101
  goodnight
107
102
  moon
108
103
 
109
- A synchronized merge (the printout is ['goodnight', 'moon'].to_s).
104
+ A synchronized merge (the printout is <tt>['goodnight', 'moon'].to_s</tt>).
110
105
 
111
106
  % tap run -- load goodnight -- load moon -- dump --[0,1][2].sync
112
107
  goodnightmoon
113
108
 
114
- == Known Issues
115
-
116
- Tap::Env and Tap::Schema are still in flux and have poor documentation. Neither should be treated as stable.
117
-
118
109
  == Installation
119
110
 
120
- Tap is available as a gem on RubyForge[http://rubyforge.org/projects/tap]. Use:
111
+ Tap is available as a gem on Gemcutter[http://gemcutter.org/gems/tap].
121
112
 
122
113
  % gem install tap
123
114
 
124
- Tap requires an updated version of RubyGems[http://docs.rubygems.org/]
125
- (>= 1.2.0). To check the version and update RubyGems:
126
-
127
- % gem --version
128
- % gem --update system
129
-
130
115
  == Info
131
116
 
132
- Copyright (c) 2006-2009, Regents of the University of Colorado.
133
- Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
134
- Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
135
- License:: {MIT-Style}[link:files/MIT-LICENSE.html]
117
+ Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com]
118
+ License:: {MIT-Style}[link:files/MIT-LICENSE.html]
data/bin/tap CHANGED
@@ -15,11 +15,11 @@
15
15
  require "#{File.dirname(__FILE__)}/../lib/tap.rb"
16
16
 
17
17
  #
18
- # setup the environment
18
+ # setup the application
19
19
  #
20
20
 
21
21
  begin
22
- exe = Tap::Exe.setup
22
+ app = Tap::App.setup
23
23
  rescue(Tap::Env::ConfigError)
24
24
  # catch errors and exit gracefully
25
25
  # (errors usu from gem loading errors)
@@ -28,40 +28,35 @@ rescue(Tap::Env::ConfigError)
28
28
  end
29
29
 
30
30
  #
31
- # launch tap
31
+ # launch the command
32
32
  #
33
33
 
34
- exe.activate
35
- exe.launch(ARGV) do
36
- template = %Q{<% unless entries.empty? || count <= 1 %>
34
+ commands = app.env.manifest do |env|
35
+ env.root.glob(:cmd, "**/*.rb")
36
+ end
37
+
38
+ case command = ARGV.shift.to_s
39
+ when '', '--help'
40
+ template = %Q{<% unless minimap.empty? || count <= 1 %>
37
41
  <%= env_key %>:
38
42
  <% end %>
39
- <% entries.each do |key, path| %>
43
+ <% minimap.each do |key, path| %>
40
44
  <%= key.ljust(width) %>
41
45
  <% end %>}
42
46
 
43
- commands = exe.manifest(:command).inspect(template,
44
- :count => 0,
45
- :width => 10
46
- ) do |templater, globals|
47
- entries = templater.manifest.minimap
48
- templater.entries = entries
49
-
50
- # determine width, count
51
- width = globals[:width]
52
- entries.each do |key, path|
53
- width = key.length if width < key.length
54
- end
55
- globals[:width] = width
56
- globals[:count] += 1 unless entries.empty?
57
- end
58
-
59
47
  puts Lazydoc.usage(__FILE__)
60
48
  puts
61
49
  puts "available commands:"
62
- puts commands
50
+ puts commands.summarize(template)
63
51
  puts
64
52
  puts "version #{Tap::VERSION} -- #{Tap::WEBSITE}"
53
+ else
54
+ if path = commands.seek(command)
55
+ load path
56
+ else
57
+ puts "Unknown command: '#{command}'"
58
+ puts "Type 'tap --help' for usage information."
59
+ end
65
60
  end
66
61
 
67
62
  exit(0)
@@ -5,7 +5,7 @@
5
5
  # environment through 'env'. For example:
6
6
  #
7
7
  # % tap console
8
- # >> env[:task][:dump]
8
+ # >> app.env[:dump]
9
9
  # => Tap::Tasks::Dump
10
10
  # >> app.info
11
11
  # => "state: 0 (READY) queue: 0"
@@ -29,17 +29,6 @@ def app
29
29
  @app ||= Tap::App.instance
30
30
  end
31
31
 
32
- def env
33
- @env ||= Tap::Env.instance
34
- end
35
-
36
- def run(cmd, reset=true)
37
- app.reset if reset
38
- schema = Tap::Schema.parse(cmd)
39
- env.run(schema, app)
40
- nil
41
- end
42
-
43
32
  IRB.start
44
33
 
45
34
  # Handles a bug in IRB that causes exit to throw :IRB_EXIT
@@ -24,15 +24,15 @@ template = %Q{<% unless manifests.empty? %>
24
24
  <%= (env_key + ':').ljust(width) %> (<%= env.root.root %>)
25
25
  <% manifests.each do |type, entries| %>
26
26
  <%= type %>
27
- <% entries.each do |key, value| %>
28
- <%= key.ljust(width-4) %> (<%= value %>)
27
+ <% entries.each do |key, paths| %>
28
+ <%= key.ljust(width) %> (<%= paths.join(', ') %>)
29
29
  <% end %>
30
30
  <% end %>
31
31
  <% end %>
32
32
  }
33
33
 
34
34
  # filter envs to manifest
35
- env = Tap::Env.instance
35
+ env = Tap::App.instance.env
36
36
  env_keys = env.minihash(true)
37
37
  filter = if ARGV.empty?
38
38
  env_keys.keys
@@ -43,6 +43,7 @@ else
43
43
  end
44
44
 
45
45
  # build the summary
46
+ constants = env.constants
46
47
  summary = env.inspect(template, :width => 10) do |templater, globals|
47
48
  current = templater.env
48
49
  manifests = []
@@ -56,26 +57,20 @@ summary = env.inspect(template, :width => 10) do |templater, globals|
56
57
  width = env_key.length if width < env_key.length
57
58
 
58
59
  # build up the entries for each type of resource
59
- current.registry(true).to_a.sort_by do |(type, entries)|
60
- type.to_s
61
- end.each do |type, entries|
62
- next if entries.empty?
60
+ types = {}
61
+ constants.entries(current).minimap.each do |key, const|
62
+ paths = const.require_paths.collect do |path|
63
+ current.root.relative_path(:root, path) || path
64
+ end.uniq
63
65
 
64
- entries.extend(Tap::Env::Minimap)
65
- entries = entries.minimap.collect do |key, entry|
66
- path = if entry.kind_of?(Tap::Env::Constant)
67
- entry.require_path
68
- else
69
- entry
70
- end
71
-
72
- width = key.length if width < key.length
73
- [key, current.root.relative_path(:root, path) || path]
74
- end
66
+ width = key.length if width < key.length
75
67
 
76
- manifests << [type, entries]
68
+ const.types.keys.each do |type|
69
+ (types[type] ||= []) << [key, paths]
70
+ end
77
71
  end
78
72
 
73
+ manifests.concat types.to_a.sort_by {|type, minimap| type }
79
74
  globals[:width] = width
80
75
  end
81
76
  puts summary
data/cmd/run.rb CHANGED
@@ -6,128 +6,138 @@
6
6
  # tap run -- load hello --: dump Say hello
7
7
  #
8
8
 
9
- env = Tap::Env.instance
10
- app = Tap::App.new
9
+ app = Tap::App.instance
11
10
 
12
- #
13
- # parse argv
14
- #
15
-
16
- # separate out argv schema
17
- argv = []
18
- while !ARGV.empty? && ARGV[0] !~ Tap::Schema::Parser::BREAK
19
- argv << ARGV.shift
20
- end
21
- schema = ARGV.empty? ? nil : Tap::Schema.parse(ARGV)
22
- ARGV.replace(argv)
23
-
24
- # parse options
25
- mode = :run
26
- ConfigParser.new(app.config) do |opts|
27
- opts.separator ""
28
- opts.separator "configurations:"
11
+ opts = {}
12
+ parser = ConfigParser.bind(app.config) do |psr|
13
+ psr.separator ""
14
+ psr.separator "configurations:"
29
15
 
30
- root_keys = Tap::Root.configurations.keys
31
- Tap::App.configurations.each_pair do |key, config|
32
- next if root_keys.include?(key)
33
- opts.define(key, config.default, config.attributes)
34
- end
35
-
36
- opts.separator ""
37
- opts.separator "options:"
16
+ psr.add Tap::App.configurations
17
+
18
+ psr.separator ""
19
+ psr.separator "options:"
38
20
 
39
- opts.on("-h", "--help", "Show this message") do
40
- Tap::App.lazydoc.resolve
21
+ psr.on("-h", "--help", "Show this message") do
41
22
  puts Lazydoc.usage(__FILE__)
42
- puts opts
23
+ puts psr
43
24
  exit(0)
44
25
  end
45
26
 
46
- opts.on('-s', '--schema FILE', 'Use the specifed schema') do |file|
47
- if schema
48
- puts "An inline schema cannot be specified with a file schema."
49
- exit(0)
50
- end
51
-
52
- schema = Tap::Schema.load_file(file)
27
+ psr.on('-s', '--serialize', 'Serialize the workflow') do
28
+ opts[:serialize] = true
53
29
  end
54
30
 
55
- opts.on('-p', '--preview', 'Print the schema as YAML') do
56
- mode = :preview
57
- end
58
-
59
- opts.on('-t', '--manifest', 'Print a list of available resources') do
60
- tasks = env.manifest(:task)
61
- tasks_found = !tasks.all_empty?
31
+ psr.on('-t', '--manifest', 'Print a list of available resources') do
32
+ constants = app.env.constants
62
33
 
63
- joins = env.manifest(:join)
64
- joins_found = !joins.all_empty?
65
-
66
- middleware = env.manifest(:middleware)
67
- middleware_found = !middleware.all_empty?
34
+ tasks, joins, middleware = %w{task join middleware}.collect do |type|
35
+ constants.summarize do |constant|
36
+ constant.types[type]
37
+ end
38
+ end
68
39
 
69
- if tasks_found
70
- puts "=== tasks ===" if middleware_found || joins_found
71
- puts tasks.summarize
40
+ unless tasks.empty?
41
+ puts "=== tasks ===" unless middleware.empty? && joins.empty?
42
+ puts tasks
72
43
  end
73
44
 
74
- if joins_found
75
- puts "=== joins ===" if tasks_found || middleware_found
76
- puts joins.summarize
45
+ unless joins.empty?
46
+ puts "=== joins ===" unless tasks.empty? && middleware.empty?
47
+ puts joins
77
48
  end
78
49
 
79
- if middleware_found
80
- puts "=== middleware ===" if tasks_found || joins_found
81
- puts middleware.summarize
50
+ unless middleware.empty?
51
+ puts "=== middleware ===" unless tasks.empty? && joins.empty?
52
+ puts middleware
82
53
  end
83
54
 
84
55
  exit(0)
85
56
  end
86
57
 
87
- opts.on('-T', '--tasks', 'Print a list of available tasks') do
88
- puts env.manifest(:task).summarize
58
+ psr.on('-T', '--tasks', 'Print a list of available tasks') do
59
+ constants = app.env.constants
60
+ tasks = constants.summarize do |constant|
61
+ constant.types['task']
62
+ end
63
+
64
+ puts tasks
89
65
  exit(0)
90
66
  end
91
67
 
92
- end.parse!(ARGV, :clear_config => false, :add_defaults => false)
68
+ psr.on('-u', '--quick-queue', 'Removes thread-safety from queue') do
69
+ mod = Module.new do
70
+ def synchronize
71
+ yield
72
+ end
73
+ end
74
+ app.queue.extend(mod)
75
+ end
76
+ end
93
77
 
94
78
  #
95
79
  # build and run
96
80
  #
97
81
 
98
- unless schema
99
- msg = "No schema specified"
100
-
101
- unless ARGV.empty?
102
- args = ARGV[0, 3].join(' ') + (argv.length > 3 ? ' ...' : '')
103
- msg = "#{msg} (did you mean 'tap run -- #{args}'?)"
82
+ # Traps interrupt the normal flow of the program and so I assume thread safety
83
+ # is an issue (ex if the INT occurs during an enque and a signal specifies
84
+ # another enque). A safer way to go is to enque the prompt... when the prompt
85
+ # is executed the app won't be be doing anything else so thread safety
86
+ # shouldn't be an issue.
87
+ Signal.trap('INT') do
88
+ puts
89
+ puts "Interrupt! Signals from an interruption are not thread-safe."
90
+
91
+ require 'tap/prompt'
92
+ prompt = Tap::Prompt.new
93
+ call_prompt = true
94
+ 3.times do
95
+ print "Wait for thread-safe break? (y/n): "
96
+
97
+ case gets.strip
98
+ when /^y(es)?$/i
99
+ puts "waiting for break..."
100
+ app.queue.unshift(prompt, [])
101
+ call_prompt = false
102
+ break
103
+
104
+ when /^no?$/i
105
+ break
106
+ end
107
+ end
108
+
109
+ if call_prompt
110
+ prompt.call
104
111
  end
105
-
106
- puts msg
107
- exit(0)
108
112
  end
109
113
 
110
114
  begin
111
- app.build(schema, :resources => env)
112
- ARGV.replace(argv)
113
-
114
- case mode
115
- when :run
116
- Tap::Exe.set_signals(app)
117
- app.run
118
- when :preview
119
- app.to_schema do |type, resources|
120
- resources.each do |resource|
121
- const_name = resource.delete(:class).to_s
122
- resource[:id] = env.reverse_seek(type, false) do |const|
123
- const.const_name == const_name
124
- end
115
+ loop do
116
+ break if ARGV.empty?
117
+ parser.scan(ARGV,
118
+ :option_break => Tap::Parser::BREAK,
119
+ :keep_break => true
120
+ ) do |path|
121
+ YAML.load_file(path).each do |spec|
122
+ app.call(spec)
125
123
  end
126
- end.dump($stdout)
124
+ end
125
+
126
+ break if ARGV.empty?
127
+ ARGV.replace app.call('sig' => 'parse', 'args' => ARGV)
127
128
  end
128
129
 
130
+ if opts[:serialize]
131
+ YAML.dump(app.serialize, $stdout)
132
+ exit(0)
133
+ end
134
+
135
+ opts = nil
136
+ parser = nil
137
+ app.run
138
+
129
139
  rescue
130
- raise if $DEBUG
140
+ raise if app.debug?
131
141
  puts $!.message
132
142
  exit(1)
133
143
  end