scarpe 0.3.0 → 0.5.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.
Files changed (297) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/rules/commit-style-preferences.mdc +72 -0
  3. data/.cursor/rules/component_context.mdc +82 -0
  4. data/.cursor/rules/debug-failed-tests.mdc +100 -0
  5. data/.cursor/rules/display_service_context.mdc +80 -0
  6. data/.cursor/rules/event_handling_context.mdc +100 -0
  7. data/.cursor/rules/git-pager-handling.mdc +64 -0
  8. data/.cursor/rules/lacci-context.mdc +52 -0
  9. data/.cursor/rules/scarpe_design_context.mdc +46 -0
  10. data/.cursor/rules/shoes_compatibility_context.mdc +75 -0
  11. data/.cursor/rules/timeout_context.mdc +78 -0
  12. data/.cursor/rules/update_lacci_and_wv.mdc +8 -0
  13. data/.cursor/rules/what_is_scarpe.mdc +22 -0
  14. data/.cursor/rules/writing-new-rules.mdc +73 -0
  15. data/CHANGELOG.md +46 -5
  16. data/CLAUDE.md +223 -0
  17. data/Gemfile +2 -2
  18. data/Gemfile.lock +85 -55
  19. data/LICENSE.txt +7 -1
  20. data/README.md +80 -20
  21. data/Rakefile +73 -14
  22. data/docs/SCARPE_FEATURES.md +38 -0
  23. data/docs/_config.yml +13 -0
  24. data/docs/calzini_components_and_updates.md +78 -0
  25. data/docs/display_service_separation.md +39 -0
  26. data/docs/documentation.md +43 -0
  27. data/docs/event_loops.md +66 -0
  28. data/docs/image.png +0 -0
  29. data/docs/index.md +118 -0
  30. data/docs/lacci.md +121 -0
  31. data/docs/scarpe_shoes_incompatibilities.md +71 -0
  32. data/docs/shoes_and_display_events.md +55 -0
  33. data/docs/shoes_implementations.md +79 -0
  34. data/docs/static/manual.md +5 -0
  35. data/docs/static/scarpe-logo.png +0 -0
  36. data/docs/timeouts_and_handlers.md +66 -0
  37. data/docs/web_archaeology.md +76 -0
  38. data/examples/Edit_box_Styles.rb +8 -0
  39. data/examples/Kerning.rb +7 -0
  40. data/examples/background_with_image.rb +14 -5
  41. data/examples/bloopsaphone/working/feepogram.rb +1 -1
  42. data/examples/bloopsaphone/working/le_dance_des_rubis.rb +135 -0
  43. data/examples/bloopsaphone/working/pixel_dreams_in_ruby.rb +131 -0
  44. data/examples/bloopsaphone/working/type_rebellion.rb +157 -0
  45. data/examples/border.rb +11 -0
  46. data/examples/check.rb +2 -0
  47. data/examples/download_and_show_image.rb +3 -0
  48. data/examples/flags/finland.rb +15 -0
  49. data/examples/flags/italy.rb +11 -0
  50. data/examples/flags/mauritius.rb +14 -0
  51. data/examples/font_family.rb +17 -0
  52. data/examples/font_shorthand.rb +9 -0
  53. data/examples/gen.rb +4 -0
  54. data/examples/internal_link_navigation.rb +19 -0
  55. data/examples/legacy/not_checked/shoes-manual/append.rb +10 -0
  56. data/examples/legacy/not_checked/shoes-manual/background_change.rb +12 -0
  57. data/examples/legacy/not_checked/shoes-manual/background_pattern.rb +5 -0
  58. data/examples/legacy/not_checked/shoes-manual/basic_app.rb +8 -0
  59. data/examples/legacy/not_checked/shoes-manual/border.rb +9 -0
  60. data/examples/legacy/not_checked/shoes-manual/builtins/FONTS.rb +5 -0
  61. data/examples/legacy/not_checked/shoes-manual/builtins/ask.rb +2 -0
  62. data/examples/legacy/not_checked/shoes-manual/builtins/ask_color.rb +5 -0
  63. data/examples/legacy/not_checked/shoes-manual/builtins/ask_open_file.rb +5 -0
  64. data/examples/legacy/not_checked/shoes-manual/builtins/ask_save_folder.rb +2 -0
  65. data/examples/legacy/not_checked/shoes-manual/builtins/confirm.rb +4 -0
  66. data/examples/legacy/not_checked/shoes-manual/builtins/debug.rb +2 -0
  67. data/examples/legacy/not_checked/shoes-manual/builtins/info.rb +3 -0
  68. data/examples/legacy/not_checked/shoes-manual/button.rb +9 -0
  69. data/examples/legacy/not_checked/shoes-manual/clear.rb +7 -0
  70. data/examples/legacy/not_checked/shoes-manual/custom_header.rb +13 -0
  71. data/examples/legacy/not_checked/shoes-manual/displace.rb +14 -0
  72. data/examples/legacy/not_checked/shoes-manual/edit_box.rb +8 -0
  73. data/examples/legacy/not_checked/shoes-manual/fill_pattern.rb +5 -0
  74. data/examples/legacy/not_checked/shoes-manual/fonts.rb +7 -0
  75. data/examples/legacy/not_checked/shoes-manual/gutter.rb +6 -0
  76. data/examples/legacy/not_checked/shoes-manual/image_web.rb +4 -0
  77. data/examples/legacy/not_checked/shoes-manual/keypress.rb +7 -0
  78. data/examples/legacy/not_checked/shoes-manual/list_box.rb +10 -0
  79. data/examples/legacy/not_checked/shoes-manual/motion.rb +10 -0
  80. data/examples/legacy/not_checked/shoes-manual/mouse.rb +8 -0
  81. data/examples/legacy/not_checked/shoes-manual/move.rb +14 -0
  82. data/examples/legacy/not_checked/shoes-manual/nested_ovals.rb +8 -0
  83. data/examples/legacy/not_checked/shoes-manual/oval.rb +7 -0
  84. data/examples/legacy/not_checked/shoes-manual/ovals.rb +6 -0
  85. data/examples/legacy/not_checked/shoes-manual/ovals_image.rb +8 -0
  86. data/examples/legacy/not_checked/shoes-manual/prepend.rb +7 -0
  87. data/examples/legacy/not_checked/shoes-manual/progress_bar.rb +10 -0
  88. data/examples/legacy/not_checked/shoes-manual/radio.rb +18 -0
  89. data/examples/legacy/not_checked/shoes-manual/radio_alternative_1.rb +7 -0
  90. data/examples/legacy/not_checked/shoes-manual/radio_alternative_2.rb +9 -0
  91. data/examples/legacy/not_checked/shoes-manual/rotate_rectangle.rb +6 -0
  92. data/examples/legacy/not_checked/shoes-manual/shape.rb +11 -0
  93. data/examples/legacy/not_checked/shoes-manual/static/avatar.png +0 -0
  94. data/examples/legacy/not_checked/shoes-manual/stroke.rb +5 -0
  95. data/examples/legacy/not_checked/shoes-manual/style.rb +3 -0
  96. data/examples/legacy/not_checked/shoes-manual/style_alternative_1.rb +4 -0
  97. data/examples/legacy/not_checked/shoes-manual/style_alternative_2.rb +5 -0
  98. data/examples/legacy/not_checked/shoes-manual/style_length.rb +5 -0
  99. data/examples/legacy/not_checked/shoes-manual/timer.rb +6 -0
  100. data/examples/legacy/not_checked/shoes-manual/trigger_window.rb +8 -0
  101. data/examples/legacy/not_checked/shoes-manual/window_owner.rb +8 -0
  102. data/examples/legacy/working/shoes_manual/alert_button.rb +2 -0
  103. data/examples/legacy/working/shoes_manual/animate.rb +7 -0
  104. data/examples/legacy/working/shoes_manual/background_para.rb +4 -0
  105. data/examples/legacy/working/shoes_manual/button_alternative.rb +7 -0
  106. data/examples/legacy/working/shoes_manual/checkbox.rb +17 -0
  107. data/examples/legacy/working/shoes_manual/download.rb +12 -0
  108. data/examples/legacy/working/shoes_manual/edit_box.rb +6 -0
  109. data/examples/legacy/working/shoes_manual/editline.rb +7 -0
  110. data/examples/legacy/working/shoes_manual/fixed_height.rb +8 -0
  111. data/examples/legacy/working/shoes_manual/fixed_width.rb +12 -0
  112. data/examples/legacy/working/shoes_manual/image.rb +5 -0
  113. data/examples/legacy/working/shoes_manual/instance_variable_check.rb +10 -0
  114. data/examples/legacy/working/shoes_manual/message.rb +18 -0
  115. data/examples/legacy/working/shoes_manual/rectangle.rb +6 -0
  116. data/examples/legacy/working/shoes_manual/save_download.rb +12 -0
  117. data/examples/legacy/working/shoes_manual/self_check.rb +10 -0
  118. data/examples/legacy/working/shoes_manual/stack.rb +7 -0
  119. data/examples/legacy/working/shoes_manual/style_info.rb +8 -0
  120. data/examples/legacy/working/shoes_manual/utf8_support.rb +8 -0
  121. data/examples/legacy/working/shoes_manual/width.rb +4 -0
  122. data/examples/local_assets/multi_image.rb +5 -0
  123. data/examples/local_assets/small.png +0 -0
  124. data/examples/local_fonts.rb +3 -0
  125. data/examples/margin.rb +13 -0
  126. data/examples/margin_check.rb +27 -0
  127. data/examples/oval-with-kwargs.rb +3 -0
  128. data/examples/oval.rb +26 -0
  129. data/examples/page_navigation_single_app.rb +42 -0
  130. data/examples/para_font_styles.rb +17 -0
  131. data/examples/para_font_variant.rb +6 -0
  132. data/examples/para_fontweight.rb +13 -0
  133. data/examples/parse_xl_funnies.rb +3 -0
  134. data/examples/rect.rb +1 -1
  135. data/examples/scarpe_ext.rb +3 -0
  136. data/examples/shapes/star.rb +1 -3
  137. data/examples/shoes_subclass_app.rb +25 -0
  138. data/examples/spacing.rb +1 -1
  139. data/examples/span.rb +4 -2
  140. data/examples/url_routing_example.rb +67 -0
  141. data/lacci/Gemfile +0 -2
  142. data/lacci/Gemfile.lock +4 -32
  143. data/lacci/lacci.gemspec +3 -3
  144. data/lacci/lib/lacci/scarpe_cli.rb +0 -1
  145. data/lacci/lib/lacci/version.rb +1 -1
  146. data/lacci/lib/scarpe/niente/app.rb +12 -1
  147. data/lacci/lib/scarpe/niente/display_service.rb +5 -1
  148. data/lacci/lib/scarpe/niente/drawable.rb +2 -0
  149. data/lacci/lib/scarpe/niente/shoes_spec.rb +10 -5
  150. data/lacci/lib/scarpe/niente.rb +15 -2
  151. data/lacci/lib/shoes/app.rb +204 -105
  152. data/lacci/lib/shoes/constants.rb +24 -2
  153. data/lacci/lib/shoes/display_service.rb +43 -4
  154. data/lacci/lib/shoes/drawable.rb +326 -36
  155. data/lacci/lib/shoes/drawables/arc.rb +4 -26
  156. data/lacci/lib/shoes/drawables/arrow.rb +3 -23
  157. data/lacci/lib/shoes/drawables/border.rb +28 -0
  158. data/lacci/lib/shoes/drawables/button.rb +5 -21
  159. data/lacci/lib/shoes/drawables/check.rb +7 -3
  160. data/lacci/lib/shoes/drawables/document_root.rb +4 -4
  161. data/lacci/lib/shoes/drawables/edit_box.rb +6 -5
  162. data/lacci/lib/shoes/drawables/edit_line.rb +5 -4
  163. data/lacci/lib/shoes/drawables/flow.rb +4 -6
  164. data/lacci/lib/shoes/drawables/font_helper.rb +62 -0
  165. data/lacci/lib/shoes/drawables/image.rb +2 -2
  166. data/lacci/lib/shoes/drawables/line.rb +3 -6
  167. data/lacci/lib/shoes/drawables/link.rb +16 -9
  168. data/lacci/lib/shoes/drawables/list_box.rb +8 -5
  169. data/lacci/lib/shoes/drawables/oval.rb +48 -0
  170. data/lacci/lib/shoes/drawables/para.rb +106 -18
  171. data/lacci/lib/shoes/drawables/progress.rb +2 -1
  172. data/lacci/lib/shoes/drawables/radio.rb +5 -3
  173. data/lacci/lib/shoes/drawables/rect.rb +7 -6
  174. data/lacci/lib/shoes/drawables/shape.rb +4 -3
  175. data/lacci/lib/shoes/drawables/slot.rb +102 -9
  176. data/lacci/lib/shoes/drawables/stack.rb +7 -12
  177. data/lacci/lib/shoes/drawables/star.rb +9 -31
  178. data/lacci/lib/shoes/drawables/text_drawable.rb +93 -34
  179. data/lacci/lib/shoes/drawables/video.rb +3 -2
  180. data/lacci/lib/shoes/drawables/widget.rb +9 -4
  181. data/lacci/lib/shoes/drawables.rb +2 -1
  182. data/lacci/lib/shoes/errors.rb +13 -3
  183. data/lacci/lib/shoes/margin_helper.rb +79 -0
  184. data/lacci/lib/shoes.rb +98 -20
  185. data/lacci/test/.gitignore +1 -0
  186. data/lacci/test/test_draw_context.rb +167 -0
  187. data/lacci/test/test_font_helper.rb +57 -0
  188. data/lacci/test/test_helper.rb +31 -4
  189. data/lacci/test/test_lacci.rb +93 -6
  190. data/lacci/test/test_margin_helper.rb +82 -0
  191. data/lacci/test/test_niente_test_infra.rb +40 -0
  192. data/lacci/test/test_oval.rb +82 -0
  193. data/lacci/test/test_parenting.rb +140 -0
  194. data/lacci/test/test_shoes_errors.rb +15 -13
  195. data/lacci/test/test_text_drawables.rb +23 -0
  196. data/lib/scarpe/assets.rb +19 -0
  197. data/lib/scarpe/cats_cradle.rb +57 -98
  198. data/lib/scarpe/shoes_spec.rb +24 -44
  199. data/lib/scarpe/version.rb +1 -1
  200. data/lib/scarpe/wv/app.rb +1 -0
  201. data/lib/scarpe/wv/arc.rb +0 -4
  202. data/lib/scarpe/wv/border.rb +15 -0
  203. data/lib/scarpe/wv/control_interface.rb +2 -10
  204. data/lib/scarpe/wv/document_root.rb +2 -2
  205. data/lib/scarpe/wv/drawable.rb +6 -40
  206. data/lib/scarpe/wv/edit_box.rb +4 -1
  207. data/lib/scarpe/wv/edit_line.rb +4 -1
  208. data/lib/scarpe/wv/image.rb +2 -5
  209. data/lib/scarpe/wv/link.rb +4 -2
  210. data/lib/scarpe/wv/oval.rb +13 -0
  211. data/lib/scarpe/wv/para.rb +1 -0
  212. data/lib/scarpe/wv/scarpe_extensions.rb +8 -0
  213. data/lib/scarpe/wv/shape.rb +10 -5
  214. data/lib/scarpe/wv/text_drawable.rb +72 -14
  215. data/lib/scarpe/wv/web_wrangler.rb +33 -11
  216. data/lib/scarpe/wv/webview_local_display.rb +6 -2
  217. data/lib/scarpe/wv.rb +16 -2
  218. data/scarpe-components/Gemfile +4 -3
  219. data/scarpe-components/Gemfile.lock +6 -37
  220. data/scarpe-components/README.md +2 -2
  221. data/scarpe-components/assets/bootstrap-themes/bootstrap-cerulean.css +12229 -0
  222. data/scarpe-components/assets/bootstrap-themes/bootstrap-cosmo.css +11810 -0
  223. data/scarpe-components/assets/bootstrap-themes/bootstrap-cyborg.css +12210 -0
  224. data/scarpe-components/assets/bootstrap-themes/bootstrap-darkly.css +12153 -0
  225. data/scarpe-components/assets/bootstrap-themes/bootstrap-flatly.css +12126 -0
  226. data/scarpe-components/assets/bootstrap-themes/bootstrap-icons.min.css +5 -0
  227. data/scarpe-components/assets/bootstrap-themes/bootstrap-journal.css +12099 -0
  228. data/scarpe-components/assets/bootstrap-themes/bootstrap-litera.css +12211 -0
  229. data/scarpe-components/assets/bootstrap-themes/bootstrap-lumen.css +12369 -0
  230. data/scarpe-components/assets/bootstrap-themes/bootstrap-lux.css +11928 -0
  231. data/scarpe-components/assets/bootstrap-themes/bootstrap-materia.css +13184 -0
  232. data/scarpe-components/assets/bootstrap-themes/bootstrap-minty.css +12177 -0
  233. data/scarpe-components/assets/bootstrap-themes/bootstrap-morph.css +12750 -0
  234. data/scarpe-components/assets/bootstrap-themes/bootstrap-pulse.css +11890 -0
  235. data/scarpe-components/assets/bootstrap-themes/bootstrap-quartz.css +12622 -0
  236. data/scarpe-components/assets/bootstrap-themes/bootstrap-sandstone.css +12201 -0
  237. data/scarpe-components/assets/bootstrap-themes/bootstrap-simplex.css +12186 -0
  238. data/scarpe-components/assets/bootstrap-themes/bootstrap-sketchy.css +12451 -0
  239. data/scarpe-components/assets/bootstrap-themes/bootstrap-slate.css +12492 -0
  240. data/scarpe-components/assets/bootstrap-themes/bootstrap-solar.css +12149 -0
  241. data/scarpe-components/assets/bootstrap-themes/bootstrap-spacelab.css +12266 -0
  242. data/scarpe-components/assets/bootstrap-themes/bootstrap-superhero.css +12216 -0
  243. data/scarpe-components/assets/bootstrap-themes/bootstrap-united.css +12077 -0
  244. data/scarpe-components/assets/bootstrap-themes/bootstrap-vapor.css +12549 -0
  245. data/scarpe-components/assets/bootstrap-themes/bootstrap-yeti.css +12325 -0
  246. data/scarpe-components/assets/bootstrap-themes/bootstrap-zephyr.css +12283 -0
  247. data/scarpe-components/assets/bootstrap-themes/bootstrap.bundle.min.js +7 -0
  248. data/scarpe-components/lib/scarpe/components/asset_server.rb +219 -0
  249. data/scarpe-components/lib/scarpe/components/base64.rb +22 -0
  250. data/scarpe-components/lib/scarpe/components/calzini/{art_widgets.rb → art_drawables.rb} +42 -18
  251. data/scarpe-components/lib/scarpe/components/calzini/border.rb +38 -0
  252. data/scarpe-components/lib/scarpe/components/calzini/button.rb +6 -8
  253. data/scarpe-components/lib/scarpe/components/calzini/misc.rb +14 -16
  254. data/scarpe-components/lib/scarpe/components/calzini/para.rb +218 -11
  255. data/scarpe-components/lib/scarpe/components/calzini/slots.rb +16 -60
  256. data/scarpe-components/lib/scarpe/components/calzini.rb +88 -1
  257. data/scarpe-components/lib/scarpe/components/errors.rb +4 -0
  258. data/scarpe-components/lib/scarpe/components/html.rb +4 -1
  259. data/scarpe-components/lib/scarpe/components/minitest_export_reporter.rb +11 -3
  260. data/scarpe-components/lib/scarpe/components/minitest_result.rb +41 -0
  261. data/scarpe-components/lib/scarpe/components/port_helpers.rb +30 -0
  262. data/scarpe-components/lib/scarpe/components/print_logger.rb +17 -2
  263. data/scarpe-components/lib/scarpe/components/process_helpers.rb +37 -0
  264. data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +1 -1
  265. data/scarpe-components/lib/scarpe/components/tiranti.rb +42 -100
  266. data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +3 -1
  267. data/scarpe-components/lib/scarpe/components/version.rb +1 -1
  268. data/scarpe-components/scarpe-components.gemspec +1 -1
  269. data/scarpe-components/test/assets/big-image.png +0 -0
  270. data/scarpe-components/test/assets/big-stylesheet.css +497 -0
  271. data/scarpe-components/test/assets/little-image.png +0 -0
  272. data/scarpe-components/test/assets/little-stylesheet.css +1 -0
  273. data/scarpe-components/test/calzini/test_calzini_art_drawables.rb +7 -7
  274. data/scarpe-components/test/calzini/test_calzini_button.rb +7 -5
  275. data/scarpe-components/test/calzini/test_calzini_misc.rb +9 -9
  276. data/scarpe-components/test/calzini/test_calzini_para.rb +6 -9
  277. data/scarpe-components/test/calzini/test_calzini_slots.rb +12 -57
  278. data/scarpe-components/test/calzini/test_calzini_text_drawables.rb +83 -18
  279. data/scarpe-components/test/calzini/test_various.rb +133 -0
  280. data/scarpe-components/test/test_asset_server.rb +72 -0
  281. data/scarpe-components/test/test_components.rb +31 -2
  282. data/scarpe-components/test/test_helper.rb +0 -1
  283. data/scarpe-components/test/test_minitest_result.rb +7 -0
  284. data/scarpe-components/test/test_port_helpers.rb +12 -0
  285. data/scarpe-components/test/test_segmented_app_files.rb +2 -0
  286. data/tasks/check_html_fixtures.rb +140 -0
  287. data/tasks/regenerate_html_fixtures.rb +104 -0
  288. data/templates/class_template_with_shapes.erb +0 -11
  289. metadata +225 -39
  290. data/.rubocop.yml +0 -88
  291. data/lacci/lib/scarpe/niente/logger.rb +0 -29
  292. data/lacci/lib/shoes/drawables/span.rb +0 -27
  293. data/lacci/lib/shoes/spacing.rb +0 -9
  294. data/lib/scarpe/evented_assertions.rb +0 -121
  295. data/lib/scarpe/wv/span.rb +0 -44
  296. data/scarpe-components/lib/scarpe/components/calzini/text_widgets.rb +0 -65
  297. /data/examples/legacy/{not_checked → working}/shoes3-tests/editline/editline.rb +0 -0
