tap 0.12.4 → 0.17.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 (102) hide show
  1. data/History +34 -0
  2. data/README +62 -41
  3. data/bin/tap +36 -40
  4. data/cmd/console.rb +14 -6
  5. data/cmd/manifest.rb +62 -58
  6. data/cmd/run.rb +49 -31
  7. data/doc/API +84 -0
  8. data/doc/Class Reference +83 -115
  9. data/doc/Examples/Command Line +36 -0
  10. data/doc/Examples/Workflow +40 -0
  11. data/lib/tap/app.rb +293 -214
  12. data/lib/tap/app/node.rb +43 -0
  13. data/lib/tap/app/queue.rb +77 -0
  14. data/lib/tap/app/stack.rb +16 -0
  15. data/lib/tap/app/state.rb +22 -0
  16. data/lib/tap/constants.rb +2 -2
  17. data/lib/tap/env.rb +400 -314
  18. data/lib/tap/env/constant.rb +227 -0
  19. data/lib/tap/env/gems.rb +63 -0
  20. data/lib/tap/env/manifest.rb +89 -0
  21. data/lib/tap/env/minimap.rb +292 -0
  22. data/lib/tap/{support → env}/string_ext.rb +2 -2
  23. data/lib/tap/exe.rb +113 -125
  24. data/lib/tap/join.rb +175 -0
  25. data/lib/tap/joins.rb +9 -0
  26. data/lib/tap/joins/switch.rb +44 -0
  27. data/lib/tap/joins/sync.rb +99 -0
  28. data/lib/tap/root.rb +100 -491
  29. data/lib/tap/root/utils.rb +220 -0
  30. data/lib/tap/{support → root}/versions.rb +31 -29
  31. data/lib/tap/schema.rb +248 -0
  32. data/lib/tap/schema/parser.rb +413 -0
  33. data/lib/tap/schema/utils.rb +82 -0
  34. data/lib/tap/support/intern.rb +19 -6
  35. data/lib/tap/support/templater.rb +8 -3
  36. data/lib/tap/task.rb +175 -171
  37. data/lib/tap/tasks/dump.rb +58 -0
  38. data/lib/tap/tasks/load.rb +62 -0
  39. metadata +30 -73
  40. data/cmd/destroy.rb +0 -27
  41. data/cmd/generate.rb +0 -27
  42. data/doc/Command Reference +0 -105
  43. data/doc/Syntax Reference +0 -234
  44. data/doc/Tutorial +0 -348
  45. data/lib/tap/dump.rb +0 -142
  46. data/lib/tap/file_task.rb +0 -384
  47. data/lib/tap/generator/arguments.rb +0 -13
  48. data/lib/tap/generator/base.rb +0 -176
  49. data/lib/tap/generator/destroy.rb +0 -60
  50. data/lib/tap/generator/generate.rb +0 -93
  51. data/lib/tap/generator/generators/command/command_generator.rb +0 -21
  52. data/lib/tap/generator/generators/command/templates/command.erb +0 -32
  53. data/lib/tap/generator/generators/config/config_generator.rb +0 -98
  54. data/lib/tap/generator/generators/generator/generator_generator.rb +0 -37
  55. data/lib/tap/generator/generators/generator/templates/task.erb +0 -27
  56. data/lib/tap/generator/generators/generator/templates/test.erb +0 -26
  57. data/lib/tap/generator/generators/root/root_generator.rb +0 -84
  58. data/lib/tap/generator/generators/root/templates/MIT-LICENSE +0 -22
  59. data/lib/tap/generator/generators/root/templates/README +0 -14
  60. data/lib/tap/generator/generators/root/templates/Rakefile +0 -84
  61. data/lib/tap/generator/generators/root/templates/Rapfile +0 -11
  62. data/lib/tap/generator/generators/root/templates/gemspec +0 -27
  63. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -3
  64. data/lib/tap/generator/generators/task/task_generator.rb +0 -25
  65. data/lib/tap/generator/generators/task/templates/task.erb +0 -14
  66. data/lib/tap/generator/generators/task/templates/test.erb +0 -19
  67. data/lib/tap/generator/manifest.rb +0 -20
  68. data/lib/tap/generator/preview.rb +0 -69
  69. data/lib/tap/load.rb +0 -64
  70. data/lib/tap/spec.rb +0 -41
  71. data/lib/tap/support/aggregator.rb +0 -65
  72. data/lib/tap/support/audit.rb +0 -333
  73. data/lib/tap/support/constant.rb +0 -143
  74. data/lib/tap/support/constant_manifest.rb +0 -126
  75. data/lib/tap/support/dependencies.rb +0 -54
  76. data/lib/tap/support/dependency.rb +0 -44
  77. data/lib/tap/support/executable.rb +0 -198
  78. data/lib/tap/support/executable_queue.rb +0 -125
  79. data/lib/tap/support/gems.rb +0 -43
  80. data/lib/tap/support/join.rb +0 -144
  81. data/lib/tap/support/joins.rb +0 -12
  82. data/lib/tap/support/joins/switch.rb +0 -27
  83. data/lib/tap/support/joins/sync_merge.rb +0 -38
  84. data/lib/tap/support/manifest.rb +0 -171
  85. data/lib/tap/support/minimap.rb +0 -90
  86. data/lib/tap/support/node.rb +0 -176
  87. data/lib/tap/support/parser.rb +0 -450
  88. data/lib/tap/support/schema.rb +0 -385
  89. data/lib/tap/support/shell_utils.rb +0 -67
  90. data/lib/tap/test.rb +0 -77
  91. data/lib/tap/test/assertions.rb +0 -38
  92. data/lib/tap/test/env_vars.rb +0 -29
  93. data/lib/tap/test/extensions.rb +0 -73
  94. data/lib/tap/test/file_test.rb +0 -362
  95. data/lib/tap/test/file_test_class.rb +0 -15
  96. data/lib/tap/test/regexp_escape.rb +0 -87
  97. data/lib/tap/test/script_test.rb +0 -46
  98. data/lib/tap/test/script_tester.rb +0 -115
  99. data/lib/tap/test/subset_test.rb +0 -260
  100. data/lib/tap/test/subset_test_class.rb +0 -99
  101. data/lib/tap/test/tap_test.rb +0 -109
  102. data/lib/tap/test/utils.rb +0 -231
