Neurogami-jimpanzee 1.0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/Jimpanzee.gemspec +0 -0
- data/Manifest.txt +62 -0
- data/README.md +23 -0
- data/Rakefile +98 -0
- data/bin/jimpanzee +41 -0
- data/lib/monkeybars.rb +7 -0
- data/lib/monkeybars/controller.rb +608 -0
- data/lib/monkeybars/debug.rb +69 -0
- data/lib/monkeybars/event_handler.rb +89 -0
- data/lib/monkeybars/event_handler_registration_and_dispatch_mixin.rb +251 -0
- data/lib/monkeybars/exceptions.rb +10 -0
- data/lib/monkeybars/global_error_handler.rb +34 -0
- data/lib/monkeybars/inflector.rb +68 -0
- data/lib/monkeybars/key.rb +206 -0
- data/lib/monkeybars/task_processor.rb +47 -0
- data/lib/monkeybars/validated_hash.rb +22 -0
- data/lib/monkeybars/view.rb +609 -0
- data/lib/monkeybars/view_mapping.rb +379 -0
- data/lib/monkeybars/view_nesting.rb +114 -0
- data/lib/monkeybars/view_positioning.rb +66 -0
- data/skeleton/Rakefile +5 -0
- data/skeleton/lib/java/README.txt +1 -0
- data/skeleton/lib/java/monkeybars-1.0.2.1.jar +0 -0
- data/skeleton/lib/ruby/README.md +1 -0
- data/skeleton/lib/ruby/README.txt +1 -0
- data/skeleton/src/application_controller.rb +4 -0
- data/skeleton/src/application_view.rb +3 -0
- data/skeleton/src/main.rb +52 -0
- data/skeleton/src/manifest.rb +58 -0
- data/skeleton/src/resolver.rb +33 -0
- data/skeleton/tasks/monkeybars.rake +89 -0
- metadata +99 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
require "monkeybars/task_processor"
|
2
|
+
|
3
|
+
module Monkeybars
|
4
|
+
class Debug
|
5
|
+
extend Monkeybars::TaskProcessor
|
6
|
+
|
7
|
+
# Reads in ARGV, and enables various Monkeybars specific debugging capabilities
|
8
|
+
# TODO: Restore args not used
|
9
|
+
def self.enable_on_debugging_args
|
10
|
+
puts "inspecting args"
|
11
|
+
puts ARGV
|
12
|
+
until ARGV.empty?
|
13
|
+
arg = ARGV.pop
|
14
|
+
case arg
|
15
|
+
when '--debug-server'
|
16
|
+
port = ARGV.pop
|
17
|
+
begin
|
18
|
+
port.to_i
|
19
|
+
rescue
|
20
|
+
port = 4848
|
21
|
+
# put the arg back
|
22
|
+
ARGV.unshift port
|
23
|
+
end
|
24
|
+
start_server port
|
25
|
+
when '--record-edt'
|
26
|
+
puts "recording EDT"
|
27
|
+
record_edt
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.record_edt
|
33
|
+
listener = lambda do |event|
|
34
|
+
puts "found event #{event}"
|
35
|
+
end
|
36
|
+
Java::java::awt::Toolkit.default_toolkit.addAWTEventListener listener, 0xFFFFFFFFFFFF
|
37
|
+
end
|
38
|
+
# Use --debug-server to enable
|
39
|
+
# allows user to telnet in and send code to be evaled. Results are returned.
|
40
|
+
def self.start_server(port=4848)
|
41
|
+
Thread.new do
|
42
|
+
require 'socket'
|
43
|
+
server = TCPServer.new(port)
|
44
|
+
begin
|
45
|
+
socket = server.accept_nonblock
|
46
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
|
47
|
+
IO.select([server])
|
48
|
+
retry
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "connected!"
|
52
|
+
until socket.closed?
|
53
|
+
begin
|
54
|
+
line = socket.readline
|
55
|
+
puts "evaling #{line}"
|
56
|
+
result = on_edt { eval line }
|
57
|
+
puts "returning result #{result}"
|
58
|
+
socket.write "#{result}\n"
|
59
|
+
rescue => e
|
60
|
+
puts "error, returning error #{e}"
|
61
|
+
socket.write "#{e}\n"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
enable_on_debugging_args
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'monkeybars/inflector'
|
2
|
+
|
3
|
+
include_class 'javax.swing.SwingUtilities'
|
4
|
+
|
5
|
+
module Monkeybars
|
6
|
+
# This class is primarily used internally for setting up a handler for window
|
7
|
+
# close events although any of the WindowAdapter methods can be set. To instantiate
|
8
|
+
# a new MonkeybarsWindowAdapter, pass in a hash of method name symbols and method
|
9
|
+
# objects. The method names must be the various methods from the
|
10
|
+
# java.awt.event.WindowListener interface.
|
11
|
+
#
|
12
|
+
# For example:
|
13
|
+
#
|
14
|
+
# def handle_window_closing(event)
|
15
|
+
# puts "the window is closing"
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# handler = MonkeybarsWindowAdapter.new(:windowClosing => method(handle_window_closing))
|
19
|
+
class MonkeybarsWindowAdapter #:nodoc:
|
20
|
+
def initialize(methods)
|
21
|
+
super()
|
22
|
+
raise ArgumentError if methods.empty?
|
23
|
+
methods.each { |method, proc| raise ArgumentError.new("Only window and internalFrame events can be used to create a MonkeybarsWindowAdapter") unless (/^(window|internalFrame)/ =~ method.to_s) and (proc.respond_to? :to_proc) }
|
24
|
+
@methods = methods
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(method, *args, &blk)
|
28
|
+
if /^(window|internalFrame)/ =~ method.to_s
|
29
|
+
@methods[method].call(*args) if @methods[method]
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# This module is used internally by the various XYZHandler classes as the
|
38
|
+
# recipent of events. It dispatches the event handling to the controller's
|
39
|
+
# handle_event method.
|
40
|
+
module BaseHandler
|
41
|
+
def method_missing(method, *args, &block)
|
42
|
+
@callback.handle_event(@component_name, method.underscore, args[0])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Handlers
|
47
|
+
# TODO: add bean types like vetoable change, property change, etc.
|
48
|
+
BEAN_TYPES = ["PropertyChange"]
|
49
|
+
AWT_TYPES = ["Action","Adjustment","AWTEvent","Component","Container","Focus",
|
50
|
+
"HierarchyBounds","Hierarchy","InputMethod","Item","Key","Mouse",
|
51
|
+
"MouseMotion","MouseWheel","Text", "WindowFocus","Window","WindowState"]
|
52
|
+
SWING_TYPES = ["Ancestor", "Caret", "CellEditor", "Change", "Document",
|
53
|
+
"Hyperlink", "InternalFrame", "ListData", "ListSelection",
|
54
|
+
"MenuDragMouse", "MenuKey", "Menu", "MouseInput", "PopupMenu",
|
55
|
+
"TableColumnModel", "TableModel", "TreeExpansion", "TreeModel",
|
56
|
+
"TreeSelection", "TreeWillExpand", "UndoableEdit"]
|
57
|
+
ALL_EVENT_NAMES = []
|
58
|
+
EVENT_NAMES_BY_TYPE = Hash.new{|h,k| h[k] = []}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
{"java.awt.event" => Monkeybars::Handlers::AWT_TYPES, "javax.swing.event" => Monkeybars::Handlers::SWING_TYPES, "java.beans" => Monkeybars::Handlers::BEAN_TYPES}.each do |java_package, types|
|
63
|
+
types.each do |type|
|
64
|
+
eval <<-ENDL
|
65
|
+
module Monkeybars
|
66
|
+
class #{type}Handler
|
67
|
+
def initialize(callback, component_name)
|
68
|
+
@callback = callback
|
69
|
+
@component_name = component_name
|
70
|
+
end
|
71
|
+
|
72
|
+
def type
|
73
|
+
"#{type}"
|
74
|
+
end
|
75
|
+
|
76
|
+
include Monkeybars::BaseHandler
|
77
|
+
include #{java_package}.#{type}Listener
|
78
|
+
end
|
79
|
+
end
|
80
|
+
ENDL
|
81
|
+
|
82
|
+
interface = eval "#{java_package}.#{type}Listener"
|
83
|
+
interface.java_class.java_instance_methods.each do |method|
|
84
|
+
Monkeybars::Handlers::ALL_EVENT_NAMES << method.name.underscore
|
85
|
+
Monkeybars::Handlers::EVENT_NAMES_BY_TYPE[type] << method.name.underscore
|
86
|
+
end
|
87
|
+
Monkeybars::Handlers::ALL_EVENT_NAMES.uniq!
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'monkeybars/exceptions'
|
2
|
+
module Monkeybars
|
3
|
+
module EventHandlerRegistrationAndDispatchMixin
|
4
|
+
def self.included(target)
|
5
|
+
target.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
@@event_handler_procs ||= {}
|
10
|
+
def event_handler_procs
|
11
|
+
@@event_handler_procs[self] ||= Hash.new {|hash, key| hash[key] = []}
|
12
|
+
end
|
13
|
+
|
14
|
+
@@handlers ||= {}
|
15
|
+
def handlers
|
16
|
+
@@handlers[self] ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Declares which components you want events to be generated for. add_listener
|
20
|
+
# takes a hash of the form :type => type, :components => [components for events]
|
21
|
+
# All AWT and Swing listener types are supported. See Monkeybars::Handlers for
|
22
|
+
# the full list.
|
23
|
+
#
|
24
|
+
# The array of components should be strings or symbols with the exact naming of the
|
25
|
+
# component in the Java class declared in the view. As an example, if you have a JFrame
|
26
|
+
# with a text area named infoTextField that you wanted to receive key events for, perhaps
|
27
|
+
# to filter certain key input or to enable an auto-completion feature you could use:
|
28
|
+
#
|
29
|
+
# add_listener :type => :key, :components => [:infoTextField]
|
30
|
+
#
|
31
|
+
# To handle the event you would then need to implement a method named
|
32
|
+
# <component>_<event> which in this case would be info_text_field_key_pressed,
|
33
|
+
# info_text_field_key_released or info_text_field_key_typed.
|
34
|
+
#
|
35
|
+
# If you have a single component you can omit the array and pass a single string or symbol.
|
36
|
+
#
|
37
|
+
# add_listener :type => :key, :components => :infoTextField
|
38
|
+
#
|
39
|
+
# You will run into errors if your component is a nested name, for example
|
40
|
+
#
|
41
|
+
# add_listener :type => :document, :components => "infoTextField.document"
|
42
|
+
#
|
43
|
+
# because when the event is generated and a handler is attempted to be located,
|
44
|
+
# the name infoTextField.document doesn't map well to a method. To resolve this,
|
45
|
+
# the component name can be a hash, the key being the component name and the value
|
46
|
+
# being the desired callback name.
|
47
|
+
#
|
48
|
+
# add_listener :type => :document, :components => {"infoTextField.document" => "info_text_field"}
|
49
|
+
#
|
50
|
+
# This will cause the info_text_field_action_performed method to be called when
|
51
|
+
# the action performed event is generated by infoTextField.document.
|
52
|
+
#
|
53
|
+
# If you want to add a listener to the view itself (JFrame, JDialog, etc.)
|
54
|
+
# then you can use :java_window as the component
|
55
|
+
#
|
56
|
+
# add_listener :type => :window, :components => [:java_window]
|
57
|
+
#
|
58
|
+
# If it is not possible to declare a method, or it is desirable to do so dynamically
|
59
|
+
# (even from outside the class), you can use the define_handler method.
|
60
|
+
#
|
61
|
+
# If you wish to override the default event handling behavior, override handle_event
|
62
|
+
def add_listener(details)
|
63
|
+
handlers << details
|
64
|
+
end
|
65
|
+
|
66
|
+
# define_handler takes a component/event name and a block to be called when that
|
67
|
+
# event is generated for that component. This can be used in place of a method
|
68
|
+
# declaration for that component/event pair.
|
69
|
+
#
|
70
|
+
# So, if you have declared:
|
71
|
+
#
|
72
|
+
# add_listener :type => :action, :components => [:ok_button]
|
73
|
+
#
|
74
|
+
# you could implement the handler using:
|
75
|
+
#
|
76
|
+
# define_handler(:ok_button_action_performed) do |event|
|
77
|
+
# # handle the event here
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# Note that handlers defined using this method will create implicit listener
|
81
|
+
# registrations the same as a declared method would.
|
82
|
+
#
|
83
|
+
# define_handler also accepts multiple event names
|
84
|
+
#
|
85
|
+
# define_handler(:ok_button_action_performed, :cancel_button_action_performed) do
|
86
|
+
# # handle event(s) here
|
87
|
+
# end
|
88
|
+
def define_handler(*actions, &block)
|
89
|
+
actions.each {|action| event_handler_procs[action.to_sym] << block}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# This method should be called from the initialize method of the class using
|
94
|
+
# this mixin to set up the needed instance variables and both declared
|
95
|
+
# and implicit handlers
|
96
|
+
def setup_implicit_and_explicit_event_handlers # :nodoc:
|
97
|
+
@__event_handler_view_target = if self.class.ancestors.member?(Monkeybars::Controller)
|
98
|
+
@__view
|
99
|
+
else
|
100
|
+
self
|
101
|
+
end
|
102
|
+
@__registered_handlers = Hash.new{|h,k| h[k] = []}
|
103
|
+
@__event_handler_procs = Hash.new{|h,k| h[k] = []}
|
104
|
+
|
105
|
+
unless self.class.handlers.empty?
|
106
|
+
if @__event_handler_view_target.nil?
|
107
|
+
raise "A view must be declared in order to add event listeners"
|
108
|
+
end
|
109
|
+
|
110
|
+
self.class.handlers.each do |handler|
|
111
|
+
handler[:components].each do |component|
|
112
|
+
if component.kind_of? Array
|
113
|
+
component = component.first
|
114
|
+
end
|
115
|
+
begin
|
116
|
+
resolved_component = @__event_handler_view_target.instance_eval(component.to_s, __FILE__, __LINE__)
|
117
|
+
rescue NoMethodError => e
|
118
|
+
raise InvalidHandlerError, "Could not find component: #{component} on view #{@__event_handler_view_target}\nOriginal exception: #{e.message}"
|
119
|
+
end
|
120
|
+
|
121
|
+
add_handler_for handler[:type], handler[:components], resolved_component
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
monkeybars_base_class = self.class.ancestors.find {|klass| /^Monkeybars::(Controller|View)$/ =~ klass.name }
|
127
|
+
(methods.grep(/_/) - (EventHandlerRegistrationAndDispatchMixin.instance_methods + monkeybars_base_class.instance_methods)).each {|method| add_implicit_handler_for_method(method) }
|
128
|
+
|
129
|
+
self.class.event_handler_procs.each {|method, proc| add_implicit_handler_for_method(method)}
|
130
|
+
end
|
131
|
+
|
132
|
+
# Instance-level version of Monkeybars::Controller.define_handler. It follows the same
|
133
|
+
# syntax as the class-level version but applies the callback block as a listener to events
|
134
|
+
# generated by this instance of the controller class' view. This callback is
|
135
|
+
# useful when the application has nested controllers and event handling needs to be different
|
136
|
+
# for each instance of a controller class.
|
137
|
+
#
|
138
|
+
# define_handler can accept either a single event or a list of events to apply the block to:
|
139
|
+
#
|
140
|
+
# define_handler :ok_button_action_performed { puts "action performed on 'ok button'" }
|
141
|
+
#
|
142
|
+
# define_handler :ok_button_action_performed, :cancel_button_action_performed { puts "action performed on a button" }
|
143
|
+
#
|
144
|
+
# If you are defining a handler that requires aliasing, define handler can also be passed a hash of method => component mappings
|
145
|
+
# mixed in with the methods to apply the handler to.
|
146
|
+
#
|
147
|
+
# define_handler :text_field_insert_update => "text_field.document" { puts "you typed something" }
|
148
|
+
#
|
149
|
+
# define_handler :text_field_insert_update => "text_field.document", :text_field_remove_update => "text_field.document" { puts "you typed or deleted something" }
|
150
|
+
#
|
151
|
+
# These mappings can also be mixed in with regular methods. It is suggested that you put
|
152
|
+
# all of your hash items at the end of the argument list so they are wrapped up into an
|
153
|
+
# implicit Hash object although this is not strictly necessary.
|
154
|
+
#
|
155
|
+
# define_handler :ok_button_action_performed, :text_field_insert_update => "text_field.document" { puts "you did ... something" }
|
156
|
+
#
|
157
|
+
def define_handler(*actions, &block)
|
158
|
+
# define_handler :foo_action_performed => :foo_document_action_performed, { handle event here }
|
159
|
+
actions.each do |action|
|
160
|
+
if action.kind_of? Hash
|
161
|
+
# handle a hash with multiple mappings, e.g.
|
162
|
+
# define_handler :text_field_insert_update => "text_field.document", :text_field2_insert_update => "text_field2.document { ... handler code here ... }
|
163
|
+
action.each do |method, component|
|
164
|
+
@__event_handler_procs[method.to_sym] << block
|
165
|
+
add_implicit_handler_for_method(method, component)
|
166
|
+
end
|
167
|
+
else
|
168
|
+
@__event_handler_procs[action.to_sym] << block
|
169
|
+
add_implicit_handler_for_method(action)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Specific handlers get precedence over general handlers, that is button_mouse_released
|
175
|
+
# gets called before mouse_released.
|
176
|
+
def handle_event(component_name, event_name, event) #:nodoc:
|
177
|
+
return if event.nil?
|
178
|
+
|
179
|
+
callbacks = get_callbacks("#{component_name}_#{event_name}".to_sym)
|
180
|
+
if callbacks.empty?
|
181
|
+
callbacks = get_callbacks(event_name.to_sym)
|
182
|
+
end
|
183
|
+
|
184
|
+
callbacks.each{ |proc| 0 == proc.arity ? proc.call : proc.call(event) }
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
def get_callbacks(method)
|
189
|
+
callbacks = []
|
190
|
+
begin
|
191
|
+
callbacks << method(method)
|
192
|
+
rescue NameError; end
|
193
|
+
callbacks + self.class.event_handler_procs[method] + @__event_handler_procs[method]
|
194
|
+
end
|
195
|
+
|
196
|
+
def add_implicit_handler_for_method(method, component_to_alias = nil)
|
197
|
+
component_match = nil
|
198
|
+
|
199
|
+
Monkeybars::Handlers::ALL_EVENT_NAMES.each do |event|
|
200
|
+
component_match = Regexp.new("(.*)_(#{event})").match(method.to_s)
|
201
|
+
break unless component_match.nil?
|
202
|
+
end
|
203
|
+
|
204
|
+
return if component_match.nil?
|
205
|
+
component_name, event_name = component_match[1], component_match[2]
|
206
|
+
|
207
|
+
begin
|
208
|
+
if component_to_alias.nil?
|
209
|
+
component = @__event_handler_view_target.instance_eval(component_name)
|
210
|
+
else
|
211
|
+
component = @__event_handler_view_target.instance_eval(component_to_alias)
|
212
|
+
end
|
213
|
+
rescue NameError => e
|
214
|
+
rescue Monkeybars::UndefinedComponentError => e
|
215
|
+
# swallow, handler style methods for controls that don't exist is allowed
|
216
|
+
else
|
217
|
+
component.methods.each do |method|
|
218
|
+
listener_match = /add(.*)Listener/.match(method)
|
219
|
+
next if listener_match.nil?
|
220
|
+
if Monkeybars::Handlers::EVENT_NAMES_BY_TYPE[listener_match[1]].member? event_name
|
221
|
+
if component_to_alias.nil?
|
222
|
+
add_handler_for listener_match[1], component_name, component
|
223
|
+
else
|
224
|
+
add_handler_for listener_match[1], {component_to_alias => component_name}, component
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def add_handler_for(handler_type, components, java_component)
|
232
|
+
components = ["global"] if components.nil?
|
233
|
+
components = [components] unless components.kind_of? Array
|
234
|
+
components.each do |component|
|
235
|
+
# handle aliases :components => {"text_area.document" => "text_area"}
|
236
|
+
if component.kind_of? Hash
|
237
|
+
component_name = component.values[0]
|
238
|
+
component_field = component.keys[0]
|
239
|
+
else
|
240
|
+
component_name = component
|
241
|
+
component_field = component
|
242
|
+
end
|
243
|
+
unless @__registered_handlers[java_component].member? handler_type.underscore
|
244
|
+
handler = "Monkeybars::#{handler_type.camelize}Handler".constantize.new(self, component_name.to_s)
|
245
|
+
@__event_handler_view_target.add_handler(handler, component_field)
|
246
|
+
@__registered_handlers[java_component] << handler_type.underscore
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Monkeybars
|
2
|
+
class UndefinedComponentError < RuntimeError; end
|
3
|
+
class InvalidSignalHandlerError < RuntimeError; end
|
4
|
+
class UndefinedSignalError < RuntimeError; end
|
5
|
+
class InvalidCloseAction < RuntimeError; end
|
6
|
+
class InvalidMappingError < RuntimeError; end
|
7
|
+
class TranslationError < RuntimeError; end
|
8
|
+
class InvalidNestingError < RuntimeError; end
|
9
|
+
class InvalidHandlerError < RuntimeError; end
|
10
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Interface to setting up a handler for any uncaught exceptions in the application.
|
2
|
+
# The block that is passed into GlobalErrorHandler.on_error will be called when
|
3
|
+
# an uncaught exception occurs.
|
4
|
+
#
|
5
|
+
# You must be *VERY* careful when implementing your handler. All uncaught exceptions
|
6
|
+
# will be routed to this block so any error that occur inside the block will not
|
7
|
+
# generate exceptions.
|
8
|
+
class GlobalErrorHandler
|
9
|
+
include Java::java::lang::Thread::UncaughtExceptionHandler
|
10
|
+
|
11
|
+
# Creation point for the GlobalErrorHandler. To use, pass in a block that takes
|
12
|
+
# 2 parameters, the exception and the thread that the exception occured on.
|
13
|
+
#
|
14
|
+
# The exception passed into this block is a *Java* Throwable, not a Ruby exception.
|
15
|
+
# http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Throwable.html
|
16
|
+
#
|
17
|
+
# GlobalErrorHandler.on_error {|exception, thread| puts "Error #{exception} occured on thread #{thread}" }
|
18
|
+
#
|
19
|
+
# or you may want to dispatch to an error handler method.
|
20
|
+
#
|
21
|
+
# GlobalErrorHandler.on_error {|exception, thread| my_error_handler_method(exception, thread) }
|
22
|
+
def self.on_error(&callback)
|
23
|
+
java.lang.Thread.default_uncaught_exception_handler = self.new(&callback)
|
24
|
+
end
|
25
|
+
|
26
|
+
def uncaughtException(thread, exception)
|
27
|
+
@callback.call(exception, thread)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def initialize(&callback)
|
32
|
+
@callback = callback
|
33
|
+
end
|
34
|
+
end
|