data/lacci/lib/shoes.rb CHANGED
@@ -7,49 +7,47 @@
7
7
  # to handle the DSL and command-line parts of Shoes without knowing anything about how the
8
8
  # display side works at all.
9
9
 
10
- if RUBY_VERSION[0..2] < "3.2"
11
- Shoes::Log.logger("Shoes").error("Lacci (Scarpe, Shoes) requires Ruby 3.2 or higher!")
10
+ if RUBY_VERSION[0..2] < '3.2'
11
+ Shoes::Log.logger('Shoes').error('Lacci (Scarpe, Shoes) requires Ruby 3.2 or higher!')
12
12
  exit(-1)
13
13
  end
14
14
 
15
15
  class Shoes; end
16
16
  class Shoes::Error < StandardError; end
17
- require_relative "shoes/errors"
17
+ require_relative 'shoes/errors'
18
18
 
19
- require_relative "shoes/constants"
20
- require_relative "shoes/ruby_extensions"
19
+ require_relative 'shoes/constants'
20
+ require_relative 'shoes/ruby_extensions'
21
21
 
22
22
  # Shoes adds some top-level methods and constants that can be used everywhere. Kernel is where they go.
23
23
  module Kernel
24
24
  include Shoes::Constants
25
25
  end
26
26
 
