florby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +2 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +28 -0
- data/LICENSE +21 -0
- data/README.md +1 -0
- data/Rakefile +4 -0
- data/exe/florby +5 -0
- data/florby.gemspec +29 -0
- data/lib/florby/builder.rb +84 -0
- data/lib/florby/cli.rb +19 -0
- data/lib/florby/collection.rb +24 -0
- data/lib/florby/config.rb +28 -0
- data/lib/florby/page.rb +68 -0
- data/lib/florby/plugins/wiki_link_replacer.rb +35 -0
- data/lib/florby/plugins.rb +6 -0
- data/lib/florby/renderer.rb +29 -0
- data/lib/florby/server.rb +22 -0
- data/lib/florby/version.rb +5 -0
- data/lib/florby.rb +7 -0
- data/sig/tororo.rbs +4 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 213febbaf5b4ac4037ec69a179102882cf3aee4d5db0d1b9a1481d78af8ad46e
|
4
|
+
data.tar.gz: 5824623b2f65617a09a37780a7e870b47fce2454c0569d65f40f9cf96c76b027
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 06712e6540eb8fa39a879a4da61b4a8b1f367240ecad537ebcac6e63327352c659df3c8a32362f1777dd01a85b9432e6a4b22a6f6df28fdc333c0d64dcfa650e
|
7
|
+
data.tar.gz: d88c5b4c34b3f99163a14ae6856c0544c24579b68eacfc8866e3ceda3e10305c6d7f363131855d0eccc66e5a6aa60c1007b721030e0418c385cc40ffcc878f48
|
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
florby (0.1.0)
|
5
|
+
commonmarker
|
6
|
+
sitemap_generator
|
7
|
+
webrick
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
builder (3.2.4)
|
13
|
+
commonmarker (1.0.4-arm64-darwin)
|
14
|
+
rake (13.1.0)
|
15
|
+
sitemap_generator (6.3.0)
|
16
|
+
builder (~> 3.0)
|
17
|
+
webrick (1.8.1)
|
18
|
+
|
19
|
+
PLATFORMS
|
20
|
+
arm64-darwin-21
|
21
|
+
arm64-darwin-22
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
florby!
|
25
|
+
rake (~> 13.0)
|
26
|
+
|
27
|
+
BUNDLED WITH
|
28
|
+
2.3.26
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Yuki Akamatsu
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Florby
|
data/Rakefile
ADDED
data/exe/florby
ADDED
data/florby.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/florby/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "florby"
|
7
|
+
spec.version = Florby::VERSION
|
8
|
+
spec.authors = ["AKAMATSU Yuki"]
|
9
|
+
spec.email = ["y.akamatsu@ukstudio.jp"]
|
10
|
+
|
11
|
+
spec.summary = "a static site generator for digital garden"
|
12
|
+
spec.required_ruby_version = ">= 2.6.0"
|
13
|
+
|
14
|
+
spec.metadata["homepage_uri"] = 'https://github.com/ukstudio/florby'
|
15
|
+
spec.metadata["source_code_uri"] = 'https://github.com/ukstudio/florby'
|
16
|
+
|
17
|
+
spec.files = Dir.chdir(__dir__) do
|
18
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency "commonmarker"
|
27
|
+
spec.add_dependency "webrick"
|
28
|
+
spec.add_dependency "sitemap_generator"
|
29
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'sitemap_generator'
|
3
|
+
|
4
|
+
require 'florby/page'
|
5
|
+
require 'florby/plugins'
|
6
|
+
require 'florby/renderer'
|
7
|
+
require 'florby/collection'
|
8
|
+
require 'florby/config'
|
9
|
+
|
10
|
+
module Florby
|
11
|
+
class Builder
|
12
|
+
def self.run
|
13
|
+
new.run
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
remove_old_files
|
18
|
+
|
19
|
+
@config = Florby::Config.new
|
20
|
+
if File.exist?(File.join(Dir.pwd, 'config.rb'))
|
21
|
+
@config.load_file('config.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
copy_files
|
25
|
+
|
26
|
+
collection = Collection.new
|
27
|
+
Plugins::WikiLinkReplacer.new(collection: collection).replace!
|
28
|
+
|
29
|
+
collection.all_pages.each do |page|
|
30
|
+
html = Renderer.new(page: page, collection: collection).render
|
31
|
+
write_html(page.permalink, html)
|
32
|
+
page.aliases.each do |alias_path|
|
33
|
+
write_html(alias_path, alias_html(page.permalink))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
SitemapGenerator::Sitemap.default_host = @config.fetch('host')
|
38
|
+
SitemapGenerator::Sitemap.public_path = '_build'
|
39
|
+
SitemapGenerator::Sitemap.compress = false
|
40
|
+
SitemapGenerator::Sitemap.create do
|
41
|
+
collection.all_pages.each do |page|
|
42
|
+
add page.permalink, lastmod: page.updated
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private def remove_old_files
|
48
|
+
puts 'remove old files'
|
49
|
+
FileUtils.rm_rf(Dir.glob(File.join(Dir.pwd, '_build', '*')))
|
50
|
+
end
|
51
|
+
|
52
|
+
private def copy_files
|
53
|
+
@config.copy_files.each do |path|
|
54
|
+
puts "copy #{path}"
|
55
|
+
FileUtils.cp_r(File.join(Dir.pwd, 'src', path), File.join(Dir.pwd, '_build'))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private def write_html(permalink, html)
|
60
|
+
paths = permalink.split('/')
|
61
|
+
|
62
|
+
if paths.empty?
|
63
|
+
File.open(File.join(Dir.pwd, '_build', 'index.html'), 'w+') do |f|
|
64
|
+
f.write(html)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
full_path = File.join('_build', File.join(paths))
|
68
|
+
FileUtils.mkdir_p(File.join(Dir.pwd, full_path))
|
69
|
+
File.open(File.join(Dir.pwd, full_path, 'index.html'), 'w+') do |f|
|
70
|
+
f.write(html)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private def alias_html(canonical)
|
76
|
+
<<~HTML
|
77
|
+
<html>
|
78
|
+
<meta http-equiv="refresh" content="0; URL='#{@config.fetch('host')}#{canonical}'">
|
79
|
+
<link rel="canonical" href="#{@config.fetch('host')}#{canonical}">
|
80
|
+
</html>
|
81
|
+
HTML
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/florby/cli.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Florby
|
2
|
+
require 'florby/builder'
|
3
|
+
require 'florby/server'
|
4
|
+
|
5
|
+
class CLI
|
6
|
+
def self.run(args)
|
7
|
+
subcommand = args.shift
|
8
|
+
|
9
|
+
case subcommand
|
10
|
+
when "build"
|
11
|
+
Florby::Builder.run
|
12
|
+
when "server"
|
13
|
+
Florby::Server.run
|
14
|
+
else
|
15
|
+
puts "unknown command"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Florby
|
2
|
+
class Collection
|
3
|
+
def initialize
|
4
|
+
files = Dir.glob(File.join(Dir.pwd, 'src', '*.md'))
|
5
|
+
@hash = files.map { |file| [File.basename(file).gsub(/\.md$/, ''), Page.new(file: file)] }.to_h
|
6
|
+
end
|
7
|
+
|
8
|
+
def titles
|
9
|
+
@hash.keys
|
10
|
+
end
|
11
|
+
|
12
|
+
def all_pages
|
13
|
+
@hash.values.sort_by(&:created).reverse
|
14
|
+
end
|
15
|
+
|
16
|
+
def pages
|
17
|
+
all_pages.reject(&:exclude_from_collections?)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find(title)
|
21
|
+
@hash[title]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Florby
|
2
|
+
class Config
|
3
|
+
def initialize
|
4
|
+
@options = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def load_file(path)
|
8
|
+
instance_eval(File.read(File.join(Dir.pwd, path)))
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch(key)
|
12
|
+
@options.fetch(key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def copy_from(path)
|
16
|
+
@options['copy_from'] ||= []
|
17
|
+
@options['copy_from'] << path
|
18
|
+
end
|
19
|
+
|
20
|
+
def copy_files
|
21
|
+
@options['copy_from'] ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
def host(url)
|
25
|
+
@options['host'] = url
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/florby/page.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'commonmarker'
|
2
|
+
require 'yaml'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Florby
|
6
|
+
class Page
|
7
|
+
attr_reader :meta
|
8
|
+
|
9
|
+
def initialize(file:)
|
10
|
+
@file = file
|
11
|
+
@meta = {}
|
12
|
+
@markdown = File.read(@file)
|
13
|
+
frontmatter, @body = @markdown.split("---\n", 3).last(2)
|
14
|
+
@frontmatter = YAML.unsafe_load(frontmatter)
|
15
|
+
end
|
16
|
+
|
17
|
+
def filename
|
18
|
+
@filename ||= File.basename(@file)
|
19
|
+
end
|
20
|
+
|
21
|
+
def title
|
22
|
+
@frontmatter['title'] ||filename.gsub(/\.md$/, '')
|
23
|
+
end
|
24
|
+
|
25
|
+
def permalink
|
26
|
+
link = @frontmatter['permalink'] || title
|
27
|
+
link = "/#{link}" unless link =~ /^\//
|
28
|
+
link = "#{link}/" unless link =~ /\/$/
|
29
|
+
link
|
30
|
+
end
|
31
|
+
|
32
|
+
def created
|
33
|
+
if @frontmatter['created'].is_a?(String)
|
34
|
+
Date.parse(@frontmatter['created'])
|
35
|
+
else
|
36
|
+
@frontmatter['created'] || File::Stat.new(@file).birthtime.to_date
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def updated
|
41
|
+
if @frontmatter['updated'].is_a?(String)
|
42
|
+
Date.parse(@frontmatter['updated'])
|
43
|
+
else
|
44
|
+
@frontmatter['updated'] || File::Stat.new(@file).mtime.to_date
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def exclude_from_collections?
|
49
|
+
@frontmatter['exclude_from_collections'] == 'true'
|
50
|
+
end
|
51
|
+
|
52
|
+
def layout
|
53
|
+
@frontmatter['layout'] || 'default'
|
54
|
+
end
|
55
|
+
|
56
|
+
def aliases
|
57
|
+
@frontmatter['aliases'] || []
|
58
|
+
end
|
59
|
+
|
60
|
+
def content
|
61
|
+
@content ||= Commonmarker.to_html(@body, options: { extension: { tagfilter: false, autolink: true }, render: { unsafe: true } })
|
62
|
+
end
|
63
|
+
|
64
|
+
def content=(html)
|
65
|
+
@content = html
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Florby
|
4
|
+
module Plugins
|
5
|
+
class WikiLinkReplacer
|
6
|
+
WIKI_LINK_REGEXP = /\[\[\s?([^\[\]\|\n\r]+)(\|[^\[\]\|\n\r]+)?\s?\]\]/
|
7
|
+
|
8
|
+
def initialize(collection:)
|
9
|
+
@collection = collection
|
10
|
+
end
|
11
|
+
|
12
|
+
def replace!
|
13
|
+
@collection.all_pages.each do |page|
|
14
|
+
page.meta['backlinks'] ||= []
|
15
|
+
|
16
|
+
page.content =
|
17
|
+
page.content.gsub(WIKI_LINK_REGEXP) do |match|
|
18
|
+
|
19
|
+
title = CGI.unescapeHTML(Regexp.last_match(1))
|
20
|
+
destination = @collection.find(title)
|
21
|
+
|
22
|
+
unless page.exclude_from_collections?
|
23
|
+
destination.meta['backlinks'] ||= []
|
24
|
+
destination.meta['backlinks'] << page
|
25
|
+
end
|
26
|
+
|
27
|
+
raise "Page not found: #{title}. #{@collection.titles}" unless destination
|
28
|
+
|
29
|
+
"<a href='#{destination.permalink}'>#{destination.title}</a>"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Florby
|
4
|
+
class Renderer
|
5
|
+
DEFAULT_TEMPLATE = <<~ERB
|
6
|
+
<html>
|
7
|
+
<meta charset="utf-8">
|
8
|
+
<body>
|
9
|
+
<%= page.content %>
|
10
|
+
</body>
|
11
|
+
</html>
|
12
|
+
ERB
|
13
|
+
attr_reader :page, :collection
|
14
|
+
|
15
|
+
def initialize(page:, collection:)
|
16
|
+
@page = page
|
17
|
+
@collection = collection
|
18
|
+
|
19
|
+
if File.exist?(File.join(Dir.pwd, 'layouts', "#{page.layout}.erb"))
|
20
|
+
@layout = File.read(File.join(Dir.pwd, 'layouts', "#{page.layout}.erb"))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def render
|
25
|
+
erb = ERB.new(@layout || DEFAULT_TEMPLATE)
|
26
|
+
erb.result(binding)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
|
3
|
+
module Florby
|
4
|
+
class Server
|
5
|
+
def self.run
|
6
|
+
new.run
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
server = WEBrick::HTTPServer.new(
|
11
|
+
DocumentRoot: File.join(Dir.pwd, '_build'),
|
12
|
+
Port: 8081,
|
13
|
+
)
|
14
|
+
|
15
|
+
trap(:INT) do
|
16
|
+
server.shutdown
|
17
|
+
end
|
18
|
+
|
19
|
+
server.start
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/florby.rb
ADDED
data/sig/tororo.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: florby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- AKAMATSU Yuki
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-01-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: commonmarker
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: webrick
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sitemap_generator
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- y.akamatsu@ukstudio.jp
|
58
|
+
executables:
|
59
|
+
- florby
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".rubocop.yml"
|
64
|
+
- Gemfile
|
65
|
+
- Gemfile.lock
|
66
|
+
- LICENSE
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- exe/florby
|
70
|
+
- florby.gemspec
|
71
|
+
- lib/florby.rb
|
72
|
+
- lib/florby/builder.rb
|
73
|
+
- lib/florby/cli.rb
|
74
|
+
- lib/florby/collection.rb
|
75
|
+
- lib/florby/config.rb
|
76
|
+
- lib/florby/page.rb
|
77
|
+
- lib/florby/plugins.rb
|
78
|
+
- lib/florby/plugins/wiki_link_replacer.rb
|
79
|
+
- lib/florby/renderer.rb
|
80
|
+
- lib/florby/server.rb
|
81
|
+
- lib/florby/version.rb
|
82
|
+
- sig/tororo.rbs
|
83
|
+
homepage:
|
84
|
+
licenses: []
|
85
|
+
metadata:
|
86
|
+
homepage_uri: https://github.com/ukstudio/florby
|
87
|
+
source_code_uri: https://github.com/ukstudio/florby
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.6.0
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubygems_version: 3.3.26
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: a static site generator for digital garden
|
107
|
+
test_files: []
|