data/History CHANGED
@@ -1,3 +1,37 @@
1
+ == 0.17.0 / 2009-05-25
2
+
3
+ Significant reorganization and update to Tap internals.
4
+ The workflow syntax for joins has changed in incompatible
5
+ ways, and several key methods have been renamed.
6
+ Obviously not backwards compatible but most tasks will
7
+ work without alteration.
8
+
9
+ Upgrade Notes:
10
+
11
+ * Tasks now are identified by "::task" rather than "::manifest"
12
+ The manifest identifier still works for now but will be
13
+ removed by 1.0.
14
+ * Root#filepath and Root#relative_filepath have been renamed
15
+ Root#path and Root#relative_path
16
+ * Audits have been removed
17
+ * Generators, FileTask, and test modules have been removed
18
+ to separate modules (tap-gen, tap-tasks, tap-test). Install
19
+ tap-suite to get them all back.
20
+ * switched const_name, name on Constant
21
+ * refactored on_complete_block to join
22
+ * callbacks in Task have been removed
23
+ * schema syntax no longer supports rounds, globals
24
+
25
+ Changes:
26
+
27
+ * added application middleware
28
+ * reworked dump to use an io target config
29
+ * added unloading to Constant
30
+ * reworked App to use aggregators that respond_to? call
31
+ * reworked Join so that it may be dumped as YAML
32
+ * reworked Schema syntax
33
+ * etc
34
+
1
35
  == 0.12.4 / 2009-03-23
2
36
 