27
- require_relative "shoes/display_service"
27
+ require_relative 'shoes/display_service'
28
28
 
29
29
  # Pre-declare classes that get referenced outside their own require file
30
30
  class Shoes::Drawable < Shoes::Linkable; end
31
31
  class Shoes::Slot < Shoes::Drawable; end
32
32
  class Shoes::Widget < Shoes::Slot; end
33
33
 
34
- require_relative "shoes/log"
35
- require_relative "shoes/colors"
34
+ require_relative 'shoes/log'
35
+ require_relative 'shoes/colors'
36
36
 
37
- require_relative "shoes/builtins"
37
+ require_relative 'shoes/builtins'
38
38
 
39
- require_relative "shoes/background"
40
- require_relative "shoes/border"
41
- require_relative "shoes/spacing"
39
+ require_relative 'shoes/background'
42
40
 
43
- require_relative "shoes/drawable"
44
- require_relative "shoes/app"
45
- require_relative "shoes/drawables"
41
+ require_relative 'shoes/drawable'
42
+ require_relative 'shoes/app'
43
+ require_relative 'shoes/drawables'
46
44
 
47
- require_relative "shoes/download"
45
+ require_relative 'shoes/download'
48
46
 
49
47
  # No easy way to tell at this point whether
50
48
  # we will later load Shoes-Spec code, e.g.
