celluloid 0.18.0.pre → 0.18.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 (177) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +258 -39
  3. data/CONDUCT.md +13 -0
  4. data/CONTRIBUTING.md +39 -0
  5. data/README.md +54 -165
  6. data/REFACTOR.md +1 -0
  7. data/architecture.md +120 -0
  8. data/examples/basic_usage.rb +1 -1
  9. data/examples/configurations.rb +78 -0
  10. data/examples/futures.rb +1 -1
  11. data/examples/ring.rb +5 -4
  12. data/examples/simple_pmap.rb +1 -1
  13. data/examples/stack.rb +2 -2
  14. data/examples/supervisors_and_registry.rb +82 -0
  15. data/examples/timers.rb +2 -2
  16. data/lib/celluloid.rb +72 -47
  17. data/lib/celluloid/actor.rb +27 -17
  18. data/lib/celluloid/actor/system.rb +13 -29
  19. data/lib/celluloid/autostart.rb +5 -5
  20. data/lib/celluloid/call/async.rb +2 -0
  21. data/lib/celluloid/call/sync.rb +10 -3
  22. data/lib/celluloid/calls.rb +5 -12
  23. data/lib/celluloid/cell.rb +5 -9
  24. data/lib/celluloid/condition.rb +3 -3
  25. data/lib/celluloid/core_ext.rb +0 -2
  26. data/lib/celluloid/debug.rb +3 -0
  27. data/lib/celluloid/exceptions.rb +2 -2
  28. data/lib/celluloid/future.rb +7 -9
  29. data/lib/celluloid/group.rb +12 -8
  30. data/lib/celluloid/group/pool.rb +1 -3
  31. data/lib/celluloid/group/spawner.rb +2 -6
  32. data/lib/celluloid/internals/call_chain.rb +15 -0
  33. data/lib/celluloid/internals/cpu_counter.rb +62 -0
  34. data/lib/celluloid/internals/handlers.rb +42 -0
  35. data/lib/celluloid/internals/links.rb +38 -0
  36. data/lib/celluloid/internals/logger.rb +104 -0
  37. data/lib/celluloid/internals/method.rb +34 -0
  38. data/lib/celluloid/internals/properties.rb +32 -0
  39. data/lib/celluloid/internals/receivers.rb +64 -0
  40. data/lib/celluloid/internals/registry.rb +102 -0
  41. data/lib/celluloid/internals/responses.rb +46 -0
  42. data/lib/celluloid/internals/signals.rb +24 -0
  43. data/lib/celluloid/internals/stack.rb +74 -0
  44. data/lib/celluloid/internals/stack/dump.rb +12 -0
  45. data/lib/celluloid/internals/stack/states.rb +72 -0
  46. data/lib/celluloid/internals/stack/summary.rb +12 -0
  47. data/lib/celluloid/internals/task_set.rb +51 -0
  48. data/lib/celluloid/internals/thread_handle.rb +52 -0
  49. data/lib/celluloid/internals/uuid.rb +40 -0
  50. data/lib/celluloid/logging/incident.rb +21 -0
  51. data/lib/celluloid/logging/incident_logger.rb +147 -0
  52. data/lib/celluloid/logging/incident_reporter.rb +49 -0
  53. data/lib/celluloid/logging/log_event.rb +20 -0
  54. data/lib/celluloid/logging/ring_buffer.rb +64 -0
  55. data/lib/celluloid/mailbox.rb +22 -9
  56. data/lib/celluloid/mailbox/evented.rb +13 -5
  57. data/lib/celluloid/notifications.rb +95 -0
  58. data/lib/celluloid/pool.rb +6 -0
  59. data/lib/celluloid/probe.rb +81 -0
  60. data/lib/celluloid/proxy/abstract.rb +9 -9
  61. data/lib/celluloid/proxy/async.rb +1 -1
  62. data/lib/celluloid/proxy/block.rb +2 -2
  63. data/lib/celluloid/proxy/cell.rb +1 -1
  64. data/lib/celluloid/proxy/future.rb +2 -4
  65. data/lib/celluloid/proxy/sync.rb +1 -3
  66. data/lib/celluloid/rspec.rb +22 -33
  67. data/lib/celluloid/supervision.rb +17 -0
  68. data/lib/celluloid/supervision/configuration.rb +169 -0
  69. data/lib/celluloid/supervision/configuration/injections.rb +8 -0
  70. data/lib/celluloid/supervision/configuration/instance.rb +113 -0
  71. data/lib/celluloid/supervision/constants.rb +123 -0
  72. data/lib/celluloid/supervision/container.rb +144 -0
  73. data/lib/celluloid/supervision/container/behavior.rb +89 -0
  74. data/lib/celluloid/supervision/container/behavior/pool.rb +71 -0
  75. data/lib/celluloid/supervision/container/behavior/tree.rb +23 -0
  76. data/lib/celluloid/supervision/container/injections.rb +8 -0
  77. data/lib/celluloid/supervision/container/instance.rb +116 -0
  78. data/lib/celluloid/supervision/container/pool.rb +210 -0
  79. data/lib/celluloid/supervision/service.rb +27 -0
  80. data/lib/celluloid/supervision/supervise.rb +34 -0
  81. data/lib/celluloid/supervision/validation.rb +40 -0
  82. data/lib/celluloid/supervision/version.rb +5 -0
  83. data/lib/celluloid/system_events.rb +11 -6
  84. data/lib/celluloid/task.rb +25 -12
  85. data/lib/celluloid/task/fibered.rb +2 -0
  86. data/lib/celluloid/task/threaded.rb +3 -3
  87. data/lib/celluloid/test.rb +5 -2
  88. data/lib/celluloid/thread.rb +0 -2
  89. data/lib/celluloid/version.rb +1 -1
  90. data/spec/celluloid/block_spec.rb +29 -32
  91. data/spec/celluloid/calls_spec.rb +5 -15
  92. data/spec/celluloid/future_spec.rb +2 -2
  93. data/spec/celluloid/internals/cpu_counter_spec.rb +129 -0
  94. data/spec/celluloid/internals/links_spec.rb +43 -0
  95. data/spec/celluloid/internals/properties_spec.rb +40 -0
  96. data/spec/celluloid/internals/registry_spec.rb +62 -0
  97. data/spec/celluloid/internals/stack/dump_spec.rb +4 -0
  98. data/spec/celluloid/internals/stack/summary_spec.rb +4 -0
  99. data/spec/celluloid/internals/thread_handle_spec.rb +60 -0
  100. data/spec/celluloid/internals/uuid_spec.rb +9 -0
  101. data/spec/celluloid/logging/ring_buffer_spec.rb +36 -0
  102. data/spec/celluloid/mailbox/evented_spec.rb +11 -22
  103. data/spec/celluloid/misc/leak_spec.rb +3 -4
  104. data/spec/celluloid/notifications_spec.rb +140 -0
  105. data/spec/celluloid/probe_spec.rb +102 -0
  106. data/spec/celluloid/proxy_spec.rb +30 -30
  107. data/spec/celluloid/supervision/behavior_spec.rb +74 -0
  108. data/spec/celluloid/supervision/configuration_spec.rb +181 -0
  109. data/spec/celluloid/supervision/container_spec.rb +72 -0
  110. data/spec/celluloid/supervision/instance_spec.rb +13 -0
  111. data/spec/celluloid/supervision/root_spec.rb +28 -0
  112. data/spec/celluloid/supervision/supervisor_spec.rb +93 -0
  113. data/spec/celluloid/task/fibered_spec.rb +1 -3
  114. data/spec/celluloid/task/threaded_spec.rb +1 -3
  115. data/spec/shared/actor_examples.rb +58 -33
  116. data/spec/shared/group_examples.rb +2 -2
  117. data/spec/shared/mailbox_examples.rb +1 -1
  118. data/spec/shared/stack_examples.rb +87 -0
  119. data/spec/shared/task_examples.rb +2 -3
  120. data/spec/spec_helper.rb +2 -4
  121. data/spec/support/configure_rspec.rb +2 -3
  122. data/spec/support/coverage.rb +2 -4
  123. data/spec/support/crash_checking.rb +2 -2
  124. data/spec/support/examples/actor_class.rb +3 -8
  125. data/spec/support/examples/call_class.rb +2 -2
  126. data/spec/support/examples/container_class.rb +35 -0
  127. data/spec/support/examples/evented_mailbox_class.rb +1 -2
  128. data/spec/support/examples/stack_classes.rb +58 -0
  129. data/spec/support/examples/stack_methods.rb +23 -0
  130. data/spec/support/examples/subordinate_class.rb +19 -0
  131. data/spec/support/logging.rb +2 -34
  132. data/spec/support/loose_threads.rb +3 -16
  133. data/spec/support/reset_class_variables.rb +5 -1
  134. data/spec/support/stubbing.rb +1 -1
  135. metadata +91 -323
  136. data/culture/CONDUCT.md +0 -38
  137. data/culture/GSoC/1010-why_we_will_participate.md +0 -17
  138. data/culture/GSoC/1020-how_mentors_stay_engaged.md +0 -7
  139. data/culture/GSoC/1030-keeping_students_on_schedule.md +0 -9
  140. data/culture/GSoC/1040-getting_students_involved.md +0 -5
  141. data/culture/GSoC/1050-student_involvement_after.md +0 -5
  142. data/culture/GSoC/README.md +0 -16
  143. data/culture/Gemfile +0 -9
  144. data/culture/LICENSE.txt +0 -22
  145. data/culture/README.md +0 -22
  146. data/culture/Rakefile +0 -5
  147. data/culture/SYNC.md +0 -70
  148. data/culture/celluloid-culture.gemspec +0 -18
  149. data/culture/gems/README.md +0 -39
  150. data/culture/gems/dependencies.yml +0 -93
  151. data/culture/gems/loader.rb +0 -101
  152. data/culture/rubocop/README.md +0 -38
  153. data/culture/rubocop/lint.yml +0 -8
  154. data/culture/rubocop/metrics.yml +0 -15
  155. data/culture/rubocop/perf.yml +0 -0
  156. data/culture/rubocop/rubocop.yml +0 -5
  157. data/culture/rubocop/style.yml +0 -61
  158. data/culture/spec/gems_spec.rb +0 -2
  159. data/culture/spec/spec_helper.rb +0 -0
  160. data/culture/spec/sync_spec.rb +0 -2
  161. data/culture/sync.rb +0 -56
  162. data/culture/tasks/rspec.rake +0 -5
  163. data/culture/tasks/rubocop.rake +0 -2
  164. data/lib/celluloid/actor/manager.rb +0 -7
  165. data/lib/celluloid/backported.rb +0 -2
  166. data/lib/celluloid/current.rb +0 -2
  167. data/lib/celluloid/deprecate.rb +0 -34
  168. data/lib/celluloid/fiber.rb +0 -32
  169. data/lib/celluloid/managed.rb +0 -3
  170. data/lib/celluloid/notices.rb +0 -15
  171. data/spec/deprecate/actor_system_spec.rb +0 -72
  172. data/spec/deprecate/block_spec.rb +0 -52
  173. data/spec/deprecate/calls_spec.rb +0 -39
  174. data/spec/deprecate/evented_mailbox_spec.rb +0 -34
  175. data/spec/deprecate/future_spec.rb +0 -32
  176. data/spec/deprecate/internal_pool_spec.rb +0 -4
  177. data/spec/support/env.rb +0 -21
