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 +7 -0
- data/exe/tiny_wiki_server +54 -0
- data/lib/tiny_wiki/app.rb +153 -0
- data/lib/tiny_wiki/templates/edit.erb +6 -0
- data/lib/tiny_wiki/templates/layout.erb +54 -0
- data/lib/tiny_wiki/templates/list.erb +11 -0
- data/lib/tiny_wiki/templates/show.erb +5 -0
- data/lib/tiny_wiki/version.rb +5 -0
- data/lib/tiny_wiki.rb +11 -0
- metadata +133 -0
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>© <%= 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 %>
|
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: []
|