truex-skylight 0.6.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 (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +277 -0
  3. data/CLA.md +9 -0
  4. data/CONTRIBUTING.md +1 -0
  5. data/LICENSE.md +79 -0
  6. data/README.md +4 -0
  7. data/bin/skylight +3 -0
  8. data/ext/extconf.rb +186 -0
  9. data/ext/libskylight.yml +6 -0
  10. data/ext/skylight_memprof.c +115 -0
  11. data/ext/skylight_native.c +416 -0
  12. data/ext/skylight_native.h +20 -0
  13. data/lib/skylight.rb +2 -0
  14. data/lib/skylight/api.rb +79 -0
  15. data/lib/skylight/cli.rb +146 -0
  16. data/lib/skylight/compat.rb +47 -0
  17. data/lib/skylight/config.rb +498 -0
  18. data/lib/skylight/core.rb +122 -0
  19. data/lib/skylight/data/cacert.pem +3894 -0
  20. data/lib/skylight/formatters/http.rb +17 -0
  21. data/lib/skylight/gc.rb +107 -0
  22. data/lib/skylight/helpers.rb +137 -0
  23. data/lib/skylight/instrumenter.rb +290 -0
  24. data/lib/skylight/middleware.rb +75 -0
  25. data/lib/skylight/native.rb +69 -0
  26. data/lib/skylight/normalizers.rb +133 -0
  27. data/lib/skylight/normalizers/action_controller/process_action.rb +35 -0
  28. data/lib/skylight/normalizers/action_controller/send_file.rb +76 -0
  29. data/lib/skylight/normalizers/action_view/render_collection.rb +18 -0
  30. data/lib/skylight/normalizers/action_view/render_partial.rb +18 -0
  31. data/lib/skylight/normalizers/action_view/render_template.rb +18 -0
  32. data/lib/skylight/normalizers/active_record/sql.rb +79 -0
  33. data/lib/skylight/normalizers/active_support/cache.rb +50 -0
  34. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  35. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  36. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  37. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  38. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  39. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  40. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  41. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  42. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  43. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  44. data/lib/skylight/normalizers/default.rb +21 -0
  45. data/lib/skylight/normalizers/moped/query.rb +141 -0
  46. data/lib/skylight/probes.rb +91 -0
  47. data/lib/skylight/probes/excon.rb +25 -0
  48. data/lib/skylight/probes/excon/middleware.rb +65 -0
  49. data/lib/skylight/probes/net_http.rb +44 -0
  50. data/lib/skylight/probes/redis.rb +30 -0
  51. data/lib/skylight/probes/sequel.rb +30 -0
  52. data/lib/skylight/probes/sinatra.rb +74 -0
  53. data/lib/skylight/probes/tilt.rb +27 -0
  54. data/lib/skylight/railtie.rb +122 -0
  55. data/lib/skylight/sinatra.rb +4 -0
  56. data/lib/skylight/subscriber.rb +92 -0
  57. data/lib/skylight/trace.rb +191 -0
  58. data/lib/skylight/util.rb +16 -0
  59. data/lib/skylight/util/allocation_free.rb +17 -0
  60. data/lib/skylight/util/clock.rb +53 -0
  61. data/lib/skylight/util/gzip.rb +15 -0
  62. data/lib/skylight/util/hostname.rb +17 -0
  63. data/lib/skylight/util/http.rb +218 -0
  64. data/lib/skylight/util/inflector.rb +110 -0
  65. data/lib/skylight/util/logging.rb +87 -0
  66. data/lib/skylight/util/multi_io.rb +21 -0
  67. data/lib/skylight/util/native_ext_fetcher.rb +205 -0
  68. data/lib/skylight/util/platform.rb +67 -0
  69. data/lib/skylight/util/ssl.rb +50 -0
  70. data/lib/skylight/vendor/active_support/notifications.rb +207 -0
  71. data/lib/skylight/vendor/active_support/notifications/fanout.rb +159 -0
  72. data/lib/skylight/vendor/active_support/notifications/instrumenter.rb +72 -0
  73. data/lib/skylight/vendor/active_support/per_thread_registry.rb +52 -0
  74. data/lib/skylight/vendor/cli/highline.rb +1034 -0
  75. data/lib/skylight/vendor/cli/highline/color_scheme.rb +134 -0
  76. data/lib/skylight/vendor/cli/highline/compatibility.rb +16 -0
  77. data/lib/skylight/vendor/cli/highline/import.rb +41 -0
  78. data/lib/skylight/vendor/cli/highline/menu.rb +381 -0
  79. data/lib/skylight/vendor/cli/highline/question.rb +481 -0
  80. data/lib/skylight/vendor/cli/highline/simulate.rb +48 -0
  81. data/lib/skylight/vendor/cli/highline/string_extensions.rb +111 -0
  82. data/lib/skylight/vendor/cli/highline/style.rb +181 -0
  83. data/lib/skylight/vendor/cli/highline/system_extensions.rb +242 -0
  84. data/lib/skylight/vendor/cli/thor.rb +473 -0
  85. data/lib/skylight/vendor/cli/thor/actions.rb +318 -0
  86. data/lib/skylight/vendor/cli/thor/actions/create_file.rb +105 -0
  87. data/lib/skylight/vendor/cli/thor/actions/create_link.rb +60 -0
  88. data/lib/skylight/vendor/cli/thor/actions/directory.rb +119 -0
  89. data/lib/skylight/vendor/cli/thor/actions/empty_directory.rb +137 -0
  90. data/lib/skylight/vendor/cli/thor/actions/file_manipulation.rb +314 -0
  91. data/lib/skylight/vendor/cli/thor/actions/inject_into_file.rb +109 -0
  92. data/lib/skylight/vendor/cli/thor/base.rb +652 -0
  93. data/lib/skylight/vendor/cli/thor/command.rb +136 -0
  94. data/lib/skylight/vendor/cli/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  95. data/lib/skylight/vendor/cli/thor/core_ext/io_binary_read.rb +12 -0
  96. data/lib/skylight/vendor/cli/thor/core_ext/ordered_hash.rb +100 -0
  97. data/lib/skylight/vendor/cli/thor/error.rb +28 -0
  98. data/lib/skylight/vendor/cli/thor/group.rb +282 -0
  99. data/lib/skylight/vendor/cli/thor/invocation.rb +172 -0
  100. data/lib/skylight/vendor/cli/thor/parser.rb +4 -0
  101. data/lib/skylight/vendor/cli/thor/parser/argument.rb +74 -0
  102. data/lib/skylight/vendor/cli/thor/parser/arguments.rb +171 -0
  103. data/lib/skylight/vendor/cli/thor/parser/option.rb +121 -0
  104. data/lib/skylight/vendor/cli/thor/parser/options.rb +218 -0
  105. data/lib/skylight/vendor/cli/thor/rake_compat.rb +72 -0
  106. data/lib/skylight/vendor/cli/thor/runner.rb +322 -0
  107. data/lib/skylight/vendor/cli/thor/shell.rb +88 -0
  108. data/lib/skylight/vendor/cli/thor/shell/basic.rb +393 -0
  109. data/lib/skylight/vendor/cli/thor/shell/color.rb +148 -0
  110. data/lib/skylight/vendor/cli/thor/shell/html.rb +127 -0
  111. data/lib/skylight/vendor/cli/thor/util.rb +270 -0
  112. data/lib/skylight/vendor/cli/thor/version.rb +3 -0
  113. data/lib/skylight/vendor/thread_safe.rb +126 -0
  114. data/lib/skylight/vendor/thread_safe/non_concurrent_cache_backend.rb +133 -0
  115. data/lib/skylight/vendor/thread_safe/synchronized_cache_backend.rb +76 -0
  116. data/lib/skylight/version.rb +4 -0
  117. data/lib/skylight/vm/gc.rb +70 -0
  118. data/lib/sql_lexer.rb +6 -0
  119. data/lib/sql_lexer/lexer.rb +579 -0
  120. data/lib/sql_lexer/string_scanner.rb +11 -0
  121. data/lib/sql_lexer/version.rb +3 -0
  122. metadata +179 -0
@@ -0,0 +1,50 @@
1
+ require 'openssl'
2
+
3
+ module Skylight
4
+ module Util
5
+ class SSL
6
+ DEFAULT_CA_FILE = File.expand_path('../../data/cacert.pem', __FILE__)
7
+
8
+ def self.detect_ca_cert_file!
9
+ @ca_cert_file = false
10
+ if defined?(OpenSSL::X509::DEFAULT_CERT_FILE)
11
+ f = OpenSSL::X509::DEFAULT_CERT_FILE
12
+
13
+ if f && File.exist?(f)
14
+ @ca_cert_file = f
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.detect_ca_cert_dir!
20
+ @ca_cert_dir = false
21
+ if defined?(OpenSSL::X509::DEFAULT_CERT_DIR)
22
+ d = OpenSSL::X509::DEFAULT_CERT_DIR
23
+
24
+ if d && File.exist?(d)
25
+ @ca_cert_dir = d
26
+ end
27
+ end
28
+ end
29
+
30
+ detect_ca_cert_file!
31
+ detect_ca_cert_dir!
32
+
33
+ def self.ca_cert_file?
34
+ !!@ca_cert_file
35
+ end
36
+
37
+ def self.ca_cert_dir?
38
+ !!@ca_cert_dir
39
+ end
40
+
41
+ def self.ca_cert_file_or_default
42
+ @ca_cert_file || DEFAULT_CA_FILE
43
+ end
44
+
45
+ def self.ca_cert_dir
46
+ @ca_cert_dir
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,207 @@
1
+ require 'skylight/vendor/active_support/notifications/instrumenter'
2
+ require 'skylight/vendor/active_support/notifications/fanout'
3
+ require 'skylight/vendor/active_support/per_thread_registry'
4
+
5
+ module ActiveSupport
6
+ # = Notifications
7
+ #
8
+ # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
9
+ # Ruby.
10
+ #
11
+ # == Instrumenters
12
+ #
13
+ # To instrument an event you just need to do:
14
+ #
15
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
16
+ # render text: 'Foo'
17
+ # end
18
+ #
19
+ # That executes the block first and notifies all subscribers once done.
20
+ #
21
+ # In the example above +render+ is the name of the event, and the rest is called
22
+ # the _payload_. The payload is a mechanism that allows instrumenters to pass
23
+ # extra information to subscribers. Payloads consist of a hash whose contents
24
+ # are arbitrary and generally depend on the event.
25
+ #
26
+ # == Subscribers
27
+ #
28
+ # You can consume those events and the information they provide by registering
29
+ # a subscriber.
30
+ #
31
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
32
+ # name # => String, name of the event (such as 'render' from above)
33
+ # start # => Time, when the instrumented block started execution
34
+ # finish # => Time, when the instrumented block ended execution
35
+ # id # => String, unique ID for this notification
36
+ # payload # => Hash, the payload
37
+ # end
38
+ #
39
+ # For instance, let's store all "render" events in an array:
40
+ #
41
+ # events = []
42
+ #
43
+ # ActiveSupport::Notifications.subscribe('render') do |*args|
44
+ # events << ActiveSupport::Notifications::Event.new(*args)
45
+ # end
46
+ #
47
+ # That code returns right away, you are just subscribing to "render" events.
48
+ # The block is saved and will be called whenever someone instruments "render":
49
+ #
50
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
51
+ # render text: 'Foo'
52
+ # end
53
+ #
54
+ # event = events.first
55
+ # event.name # => "render"
56
+ # event.duration # => 10 (in milliseconds)
57
+ # event.payload # => { extra: :information }
58
+ #
59
+ # The block in the <tt>subscribe</tt> call gets the name of the event, start
60
+ # timestamp, end timestamp, a string with a unique identifier for that event
61
+ # (something like "535801666f04d0298cd6"), and a hash with the payload, in
62
+ # that order.
63
+ #
64
+ # If an exception happens during that particular instrumentation the payload will
65
+ # have a key <tt>:exception</tt> with an array of two elements as value: a string with
66
+ # the name of the exception class, and the exception message.
67
+ #
68
+ # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
69
+ # is able to take the arguments as they come and provide an object-oriented
70
+ # interface to that data.
71
+ #
72
+ # It is also possible to pass an object as the second parameter passed to the
73
+ # <tt>subscribe</tt> method instead of a block:
74
+ #
75
+ # module ActionController
76
+ # class PageRequest
77
+ # def call(name, started, finished, unique_id, payload)
78
+ # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
79
+ # end
80
+ # end
81
+ # end
82
+ #
83
+ # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
84
+ #
85
+ # resulting in the following output within the logs including a hash with the payload:
86
+ #
87
+ # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
88
+ # controller: "Devise::SessionsController",
89
+ # action: "new",
90
+ # params: {"action"=>"new", "controller"=>"devise/sessions"},
91
+ # format: :html,
92
+ # method: "GET",
93
+ # path: "/login/sign_in",
94
+ # status: 200,
95
+ # view_runtime: 279.3080806732178,
96
+ # db_runtime: 40.053
97
+ # }
98
+ #
99
+ # You can also subscribe to all events whose name matches a certain regexp:
100
+ #
101
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
102
+ # ...
103
+ # end
104
+ #
105
+ # and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
106
+ # to all events.
107
+ #
108
+ # == Temporary Subscriptions
109
+ #
110
+ # Sometimes you do not want to subscribe to an event for the entire life of
111
+ # the application. There are two ways to unsubscribe.
112
+ #
113
+ # WARNING: The instrumentation framework is designed for long-running subscribers,
114
+ # use this feature sparingly because it wipes some internal caches and that has
115
+ # a negative impact on performance.
116
+ #
117
+ # === Subscribe While a Block Runs
118
+ #
119
+ # You can subscribe to some event temporarily while some block runs. For
120
+ # example, in
121
+ #
122
+ # callback = lambda {|*args| ... }
123
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
124
+ # ...
125
+ # end
126
+ #
127
+ # the callback will be called for all "sql.active_record" events instrumented
128
+ # during the execution of the block. The callback is unsubscribed automatically
129
+ # after that.
130
+ #
131
+ # === Manual Unsubscription
132
+ #
133
+ # The +subscribe+ method returns a subscriber object:
134
+ #
135
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
136
+ # ...
137
+ # end
138
+ #
139
+ # To prevent that block from being called anymore, just unsubscribe passing
140
+ # that reference:
141
+ #
142
+ # ActiveSupport::Notifications.unsubscribe(subscriber)
143
+ #
144
+ # == Default Queue
145
+ #
146
+ # Notifications ships with a queue implementation that consumes and publish events
147
+ # to log subscribers in a thread. You can use any queue implementation you want.
148
+ #
149
+ module Notifications
150
+ class << self
151
+ attr_accessor :notifier
152
+
153
+ def publish(name, *args)
154
+ notifier.publish(name, *args)
155
+ end
156
+
157
+ def instrument(name, payload = {})
158
+ if notifier.listening?(name)
159
+ instrumenter.instrument(name, payload) { yield payload if block_given? }
160
+ else
161
+ yield payload if block_given?
162
+ end
163
+ end
164
+
165
+ def subscribe(*args, &block)
166
+ notifier.subscribe(*args, &block)
167
+ end
168
+
169
+ def subscribed(callback, *args, &block)
170
+ subscriber = subscribe(*args, &callback)
171
+ yield
172
+ ensure
173
+ unsubscribe(subscriber)
174
+ end
175
+
176
+ def unsubscribe(args)
177
+ notifier.unsubscribe(args)
178
+ end
179
+
180
+ def instrumenter
181
+ InstrumentationRegistry.instrumenter_for(notifier)
182
+ end
183
+ end
184
+
185
+ # This class is a registry which holds all of the +Instrumenter+ objects
186
+ # in a particular thread local. To access the +Instrumenter+ object for a
187
+ # particular +notifier+, you can call the following method:
188
+ #
189
+ # InstrumentationRegistry.instrumenter_for(notifier)
190
+ #
191
+ # The instrumenters for multiple notifiers are held in a single instance of
192
+ # this class.
193
+ class InstrumentationRegistry # :nodoc:
194
+ extend ActiveSupport::PerThreadRegistry
195
+
196
+ def initialize
197
+ @registry = {}
198
+ end
199
+
200
+ def instrumenter_for(notifier)
201
+ @registry[notifier] ||= Instrumenter.new(notifier)
202
+ end
203
+ end
204
+
205
+ self.notifier = Fanout.new
206
+ end
207
+ end
@@ -0,0 +1,159 @@
1
+ require 'mutex_m'
2
+ begin
3
+ require 'thread_safe'
4
+ rescue LoadError
5
+ require 'skylight/vendor/thread_safe'
6
+ end
7
+
8
+ module ActiveSupport
9
+ module Notifications
10
+ # This is a default queue implementation that ships with Notifications.
11
+ # It just pushes events to all registered log subscribers.
12
+ #
13
+ # This class is thread safe. All methods are reentrant.
14
+ class Fanout
15
+ include Mutex_m
16
+
17
+ def initialize
18
+ @subscribers = []
19
+ @listeners_for = ThreadSafe::Cache.new
20
+ super
21
+ end
22
+
23
+ def subscribe(pattern = nil, block = Proc.new)
24
+ subscriber = Subscribers.new pattern, block
25
+ synchronize do
26
+ @subscribers << subscriber
27
+ @listeners_for.clear
28
+ end
29
+ subscriber
30
+ end
31
+
32
+ def unsubscribe(subscriber)
33
+ synchronize do
34
+ @subscribers.reject! { |s| s.matches?(subscriber) }
35
+ @listeners_for.clear
36
+ end
37
+ end
38
+
39
+ def start(name, id, payload)
40
+ listeners_for(name).each { |s| s.start(name, id, payload) }
41
+ end
42
+
43
+ def finish(name, id, payload)
44
+ listeners_for(name).each { |s| s.finish(name, id, payload) }
45
+ end
46
+
47
+ def publish(name, *args)
48
+ listeners_for(name).each { |s| s.publish(name, *args) }
49
+ end
50
+
51
+ def listeners_for(name)
52
+ # this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics)
53
+ @listeners_for[name] || synchronize do
54
+ # use synchronisation when accessing @subscribers
55
+ @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
56
+ end
57
+ end
58
+
59
+ def listening?(name)
60
+ listeners_for(name).any?
61
+ end
62
+
63
+ # This is a sync queue, so there is no waiting.
64
+ def wait
65
+ end
66
+
67
+ module Subscribers # :nodoc:
68
+ def self.new(pattern, listener)
69
+ if listener.respond_to?(:start) and listener.respond_to?(:finish)
70
+ subscriber = Evented.new pattern, listener
71
+ else
72
+ subscriber = Timed.new pattern, listener
73
+ end
74
+
75
+ unless pattern
76
+ AllMessages.new(subscriber)
77
+ else
78
+ subscriber
79
+ end
80
+ end
81
+
82
+ class Evented #:nodoc:
83
+ def initialize(pattern, delegate)
84
+ @pattern = pattern
85
+ @delegate = delegate
86
+ @can_publish = delegate.respond_to?(:publish)
87
+ end
88
+
89
+ def publish(name, *args)
90
+ if @can_publish
91
+ @delegate.publish name, *args
92
+ end
93
+ end
94
+
95
+ def start(name, id, payload)
96
+ @delegate.start name, id, payload
97
+ end
98
+
99
+ def finish(name, id, payload)
100
+ @delegate.finish name, id, payload
101
+ end
102
+
103
+ def subscribed_to?(name)
104
+ @pattern === name.to_s
105
+ end
106
+
107
+ def matches?(subscriber_or_name)
108
+ self === subscriber_or_name ||
109
+ @pattern && @pattern === subscriber_or_name
110
+ end
111
+ end
112
+
113
+ class Timed < Evented
114
+ def initialize(pattern, delegate)
115
+ @timestack = []
116
+ super
117
+ end
118
+
119
+ def publish(name, *args)
120
+ @delegate.call name, *args
121
+ end
122
+
123
+ def start(name, id, payload)
124
+ @timestack.push Time.now
125
+ end
126
+
127
+ def finish(name, id, payload)
128
+ started = @timestack.pop
129
+ @delegate.call(name, started, Time.now, id, payload)
130
+ end
131
+ end
132
+
133
+ class AllMessages # :nodoc:
134
+ def initialize(delegate)
135
+ @delegate = delegate
136
+ end
137
+
138
+ def start(name, id, payload)
139
+ @delegate.start name, id, payload
140
+ end
141
+
142
+ def finish(name, id, payload)
143
+ @delegate.finish name, id, payload
144
+ end
145
+
146
+ def publish(name, *args)
147
+ @delegate.publish name, *args
148
+ end
149
+
150
+ def subscribed_to?(name)
151
+ true
152
+ end
153
+
154
+ alias :matches? :===
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,72 @@
1
+ require 'securerandom'
2
+
3
+ module ActiveSupport
4
+ module Notifications
5
+ # Instrumenters are stored in a thread local.
6
+ class Instrumenter
7
+ attr_reader :id
8
+
9
+ def initialize(notifier)
10
+ @id = unique_id
11
+ @notifier = notifier
12
+ end
13
+
14
+ # Instrument the given block by measuring the time taken to execute it
15
+ # and publish it. Notice that events get sent even if an error occurs
16
+ # in the passed-in block.
17
+ def instrument(name, payload={})
18
+ start name, payload
19
+ begin
20
+ yield payload
21
+ rescue Exception => e
22
+ payload[:exception] = [e.class.name, e.message]
23
+ raise e
24
+ ensure
25
+ finish name, payload
26
+ end
27
+ end
28
+
29
+ # Send a start notification with +name+ and +payload+.
30
+ def start(name, payload)
31
+ @notifier.start name, @id, payload
32
+ end
33
+
34
+ # Send a finish notification with +name+ and +payload+.
35
+ def finish(name, payload)
36
+ @notifier.finish name, @id, payload
37
+ end
38
+
39
+ private
40
+
41
+ def unique_id
42
+ ::SecureRandom.hex(10)
43
+ end
44
+ end
45
+
46
+ class Event
47
+ attr_reader :name, :time, :transaction_id, :payload, :children
48
+ attr_accessor :end
49
+
50
+ def initialize(name, start, ending, transaction_id, payload)
51
+ @name = name
52
+ @payload = payload.dup
53
+ @time = start
54
+ @transaction_id = transaction_id
55
+ @end = ending
56
+ @children = []
57
+ end
58
+
59
+ def duration
60
+ 1000.0 * (self.end - time)
61
+ end
62
+
63
+ def <<(event)
64
+ @children << event
65
+ end
66
+
67
+ def parent_of?(event)
68
+ @children.include? event
69
+ end
70
+ end
71
+ end
72
+ end