hyphae 0.2.1

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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/hyphae +54 -0
  3. data/lib/hyphae.rb +166 -0
  4. metadata +59 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 26fffd773e8c0b6c43d8a286fd4be72a835246377634ee003cd41adead85234c
4
+ data.tar.gz: 37abf4892e01b535f8e8585cbf616eae7bbce2d3a27663cb318f3f514d49b4b7
5
+ SHA512:
6
+ metadata.gz: 5aecfc870811743bcd373bbdab8942982e0781554d30f867df1045597550f29caa20bff837d1c461b89925f4632dfcd6f73a7e17b3a73dcf47a01d5aeea750ad
7
+ data.tar.gz: e09d2ac065b089110e335d556494a3fbec5eb80c6456c8f2aa6c0cf66ecd9fd6df58f4023cbe0545149adbd6ca4bcc0d1b45e59f6b60a675c23874ec713414cc
data/bin/hyphae ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'hyphae'
4
+
5
+ ###################
6
+ ### Main Script ###
7
+ ###################
8
+
9
+ # The first argument given is the site input directory
10
+ input_dir = ARGV[0] || "site"
11
+ if not Dir.exist?(input_dir) then
12
+ puts "ERROR: No input directory '#{input_dir}'"
13
+ exit
14
+ end
15
+
16
+ # The second argument is the rendered html site output directory
17
+ output_dir = ARGV[1] || "_build"
18
+
19
+ # Builds the page tree of the site
20
+ if not Dir.exist?(input_dir + "/pages") then
21
+ puts "ERROR: No '/pages' directory in #{input_dir}"
22
+ exit
23
+ end
24
+ page_tree = Hyphae::SiteDir.new(input_dir + '/pages')
25
+
26
+ # Reads site template file
27
+ if not File.exist?(input_dir + "/template.html") then
28
+ puts "ERROR: No 'template.html' file in #{input_dir}"
29
+ exit
30
+ end
31
+ template = File.read(input_dir + "/template.html")
32
+
33
+ # Reads site index content file
34
+ index_content = ''
35
+ if File.exist?(input_dir + "/index.html") then
36
+ index_content = File.read(input_dir + "/template.html")
37
+ end
38
+
39
+ # Makes the output directory
40
+ FileUtils.rm_rf output_dir
41
+ FileUtils.mkdir output_dir
42
+
43
+ # Moves site's static files
44
+ if not Dir.exist?(input_dir + "/static") then
45
+ puts "ERROR: No '/static' directory in #{input_dir}"
46
+ exit
47
+ end
48
+ FileUtils.cp_r(input_dir + '/static', output_dir)
49
+
50
+ # Builds html pages
51
+ page_tree.build(output_dir, template)
52
+
53
+ # Build site index
54
+ page_tree.build_index(output_dir, template, index_content)
data/lib/hyphae.rb ADDED
@@ -0,0 +1,166 @@
1
+ require 'kramdown'
2
+ require 'fileutils'
3
+
4
+ module Hyphae
5
+
6
+ #########################
7
+ ### Utility Functions ###
8
+ #########################
9
+
10
+ # Produces a URL safe version of a string
11
+ def self.sanitize(str)
12
+ str.downcase.gsub(' ', '_').gsub(/[^\d\w_]/, "")
13
+ end
14
+
15
+ # Renders an HTML tag
16
+ def self.make_tag(tag, content, attrs={})
17
+ attrs_str = ""
18
+ attrs.map { |x, y| attrs_str << " #{x}=\"#{y}\""}
19
+ "<#{tag}#{attrs_str}>#{content}</#{tag}>"
20
+ end
21
+
22
+
23
+
24
+ #############################
25
+ ### Site Building Classes ###
26
+ #############################
27
+
28
+ # Site content node, all other site node classes inherit from this one
29
+ class SiteNode
30
+ attr_reader :name, :slug, :path, :parent, :order, :date
31
+
32
+ def initialize(filename, parent=nil)
33
+ @name = File.basename(filename, '.*')
34
+ @parent = parent
35
+ @hidden = false
36
+ @order = nil
37
+ @date = nil
38
+ if captures = @name.match(/^_(.+)/) then
39
+ @hidden = true
40
+ @name = captures[1]
41
+ elsif captures = @name.match(/^o(\d+)_(.+)/) then
42
+ @order = captures[1].to_i
43
+ @name = captures[2]
44
+ elsif captures = @name.match(/^ut(\d+)_(.+)/) then
45
+ @date = Time.at(captures[1].to_i)
46
+ @name = captures[2]
47
+ end
48
+ @slug = Hyphae::sanitize @name
49
+ @path = @parent ? @parent.path + [(@slug)] : []
50
+ return self
51
+ end
52
+
53
+ # Get the root SiteNode of a tree of SiteNodes
54
+ def root
55
+ return self if @parent.nil?
56
+ @parent.root
57
+ end
58
+
59
+ # Compare this SiteNode with another
60
+ def cmp(other)
61
+ if other.order then
62
+ return 1 if @order.nil?
63
+ return @order <=> other.order
64
+ end
65
+ if other.date then
66
+ return 1 if @date.nil?
67
+ return other.date <=> @date
68
+ end
69
+ @name <=> other.name
70
+ end
71
+ end
72
+
73
+
74
+ # A SiteDir represents a directory containing nested SiteNodes
75
+ class SiteDir < SiteNode
76
+ def initialize(filename, parent=nil)
77
+ super
78
+ @children = []
79
+ Dir.glob(filename + '/*').each do |f|
80
+ if File.directory? f then
81
+ @children.push SiteDir.new(f, self)
82
+ elsif File.extname(f) == '.md' then
83
+ @children.push SitePage.new(f, self)
84
+ elsif File.extname(f) == '.link' then
85
+ @children.push SiteLink.new(f, self)
86
+ end
87
+ end
88
+ @children.sort! { |x, y| x.cmp(y) }
89
+ return self
90
+ end
91
+
92
+ # Returns the html for each child's nav menu link
93
+ def menu_items(open_path=[])
94
+ @children.map { |x| x.nav_link(open_path) }.join
95
+ end
96
+
97
+ # Returns the html for this SiteDir's nav menu link
98
+ def nav_link(open_path=[])
99
+ return "" if @hidden
100
+ link = Hyphae::make_tag('span', @name, {'class' => 'nav_menu_link'})
101
+ menu_attrs = { 'class' => 'nav_menu_items' }
102
+ menu_attrs['class'] += ' open' if open_path.first == @slug
103
+ menu = Hyphae::make_tag('div', menu_items(open_path[1..-1] || []), menu_attrs)
104
+ Hyphae::make_tag('div', link + menu, {'class' => 'nav_menu'})
105
+ end
106
+
107
+ # Recursively builds pages for all children
108
+ def build(build_dir, template)
109
+ @children.each { |child| child.build(build_dir, template) }
110
+ end
111
+
112
+ # Builds an index page for this SiteDir
113
+ def build_index(build_dir, template, index_content='')
114
+ page_html = template.gsub('{{navbar}}', menu_items).gsub("{{content}}", index_content).gsub('{{index_class}}', ' index_page')
115
+ File.open("#{build_dir}/index.html", 'w') { |f| f << page_html }
116
+ end
117
+ end
118
+
119
+
120
+ # A SitePage represents a markdown document
121
+ class SitePage < SiteNode
122
+ def initialize(filename, parent)
123
+ super
124
+ markdown = File.read filename
125
+ @html = Kramdown::Document.new(markdown).to_html
126
+ end
127
+
128
+ # Builds the nav menu tag for this page
129
+ def nav_link(open_path=[])
130
+ return "" if @hidden
131
+ attrs = { 'class' => 'nav_page', 'href' => "/#{@path.join('/')}.html" }
132
+ attrs['class'] += ' current_page' if @slug == open_path.first
133
+ Hyphae::make_tag('a', @name, attrs)
134
+ end
135
+
136
+ # Builds html page
137
+ def build(build_dir, template)
138
+ nav = root.menu_items(@path)
139
+ page_html = template.gsub('{{navbar}}', nav).gsub("{{content}}", @html).gsub('{{index_class}}', '')
140
+ page_dir_path = "#{build_dir}/#{@parent.path.join('/')}"
141
+ FileUtils.mkdir_p page_dir_path
142
+ File.open("#{page_dir_path}/#{@slug}.html", 'w') { |f| f << page_html }
143
+ end
144
+ end
145
+
146
+
147
+ # A SiteLink represents an arbitrary link
148
+ class SiteLink < SiteNode
149
+ def initialize(filename, parent)
150
+ super
151
+ @link = File.read(filename).strip
152
+ end
153
+
154
+ # Builds the nav menu tag for this page
155
+ def nav_link(open_path=[])
156
+ return "" if @hidden
157
+ attrs = { 'class' => 'nav_link', 'href' => @link }
158
+ Hyphae::make_tag('a', @name, attrs)
159
+ end
160
+
161
+ # Builds html page (does nothing for a link)
162
+ def build(build_dir, template)
163
+ end
164
+ end
165
+
166
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyphae
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Nicholas Rakita
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-10-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kramdown
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.11.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.11.1
27
+ description: A Minimal SSG
28
+ email: rakita@protonmail.ch
29
+ executables:
30
+ - hyphae
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/hyphae
35
+ - lib/hyphae.rb
36
+ homepage: https://rubygems.org/gems/hyphae
37
+ licenses:
38
+ - Unlicense
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.3.7
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Hyphae
59
+ test_files: []