51
49
  # by running a segmented app with test code.
52
- require_relative "shoes-spec"
50
+ require_relative 'shoes-spec'
53
51
 
54
52
  # The module containing Shoes in all its glory.
55
53
  # Shoes is a platform-independent GUI library, designed to create
@@ -57,6 +55,40 @@ require_relative "shoes-spec"
57
55
  #
58
56
  class Shoes
59
57
  class << self
58
+ attr_accessor :APPS
59
+
60
+ # Track the most recently defined Shoes subclass for the inheritance pattern
61
+ # e.g., class Book < Shoes; end; Shoes.app
62
+ attr_accessor :pending_app_class
63
+
64
+ # When someone does `class MyApp < Shoes`, track it
65
+ def inherited(subclass)
66
+ # Only track direct subclasses of Shoes, not Shoes::App, Shoes::Drawable, etc.
67
+ # Those have their own inheritance tracking
68
+ if self == ::Shoes
69
+ Shoes.pending_app_class = subclass
70
+ end
71
+ super
72
+ end
73
+
74
+ # Class-level url method for defining routes in Shoes subclasses
75
+ # e.g., class Book < Shoes; url '/', :index; end
76
+ def url(path, method_name)
77
+ @class_routes ||= {}
78
+ if path.is_a?(String) && path.include?('(')
79
+ # Convert string patterns like '/page/(\d+)' to regex
80
+ regex = Regexp.new("^#{path.gsub(/\(.*?\)/, '(.*?)')}$")
81
+ @class_routes[regex] = method_name
82
+ else
83
+ @class_routes[path] = method_name
84
+ end
85
+ end
86
+
87
+ # Get the routes defined on this class
88
+ def class_routes
89
+ @class_routes ||= {}
90
+ end
91
+
60
92
  # Creates a Shoes app with a new window. The block parameter is used to create
