tap 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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