@@ -0,0 +1,123 @@
1
+ module Celluloid
2
+ module Supervision
3
+ # TODO: Do not hard-code. Allow configurable values.
4
+ INSTANCE_RETRY_WAIT = 3
5
+ INSTANCE_RETRY_LIMIT = 5
6
+
7
+ module Error
8
+ class NoPublicService < Celluloid::Error; end
9
+ end
10
+
11
+ class Configuration
12
+ module Error
13
+ class AlreadyDefined < Celluloid::Error; end
14
+ class InvalidSupervisor < Celluloid::Error; end
15
+ class InvalidValues < Celluloid::Error; end
16
+ class Incomplete < Celluloid::Error; end
17
+ class Invalid < Celluloid::Error; end
18
+ end
19
+
20
+ # Using class variable so that parameters can be added by plugins.
21
+
22
+ @@parameters = {
23
+
24
+ mandatory: [:type],
25
+
26
+ optional: %i[
27
+ as
28
+ args
29
+ block
30
+ ],
31
+
32
+ # TODO: Move these into Behaviors.
33
+ plugins: [
34
+ # de :size, # Supervision::Pool
35
+ # de :routers, # Supervision::Coordinator
36
+ # de :supervises # Supervision::Tree
37
+ ],
38
+
39
+ meta: %i[
40
+ registry
41
+ branch
42
+ method
43
+ ]
44
+ }
45
+
46
+ @@arity = {
47
+ type: :args
48
+ }
49
+
50
+ @@aliases = {
51
+ name: :as,
52
+ kind: :type,
53
+ # de :pool => :size, # TODO: Move into Behaviors.
54
+ # de :supervise => :supervises
55
+ }
56
+
57
+ @@defaults = {}
58
+
59
+ class << self
60
+ def save_defaults
61
+ @@defaults = {
62
+ parameters: @@parameters.each_with_object({}) { |(k, v), p| p[k] = v.dup; },
63
+ aliases: @@aliases.dup,
64
+ arity: @@arity.dup
65
+ }
66
+ end
67
+
68
+ def resync_parameters
69
+ @@parameters = @@defaults[:parameters].each_with_object({}) { |(k, v), p| p[k] = v.dup; }
70
+ @@aliases = @@defaults[:aliases].dup
71
+ @@arity = @@defaults[:arity].dup
72
+ end
73
+
74
+ def parameters(*args)
75
+ args.inject([]) { |parameters, p| parameters += @@parameters[p]; parameters }
76
+ end
77
+
78
+ def parameter!(key, value)
79
+ @@parameters[key] << value unless @@parameters[key].include? value
80
+ end
81
+
82
+ def arity
83
+ @@arity
84
+ end
85
+
86
+ def arity!(key, value)
87
+ @@arity[key] = value
88
+ end
89
+
90
+ def aliases
91
+ @@aliases
92
+ end
93
+
94
+ def alias!(aliased, original)
95
+ @@aliases[aliased] = original
96
+ end
97
+ end
98
+
99
+ save_defaults
100
+ resync_parameters
101
+
102
+ # This was originally added for `#pool` and `PoolManager
103
+ # `:before_initialize` was added to allow detecting `:size => N`
104
+ # and turning that into a pool. Other uses could be for `coordinator`
105
+ # appointing a `router` by detecting `:routers => N`, and many other uses.
106
+
107
+ ################ These are applied inside Supervision::Member ################
108
+
109
+ REMOVE_AT_EXPORT = %i[
110
+ configuration
111
+ behavior
112
+ ].freeze
113
+
114
+ INJECTIONS = %i[
115
+ configuration
116
+ before_initialization
117
+ after_initialization
118
+ before_start
119
+ before_restart
120
+ ].freeze
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,144 @@
1
+ module Celluloid
2
+ # Supervise actor instances in a container.
3
+ module Supervision
4
+ class Container
5
+ include Celluloid
6
+
7
+ trap_exit :restart_actor
8
+
9
+ class << self
10
+ def define(options)
11
+ Configuration.define(top(options))
12
+ end
13
+
14
+ def deploy(options)
15
+ Configuration.deploy(top(options))
16
+ end
17
+
18
+ def top(options)
19
+ {
20
+ as: options.delete(:as),
21
+ type: (options.delete(:type) || self),
22
+ branch: (options.delete(:branch) || :services),
23
+ supervise: options.delete(:supervise) || []
24
+ }
25
+ end
26
+
27
+ # Actors or sub-applications to be supervised
28
+ def blocks
29
+ @blocks ||= []
30
+ end
31
+
32
+ # Start this application (and watch it with a supervisor)
33
+ def run!(options = {})
34
+ container = new(options) do |g|
35
+ blocks.each do |block|
36
+ block.call(g)
37
+ end
38
+ end
39
+ container
40
+ end
41
+
42
+ # Run the application in the foreground with a simple watchdog
43
+ def run(options = {})
44
+ loop do
45
+ supervisor = run!(options)
46
+
47
+ # Take five, toplevel supervisor
48
+ sleep 5 while supervisor.alive? # Why 5?
49
+
50
+ Internals::Logger.error "!!! Celluloid::Supervision::Container #{self} crashed. Restarting..."
51
+ end
52
+ end
53
+ end
54
+
55
+ finalizer :finalize
56
+
57
+ attr_accessor :registry
58
+
59
+ # Start the container.
60
+ def initialize(options = {})
61
+ options = { registry: options } if options.is_a? Internals::Registry
62
+ @state = :initializing
63
+ @actors = [] # instances in the container
64
+ @registry = options.delete(:registry) || Celluloid.actor_system.registry
65
+ @branch = options.delete(:branch) || :services
66
+ yield current_actor if block_given?
67
+ end
68
+
69
+ execute_block_on_receiver :initialize, :supervise, :supervise_as
70
+
71
+ def add(configuration)
72
+ Configuration.valid?(configuration, true)
73
+ @actors << Instance.new(configuration.merge(registry: @registry, branch: @branch))
74
+ @state = :running
75
+ add_accessors configuration
76
+ Actor.current
77
+ end
78
+
79
+ def add_accessors(configuration)
80
+ if configuration[:as]
81
+ unless methods.include? configuration[:as]
82
+ self.class.instance_eval do
83
+ define_method(configuration[:as]) do
84
+ @registry[configuration[:as]]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def remove_accessors; end
92
+
93
+ def remove(actor)
94
+ actor = Celluloid::Actor[actor] if actor.is_a? Symbol
95
+ instance = find(actor)
96
+ instance.terminate if instance
97
+ end
98
+
99
+ def actors
100
+ @actors.map(&:actor)
101
+ end
102
+
103
+ def find(actor)
104
+ @actors.find do |instance|
105
+ instance.actor == actor
106
+ end
107
+ end
108
+
109
+ def [](actor_name)
110
+ @registry[actor_name]
111
+ end
112
+
113
+ # Restart a crashed actor
114
+ def restart_actor(actor, reason)
115
+ return if @state == :shutdown
116
+ instance = find(actor)
117
+ raise "a container instance went missing. This shouldn't be!" unless instance
118
+
119
+ if reason
120
+ exclusive { instance.restart }
121
+ else
122
+ instance.cleanup
123
+ @actors.delete(instance)
124
+ end
125
+ end
126
+
127
+ def shutdown
128
+ @state = :shutdown
129
+ finalize
130
+ end
131
+
132
+ private
133
+
134
+ def finalize
135
+ if @actors
136
+ @actors.reverse_each do |instance|
137
+ instance.terminate
138
+ @actors.delete(instance)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,89 @@
1
+ module Celluloid
2
+ module Supervision
3
+ class Container
4
+ module Behavior
5
+ @@injections = {} # Hash of Class => Hash of Injections
6
+ @@behaviors = {} # Hash of identifying symbol parameter => Class
7
+
8
+ module Error
9
+ class Mutant < Celluloid::Error; end
10
+ end
11
+
12
+ class << self
13
+ def included(klass)
14
+ klass.send :extend, ClassMethods
15
+ end
16
+
17
+ def injections
18
+ @@injections
19
+ end
20
+
21
+ def [](identifier)
22
+ @@behaviors[identifier]
23
+ end
24
+
25
+ def []=(identifier, behavior)
26
+ @@behaviors[identifier] = behavior
27
+ end
28
+
29
+ def parameter(identifier, options)
30
+ found = nil
31
+ p = Configuration.aliases.each_with_object([identifier]) { |(a, i), invoke| invoke << a if i == identifier; }
32
+ case p.count { |parameter| found = parameter; options.key?(parameter) }
33
+ when 1
34
+ found
35
+ when 0
36
+
37
+ else
38
+ raise Error::Mutant, "More than one kind of identifiable behavior parameter."
39
+ end
40
+ end
41
+
42
+ # Beware of order. There may be multiple behavior injections, but their order is not determined ( yet )
43
+ # Right now, something like a pool-coordinator-tree supervisor mutant are absolutely expected to crash.
44
+ # Therefore, sorry Professor X -- we kill every Mutant. On sight, no questions asked. Zero mutant love.
45
+ def configure(options)
46
+ behavior = nil
47
+ injection = nil
48
+ @@behaviors.map do |identifier, injector|
49
+ if identifier = parameter(identifier, options)
50
+ if behavior
51
+ raise Error::Mutant, "More than one type of behavior expected."
52
+ else
53
+ if @@injections[injector].include?(:configuration)
54
+ injection = @@injections[behavior = injector][:configuration]
55
+ options[:behavior] ||= behavior
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ options[:type] ||= behavior
62
+ injection || proc { @configuration }
63
+ end
64
+
65
+ module ClassMethods
66
+ def identifier!(identifier, *aliases)
67
+ Behavior[identifier] = self
68
+ Configuration.parameter! :plugins, identifier
69
+ aliases.each do |aliased|
70
+ Configuration.alias! aliased, identifier
71
+ end
72
+ Configuration.save_defaults
73
+ end
74
+
75
+ def behavior_injections
76
+ Behavior.injections[self] ||= {}
77
+ end
78
+
79
+ Configuration::INJECTIONS.each do |point|
80
+ define_method(point) do |&injector|
81
+ behavior_injections[point] = injector
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,71 @@
1
+ require "set"
2
+
3
+ module Celluloid
4
+ module ClassMethods
5
+ extend Forwardable
6
+ def_delegators :"Celluloid::Supervision::Container::Pool", :pooling_options
7
+ # Create a new pool of workers. Accepts the following options:
8
+ #
9
+ # * size: how many workers to create. Default is worker per CPU core
10
+ # * args: array of arguments to pass when creating a worker
11
+ #
12
+ def pool(config = {}, &block)
13
+ _ = Celluloid.supervise(pooling_options(config, block: block, actors: self))
14
+ _.actors.last
15
+ end
16
+
17
+ # Same as pool, but links to the pool manager
18
+ def pool_link(klass, config = {}, &block)
19
+ Supervision::Container::Pool.new_link(pooling_options(config, block: block, actors: klass))
20
+ end
21
+ end
22
+
23
+ module Supervision
24
+ class Container
25
+ extend Forwardable
26
+ def_delegators :"Celluloid::Supervision::Container::Pool", :pooling_options
27
+
28
+ def pool(klass, config = {}, &block)
29
+ _ = supervise(pooling_options(config, block: block, actors: klass))
30
+ _.actors.last
31
+ end
32
+
33
+ class Instance
34
+ attr_accessor :pool, :pool_size
35
+ end
36
+
37
+ class << self
38
+ # Register a pool of actors to be launched on group startup
39
+ def pool(klass, config, &block)
40
+ blocks << lambda do |container|
41
+ container.pool(klass, config, &block)
42
+ end
43
+ end
44
+ end
45
+
46
+ class Pool
47
+ include Behavior
48
+
49
+ class << self
50
+ def pooling_options(config = {}, mixins = {})
51
+ combined = { type: Celluloid::Supervision::Container::Pool }.merge(config).merge(mixins)
52
+ combined[:args] = [%i[block actors size args].each_with_object({}) do |p, e|
53
+ e[p] = combined.delete(p) if combined[p]
54
+ end]
55
+ combined
56
+ end
57
+ end
58
+
59
+ identifier! :size, :pool
60
+
61
+ configuration do
62
+ @supervisor = Container::Pool
63
+ @method = "pool_link"
64
+ @pool = true
65
+ @pool_size = @configuration[:size]
66
+ @configuration
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ module Celluloid
2
+ module Supervision
3
+ class Container
4
+ class Tree
5
+ include Behavior
6
+
7
+ identifier! :supervises, :supervise
8
+
9
+ configuration do
10
+ if @configuration[:supervise].is_a? Array
11
+ @supervisor = @configuration.dup
12
+ @branch = @configuration.fetch(:branch, @configuration[:as])
13
+ @configuration.delete(Behavior.parameter(:supervise, @configuration))
14
+ elsif @configuration[:supervise].is_a?(Celluloid::Supervision::Configuration)
15
+ @configuration
16
+ else
17
+ raise ArgumentError, "No actors given to Tree to supervise."
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end