61
93
  # drawables and set up handlers. Arguments are passed to Shoes::App.new internally.
62
94
  #
@@ -76,16 +108,60 @@ class Shoes
76
108
  # @param width [Integer] The new app window width
77
109
  # @param height [Integer] The new app window height
78
110
  # @param resizable [Boolean] Whether the app window should be resizeable
111
+ # @param features [Symbol,Array<Symbol>] Additional Shoes extensions requested by the app
79
112
  # @return [void]
80
113
  # @see Shoes::App#new
81
114
  def app(
82
- title: "Shoes!",
115
+ title: 'Shoes!',
83
116
  width: 480,
84
117
  height: 420,
85
118
  resizable: true,
119
+ features: [],
86
120
  &app_code_body
87
121
  )
88
- app = Shoes::App.new(title:, width:, height:, resizable:, &app_code_body)
122
+ f = [features].flatten # Make sure this is a list, not a single symbol
123
+ app = Shoes::App.new(title:, width:, height:, resizable:, features: f, &app_code_body)
124
+
125
+ # If there's a pending Shoes subclass (e.g., class Book < Shoes), use it
126
+ if Shoes.pending_app_class
127
+ subclass = Shoes.pending_app_class
128
+ Shoes.pending_app_class = nil # Clear it so it doesn't affect future apps
129
+
130
+ # Include the subclass as a module to get its instance methods
131
+ # This works because we're extending the singleton class
132
+ methods_to_copy = subclass.instance_methods(false)
133
+
134
+ methods_to_copy.each do |method_name|
135
+ # Get source location and use eval to redefine - but that's fragile
136
+ # Instead, let's use a delegation pattern with the app as context
137
+
138
+ # Read the method's arity and create a proper wrapper
139
+ um = subclass.instance_method(method_name)
140
+
141
+ # Define a wrapper that will eval the original method body in app's context
142
+ # This is a bit hacky but works: we store the subclass and call via instance_eval
143
+ app.define_singleton_method(method_name) do |*args, &block|
144
+ # Create a temporary subclass instance that delegates to app for Shoes methods
145
+ temp = subclass.allocate
146
+ temp.instance_variable_set(:@__shoes_app__, self)
147
+
148
+ # Define method_missing on the temp to delegate Shoes DSL calls to the app
149
+ temp.define_singleton_method(:method_missing) do |name, *a, **kw, &b|
150
+ @__shoes_app__.send(name, *a, **kw, &b)
151
+ end
152
+ temp.define_singleton_method(:respond_to_missing?) { |*| true }
153
+
154
+ # Call the original method on temp (which delegates DSL calls to app)
155
+ temp.send(method_name, *args, &block)
156
+ end
157
+ end
158
+
159
+ # Copy routes from the subclass to the app
160
+ subclass.class_routes.each do |path, method_name|
161
+ app.url(path, method_name)
162
+ end
163
+ end
164
+
89
165
  app.init
