lei 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/bin/_app/_controllers.rb +116 -0
- data/bin/_app/_helpers.rb +191 -0
- data/bin/_app/_root.rb +199 -0
- data/bin/_app/_src.rb +170 -0
- data/bin/_app/_static.rb +7 -0
- data/bin/_app/_views.rb +122 -0
- data/bin/_app/_welcome.rb +13 -0
- data/bin/_new/_content.rb +44 -0
- data/bin/_new/_custom.rb +71 -0
- data/bin/functions.rb +7 -0
- data/bin/lei +65 -0
- data/example/controllers/content.rb +67 -0
- data/example/controllers/custom.rb +33 -0
- data/example/controllers/test.rb +12 -0
- data/example/helpers/content_helpers.rb +131 -0
- data/example/helpers/global_utils.rb +44 -0
- data/example/spec/spec_helper.rb +100 -0
- data/example/spec/test/content_helper_spec.rb +176 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f87a0bde671b78a0057e812d57d56f072716df67242f9224c02c9787726c9aa2
|
4
|
+
data.tar.gz: dea85cb6c79dff5a7f188dfd376df76839ed02043833f4426f2271cb42225bf4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ed07ec2339b7ca5dd0ff4c501604ea26d78469943f3ece112bb1a73e5716a876b61e7efe797a5232c99268cbf54fea06883da7480fd6b249f5d81a1a2ecee187
|
7
|
+
data.tar.gz: b3e6184e0e83b634b7ad11582dba6aea8a425dd73b00c366b91c01cbafc966d7d9272a1a7580764b85526fcf3d90f832332001abe1349406f7a06ac63fd3de7e
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require_relative "../functions"
|
2
|
+
|
3
|
+
content = <<~CONTENT
|
4
|
+
puts "\\tController: Content"
|
5
|
+
|
6
|
+
class ContentController < Sinatra::Base
|
7
|
+
|
8
|
+
set :content_dir, "\#{$root}/content"
|
9
|
+
set :stylesheet, "content"
|
10
|
+
set :public_folder, $assets_root
|
11
|
+
set :views, $views
|
12
|
+
|
13
|
+
get "/" do
|
14
|
+
content = ContentHelpers.get_content(settings.content_dir)
|
15
|
+
redirect 404 if content.length == 0
|
16
|
+
|
17
|
+
url = request.url
|
18
|
+
|
19
|
+
ContentHelpers.time_sort(content)
|
20
|
+
contentParts = ContentHelpers.paginate(content, params, url)
|
21
|
+
|
22
|
+
slim :content, locals: {
|
23
|
+
**contentParts,
|
24
|
+
style: ContentHelpers.load_css(settings.stylesheet),
|
25
|
+
title: "Posts",
|
26
|
+
url: url
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
get "/post/:title" do
|
31
|
+
title = params["title"]
|
32
|
+
redirect 404 if title.nil? || title.length == 0
|
33
|
+
|
34
|
+
content = ContentHelpers.get_post(settings.content_dir, title)
|
35
|
+
redirect 404 if content.length != 1
|
36
|
+
|
37
|
+
parsed_title = title.split("-").join(" ")
|
38
|
+
|
39
|
+
slim :post, locals: {
|
40
|
+
content: ContentHelpers.parse_md(content)[0],
|
41
|
+
style: ContentHelpers.load_css(settings.stylesheet),
|
42
|
+
title: parsed_title,
|
43
|
+
url: request.url
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
get "/filter" do
|
48
|
+
content = ContentHelpers.get_content(settings.content_dir)
|
49
|
+
|
50
|
+
ContentHelpers.filter_content(content, params)
|
51
|
+
redirect 404 if content.length == 0
|
52
|
+
|
53
|
+
url = request.url
|
54
|
+
|
55
|
+
ContentHelpers.time_sort(content)
|
56
|
+
contentParts = ContentHelpers.paginate(content, params, url)
|
57
|
+
|
58
|
+
slim :search_results, locals: {
|
59
|
+
**contentParts,
|
60
|
+
style: ContentHelpers.load_css(settings.stylesheet),
|
61
|
+
title: "Filtered Results",
|
62
|
+
url: url
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
not_found do
|
67
|
+
slim :notfound, locals: { **ContentHelpers.nf_404, url: request.url }
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
CONTENT
|
72
|
+
|
73
|
+
custom = <<~CUSTOM
|
74
|
+
puts "\\tController: Custom"
|
75
|
+
|
76
|
+
class CustomController < Sinatra::Base
|
77
|
+
|
78
|
+
set :public_folder, $assets_root
|
79
|
+
set :views, $views
|
80
|
+
|
81
|
+
routes = YAML.load_file("\#{$root}/customlist.yml")
|
82
|
+
|
83
|
+
routes.each do |route|
|
84
|
+
name = route["name"]
|
85
|
+
path = route["path"]
|
86
|
+
puts "\\t\\t\#{path}: \#{name}"
|
87
|
+
|
88
|
+
get path do
|
89
|
+
folder = "\#{$root}/\#{name}"
|
90
|
+
|
91
|
+
content = route["content"].map { |section| "\#{folder}/\#{section}" }
|
92
|
+
|
93
|
+
slim name.to_sym, locals: {
|
94
|
+
content: ContentHelpers.parse_md(content),
|
95
|
+
style: ContentHelpers.load_css(name),
|
96
|
+
title: route["title"],
|
97
|
+
url: request.url
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
not_found do
|
103
|
+
slim :notfound, locals: { **ContentHelpers.nf_404, url: request.url }
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
CUSTOM
|
108
|
+
|
109
|
+
files = {
|
110
|
+
"content.rb": content,
|
111
|
+
"custom.rb": custom
|
112
|
+
}
|
113
|
+
|
114
|
+
loc = "$(pwd)/controllers"
|
115
|
+
|
116
|
+
makeFiles(files, loc)
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require_relative "../functions"
|
2
|
+
|
3
|
+
content = <<~CONTENT
|
4
|
+
puts "\\tHelper: Content ---"
|
5
|
+
|
6
|
+
require "redcarpet"
|
7
|
+
|
8
|
+
module ContentHelpers
|
9
|
+
|
10
|
+
CARPET = Redcarpet::Markdown.new(
|
11
|
+
Redcarpet::Render::HTML.new({
|
12
|
+
hard_wrap: true,
|
13
|
+
highlight: true
|
14
|
+
})
|
15
|
+
)
|
16
|
+
|
17
|
+
def self.filter_content(content, params)
|
18
|
+
term = params["term"]
|
19
|
+
if !term.nil? && term.length > 2
|
20
|
+
content.reject! do |c|
|
21
|
+
post = `cat \#{c}`
|
22
|
+
!post.match?(Regexp.new(term, true))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
yyyy = params["year"]
|
27
|
+
if !yyyy.nil? && yyyy.length == 4
|
28
|
+
content.reject! { |c| File::Stat.new(c).mtime.year != yyyy.to_i }
|
29
|
+
end
|
30
|
+
|
31
|
+
mm = params["month"]
|
32
|
+
if !mm.nil? && mm.length == 2
|
33
|
+
content.reject! { |c| File::Stat.new(c).mtime.month != mm.to_i }
|
34
|
+
end
|
35
|
+
|
36
|
+
dd = params["day"]
|
37
|
+
if !dd.nil? && dd.length == 2
|
38
|
+
content.reject! { |c| File::Stat.new(c).mtime.day != dd.to_i }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_content(contentDir)
|
43
|
+
all = Dir.glob("\#{contentDir}/*.md")
|
44
|
+
classified = YAML.load_file($banlist)
|
45
|
+
|
46
|
+
all - classified
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.get_post(contentDir, title)
|
50
|
+
content = self.get_content(contentDir)
|
51
|
+
|
52
|
+
post = "\#{contentDir}/\#{title}.md"
|
53
|
+
|
54
|
+
content.include?(post) ? [ post ] : []
|
55
|
+
end
|
56
|
+
|
57
|
+
<<~load_css
|
58
|
+
This function reads and returns the contents of a CSS file as a string.
|
59
|
+
Its return value gets stored in a variable for ease of interpolation in
|
60
|
+
templates.
|
61
|
+
load_css
|
62
|
+
|
63
|
+
def self.load_css(filename)
|
64
|
+
`cat \#{$style_root}/\#{filename}.css`
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.mp_eu(path, params, replacement)
|
68
|
+
newParams = {}
|
69
|
+
params.each { |k, v| newParams[k] = v }
|
70
|
+
replacement.each { |i, j| newParams[i] = j }
|
71
|
+
encoded = URI.encode_www_form(newParams)
|
72
|
+
|
73
|
+
"\#{path}?\#{encoded}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.nf_404
|
77
|
+
{
|
78
|
+
title: "404: Not Found",
|
79
|
+
style: self.load_css("notfound")
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.paginate(content, params, url)
|
84
|
+
pageParam = params["page"].to_i
|
85
|
+
|
86
|
+
page = pageParam != 0 ? pageParam : 1
|
87
|
+
pages = (content.length / 5.0).ceil
|
88
|
+
|
89
|
+
firstIndex = (page - 1) * 5
|
90
|
+
lastIndex = page * 5 - 1
|
91
|
+
|
92
|
+
path = URI(url).path
|
93
|
+
pageUrls = [ nil, nil, nil, nil ]
|
94
|
+
|
95
|
+
if page > 1
|
96
|
+
pageUrls[0] = self.mp_eu(path, params, { "page" => "1" })
|
97
|
+
|
98
|
+
prev = (page - 1).to_s
|
99
|
+
pageUrls[1] = self.mp_eu(path, params, { "page" => prev })
|
100
|
+
end
|
101
|
+
|
102
|
+
if page < pages
|
103
|
+
foll = (page + 1).to_s
|
104
|
+
pageUrls[2] = self.mp_eu(path, params, { "page" => foll })
|
105
|
+
|
106
|
+
pageUrls[3] = self.mp_eu(path, params, { "page" => pages.to_s })
|
107
|
+
end
|
108
|
+
|
109
|
+
{
|
110
|
+
content: self.parse_md(content[firstIndex..lastIndex]),
|
111
|
+
page: page,
|
112
|
+
pages: pages,
|
113
|
+
pageUrls: pageUrls
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
<<~parse_md
|
118
|
+
This function takes a list of filenames, reads their contents, and
|
119
|
+
utilizes the Redcarpet gem to parse them into HTML.
|
120
|
+
parse_md
|
121
|
+
|
122
|
+
def self.parse_md(filenames)
|
123
|
+
filenames.map do |filename|
|
124
|
+
content = `cat \#{filename}`
|
125
|
+
CARPET.render(content)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.time_sort(content)
|
130
|
+
content.sort_by! { |c| File::Stat.new(c).mtime }
|
131
|
+
content.reverse!
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
CONTENT
|
136
|
+
|
137
|
+
global = <<~GLOBAL
|
138
|
+
puts "\\tHelper: Global Utilities ---"
|
139
|
+
|
140
|
+
require "dotenv"
|
141
|
+
|
142
|
+
module GlobalUtils
|
143
|
+
|
144
|
+
def self.declare_globals
|
145
|
+
puts "\\t\\t--- Loading Environment Variables ---"
|
146
|
+
|
147
|
+
Dotenv.load
|
148
|
+
|
149
|
+
puts "\\t\\t--- Declaring Globals ---"
|
150
|
+
|
151
|
+
$assets_root = "\#{$root}/static"
|
152
|
+
$style_root = "\#{$assets_root}/styles"
|
153
|
+
$views = "\#{$root}/views"
|
154
|
+
|
155
|
+
$banlist = "\#{$root}/banlist.yml"
|
156
|
+
|
157
|
+
<<~AMP
|
158
|
+
AMP Static Header Parts, as of 15 APR 2018:
|
159
|
+
https://www.ampproject.org/docs/fundamentals/spec
|
160
|
+
|
161
|
+
AMP Boilerplate, as of 15 APR 2018:
|
162
|
+
https://www.ampproject.org/docs/fundamentals/spec/amp-boilerplate
|
163
|
+
|
164
|
+
amp-bind, as of 15 APR 2018:
|
165
|
+
https://www.ampproject.org/docs/reference/components/amp-bind
|
166
|
+
AMP
|
167
|
+
|
168
|
+
$amp_static_header = <<~HEADER
|
169
|
+
<meta charset="utf-8">
|
170
|
+
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
|
171
|
+
<script async src="https://cdn.ampproject.org/v0.js"></script>
|
172
|
+
HEADER
|
173
|
+
|
174
|
+
$amp_boiler = <<~BOILER
|
175
|
+
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
|
176
|
+
BOILER
|
177
|
+
|
178
|
+
$amp_bind = "<script async custom-element=\\"amp-bind\\" src=\\"https://cdn.ampproject.org/v0/amp-bind-0.1.js\\"></script>"
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
GLOBAL
|
183
|
+
|
184
|
+
files = {
|
185
|
+
"content_helpers.rb": content,
|
186
|
+
"global_utils.rb": global
|
187
|
+
}
|
188
|
+
|
189
|
+
loc = "$(pwd)/helpers"
|
190
|
+
|
191
|
+
makeFiles(files, loc)
|
data/bin/_app/_root.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require_relative "../functions"
|
2
|
+
|
3
|
+
banlist = <<~BANLIST
|
4
|
+
---
|
5
|
+
# Paths of content from `$root`.
|
6
|
+
|
7
|
+
- ./banned-content.md
|
8
|
+
...
|
9
|
+
BANLIST
|
10
|
+
|
11
|
+
config = <<~CONFIG
|
12
|
+
require "rack/deflater"
|
13
|
+
require "rack/protection"
|
14
|
+
require "sinatra"
|
15
|
+
require "slim"
|
16
|
+
require "yaml"
|
17
|
+
|
18
|
+
# Project Root
|
19
|
+
|
20
|
+
$root = File.dirname(__FILE__)
|
21
|
+
|
22
|
+
# Global Settings and Utilities
|
23
|
+
|
24
|
+
puts "--- Loading Helpers ---"
|
25
|
+
|
26
|
+
utils_root = "\#{$root}/helpers"
|
27
|
+
|
28
|
+
require "\#{utils_root}/global_utils"
|
29
|
+
GlobalUtils.declare_globals
|
30
|
+
|
31
|
+
require "\#{utils_root}/content_helpers"
|
32
|
+
|
33
|
+
# Sinatra Configuration
|
34
|
+
|
35
|
+
configure do
|
36
|
+
use Rack::Deflater
|
37
|
+
use Rack::Protection, except: [ :remote_token, :session_hijacking ]
|
38
|
+
|
39
|
+
set :server, :puma
|
40
|
+
set :bind, "0.0.0.0"
|
41
|
+
end
|
42
|
+
|
43
|
+
puts "--- Loading Controllers ---"
|
44
|
+
|
45
|
+
controllers = YAML.load_file("\#{$root}/controllerlist.yml")
|
46
|
+
|
47
|
+
controllers.each do |controller|
|
48
|
+
require "\#{$root}/controllers/\#{controller["file"]}"
|
49
|
+
|
50
|
+
map(controller["path"]) { run eval(controller["name"]) }
|
51
|
+
end
|
52
|
+
CONFIG
|
53
|
+
|
54
|
+
controllerlist = <<~CONTROLLERLIST
|
55
|
+
---
|
56
|
+
-
|
57
|
+
file: custom
|
58
|
+
name: CustomController
|
59
|
+
path: /
|
60
|
+
-
|
61
|
+
file: content
|
62
|
+
name: ContentController
|
63
|
+
path: /content
|
64
|
+
...
|
65
|
+
CONTROLLERLIST
|
66
|
+
|
67
|
+
customlist = <<~CUSTOMLIST
|
68
|
+
---
|
69
|
+
-
|
70
|
+
name: welcome
|
71
|
+
path: /
|
72
|
+
title: Welcome
|
73
|
+
content:
|
74
|
+
- welcome.md
|
75
|
+
...
|
76
|
+
CUSTOMLIST
|
77
|
+
|
78
|
+
gems = <<~GEMS
|
79
|
+
source "https://rubygems.org"
|
80
|
+
|
81
|
+
gem "dotenv", "~>2.2.1"
|
82
|
+
gem "puma", "~>3.11.3"
|
83
|
+
gem "rack", "~>2.0.4"
|
84
|
+
gem "rake", "~>12.3.1"
|
85
|
+
gem "redcarpet", "~>3.4.0"
|
86
|
+
gem "rspec", "~>3.7.0"
|
87
|
+
gem "sinatra", "~>2.0.3"
|
88
|
+
gem "slim", "~>3.0.9"
|
89
|
+
GEMS
|
90
|
+
|
91
|
+
gulp = <<~GULP
|
92
|
+
const exec = require("child_process").exec;
|
93
|
+
const gulp = require("gulp");
|
94
|
+
const pump = require("pump");
|
95
|
+
|
96
|
+
const cleanCSS = require("gulp-clean-css");
|
97
|
+
const less = require("gulp-less");
|
98
|
+
|
99
|
+
const lessFiles = `${__dirname}/src/styles/*.less`;
|
100
|
+
const excludedLess = `!${__dirname}/src/styles/_*.less`;
|
101
|
+
const cssDest = `${__dirname}/static/styles`;
|
102
|
+
|
103
|
+
async function shrink() {
|
104
|
+
console.log("** Compiling and minifying .less **");
|
105
|
+
|
106
|
+
await pump(
|
107
|
+
[
|
108
|
+
gulp.src([ lessFiles, excludedLess ]),
|
109
|
+
less(),
|
110
|
+
cleanCSS(),
|
111
|
+
gulp.dest(cssDest)
|
112
|
+
],
|
113
|
+
function(err) {
|
114
|
+
console.log("--- err:", err);
|
115
|
+
}
|
116
|
+
);
|
117
|
+
}
|
118
|
+
|
119
|
+
gulp.task("shrink", shrink);
|
120
|
+
|
121
|
+
gulp.task("default", gulp.series("shrink", function() {
|
122
|
+
console.log("--- Starting .less watcher ---");
|
123
|
+
|
124
|
+
gulp.watch(lessFiles).on("change", shrink);
|
125
|
+
}));
|
126
|
+
GULP
|
127
|
+
|
128
|
+
ignore = <<~IGNORE
|
129
|
+
# Environment Variables
|
130
|
+
.env
|
131
|
+
nohup.out
|
132
|
+
|
133
|
+
# Certificates
|
134
|
+
*.pem
|
135
|
+
*.cert
|
136
|
+
|
137
|
+
# Mac File System Artifacts
|
138
|
+
.DS_Store
|
139
|
+
|
140
|
+
# Node.js Dependencies
|
141
|
+
node_modules
|
142
|
+
|
143
|
+
# ** Content **
|
144
|
+
# *.md
|
145
|
+
IGNORE
|
146
|
+
|
147
|
+
launch = <<~LAUNCH
|
148
|
+
#!/bin/bash
|
149
|
+
|
150
|
+
# Provide port number as argument.
|
151
|
+
puma -b tcp://0.0.0.0:$1
|
152
|
+
LAUNCH
|
153
|
+
|
154
|
+
package = <<~PACKAGE
|
155
|
+
{
|
156
|
+
"name": "project-name",
|
157
|
+
"version": "1.0.0",
|
158
|
+
"repository": "repository-url",
|
159
|
+
"author": "your-name",
|
160
|
+
"license": "MIT",
|
161
|
+
"dependencies": {
|
162
|
+
"gulp": "^4.0.0",
|
163
|
+
"gulp-clean-css": "3.10.0",
|
164
|
+
"gulp-less": "^4.0.0",
|
165
|
+
"gulp-uglify": "^3.0.0",
|
166
|
+
"pump": "3.0.0"
|
167
|
+
}
|
168
|
+
}
|
169
|
+
PACKAGE
|
170
|
+
|
171
|
+
loc = "$(pwd)"
|
172
|
+
|
173
|
+
dirs = [
|
174
|
+
"content",
|
175
|
+
"controllers",
|
176
|
+
"helpers",
|
177
|
+
"index",
|
178
|
+
"spec",
|
179
|
+
"src",
|
180
|
+
"static",
|
181
|
+
"views",
|
182
|
+
"welcome"
|
183
|
+
]
|
184
|
+
|
185
|
+
makeDirs(dirs, loc)
|
186
|
+
|
187
|
+
files = {
|
188
|
+
".gitignore": ignore,
|
189
|
+
"banlist.yml": banlist,
|
190
|
+
"config.ru": config,
|
191
|
+
"controllerlist.yml": controllerlist,
|
192
|
+
"customlist.yml": customlist,
|
193
|
+
"Gemfile": gems,
|
194
|
+
"gulpfile.js": gulp,
|
195
|
+
"launch.sh": launch,
|
196
|
+
"package.json": package
|
197
|
+
}
|
198
|
+
|
199
|
+
makeFiles(files, loc)
|