florby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ AllCops:
2
+ DisabledByDefault: true
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake", "~> 13.0"
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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
data/exe/florby ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "florby"
4
+
5
+ Florby::CLI.run(ARGV)
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
@@ -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,6 @@
1
+ require 'florby/plugins/wiki_link_replacer'
2
+
3
+ module Florby
4
+ module Plugins
5
+ end
6
+ 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Florby
4
+ VERSION = "0.1.0"
5
+ end
data/lib/florby.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "florby/version"
4
+ require_relative "florby/cli"
5
+
6
+ module Florby
7
+ end
data/sig/tororo.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Tororo
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
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: []