90
166
  app.run
91
167
  nil
@@ -124,7 +200,7 @@ class Shoes
124
200
  proc do |path|
125
201
  load path
126
202
  true
127
- end,
203
+ end
128
204
  ]
129
205
  end
130
206
 
@@ -144,4 +220,6 @@ class Shoes
144
220
  @file_loaders = loaders
145
221
  end
146
222
  end
223
+
224
+ Shoes.APPS ||= []
147
225
  end
@@ -0,0 +1 @@
1
+ niente_test.json
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class TestDrawContext < NienteTest
6
+ # need tests for:
7
+ # default values vs draw context values
8
+ # setting value explicitly w/ positional or keyword arg
9
+
10
+ # Test w/ color string names for now. This reveals a
11
+ # problem - our highly-variable color handling makes it
12
+ # hard to be sure about what comes through in a style.
13
+
14
+ # We do a bunch of wacky stuff with colors. We need some kind of
15
+ # equality check. This is good enough for this test until we
16
+ # have a proper color object backing up what we do in Scarpe.
17
+ def assert_color_equal(c1, c2)
18
+ if c1.nil? && c2.nil?
19
+ raise "Is this an expected use case?"
20
+ end
21
+
22
+ if (c1.is_a?(String) || c1.is_a?(Symbol)) && (c2.is_a?(String) || c2.is_a?(Symbol))
23
+ return assert_equal c1.to_s, c2.to_s, "Expected color #{c1.inspect} to equal #{c2.inspect}"
24
+ end
25
+
26
+ if c1.is_a?(Array) && c2.is_a?(Array)
27
+ return assert_equal c1, c2, "Expected color #{c1.inspect} to equal #{c2.inspect}"
28
+ end
29
+
30
+ raise "Oopsie! We got an unexpected comparison between colors #{c1.inspect}"
31
+ end
32
+
33
+ def test_draw_context_default
34
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
35
+ Shoes.app do
36
+ oval 5, 10, 25 # circle with radius 25 with its upper-left point at 5, 10
37
+ end
38
+ SHOES_APP
39
+ ov = oval()
40
+ assert_equal "black", ov.style["fill"]
41
+ assert_equal "black", ov.style["stroke"]
42
+ SHOES_SPEC
43
+ end
44
+
45
+ def test_draw_context_basic
46
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
47
+ Shoes.app do
48
+ fill darkgreen
49
+ stroke blue
50
+ oval 5, 10, 25 # circle with radius 25 with its upper-left point at 5, 10
51
+ end
52
+ SHOES_APP
53
+ ov = oval()
54
+ assert_equal [0, 100, 0, 255], ov.style["fill"]
55
+ assert_equal [0, 0, 255, 255], ov.style["stroke"]
56
+ SHOES_SPEC
57
+ end
58
+
59
+ def test_draw_context_explicit
60
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
61
+ Shoes.app do
62
+ fill blue
63
+ stroke blue
64
+ oval 5, 10, 25, fill: darkgreen
65
+ end
66
+ SHOES_APP
67
+ ov = oval()
68
+ assert_equal [0, 100, 0, 255], ov.style["fill"]
69
+ assert_equal [0, 0, 255, 255], ov.style["stroke"]
70
+ SHOES_SPEC
71
+ end
72
+
73
+ def test_draw_context_basic_nofill
74
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
75
+ Shoes.app do
76
+ nofill
77
+ oval 5, 10, 25
78
+ end
79
+ SHOES_APP
80
+ ov = oval()
81
+ assert_equal [0, 0, 0, 0], ov.style["fill"]
82
+ SHOES_SPEC
83
+ end
84
+
85
+ def test_draw_context_inherited_nofill
86
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
87
+ Shoes.app do
88
+ nofill
89
+ nostroke
90
+ stack do
91
+ oval 5, 10, 25
92
+ end
93
+ end
94
+ SHOES_APP
95
+ ov = oval()
96
+ assert_equal [0, 0, 0, 0], ov.style["fill"]
97
+ assert_equal [0, 0, 0, 0], ov.style["stroke"]
98
+ SHOES_SPEC
99
+ end
100
+
101
+ def test_draw_context_inherited_no_sibling
102
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
103
+ Shoes.app do
104
+ fill yellow
105
+ stroke yellow
106
+ stack do
107
+ fill blue
108
+ stroke darkgreen
109
+ stack do
110
+ fill aquamarine
111
+ stroke aquamarine
112
+ end
113
+ stack do
114
+ oval 5, 10, 25
115
+ end
116
+ end
117
+ end
118
+ SHOES_APP
119
+ ov = oval()
120
+ assert_equal [0, 0, 255, 255], ov.style["fill"]
121
+ assert_equal [0, 100, 0, 255], ov.style["stroke"]
122
+ SHOES_SPEC
123
+ end
124
+
125
+ def test_draw_context_inherited_nil_props
126
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
127
+ Shoes.app do
128
+ stack do
129
+ fill blue
130
+ stroke darkgreen
131
+ stack do
132
+ fill nil
133
+ stroke nil
134
+ oval 5, 10, 25
135
+ end
136
+ end
137
+ end
138
+ SHOES_APP
139
+ ov = oval()
140
+ assert_equal [0, 0, 255, 255], ov.style["fill"]
141
+ assert_equal [0, 100, 0, 255], ov.style["stroke"]
142
+ SHOES_SPEC
143
+ end
144
+
145
+ def test_draw_context_inherited_cancel_default
146
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
147
+ Shoes.app do
148
+ stack do
149
+ fill blue
150
+ stroke darkgreen
151
+ stack do
152
+ fill yellow
153
+ stroke yellow
154
+ oval 5, 10, 25
155
+ fill nil
156
+ stroke nil
157
+ @o2 = oval 5, 10, 25
158
+ end
159
+ end
160
+ end
161
+ SHOES_APP
162
+ ov = oval("@o2")
163
+ assert_equal [0, 0, 255, 255], ov.style["fill"]
164
+ assert_equal [0, 100, 0, 255], ov.style["stroke"]
165
+ SHOES_SPEC
166
+ end
167
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+
6
+ class TestFontHelper < Minitest::Test
7
+ include FontHelper
8
+ def test_parse_full_font
9
+
10
+ string = "Pacifico 20px bold italic small-caps"
11
+
12
+ assert_equal(["italic", "small-caps" , "bold" , "20px" , "Pacifico"],parse_font(string))
13
+
14
+ end
15
+
16
+ def test_parse_quotesFamily
17
+ string = "'Times new roman', serif 20px bold italic small-caps"
18
+
19
+ assert_equal(["italic", "small-caps", "bold", "20px", "'Times new roman', serif"],parse_font(string))
20
+ end
21
+
22
+ def test_parse_empty
23
+ string = ""
24
+
25
+ assert_equal([nil,nil,nil,nil,""],parse_font(string))
26
+ end
27
+
28
+ def test_parse_onlyFamily
29
+ string = "arial"
30
+
31
+ assert_equal([nil,nil,nil,nil,"arial"],parse_font(string))
32
+ end
33
+
34
+ def test_parse_onlySize
35
+ string = "40px"
36
+
37
+ assert_equal([nil,nil,nil,"40px",""],parse_font(string))
38
+ end
39
+
40
+ def test_parse_onlyFontStyle
41
+ string = "italic"
42
+
43
+ assert_equal(["italic",nil,nil,nil,""],parse_font(string))
44
+ end
45
+
46
+ def test_parse_onlyFontVariant
47
+ string = "small-caps"
48
+
49
+ assert_equal([nil,"small-caps",nil,nil,""],parse_font(string))
50
+ end
51
+
52
+ def test_parse_onlyFontWeight
53
+ string = "900"
54
+
55
+ assert_equal([nil,nil,"900",nil,""],parse_font(string))
56
+ end
57
+ end
@@ -4,6 +4,7 @@ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
4
  require "shoes"
