scriptorium 0.0.3 → 0.7.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.
Files changed (353) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +324 -0
  3. data/README.md +3155 -1
  4. data/assets/.DS_Store +0 -0
  5. data/assets/README.md +44 -0
  6. data/assets/icons/social/reddit.png +0 -0
  7. data/assets/icons/social/x-logo.png +0 -0
  8. data/assets/icons/ui/.DS_Store +0 -0
  9. data/assets/icons/ui/back.png +0 -0
  10. data/assets/icons/ui/copy.png +0 -0
  11. data/assets/icons/ui/down.png +0 -0
  12. data/assets/icons/ui/end.png +0 -0
  13. data/assets/icons/ui/exit.png +0 -0
  14. data/assets/icons/ui/foo +10 -0
  15. data/assets/icons/ui/home.png +0 -0
  16. data/assets/icons/ui/left.png +0 -0
  17. data/assets/icons/ui/next.png +0 -0
  18. data/assets/icons/ui/right.png +0 -0
  19. data/assets/icons/ui/start.png +0 -0
  20. data/assets/icons/ui/up.png +0 -0
  21. data/assets/imagenotfound.jpg +0 -0
  22. data/assets/samples/placeholder.svg +9 -0
  23. data/assets/themes/standard/favicon.svg +6 -0
  24. data/bin/sblog +84 -5
  25. data/bin/scriptorium +1 -0
  26. data/doc/README.txt +6 -0
  27. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +94 -0
  28. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +2 -0
  29. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +45 -0
  30. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
  31. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
  32. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
  33. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +35 -0
  34. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +27 -0
  35. data/doc/anti-amnesia/20250807-213025.md +116 -0
  36. data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
  37. data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
  38. data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
  39. data/doc/banner_svg_config.md +114 -0
  40. data/doc/contrib.lt3 +8 -0
  41. data/doc/dependencies.md +281 -0
  42. data/doc/hacker.lt3 +5 -0
  43. data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
  44. data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
  45. data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
  46. data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
  47. data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
  48. data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
  49. data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
  50. data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
  51. data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
  52. data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
  53. data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
  54. data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
  55. data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
  56. data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
  57. data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
  58. data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
  59. data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
  60. data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
  61. data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
  62. data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
  63. data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
  64. data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
  65. data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
  66. data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
  67. data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
  68. data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
  69. data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
  70. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
  71. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
  72. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
  73. data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
  74. data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
  75. data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
  76. data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
  77. data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
  78. data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
  79. data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
  80. data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
  81. data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
  82. data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
  83. data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
  84. data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
  85. data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
  86. data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
  87. data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
  88. data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
  89. data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
  90. data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
  91. data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
  92. data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
  93. data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
  94. data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
  95. data/doc/imported/0018-ruby-puns/post.html +46 -0
  96. data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
  97. data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
  98. data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
  99. data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
  100. data/doc/imported/0020-fffff/metadata.txt +7 -0
  101. data/doc/imported/0020-fffff/post.html +24 -0
  102. data/doc/imported/0020-fffff/source.lt3 +12 -0
  103. data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
  104. data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
  105. data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
  106. data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
  107. data/doc/imported/0023-doctor-sleep/post.html +63 -0
  108. data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
  109. data/doc/imported/0024-just-a-test/metadata.txt +7 -0
  110. data/doc/imported/0024-just-a-test/post.html +24 -0
  111. data/doc/imported/0024-just-a-test/source.lt3 +12 -0
  112. data/doc/imported/import_summary.txt +98 -0
  113. data/doc/livetext-informal-spec.txt +65 -0
  114. data/doc/myuserdoc/ch-0.lt3 +31 -0
  115. data/doc/myuserdoc/ch-1.lt3 +37 -0
  116. data/doc/myuserdoc/ch-10.lt3 +22 -0
  117. data/doc/myuserdoc/ch-2.lt3 +37 -0
  118. data/doc/myuserdoc/ch-3.lt3 +19 -0
  119. data/doc/myuserdoc/ch-4.lt3 +43 -0
  120. data/doc/myuserdoc/ch-5.lt3 +22 -0
  121. data/doc/myuserdoc/ch-6.lt3 +19 -0
  122. data/doc/myuserdoc/ch-7.lt3 +16 -0
  123. data/doc/myuserdoc/ch-8.lt3 +13 -0
  124. data/doc/myuserdoc/ch-9.lt3 +19 -0
  125. data/doc/myuserdoc/tweak.rb +18 -0
  126. data/doc/myuserdoc/userdoc-toc.txt +88 -0
  127. data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
  128. data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
  129. data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
  130. data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
  131. data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
  132. data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
  133. data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
  134. data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
  135. data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
  136. data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
  137. data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
  138. data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
  139. data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
  140. data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
  141. data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
  142. data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
  143. data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
  144. data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
  145. data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
  146. data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
  147. data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
  148. data/doc/old-posts/0024-just-a-test.lt3 +12 -0
  149. data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
  150. data/doc/old-repo +1 -0
  151. data/doc/reddit_credentials_template.json +8 -0
  152. data/doc/reddit_integration.md +207 -0
  153. data/doc/user.lt3 +35 -0
  154. data/doc/user_guide_section_1.md +137 -0
  155. data/doc/user_guide_section_10.md +515 -0
  156. data/doc/user_guide_section_11.md +708 -0
  157. data/doc/user_guide_section_2.md +233 -0
  158. data/doc/user_guide_section_3.md +5 -0
  159. data/doc/user_guide_section_4.md +221 -0
  160. data/doc/user_guide_section_5.md +243 -0
  161. data/doc/user_guide_section_6.md +147 -0
  162. data/doc/user_guide_section_7.md +311 -0
  163. data/doc/user_guide_section_8.md +224 -0
  164. data/doc/user_guide_section_9.md +375 -0
  165. data/lib/rouge/lexers/livetext.rb +74 -0
  166. data/lib/scriptorium/api.rb +2373 -0
  167. data/lib/scriptorium/banner_svg.rb +729 -0
  168. data/lib/scriptorium/contract.rb +34 -0
  169. data/lib/scriptorium/exceptions.rb +201 -1
  170. data/lib/scriptorium/helpers.rb +675 -0
  171. data/lib/scriptorium/post.rb +259 -0
  172. data/lib/scriptorium/reddit.rb +83 -0
  173. data/lib/scriptorium/repo.rb +938 -0
  174. data/lib/scriptorium/standard_files.rb +149 -0
  175. data/lib/scriptorium/support/bootstrap/css.txt +5 -0
  176. data/lib/scriptorium/support/bootstrap/js.txt +4 -0
  177. data/lib/scriptorium/support/common_js/clipboard.js +35 -0
  178. data/lib/scriptorium/support/common_js/content-loader.js +187 -0
  179. data/lib/scriptorium/support/common_js/navigation.js +52 -0
  180. data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
  181. data/lib/scriptorium/support/config/reddit.txt +10 -0
  182. data/lib/scriptorium/support/config/reddit_template.txt +17 -0
  183. data/lib/scriptorium/support/config/social.txt +8 -0
  184. data/lib/scriptorium/support/highlight/css.txt +2 -0
  185. data/lib/scriptorium/support/highlight/custom.css +119 -0
  186. data/lib/scriptorium/support/highlight/js.txt +1 -0
  187. data/lib/scriptorium/support/post_index/config.txt +15 -0
  188. data/lib/scriptorium/support/post_index/style.css +55 -0
  189. data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
  190. data/lib/scriptorium/support/templates/initial_post.lt3 +12 -0
  191. data/lib/scriptorium/support/templates/layout.txt +5 -0
  192. data/lib/scriptorium/support/templates/post.lt3 +104 -0
  193. data/lib/scriptorium/support/theme/footer.lt3 +2 -0
  194. data/lib/scriptorium/support/theme/header.lt3 +4 -0
  195. data/lib/scriptorium/support/theme/left.lt3 +3 -0
  196. data/lib/scriptorium/support/theme/main.lt3 +5 -0
  197. data/lib/scriptorium/support/theme/right.lt3 +3 -0
  198. data/lib/scriptorium/theme.rb +192 -0
  199. data/lib/scriptorium/version.rb +1 -1
  200. data/lib/scriptorium/view.rb +1021 -0
  201. data/lib/scriptorium/widgets/featured_posts.rb +149 -0
  202. data/lib/scriptorium/widgets/links.rb +112 -0
  203. data/lib/scriptorium/widgets/pages.rb +133 -0
  204. data/lib/scriptorium/widgets/widget.rb +133 -0
  205. data/lib/scriptorium.rb +38 -34
  206. data/lib/skeleton.rb +10 -1
  207. data/scriptorium.gemspec +17 -5
  208. data/test/README.md +69 -0
  209. data/test/WEB_INTEGRATION_README.md +196 -0
  210. data/test/all +83 -0
  211. data/test/api_demo.rb +99 -0
  212. data/test/assets/imagenotfound.jpg +0 -0
  213. data/test/assets/images/.DS_Store +0 -0
  214. data/test/assets/images/README.md +27 -0
  215. data/test/assets/images/odd_aspect.png +0 -0
  216. data/test/assets/images/perfect.png +0 -0
  217. data/test/assets/images/small.png +0 -0
  218. data/test/assets/images/tall.png +0 -0
  219. data/test/assets/images/very_tall.png +0 -0
  220. data/test/assets/images/very_wide.png +0 -0
  221. data/test/assets/images/wide.png +0 -0
  222. data/test/assets/testbanner.jpg +0 -0
  223. data/test/banner_svg/simple_helpers.rb +13 -0
  224. data/test/banner_svg/unit.rb +1000 -0
  225. data/test/config/deployment.txt +5 -0
  226. data/test/ed_test.rb +204 -0
  227. data/test/integration/cursor_banner_combinations.rb +193 -0
  228. data/test/integration/cursor_banner_features.rb +374 -0
  229. data/test/integration/integration_test.rb +326 -0
  230. data/test/integration/preview_flow_test.rb +94 -0
  231. data/test/livetext_plugin_test.rb +500 -0
  232. data/test/manual/asset_mgmt.rb +67 -0
  233. data/test/manual/banner-tests/index.html +45 -0
  234. data/test/manual/banner-tests/svg.txt +3 -0
  235. data/test/manual/banner-tests/test01.html +122 -0
  236. data/test/manual/banner-tests/test02.html +122 -0
  237. data/test/manual/banner-tests/test03.html +122 -0
  238. data/test/manual/banner-tests/test04.html +129 -0
  239. data/test/manual/banner-tests/test05.html +129 -0
  240. data/test/manual/banner-tests/test06.html +129 -0
  241. data/test/manual/banner-tests/test07.html +129 -0
  242. data/test/manual/banner-tests/test08.html +123 -0
  243. data/test/manual/banner-tests/test09.html +123 -0
  244. data/test/manual/banner-tests/test10.html +123 -0
  245. data/test/manual/banner-tests/test11.html +123 -0
  246. data/test/manual/banner-tests/test12.html +123 -0
  247. data/test/manual/banner-tests/test13.html +123 -0
  248. data/test/manual/banner-tests/test14.html +123 -0
  249. data/test/manual/banner-tests/test15.html +122 -0
  250. data/test/manual/banner-tests/test16.html +122 -0
  251. data/test/manual/banner-tests/test17.html +122 -0
  252. data/test/manual/banner-tests/test18.html +132 -0
  253. data/test/manual/banner-tests/test19.html +132 -0
  254. data/test/manual/banner-tests/test20.html +132 -0
  255. data/test/manual/banner-tests/test21.html +132 -0
  256. data/test/manual/banner-tests/test22.html +132 -0
  257. data/test/manual/banner-tests/test23.html +132 -0
  258. data/test/manual/banner-tests/test24.html +132 -0
  259. data/test/manual/banner-tests/test25.html +131 -0
  260. data/test/manual/banner_environment.rb +205 -0
  261. data/test/manual/codemirror_demo.html +773 -0
  262. data/test/manual/create_posts_for_web.rb +114 -0
  263. data/test/manual/environment.rb +67 -0
  264. data/test/manual/make_banner.rb +153 -0
  265. data/test/manual/preview_manual_test.rb +129 -0
  266. data/test/manual/sample_banner_config.txt +12 -0
  267. data/test/manual/test_advanced_widgets.rb +73 -0
  268. data/test/manual/test_banner_combinations.rb +120 -0
  269. data/test/manual/test_banner_features.rb +306 -0
  270. data/test/manual/test_banner_integration.rb +115 -0
  271. data/test/manual/test_banner_radial.rb +87 -0
  272. data/test/manual/test_basic_posts.rb +47 -0
  273. data/test/manual/test_layout_widgets.rb +40 -0
  274. data/test/manual/test_pagination.rb +24 -0
  275. data/test/manual/test_random_posts.rb +38 -0
  276. data/test/manual/test_syntax_highlighting.rb +167 -0
  277. data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
  278. data/test/rubytext/rubytext_demo_test.rb +42 -0
  279. data/test/rubytext/rubytext_testing_guide.md +277 -0
  280. data/test/run_automated_tests.rb +45 -0
  281. data/test/staging/.DS_Store +0 -0
  282. data/test/support/preview_utils.rb +88 -0
  283. data/test/syntax_highlighting_test.lt3 +124 -0
  284. data/test/test_gem_assets.rb +48 -0
  285. data/test/test_helpers.rb +240 -0
  286. data/test/tui_editor_integration_test.rb +296 -0
  287. data/test/tui_integration_test.rb +883 -0
  288. data/test/unit/api.rb +1776 -0
  289. data/test/unit/asset_management.rb +219 -0
  290. data/test/unit/backup_test.rb +451 -0
  291. data/test/unit/clipboard_test.rb +60 -0
  292. data/test/unit/contract_test.rb +69 -0
  293. data/test/unit/core.rb +1211 -0
  294. data/test/unit/deploy_config_test.rb +248 -0
  295. data/test/unit/deploy_test.rb +478 -0
  296. data/test/unit/edit_post_test.rb +168 -0
  297. data/test/unit/gem_asset_management.rb +183 -0
  298. data/test/unit/livetext_basic.rb +57 -0
  299. data/test/unit/livetext_compatibility.rb +82 -0
  300. data/test/unit/parse_cmd_test.rb +260 -0
  301. data/test/unit/permalink_copy_test.rb +211 -0
  302. data/test/unit/post.rb +309 -0
  303. data/test/unit/post_index_config_test.rb +258 -0
  304. data/test/unit/post_state_helpers_test.rb +137 -0
  305. data/test/unit/read_commented_file_test.rb +278 -0
  306. data/test/unit/reddit_test.rb +235 -0
  307. data/test/unit/repo.rb +569 -0
  308. data/test/unit/social_test.rb +366 -0
  309. data/test/unit/syntax_highlighting.rb +70 -0
  310. data/test/unit/theme_management_test.rb +91 -0
  311. data/test/unit/view.rb +498 -0
  312. data/test/unit/widgets.rb +669 -0
  313. data/test/web_integration_test.rb +231 -0
  314. data/test/web_test_helper.rb +218 -0
  315. data/test/web_workflow_test.rb +527 -0
  316. data/test/wizard_test.rb +123 -0
  317. data/ui/README.md +67 -0
  318. data/ui/common/lib/ui_common.rb +8 -0
  319. data/ui/rubytext/README.md +191 -0
  320. data/ui/rubytext/bin/scriptorium-rubytext +402 -0
  321. data/ui/rubytext/lib/rubytext_ui.rb +300 -0
  322. data/ui/tui/bin/scriptorium +1890 -0
  323. data/ui/tui/test/tui_test.rb +23 -0
  324. data/ui/web/app/app.rb +2600 -0
  325. data/ui/web/app/assets/livetext_mode.js +244 -0
  326. data/ui/web/app/error_helpers.rb +150 -0
  327. data/ui/web/app/views/advanced_config.erb +196 -0
  328. data/ui/web/app/views/asset_management.erb +645 -0
  329. data/ui/web/app/views/backup_management.erb +238 -0
  330. data/ui/web/app/views/banner_config.erb +200 -0
  331. data/ui/web/app/views/config_widget.erb +232 -0
  332. data/ui/web/app/views/configure_view.erb +401 -0
  333. data/ui/web/app/views/dashboard.erb +154 -0
  334. data/ui/web/app/views/deploy_config.erb +149 -0
  335. data/ui/web/app/views/edit_pages.erb +363 -0
  336. data/ui/web/app/views/edit_post.erb +175 -0
  337. data/ui/web/app/views/edit_theme.erb +73 -0
  338. data/ui/web/app/views/edit_theme_file.erb +74 -0
  339. data/ui/web/app/views/error_page.erb +29 -0
  340. data/ui/web/app/views/header_config.erb +155 -0
  341. data/ui/web/app/views/layout_config.erb +147 -0
  342. data/ui/web/app/views/navbar_config.erb +411 -0
  343. data/ui/web/app/views/theme_management.erb +130 -0
  344. data/ui/web/app/views/view_dashboard.erb +779 -0
  345. data/ui/web/app/views/widgets.erb +249 -0
  346. data/ui/web/bin/scriptorium-web +164 -0
  347. data/ui/web/test/web_basic_test.rb +38 -0
  348. data/ui/web/test_navbar.txt +7 -0
  349. data/ui/web/tmp/timing.log +17 -0
  350. data/ui/web/tmp/web_server.log +0 -0
  351. metadata +434 -8
  352. data/lib/scriptorium/engine.rb +0 -22
  353. data/test/engine/unit.rb +0 -44
