scriptorium 0.0.2 → 0.6.1
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/README.lt3 +324 -0
- data/README.md +3155 -1
- data/assets/.DS_Store +0 -0
- data/assets/README.md +44 -0
- data/assets/back-icon.png +0 -0
- data/assets/icons/facebook.svg +1 -0
- data/assets/icons/github.svg +1 -0
- data/assets/icons/instagram.svg +1 -0
- data/assets/icons/reddit.svg +1 -0
- data/assets/icons/ui/.DS_Store +0 -0
- data/assets/icons/ui/back.png +0 -0
- data/assets/icons/ui/copy.png +0 -0
- data/assets/icons/ui/down.png +0 -0
- data/assets/icons/ui/end.png +0 -0
- data/assets/icons/ui/exit.png +0 -0
- data/assets/icons/ui/foo +10 -0
- data/assets/icons/ui/home.png +0 -0
- data/assets/icons/ui/left.png +0 -0
- data/assets/icons/ui/next.png +0 -0
- data/assets/icons/ui/right.png +0 -0
- data/assets/icons/ui/start.png +0 -0
- data/assets/icons/ui/up.png +0 -0
- data/assets/icons/x.svg +1 -0
- data/assets/icons/youtube.svg +1 -0
- data/assets/samples/placeholder.svg +9 -0
- data/assets/themes/standard/favicon.svg +6 -0
- data/bin/scriptorium +1511 -0
- data/doc/README.txt +6 -0
- data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +95 -0
- data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +34 -0
- data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +50 -0
- data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +31 -0
- data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +73 -0
- data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +64 -0
- data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +70 -0
- data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
- data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +110 -0
- data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +73 -0
- data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
- data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +124 -0
- data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +46 -0
- data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +97 -0
- data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
- data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +211 -0
- data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +141 -0
- data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +211 -0
- data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +134 -0
- data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +41 -0
- data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +45 -0
- data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +30 -0
- data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +49 -0
- data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +46 -0
- data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +41 -0
- data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +41 -0
- data/doc/anti-amnesia/20250807-213025.md +116 -0
- data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +256 -0
- data/doc/banner_svg_config.md +114 -0
- data/doc/contrib.lt3 +8 -0
- data/doc/dependencies.md +281 -0
- data/doc/hacker.lt3 +5 -0
- data/doc/reddit_credentials_template.json +8 -0
- data/doc/reddit_integration.md +207 -0
- data/doc/user.lt3 +38 -0
- data/doc/user_guide_section_1.md +137 -0
- data/doc/user_guide_section_10.md +515 -0
- data/doc/user_guide_section_11.md +708 -0
- data/doc/user_guide_section_2.md +233 -0
- data/doc/user_guide_section_3.md +5 -0
- data/doc/user_guide_section_4.md +221 -0
- data/doc/user_guide_section_5.md +243 -0
- data/doc/user_guide_section_6.md +147 -0
- data/doc/user_guide_section_7.md +311 -0
- data/doc/user_guide_section_8.md +224 -0
- data/doc/user_guide_section_9.md +375 -0
- data/doc/userdoc-toc.txt +88 -0
- data/lib/rouge/lexers/livetext.rb +74 -0
- data/lib/scriptorium/api.rb +640 -0
- data/lib/scriptorium/banner_svg.rb +742 -0
- data/lib/scriptorium/contract.rb +33 -0
- data/lib/scriptorium/exceptions.rb +174 -0
- data/lib/scriptorium/helpers.rb +475 -0
- data/lib/scriptorium/post.rb +195 -0
- data/lib/scriptorium/reddit.rb +83 -0
- data/lib/scriptorium/repo.rb +624 -0
- data/lib/scriptorium/standard_files.rb +515 -0
- data/lib/scriptorium/syntax_highlighter.rb +234 -0
- data/lib/scriptorium/theme.rb +179 -0
- data/lib/scriptorium/version.rb +2 -2
- data/lib/scriptorium/view.rb +976 -0
- data/lib/scriptorium/widgets/featured_posts.rb +149 -0
- data/lib/scriptorium/widgets/links.rb +112 -0
- data/lib/scriptorium/widgets/pages.rb +133 -0
- data/lib/scriptorium/widgets/widget.rb +133 -0
- data/lib/scriptorium.rb +22 -9
- data/lib/skeleton.rb +11 -2
- data/scriptorium.gemspec +15 -4
- data/test/README.md +69 -0
- data/test/all +43 -0
- data/test/api_demo.rb +99 -0
- data/test/assets/imagenotfound.jpg +0 -0
- data/test/assets/images/.DS_Store +0 -0
- data/test/assets/images/README.md +27 -0
- data/test/assets/images/odd_aspect.png +0 -0
- data/test/assets/images/perfect.png +0 -0
- data/test/assets/images/small.png +0 -0
- data/test/assets/images/tall.png +0 -0
- data/test/assets/images/very_tall.png +0 -0
- data/test/assets/images/very_wide.png +0 -0
- data/test/assets/images/wide.png +0 -0
- data/test/assets/testbanner.jpg +0 -0
- data/test/banner_svg/simple_helpers.rb +13 -0
- data/test/banner_svg/unit.rb +768 -0
- data/test/ed_test.rb +204 -0
- data/test/integration/cursor_banner_combinations.rb +193 -0
- data/test/integration/cursor_banner_features.rb +374 -0
- data/test/integration/integration_test.rb +326 -0
- data/test/livetext_plugin_test.rb +229 -0
- data/test/manual/asset_mgmt.rb +67 -0
- data/test/manual/banner-tests/config.txt +3 -0
- data/test/manual/banner-tests/index.html +45 -0
- data/test/manual/banner-tests/test01.html +58 -0
- data/test/manual/banner-tests/test02.html +58 -0
- data/test/manual/banner-tests/test03.html +58 -0
- data/test/manual/banner-tests/test04.html +65 -0
- data/test/manual/banner-tests/test05.html +65 -0
- data/test/manual/banner-tests/test06.html +65 -0
- data/test/manual/banner-tests/test07.html +65 -0
- data/test/manual/banner-tests/test08.html +59 -0
- data/test/manual/banner-tests/test09.html +59 -0
- data/test/manual/banner-tests/test10.html +59 -0
- data/test/manual/banner-tests/test11.html +59 -0
- data/test/manual/banner-tests/test12.html +59 -0
- data/test/manual/banner-tests/test13.html +59 -0
- data/test/manual/banner-tests/test14.html +59 -0
- data/test/manual/banner-tests/test15.html +58 -0
- data/test/manual/banner-tests/test16.html +58 -0
- data/test/manual/banner-tests/test17.html +58 -0
- data/test/manual/banner-tests/test18.html +68 -0
- data/test/manual/banner-tests/test19.html +68 -0
- data/test/manual/banner-tests/test20.html +68 -0
- data/test/manual/banner-tests/test21.html +68 -0
- data/test/manual/banner-tests/test22.html +68 -0
- data/test/manual/banner-tests/test23.html +68 -0
- data/test/manual/banner-tests/test24.html +68 -0
- data/test/manual/banner-tests/test25.html +67 -0
- data/test/manual/banner_environment.rb +192 -0
- data/test/manual/deploy_symlink_demo.rb +142 -0
- data/test/manual/environment.rb +67 -0
- data/test/manual/make_banner.rb +153 -0
- data/test/manual/sample_banner_config.txt +12 -0
- data/test/manual/symlink_demo.rb +117 -0
- data/test/manual/test1.rb +47 -0
- data/test/manual/test2.rb +12 -0
- data/test/manual/test3.rb +38 -0
- data/test/manual/test4.rb +40 -0
- data/test/manual/test5.rb +24 -0
- data/test/manual/test6.rb +73 -0
- data/test/manual/test_banner_combinations.rb +120 -0
- data/test/manual/test_banner_features.rb +306 -0
- data/test/manual/test_banner_from_file.rb +150 -0
- data/test/manual/test_banner_in_header.rb +35 -0
- data/test/manual/test_code_highlighting.rb +68 -0
- data/test/manual/test_complex_header.rb +74 -0
- data/test/manual/test_empty_header.rb +32 -0
- data/test/manual/test_radial_custom.rb +58 -0
- data/test/manual/test_radial_large_radius.rb +52 -0
- data/test/manual/test_svg_debug.rb +47 -0
- data/test/manual/test_syntax_highlighting.rb +147 -0
- data/test/pages-demo/config/currentview.txt +1 -0
- data/test/pages-demo/views/demo/config/bootstrap_css.txt +5 -0
- data/test/pages-demo/views/demo/config/bootstrap_js.txt +4 -0
- data/test/pages-demo/views/demo/config/common.js +57 -0
- data/test/pages-demo/views/demo/config/footer.txt +1 -0
- data/test/pages-demo/views/demo/config/global-head.txt +8 -0
- data/test/pages-demo/views/demo/config/header.txt +1 -0
- data/test/pages-demo/views/demo/config/layout.txt +1 -0
- data/test/pages-demo/views/demo/config/left.txt +1 -0
- data/test/pages-demo/views/demo/config/main.txt +1 -0
- data/test/pages-demo/views/demo/config/right.txt +1 -0
- data/test/pages-demo/views/demo/config.txt +3 -0
- data/test/pages-demo/views/demo/output/panes/footer.html +1 -0
- data/test/pages-demo/views/demo/output/panes/header.html +1 -0
- data/test/pages-demo/views/demo/output/panes/left.html +1 -0
- data/test/pages-demo/views/demo/output/panes/main.html +1 -0
- data/test/pages-demo/views/demo/output/panes/right.html +1 -0
- data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
- data/test/rubytext/rubytext_demo_test.rb +42 -0
- data/test/rubytext/rubytext_testing_guide.md +277 -0
- data/test/run_automated_tests.rb +45 -0
- data/test/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/config/common.js +57 -0
- data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +9 -0
- data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +4 -0
- data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +8 -0
- data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +6 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 +12 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt +2 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/layout.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +14 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +13 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +57 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +2 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +9 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/reddit.txt +10 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/social.txt +7 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +7 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +1 -0
- data/test/staging/.DS_Store +0 -0
- data/test/syntax_highlighting_test.lt3 +124 -0
- data/test/test_helpers.rb +230 -0
- data/test/tui_editor_integration_test.rb +296 -0
- data/test/tui_integration_test.rb +637 -0
- data/test/unit/api.rb +1056 -0
- data/test/unit/asset_management.rb +245 -0
- data/test/unit/clipboard_test.rb +60 -0
- data/test/unit/contract_test.rb +91 -0
- data/test/unit/core.rb +857 -0
- data/test/unit/deploy_test.rb +187 -0
- data/test/unit/gem_asset_management.rb +189 -0
- data/test/unit/livetext_basic.rb +69 -0
- data/test/unit/livetext_compatibility.rb +89 -0
- data/test/unit/post.rb +244 -0
- data/test/unit/read_commented_file_test.rb +276 -0
- data/test/unit/reddit_test.rb +235 -0
- data/test/unit/repo.rb +548 -0
- data/test/unit/social_test.rb +369 -0
- data/test/unit/symlink_test.rb +213 -0
- data/test/unit/view.rb +431 -0
- data/test/unit/widgets.rb +669 -0
- data/test/wizard_test.rb +123 -0
- data/ui/README.md +67 -0
- data/ui/common/lib/ui_common.rb +8 -0
- data/ui/rubytext/README.md +191 -0
- data/ui/rubytext/bin/scriptorium-rubytext +402 -0
- data/ui/rubytext/lib/rubytext_ui.rb +300 -0
- data/ui/tui/bin/scriptorium +1420 -0
- data/ui/tui/test/tui_test.rb +23 -0
- data/ui/web/app/app.rb +1378 -0
- data/ui/web/app/error_helpers.rb +150 -0
- data/ui/web/app/views/advanced_config.erb +190 -0
- data/ui/web/app/views/asset_management.erb +589 -0
- data/ui/web/app/views/banner_config.erb +200 -0
- data/ui/web/app/views/configure_view.erb +401 -0
- data/ui/web/app/views/dashboard.erb +162 -0
- data/ui/web/app/views/deploy_config.erb +146 -0
- data/ui/web/app/views/edit_pages.erb +195 -0
- data/ui/web/app/views/edit_post.erb +54 -0
- data/ui/web/app/views/error_page.erb +29 -0
- data/ui/web/app/views/header_config.erb +155 -0
- data/ui/web/app/views/layout_config.erb +147 -0
- data/ui/web/app/views/navbar_config.erb +411 -0
- data/ui/web/app/views/view_dashboard.erb +138 -0
- data/ui/web/bin/scriptorium-web +153 -0
- data/ui/web/test/web_basic_test.rb +38 -0
- data/ui/web/test_navbar.txt +7 -0
- data/ui/web/tmp/web_server.log +5 -0
- data/ui/web/tmp/web_server.pid +1 -0
- metadata +360 -5
@@ -0,0 +1,624 @@
|
|
1
|
+
class Scriptorium::Repo
|
2
|
+
include Scriptorium::Exceptions
|
3
|
+
extend Scriptorium::Exceptions
|
4
|
+
include Scriptorium::Helpers
|
5
|
+
extend Scriptorium::Helpers
|
6
|
+
include Scriptorium::Contract
|
7
|
+
extend Scriptorium::Contract
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :testing
|
11
|
+
attr_reader :root, :repo # class level
|
12
|
+
end
|
13
|
+
|
14
|
+
# instance attrs
|
15
|
+
|
16
|
+
attr_reader :root, :views, :current_view
|
17
|
+
|
18
|
+
def self.exist?
|
19
|
+
dir = Scriptorium::Repo.root
|
20
|
+
return false if dir.nil?
|
21
|
+
Dir.exist?(dir)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.create(path = nil, testmode: false)
|
25
|
+
assume { path.nil? || path.is_a?(String) }
|
26
|
+
# Handle backward compatibility: boolean true means testing mode
|
27
|
+
if testmode == true
|
28
|
+
Scriptorium::Repo.testing = path
|
29
|
+
else
|
30
|
+
Scriptorium::Repo.testing = nil
|
31
|
+
end
|
32
|
+
home = ENV['HOME']
|
33
|
+
@predef = Scriptorium::StandardFiles.new
|
34
|
+
@root = path || "#{home}/.scriptorium"
|
35
|
+
parent = path ? "." : home
|
36
|
+
file = path || ".scriptorium"
|
37
|
+
@root = parent/file
|
38
|
+
raise self.RepoDirAlreadyExists(@root) if Dir.exist?(@root)
|
39
|
+
make_tree(parent, <<~EOS)
|
40
|
+
#@root
|
41
|
+
├── config/ # Global config files
|
42
|
+
├── views/ # Views
|
43
|
+
├── drafts/ # Draft posts (global)
|
44
|
+
├── posts/ # Global generated posts (slug.html)
|
45
|
+
├── assets/ # Images, etc.
|
46
|
+
│ └── library/ # Common images, icons, etc.
|
47
|
+
└── themes/ # Themes
|
48
|
+
EOS
|
49
|
+
|
50
|
+
postnum_file = "#@root/config/last_post_num.txt"
|
51
|
+
write_file(postnum_file, "0")
|
52
|
+
write_file(@root/:config/"global-head.txt", @predef.html_head_content)
|
53
|
+
write_file(@root/:config/"bootstrap_js.txt", @predef.bootstrap_js)
|
54
|
+
write_file(@root/:config/"bootstrap_css.txt", @predef.bootstrap_css)
|
55
|
+
write_file(@root/:config/"common.js", @predef.common_js)
|
56
|
+
write_file(@root/:config/"widgets.txt", @predef.available_widgets)
|
57
|
+
Scriptorium::Theme.create_standard(@root) # Theme: templates, etc.
|
58
|
+
|
59
|
+
# Copy application-wide gem assets to library
|
60
|
+
Scriptorium::Theme.copy_gem_assets_to_library(@root)
|
61
|
+
|
62
|
+
# Generate OS-specific helper code
|
63
|
+
generate_os_helpers(@root)
|
64
|
+
|
65
|
+
@repo = self.open(@root)
|
66
|
+
Scriptorium::View.create_sample_view(repo)
|
67
|
+
verify { @repo.is_a?(Scriptorium::Repo) }
|
68
|
+
return repo
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.open(root)
|
72
|
+
assume { root.is_a?(String) && !root.empty? }
|
73
|
+
repo = Scriptorium::Repo.new(root)
|
74
|
+
verify { repo.is_a?(Scriptorium::Repo) }
|
75
|
+
repo
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.destroy
|
79
|
+
assume { Scriptorium::Repo.testing }
|
80
|
+
raise self.TestModeOnly unless Scriptorium::Repo.testing
|
81
|
+
system!("rm -rf #@root", "destroying repository")
|
82
|
+
verify { !Dir.exist?(@root) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def postnum_file
|
86
|
+
"#@root/config/last_post_num.txt"
|
87
|
+
end
|
88
|
+
|
89
|
+
# Invariants
|
90
|
+
def define_invariants
|
91
|
+
invariant { @root.is_a?(String) && !@root.empty? }
|
92
|
+
invariant { @views.is_a?(Array) }
|
93
|
+
invariant { @current_view.nil? || @current_view.is_a?(Scriptorium::View) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize(root) # repo
|
97
|
+
assume { root.is_a?(String) && !root.empty? }
|
98
|
+
@root = root
|
99
|
+
@predef = Scriptorium::StandardFiles.new
|
100
|
+
# Scriptorium::Repo.class_eval { @root, @repo = root, self }
|
101
|
+
self.class.instance_variable_set(:@root, root)
|
102
|
+
self.class.instance_variable_set(:@repo, self)
|
103
|
+
load_views
|
104
|
+
@reddit = nil # Lazy load Reddit integration
|
105
|
+
define_invariants
|
106
|
+
verify { @root == root }
|
107
|
+
check_invariants
|
108
|
+
end
|
109
|
+
|
110
|
+
private def load_views
|
111
|
+
@views = []
|
112
|
+
list = Dir.entries(@root/:views) - %w[. .. config.txt]
|
113
|
+
list.each {|dir| open_view(dir) }
|
114
|
+
cview_file = @root/:config/"currentview.txt"
|
115
|
+
@current_view = nil
|
116
|
+
if File.exist?(cview_file)
|
117
|
+
view_name = read_file(cview_file).chomp
|
118
|
+
begin
|
119
|
+
@current_view = lookup_view(view_name)
|
120
|
+
rescue => e
|
121
|
+
# If the saved view doesn't exist, just leave current_view as nil
|
122
|
+
# It will be set when a view is created or selected
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
### View methods...
|
128
|
+
|
129
|
+
def lookup_view(target)
|
130
|
+
return target if target.is_a?(Scriptorium::View)
|
131
|
+
|
132
|
+
validate_view_target(target)
|
133
|
+
|
134
|
+
list = @views.select {|v| v.name == target }
|
135
|
+
raise CannotLookupView(target) if list.empty?
|
136
|
+
raise MoreThanOneResult(target) if list.size > 1
|
137
|
+
return list[0]
|
138
|
+
end
|
139
|
+
|
140
|
+
private def validate_view_target(target)
|
141
|
+
raise CannotLookupViewTargetNil if target.nil?
|
142
|
+
|
143
|
+
raise CannotLookupViewTargetEmpty if target.to_s.strip.empty?
|
144
|
+
end
|
145
|
+
|
146
|
+
def view(change = nil) # get/set current view
|
147
|
+
return @current_view if change.nil?
|
148
|
+
vnew = change.is_a?(Scriptorium::View) ? change : lookup_view(change)
|
149
|
+
write_file(@root/:config/"currentview.txt", vnew.name)
|
150
|
+
@current_view = vnew
|
151
|
+
@current_view
|
152
|
+
end
|
153
|
+
|
154
|
+
def current_view
|
155
|
+
@current_view
|
156
|
+
end
|
157
|
+
|
158
|
+
def view_exist?(name)
|
159
|
+
Dir.exist?("#@root/views/#{name}")
|
160
|
+
end
|
161
|
+
|
162
|
+
def create_view(name, title, subtitle = "", theme: "standard")
|
163
|
+
assume { name.is_a?(String) }
|
164
|
+
assume { title.is_a?(String) }
|
165
|
+
validate_view_name(name)
|
166
|
+
validate_view_title(title)
|
167
|
+
|
168
|
+
# Validate name format (only allow alphanumeric, hyphen, underscore)
|
169
|
+
unless name.match?(/^[a-zA-Z0-9_-]+$/)
|
170
|
+
raise CannotCreateViewNameInvalid(name)
|
171
|
+
end
|
172
|
+
|
173
|
+
raise ViewDirAlreadyExists(name) if view_exist?(name)
|
174
|
+
make_tree(@root/:views, <<~EOS)
|
175
|
+
#{name}/
|
176
|
+
├── config/ # View-specific config files
|
177
|
+
│ ├── layout.txt # Overall layout for front page
|
178
|
+
│ ├── footer.txt # Content for footer.html
|
179
|
+
│ ├── header.txt # Content for header.html
|
180
|
+
│ ├── left.txt # Content for left.html
|
181
|
+
│ ├── main.txt # Content for main.html
|
182
|
+
│ └── right.txt # Content for right.html
|
183
|
+
├── config.txt # View-specific config file # maybe call settings.txt?
|
184
|
+
├── layout/ # Unused?
|
185
|
+
├── pages/ # Static pages for view
|
186
|
+
├── assets/ # Images, etc. (view-specific)
|
187
|
+
│ └── missing/ # Missing assets (SVG placeholder files)
|
188
|
+
├── output/ # Output files (generated HTML)
|
189
|
+
│ ├── panes/ # Containers from layout.txt
|
190
|
+
│ │ ├── footer.html # Generated from footer.txt
|
191
|
+
│ │ ├── header.html # Generated from header.txt
|
192
|
+
│ │ ├── left.html # Generated from left.txt
|
193
|
+
│ │ ├── main.html # Generated from main.txt
|
194
|
+
│ │ └── right.html # Generated from right.txt
|
195
|
+
│ └── posts/ # Generated posts for view (slug.html)
|
196
|
+
├── widgets/ # Widgets for view
|
197
|
+
└── staging/ # Staging area prior to deployment
|
198
|
+
EOS
|
199
|
+
|
200
|
+
###
|
201
|
+
|
202
|
+
dir = "#@root/views/#{name}"
|
203
|
+
write_file!(dir/"config.txt",
|
204
|
+
"title #{title}",
|
205
|
+
"subtitle #{subtitle}",
|
206
|
+
"theme #{theme}")
|
207
|
+
write_file(dir/:config/"global-head.txt", @predef.html_head_content(true)) # true = view-specific
|
208
|
+
write_file(dir/:config/"bootstrap_js.txt", @predef.bootstrap_js)
|
209
|
+
write_file(dir/:config/"bootstrap_css.txt", @predef.bootstrap_css)
|
210
|
+
write_file(dir/:config/"common.js", @predef.common_js)
|
211
|
+
write_file(dir/:config/"social.txt", @predef.social_config)
|
212
|
+
write_file(dir/:config/"reddit.txt", @predef.reddit_config)
|
213
|
+
write_file(dir/:config/"deploy.txt", @predef.deploy_text % {view: name, domain: "example.com"})
|
214
|
+
write_file(dir/:config/"status.txt", @predef.status_txt)
|
215
|
+
view = open_view(name)
|
216
|
+
@views -= [view]
|
217
|
+
@views << view
|
218
|
+
@current_view = view
|
219
|
+
write_file(@root/:config/"currentview.txt", view.name)
|
220
|
+
cfg = dir/:config # Should these be copied from theme??
|
221
|
+
theme_config = @root/:themes/theme/:layout/:config
|
222
|
+
containers = %w[header.txt footer.txt left.txt right.txt main.txt]
|
223
|
+
containers.each { |container| FileUtils.cp(theme_config/container, cfg/container) } # from theme to view
|
224
|
+
view.apply_theme(theme)
|
225
|
+
verify { view.is_a?(Scriptorium::View) }
|
226
|
+
return view
|
227
|
+
end
|
228
|
+
|
229
|
+
def open_view(name)
|
230
|
+
vhash = getvars(view_dir(name)/"config.txt")
|
231
|
+
title, subtitle, theme = vhash.values_at(:title, :subtitle, :theme)
|
232
|
+
view = Scriptorium::View.new(name, title, subtitle, theme)
|
233
|
+
@views -= [view]
|
234
|
+
@views << view
|
235
|
+
# Remove this line - current view should only be set from currentview.txt
|
236
|
+
# @current_view = view
|
237
|
+
# write_file(@root/:config/"currentview.txt", view.name)
|
238
|
+
view
|
239
|
+
end
|
240
|
+
|
241
|
+
def create_draft(title: nil, blurb: nil, views: nil, tags: nil, body: nil)
|
242
|
+
ts = Time.now.strftime("%Y%m%d-%H%M%S")
|
243
|
+
content_name = "#@root/drafts/#{ts}-draft.lt3"
|
244
|
+
metadata_name = "#@root/drafts/#{ts}-draft.meta"
|
245
|
+
|
246
|
+
# Whoa - what if different views have different themes??? FIXME
|
247
|
+
# Maybe solution is as simple as: Initial post is not theme-dependent
|
248
|
+
theme = @current_view.theme
|
249
|
+
views ||= @current_view.name # initial_post wants a String!
|
250
|
+
views, tags = Array(views), Array(tags)
|
251
|
+
id = incr_post_num
|
252
|
+
|
253
|
+
# Create content file (no ID, no created date)
|
254
|
+
content = @predef.initial_post_content(title: title, blurb: blurb,
|
255
|
+
views: views, tags: tags, body: body)
|
256
|
+
write_file(content_name, content)
|
257
|
+
|
258
|
+
# Create metadata file (with ID and created date)
|
259
|
+
metadata = @predef.initial_post_metadata(num: id, title: title, blurb: blurb,
|
260
|
+
views: views, tags: tags)
|
261
|
+
write_file(metadata_name, metadata)
|
262
|
+
|
263
|
+
# Return the content file name (for backward compatibility)
|
264
|
+
content_name
|
265
|
+
end
|
266
|
+
|
267
|
+
def last_post_num
|
268
|
+
read_file(postnum_file).to_i
|
269
|
+
end
|
270
|
+
|
271
|
+
def incr_post_num
|
272
|
+
num = last_post_num + 1
|
273
|
+
write_file(postnum_file, num.to_s)
|
274
|
+
num
|
275
|
+
end
|
276
|
+
|
277
|
+
def finish_draft(name)
|
278
|
+
id = last_post_num
|
279
|
+
id4 = d4(id)
|
280
|
+
posts = @root/:posts
|
281
|
+
make_dir(posts/id4)
|
282
|
+
make_dir(posts/id4/:assets)
|
283
|
+
|
284
|
+
# Move content file
|
285
|
+
FileUtils.mv(name, posts/id4/"source.lt3")
|
286
|
+
|
287
|
+
# Move metadata file (same timestamp, different extension)
|
288
|
+
metadata_name = name.sub('.lt3', '.meta')
|
289
|
+
FileUtils.mv(metadata_name, posts/id4/"meta.txt") if File.exist?(metadata_name)
|
290
|
+
id
|
291
|
+
end
|
292
|
+
|
293
|
+
def tree(file = nil)
|
294
|
+
cmd = "tree #@root"
|
295
|
+
cmd << " >#{file}" if file
|
296
|
+
system!(cmd, "generating tree structure")
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
private def copy_post_assets_to_view(num, view)
|
301
|
+
id4 = d4(num)
|
302
|
+
post_assets_dir = @root/:posts/id4/"assets"
|
303
|
+
view_assets_dir = view.dir/:output/"assets"
|
304
|
+
|
305
|
+
# Only copy if post has assets
|
306
|
+
return unless Dir.exist?(post_assets_dir)
|
307
|
+
|
308
|
+
# Create view assets directory if it doesn't exist
|
309
|
+
make_dir(view_assets_dir)
|
310
|
+
|
311
|
+
# Copy all files from post assets to view assets
|
312
|
+
Dir.glob(post_assets_dir/"*").each do |file|
|
313
|
+
next unless File.file?(file)
|
314
|
+
filename = File.basename(file)
|
315
|
+
target_file = view_assets_dir/filename
|
316
|
+
|
317
|
+
# Copy file, overwriting if it exists (post assets take precedence)
|
318
|
+
FileUtils.cp(file, target_file)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
private def write_post_metadata(data, view)
|
323
|
+
num, title = data.values_at(:"post.id", :"post.title")
|
324
|
+
metadata_file = @root/:posts/d4(num)/"meta.txt"
|
325
|
+
|
326
|
+
# Read existing metadata to preserve fields like post.published
|
327
|
+
existing_metadata = {}
|
328
|
+
existing_metadata = getvars(metadata_file) if File.exist?(metadata_file)
|
329
|
+
|
330
|
+
# Prepare new metadata from data
|
331
|
+
new_metadata = data.select {|k,v| k.to_s.start_with?("post.") }
|
332
|
+
new_metadata.delete(:"post.body")
|
333
|
+
new_metadata[:"post.slug"] = slugify(num, title) + ".html"
|
334
|
+
|
335
|
+
# Merge existing metadata over new metadata to preserve important fields
|
336
|
+
# Only preserve fields that should not be overwritten by source file changes
|
337
|
+
fields_to_preserve = [:"post.published", :"post.deployed", :"post.created"]
|
338
|
+
existing_metadata.each { |key, value| new_metadata[key] = value if fields_to_preserve.include?(key) }
|
339
|
+
|
340
|
+
lines = new_metadata.map { |k, v| sprintf("%-18s %s", k, v) }
|
341
|
+
write_file(metadata_file, lines.join("\n"))
|
342
|
+
end
|
343
|
+
|
344
|
+
private def write_generated_post(data, view, final)
|
345
|
+
num, title = data.values_at(:"post.id", :"post.title")
|
346
|
+
id4 = d4(num)
|
347
|
+
slug = slugify(num, title) + ".html"
|
348
|
+
# Write to:
|
349
|
+
# root/posts/0123/body.html meta.txt (assets/ draft.lt3)
|
350
|
+
top = @root/:posts/id4/"body.html"
|
351
|
+
write_file(top, final)
|
352
|
+
write_post_metadata(data, view)
|
353
|
+
# view/.../output/posts/0123-this-is-me.html
|
354
|
+
path = view.dir/:output/:posts/slug
|
355
|
+
write_file(path, final)
|
356
|
+
# view/.../output/permalink/0123-this-is-me.html (for direct access)
|
357
|
+
permalink_path = view.dir/:output/:permalink/slug
|
358
|
+
make_dir(File.dirname(permalink_path))
|
359
|
+
# Write the permalink version with "Visit Blog" link and "Copy link" button
|
360
|
+
permalink_content = final + "\n<div style=\"text-align: center; margin-top: 20px;\">\n<a href=\"../index.html\">Visit Blog</a>\n</div>\n<div style=\"text-align: center; margin-top: 10px;\">\n<button onclick=\"copyPermalinkToClipboard()\" style=\"padding: 8px 16px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;\">Copy link</button>\n</div>\n<script>\nfunction copyPermalinkToClipboard() {\n navigator.clipboard.writeText(window.location.href).then(function() {\n // Change button text temporarily to show success\n const button = event.target;\n const originalText = button.textContent;\n button.textContent = 'Copied!';\n button.style.background = '#28a745';\n setTimeout(function() {\n button.textContent = originalText;\n button.style.background = '#007bff';\n }, 2000);\n }).catch(function(err) {\n console.error('Failed to copy: ', err);\n alert('Failed to copy link to clipboard');\n });\n}\n</script>"
|
361
|
+
write_file(permalink_path, permalink_content)
|
362
|
+
|
363
|
+
# Create symlink for clean URL (without numeric prefix)
|
364
|
+
clean_slug = clean_slugify(title) + ".html"
|
365
|
+
clean_symlink_path = view.dir/:output/:permalink/clean_slug
|
366
|
+
|
367
|
+
# Remove existing symlink if it exists
|
368
|
+
File.delete(clean_symlink_path) if File.exist?(clean_symlink_path) && File.symlink?(clean_symlink_path)
|
369
|
+
|
370
|
+
# Create symlink (relative path from clean_symlink_path to slug)
|
371
|
+
begin
|
372
|
+
File.symlink(slug, clean_symlink_path)
|
373
|
+
rescue Errno::EEXIST => e
|
374
|
+
# If symlink already exists (not a symlink), remove it and try again
|
375
|
+
File.delete(clean_symlink_path) if File.exist?(clean_symlink_path)
|
376
|
+
File.symlink(slug, clean_symlink_path)
|
377
|
+
end
|
378
|
+
|
379
|
+
# Copy post-specific assets to view output directory for deployment
|
380
|
+
copy_post_assets_to_view(num, view)
|
381
|
+
end
|
382
|
+
|
383
|
+
def create_post(title: nil, views: nil, tags: nil, body: nil, blurb: nil)
|
384
|
+
assume { title.nil? || title.is_a?(String) }
|
385
|
+
assume { views.nil? || views.is_a?(Array) || views.is_a?(String) }
|
386
|
+
assume { tags.nil? || tags.is_a?(Array) || tags.is_a?(String) }
|
387
|
+
assume { body.nil? || body.is_a?(String) }
|
388
|
+
assume { blurb.nil? || blurb.is_a?(String) }
|
389
|
+
name = create_draft(title: title, views: views, tags: tags, body: body, blurb: blurb)
|
390
|
+
num = finish_draft(name)
|
391
|
+
generate_post(num)
|
392
|
+
post = self.post(num) # Return the Post object
|
393
|
+
verify { post.is_a?(Scriptorium::Post) }
|
394
|
+
post
|
395
|
+
end
|
396
|
+
|
397
|
+
def publish_post(num)
|
398
|
+
validate_post_id(num)
|
399
|
+
metadata_file = @root/:posts/d4(num)/"meta.txt"
|
400
|
+
|
401
|
+
# Read current metadata if it exists
|
402
|
+
metadata = {}
|
403
|
+
metadata = getvars(metadata_file) if File.exist?(metadata_file)
|
404
|
+
|
405
|
+
# Check if already published
|
406
|
+
if metadata[:"post.published"] != "no" && metadata[:"post.published"] != nil
|
407
|
+
raise "Post #{num} is already published"
|
408
|
+
end
|
409
|
+
|
410
|
+
# Update published timestamp
|
411
|
+
metadata[:"post.published"] = ymdhms
|
412
|
+
|
413
|
+
# Write updated metadata
|
414
|
+
lines = metadata.map { |k, v| sprintf("%-18s %s", k, v) }
|
415
|
+
write_file(metadata_file, lines.join("\n"))
|
416
|
+
|
417
|
+
# Generate the post (this will preserve the updated metadata)
|
418
|
+
generate_post(num)
|
419
|
+
|
420
|
+
self.post(num)
|
421
|
+
end
|
422
|
+
|
423
|
+
def post_published?(num)
|
424
|
+
validate_post_id(num)
|
425
|
+
metadata_file = @root/:posts/d4(num)/"meta.txt"
|
426
|
+
return false unless File.exist?(metadata_file)
|
427
|
+
|
428
|
+
metadata = getvars(metadata_file)
|
429
|
+
result = metadata[:"post.published"] != "no"
|
430
|
+
result
|
431
|
+
end
|
432
|
+
|
433
|
+
def get_published_posts(view = nil)
|
434
|
+
all_posts = all_posts(view)
|
435
|
+
all_posts.select { |post| post_published?(post.id) }
|
436
|
+
end
|
437
|
+
|
438
|
+
def generate_post(num)
|
439
|
+
content_file = @root/:posts/d4(num)/"source.lt3"
|
440
|
+
metadata_file = @root/:posts/d4(num)/"meta.txt"
|
441
|
+
|
442
|
+
need(:file, content_file)
|
443
|
+
|
444
|
+
# Read content file
|
445
|
+
vars = { View: @current_view.name, :"post.id" => num }
|
446
|
+
# live = Livetext.customize(mix: "lt3scriptor", call: ".nopara", vars: vars)
|
447
|
+
# text = live.xform_file(content_file)
|
448
|
+
# vars, _body = live.vars.vars, live.body
|
449
|
+
|
450
|
+
live = Livetext.customize(mix: "lt3scriptor", call: ".nopara", vars: vars)
|
451
|
+
body, vars = live.process(file: content_file)
|
452
|
+
|
453
|
+
# Create or update metadata from post content
|
454
|
+
if File.exist?(metadata_file)
|
455
|
+
# Preserve existing metadata (like post.published timestamp)
|
456
|
+
existing_metadata = getvars(metadata_file)
|
457
|
+
metadata_vars = create_metadata_from_content(num, vars)
|
458
|
+
# Merge existing metadata over defaults
|
459
|
+
existing_metadata.each do |key, value|
|
460
|
+
metadata_vars[key] = value
|
461
|
+
end
|
462
|
+
else
|
463
|
+
# Create new metadata
|
464
|
+
metadata_vars = create_metadata_from_content(num, vars)
|
465
|
+
end
|
466
|
+
|
467
|
+
# Write metadata file
|
468
|
+
lines = metadata_vars.map { |k, v| sprintf("%-18s %s", k, v) }
|
469
|
+
write_file(metadata_file, lines.join("\n"))
|
470
|
+
|
471
|
+
# Merge metadata into vars, but don't override content vars
|
472
|
+
metadata_vars.each { |key, value| vars[key] = value unless vars.key?(key) }
|
473
|
+
|
474
|
+
views = vars[:"post.views"].strip.split(/\s+/)
|
475
|
+
vars[:"post.views"] = views.join(" ") # Ensure post.views is set in vars
|
476
|
+
views.each do |view|
|
477
|
+
view = lookup_view(view)
|
478
|
+
theme = view.theme
|
479
|
+
vars[:"post.id"] = num.to_s # Always use the post number as ID
|
480
|
+
vars[:"post.body"] = body
|
481
|
+
template = @predef.post_template("standard")
|
482
|
+
set_pubdate(vars)
|
483
|
+
# Add Reddit button if enabled
|
484
|
+
vars[:"reddit_button"] = view.generate_reddit_button(vars)
|
485
|
+
final = substitute(vars, template)
|
486
|
+
write_generated_post(vars, view, final)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
private def create_metadata_from_content(num, vars)
|
491
|
+
metadata = {}
|
492
|
+
|
493
|
+
# Set required fields
|
494
|
+
metadata[:"post.id"] = d4(num)
|
495
|
+
metadata[:"post.created"] = ymdhms
|
496
|
+
metadata[:"post.published"] = "no" # Default to unpublished
|
497
|
+
metadata[:"post.deployed"] = "no"
|
498
|
+
|
499
|
+
# Copy fields from content vars
|
500
|
+
metadata[:"post.title"] = vars[:"post.title"] || "ADD TITLE HERE"
|
501
|
+
metadata[:"post.blurb"] = vars[:"post.blurb"] || "ADD BLURB HERE"
|
502
|
+
metadata[:"post.views"] = vars[:"post.views"] || "sample"
|
503
|
+
metadata[:"post.tags"] = vars[:"post.tags"] || ""
|
504
|
+
|
505
|
+
metadata
|
506
|
+
end
|
507
|
+
|
508
|
+
private def set_pubdate(vars) # Not Post#set_pubdate
|
509
|
+
t = Time.now
|
510
|
+
vars[:"post.pubdate"] = t.strftime("%Y-%m-%d %H:%M:%S")
|
511
|
+
vars[:"post.pubdate.month"] = t.strftime("%B")
|
512
|
+
vars[:"post.pubdate.day"] = t.strftime("%d")
|
513
|
+
vars[:"post.pubdate.year"] = t.strftime("%Y")
|
514
|
+
end
|
515
|
+
|
516
|
+
def all_posts(view = nil)
|
517
|
+
posts = []
|
518
|
+
dirs = Dir.children(@root/:posts)
|
519
|
+
dirs.each do |id4|
|
520
|
+
# Skip deleted posts (directories starting with underscore)
|
521
|
+
next if id4.start_with?('_')
|
522
|
+
posts << Scriptorium::Post.read(self, id4)
|
523
|
+
end
|
524
|
+
return posts if view.nil?
|
525
|
+
view = lookup_view(view)
|
526
|
+
posts.select {|x| x.views.include?(view.name) }
|
527
|
+
end
|
528
|
+
|
529
|
+
def generate_post_index(view)
|
530
|
+
view = lookup_view(view)
|
531
|
+
view.generate_post_index
|
532
|
+
end
|
533
|
+
|
534
|
+
def post(id)
|
535
|
+
validate_post_id(id)
|
536
|
+
|
537
|
+
# Check normal directory first
|
538
|
+
meta = @root/:posts/d4(id)/"meta.txt"
|
539
|
+
return Scriptorium::Post.new(self, id) if File.exist?(meta)
|
540
|
+
|
541
|
+
# Check deleted directory (with underscore prefix)
|
542
|
+
deleted_meta = @root/:posts/"_#{d4(id)}"/"meta.txt"
|
543
|
+
return Scriptorium::Post.new(self, id) if File.exist?(deleted_meta)
|
544
|
+
|
545
|
+
# Post not found in either location
|
546
|
+
nil
|
547
|
+
end
|
548
|
+
|
549
|
+
private def validate_post_id(id)
|
550
|
+
raise CannotGetPostIdNil if id.nil?
|
551
|
+
|
552
|
+
raise CannotGetPostIdEmpty if id.to_s.strip.empty?
|
553
|
+
|
554
|
+
unless id.to_s.match?(/^\d+$/)
|
555
|
+
raise CannotGetPostIdInvalid(id)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
def generate_front_page(view)
|
560
|
+
view = lookup_view(view)
|
561
|
+
view.generate_front_page
|
562
|
+
end
|
563
|
+
|
564
|
+
# Reddit integration
|
565
|
+
def reddit
|
566
|
+
@reddit ||= Scriptorium::Reddit.new(self)
|
567
|
+
end
|
568
|
+
|
569
|
+
def autopost_to_reddit(post_data, subreddit = nil)
|
570
|
+
reddit.autopost(post_data, subreddit)
|
571
|
+
end
|
572
|
+
|
573
|
+
def reddit_configured?
|
574
|
+
reddit.configured?
|
575
|
+
end
|
576
|
+
|
577
|
+
private def validate_view_name(name)
|
578
|
+
raise CannotCreateViewNameNil if name.nil?
|
579
|
+
|
580
|
+
raise CannotCreateViewNameEmpty if name.to_s.strip.empty?
|
581
|
+
end
|
582
|
+
|
583
|
+
private def validate_view_title(title)
|
584
|
+
raise CannotCreateViewTitleNil if title.nil?
|
585
|
+
|
586
|
+
raise CannotCreateViewTitleEmpty if title.to_s.strip.empty?
|
587
|
+
end
|
588
|
+
|
589
|
+
def self.generate_os_helpers(root)
|
590
|
+
os_code = case RbConfig::CONFIG['host_os']
|
591
|
+
when /darwin/ # macOS
|
592
|
+
<<~RUBY
|
593
|
+
# Generated at repo creation for macOS
|
594
|
+
def open_file(file_path)
|
595
|
+
system("open", file_path)
|
596
|
+
end
|
597
|
+
RUBY
|
598
|
+
when /linux/ # Linux
|
599
|
+
<<~RUBY
|
600
|
+
# Generated at repo creation for Linux
|
601
|
+
def open_file(file_path)
|
602
|
+
system("xdg-open", file_path)
|
603
|
+
end
|
604
|
+
RUBY
|
605
|
+
when /mswin|mingw|cygwin/ # Windows
|
606
|
+
<<~RUBY
|
607
|
+
# Generated at repo creation for Windows
|
608
|
+
def open_file(file_path)
|
609
|
+
system("start", file_path)
|
610
|
+
end
|
611
|
+
RUBY
|
612
|
+
else
|
613
|
+
<<~RUBY
|
614
|
+
# Generated at repo creation for unknown OS
|
615
|
+
def open_file(file_path)
|
616
|
+
puts " Unable to open file on this OS"
|
617
|
+
end
|
618
|
+
RUBY
|
619
|
+
end
|
620
|
+
|
621
|
+
write_file(root/:config/"os_helpers.rb", os_code)
|
622
|
+
end
|
623
|
+
|
624
|
+
end
|