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,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "scarpe/components/file_helpers"
|
|
4
|
+
|
|
5
|
+
module Scarpe::Components
|
|
6
|
+
class SegmentedFileLoader
|
|
7
|
+
include Scarpe::Components::FileHelpers
|
|
8
|
+
|
|
9
|
+
# Add a new segment type (e.g. "catscradle") with a different
|
|
10
|
+
# file handler.
|
|
11
|
+
#
|
|
12
|
+
# @param type [String] the new name for this segment type
|
|
13
|
+
# @param handler [Object] an object that will be called as obj.call(filename) - often a proc
|
|
14
|
+
# @return <void>
|
|
15
|
+
def add_segment_type(type, handler)
|
|
16
|
+
if segment_type_hash.key?(type)
|
|
17
|
+
raise "Segment type #{type.inspect} already exists!"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
segment_type_hash[type] = handler
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Return an Array of segment type labels, such as "code" and "app_test".
|
|
24
|
+
#
|
|
25
|
+
# @return Array<String> the segment type labels
|
|
26
|
+
def segment_types
|
|
27
|
+
segment_type_hash.keys
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Load a .sca file with an optional YAML frontmatter prefix and
|
|
31
|
+
# multiple file sections which can be treated differently.
|
|
32
|
+
#
|
|
33
|
+
# The file loader acts like a proc, being called with .call()
|
|
34
|
+
# and returning true or false for whether it has handled the
|
|
35
|
+
# file load. This allows chaining loaders in order and the
|
|
36
|
+
# first loader to recognise a file will run it.
|
|
37
|
+
#
|
|
38
|
+
# @param path [String] the file or directory to treat as a Scarpe app
|
|
39
|
+
# @return [Boolean] return true if the file is loaded as a segmented Scarpe app file
|
|
40
|
+
def call(path)
|
|
41
|
+
return false unless path.end_with?(".scas")
|
|
42
|
+
|
|
43
|
+
file_load(path)
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Segment type handlers can call this to perform an operation after the load
|
|
48
|
+
# has completed. This is important for ordering, and because loading a Shoes
|
|
49
|
+
# app often doesn't return. So to have a later section (e.g. tests, additional
|
|
50
|
+
# data) do something that affects Shoes app loading (e.g. set an env var,
|
|
51
|
+
# affect the display service) it's important that app loading take place later
|
|
52
|
+
# in the sequence.
|
|
53
|
+
def after_load(&block)
|
|
54
|
+
@after_load ||= []
|
|
55
|
+
@after_load << block
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def gen_name(segmap)
|
|
61
|
+
ctr = (1..10_000).detect { |i| !segmap.key?("%5d" % i) }
|
|
62
|
+
"%5d" % ctr
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def tokenize_segments(contents)
|
|
66
|
+
require "yaml" # Only load when needed
|
|
67
|
+
require "English"
|
|
68
|
+
|
|
69
|
+
segments = contents.split(/\n-{5,}/)
|
|
70
|
+
front_matter = {}
|
|
71
|
+
|
|
72
|
+
# The very first segment can start with front matter, or with a divider, or with no divider.
|
|
73
|
+
if segments[0].start_with?("---\n") || segments[0] == "---"
|
|
74
|
+
# We have YAML front matter at the start. All later segments will have a divider.
|
|
75
|
+
front_matter = YAML.load segments[0]
|
|
76
|
+
front_matter ||= {} # If the front matter is just the three dashes it returns nil
|
|
77
|
+
segments = segments[1..-1]
|
|
78
|
+
elsif segments[0].start_with?("-----")
|
|
79
|
+
# We have a divider at the start. Great! We're already well set up for this case.
|
|
80
|
+
elsif segments.size == 1
|
|
81
|
+
# No front matter, no divider, a single unnamed segment. No more parsing needed.
|
|
82
|
+
return [{}, { "" => segments[0] }]
|
|
83
|
+
else
|
|
84
|
+
# No front matter, no divider before the first segment, multiple segments.
|
|
85
|
+
# We'll add an artificial divider to the first segment for uniformity.
|
|
86
|
+
segments = ["-----\n" + segments[0]] + segments[1..-1]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
segmap = {}
|
|
90
|
+
segments.each do |segment|
|
|
91
|
+
if segment =~ /\A-* +(.*?)\n/
|
|
92
|
+
# named segment with separator
|
|
93
|
+
segmap[::Regexp.last_match(1)] = ::Regexp.last_match.post_match
|
|
94
|
+
elsif segment =~ /\A-* *\n/
|
|
95
|
+
# unnamed segment with separator
|
|
96
|
+
segmap[gen_name(segmap)] = ::Regexp.last_match.post_match
|
|
97
|
+
else
|
|
98
|
+
raise "Internal error when parsing segments in segmented app file! seg: #{segment.inspect}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
[front_matter, segmap]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def file_load(path)
|
|
106
|
+
contents = File.read(path)
|
|
107
|
+
|
|
108
|
+
front_matter, segmap = tokenize_segments(contents)
|
|
109
|
+
|
|
110
|
+
if segmap.empty?
|
|
111
|
+
raise "Illegal segmented Scarpe file: must have at least one code segment, not just front matter!"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if front_matter[:segments]
|
|
115
|
+
if front_matter[:segments].size != segmap.size
|
|
116
|
+
raise "Number of front matter :segments must equal number of file segments!"
|
|
117
|
+
end
|
|
118
|
+
else
|
|
119
|
+
if segmap.size > 2
|
|
120
|
+
raise "Segmented files with more than two segments have to specify what they're for!"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Set to default of shoes code only or shoes code and app test code.
|
|
124
|
+
front_matter[:segments] = segmap.size == 2 ? ["shoes", "app_test"] : ["shoes"]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Match up front_matter[:segments] with the segments, or use the default of shoes and app_test.
|
|
128
|
+
|
|
129
|
+
sth = segment_type_hash
|
|
130
|
+
sv = segmap.values
|
|
131
|
+
|
|
132
|
+
tf_specs = []
|
|
133
|
+
front_matter[:segments].each.with_index do |seg_type, idx|
|
|
134
|
+
unless sth.key?(seg_type)
|
|
135
|
+
raise "Unrecognized segment type #{seg_type.inspect}! No matching segment type available!"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
tf_specs << ["scarpe_#{seg_type}_segment_contents", sv[idx]]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
with_tempfiles(tf_specs) do |filenames|
|
|
142
|
+
filenames.each.with_index do |filename, idx|
|
|
143
|
+
seg_name = front_matter[:segments][idx]
|
|
144
|
+
sth[seg_name].call(filename)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Need to call @after_load hooks while tempfiles still exist
|
|
148
|
+
if @after_load && !@after_load.empty?
|
|
149
|
+
@after_load.each(&:call)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# The hash of segment type labels mapped to handlers which will be called.
|
|
155
|
+
# Normal client code shouldn't ever call this.
|
|
156
|
+
#
|
|
157
|
+
# @return Hash<String, Object> the name/handler pairs
|
|
158
|
+
def segment_type_hash
|
|
159
|
+
@segment_handlers ||= {
|
|
160
|
+
"shoes" => proc { |seg_file| after_load { load seg_file } },
|
|
161
|
+
"app_test" => proc { |seg_file| ENV["SCARPE_APP_TEST"] = seg_file },
|
|
162
|
+
}
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# You can add additional segment types to the segmented file loader
|
|
168
|
+
# loader = Scarpe::Components::SegmentedFileLoader.new
|
|
169
|
+
# loader.add_segment_type "capybara", proc { |seg_file| load_file_as_capybara(seg_file) }
|
|
170
|
+
# Shoes.add_file_loader loader
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
require "json"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
require "scarpe/components/file_helpers"
|
|
8
|
+
|
|
9
|
+
module Scarpe::Test; end
|
|
10
|
+
|
|
11
|
+
# We want test failures set up once *total*, not per Minitest::Test. So an instance var
|
|
12
|
+
# doesn't do it.
|
|
13
|
+
ALREADY_SET_UP_LOGGED_TEST_FAILURES = { setup: false }
|
|
14
|
+
|
|
15
|
+
# General helpers for general usage.
|
|
16
|
+
# Helpers here should *not* use Webview-specific functionality.
|
|
17
|
+
# The intention is that these are helpers for various Scarpe display
|
|
18
|
+
# services that do *not* necessarily use Webview.
|
|
19
|
+
|
|
20
|
+
module Scarpe::Test::Helpers
|
|
21
|
+
# Very useful for tests
|
|
22
|
+
include Scarpe::Components::FileHelpers
|
|
23
|
+
|
|
24
|
+
# Temporarily set env vars for the block of code inside. The old environment
|
|
25
|
+
# variable values will be restored after the block finishes.
|
|
26
|
+
#
|
|
27
|
+
# @param envs [Hash<String,String>] A hash of environment variable names and values
|
|
28
|
+
def with_env_vars(envs)
|
|
29
|
+
old_env = {}
|
|
30
|
+
envs.each do |k, v|
|
|
31
|
+
old_env[k] = ENV[k]
|
|
32
|
+
ENV[k] = v
|
|
33
|
+
end
|
|
34
|
+
yield
|
|
35
|
+
ensure
|
|
36
|
+
old_env.each { |k, v| ENV[k] = v }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# This test will save extensive logs in case of test failure.
|
|
41
|
+
# Note that it defines setup/teardown methods. If you want
|
|
42
|
+
# multiple setup/teardowns from multiple places to happen you
|
|
43
|
+
# may need to explictly call (e.g. with logged_test_setup/teardown)
|
|
44
|
+
# to ensure everything you want happens.
|
|
45
|
+
module Scarpe::Test::LoggedTest
|
|
46
|
+
def self.included(includer)
|
|
47
|
+
class << includer
|
|
48
|
+
attr_accessor :logger_dir
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def file_id
|
|
53
|
+
"#{self.class.name}_#{self.name}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# This should be called by the test during setup to make sure that
|
|
57
|
+
# failure logs will be saved if this test fails. It makes sure the
|
|
58
|
+
# log config will save all logs from all sources, but keeps a copy
|
|
59
|
+
# of the old log config to restore after the test is finished.
|
|
60
|
+
#
|
|
61
|
+
# @return [void]
|
|
62
|
+
def logged_test_setup
|
|
63
|
+
# Make sure test failures will be saved at the end of the run.
|
|
64
|
+
# Delete stale test failures and logging only the *first* time this is called.
|
|
65
|
+
set_up_test_failures
|
|
66
|
+
|
|
67
|
+
@normal_log_config = Shoes::Log.current_log_config
|
|
68
|
+
Shoes::Log.configure_logger(log_config_for_test)
|
|
69
|
+
|
|
70
|
+
Shoes::Log.logger("LoggedScarpeTest").info("Test: #{self.class.name}##{self.name}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# If you include this module and don't override setup/teardown, everything will
|
|
74
|
+
# work fine. But if you need more setup/teardown steps, you can do that too.
|
|
75
|
+
#
|
|
76
|
+
# The setup method guarantees that just including this module will do setup
|
|
77
|
+
# automatically. If you override it, be sure to call `super` or `logged_test_setup`.
|
|
78
|
+
#
|
|
79
|
+
# @return [void]
|
|
80
|
+
def setup
|
|
81
|
+
logged_test_setup
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# After the test has finished, this will restore the old log configuration.
|
|
85
|
+
# It will also save the logfiles, but only if the test failed, not if it
|
|
86
|
+
# succeeded or was skipped.
|
|
87
|
+
#
|
|
88
|
+
# @return [void]
|
|
89
|
+
def logged_test_teardown
|
|
90
|
+
# Restore previous log config
|
|
91
|
+
Shoes::Log.configure_logger(@normal_log_config)
|
|
92
|
+
|
|
93
|
+
if self.failure
|
|
94
|
+
save_failure_logs
|
|
95
|
+
else
|
|
96
|
+
remove_unsaved_logs
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Make sure that, by default, #logged_test_teardown will be called for teardown.
|
|
101
|
+
# If a class overrides teardown, it should also call `super` or `logged_test_teardown`
|
|
102
|
+
# to make sure this still happens.
|
|
103
|
+
#
|
|
104
|
+
# @return [void]
|
|
105
|
+
def teardown
|
|
106
|
+
logged_test_teardown
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Set additional LoggedTest configuration for specific logs to separate or save.
|
|
110
|
+
# This is normally going to be display-service-specific log components.
|
|
111
|
+
# Note that this only really works with the modular logger or another logger
|
|
112
|
+
# that does something useful with the log config. The simple print logger
|
|
113
|
+
# doesn't do a lot with it.
|
|
114
|
+
def extra_log_config=(additional_log_config)
|
|
115
|
+
@additional_log_config = additional_log_config
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# This is the log config that LoggedTests use. It makes sure all components keep all
|
|
119
|
+
# logs, but also splits the logs into several different files for later ease of scanning.
|
|
120
|
+
#
|
|
121
|
+
# TODO: this shouldn't directly include any Webview entries like WebviewAPI or
|
|
122
|
+
# CatsCradle. Those should be overridden in Webview.
|
|
123
|
+
#
|
|
124
|
+
# @return [Hash] the log config
|
|
125
|
+
def log_config_for_test
|
|
126
|
+
{
|
|
127
|
+
"default" => ["debug", "logger/test_failure_#{file_id}.log"],
|
|
128
|
+
"DisplayService" => ["debug", "logger/test_failure_events_#{file_id}.log"],
|
|
129
|
+
}.merge(@additional_log_config || {})
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The list of logfiles that should be saved. Normally this is called internally by the
|
|
133
|
+
# class, not externally from elsewhere.
|
|
134
|
+
#
|
|
135
|
+
# This could be a lot simpler except I want to only update the file list in one place,
|
|
136
|
+
# log_config_for_test(). Having a single spot should (I hope) make it a lot friendlier to
|
|
137
|
+
# add more logfiles for different components, logged API objects, etc.
|
|
138
|
+
def saved_log_files
|
|
139
|
+
lc = log_config_for_test
|
|
140
|
+
log_outfiles = lc.values.map { |_level, loc| loc }
|
|
141
|
+
log_outfiles.select { |s| s.start_with?("logger/") }.map { |s| s.delete_prefix("logger/") }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Make sure that test failure logs will be noticed, and a message will be printed,
|
|
145
|
+
# if any logged tests fail. This needs to be called at least once in any Minitest-enabled
|
|
146
|
+
# process using logged tests.
|
|
147
|
+
#
|
|
148
|
+
# @return [void]
|
|
149
|
+
def set_up_test_failures
|
|
150
|
+
return if ALREADY_SET_UP_LOGGED_TEST_FAILURES[:setup]
|
|
151
|
+
|
|
152
|
+
log_dir = self.class.logger_dir
|
|
153
|
+
raise("Must set logger directory!") unless log_dir
|
|
154
|
+
raise("Can't find logger directory!") unless File.directory?(log_dir)
|
|
155
|
+
|
|
156
|
+
ALREADY_SET_UP_LOGGED_TEST_FAILURES[:setup] = true
|
|
157
|
+
# Delete stale test failures, if any, before starting the first failure-logged test
|
|
158
|
+
Dir["#{log_dir}/test_failure*.log"].each { |fn| File.unlink(fn) }
|
|
159
|
+
|
|
160
|
+
Minitest.after_run do
|
|
161
|
+
# Print test failure notice to console
|
|
162
|
+
unless Dir["#{log_dir}/test_failure*.out.log"].empty?
|
|
163
|
+
puts "Some tests have failed! See #{log_dir}/test_failure*.out.log for test logs!"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Remove un-saved test logs
|
|
167
|
+
Dir["#{log_dir}/test_failure*.log"].each do |f|
|
|
168
|
+
next if f.include?(".out.log")
|
|
169
|
+
|
|
170
|
+
File.unlink(f) if File.exist?(f)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Failure log output location for a given file path. This is normally used internally to this
|
|
176
|
+
# class, not externally.
|
|
177
|
+
#
|
|
178
|
+
# @return [String] the output path
|
|
179
|
+
def logfail_out_loc(filepath)
|
|
180
|
+
# Add a .out prefix before final .log
|
|
181
|
+
out_loc = filepath.gsub(%r{.log\Z}, ".out.log")
|
|
182
|
+
|
|
183
|
+
if out_loc == filepath
|
|
184
|
+
raise "Something is wrong! Could not figure out failure-log output path for #{filepath.inspect}!"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
if File.exist?(out_loc)
|
|
188
|
+
raise "Duplicate test file #{out_loc.inspect}? This file should *not* already exist!"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
out_loc
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Save the failure logs in the appropriate place(s). This is normally used internally, not externally.
|
|
195
|
+
#
|
|
196
|
+
# @return [void]
|
|
197
|
+
def save_failure_logs
|
|
198
|
+
saved_log_files.each do |log_file|
|
|
199
|
+
full_loc = File.expand_path("#{self.class.logger_dir}/#{log_file}")
|
|
200
|
+
# TODO: we'd like to skip 0-length logfiles. But also Logging doesn't flush. For now, ignore.
|
|
201
|
+
next unless File.exist?(full_loc)
|
|
202
|
+
|
|
203
|
+
FileUtils.mv full_loc, logfail_out_loc(full_loc)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Remove unsaved failure logs. This is normally used internally, not externally.
|
|
208
|
+
#
|
|
209
|
+
# @return [void]
|
|
210
|
+
def remove_unsaved_logs
|
|
211
|
+
Dir["#{self.class.logger_dir}/test_failure*.log"].each do |f|
|
|
212
|
+
next if f.include?(".out.log") # Don't delete saved logs
|
|
213
|
+
|
|
214
|
+
File.unlink(f)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/scarpe/components/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "scarpe-components"
|
|
7
|
+
spec.version = Scarpe::Components::VERSION
|
|
8
|
+
spec.authors = ["Marco Concetto Rudilosso", "Noah Gibbs"]
|
|
9
|
+
spec.email = ["marcoc.r@outlook.com", "the.codefolio.guy@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Reusable components for Scarpe display libraries"
|
|
12
|
+
spec.homepage = "https://github.com/scarpe-team/scarpe"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
15
|
+
|
|
16
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
|
17
|
+
|
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/scarpe-team/scarpe"
|
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/scarpe-team/scarpe/blob/main/CHANGELOG.md"
|
|
21
|
+
|
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
26
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
spec.bindir = "exe"
|
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
32
|
+
|
|
33
|
+
# Scarpe-Components should add *no* runtime dependencies. Since each component
|
|
34
|
+
# is optional, the display library should add dependencies relevant to only
|
|
35
|
+
# the components it directly uses and no others.
|
|
36
|
+
|
|
37
|
+
#spec.add_dependency ""
|
|
38
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
4
|
+
|
|
5
|
+
require "shoes"
|
|
6
|
+
|
|
7
|
+
require "minitest/autorun"
|
|
8
|
+
|
|
9
|
+
require "minitest/reporters"
|
|
10
|
+
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
|
11
|
+
|
|
12
|
+
# For tests, default to simple print logger
|
|
13
|
+
# TODO: switch to modular logger and failure-logged tests?
|
|
14
|
+
require "shoes/log"
|
|
15
|
+
require "scarpe/components/print_logger"
|
|
16
|
+
Shoes::Log.instance = Scarpe::Components::PrintLogImpl.new
|
|
17
|
+
Shoes::Log.configure_logger(Shoes::Log::DEFAULT_LOG_CONFIG)
|
|
18
|
+
|
|
19
|
+
require "scarpe/components/unit_test_helpers"
|
|
20
|
+
|
|
21
|
+
class Minitest::Test
|
|
22
|
+
include Scarpe::Test::Helpers
|
|
23
|
+
end
|