@@ -0,0 +1,211 @@
1
+ # Test file for permalink copy functionality
2
+ # Tests the clean URL copy generation for posts
3
+
4
+ require 'minitest/autorun'
5
+ require_relative '../../lib/scriptorium'
6
+ require_relative '../test_helpers'
7
+ require 'fileutils'
8
+
9
+ class TestPermalinkCopy < Minitest::Test
10
+ include Scriptorium::Helpers
11
+ include TestHelpers
12
+
13
+ def setup
14
+ @test_dir = "test/symlink_test_files"
15
+ FileUtils.mkdir_p(@test_dir)
16
+ Scriptorium::Repo.testing = true
17
+
18
+ # Create test repository
19
+ @repo = Scriptorium::Repo.create("test/scriptorium-TEST", testmode: true)
20
+ @view = @repo.create_view("test_view", "Test View", "Test Subtitle")
21
+ end
22
+
23
+ def teardown
24
+ FileUtils.rm_rf(@test_dir) if File.exist?(@test_dir)
25
+ FileUtils.rm_rf("test/scriptorium-TEST") if File.exist?("test/scriptorium-TEST")
26
+ end
27
+
28
+ # ========================================
29
+ # clean_slugify function tests
30
+ # ========================================
31
+
32
+ def test_001_clean_slugify_basic
33
+ result = clean_slugify("My Test Post")
34
+ assert_equal "my-test-post", result
35
+ end
36
+
37
+ def test_002_clean_slugify_with_special_characters
38
+ result = clean_slugify("Post with & < > \" ' characters!")
39
+ assert_equal "post-with-characters", result
40
+ end
41
+
42
+ def test_003_clean_slugify_with_underscores_and_hyphens
43
+ result = clean_slugify("Post with_underscores-and-hyphens")
44
+ assert_equal "post-with-underscores-and-hyphens", result
45
+ end
46
+
47
+ def test_004_clean_slugify_with_multiple_spaces
48
+ result = clean_slugify("Post with multiple spaces")
49
+ assert_equal "post-with-multiple-spaces", result
50
+ end
51
+
52
+ def test_005_clean_slugify_with_leading_trailing_hyphens
53
+ result = clean_slugify("-Post with leading/trailing hyphens-")
54
+ assert_equal "post-with-leadingtrailing-hyphens", result
55
+ end
56
+
57
+ # ========================================
58
+ # Permalink copy generation tests
59
+ # ========================================
60
+
61
+ def test_006_permalink_copy_creation_for_post
62
+ # Create a test post
63
+ post_title = "My First Test Post"
64
+ post_body = "This is the body of my test post."
65
+
66
+ # Create draft and finish it
67
+ draft_name = @repo.create_draft(title: post_title, body: post_body)
68
+ post_num = @repo.finish_draft(draft_name)
69
+
70
+ # Generate the post
71
+ @repo.generate_post(post_num)
72
+
73
+ # Check that both files exist
74
+ numbered_slug = slugify(post_num, post_title) + ".html"
75
+ clean_slug = clean_slugify(post_title) + ".html"
76
+
77
+ numbered_path = @view.dir/:output/:permalink/numbered_slug
78
+ clean_symlink_path = @view.dir/:output/:permalink/clean_slug
79
+
80
+ # Both files should exist
81
+ assert File.exist?(numbered_path), "Numbered post file should exist: #{numbered_path}"
82
+ assert File.exist?(clean_symlink_path), "Clean symlink should exist: #{clean_symlink_path}"
83
+
84
+ # Clean copy should exist
85
+ assert File.exist?(clean_symlink_path), "Clean copy should exist"
86
+
87
+ # Copy should have same content as numbered file
88
+ copy_content = File.read(clean_symlink_path)
89
+ numbered_content = File.read(numbered_path)
90
+ assert_equal numbered_content, copy_content, "Copy should have same content as numbered file"
91
+ end
92
+
93
+ def test_007_permalink_copy_has_correct_content
94
+ # Create a test post with a specific title
95
+ post_title = "Another Test Post"
96
+ post_body = "This is another test post."
97
+
98
+ # Create draft and finish it
99
+ draft_name = @repo.create_draft(title: post_title, body: post_body)
100
+ post_num = @repo.finish_draft(draft_name)
101
+
102
+ # Generate the post
103
+ @repo.generate_post(post_num)
104
+
105
+ # Check symlink target
106
+ clean_slug = clean_slugify(post_title) + ".html"
107
+ clean_symlink_path = @view.dir/:output/:permalink/clean_slug
108
+
109
+ assert File.exist?(clean_symlink_path), "Should be a copy"
110
+
111
+ # Get the actual content to verify it's a copy
112
+ copy_content = File.read(clean_symlink_path)
113
+ target_content = File.read(@view.dir/:output/:permalink/slugify(post_num, post_title) + ".html")
114
+
115
+ # Expected content should match the numbered file
116
+ assert_equal target_content, copy_content, "Copy should have same content as numbered file"
117
+ end
118
+
119
+ def test_008_permalink_copy_overwrites_existing
120
+ # Create a test post
121
+ post_title = "Duplicate Test Post"
122
+ post_body = "This is a test post."
123
+
124
+ # Create draft and finish it
125
+ draft_name = @repo.create_draft(title: post_title, body: post_body)
126
+ post_num = @repo.finish_draft(draft_name)
127
+
128
+ # Generate the post (should create copy)
129
+ @repo.generate_post(post_num)
130
+
131
+ # Check that copy exists
132
+ clean_slug = clean_slugify(post_title) + ".html"
133
+ clean_symlink_path = @view.dir/:output/:permalink/clean_slug
134
+
135
+ assert File.exist?(clean_symlink_path), "Copy should exist"
136
+
137
+ # Get the original content
138
+ original_content = File.read(clean_symlink_path)
139
+
140
+ # Now create another post with the same clean slug (different numbered slug)
141
+ post_title2 = "Duplicate Test Post" # Same title, different post
142
+ post_body2 = "This is another test post with same title."
143
+
144
+ draft_name2 = @repo.create_draft(title: post_title2, body: post_body2)
145
+ post_num2 = @repo.finish_draft(draft_name2)
146
+
147
+ # Generate the second post (should overwrite copy)
148
+ @repo.generate_post(post_num2)
149
+
150
+ # Check that copy still exists and has new content
151
+ assert File.exist?(clean_symlink_path), "Copy should still exist"
152
+
153
+ new_content = File.read(clean_symlink_path)
154
+ expected_content = File.read(@view.dir/:output/:permalink/slugify(post_num2, post_title2) + ".html")
155
+
156
+ assert_equal expected_content, new_content, "Copy should have new content"
157
+ refute_equal original_content, new_content, "Copy should have been updated"
158
+ end
159
+
160
+ def test_009_permalink_copy_with_special_characters_in_title
161
+ # Test with a title that has special characters
162
+ post_title = "Post with Special Characters: & < > \" ' !"
163
+ post_body = "This post has special characters in the title."
164
+
165
+ # Create draft and finish it
166
+ draft_name = @repo.create_draft(title: post_title, body: post_body)
167
+ post_num = @repo.finish_draft(draft_name)
168
+
169
+ # Generate the post
170
+ @repo.generate_post(post_num)
171
+
172
+ # Check that copy exists with cleaned slug
173
+ clean_slug = clean_slugify(post_title) + ".html"
174
+ clean_symlink_path = @view.dir/:output/:permalink/clean_slug
175
+
176
+ assert File.exist?(clean_symlink_path), "Copy should exist for cleaned title"
177
+
178
+ # Check that the copy has the same content as the numbered file
179
+ copy_content = File.read(clean_symlink_path)
180
+ expected_content = File.read(@view.dir/:output/:permalink/slugify(post_num, post_title) + ".html")
181
+ assert_equal expected_content, copy_content, "Copy should have same content as numbered file"
182
+ end
183
+
184
+ def test_010_permalink_copy_deployment_ready
185
+ # Test that permalink copies are created in the correct location for deployment
186
+ post_title = "Deployment Test Post"
187
+ post_body = "This post tests deployment readiness."
188
+
189
+ # Create draft and finish it
190
+ draft_name = @repo.create_draft(title: post_title, body: post_body)
191
+ post_num = @repo.finish_draft(draft_name)
192
+
193
+ # Generate the post
194
+ @repo.generate_post(post_num)
195
+
196
+ # Check that copy is in the correct deployment location
197
+ clean_slug = clean_slugify(post_title) + ".html"
198
+ clean_symlink_path = @view.dir/:output/:permalink/clean_slug
199
+
200
+ # Verify the path structure
201
+ assert_equal @view.dir/:output/:permalink/clean_slug, clean_symlink_path
202
+ assert File.exist?(clean_symlink_path), "Copy should exist in deployment location"
203
+
204
+ # Verify it's a copy (not a symlink)
205
+ assert !File.symlink?(clean_symlink_path), "Should not be a symlink"
206
+
207
+ # Verify the copy has content
208
+ copy_content = File.read(clean_symlink_path)
209
+ assert copy_content.length > 0, "Copy should have content"
210
+ end
211
+ end
data/test/unit/post.rb ADDED
@@ -0,0 +1,309 @@
1
+ # test/unit/post.rb
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../../lib/scriptorium'
5
+ require_relative '../test_helpers'
6
+ require_relative '../../lib/scriptorium/post'
7
+
8
+ class TestScriptoriumPost < Minitest::Test
9
+ include Scriptorium::Exceptions
10
+ include Scriptorium::Helpers
11
+
12
+ def setup
13
+ @test_dir = "test/scriptorium-TEST"
14
+ # Clean up any existing test repository
15
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
16
+
17
+ # Create API and use it to create repo
18
+ @api = Scriptorium::API.new(testmode: true)
19
+ @api.create_repo(@test_dir)
20
+
21
+ # Keep @repo available for constructor tests
22
+ @repo = @api.repo
23
+
24
+ # Create a basic Post object for constructor tests (no directories yet)
25
+ @post = Scriptorium::Post.new(@repo, 1)
26
+ end
27
+
28
+ # Helper method for tests that need a real post with directories
29
+ def create_real_post
30
+ @api.create_view("test_view", "Test View", "A test view")
31
+ @api.create_post("Test Post", "Test body", views: "test_view")
32
+ end
33
+
34
+ def teardown
35
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
36
+ Scriptorium::Repo.destroy if Scriptorium::Repo.testing
37
+ end
38
+
39
+ # Constructor validation tests
40
+ def test_001_initialize_with_valid_params
41
+ post = Scriptorium::Post.new(@repo, 123)
42
+ assert_equal @repo, post.repo
43
+ assert_equal "0123", post.num
44
+ end
45
+
46
+ def test_002_initialize_with_nil_repo
47
+ assert_raises(PostRepoNil) do
48
+ Scriptorium::Post.new(nil, 1)
49
+ end
50
+ end
51
+
52
+ def test_003_initialize_with_nil_num
53
+ assert_raises(PostNumNil) do
54
+ Scriptorium::Post.new(@repo, nil)
55
+ end
56
+ end
57
+
58
+ def test_004_initialize_with_empty_num
59
+ assert_raises(PostNumEmpty) do
60
+ Scriptorium::Post.new(@repo, "")
61
+ end
62
+ end
63
+
64
+ def test_005_initialize_with_whitespace_num
65
+ assert_raises(PostNumEmpty) do
66
+ Scriptorium::Post.new(@repo, " ")
67
+ end
68
+ end
69
+
70
+ def test_006_initialize_with_invalid_num_format
71
+ assert_raises(PostNumInvalid) do
72
+ Scriptorium::Post.new(@repo, "abc")
73
+ end
74
+ end
75
+
76
+ def test_007_initialize_with_negative_num
77
+ assert_raises(PostNumInvalid) do
78
+ Scriptorium::Post.new(@repo, -1)
79
+ end
80
+ end
81
+
82
+ # Basic property tests
83
+ def test_008_dir
84
+ create_real_post
85
+ expected_dir = @repo.root/:posts/"0001"
86
+ assert_equal expected_dir, @post.dir
87
+ end
88
+
89
+ def test_009_meta_file
90
+ create_real_post
91
+ expected_file = @repo.root/:posts/"0001"/"meta.txt"
92
+ assert_equal expected_file, @post.meta_file
93
+ end
94
+
95
+ def test_010_id
96
+ create_real_post
97
+ assert_equal 1, @post.id
98
+ end
99
+
100
+ def test_011_num_with_leading_zeros
101
+ post = Scriptorium::Post.new(@repo, 1)
102
+ assert_equal "0001", post.num
103
+ end
104
+
105
+ def test_012_num_without_leading_zeros
106
+ post = Scriptorium::Post.new(@repo, 123)
107
+ assert_equal "0123", post.num
108
+ end
109
+
110
+ def test_013_num_with_existing_zeros
111
+ post = Scriptorium::Post.new(@repo, 0001)
112
+ assert_equal "0001", post.num
113
+ end
114
+
115
+
116
+
117
+ # Metadata tests
118
+ def test_014_meta_without_file
119
+ # Create a real post for this test
120
+ create_real_post
121
+ # Clear the metadata file to test empty case
122
+ File.delete(@post.meta_file) if File.exist?(@post.meta_file)
123
+ # When meta file doesn't exist, should return empty hash
124
+ assert_equal({}, @post.meta)
125
+ end
126
+
127
+ def test_015_meta_with_file
128
+ # Create a real post for this test
129
+ create_real_post
130
+ # Create a meta file
131
+ meta_content = "post.title Test Post\npost.blurb Test blurb\npost.slug test-post\npost.pubdate 2023-01-01 12:00:00"
132
+ write_file(@post.meta_file, meta_content)
133
+
134
+ meta = @post.meta
135
+ assert_equal "Test Post", meta["post.title"]
136
+ assert_equal "Test blurb", meta["post.blurb"]
137
+ assert_equal "test-post", meta["post.slug"]
138
+ assert_equal "2023-01-01 12:00:00", meta["post.pubdate"]
139
+ end
140
+
141
+ # Individual metadata field tests
142
+ def test_016_title
143
+ create_real_post
144
+ write_file(@post.meta_file, "post.title Test Post")
145
+ assert_equal "Test Post", @post.title
146
+ end
147
+
148
+ def test_017_blurb
149
+ create_real_post
150
+ write_file(@post.meta_file, "post.blurb Test blurb")
151
+ assert_equal "Test blurb", @post.blurb
152
+ end
153
+
154
+ def test_018_slug
155
+ create_real_post
156
+ write_file(@post.meta_file, "post.slug test-post")
157
+ assert_equal "test-post", @post.slug
158
+ end
159
+
160
+ def test_019_pubdate
161
+ create_real_post
162
+ write_file(@post.meta_file, "post.pubdate 2023-01-01 12:00:00")
163
+ assert_equal "2023-01-01 12:00:00", @post.pubdate
164
+ end
165
+
166
+ def test_020_views
167
+ create_real_post
168
+ write_file(@post.meta_file, "post.views view1 view2")
169
+ assert_equal "view1 view2", @post.views
170
+ end
171
+
172
+ def test_021_tags
173
+ create_real_post
174
+ write_file(@post.meta_file, "post.tags tag1,tag2")
175
+ assert_equal "tag1,tag2", @post.tags
176
+ end
177
+
178
+ # set_pubdate tests
179
+ def test_022_set_pubdate_with_valid_date
180
+ create_real_post
181
+ Scriptorium::Repo.testing = true
182
+ @post.set_pubdate("2023-01-15")
183
+
184
+ meta = @post.meta
185
+ assert_equal "2023-01-15 00:00:00", meta["post.pubdate"]
186
+ assert_equal "January", meta["post.pubdate.month"]
187
+ assert_equal "15", meta["post.pubdate.day"]
188
+ assert_equal "2023", meta["post.pubdate.year"]
189
+ end
190
+
191
+ def test_023_set_pubdate_with_nil_date
192
+ Scriptorium::Repo.testing = true
193
+ assert_raises(PubdateYmdNil) do
194
+ @post.set_pubdate(nil)
195
+ end
196
+ end
197
+
198
+ def test_024_set_pubdate_with_empty_date
199
+ Scriptorium::Repo.testing = true
200
+ assert_raises(PubdateYmdEmpty) do
201
+ @post.set_pubdate("")
202
+ end
203
+ end
204
+
205
+ def test_025_set_pubdate_with_invalid_format
206
+ Scriptorium::Repo.testing = true
207
+ assert_raises(PubdateInvalidFormat) do
208
+ @post.set_pubdate("2023/01/15")
209
+ end
210
+ end
211
+
212
+ def test_026_set_pubdate_with_invalid_date
213
+ Scriptorium::Repo.testing = true
214
+ assert_raises(ArgumentError) do
215
+ @post.set_pubdate("2023-13-45")
216
+ end
217
+ end
218
+
219
+ def test_027_set_pubdate_without_testing_mode
220
+ Scriptorium::Repo.testing = false
221
+ assert_raises(TestModeOnly) do
222
+ @post.set_pubdate("2023-01-15")
223
+ end
224
+ Scriptorium::Repo.testing = true
225
+ end
226
+
227
+ def test_028_set_pubdate_with_seconds
228
+ create_real_post
229
+ Scriptorium::Repo.testing = true
230
+ @post.set_pubdate_with_seconds("2023-01-15", 30)
231
+
232
+ meta = @post.meta
233
+ assert_equal "2023-01-15 12:00:30", meta["post.pubdate"]
234
+ assert_equal "January", meta["post.pubdate.month"]
235
+ assert_equal "15", meta["post.pubdate.day"]
236
+ assert_equal "2023", meta["post.pubdate.year"]
237
+ end
238
+
239
+ def test_029_pubdate_month_day_year
240
+ create_real_post
241
+ # Write metadata with the specific fields this test expects
242
+ meta_content = "post.pubdate 2023-01-15 12:00:00\npost.pubdate.month January\npost.pubdate.day 15\npost.pubdate.year 2023"
243
+ write_file(@post.meta_file, meta_content)
244
+ assert_equal ["January", "15", "2023"], @post.pubdate_month_day_year
245
+ end
246
+
247
+ # attrs tests
248
+ def test_030_attrs_single
249
+ create_real_post
250
+ result = @post.attrs(:title)
251
+ assert_equal ["Test Post"], result
252
+ end
253
+
254
+ def test_031_attrs_multiple
255
+ create_real_post
256
+ result = @post.attrs(:title, :blurb)
257
+ assert_equal ["Test Post", "ADD BLURB HERE"], result
258
+ end
259
+
260
+ # vars tests
261
+ def test_032_vars
262
+ create_real_post
263
+ vars = @post.vars
264
+ assert_equal "Test Post", vars[:"post.title"]
265
+ assert_equal "ADD BLURB HERE", vars[:"post.blurb"]
266
+ end
267
+
268
+ def test_033_vars_default_behavior
269
+ create_real_post
270
+ vars = @post.vars
271
+ assert_equal "", vars[:nonexistent_field]
272
+ end
273
+
274
+ # Class method tests
275
+ def test_034_read
276
+ create_real_post
277
+ post = Scriptorium::Post.read(@repo, 1)
278
+ assert_equal "Test Post", post.title
279
+ assert_equal "ADD BLURB HERE", post.blurb
280
+ end
281
+
282
+ # ========================================
283
+ # Post Validation Error Tests
284
+ # ========================================
285
+
286
+ def test_035_post_id_validation_exceptions
287
+ # Test that exception classes exist
288
+ assert PostIdNil
289
+ assert PostIdEmpty
290
+ assert PostIdInvalid
291
+ end
292
+
293
+ def test_036_post_creation_exceptions
294
+ # Test that exception classes exist
295
+ assert CannotCreatePost
296
+ assert CannotGetPost
297
+ assert CannotSetPubdate
298
+ end
299
+
300
+ def test_037_post_id_validation_scenarios
301
+ # Test actual exception raising for post ID validation
302
+ # These would need methods that actually raise these exceptions
303
+ # For now, just verify the classes exist
304
+ assert PostIdNil
305
+ assert PostIdEmpty
306
+ assert PostIdInvalid
307
+ end
308
+
309
+ end