scarpe 0.2.1 → 0.2.2
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.yardopts +11 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +112 -0
- data/README.md +31 -24
- data/Rakefile +13 -1
- data/docs/yard/catscradle.md +44 -0
- data/docs/yard/template/default/fulldoc/html/setup.rb +13 -0
- data/docs/yard/template/default/layout/html/setup.rb +9 -0
- data/examples/background_with_image.rb +16 -0
- data/examples/bloopsaphone/working/bronx_army_knife.rb +66 -0
- data/examples/bloopsaphone/working/morning_serenity.rb +21 -0
- data/examples/bloopsaphone/working/simpsons_theme_song_by_why.rb +6 -4
- data/examples/button_go_away.rb +1 -1
- data/examples/check.rb +18 -0
- data/examples/clear_and_append.rb +24 -0
- data/examples/download_and_show_image.rb +28 -0
- data/examples/edit_box.rb +3 -5
- data/examples/fonts.rb +2 -2
- data/examples/get_headers.rb +10 -0
- data/examples/highlander.rb +2 -0
- data/examples/link.rb +2 -2
- data/examples/local_fonts.rb +4 -0
- data/examples/local_images.rb +4 -0
- data/examples/motion_events.rb +20 -0
- data/examples/parse_xl_funnies.rb +58 -0
- data/examples/radio/radio.rb +16 -0
- data/examples/radio/radio_groups.rb +18 -0
- data/examples/radio/radio_same_slot.rb +6 -0
- data/examples/ruby_racer.rb +13 -15
- data/examples/selfitude.rb +18 -0
- data/examples/shapes/shapes_fill.rb +4 -3
- data/examples/shoes_school.rb +2 -4
- data/examples/show_hide.rb +6 -0
- data/examples/skip_ci/change_my_audio_source.rb +21 -0
- data/examples/skip_ci/guitar_fretboard.rb +137 -0
- data/examples/video.rb +10 -0
- data/exe/scarpe +42 -66
- data/fonts/Pacifico.ttf +0 -0
- data/lacci/Gemfile +22 -0
- data/lacci/Gemfile.lock +72 -0
- data/lacci/Rakefile +12 -0
- data/lacci/lacci.gemspec +37 -0
- data/lacci/lib/lacci/scarpe_cli.rb +70 -0
- data/lacci/lib/lacci/scarpe_core.rb +21 -0
- data/lacci/lib/lacci/version.rb +13 -0
- data/lacci/lib/shoes/app.rb +264 -0
- data/{lib/scarpe → lacci/lib/shoes}/background.rb +1 -1
- data/{lib/scarpe → lacci/lib/shoes}/border.rb +1 -1
- data/{lib/scarpe → lacci/lib/shoes}/colors.rb +1 -1
- data/lacci/lib/shoes/constants.rb +29 -0
- data/{lib/scarpe → lacci/lib/shoes}/display_service.rb +40 -45
- data/lacci/lib/shoes/download.rb +123 -0
- data/lacci/lib/shoes/log.rb +71 -0
- data/lacci/lib/shoes/spacing.rb +9 -0
- data/{lib/scarpe → lacci/lib/shoes}/widget.rb +63 -43
- data/{lib/scarpe → lacci/lib/shoes/widgets}/alert.rb +3 -3
- data/{lib/scarpe → lacci/lib/shoes/widgets}/arc.rb +7 -5
- data/{lib/scarpe → lacci/lib/shoes/widgets}/button.rb +3 -3
- data/lacci/lib/shoes/widgets/check.rb +28 -0
- data/lacci/lib/shoes/widgets/document_root.rb +20 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_box.rb +10 -5
- data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_line.rb +2 -2
- data/lacci/lib/shoes/widgets/flow.rb +22 -0
- data/lacci/lib/shoes/widgets/font.rb +14 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/image.rb +3 -7
- data/lacci/lib/shoes/widgets/line.rb +18 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/link.rb +2 -2
- data/{lib/scarpe → lacci/lib/shoes/widgets}/list_box.rb +2 -2
- data/{lib/scarpe → lacci/lib/shoes/widgets}/para.rb +4 -26
- data/lacci/lib/shoes/widgets/radio.rb +35 -0
- data/lacci/lib/shoes/widgets/shape.rb +37 -0
- data/lacci/lib/shoes/widgets/slot.rb +75 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/span.rb +2 -2
- data/lacci/lib/shoes/widgets/stack.rb +24 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/star.rb +6 -9
- data/lacci/lib/shoes/widgets/subscription_item.rb +60 -0
- data/lacci/lib/shoes/widgets/text_widget.rb +51 -0
- data/lacci/lib/shoes/widgets/video.rb +15 -0
- data/lacci/lib/shoes/widgets.rb +29 -0
- data/lacci/lib/shoes.rb +127 -0
- data/lacci/test/test_colors.rb +39 -0
- data/lacci/test/test_helper.rb +9 -0
- data/lacci/test/test_lacci.rb +9 -0
- data/lib/scarpe/cats_cradle.rb +249 -0
- data/lib/scarpe/evented_assertions.rb +88 -0
- data/lib/scarpe/version.rb +1 -1
- data/lib/scarpe/wv/alert.rb +3 -2
- data/lib/scarpe/wv/app.rb +30 -8
- data/lib/scarpe/wv/arc.rb +5 -6
- data/lib/scarpe/wv/background.rb +10 -1
- data/lib/scarpe/wv/border.rb +5 -3
- data/lib/scarpe/wv/button.rb +11 -9
- data/lib/scarpe/wv/check.rb +29 -0
- data/lib/scarpe/wv/control_interface.rb +14 -20
- data/lib/scarpe/wv/control_interface_test.rb +13 -28
- data/lib/scarpe/wv/document_root.rb +3 -45
- data/lib/scarpe/wv/edit_box.rb +5 -7
- data/lib/scarpe/wv/edit_line.rb +2 -2
- data/lib/scarpe/wv/flow.rb +10 -20
- data/lib/scarpe/wv/font.rb +36 -0
- data/lib/scarpe/wv/html.rb +3 -2
- data/lib/scarpe/wv/image.rb +7 -2
- data/lib/scarpe/wv/line.rb +4 -7
- data/lib/scarpe/wv/link.rb +1 -0
- data/lib/scarpe/wv/list_box.rb +3 -3
- data/lib/scarpe/wv/para.rb +16 -14
- data/lib/scarpe/wv/radio.rb +34 -0
- data/lib/scarpe/wv/shape.rb +44 -8
- data/lib/scarpe/wv/slot.rb +81 -0
- data/lib/scarpe/wv/spacing.rb +1 -1
- data/lib/scarpe/wv/span.rb +10 -8
- data/lib/scarpe/wv/stack.rb +10 -30
- data/lib/scarpe/wv/star.rb +11 -12
- data/lib/scarpe/wv/subscription_item.rb +50 -0
- data/lib/scarpe/wv/video.rb +34 -0
- data/lib/scarpe/wv/web_wrangler.rb +238 -58
- data/lib/scarpe/wv/webview_local_display.rb +27 -5
- data/lib/scarpe/wv/webview_relay_display.rb +18 -119
- data/lib/scarpe/wv/webview_relay_util.rb +143 -0
- data/lib/scarpe/wv/widget.rb +80 -11
- data/lib/scarpe/wv/wv_display_worker.rb +17 -4
- data/lib/scarpe/wv.rb +33 -4
- data/lib/scarpe/wv_local.rb +1 -1
- data/lib/scarpe/wv_relay.rb +1 -1
- data/lib/scarpe.rb +3 -32
- data/scarpe-components/.gitignore +1 -0
- data/scarpe-components/Gemfile +22 -0
- data/scarpe-components/README.md +35 -0
- data/scarpe-components/Rakefile +12 -0
- data/scarpe-components/lib/scarpe/components/base64.rb +29 -0
- data/scarpe-components/lib/scarpe/components/file_helpers.rb +65 -0
- data/scarpe-components/lib/scarpe/components/modular_logger.rb +113 -0
- data/scarpe-components/lib/scarpe/components/print_logger.rb +43 -0
- data/{lib/scarpe → scarpe-components/lib/scarpe/components}/promises.rb +102 -35
- data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +170 -0
- data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +217 -0
- data/scarpe-components/lib/scarpe/components/version.rb +7 -0
- data/scarpe-components/scarpe-components.gemspec +38 -0
- data/scarpe-components/test/test_components.rb +9 -0
- data/scarpe-components/test/test_helper.rb +23 -0
- data/scarpe-components/test/test_promises.rb +260 -0
- data/scarpe-components/test/test_segmented_app_files.rb +182 -0
- data/{lib/scarpe → spikes}/glibui/widget.rb +2 -2
- data/{lib/scarpe → spikes}/glibui.rb +1 -1
- data/templates/basic_class_template.erb +1 -1
- data/templates/class_template_with_event_bind.erb +1 -1
- data/templates/class_template_with_shapes.erb +1 -1
- data/templates/webview_template.erb +0 -3
- metadata +151 -118
- data/examples/fill.rb +0 -25
- data/examples/legacy/not_checked/shoes-contrib/basic/class-book.yaml +0 -387
- data/examples/legacy/not_checked/shoes-contrib/good/good-clock.rb +0 -51
- data/examples/legacy/not_checked/shoes-contrib/good/good-follow.rb +0 -26
- data/examples/legacy/not_checked/shoes-contrib/good/good-reminder.rb +0 -174
- data/examples/legacy/not_checked/shoes-contrib/good/good-vjot.rb +0 -56
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-timer.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/good-clock.rb +0 -51
- data/examples/legacy/not_checked/shoes-dep-samples/good-follow.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/good-reminder.rb +0 -174
- data/examples/legacy/not_checked/shoes-dep-samples/good-vjot.rb +0 -56
- data/examples/legacy/not_checked/shoes-dep-samples/simple-accordion.rb +0 -75
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-shapes.rb +0 -17
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-text.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-arc.rb +0 -23
- data/examples/legacy/not_checked/shoes-dep-samples/simple-bounce.rb +0 -24
- data/examples/legacy/not_checked/shoes-dep-samples/simple-calc.rb +0 -70
- data/examples/legacy/not_checked/shoes-dep-samples/simple-chipmunk.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/simple-control-sizes.rb +0 -24
- data/examples/legacy/not_checked/shoes-dep-samples/simple-curve.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/simple-dialogs.rb +0 -29
- data/examples/legacy/not_checked/shoes-dep-samples/simple-draw.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-editor.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.shy +0 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-mask.rb +0 -21
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu.rb +0 -31
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu1.rb +0 -35
- data/examples/legacy/not_checked/shoes-dep-samples/simple-rubygems.rb +0 -29
- data/examples/legacy/not_checked/shoes-dep-samples/simple-slide.rb +0 -45
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sphere.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sqlite3.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-timer.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-video.rb +0 -13
- data/examples/legacy/not_checked/simple/anim-text.rb +0 -13
- data/examples/legacy/not_checked/simple/arc.rb +0 -23
- data/examples/legacy/not_checked/simple/bounce.rb +0 -24
- data/examples/legacy/not_checked/simple/chipmunk.rb +0 -26
- data/examples/legacy/not_checked/simple/curve.rb +0 -26
- data/examples/legacy/not_checked/simple/dialogs.rb +0 -29
- data/examples/legacy/not_checked/simple/downloader.rb +0 -40
- data/examples/legacy/not_checked/simple/draw.rb +0 -13
- data/examples/legacy/not_checked/simple/mask.rb +0 -21
- data/examples/legacy/not_checked/simple/slide.rb +0 -45
- data/examples/legacy/not_checked/simple/sphere.rb +0 -28
- data/lib/constants.rb +0 -5
- data/lib/scarpe/app.rb +0 -78
- data/lib/scarpe/document_root.rb +0 -20
- data/lib/scarpe/fill.rb +0 -23
- data/lib/scarpe/flow.rb +0 -19
- data/lib/scarpe/line.rb +0 -25
- data/lib/scarpe/logger.rb +0 -155
- data/lib/scarpe/shape.rb +0 -19
- data/lib/scarpe/spacing.rb +0 -9
- data/lib/scarpe/stack.rb +0 -70
- data/lib/scarpe/text_widget.rb +0 -42
- data/lib/scarpe/unit_test_helpers.rb +0 -163
- data/lib/scarpe/widgets.rb +0 -30
- data/lib/scarpe/wv/fill.rb +0 -30
- data/lib/scarpe/wv/shape_helper.rb +0 -44
- data/scarpe-0.2.0.gem +0 -0
- /data/{lib/scarpe → spikes}/glibui/README.md +0 -0
- /data/{lib/scarpe → spikes}/glibui/alert.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/app.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/background.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/border.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/button.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/dimensions.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/document_root.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/edit_box.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/edit_line.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/flow.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/html.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/image.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/link.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/local_display.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/para.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/spacing.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/stack.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/text_widget.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/alert.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/button.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/colors.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/core.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/flow.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/libui.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/notepad.md +0 -0
- /data/{lib/scarpe → spikes}/libui/para.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/stack.rb +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "scarpe/components/unit_test_helpers"
|
|
4
|
+
require "scarpe/evented_assertions"
|
|
5
|
+
|
|
6
|
+
require "fiber"
|
|
7
|
+
|
|
8
|
+
module Scarpe::Test
|
|
9
|
+
# We'd like something we can call Shoes widget methods on, such as para.replace.
|
|
10
|
+
# But we'd also like to be able to grab the corresponding display widget and
|
|
11
|
+
# call some of *those* methods.
|
|
12
|
+
class CCProxy
|
|
13
|
+
attr_reader :display
|
|
14
|
+
attr_reader :obj
|
|
15
|
+
|
|
16
|
+
def initialize(obj)
|
|
17
|
+
@obj = obj
|
|
18
|
+
# TODO: how to do this with Webview relay? Proxy object to send a message, maybe?
|
|
19
|
+
@display = ::Shoes::DisplayService.display_service.query_display_widget_for(obj.linkable_id)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def method_missing(method, ...)
|
|
23
|
+
if @obj.respond_to?(method)
|
|
24
|
+
self.singleton_class.define_method(method) do |*args, **kwargs, &block|
|
|
25
|
+
@obj.send(method, *args, **kwargs, &block)
|
|
26
|
+
end
|
|
27
|
+
send(method, ...)
|
|
28
|
+
else
|
|
29
|
+
super # raise an exception
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
34
|
+
@obj.respond_to_missing?(method_name, include_private)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# This class defines the CatsCradle DSL. It also holds a "bag of fibers"
|
|
39
|
+
# with promises for when they should next resume.
|
|
40
|
+
class CCInstance
|
|
41
|
+
include Shoes::Log
|
|
42
|
+
include Scarpe::Test::EventedAssertions
|
|
43
|
+
include Scarpe::Test::Helpers
|
|
44
|
+
|
|
45
|
+
def self.instance
|
|
46
|
+
@instance ||= CCInstance.new
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize
|
|
50
|
+
log_init("CatsCradle")
|
|
51
|
+
|
|
52
|
+
@assertion_data = []
|
|
53
|
+
@assertions_passed = 0
|
|
54
|
+
@assertions_failed = []
|
|
55
|
+
|
|
56
|
+
@waiting_fibers = []
|
|
57
|
+
@event_promises = {}
|
|
58
|
+
|
|
59
|
+
@manager_fiber = Fiber.new do
|
|
60
|
+
loop do
|
|
61
|
+
# A fiber can run briefly and then exit. It can run and then block on an API call.
|
|
62
|
+
# These fibers return promises to indicate to CatsCradle when they can run again.
|
|
63
|
+
# A fiber that is no longer #alive? is assumed to be successfully finished.
|
|
64
|
+
@waiting_fibers.each do |fiber_data|
|
|
65
|
+
next unless fiber_data[:promise].fulfilled?
|
|
66
|
+
|
|
67
|
+
@log.debug("Resuming fiber with value #{fiber_data[:promise].returned_value.inspect}")
|
|
68
|
+
result = fiber_data[:fiber].transfer fiber_data[:promise].returned_value
|
|
69
|
+
|
|
70
|
+
# Dead fibers will be removed later, just leave it
|
|
71
|
+
next unless fiber_data[:fiber].alive?
|
|
72
|
+
|
|
73
|
+
case result
|
|
74
|
+
when ::Scarpe::Promise
|
|
75
|
+
fiber_data[:promise] = result
|
|
76
|
+
else
|
|
77
|
+
raise "Unexpected object returned from Fiber#transfer for still-living Fiber! #{result.inspect}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Throw out dead fibers or those that will never wake
|
|
82
|
+
@waiting_fibers.select! do |fiber_data|
|
|
83
|
+
fiber_data[:fiber].alive? && !fiber_data[:promise].rejected?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Done with this iteration
|
|
87
|
+
Fiber.yield
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# If we add "every" events, that's likely to complicate timing and event_promise handling.
|
|
93
|
+
EVENT_TYPES = [:next_heartbeat, :next_redraw]
|
|
94
|
+
|
|
95
|
+
# This needs to be called after the basic display service objects exist
|
|
96
|
+
# and we can find the control interface.
|
|
97
|
+
def event_init
|
|
98
|
+
return if @cc_init_done
|
|
99
|
+
|
|
100
|
+
@cc_init_done = true
|
|
101
|
+
|
|
102
|
+
@control_interface = ::Shoes::DisplayService.display_service.control_interface
|
|
103
|
+
@wrangler = @control_interface.wrangler
|
|
104
|
+
|
|
105
|
+
cc_instance = self # ControlInterface#on_event does an instance eval. We'll reset self with another.
|
|
106
|
+
|
|
107
|
+
@control_interface.on_event(:every_heartbeat) do
|
|
108
|
+
cc_instance.instance_eval do
|
|
109
|
+
p = @event_promises.delete(:next_heartbeat)
|
|
110
|
+
p&.fulfilled!
|
|
111
|
+
|
|
112
|
+
# Give every ready fiber a chance to run once.
|
|
113
|
+
@manager_fiber.resume
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
@control_interface.on_event(:every_redraw) do
|
|
118
|
+
cc_instance.instance_eval do
|
|
119
|
+
p = @event_promises.delete(:next_redraw)
|
|
120
|
+
p&.fulfilled!
|
|
121
|
+
|
|
122
|
+
# Give every ready fiber a chance to run once.
|
|
123
|
+
@manager_fiber.resume
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def event_promise(event)
|
|
129
|
+
@event_promises[event] ||= ::Scarpe::Promise.new
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def on_event(event, &block)
|
|
133
|
+
raise "Unknown event type: #{event.inspect}!" unless EVENT_TYPES.include?(event)
|
|
134
|
+
|
|
135
|
+
f = Fiber.new do
|
|
136
|
+
CCInstance.instance.instance_eval(&block)
|
|
137
|
+
end
|
|
138
|
+
@waiting_fibers << { promise: event_promise(event), fiber: f }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# What to do about TextWidgets? Link, code, em, strong?
|
|
142
|
+
# Also, wait, what's up with span? What *is* that?
|
|
143
|
+
Shoes::Widget.widget_classes.each do |widget_class|
|
|
144
|
+
finder_name = widget_class.dsl_name
|
|
145
|
+
|
|
146
|
+
define_method(finder_name) do |*args|
|
|
147
|
+
app = Shoes::App.instance
|
|
148
|
+
|
|
149
|
+
widgets = app.find_widgets_by(widget_class, *args)
|
|
150
|
+
raise "Found more than one #{finder_name} matching #{args.inspect}!" if widgets.size > 1
|
|
151
|
+
raise "Found no #{finder_name} matching #{args.inspect}!" if widgets.empty?
|
|
152
|
+
|
|
153
|
+
CCProxy.new(widgets[0])
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def proxy_for(shoes_widget)
|
|
158
|
+
CCProxy.new(shoes_widget)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def wait(promise)
|
|
162
|
+
raise("Must supply a promise to wait!") unless promise.is_a?(::Scarpe::Promise)
|
|
163
|
+
|
|
164
|
+
# Wait until this promise is complete before running again
|
|
165
|
+
@manager_fiber.transfer(promise)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# This returns a promise, which can be waited on using wait()
|
|
169
|
+
def fully_updated
|
|
170
|
+
@wrangler.promise_dom_fully_updated
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def dom_html(timeout: 1.0)
|
|
174
|
+
query_js_value("document.getElementById('wrapper-wvroot').innerHTML", timeout:)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def query_js_value(js_code, timeout: 1.0)
|
|
178
|
+
js_promise = @wrangler.eval_js_async(js_code, timeout:)
|
|
179
|
+
|
|
180
|
+
# This promise will return the string, so we can just pass it to #transfer
|
|
181
|
+
@manager_fiber.transfer(js_promise)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def assert(value, msg = nil)
|
|
185
|
+
msg ||= "Assertion #{value ? "succeeded" : "failed"}"
|
|
186
|
+
@assertion_data << [value ? true : false, msg]
|
|
187
|
+
if value
|
|
188
|
+
@assertions_passed += 1
|
|
189
|
+
else
|
|
190
|
+
@assertions_failed << msg
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def assert_equal(expected, actual, msg = nil)
|
|
195
|
+
msg ||= "Expected #{actual.inspect} to equal #{expected.inspect}!"
|
|
196
|
+
assert actual == expected, msg
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def assertion_data_as_a_struct
|
|
200
|
+
{
|
|
201
|
+
still_pending: 0,
|
|
202
|
+
succeeded: @assertions_passed,
|
|
203
|
+
failed: @assertions_failed.size,
|
|
204
|
+
failures: @assertions_failed,
|
|
205
|
+
}
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def test_metadata
|
|
209
|
+
{}
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def test_finished
|
|
213
|
+
if !@assertions_failed.empty?
|
|
214
|
+
return_results(false, "Assertions failed", assertion_data_as_a_struct)
|
|
215
|
+
elsif @assertions_passed > 0
|
|
216
|
+
return_results(true, "All assertions passed", assertion_data_as_a_struct)
|
|
217
|
+
else
|
|
218
|
+
return_results(true, "Test finished successfully")
|
|
219
|
+
end
|
|
220
|
+
::Shoes::DisplayService.dispatch_event("destroy", nil)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# "Cat's Cradle" is a children's game where they interlace string between
|
|
225
|
+
# their fingers to make beautiful complicated shapes. The interlacing
|
|
226
|
+
# of fibers made it a good name for a prototype.
|
|
227
|
+
|
|
228
|
+
# An attempt at an experimental Fiber-based testing system to deal with
|
|
229
|
+
# Shoes, Display and JS all at the same time.
|
|
230
|
+
#
|
|
231
|
+
# In general, we'll use Fiber.transfer to bounce control back and forth
|
|
232
|
+
# between the evented implementations (e.g. waiting for redraw) that
|
|
233
|
+
# need to return control to Webview, and the procedural test flows
|
|
234
|
+
# that look far better if we don't do that explicitly.
|
|
235
|
+
#
|
|
236
|
+
# Ruby Fiber basic docs: https://ruby-doc.org/core-3.0.0/Fiber.html
|
|
237
|
+
#
|
|
238
|
+
# This module is mixed into Shoes::App if we're running CatsCradle-based tests
|
|
239
|
+
module CatsCradle
|
|
240
|
+
def event_init
|
|
241
|
+
@cc_instance = CCInstance.instance
|
|
242
|
+
@cc_instance.event_init
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def on_heartbeat(&block)
|
|
246
|
+
@cc_instance.on_event(:next_heartbeat, &block)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
require "json"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
module Scarpe::Test; end
|
|
8
|
+
|
|
9
|
+
# We need a separate assertion system for the kind of Scarpe
|
|
10
|
+
# testing where we start a subprocess, perform the assertion
|
|
11
|
+
# logic in the subprocess, and then run Minitest in the parent.
|
|
12
|
+
# It's an unusual setup.
|
|
13
|
+
module Scarpe::Test::EventedAssertions
|
|
14
|
+
# Assert that `text` includes `subtext`.
|
|
15
|
+
#
|
|
16
|
+
# @param text [String] the longer text
|
|
17
|
+
# @param subtext [String] the text that is asserted to be included
|
|
18
|
+
# @param msg [String,nil] if supplied, the failure message for the assertion
|
|
19
|
+
# @return [void]
|
|
20
|
+
def assert_include(text, subtext, msg = nil)
|
|
21
|
+
msg ||= "Expected #{text.inspect} to include #{subtext.inspect}"
|
|
22
|
+
assert text.include?(subtext), msg
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Assert that `text` does not include `subtext`.
|
|
26
|
+
#
|
|
27
|
+
# @param text [String] the longer text
|
|
28
|
+
# @param subtext [String] the text that is asserted to not be included
|
|
29
|
+
# @param msg [String,nil] if supplied, the failure message for the assertion
|
|
30
|
+
# @return [void]
|
|
31
|
+
def assert_not_include(text, subtext, msg = nil)
|
|
32
|
+
msg ||= "Expected #{text.inspect} not to include #{subtext.inspect}"
|
|
33
|
+
assert !text.include?(subtext), msg
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Assert that `actual_html` is the same as `expected_tag` with `opts`.
|
|
37
|
+
# This uses Scarpe's HTML tag-based renderer to render the tag and options
|
|
38
|
+
# into text, and valides that the text is the same.
|
|
39
|
+
#
|
|
40
|
+
# @see Scarpe::HTML.render
|
|
41
|
+
#
|
|
42
|
+
# @param actual_html [String] the html to compare to
|
|
43
|
+
# @param expected_tag [String,Symbol] the HTML tag, used to send a method call
|
|
44
|
+
# @param opts keyword options passed to the tag method call
|
|
45
|
+
# @yield block passed to the tag method call.
|
|
46
|
+
# @return [void]
|
|
47
|
+
def assert_html(actual_html, expected_tag, **opts, &block)
|
|
48
|
+
expected_html = Scarpe::HTML.render do |h|
|
|
49
|
+
h.public_send(expected_tag, opts, &block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
assert_equal expected_html, actual_html
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# This does a final return of results. If it gets called
|
|
56
|
+
# multiple times, the test fails because that's not allowed.
|
|
57
|
+
#
|
|
58
|
+
# @param result_bool [Boolean] true if the results are success, false if failure
|
|
59
|
+
# @param msg [String] the message included with the results
|
|
60
|
+
# @param data [Hash] any additional data to pass with the results
|
|
61
|
+
# @return void
|
|
62
|
+
def return_results(result_bool, msg, data = {})
|
|
63
|
+
result_file = ENV["SCARPE_TEST_RESULTS"] || "./scarpe_results.txt"
|
|
64
|
+
|
|
65
|
+
result_structs = [result_bool, msg, data.merge(test_metadata)]
|
|
66
|
+
if File.exist?(result_file)
|
|
67
|
+
results_returned = JSON.parse File.read(result_file)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Multiple different sets of results is bad, even if both are passing.
|
|
71
|
+
if results_returned && results_returned[0..1] != result_structs[0..1]
|
|
72
|
+
# Just raising here doesn't reliably fail the test.
|
|
73
|
+
# See: https://github.com/scarpe-team/scarpe/issues/212
|
|
74
|
+
Shoes::Log.logger("Test Results").error("Writing multi-result failure file to #{result_file.inspect}!")
|
|
75
|
+
|
|
76
|
+
new_res_data = { first_result: results_returned, second_result: result_structs }.merge(test_metadata)
|
|
77
|
+
bad_result = [false, "Returned two sets of results!", new_res_data]
|
|
78
|
+
File.write(result_file, JSON.pretty_generate(bad_result))
|
|
79
|
+
|
|
80
|
+
return
|
|
81
|
+
elsif results_returned
|
|
82
|
+
Shoes::Log.logger("Test Results").warn "Returning identical results twice: #{results_returned.inspect}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
Shoes::Log.logger("Test Results").debug("Writing results file #{result_file.inspect} to disk!")
|
|
86
|
+
File.write(result_file, JSON.pretty_generate(result_structs))
|
|
87
|
+
end
|
|
88
|
+
end
|
data/lib/scarpe/version.rb
CHANGED
data/lib/scarpe/wv/alert.rb
CHANGED
|
@@ -23,8 +23,9 @@ class Scarpe
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
protected
|
|
27
27
|
|
|
28
|
+
# If the whole widget is hidden, the parent style adds display:none
|
|
28
29
|
def overlay_style
|
|
29
30
|
{
|
|
30
31
|
position: "fixed",
|
|
@@ -38,7 +39,7 @@ class Scarpe
|
|
|
38
39
|
display: "flex",
|
|
39
40
|
"align-items": "center",
|
|
40
41
|
"justify-content": "center",
|
|
41
|
-
}
|
|
42
|
+
}.merge(style)
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def modal_style
|
data/lib/scarpe/wv/app.rb
CHANGED
|
@@ -3,15 +3,11 @@
|
|
|
3
3
|
class Scarpe
|
|
4
4
|
# Scarpe::WebviewApp must only be used from the main thread, due to GTK+ limitations.
|
|
5
5
|
class WebviewApp < WebviewWidget
|
|
6
|
-
attr_reader :debug
|
|
7
6
|
attr_reader :control_interface
|
|
8
7
|
|
|
9
8
|
attr_writer :shoes_linkable_id
|
|
10
9
|
|
|
11
10
|
def initialize(properties)
|
|
12
|
-
# Is this a thing? Do we care about this?
|
|
13
|
-
# opts = @control_interface.app_opts_get_override(opts)
|
|
14
|
-
|
|
15
11
|
super
|
|
16
12
|
|
|
17
13
|
# It's possible to provide a Ruby script by setting
|
|
@@ -24,6 +20,7 @@ class Scarpe
|
|
|
24
20
|
# events, specify overrides and so on.
|
|
25
21
|
@control_interface = ControlInterface.new
|
|
26
22
|
if ENV["SCARPE_TEST_CONTROL"]
|
|
23
|
+
require "scarpe/components/unit_test_helpers"
|
|
27
24
|
@control_interface.instance_eval File.read(ENV["SCARPE_TEST_CONTROL"])
|
|
28
25
|
end
|
|
29
26
|
|
|
@@ -31,8 +28,9 @@ class Scarpe
|
|
|
31
28
|
@view = Scarpe::WebWrangler.new title: @title,
|
|
32
29
|
width: @width,
|
|
33
30
|
height: @height,
|
|
34
|
-
resizable: @resizable
|
|
35
|
-
|
|
31
|
+
resizable: @resizable
|
|
32
|
+
|
|
33
|
+
@callbacks = {}
|
|
36
34
|
|
|
37
35
|
# The control interface has to exist to get callbacks like "override Scarpe app opts".
|
|
38
36
|
# But the Scarpe App needs those options to be created. So we can't pass these to
|
|
@@ -50,11 +48,11 @@ class Scarpe
|
|
|
50
48
|
scarpe_app = self
|
|
51
49
|
|
|
52
50
|
@view.init_code("scarpeInit") do
|
|
53
|
-
|
|
51
|
+
request_redraw!
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
@view.bind("scarpeHandler") do |*args|
|
|
57
|
-
|
|
55
|
+
handle_callback(*args)
|
|
58
56
|
end
|
|
59
57
|
|
|
60
58
|
@view.bind("scarpeExit") do
|
|
@@ -81,5 +79,29 @@ class Scarpe
|
|
|
81
79
|
@view = nil
|
|
82
80
|
end
|
|
83
81
|
end
|
|
82
|
+
|
|
83
|
+
# All JS callbacks to Scarpe widgets are dispatched
|
|
84
|
+
# via this handler
|
|
85
|
+
def handle_callback(name, *args)
|
|
86
|
+
@callbacks[name].call(*args)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Bind a Scarpe callback name; see handle_callback above.
|
|
90
|
+
# See Scarpe::Widget for how the naming is set up
|
|
91
|
+
def bind(name, &block)
|
|
92
|
+
@callbacks[name] = block
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Request a full redraw if Webview is running. Otherwise
|
|
96
|
+
# this is a no-op.
|
|
97
|
+
#
|
|
98
|
+
# @return [void]
|
|
99
|
+
def request_redraw!
|
|
100
|
+
wrangler = WebviewDisplayService.instance.wrangler
|
|
101
|
+
if wrangler.is_running
|
|
102
|
+
wrangler.replace(@document_root.to_html)
|
|
103
|
+
end
|
|
104
|
+
nil
|
|
105
|
+
end
|
|
84
106
|
end
|
|
85
107
|
end
|
data/lib/scarpe/wv/arc.rb
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "shape_helper"
|
|
4
|
-
|
|
5
3
|
class Scarpe
|
|
6
4
|
class WebviewArc < Scarpe::WebviewWidget
|
|
7
|
-
include ShapeHelper
|
|
8
5
|
def initialize(properties)
|
|
9
6
|
super(properties)
|
|
10
7
|
end
|
|
@@ -20,17 +17,19 @@ class Scarpe
|
|
|
20
17
|
end
|
|
21
18
|
end
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
protected
|
|
24
21
|
|
|
25
22
|
def style
|
|
26
|
-
{
|
|
23
|
+
super.merge({
|
|
27
24
|
left: "#{@left}px",
|
|
28
25
|
top: "#{@top}px",
|
|
29
26
|
width: "#{@width}px",
|
|
30
27
|
height: "#{@height}px",
|
|
31
|
-
}
|
|
28
|
+
})
|
|
32
29
|
end
|
|
33
30
|
|
|
31
|
+
private
|
|
32
|
+
|
|
34
33
|
def arc_path
|
|
35
34
|
center_x = @width / 2
|
|
36
35
|
center_y = @height / 2
|
data/lib/scarpe/wv/background.rb
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "scarpe/components/base64"
|
|
4
|
+
|
|
3
5
|
class Scarpe
|
|
4
6
|
module WebviewBackground
|
|
7
|
+
include Components::Base64
|
|
8
|
+
|
|
5
9
|
def style
|
|
6
10
|
styles = (super if defined?(super)) || {}
|
|
7
11
|
return styles unless @background_color
|
|
8
12
|
|
|
9
|
-
color =
|
|
13
|
+
color = case @background_color
|
|
14
|
+
when Array
|
|
15
|
+
"rgba(#{@background_color.join(", ")})"
|
|
16
|
+
when Range
|
|
10
17
|
"linear-gradient(45deg, #{@background_color.first}, #{@background_color.last})"
|
|
18
|
+
when ->(value) { File.exist?(value) }
|
|
19
|
+
"url(data:image/png;base64,#{encode_file_to_base64(@background_color)})"
|
|
11
20
|
else
|
|
12
21
|
@background_color
|
|
13
22
|
end
|
data/lib/scarpe/wv/border.rb
CHANGED
|
@@ -6,12 +6,14 @@ class Scarpe
|
|
|
6
6
|
styles = (super if defined?(super)) || {}
|
|
7
7
|
return styles unless @border_color
|
|
8
8
|
|
|
9
|
-
border_color =
|
|
10
|
-
|
|
9
|
+
border_color = case @border_color
|
|
10
|
+
when Range
|
|
11
|
+
{ "border-image": "linear-gradient(45deg, #{@border_color.first}, #{@border_color.last})" }
|
|
12
|
+
when Array
|
|
13
|
+
{ "border-color": "rgba(#{@border_color.join(", ")})" }
|
|
11
14
|
else
|
|
12
15
|
{ "border-color": @border_color }
|
|
13
16
|
end
|
|
14
|
-
|
|
15
17
|
styles.merge(
|
|
16
18
|
"border-style": "solid",
|
|
17
19
|
"border-width": "#{@options[:strokewidth] || 1}px",
|
data/lib/scarpe/wv/button.rb
CHANGED
|
@@ -20,24 +20,26 @@ class Scarpe
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
protected
|
|
24
24
|
|
|
25
25
|
def style
|
|
26
|
-
styles =
|
|
27
|
-
|
|
28
|
-
styles["
|
|
29
|
-
styles["padding-
|
|
26
|
+
styles = super
|
|
27
|
+
|
|
28
|
+
styles[:"background-color"] = @color
|
|
29
|
+
styles[:"padding-top"] = @padding_top
|
|
30
|
+
styles[:"padding-bottom"] = @padding_bottom
|
|
30
31
|
styles[:color] = @text_color
|
|
31
32
|
styles[:width] = Dimensions.length(@width) if @width
|
|
32
33
|
styles[:height] = Dimensions.length(@height) if @height
|
|
33
|
-
styles["font-size"] = @font_size
|
|
34
|
+
styles[:"font-size"] = @font_size
|
|
34
35
|
|
|
35
36
|
styles[:top] = Dimensions.length(@top) if @top
|
|
36
37
|
styles[:left] = Dimensions.length(@left) if @left
|
|
37
38
|
styles[:position] = "absolute" if @top || @left
|
|
38
|
-
styles["font-size"] = Dimensions.length(font_size) if @size
|
|
39
|
-
styles["font-family"] = @font if @font
|
|
40
|
-
styles[
|
|
39
|
+
styles[:"font-size"] = Dimensions.length(font_size) if @size
|
|
40
|
+
styles[:"font-family"] = @font if @font
|
|
41
|
+
styles[:color] = rgb_to_hex(@stroke) if @stroke
|
|
42
|
+
|
|
41
43
|
styles
|
|
42
44
|
end
|
|
43
45
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Scarpe
|
|
4
|
+
class WebviewCheck < Scarpe::WebviewWidget
|
|
5
|
+
attr_reader :text
|
|
6
|
+
|
|
7
|
+
def initialize(properties)
|
|
8
|
+
super
|
|
9
|
+
|
|
10
|
+
bind("click") do
|
|
11
|
+
send_self_event(event_name: "click", target: shoes_linkable_id)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def properties_changed(changes)
|
|
16
|
+
checked = changes.delete("checked")
|
|
17
|
+
|
|
18
|
+
html_element.toggle_input_button(checked)
|
|
19
|
+
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def element
|
|
24
|
+
HTML.render do |h|
|
|
25
|
+
h.input(type: :checkbox, id: html_id, onclick: handler_js_code("click"), value: "hmm #{text}", checked: @checked, style:)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# The ControlInterface is used for testing. It's a way to register interest
|
|
4
|
-
# in important events like redraw, init and shutdown, and to
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
4
|
+
# in important events like redraw, init and shutdown, and to configure a
|
|
5
|
+
# Shoes app for testing. Note that no part of the Scarpe framework should
|
|
6
|
+
# ever *depend* on ControlInterface. It's for testing, not normal operation.
|
|
7
|
+
# If no ControlInterface were ever created or called, Scarpe apps should run
|
|
8
|
+
# fine with no modifications.
|
|
9
9
|
#
|
|
10
10
|
# And if you depend on this from the framework, I'll add a check-mode that
|
|
11
11
|
# never dispatches any events to any handlers. Do NOT test me on this.
|
|
12
12
|
|
|
13
13
|
class Scarpe
|
|
14
14
|
class ControlInterface
|
|
15
|
-
include
|
|
15
|
+
include Shoes::Log
|
|
16
16
|
|
|
17
17
|
SUBSCRIBE_EVENTS = [:init, :shutdown, :next_redraw, :every_redraw, :next_heartbeat, :every_heartbeat]
|
|
18
18
|
DISPATCH_EVENTS = [:init, :shutdown, :redraw, :heartbeat]
|
|
@@ -33,7 +33,7 @@ class Scarpe
|
|
|
33
33
|
"<#ControlInterface>"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
# This should get called once, from
|
|
36
|
+
# This should get called once, from Shoes::App
|
|
37
37
|
def set_system_components(app:, doc_root:, wrangler:)
|
|
38
38
|
unless app && wrangler
|
|
39
39
|
@log.error("False app passed to set_system_components!") unless app
|
|
@@ -79,23 +79,17 @@ class Scarpe
|
|
|
79
79
|
# The control interface has overrides for certain settings. If the override has been specified,
|
|
80
80
|
# those settings will be overridden.
|
|
81
81
|
|
|
82
|
-
# Override the Shoes app opts like "debug:" and "die_after:" with new ones.
|
|
83
|
-
def override_app_opts(new_opts)
|
|
84
|
-
@new_app_opts = new_opts
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Called by Scarpe::App to get the override options
|
|
88
|
-
def app_opts_get_override(opts)
|
|
89
|
-
@new_app_opts || opts
|
|
90
|
-
end
|
|
91
|
-
|
|
92
82
|
# On recognised events, this sets a handler for that event
|
|
93
83
|
def on_event(event, &block)
|
|
94
84
|
unless SUBSCRIBE_EVENTS.include?(event)
|
|
95
85
|
raise "Illegal subscribe to event #{event.inspect}! Valid values are: #{SUBSCRIBE_EVENTS.inspect}"
|
|
96
86
|
end
|
|
97
87
|
|
|
98
|
-
@
|
|
88
|
+
@unsub_id ||= 0
|
|
89
|
+
@unsub_id += 1
|
|
90
|
+
|
|
91
|
+
@event_handlers[event] << { handler: block, unsub: @unsub_id }
|
|
92
|
+
@unsub_id
|
|
99
93
|
end
|
|
100
94
|
|
|
101
95
|
# Send out the specified event
|
|
@@ -145,8 +139,8 @@ class Scarpe
|
|
|
145
139
|
private
|
|
146
140
|
|
|
147
141
|
def dumb_dispatch_event(event, *args, **keywords)
|
|
148
|
-
@event_handlers[event].each do |
|
|
149
|
-
instance_eval(*args, **keywords, &handler)
|
|
142
|
+
@event_handlers[event].each do |data|
|
|
143
|
+
instance_eval(*args, **keywords, &data[:handler])
|
|
150
144
|
end
|
|
151
145
|
end
|
|
152
146
|
end
|