scarpe 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +16 -2
- data/Gemfile.lock +7 -3
- data/README.md +24 -8
- data/Rakefile +1 -1
- data/examples/animate.rb +20 -0
- data/examples/arrow.rb +10 -0
- data/examples/btn_tooltip.rb +7 -0
- data/examples/button_style_changed.rb +7 -0
- data/examples/button_styles_default.rb +6 -0
- data/examples/gen.rb +8 -8
- data/examples/highlander.rb +3 -3
- data/examples/legacy/README.md +6 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/shoes-notes.rb +1 -1
- data/examples/legacy/not_checked/simple/anim-shapes.rb +1 -1
- data/examples/legacy/not_checked/speedometer_app.rb +55 -0
- data/examples/legacy/working/simple/image-icon.rb +3 -0
- data/examples/legacy/{not_checked → working}/simple/image.rb +1 -1
- data/examples/list_box_choose.rb +17 -0
- data/examples/local_assets/local_file_server.rb +82 -0
- data/examples/local_assets/sample.gif +0 -0
- data/examples/local_assets/sample.mp4 +0 -0
- data/examples/local_fonts.rb +2 -2
- data/examples/local_images.rb +2 -3
- data/examples/para/para_text.rb +14 -0
- data/examples/progress.rb +31 -0
- data/examples/radio/radio_groups.rb +2 -2
- data/examples/rect.rb +4 -0
- data/examples/rotate_shapes.rb +17 -0
- data/examples/simpler-menu.rb +21 -0
- data/exe/scarpe +2 -1
- data/lacci/Gemfile +2 -0
- data/lacci/Gemfile.lock +8 -1
- data/lacci/lacci.gemspec +1 -1
- data/lacci/lib/lacci/scarpe_cli.rb +2 -1
- data/lacci/lib/lacci/scarpe_core.rb +2 -1
- data/lacci/lib/lacci/version.rb +1 -1
- data/lacci/lib/scarpe/niente/app.rb +23 -0
- data/lacci/lib/scarpe/niente/display_service.rb +62 -0
- data/lacci/lib/scarpe/niente/drawable.rb +57 -0
- data/lacci/lib/scarpe/niente/logger.rb +29 -0
- data/lacci/lib/scarpe/niente/shoes_spec.rb +87 -0
- data/lacci/lib/scarpe/niente.rb +20 -0
- data/lacci/lib/shoes/app.rb +88 -43
- data/lacci/lib/shoes/background.rb +2 -2
- data/lacci/lib/shoes/border.rb +2 -2
- data/lacci/lib/shoes/builtins.rb +63 -0
- data/lacci/lib/shoes/changelog.rb +52 -0
- data/lacci/lib/shoes/colors.rb +3 -1
- data/lacci/lib/shoes/constants.rb +19 -1
- data/lacci/lib/shoes/display_service.rb +39 -16
- data/lacci/lib/shoes/download.rb +2 -2
- data/lacci/lib/shoes/drawable.rb +380 -0
- data/lacci/lib/shoes/drawables/arc.rb +49 -0
- data/lacci/lib/shoes/drawables/arrow.rb +41 -0
- data/lacci/lib/shoes/drawables/button.rb +73 -0
- data/lacci/lib/shoes/{widgets → drawables}/check.rb +5 -4
- data/lacci/lib/shoes/{widgets → drawables}/document_root.rb +3 -3
- data/lacci/lib/shoes/{widgets → drawables}/edit_box.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/edit_line.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/flow.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/image.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/line.rb +7 -5
- data/lacci/lib/shoes/drawables/link.rb +34 -0
- data/lacci/lib/shoes/drawables/list_box.rb +56 -0
- data/lacci/lib/shoes/drawables/para.rb +118 -0
- data/lacci/lib/shoes/drawables/progress.rb +14 -0
- data/lacci/lib/shoes/drawables/radio.rb +33 -0
- data/lacci/lib/shoes/drawables/rect.rb +17 -0
- data/lacci/lib/shoes/{widgets → drawables}/shape.rb +6 -7
- data/lacci/lib/shoes/{widgets → drawables}/slot.rb +32 -20
- data/lacci/lib/shoes/{widgets → drawables}/span.rb +8 -7
- data/lacci/lib/shoes/{widgets → drawables}/stack.rb +6 -4
- data/lacci/lib/shoes/drawables/star.rb +50 -0
- data/lacci/lib/shoes/drawables/subscription_item.rb +93 -0
- data/lacci/lib/shoes/drawables/text_drawable.rb +63 -0
- data/lacci/lib/shoes/drawables/video.rb +16 -0
- data/lacci/lib/shoes/drawables/widget.rb +69 -0
- data/lacci/lib/shoes/drawables.rb +31 -0
- data/lacci/lib/shoes/errors.rb +28 -0
- data/lacci/lib/shoes/log.rb +2 -2
- data/lacci/lib/shoes/ruby_extensions.rb +15 -0
- data/lacci/lib/shoes/spacing.rb +2 -2
- data/lacci/lib/shoes-spec.rb +93 -0
- data/lacci/lib/shoes.rb +27 -7
- data/lacci/test/test_helper.rb +54 -0
- data/lacci/test/test_lacci.rb +12 -3
- data/lacci/test/test_shoes_errors.rb +49 -0
- data/lib/scarpe/cats_cradle.rb +81 -59
- data/lib/scarpe/errors.rb +77 -0
- data/lib/scarpe/evented_assertions.rb +50 -17
- data/lib/scarpe/shoes_spec.rb +181 -0
- data/lib/scarpe/version.rb +2 -2
- data/lib/scarpe/wv/app.rb +20 -20
- data/lib/scarpe/wv/arc.rb +4 -47
- data/lib/scarpe/wv/arrow.rb +9 -0
- data/lib/scarpe/wv/button.rb +7 -35
- data/lib/scarpe/wv/check.rb +3 -5
- data/lib/scarpe/wv/control_interface.rb +18 -20
- data/lib/scarpe/wv/document_root.rb +81 -4
- data/lib/scarpe/wv/{widget.rb → drawable.rb} +66 -43
- data/lib/scarpe/wv/edit_box.rb +4 -17
- data/lib/scarpe/wv/edit_line.rb +4 -18
- data/lib/scarpe/wv/flow.rb +2 -18
- data/lib/scarpe/wv/image.rb +8 -28
- data/lib/scarpe/wv/line.rb +3 -25
- data/lib/scarpe/wv/link.rb +3 -16
- data/lib/scarpe/wv/list_box.rb +6 -29
- data/lib/scarpe/wv/para.rb +11 -30
- data/lib/scarpe/wv/progress.rb +19 -0
- data/lib/scarpe/wv/radio.rb +9 -10
- data/lib/scarpe/wv/rect.rb +13 -0
- data/lib/scarpe/wv/shape.rb +3 -8
- data/lib/scarpe/wv/slot.rb +8 -25
- data/lib/scarpe/wv/span.rb +3 -27
- data/lib/scarpe/wv/stack.rb +2 -18
- data/lib/scarpe/wv/star.rb +3 -53
- data/lib/scarpe/wv/subscription_item.rb +38 -4
- data/lib/scarpe/wv/text_drawable.rb +32 -0
- data/lib/scarpe/wv/video.rb +15 -15
- data/lib/scarpe/wv/web_wrangler.rb +299 -329
- data/lib/scarpe/wv/webview_local_display.rb +48 -33
- data/lib/scarpe/wv/webview_relay_display.rb +12 -12
- data/lib/scarpe/wv/webview_relay_util.rb +7 -10
- data/lib/scarpe/wv/wv_display_worker.rb +2 -2
- data/lib/scarpe/wv.rb +45 -12
- data/lib/scarpe/wv_local.rb +1 -1
- data/lib/scarpe/wv_relay.rb +1 -1
- data/lib/scarpe.rb +1 -0
- data/logger/debug_web_wrangler.json +1 -1
- data/logger/scarpe_wv_test.json +1 -1
- data/scarpe-components/Gemfile.lock +86 -0
- data/scarpe-components/lib/scarpe/components/base64.rb +3 -7
- data/scarpe-components/lib/scarpe/components/calzini/alert.rb +49 -0
- data/scarpe-components/lib/scarpe/components/calzini/art_widgets.rb +203 -0
- data/scarpe-components/lib/scarpe/components/calzini/button.rb +39 -0
- data/scarpe-components/lib/scarpe/components/calzini/misc.rb +146 -0
- data/scarpe-components/lib/scarpe/components/calzini/para.rb +35 -0
- data/scarpe-components/lib/scarpe/components/calzini/slots.rb +155 -0
- data/scarpe-components/lib/scarpe/components/calzini/text_widgets.rb +65 -0
- data/scarpe-components/lib/scarpe/components/calzini.rb +149 -0
- data/scarpe-components/lib/scarpe/components/errors.rb +20 -0
- data/scarpe-components/lib/scarpe/components/file_helpers.rb +1 -0
- data/scarpe-components/lib/scarpe/components/html.rb +131 -0
- data/scarpe-components/lib/scarpe/components/minitest_export_reporter.rb +75 -0
- data/scarpe-components/lib/scarpe/components/minitest_import_runnable.rb +98 -0
- data/scarpe-components/lib/scarpe/components/minitest_result.rb +86 -0
- data/scarpe-components/lib/scarpe/components/modular_logger.rb +5 -5
- data/scarpe-components/lib/scarpe/components/print_logger.rb +9 -5
- data/scarpe-components/lib/scarpe/components/promises.rb +14 -14
- data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +36 -17
- data/scarpe-components/lib/scarpe/components/string_helpers.rb +10 -0
- data/scarpe-components/lib/scarpe/components/tiranti.rb +225 -0
- data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +45 -5
- data/scarpe-components/lib/scarpe/components/version.rb +2 -2
- data/scarpe-components/test/calzini/test_calzini_alert.rb +30 -0
- data/scarpe-components/test/calzini/test_calzini_art_drawables.rb +105 -0
- data/scarpe-components/test/calzini/test_calzini_button.rb +52 -0
- data/scarpe-components/test/calzini/test_calzini_misc.rb +115 -0
- data/scarpe-components/test/calzini/test_calzini_para.rb +37 -0
- data/scarpe-components/test/calzini/test_calzini_slots.rb +130 -0
- data/scarpe-components/test/calzini/test_calzini_text_drawables.rb +41 -0
- data/scarpe-components/test/mtr_data/exception.json +1 -0
- data/scarpe-components/test/mtr_data/fail_with_message.json +1 -0
- data/scarpe-components/test/mtr_data/skipped_no_message.json +1 -0
- data/scarpe-components/test/mtr_data/skipped_w_msg.json +1 -0
- data/scarpe-components/test/mtr_data/succeed_2_asserts.json +1 -0
- data/scarpe-components/test/test_dimensions.rb +26 -0
- data/scarpe-components/test/test_helper.rb +20 -0
- data/scarpe-components/test/test_html.rb +65 -0
- data/scarpe-components/test/test_minitest_result.rb +61 -0
- data/scarpe-components/test/test_promises.rb +5 -4
- data/scarpe-components/test/test_segmented_app_files.rb +8 -6
- data/scarpegen.rb +14 -14
- data/sig/scarpe.rbs +1 -1
- data/templates/basic_class_template.erb +13 -14
- data/templates/class_template_with_event_bind.erb +4 -4
- data/templates/class_template_with_shapes.erb +8 -17
- data/templates/example_template.erb +1 -1
- data/templates/module_template.erb +4 -4
- data/templates/webview_template.erb +3 -2
- metadata +113 -55
- data/examples/legacy/not_checked/shoes-contrib/elements/image-icon.rb +0 -3
- data/lacci/lib/shoes/widget.rb +0 -218
- data/lacci/lib/shoes/widgets/alert.rb +0 -19
- data/lacci/lib/shoes/widgets/arc.rb +0 -51
- data/lacci/lib/shoes/widgets/button.rb +0 -35
- data/lacci/lib/shoes/widgets/font.rb +0 -14
- data/lacci/lib/shoes/widgets/link.rb +0 -25
- data/lacci/lib/shoes/widgets/list_box.rb +0 -25
- data/lacci/lib/shoes/widgets/para.rb +0 -68
- data/lacci/lib/shoes/widgets/radio.rb +0 -35
- data/lacci/lib/shoes/widgets/star.rb +0 -44
- data/lacci/lib/shoes/widgets/subscription_item.rb +0 -60
- data/lacci/lib/shoes/widgets/text_widget.rb +0 -51
- data/lacci/lib/shoes/widgets/video.rb +0 -15
- data/lacci/lib/shoes/widgets.rb +0 -29
- data/lib/scarpe/wv/alert.rb +0 -66
- data/lib/scarpe/wv/background.rb +0 -27
- data/lib/scarpe/wv/border.rb +0 -24
- data/lib/scarpe/wv/control_interface_test.rb +0 -238
- data/lib/scarpe/wv/dimensions.rb +0 -22
- data/lib/scarpe/wv/font.rb +0 -36
- data/lib/scarpe/wv/html.rb +0 -108
- data/lib/scarpe/wv/spacing.rb +0 -41
- data/lib/scarpe/wv/text_widget.rb +0 -30
- /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/definr.rb +0 -0
- /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/funnies.rb +0 -0
- /data/examples/legacy/not_checked/shoes-contrib/{elements → basic}/list_box-select-class.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/basic-edit-box.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/basic-fps.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/border-cat.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/check-mate.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/manipulation → working/simple}/clear-slot.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/clock.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/gradient-shoes.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/list_box-shape-report.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/list_box.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/phat-button.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib → working}/simple/simple-calc.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/position → working/simple}/stack-width.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/width-introspec.rb +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "html"
|
|
4
|
+
require_relative "base64"
|
|
5
|
+
require_relative "errors"
|
|
6
|
+
|
|
7
|
+
# Require all drawable rendering code under calzini directory
|
|
8
|
+
Dir.glob("calzini/*.rb", base: __dir__) do |drawable|
|
|
9
|
+
require_relative drawable
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# The Calzini module expects to be included by a class defining
|
|
13
|
+
# the following methods:
|
|
14
|
+
#
|
|
15
|
+
# * html_id - the HTML ID for the specific rendered DOM object
|
|
16
|
+
# * handler_js_code(event_name) - the JS handler code for this DOM object and event name
|
|
17
|
+
# * (optional) shoes_styles - the Shoes styles for this object, unless overridden in render()
|
|
18
|
+
module Scarpe::Components::Calzini
|
|
19
|
+
extend self
|
|
20
|
+
|
|
21
|
+
HTML = Scarpe::Components::HTML
|
|
22
|
+
include Scarpe::Components::Base64
|
|
23
|
+
|
|
24
|
+
SIZES = {
|
|
25
|
+
inscription: 10,
|
|
26
|
+
ins: 10,
|
|
27
|
+
para: 12,
|
|
28
|
+
caption: 14,
|
|
29
|
+
tagline: 18,
|
|
30
|
+
subtitle: 26,
|
|
31
|
+
title: 34,
|
|
32
|
+
banner: 48,
|
|
33
|
+
}.freeze
|
|
34
|
+
private_constant :SIZES
|
|
35
|
+
|
|
36
|
+
# Render the Shoes drawable of type `drawable_name` with
|
|
37
|
+
# the given properties to HTML and return it. If the
|
|
38
|
+
# drawable type takes a block (e.g. Stack or Flow) then
|
|
39
|
+
# the block will be properly rendered.
|
|
40
|
+
#
|
|
41
|
+
# @param drawable_name [String] the drawable name like "alert", "button" or "rect"
|
|
42
|
+
# @param properties [Hash] a drawable-specific hash of property names to values
|
|
43
|
+
# @block the block which, when called, will return the contents for drawable types with contents
|
|
44
|
+
# @return [String] the rendered HTML
|
|
45
|
+
def render(drawable_name, properties = shoes_styles, &block)
|
|
46
|
+
send("#{drawable_name}_element", properties, &block)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Return HTML for an empty page element, to be filled with HTML
|
|
50
|
+
# renderings of the DOM tree.
|
|
51
|
+
#
|
|
52
|
+
# The wrapper-wvroot element is where Scarpe will fill in the
|
|
53
|
+
# DOM element.
|
|
54
|
+
#
|
|
55
|
+
# @return [String] the rendered HTML for the empty page object.
|
|
56
|
+
def empty_page_element
|
|
57
|
+
<<~HTML
|
|
58
|
+
<html>
|
|
59
|
+
<head id='head-wvroot'>
|
|
60
|
+
<style id='style-wvroot'>
|
|
61
|
+
/** Style resets **/
|
|
62
|
+
body {
|
|
63
|
+
font-family: arial, Helvetica, sans-serif;
|
|
64
|
+
margin: 0;
|
|
65
|
+
height: 100%;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
}
|
|
68
|
+
p {
|
|
69
|
+
margin: 0;
|
|
70
|
+
}
|
|
71
|
+
</style>
|
|
72
|
+
</head>
|
|
73
|
+
<body id='body-wvroot'>
|
|
74
|
+
<div id='wrapper-wvroot'></div>
|
|
75
|
+
</body>
|
|
76
|
+
</html>
|
|
77
|
+
HTML
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def text_size(sz)
|
|
81
|
+
case sz
|
|
82
|
+
when Numeric
|
|
83
|
+
sz
|
|
84
|
+
when Symbol
|
|
85
|
+
SIZES[sz]
|
|
86
|
+
when String
|
|
87
|
+
SIZES[sz.to_sym] || sz.to_i
|
|
88
|
+
else
|
|
89
|
+
raise "Unexpected text size object: #{sz.inspect}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def dimensions_length(value)
|
|
94
|
+
case value
|
|
95
|
+
when Integer
|
|
96
|
+
if value < 0
|
|
97
|
+
"calc(100% - #{value.abs}px)"
|
|
98
|
+
else
|
|
99
|
+
"#{value}px"
|
|
100
|
+
end
|
|
101
|
+
when Float
|
|
102
|
+
"#{value * 100}%"
|
|
103
|
+
else
|
|
104
|
+
value
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def drawable_style(props)
|
|
109
|
+
styles = {}
|
|
110
|
+
if props["hidden"]
|
|
111
|
+
styles[:display] = "none"
|
|
112
|
+
end
|
|
113
|
+
styles
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Convert an [r, g, b, a] array to an HTML hex color code
|
|
117
|
+
# Arrays support alpha. HTML hex does not. So premultiply.
|
|
118
|
+
def rgb_to_hex(color)
|
|
119
|
+
return color if color.nil?
|
|
120
|
+
|
|
121
|
+
r, g, b, a = *color
|
|
122
|
+
if r.is_a?(Float)
|
|
123
|
+
a ||= 1.0
|
|
124
|
+
r_float = r * a
|
|
125
|
+
g_float = g * a
|
|
126
|
+
b_float = b * a
|
|
127
|
+
else
|
|
128
|
+
a ||= 255
|
|
129
|
+
a_float = (a / 255.0)
|
|
130
|
+
r_float = (r.to_f / 255.0) * a_float
|
|
131
|
+
g_float = (g.to_f / 255.0) * a_float
|
|
132
|
+
b_float = (b.to_f / 255.0) * a_float
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
r_int = (r_float * 255.0).to_i.clamp(0, 255)
|
|
136
|
+
g_int = (g_float * 255.0).to_i.clamp(0, 255)
|
|
137
|
+
b_int = (b_float * 255.0).to_i.clamp(0, 255)
|
|
138
|
+
|
|
139
|
+
"#%0.2X%0.2X%0.2X" % [r_int, g_int, b_int]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def degrees_to_radians(degrees)
|
|
143
|
+
degrees * Math::PI / 180
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def radians_to_degrees(radians)
|
|
147
|
+
radians * (180.0 / Math::PI)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Also defined in scarpe_core
|
|
4
|
+
class Scarpe::Error < StandardError; end
|
|
5
|
+
|
|
6
|
+
module Scarpe
|
|
7
|
+
class InternalError < Scarpe::Error; end
|
|
8
|
+
|
|
9
|
+
class FileContentError < Scarpe::Error; end
|
|
10
|
+
|
|
11
|
+
class NoOperationError < Scarpe::Error; end
|
|
12
|
+
|
|
13
|
+
class DuplicateFileError < Scarpe::Error; end
|
|
14
|
+
|
|
15
|
+
class NoSuchFile < Scarpe::Error; end
|
|
16
|
+
|
|
17
|
+
class MustOverrideMethod < Scarpe::Error; end
|
|
18
|
+
|
|
19
|
+
class InvalidHTMLTag < Scarpe::Error; end
|
|
20
|
+
end
|
|
@@ -4,6 +4,7 @@ require "tempfile"
|
|
|
4
4
|
|
|
5
5
|
# These can be used for unit tests, but also more generally.
|
|
6
6
|
|
|
7
|
+
module Scarpe; module Components; end; end
|
|
7
8
|
module Scarpe::Components::FileHelpers
|
|
8
9
|
# Create a temporary file with the given prefix and contents.
|
|
9
10
|
# Execute the block of code with it in place. Make sure
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Scarpe::Components::HTML
|
|
4
|
+
CONTENT_TAGS = [
|
|
5
|
+
:div,
|
|
6
|
+
:p,
|
|
7
|
+
:button,
|
|
8
|
+
:ul,
|
|
9
|
+
:li,
|
|
10
|
+
:textarea,
|
|
11
|
+
:a,
|
|
12
|
+
:video,
|
|
13
|
+
:strong,
|
|
14
|
+
:style,
|
|
15
|
+
:progress,
|
|
16
|
+
:em,
|
|
17
|
+
:code,
|
|
18
|
+
:defs,
|
|
19
|
+
:marker,
|
|
20
|
+
:u,
|
|
21
|
+
:line,
|
|
22
|
+
:span,
|
|
23
|
+
:svg,
|
|
24
|
+
:h1,
|
|
25
|
+
:h2,
|
|
26
|
+
:h3,
|
|
27
|
+
:h4,
|
|
28
|
+
:h5,
|
|
29
|
+
].freeze
|
|
30
|
+
VOID_TAGS = [:input, :img, :polygon, :source, :link, :path, :rect].freeze
|
|
31
|
+
|
|
32
|
+
TAGS = (CONTENT_TAGS + VOID_TAGS).freeze
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
def render(&block)
|
|
36
|
+
new(&block).value
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(&block)
|
|
41
|
+
@buffer = ""
|
|
42
|
+
block.call(self)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def value
|
|
46
|
+
@buffer
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def respond_to_missing?(name, include_all = false)
|
|
50
|
+
TAGS.include?(name) || super(name, include_all)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def p(*args, &block)
|
|
54
|
+
method_missing(:p, *args, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def option(**attrs, &block)
|
|
58
|
+
tag(:option, **attrs, &block)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def tag(name, **attrs, &block)
|
|
62
|
+
if VOID_TAGS.include?(name)
|
|
63
|
+
raise Shoes::Errors::InvalidAttributeValueError, "void tag #{name} cannot have content" if block_given?
|
|
64
|
+
|
|
65
|
+
@buffer += "<#{name}#{render_attributes(attrs)} />"
|
|
66
|
+
else
|
|
67
|
+
@buffer += "<#{name}#{render_attributes(attrs)}>"
|
|
68
|
+
|
|
69
|
+
if block_given?
|
|
70
|
+
result = block.call(self)
|
|
71
|
+
else
|
|
72
|
+
result = attrs[:content]
|
|
73
|
+
@buffer += result if result.is_a?(String)
|
|
74
|
+
end
|
|
75
|
+
@buffer += result if result.is_a?(String)
|
|
76
|
+
|
|
77
|
+
@buffer += "</#{name}>"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def select(**attrs, &block)
|
|
84
|
+
tag(:select, **attrs, &block)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def method_missing(name, *args, &block)
|
|
88
|
+
raise Scarpe::InvalidHTMLTag, "no method #{name} for #{self.class.name}" unless TAGS.include?(name)
|
|
89
|
+
|
|
90
|
+
if VOID_TAGS.include?(name)
|
|
91
|
+
raise Shoes::Errors::InvalidAttributeValueError, "void tag #{name} cannot have content" if block_given?
|
|
92
|
+
|
|
93
|
+
@buffer += "<#{name}#{render_attributes(*args)} />"
|
|
94
|
+
else
|
|
95
|
+
@buffer += "<#{name}#{render_attributes(*args)}>"
|
|
96
|
+
|
|
97
|
+
if block_given?
|
|
98
|
+
result = block.call(self)
|
|
99
|
+
else
|
|
100
|
+
result = args.first
|
|
101
|
+
@buffer += result if result.is_a?(String)
|
|
102
|
+
end
|
|
103
|
+
@buffer += result if result.is_a?(String)
|
|
104
|
+
|
|
105
|
+
@buffer += "</#{name}>"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def render_attributes(attributes = {})
|
|
114
|
+
return "" if attributes.empty?
|
|
115
|
+
|
|
116
|
+
attributes[:style] = render_style(attributes[:style]) if attributes[:style]
|
|
117
|
+
attributes.compact!
|
|
118
|
+
|
|
119
|
+
return if attributes.empty?
|
|
120
|
+
|
|
121
|
+
result = attributes.map { |k, v| "#{k}=\"#{v}\"" }.join(" ")
|
|
122
|
+
" #{result}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def render_style(style)
|
|
126
|
+
return style unless style.is_a?(Hash)
|
|
127
|
+
return if style.empty?
|
|
128
|
+
|
|
129
|
+
style.map { |k, v| "#{k}:#{v}" }.join(";")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Have to require this to get DefaultReporter and the Minitest::Reporters namespace.
|
|
4
|
+
ENV["MINITEST_REPORTER"] = "ShoesExportReporter"
|
|
5
|
+
require "minitest/reporters"
|
|
6
|
+
require "json"
|
|
7
|
+
require "json/add/exception"
|
|
8
|
+
|
|
9
|
+
module Minitest
|
|
10
|
+
module Reporters
|
|
11
|
+
# To use this Scarpe component, you'll need minitest-reporters in your Gemfile,
|
|
12
|
+
# probably in the "test" group. You'll need to require and activate ShoesExportReporter
|
|
13
|
+
# to register it as Minitest's reporter:
|
|
14
|
+
#
|
|
15
|
+
# require "scarpe/components/minitest_export_reporter"
|
|
16
|
+
# Minitest::Reporters::ShoesExportReporter.activate!
|
|
17
|
+
#
|
|
18
|
+
# Select a destination to export JSON test results to:
|
|
19
|
+
#
|
|
20
|
+
# export SHOES_MINITEST_EXPORT_FILE=/tmp/shoes_test_export.json
|
|
21
|
+
#
|
|
22
|
+
# This class overrides the MINITEST_REPORTER environment variable when you call activate.
|
|
23
|
+
# If MINITEST_REPORTER isn't set then when you run via Vim, TextMate, RubyMine, etc,
|
|
24
|
+
# the reporter will be automatically overridden and print to console instead.
|
|
25
|
+
#
|
|
26
|
+
# Based on https://gist.github.com/davidwessman/09a13840a8a80080e3842ac3051714c7
|
|
27
|
+
class ShoesExportReporter < DefaultReporter
|
|
28
|
+
def self.activate!
|
|
29
|
+
unless ENV["SHOES_MINITEST_EXPORT_FILE"]
|
|
30
|
+
raise "ShoesExportReporter is available, but no export file was specified! Set SHOES_MINITEST_EXPORT_FILE!"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Minitest::Reporters.use!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def serialize_failures(failures)
|
|
37
|
+
failures.map do |fail|
|
|
38
|
+
case fail
|
|
39
|
+
when Minitest::UnexpectedError
|
|
40
|
+
["unexpected", fail.to_json, fail.error.to_json]
|
|
41
|
+
when Exception
|
|
42
|
+
["exception", fail.to_json]
|
|
43
|
+
else
|
|
44
|
+
raise "Not sure how to serialize failure object! #{fail.inspect}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def report
|
|
50
|
+
super
|
|
51
|
+
|
|
52
|
+
results = tests.map do |result|
|
|
53
|
+
failures = serialize_failures result.failures
|
|
54
|
+
{
|
|
55
|
+
name: result.name,
|
|
56
|
+
klass: test_class(result),
|
|
57
|
+
assertions: result.assertions,
|
|
58
|
+
failures: failures,
|
|
59
|
+
time: result.time,
|
|
60
|
+
metadata: result.respond_to?(:metadata) ? result.metadata : {},
|
|
61
|
+
source_location: begin
|
|
62
|
+
result.source_location
|
|
63
|
+
rescue
|
|
64
|
+
["unknown", -1]
|
|
65
|
+
end,
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
out_file = File.expand_path ENV["SHOES_MINITEST_EXPORT_FILE"]
|
|
70
|
+
puts "Writing Minitest results to #{out_file.inspect}."
|
|
71
|
+
File.write(out_file, JSON.dump(results))
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest"
|
|
4
|
+
require "json"
|
|
5
|
+
require "json/add/exception"
|
|
6
|
+
|
|
7
|
+
module Scarpe; module Components; end; end
|
|
8
|
+
module Scarpe::Components::ImportRunnables
|
|
9
|
+
# Minitest Runnables are unusual - we expect to declare a class (like a Test) with
|
|
10
|
+
# a lot of methods to run. The ImportRunnable is a single Runnable. But whenever
|
|
11
|
+
# you tell it to import a JSON file, it will add all of the described tests to
|
|
12
|
+
# its runnable methods.
|
|
13
|
+
#
|
|
14
|
+
# Normally that means that your subclass tests will run up front and produce
|
|
15
|
+
# JSON files, then Minitest will autorun at the end and report all their
|
|
16
|
+
# results.
|
|
17
|
+
#
|
|
18
|
+
# It wouldn't really make sense to create these runnables during the testing
|
|
19
|
+
# phase, because Minitest has already decided what to run at that point.
|
|
20
|
+
class ImportRunnable #< Minitest::Runnable
|
|
21
|
+
# Import JSON from an exported Minitest run. Note that running this multiple
|
|
22
|
+
# times with overlapping class names may be really bad.
|
|
23
|
+
def self.import_json_data(data)
|
|
24
|
+
@imported_classes ||= {}
|
|
25
|
+
@imported_tests ||= {}
|
|
26
|
+
|
|
27
|
+
JSON.parse(data).each do |item|
|
|
28
|
+
klass = item["klass"]
|
|
29
|
+
meth = item["name"]
|
|
30
|
+
@imported_tests[klass] ||= {}
|
|
31
|
+
@imported_tests[klass][meth] = item
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@imported_tests.each do |klass_name, test_method_hash|
|
|
35
|
+
klass = @imported_classes[klass_name]
|
|
36
|
+
unless klass
|
|
37
|
+
new_klass = Class.new(Minitest::Runnable)
|
|
38
|
+
@imported_classes[klass_name] = new_klass
|
|
39
|
+
ImportRunnable.const_set(klass_name, new_klass)
|
|
40
|
+
klass = new_klass
|
|
41
|
+
|
|
42
|
+
klass.define_singleton_method(:run_one_method) do |klass, method_name, reporter|
|
|
43
|
+
reporter.prerecord klass, method_name
|
|
44
|
+
imp = test_method_hash[method_name]
|
|
45
|
+
|
|
46
|
+
res = Minitest::Result.new imp["name"]
|
|
47
|
+
res.klass = imp["klass"]
|
|
48
|
+
res.assertions = imp["assertions"]
|
|
49
|
+
res.time = imp["time"]
|
|
50
|
+
res.failures = ImportRunnable.deserialize_failures imp["failures"]
|
|
51
|
+
res.metadata = imp["metadata"] if imp["metadata"]
|
|
52
|
+
|
|
53
|
+
# Record the synthetic result built from imported data
|
|
54
|
+
reporter.record res
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Update "runnables" method to reflect all current known runnable tests
|
|
59
|
+
klass_methods = test_method_hash.keys
|
|
60
|
+
klass.define_singleton_method(:runnable_methods) do
|
|
61
|
+
klass_methods
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.json_to_err(err_json)
|
|
67
|
+
klass = begin
|
|
68
|
+
Object.const_get(err_json["json_class"])
|
|
69
|
+
rescue
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
if klass && klass <= Minitest::Assertion
|
|
73
|
+
klass.json_create(err_json)
|
|
74
|
+
else
|
|
75
|
+
err = Exception.json_create(err_json)
|
|
76
|
+
Minitest::UnexpectedError.new(err)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.deserialize_failures(failures)
|
|
81
|
+
failures.map do |fail|
|
|
82
|
+
# Instantiate the Minitest::Assertion or Minitest::UnexpectedError
|
|
83
|
+
if fail[0] == "exception"
|
|
84
|
+
exc_json = JSON.parse(fail[1])
|
|
85
|
+
json_to_err exc_json
|
|
86
|
+
elsif fail[0] == "unexpected"
|
|
87
|
+
unexpected_json = JSON.parse(fail[1])
|
|
88
|
+
inner_json = JSON.parse(fail[2])
|
|
89
|
+
outer_err = json_to_err unexpected_json
|
|
90
|
+
inner_err = json_to_err inner_json
|
|
91
|
+
outer_err.error = inner_err
|
|
92
|
+
else
|
|
93
|
+
raise "Unknown exception data when trying to deserialize! #{fail.inspect}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest"
|
|
4
|
+
require "json"
|
|
5
|
+
#require "json/add/exception"
|
|
6
|
+
|
|
7
|
+
module Scarpe; module Components; end; end
|
|
8
|
+
|
|
9
|
+
# A MinitestResult imports a JSON file from a minitest_export_reporter.
|
|
10
|
+
# But instead of creating a Minitest::Test to report the result, the
|
|
11
|
+
# MinitestResult is just a queryable Ruby object.
|
|
12
|
+
#
|
|
13
|
+
# MinitestResult assumes there will be only one class and one method
|
|
14
|
+
# in the JSON, which is true for Scarpe but not necessarily in general.
|
|
15
|
+
class Scarpe::Components::MinitestResult
|
|
16
|
+
attr_reader :assertions
|
|
17
|
+
attr_reader :method_name
|
|
18
|
+
attr_reader :class_name
|
|
19
|
+
|
|
20
|
+
def initialize(filename)
|
|
21
|
+
data = JSON.parse File.read(filename)
|
|
22
|
+
|
|
23
|
+
unless data.size == 1
|
|
24
|
+
# We would want a different interface to support this in general. For now we don't
|
|
25
|
+
# need it to work in general.
|
|
26
|
+
raise "Scarpe::Components::MinitestResult only supports one class and method in results!"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
item = data.first
|
|
30
|
+
|
|
31
|
+
@assertions = item["assertions"]
|
|
32
|
+
@method_name = item["name"]
|
|
33
|
+
@class_name = item["klass"]
|
|
34
|
+
@time = item["time"]
|
|
35
|
+
@metadata = item.key?("metadata") ? item["metadata"]: {}
|
|
36
|
+
|
|
37
|
+
@skip = false
|
|
38
|
+
@exceptions = []
|
|
39
|
+
@failures = []
|
|
40
|
+
item["failures"].each do |f|
|
|
41
|
+
# JSON.parse ignores json_class and won't create an arbitrary object. That's good
|
|
42
|
+
# because Minitest::UnexpectedError seems to load in a bad way, so we don't want
|
|
43
|
+
# it to auto-instantiate.
|
|
44
|
+
d = JSON.parse f[1]
|
|
45
|
+
msg = d["m"]
|
|
46
|
+
case d["json_class"]
|
|
47
|
+
when "Minitest::UnexpectedError"
|
|
48
|
+
@exceptions << msg
|
|
49
|
+
when "Minitest::Skip"
|
|
50
|
+
@skip = msg
|
|
51
|
+
when "Minitest::Assertion"
|
|
52
|
+
@failures << msg
|
|
53
|
+
else
|
|
54
|
+
raise Scarpe::InternalError, "Didn't expect type #{t.inspect} as exception type when importing Minitest tests!"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def error?
|
|
60
|
+
!@exceptions.empty?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def fail?
|
|
64
|
+
!@failures.empty?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def skip?
|
|
68
|
+
@skip ? true : false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def passed?
|
|
72
|
+
@exceptions.empty? && @failures.empty? && !@skip
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def error_message
|
|
76
|
+
@exceptions[0]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def fail_message
|
|
80
|
+
@failures[0]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def skip_message
|
|
84
|
+
@skip
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -7,9 +7,9 @@ require "shoes/log"
|
|
|
7
7
|
|
|
8
8
|
# Requires the logging gem
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
module Scarpe; end
|
|
11
11
|
module Scarpe::Components; end
|
|
12
|
-
|
|
12
|
+
module Scarpe
|
|
13
13
|
class Components::ModularLogImpl
|
|
14
14
|
include Shoes::Log # for constants
|
|
15
15
|
|
|
@@ -32,7 +32,7 @@ class Scarpe
|
|
|
32
32
|
when "fatal"
|
|
33
33
|
:fatal
|
|
34
34
|
else
|
|
35
|
-
raise "Don't know how to treat #{data.inspect} as a logger severity!"
|
|
35
|
+
raise Shoes::Errors::InvalidAttributeValueError, "Don't know how to treat #{data.inspect} as a logger severity!"
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -45,7 +45,7 @@ class Scarpe
|
|
|
45
45
|
when String
|
|
46
46
|
Logging.appenders.file data, layout: @custom_log_layout
|
|
47
47
|
else
|
|
48
|
-
raise "Don't know how to convert #{data.inspect} to an appender!"
|
|
48
|
+
raise Shoes::Errors::InvalidAttributeValueError, "Don't know how to convert #{data.inspect} to an appender!"
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -64,7 +64,7 @@ class Scarpe
|
|
|
64
64
|
|
|
65
65
|
logger.level = name_to_severity(level)
|
|
66
66
|
else
|
|
67
|
-
raise "Don't know how to use #{data.inspect} to specify a logger!"
|
|
67
|
+
raise Shoes::Errors::InvalidAttributeValueError, "Don't know how to use #{data.inspect} to specify a logger!"
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -3,30 +3,34 @@
|
|
|
3
3
|
require "shoes/log"
|
|
4
4
|
require "json"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
module Scarpe; end
|
|
7
7
|
module Scarpe::Components; end
|
|
8
8
|
class Scarpe::Components::PrintLogImpl
|
|
9
9
|
include Shoes::Log # for constants
|
|
10
10
|
|
|
11
11
|
class PrintLogger
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :silence
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
def initialize(component_name)
|
|
13
17
|
@comp_name = component_name
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def error(msg)
|
|
17
|
-
puts "#{@comp_name} error: #{msg}"
|
|
21
|
+
puts "#{@comp_name} error: #{msg}" unless PrintLogger.silence
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def warn(msg)
|
|
21
|
-
puts "#{@comp_name} warn: #{msg}"
|
|
25
|
+
puts "#{@comp_name} warn: #{msg}" unless PrintLogger.silence
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
def debug(msg)
|
|
25
|
-
puts "#{@comp_name} debug: #{msg}"
|
|
29
|
+
puts "#{@comp_name} debug: #{msg}" unless PrintLogger.silence
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
def info(msg)
|
|
29
|
-
puts "#{@comp_name} info: #{msg}"
|
|
33
|
+
puts "#{@comp_name} info: #{msg}" unless PrintLogger.silence
|
|
30
34
|
end
|
|
31
35
|
end
|
|
32
36
|
|