etblog 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/etblog +77 -0
- data/lib/etblog.rb +146 -0
- data/lib/index-default-etblog.html +29 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f8f54c99f9525eaf6bc88d00b08e8fa36c3c0a41d1b7f1792ecb0e4cbd26cddb
|
4
|
+
data.tar.gz: 9e4f74ba1b718cbeb1f443cc7c6a41eb7e207bcb81d08db20417a5cd5b5996d1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1ea5499df4b76e876bc98093d0f4093b68a881b7a26165586e35eee543a3b57229614fa6228efca44afab0d8bd01ce164f8ab2c9cfa3f5a857fe4b24f2c6c72c
|
7
|
+
data.tar.gz: 3324141ce656a3885dc98ae533fbb6ff4710d5dc7da7f39b0324427979955f01881f11334ba58f9d236496ea12be9aac107c738025a99acd83f7c2e271d9e92f
|
data/bin/etblog
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'etblog'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'socket'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
action = ARGV[0] || 'help'
|
8
|
+
if ['-help', '--help', '-h', '-?', 'help'].include? action then
|
9
|
+
puts "Usage: #{$0} init # creates a new blog in PWD"
|
10
|
+
puts " #{$0} build # compiles the blog into HTML files"
|
11
|
+
puts " #{$0} serve # starts a web server with the blog"
|
12
|
+
puts " #{$0} help # shows this help message"
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
if action == 'init' then
|
17
|
+
ETBlogCore.new(Dir.pwd)
|
18
|
+
puts "Created a new ETBlog blog in #{Dir.pwd}."
|
19
|
+
puts ""
|
20
|
+
puts "What to do next?"
|
21
|
+
puts " - Add some blog posts. It's simple. Just create a Markdown file with the prefix \"post-\" prepended before the actual filename, open it in your favorite editor and start editing. No need to specify the date or front matters."
|
22
|
+
puts " Example for macOS and Linux users:"
|
23
|
+
puts " touch post-hello.md"
|
24
|
+
puts " echo \"# Hello world\" > post-hello.md"
|
25
|
+
puts " echo \"Look how cool my new blog is\" >> post-hello.md"
|
26
|
+
puts " #{$0} build"
|
27
|
+
puts ""
|
28
|
+
puts " - Add a new theme. By default, the theme that comes with ETBlog looks really old-fashioned. Try adapting an existing Bootstrap or UIKit theme. Notice that a theme cannot contain any internal JavaScript or CSS dependencies - it can only use extenal libraries and JS, such as Bootstrap from CDNJS. After you are finished with the theme, save it to \"index.html\". And yes, you should definetly check out this guide on creating themes: http://timkoi.gitlab.io/etblog/newtheme"
|
29
|
+
puts " - Build your blog. The resulting blog files with be placed into \"static\". Copy over that folder onto GitLab Pages or your web server and enjoy your new blog!"
|
30
|
+
puts ""
|
31
|
+
puts "Have a great time using ETBlog!"
|
32
|
+
elsif action == 'build' || action == 'make' then
|
33
|
+
ETBlogCore.new(Dir.pwd).build
|
34
|
+
puts "Done!"
|
35
|
+
elsif action == 'serve' || action == 'server' || action == 'demo' then
|
36
|
+
if not File.exists? Dir.pwd + '/blog.plist' then
|
37
|
+
raise "There is no blog in #{Dir.pwd} - blog.plist does not exist. Try running \"#{$0} init\" first?"
|
38
|
+
end
|
39
|
+
temp_dir = "/tmp/etblogdemo#{rand(9999)}"
|
40
|
+
if ENV['OS'] == 'Windows_NT' then
|
41
|
+
temp_dir = Dir.pwd + '/' + File.dirname(test_dir)
|
42
|
+
end
|
43
|
+
if File.directory? temp_dir then
|
44
|
+
FileUtils.rm_rf(temp_dir)
|
45
|
+
end
|
46
|
+
port = ARGV[1] || 8080
|
47
|
+
if String === port then
|
48
|
+
port = port.to_i
|
49
|
+
end
|
50
|
+
puts "Running on port #{port}..."
|
51
|
+
server = TCPServer.new('0.0.0.0', port)
|
52
|
+
loop do
|
53
|
+
socket = server.accept
|
54
|
+
ETBlogCore.new(Dir.pwd, temp_dir).build
|
55
|
+
path = temp_dir + '/' + socket.gets.split(' ')[1]
|
56
|
+
if File.directory? path then
|
57
|
+
path += '/index.html'
|
58
|
+
end
|
59
|
+
if not File.exists? path then
|
60
|
+
socket.print "HTTP/1.1 404 Not Found\r\n"
|
61
|
+
socket.print "Content-Type: text/plain\r\n"
|
62
|
+
socket.print "Connection: close\r\n\r\n"
|
63
|
+
socket.print "NOT FOUND"
|
64
|
+
else
|
65
|
+
socket.print "HTTP/1.1 200 OK\r\n"
|
66
|
+
socket.print "Content-Type: text/html\r\n"
|
67
|
+
socket.print "Connection: close\r\n\r\n"
|
68
|
+
socket.print File.read(path)
|
69
|
+
end
|
70
|
+
socket.close
|
71
|
+
FileUtils.rm_rf(temp_dir)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
raise "Unknown action - \"#{action}\"."
|
75
|
+
end
|
76
|
+
|
77
|
+
exit 0
|
data/lib/etblog.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# etblog.rb - Extremely Tiny Blog
|
3
|
+
require 'plist'
|
4
|
+
require 'kramdown'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
def maplists(list1, list2)
|
8
|
+
hash = {}
|
9
|
+
index2 = -1
|
10
|
+
list1.each do |item|
|
11
|
+
index2 += 1
|
12
|
+
hash[item] = list2[index2]
|
13
|
+
end
|
14
|
+
return hash
|
15
|
+
end
|
16
|
+
|
17
|
+
class String
|
18
|
+
def unxml
|
19
|
+
inside_tag = false
|
20
|
+
out = ''
|
21
|
+
self.split('').each do |item|
|
22
|
+
if item == '<' then
|
23
|
+
inside_tag = true
|
24
|
+
elsif item == '>' then
|
25
|
+
inside_tag = false
|
26
|
+
elsif inside_tag == false then
|
27
|
+
out += item
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return out
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ETBlogCore
|
35
|
+
def initialize(folder, static_custom=nil)
|
36
|
+
if not File.directory? folder then
|
37
|
+
raise 'No such directory - ' + folder
|
38
|
+
end
|
39
|
+
@base = folder.clone
|
40
|
+
@output = @base + '/htdocs'
|
41
|
+
@conf = @base + '/blog.plist'
|
42
|
+
if not File.exists? @conf then
|
43
|
+
File.write(@conf, { 'Title' => 'Blog',
|
44
|
+
'Author' => ENV['USER'] || 'You',
|
45
|
+
'Static' => 'static',
|
46
|
+
'Description' => 'Your new ETBlog blog.'}.to_plist )
|
47
|
+
end
|
48
|
+
plist_parsed = Plist.parse_xml(@conf)
|
49
|
+
@title = plist_parsed['Title']
|
50
|
+
@author = plist_parsed['Author']
|
51
|
+
@static = @base + '/' + plist_parsed['Static'].to_s
|
52
|
+
if static_custom != nil then
|
53
|
+
@static = static_custom.clone
|
54
|
+
end
|
55
|
+
@description = plist_parsed['Description'] || 'Your new ETBlog blog.'
|
56
|
+
@description += '<br><p>Powered by <a href="http://timkoi.gitlab.io/etblog">ETBlog</a>.</p>'
|
57
|
+
if not File.directory? @static then
|
58
|
+
FileUtils.mkdir_p(@static)
|
59
|
+
end
|
60
|
+
@links = []
|
61
|
+
plist_parsed.keys.each do |key|
|
62
|
+
if not ['Title', 'Author', 'Static', 'Description'].include? key then
|
63
|
+
@links.push("<li><a href=\"#{plist_parsed[key]}\">#{key}</a></li>")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if @links.length < 1 then
|
67
|
+
@links.push("<li><a href=\"http://timkoi.gitlab.io/etblog\">ETBlog homepage</a></li>")
|
68
|
+
@links.push("<li><a href=\"http://github.com/timkoi\">@timkoi on GitHub</a></li>")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def findimages(string)
|
73
|
+
possible_image = false
|
74
|
+
inside_image_tag = false
|
75
|
+
img = ''
|
76
|
+
out = []
|
77
|
+
string.split('').each do |item|
|
78
|
+
if item == '!' then
|
79
|
+
possible_image = true
|
80
|
+
elsif item == '(' && possible_image then
|
81
|
+
possible_image = false
|
82
|
+
inside_image_tag = true
|
83
|
+
elsif item == ')' && inside_image_tag then
|
84
|
+
inside_image_tag = false
|
85
|
+
out.push(img.clone)
|
86
|
+
img = ''
|
87
|
+
elsif inside_image_tag then
|
88
|
+
img += item
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return out
|
92
|
+
end
|
93
|
+
|
94
|
+
def conv(string, put_ctnt)
|
95
|
+
return string.to_s.gsub('@{post}', put_ctnt).gsub('@{title}', @title).gsub('@{description}', @description).gsub('@{links}', '<ul>' + @links.join("") + '</ul>').gsub('@{author}', @author).gsub("@{year}", Time.now.year.to_s)
|
96
|
+
end
|
97
|
+
|
98
|
+
def build
|
99
|
+
if not File.exists? @base + '/index.html' then
|
100
|
+
FileUtils.cp(File.absolute_path(File.dirname(__FILE__)) + '/index-default-etblog.html', @base + '/index.html')
|
101
|
+
end
|
102
|
+
newest_post = ''
|
103
|
+
all_posts = Dir.glob(@base + '/post-*.md').sort_by{ |f| File.mtime(f) }.reverse
|
104
|
+
all_posts_headings = []
|
105
|
+
all_posts_outputs = []
|
106
|
+
ctnt_homepage = ''
|
107
|
+
file_first = true
|
108
|
+
all_posts.each do |item|
|
109
|
+
output = '/posts/' + File.basename(item) + '/index.html'
|
110
|
+
all_posts_outputs.push(output.clone)
|
111
|
+
output = @static + output
|
112
|
+
if not File.directory? File.dirname(output) then
|
113
|
+
FileUtils.mkdir_p(File.dirname(output))
|
114
|
+
end
|
115
|
+
FileUtils.cp(@base + '/index.html', output)
|
116
|
+
ctnt = File.read(output)
|
117
|
+
put_ctnt = File.read(item)
|
118
|
+
all_images = self.findimages(put_ctnt)
|
119
|
+
all_images.each do |item|
|
120
|
+
if File.exists? @base + '/' + item then
|
121
|
+
FileUtils.cp(@base + '/' + item, File.dirname(output) + '/' + item)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
heading = Kramdown::Document.new(put_ctnt.gsub("\r\n", "\n").split("\n")[0]).to_html.unxml
|
125
|
+
all_posts_headings.push(heading)
|
126
|
+
put_ctnt = "<p><a href=\"../../index.html\">< Back to the homepage</a></p><br>" + Kramdown::Document.new(put_ctnt).to_html
|
127
|
+
ctnt = self.conv(ctnt, put_ctnt)
|
128
|
+
File.write(output, ctnt)
|
129
|
+
puts "#{item} => #{output}"
|
130
|
+
if newest_post == '' || File.mtime(newest_post) < File.mtime(item) then
|
131
|
+
newest_post = item
|
132
|
+
end
|
133
|
+
end
|
134
|
+
ctnt_for_main = ''
|
135
|
+
maplists(all_posts_headings, all_posts).each do |key, value|
|
136
|
+
ctnt_for_main += "<h2><a href=\"posts/#{File.basename(value)}/index.html\">#{key}</a></h2>"
|
137
|
+
ctnt_for_main += "<p><i style=\"color: darkgrey;\">Posted on #{File.mtime(value).to_s} by #{@author}</i></p>"
|
138
|
+
ctnt_for_main += "<hr>"
|
139
|
+
end
|
140
|
+
if ctnt_for_main == '' then
|
141
|
+
ctnt_for_main = 'Nothing was posted on this blog yet. Stay tuned!'
|
142
|
+
end
|
143
|
+
File.write(@static + '/index.html', self.conv(File.read(@base + '/index.html'), ctnt_for_main))
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>@{title}</title>
|
4
|
+
</head>
|
5
|
+
<body style="font-family: Helvetica;">
|
6
|
+
<div style="background-color: slateblue; color: white; border: 2px solid slateblue;">
|
7
|
+
<h1> @{title}</title>
|
8
|
+
</div>
|
9
|
+
<table style="width: 100%">
|
10
|
+
<tr>
|
11
|
+
<td style="width: 80%" valign="top">
|
12
|
+
@{post}
|
13
|
+
</td>
|
14
|
+
<td valign="top">
|
15
|
+
<h4>Links</h4>
|
16
|
+
@{links}
|
17
|
+
</h4>
|
18
|
+
<h4>About this blog</h4>
|
19
|
+
@{description}
|
20
|
+
</h4>
|
21
|
+
<h4>About this theme</h4>
|
22
|
+
This is a demo theme that is used as a placeholder. Please customize it because, as you know, it is lightweight, but it is also ugly.
|
23
|
+
</td>
|
24
|
+
</tr>
|
25
|
+
</table>
|
26
|
+
<hr>
|
27
|
+
<p><i style="color: darkgrey;">Copyright (C) @{author} @{year}.</i></p>
|
28
|
+
</body>
|
29
|
+
</html>
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: etblog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tim K
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-22 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A really tiny static blog generator written in Ruby.
|
14
|
+
email: timprogrammer@rambler.ru
|
15
|
+
executables:
|
16
|
+
- etblog
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/etblog
|
21
|
+
- lib/etblog.rb
|
22
|
+
- lib/index-default-etblog.html
|
23
|
+
homepage: http://timkoi.gitlab.io/etblog
|
24
|
+
licenses:
|
25
|
+
- MIT
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubygems_version: 3.0.4
|
43
|
+
signing_key:
|
44
|
+
specification_version: 4
|
45
|
+
summary: A really tiny static blog generator written in Ruby.
|
46
|
+
test_files: []
|