5
5
 
6
6
  require "scarpe/components/unit_test_helpers"
7
+ require "scarpe/components/minitest_result"
7
8
 
8
9
  require "minitest/autorun"
9
10
 
@@ -35,29 +36,55 @@ class NienteTest < Minitest::Test
35
36
  timeout: 5.0,
36
37
  class_name: self.class,
37
38
  method_name: self.name,
38
- display_service: "niente"
39
+ expect_process_fail: false,
40
+ expect_minitest_exception: false,
41
+ display_service: "niente",
42
+ log_level: "warn"
39
43
  )
44
+ sspec_file = File.expand_path(File.join __dir__, "niente_test.json")
45
+ File.unlink sspec_file rescue nil
46
+
40
47
  with_tempfiles([
41
48
  #["scarpe_log_config.json", JSON.dump(log_config_for_test)],
42
49
  [["shoes_spec_code", ".rb"], app_test_code],
43
50
  ]) do |shoes_spec_path,_|
44
51
  system(
45
52
  "LOCALAPPDATA=\"#{Dir.tmpdir}\" " +
53
+ "NIENTE_LOG_LEVEL=#{log_level} " +
46
54
  "SHOES_SPEC_TEST=\"#{shoes_spec_path}\" " +
47
55
  "SCARPE_DISPLAY_SERVICE=\"#{display_service}\" " +
48
- "SHOES_MINITEST_EXPORT_FILE=niente_test.json " +
56
+ "SHOES_MINITEST_EXPORT_FILE=#{sspec_file} " +
49
57
  "SHOES_MINITEST_CLASS_NAME=\"#{class_name}\" " +
50
58
  "SHOES_MINITEST_METHOD_NAME=\"#{method_name}\" " +
51
59
  "ruby #{SCARPE_EXE} --dev #{test_app_location}"
52
60
  )
53
61
  end
54
62
 
63
+ if expect_process_fail
64
+ assert(false, "Expected app to fail but it succeeded!") if $?.success?
65
+ return
66
+ end
67
+
55
68
  # Check if the process exited normally or crashed (segfault, failure, timeout)
56
69
  unless $?.success?
57
- assert(false, "Niente app failed with exit code: #{$?.exitstatus}")
70
+ assert(false, "App failed with exit code: #{$?.exitstatus}")
58
71
  return
59
72
  end
60
73
 
61
-
74
+ result = Scarpe::Components::MinitestResult.new(sspec_file)
75
+ if result.error?
76
+ if expect_minitest_exception
77
+ assert_equal true, true
78
+ else
79
+ raise result.error_message
80
+ end
81
+ elsif result.fail?
82
+ assert false, result.fail_message
83
+ elsif result.skip?
84
+ skip
85
+ else
86
+ # Count out the correct number of assertions
87
+ result.assertions.times { assert true }
88
+ end
62
89
  end
63
90
  end
@@ -5,14 +5,101 @@ require_relative "test_helper"
5
5
  class TestLacci < NienteTest
6
6
  def test_simple_button_click
7
7
  run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
