lei 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|