tiny_wiki 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: af5c72a41a3d369a441ccc793dc7b2d3c7740859bea4d5dc3807731b4ce72441
4
+ data.tar.gz: a9811b3e5d374cf0108561274c2ee58e333afb5e703cbbb2c75bccd629b359d4
5
+ SHA512:
6
+ metadata.gz: 3c5d3f6c6d849c6790f8936c86e1a424faca750d32ec9f0dafefa29e6347b1f26b6df5b0c742c9aa51980be89496dbb2a742ea113281bc5dc592e2e5982b2c57
7
+ data.tar.gz: a34c9d2b61a26389a30eca5a4801f516724b311f32102df402814cae8dee85efb6479dfcb7337ccde8334c5112c34b6f7a6b67ac971d9d653c26e4574ca2c26c
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This is the executable script that users will run to start your wiki server.
4
+
5
+ require 'tiny_wiki' # Require your gem's main file
6
+ require 'optparse' # For command-line argument parsing
7
+
8
+ options = {}
9
+ OptionParser.new do |opts|
10
+ opts.banner = "Usage: tiny_wiki_server [options] <wiki_directory>"
11
+
12
+ opts.on("-p", "--port PORT", Integer, "Port to listen on (default: 4567)") do |p|
13
+ options[:port] = p
14
+ end
15
+
16
+ opts.on("-b", "--bind ADDRESS", "Address to bind to (default: 0.0.0.0)") do |b|
17
+ options[:bind] = b
18
+ end
19
+
20
+ opts.on("-h", "--help", "Prints this help") do
21
+ puts opts
22
+ exit
23
+ end
24
+ end.parse!
25
+
26
+ # The first non-option argument is expected to be the wiki directory
27
+ wiki_directory = ARGV[0]
28
+
29
+ unless wiki_directory
30
+ puts "Error: You must specify a wiki directory."
31
+ puts "Usage: tiny_wiki_server [options] <wiki_directory>"
32
+ exit 1
33
+ end
34
+
35
+ # Ensure the wiki directory exists
36
+ unless File.directory?(wiki_directory)
37
+ puts "Wiki directory '#{wiki_directory}' does not exist. Creating it."
38
+ FileUtils.mkdir_p(wiki_directory)
39
+ end
40
+
41
+ # Set the wiki root path in the Sinatra application settings
42
+ TinyWiki::App.set :wiki_root, File.expand_path(wiki_directory)
43
+
44
+ # Set optional port and bind address
45
+ TinyWiki::App.set :port, options[:port] if options[:port]
46
+ TinyWiki::App.set :bind, options[:bind] if options[:bind]
47
+
48
+ puts "Starting TinyWiki server..."
49
+ puts "Wiki root: #{TinyWiki::App.settings.wiki_root}"
50
+ puts "Listening on #{TinyWiki::App.settings.bind}:#{TinyWiki::App.settings.port}"
51
+ puts "Access your wiki at: http://localhost:#{TinyWiki::App.settings.port}/"
52
+
53
+ # Run the Sinatra application
54
+ TinyWiki::App.run!
@@ -0,0 +1,153 @@
1
+ # This file contains the Sinatra web application logic.
2
+
3
+ require 'sinatra/base'
4
+ require 'redcarpet'
5
+ require 'fileutils'
6
+ require 'uri' # For URI encoding/decoding
7
+
8
+ module TinyWiki
9
+ class App < Sinatra::Base
10
+ # Configuration for the Sinatra app
11
+ # Set the binding address to listen on all interfaces (0.0.0.0)
12
+ set :bind, '0.0.0.0'
13
+ # Set the default port
14
+ set :port, 4567
15
+ # Set the root directory for the Sinatra app (where templates are)
16
+ set :root, File.expand_path('../..', __FILE__)
17
+ # Set the views directory for ERB templates
18
+ set :views, File.join(settings.root, 'tiny_wiki', 'templates')
19
+ # Enable sessions for flash messages (optional, but good for feedback)
20
+ enable :sessions
21
+
22
+ # Custom Redcarpet renderer to handle wiki links (e.g., [[Page Name]])
23
+ class WikiLinkRenderer < Redcarpet::Render::HTML
24
+ # The postprocess method is called after all other rendering is complete.
25
+ # We use it to find and replace our custom wiki link syntax.
26
+ def postprocess(full_document)
27
+ full_document.gsub(/\[\[(.*?)\]\]/) do
28
+ page_name = $1.strip # Get the text inside the brackets
29
+ # Sanitize page name for URL: replace spaces with underscores, then URI encode
30
+ url_safe_page_name = URI.encode_www_form_component(page_name.gsub(' ', '_'))
31
+ "<a href=\"/#{url_safe_page_name}\">#{page_name}</a>"
32
+ end
33
+ end
34
+ end
35
+
36
+ # Markdown renderer setup
37
+ # Create a Redcarpet renderer that uses HTML with code highlighting and auto-links.
38
+ # The `fenced_code_blocks` and `autolink` extensions are common and useful.
39
+ markdown_renderer = WikiLinkRenderer.new(
40
+ filter_html: true,
41
+ hard_wrap: true,
42
+ link_attributes: { rel: "nofollow", target: "_blank" },
43
+ prettify: true,
44
+ with_toc_data: true
45
+ )
46
+ # Create the Redcarpet Markdown object with desired extensions
47
+ @@markdown = Redcarpet::Markdown.new(
48
+ markdown_renderer,
49
+ autolink: true,
50
+ tables: true,
51
+ fenced_code_blocks: true,
52
+ strikethrough: true,
53
+ superscript: true,
54
+ highlight: true,
55
+ quote: true,
56
+ footnotes: true
57
+ )
58
+
59
+ # Helper method to get the full path to a markdown file
60
+ # `page_name` is expected to be URL-decoded and safe for filenames.
61
+ def wiki_file_path(page_name)
62
+ # Ensure the page name doesn't contain directory traversal attempts
63
+ # This is a basic sanitization. For production, more robust checks are needed.
64
+ sanitized_page_name = page_name.gsub(/[^a-zA-Z0-9_\-]/, '') # Allow only alphanumeric, underscore, hyphen
65
+ File.join(settings.wiki_root, "#{sanitized_page_name}.md")
66
+ end
67
+
68
+ # Helper method to read the content of a wiki page
69
+ def read_page(page_name)
70
+ path = wiki_file_path(page_name)
71
+ File.exist?(path) ? File.read(path) : nil
72
+ end
73
+
74
+ # Helper method to write content to a wiki page
75
+ def write_page(page_name, content)
76
+ path = wiki_file_path(page_name)
77
+ # Ensure the directory exists
78
+ FileUtils.mkdir_p(File.dirname(path)) unless File.directory?(File.dirname(path))
79
+ File.write(path, content)
80
+ end
81
+
82
+ # Helper method to convert Markdown to HTML
83
+ def markdown_to_html(markdown_content)
84
+ @@markdown.render(markdown_content)
85
+ end
86
+
87
+ # Helper to get all wiki pages (filenames without extension)
88
+ def all_wiki_pages
89
+ Dir.glob(File.join(settings.wiki_root, '*.md')).map do |file|
90
+ File.basename(file, '.md')
91
+ end.sort
92
+ rescue Errno::ENOENT
93
+ # If the wiki_root directory doesn't exist yet, return an empty array
94
+ []
95
+ end
96
+
97
+ # --- Routes ---
98
+
99
+ # Redirect root to a default page (e.g., 'Home')
100
+ get '/' do
101
+ redirect to('/Home')
102
+ end
103
+
104
+ # List all wiki pages
105
+ get '/_list' do
106
+ @pages = all_wiki_pages
107
+ erb :list # Render a 'list.erb' template (you'll need to create this)
108
+ end
109
+
110
+ # Display a wiki page
111
+ get '/:page_name' do
112
+ @page_name = URI.decode_www_form_component(params[:page_name])
113
+ @markdown_content = read_page(@page_name)
114
+
115
+ if @markdown_content
116
+ @html_content = markdown_to_html(@markdown_content)
117
+ erb :show # Render 'show.erb'
118
+ else
119
+ # Page not found, redirect to edit page
120
+ session[:message] = "Page '#{@page_name}' does not exist. Create it!"
121
+ redirect to("/#{@page_name}/edit")
122
+ end
123
+ end
124
+
125
+ # Show the edit form for a wiki page
126
+ get '/:page_name/edit' do
127
+ @page_name = URI.decode_www_form_component(params[:page_name])
128
+ @markdown_content = read_page(@page_name) || "" # Empty string if new page
129
+ erb :edit # Render 'edit.erb'
130
+ end
131
+
132
+ # Save the content of a wiki page
133
+ post '/:page_name' do
134
+ @page_name = URI.decode_www_form_component(params[:page_name])
135
+ new_content = params[:content]
136
+
137
+ if new_content && !new_content.strip.empty?
138
+ write_page(@page_name, new_content)
139
+ session[:message] = "Page '#{@page_name}' saved successfully!"
140
+ redirect to("/#{@page_name}")
141
+ else
142
+ session[:message] = "Page content cannot be empty!"
143
+ redirect to("/#{@page_name}/edit") # Redirect back to edit with error
144
+ end
145
+ end
146
+
147
+ # Handle 404 errors (page not found)
148
+ not_found do
149
+ status 404
150
+ "<h1>404 - Page Not Found</h1><p>The page you requested does not exist.</p><p><a href='/'>Go Home</a></p>"
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,6 @@
1
+ <h1>Editing <%= @page_name.empty? ? 'New Page' : @page_name %></h1>
2
+
3
+ <form action="/<%= URI.encode_www_form_component(@page_name) %>" method="post">
4
+ <textarea name="content"><%= @markdown_content %></textarea><br>
5
+ <input type="submit" value="Save Page">
6
+ </form>
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Tiny Wiki - <%= @page_name || 'Home' %></title>
7
+ <style>
8
+ body { font-family: sans-serif; margin: 20px; background-color: #f4f4f4; color: #333; }
9
+ .container { max-width: 800px; margin: 0 auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
10
+ h1, h2, h3 { color: #333; }
11
+ a { color: #007bff; text-decoration: none; }
12
+ a:hover { text-decoration: underline; }
13
+ nav { margin-bottom: 20px; }
14
+ nav a { margin-right: 15px; }
15
+ textarea { width: 100%; height: 400px; padding: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; font-family: monospace; }
16
+ input[type="submit"] { background-color: #28a745; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
17
+ input[type="submit"]:hover { background-color: #218838; }
18
+ .message { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; padding: 10px; margin-bottom: 15px; border-radius: 4px; }
19
+ .error-message { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; padding: 10px; margin-bottom: 15px; border-radius: 4px; }
20
+ pre { background-color: #eee; padding: 10px; border-radius: 4px; overflow-x: auto; }
21
+ code { font-family: monospace; background-color: #e9e9e9; padding: 2px 4px; border-radius: 3px; }
22
+ footer {
23
+ margin-top: 30px;
24
+ padding-top: 20px;
25
+ border-top: 1px solid #e0e0e0;
26
+ text-align: center;
27
+ font-size: 0.9em;
28
+ color: #777;
29
+ }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <div class="container">
34
+ <nav>
35
+ <a href="/">Home</a>
36
+ <a href="/_list">All Pages</a>
37
+ <% if @page_name %>
38
+ | <a href="/<%= URI.encode_www_form_component(@page_name) %>/edit">Edit this Page</a>
39
+ <% end %>
40
+ </nav>
41
+
42
+ <% if session[:message] %>
43
+ <div class="message"><%= session.delete(:message) %></div>
44
+ <% end %>
45
+
46
+ <%= yield %>
47
+
48
+ <footer>
49
+ <p>&copy; <%= Time.now.year %> <a href="https://github.com/erikyuzwa/tiny-wiki-gem" target="_blank">Tiny Wiki</a>. All rights reserved.</p>
50
+ <p>Powered by <a href="https://rubyonrails.org/" target="_blank">Ruby</a>, <a href="https://sinatrarb.com/" target="_blank">Sinatra</a>, and <a href="https://github.com/vmg/redcarpet" target="_blank">Redcarpet</a>.</p>
51
+ </footer>
52
+ </div>
53
+ </body>
54
+ </html>
@@ -0,0 +1,11 @@
1
+ <h1>All Wiki Pages</h1>
2
+
3
+ <% if @pages.empty? %>
4
+ <p>No pages found. <a href="/Home/edit">Create your first page!</a></p>
5
+ <% else %>
6
+ <ul>
7
+ <% @pages.each do |page| %>
8
+ <li><a href="/<%= URI.encode_www_form_component(page) %>"><%= page %></a></li>
9
+ <% end %>
10
+ </ul>
11
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <h1><%= @page_name %></h1>
2
+
3
+ <div class="wiki-content">
4
+ <%= @html_content %>
5
+ </div>
@@ -0,0 +1,5 @@
1
+ # Defines the version of the gem.
2
+
3
+ module TinyWiki
4
+ VERSION = "0.2.0"
5
+ end
data/lib/tiny_wiki.rb ADDED
@@ -0,0 +1,11 @@
1
+ # This is the main entry point for your gem.
2
+ # It requires other parts of your gem.
3
+
4
+ require "tiny_wiki/version"
5
+ require "tiny_wiki/app" # This will load your Sinatra application
6
+ require "fileutils" # Ensure FileUtils is available for directory operations
7
+
8
+ module TinyWiki
9
+ # Main module for the gem.
10
+ # No direct code here, it's primarily for namespacing.
11
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tiny_wiki
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Erik Yuzwa
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: sinatra
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: redcarpet
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: fileutils
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.7'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.7'
54
+ - !ruby/object:Gem::Dependency
55
+ name: bundler
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: minitest
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '5.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '5.0'
96
+ description: A Ruby gem that serves a wiki from Markdown files in a local directory.
97
+ email:
98
+ - erikyuzwa@gmail.com
99
+ executables:
100
+ - tiny_wiki_server
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - exe/tiny_wiki_server
105
+ - lib/tiny_wiki.rb
106
+ - lib/tiny_wiki/app.rb
107
+ - lib/tiny_wiki/templates/edit.erb
108
+ - lib/tiny_wiki/templates/layout.erb
109
+ - lib/tiny_wiki/templates/list.erb
110
+ - lib/tiny_wiki/templates/show.erb
111
+ - lib/tiny_wiki/version.rb
112
+ homepage: https://github.com/erikyuzwa/tiny-wiki-gem
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '3.0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubygems_version: 3.6.9
131
+ specification_version: 4
132
+ summary: A simple Markdown-based wiki server gem.
133
+ test_files: []