8
- Shoes.app do
9
- @b = button "OK" do
10
- @b.text = "Yup"
8
+ Shoes.app do
9
+ @b = button "OK" do
10
+ @b.text = "Yup"
11
+ end
11
12
  end
12
- end
13
13
  SHOES_APP
14
- button().trigger_click
15
- assert_equal "Yup", button().text
14
+ button().trigger_click
15
+ assert_equal "Yup", button().text
16
+ SHOES_SPEC
17
+ end
18
+
19
+ def test_positional_default_values
20
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
21
+ Shoes.app do
22
+ star 15, 35
23
+ end
24
+ SHOES_APP
25
+ s = star()
26
+ assert_equal 10, s.points
27
+ assert_equal 50, s.inner
28
+ SHOES_SPEC
29
+ end
30
+
31
+ def test_positional_args
32
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
33
+ Shoes.app do
34
+ star 10, 25, 8 # Leave outer and inner as default
35
+ end
36
+ SHOES_APP
37
+ s = star()
38
+ assert_equal 10, s.left
39
+ assert_equal 25, s.top
40
+ assert_equal 8, s.points
41
+ assert_equal 50, s.inner
42
+ SHOES_SPEC
43
+ end
44
+
45
+ def test_keyword_args
46
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
47
+ Shoes.app do
48
+ star 5, 6, points: 8, inner: 30
49
+ end
50
+ SHOES_APP
51
+ s = star()
52
+ assert_equal 5, s.left
53
+ assert_equal 6, s.top
54
+ assert_equal 8, s.points
55
+ assert_equal 100, s.outer
56
+ assert_equal 30, s.inner
57
+ SHOES_SPEC
58
+ end
59
+
60
+ def test_too_many_positional_args
61
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
62
+ Shoes.app do
63
+ @s = stack {}
64
+ end
65
+ SHOES_APP
66
+ s = stack("@s")
67
+ assert_raises Shoes::Errors::BadArgumentListError do
68
+ s.star 5, 6, 7, 8, 9, 10, 11
69
+ end
70
+ SHOES_SPEC
71
+ end
72
+
73
+ def test_too_few_positional_args
74
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
75
+ Shoes.app do
76
+ @s = stack {}
77
+ end
78
+ SHOES_APP
79
+ s = stack("@s")
80
+ assert_raises Shoes::Errors::BadArgumentListError do
81
+ s.star 5
82
+ end
83
+ SHOES_SPEC
84
+ end
85
+
86
+ def test_unsupported_feature
87
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC, expect_process_fail: true)
88
+ Shoes.app(features: :html) do
89
+ para "Not supported by Niente, though."
90
+ end
91
+ SHOES_APP
92
+ assert true
93
+ SHOES_SPEC
94
+ end
95
+
96
+ def test_unknown_feature
97
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
98
+ Shoes.app(features: :squid) do
99
+ para "No such feature, though."
100
+ end
101
+ SHOES_APP
102
+ assert true
16
103
  SHOES_SPEC
17
104
  end
18
105
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lib/shoes/margin_helper"
4
+
5
+
6
+ class TestMarginHelper < Minitest::Test
7
+ include MarginHelper
8
+
9
+ def test_one_Number_margin
10
+
11
+ kwargs = {:margin => 20}
12
+
13
+ assert_equal({:margin => nil, :margin_left => 20, :margin_top => 20, :margin_right => 20, :margin_bottom => 20},margin_parse(kwargs))
14
+
15
+ end
16
+
17
+ def test_Array_four_margin
18
+
19
+ kwargs = {:margin => [20,20,30,40]}
20
+
21
+ assert_equal({:margin => nil, :margin_left => 20, :margin_top => 20, :margin_right => 30, :margin_bottom => 40},margin_parse(kwargs))
22
+
23
+ end
24
+
25
+ def test_Array_one_margin
26
+
27
+ kwargs = {:margin => [20]}
28
+
29
+ assert_equal({:margin => nil, :margin_left => 20, :margin_top => 20, :margin_right => 20, :margin_bottom => 20},margin_parse(kwargs))
30
+
31
+ end
32
+
33
+
34
+ def test_String_four_margin
35
+
36
+ kwargs = {:margin => "20 30 40 50"}
37
+
38
+ assert_equal({:margin => nil, :margin_left => "20", :margin_top => "30", :margin_right => "40", :margin_bottom => "50"},margin_parse(kwargs))
39
+
40
+ end
41
+
42
+ def test_String_one_margin
43
+
44
+ kwargs = {:margin => "20"}
45
+
46
+ assert_equal({:margin => nil, :margin_left => "20", :margin_top => "20", :margin_right => "20", :margin_bottom => "20"},margin_parse(kwargs))
47
+
48
+ end
49
+
50
+ def test_Hash_four_margin
51
+
52
+ kwargs = {:margin => {left:20,top:20,right:30,bottom:30}}
53
+
54
+ assert_equal({:margin => nil, :margin_left => 20, :margin_top => 20, :margin_right => 30, :margin_bottom => 30},margin_parse(kwargs))
55
+
56
+ end
57
+
58
+ def test_Hash_three_margin
59
+
60
+ kwargs = {:margin => {top:20,right:30,bottom:30}}
61
+
62
+ assert_equal({:margin => nil, :margin_top => 20, :margin_right => 30, :margin_bottom => 30},margin_parse(kwargs))
63
+
64
+ end
65
+
66
+ def test_Hash_two_margin
67
+
68
+ kwargs = {:margin => {left:20,top:20}}
69
+
70
+ assert_equal({:margin => nil, :margin_left => 20, :margin_top => 20},margin_parse(kwargs))
71
+
72
+ end
73
+
74
+ def test_Hash_one_margin
75
+
76
+ kwargs = {:margin => {bottom:30}}
77
+
78
+ assert_equal({:margin => nil, :margin_bottom => 30},margin_parse(kwargs))
79
+
80
+ end
81
+
82
+ end