3
37
  * fixed bug in App#inspect
data/README CHANGED
@@ -2,51 +2,47 @@
2
2
 
3
3
  tap n. to draw a supply from a resource
4
4
 
5
- A framework for creating configurable, distributable tasks and workflows.
5
+ A configurable, distributable workflow framework.
6
6
 
7
7
  == Description
8
8
 
9
- Tap tasks are designed to be easy to test, subclass, use in scripts, and run
10
- from the command line. Tap provides methods to join tasks into imperative
11
- and dependecy-based workflows, utilize static config files, as well as dump
12
- and reuse results. Task libraries are readily shared as gems.
13
-
14
- Check out these links for tutorials, development, and bug tracking.
15
-
16
- * {Tutorial}[link:files/doc/Tutorial.html], {Class}[link:files/doc/Class%20Reference.html], {Command}[link:files/doc/Command%20Reference.html], and {Syntax}[link:files/doc/Syntax%20Reference.html] References
9
+ Tap allows the construction of imperative and dependency-based workflows that
10
+ may be defined, configured, and run from the command line. The tasks and joins
11
+ composing a workflow are easy to test, subclass, and distribute as gems.
12
+
13
+ Tap is the core of the
14
+ {Tap-Suite}[http://tap.rubyforge.org/tap-suite/index.html]
15
+ which provides a standard library of
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.
22
+
23
+ * {Tutorial}[link:files/doc/Tutorial.html], {API}[link:files/doc/API.html], {Structure}[link:files/doc/Class%20Reference.html]
17
24
  * Website[http://tap.rubyforge.org]
18
25
  * Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/9908-tap-task-application/tickets]
19
26
  * Github[http://github.com/bahuvrihi/tap/tree/master]
20
27
  * {Google Group}[http://groups.google.com/group/ruby-on-tap]
21
28
 
22
- === Features
29
+ == Usage
23
30
 
24
- - Tap and Rake[http://rake.rubyforge.org/] are targeted at different problems,
25
- but play well together. See the {Tutorial}[link:files/doc/Tutorial.html].
26
- - Tap is tested on MRI (the standard Ruby interpreter, versions 1.8.6 and
27
- 1.9.0) and JRuby[http://jruby.codehaus.org/].
28
- - Tap is the core of the {Tap-Suite}[http://tap.rubyforge.org/tap-suite], a
29
- collection of several modules that simplify development.
30
-
31
- === Usage
32
-
33
- A simple task illustrates the usage of tap:
31
+ Tasks are defined as subclasses of Tap::Task.
34
32
 
35
33
  [lib/goodnight.rb]
36
34
 
37
- # Goodnight::manifest your basic goodnight moon task
35
+ # Goodnight::task your basic goodnight moon task
38
36
  # Says goodnight with a configurable message.
39
37
  class Goodnight < Tap::Task
40
-
41
38
  config :message, 'goodnight' # a goodnight message
42
-
39
+
43
40
  def process(name)
44
- log(message, name)
45
41
  "#{message} #{name}"
46
42
  end
47
43
  end
48
44
 
49
- Tap pulls documentation out of task classes to generate manifests:
45
+ Tap automatically discovers and pulls documentation out to generate manifests:
50
46
 
51
47
  % tap run -T
52
48
  sample:
@@ -71,26 +67,52 @@ And help:
71
67
  --help Print this help
72
68
  --name NAME Specifies the task name
73
69
  --config FILE Specifies a config file
74
- --use FILE Loads inputs to ARGV
75
70
 
76
- Tasks are immediately available to run through the tap executable:
71
+ Tasks are available immediately for use in workflows. The process method takes
72
+ inputs from the command line and passes its result to other tasks.
73
+
74
+ % tap run -- goodnight moon --: dump
75
+ goodnight moon
76
+
77
+ Tasks may be configured as if they were individual executables:
78
+
79
+ % tap run -- goodnight world --message hello --: dump
80
+ hello world
81
+
82
+ === Workflow Syntax
77
83
 
78
- % tap run -- goodnight moon
79
- I[00:09:55] goodnight moon
84
+ Workflows are specified on the command line using argument vectors separated by option breaks. The argument vectors define the tasks and modifications to the breaks (--: or --[i,j][k]) specify joins.
80
85
 
81
- Tap comes with generators. To get started:
86
+ A simple sequence:
82
87
 
83
- % tap generate root sample
84
- % cd sample
85
- % tap generate task goodnight
86
- % tap run -- goodnight moon
88
+ % tap run -- load 'goodnight moon' --: dump
89
+ goodnight moon
87
90
 
88
- === Bugs/Known Issues
91
+ A more formal way of specifying a sequence:
89
92
 
90
- - Some inconsequential tests on JRuby fail due to bugs in JRuby itself.
91
- - Joins, Parser, and Schema require additional documentation and may be
92
- reworked in a future release. The workflow syntax should remain the same.
93
- - Audit.dump only works for certain cases. Merges frequently cause trouble.
93
+ % tap run -- load 'goodnight moon' -- dump --[0][1]
94
+ goodnight moon
95
+
96
+ A fork:
97
+
98
+ % tap run -- load 'goodnight moon' -- dump -- dump --[0][1,2]
99
+ goodnight moon
100
+ goodnight moon
101
+
102
+ A merge (note that dump receives the inputs in serial):
103
+
104
+ % tap run -- load goodnight -- load moon -- dump --[0,1][2]
105
+ goodnight
106
+ moon
107
+
108
+ A synchronized merge where the results of each input are collected into an array before being passed to dump. The printout is: ['goodnight', 'moon'].to_s
109
+
110
+ % tap run -- load goodnight -- load moon -- dump --[0,1][2].sync
111
+ goodnightmoon
112
+
113
+ == Known Issues
114
+
115
+ Tap::Env and Tap::Schema are still in flux and have poor documentation. Neither should be treated as stable.
94
116
 
95
117
  == Installation
96
118
 
@@ -109,5 +131,4 @@ Tap requires an updated version of RubyGems[http://docs.rubygems.org/]
109
131
  Copyright (c) 2006-2009, Regents of the University of Colorado.
110
132
  Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
111
133
  Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
112
- License:: {MIT-Style}[link:files/MIT-LICENSE.html]
113
-
134
+ License:: {MIT-Style}[link:files/MIT-LICENSE.html]
data/bin/tap CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
- # usage: tap <command> {options} [args] [-d-]
2
+ # usage: tap <command> {options} [args]
3
3
  #
4
- # Launches a tap command. To enable debugging, append the -d- superflag.
4
+ # Launches a tap command.
5
5
  #
6
6
  # examples:
7
7
  # tap generate root . # generates a root dir
@@ -14,9 +14,12 @@
14
14
 
15
15
  require "#{File.dirname(__FILE__)}/../lib/tap.rb"
16
16
 
17
+ #
17
18
  # setup the environment
19
+ #
20
+
18
21
  begin
19
- env = Tap::Exe.setup(ARGV)
22
+ exe = Tap::Exe.setup
20
23
  rescue(Tap::Env::ConfigError)
21
24
  # catch errors and exit gracefully
22
25
  # (errors usu from gem loading errors)
@@ -25,47 +28,40 @@ rescue(Tap::Env::ConfigError)
25
28
  end
26
29
 
27
30
  #
28
- # setup after script
31
+ # launch tap
29
32
  #
30
33
 
31
- at_exit do
32
- begin
33
- eval(env.after) if env.after != nil
34
- rescue(Exception)
35
- puts "Error in after script."
36
- env.handle_error($!)
37
- exit(1)
38
- end
39
- end
40
-
41
- #
42
- # run before script
43
- #
34
+ exe.activate
35
+ exe.launch(ARGV) do
36
+ template = %Q{<% unless entries.empty? || count <= 1 %>
37
+ <%= env_key %>:
38
+ <% end %>
39
+ <% entries.each do |key, path| %>
40
+ <%= key.ljust(width) %>
41
+ <% end %>}
44
42
 
45
- begin
46
- eval(env.before) if env.before != nil
47
- rescue(Exception)
48
- puts "Error in before script."
49
- env.handle_error($!)
50
- exit(1)
51
- end
52
-
53
- #
54
- # run tap
55
- #
56
-
57
- begin
58
- env.activate
59
- env.execute(ARGV) do
60
- puts Lazydoc.usage(__FILE__)
61
- puts
62
- puts "available commands:"
63
- puts env.summarize(:commands)
64
- puts
65
- puts "version #{Tap::VERSION} -- #{Tap::WEBSITE}"
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?
66
57
  end
67
- rescue
68
- env.handle_error($!)
58
+
59
+ puts Lazydoc.usage(__FILE__)
60
+ puts
61
+ puts "available commands:"
62
+ puts commands
63
+ puts
64
+ puts "version #{Tap::VERSION} -- #{Tap::WEBSITE}"
69
65
  end
70
66
 
71
67
  exit(0)
data/cmd/console.rb CHANGED
@@ -1,13 +1,14 @@
1
1
  # usage: tap console [options]
2
2
  #
3
3
  # Opens up an IRB session with the Tap environment initialized as specified
4
- # in tap.yml. Access the Tap::App.instance through 'app', and
5
- # Tap::Env.instance through 'env'. For example:
4
+ # in tap.yml. Access a Tap::App instance through 'app' and the execution
5
+ # environment through 'env'. For example:
6
6
  #
7
7
  # % tap console
8
- # >> env.tasks.search('tap:dump').constantize=> Tap::Tasks::Dump
8
+ # >> env[:task][:dump]
9
+ # => Tap::Tasks::Dump
9
10
  # >> app.info
10
- # => "state: 0 (READY) queue: 0 results: 0"
11
+ # => "state: 0 (READY) queue: 0"
11
12
  # >>
12
13
  #
13
14
 
@@ -25,11 +26,18 @@ end.parse!(ARGV)
25
26
  require "irb"
26
27
 
27
28
  def app
28
- Tap::App.instance
29
+ @app ||= Tap::App.new
29
30
  end
30
31
 
31
32
  def env
32
- Tap::Env.instance
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
33
41
  end
34
42
 
35
43
  IRB.start
data/cmd/manifest.rb CHANGED
@@ -1,7 +1,11 @@
1
- # usage: tap manifest [options]
1
+ # usage: tap manifest [options] [filter]
2
+ #
3
+ # Prints a manifest of all resources in the current tap environment. Env
4
+ # keys may be provided to select a specific set of environments to list.
5
+ #
6
+ # % tap manifest
7
+ # % tap manifest tap-tasks
2
8
  #
3
- # Prints a manifest of all resources in the current tap environment.
4
-
5
9
  options = {}
6
10
  ConfigParser.new do |opts|
7
11
 
@@ -13,85 +17,85 @@ ConfigParser.new do |opts|
13
17
  puts opts
14
18
  exit
15
19
  end
16
-
17
- # opts.on("-t", "--tree", "Just print the env tree.") do
18
- # options[:tree] = true
19
- # end
20
-
21
- # opts.on("-r", "--require FILEPATH", "Require the specified file") do |value|
22
- # require value
23
- # end
24
-
25
20
  end.parse!(ARGV)
26
21
 
27
- env = Tap::Env.instance
28
- env_names = {}
29
- env.minimap.each do |env_name, environment|
30
- env_names[environment] = env_name
31
- end
22
+ template = %Q{<% unless manifests.empty? %>
23
+ #{'-' * 80}
24
+ <%= (env_key + ':').ljust(width) %> (<%= env.root.root %>)
25
+ <% manifests.each do |type, entries| %>
26
+ <%= type %>
27
+ <% entries.each do |key, value| %>
28
+ <%= key.ljust(width-4) %> (<%= value %>)
29
+ <% end %>
30
+ <% end %>
31
+ <% end %>
32
+ }
32
33
 
33
- filter = case
34
- when ARGV.empty? then env_names.keys
34
+ # filter envs to manifest
35
+ env = Tap::Env.instance
36
+ env_keys = env.minihash(true)
37
+ filter = if ARGV.empty?
38
+ env_keys.keys
35
39
  else
36
40
  ARGV.collect do |name|
37
- unless entry = env.find(:envs, name, false)
38
- raise "could not find an env matching: #{name}"
39
- end
40
-
41
- entry[1]
41
+ env.minimatch(name) or raise "could not find an env matching: #{name}"
42
42
  end
43
43
  end
44
44
 
45
- template = %Q{#{'-' * 80}
46
- <%= (env_name + ':').ljust(width) %> (<%= env.root.root %>)
47
- <% manifests.each do |manifest_name, entries| %>
48
- <%= manifest_name %>
49
- <% entries.each do |name, path| %>
50
- <%= name.ljust(width-4) %> (<%= path %>)
51
- <% end %>
52
- <% end %>
53
- }
54
-
55
- width = 10
56
- summary = env.inspect(template) do |templater, share|
45
+ # build the summary
46
+ summary = env.inspect(template, :width => 10) do |templater, globals|
57
47
  current = templater.env
58
- next unless filter.include?(current)
59
-
60
48
  manifests = []
61
- [:commands, :generators, :tasks].each do |name|
62
- manifest = current.send(name)
63
- next if manifest.build.empty?
49
+ templater.manifests = manifests
50
+ next unless filter.include?(current)
51
+
52
+ # determine the width of the keys
53
+ width = globals[:width]
54
+ env_key = env_keys[current]
55
+ templater.env_key = env_key
56
+ width = env_key.length if width < env_key.length
57
+
58
+ # 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?
64
63
 
65
- entries = manifest.minimap.collect do |(entry, path)|
66
- path = case path
67
- when Tap::Support::Constant then path.require_path
68
- else path
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
69
70
  end
70
71
 
71
- width = entry.length if width < entry.length
72
- [entry, current.root.relative_filepath(:root, path) || path]
72
+ width = key.length if width < key.length
73
+ [key, current.root.relative_path(:root, path) || path]
73
74
  end
74
75
 
75
- manifests << [name, entries]
76
+ manifests << [type, entries]
76
77
  end
77
- templater.manifests = manifests.compact
78
- templater.env_name = env_names[current]
79
78
 
80
- width = templater.env_name.length if width < templater.env_name.length
81
- share[:width] = width + 2
79
+ globals[:width] = width
82
80
  end
83
81
  puts summary
84
82
 
85
83
  if ARGV.empty?
86
- tree = env.recursive_inspect("<%= leader %><%= env_name %> \n", 0, nil) do |templater, share, nesting_depth, last|
87
- current = templater.env
88
-
89
- templater.leader = nesting_depth == 0 ? "" : '| ' * (nesting_depth - 1) + (last == current ? "`- " : "|- ")
90
- templater.env_name = env_names[current]
84
+ templaters = []
85
+ globals = env.recursive_inject([0, nil]) do |(nesting_depth, last), current|
86
+ leader = nesting_depth == 0 ? "" : '| ' * (nesting_depth - 1) + (last == current ? "`- " : "|- ")
87
+ templaters << Tap::Support::Templater.new("<%= leader %><%= env_key %> \n",
88
+ :env_key => env_keys[current],
89
+ :leader => leader
90
+ )
91
91
 
92
92
  [nesting_depth + 1, current.envs[-1]]
93
93
  end
94
-
94
+
95
+ tree = templaters.collect do |templater|
96
+ templater.build
97
+ end.join
98
+
95
99
  puts '-' * 80
96
100
  puts
97
101
  puts tree