Neurogami-jimpanzee 1.0.2.1
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.
- 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
|