gryphon_nest 1.3.0 → 2.0.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 +4 -4
- data/bin/nest +4 -6
- data/lib/gryphon_nest/errors.rb +9 -0
- data/lib/gryphon_nest/processors/asset_processor.rb +40 -0
- data/lib/gryphon_nest/processors/mustache_processor.rb +61 -0
- data/lib/gryphon_nest/processors.rb +8 -0
- data/lib/gryphon_nest/renderers/mustache_renderer.rb +42 -0
- data/lib/gryphon_nest/renderers.rb +7 -0
- data/lib/gryphon_nest/version.rb +1 -1
- data/lib/gryphon_nest.rb +35 -124
- metadata +12 -9
- data/lib/gryphon_nest/file_util.rb +0 -51
- data/lib/gryphon_nest/not_found_error.rb +0 -5
- data/lib/gryphon_nest/renderer.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13f76380c2a03e716f50ee3d517a0af74a5423dcea83d563106984d524368e90
|
4
|
+
data.tar.gz: 3bfd1e61ab10066a5a5256eca4081e6e38933dc7b6346af970b638a1eb264efe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1e2a37c7033667a27292820001c85e826dc86792cd7c1a8a98428659432da7b84dab599870491716eba06c08f3432687fc5668ba7aaa8f9b373e4e31137c65e
|
7
|
+
data.tar.gz: 55797182fcea5cba3af32c20ae18016b7ab2fb9a5653a1526e5c27fb540f973e0f27e4f2a450628ea4176d349c3fc0389f64d5b4fdff000517c227f815e0688b
|
data/bin/nest
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
|
4
4
|
require 'gryphon_nest'
|
5
5
|
require 'optparse'
|
6
|
-
require 'sysexits'
|
7
6
|
|
8
7
|
DEFAULT_PORT = 8000
|
8
|
+
EXIT_FAILURE = 1
|
9
9
|
|
10
10
|
options = {
|
11
11
|
port: DEFAULT_PORT
|
@@ -15,11 +15,9 @@ options = {
|
|
15
15
|
# @param parser [OptionParser]
|
16
16
|
def usage_error(msg, parser)
|
17
17
|
warn(msg, parser)
|
18
|
-
|
18
|
+
exit(EXIT_FAILURE)
|
19
19
|
end
|
20
20
|
|
21
|
-
# @param options [Hash]
|
22
|
-
# @return [Array]
|
23
21
|
begin
|
24
22
|
parser = OptionParser.new do |opts|
|
25
23
|
opts.banner = 'Usage: nest [build|serve] [options]'
|
@@ -47,7 +45,7 @@ begin
|
|
47
45
|
GryphonNest.serve_website(options[:port]) if command == 'serve'
|
48
46
|
rescue OptionParser::ParseError => e
|
49
47
|
usage_error(e.message, parser)
|
50
|
-
rescue
|
48
|
+
rescue StandardError => e
|
51
49
|
warn e.message
|
52
|
-
|
50
|
+
exit(EXIT_FAILURE)
|
53
51
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module GryphonNest
|
6
|
+
module Processors
|
7
|
+
class AssetProcessor
|
8
|
+
# @param file [Pathname]
|
9
|
+
# @return [Pathname]
|
10
|
+
def process(file)
|
11
|
+
dest = dest_name(file)
|
12
|
+
|
13
|
+
if file_modified?(file, dest)
|
14
|
+
puts "Copying #{file} to #{dest}"
|
15
|
+
dest.dirname.mkpath
|
16
|
+
FileUtils.copy_file(file, dest)
|
17
|
+
end
|
18
|
+
|
19
|
+
dest
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @param src [Pathname]
|
25
|
+
# @return [Pathname]
|
26
|
+
def dest_name(src)
|
27
|
+
src.sub(CONTENT_DIR, BUILD_DIR)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param src [Pathname]
|
31
|
+
# @param des [Pathname]
|
32
|
+
# @return [Boolean]
|
33
|
+
def file_modified?(src, dest)
|
34
|
+
return true unless dest.exist?
|
35
|
+
|
36
|
+
src.mtime > dest.mtime
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module GryphonNest
|
6
|
+
module Processors
|
7
|
+
class MustacheProcessor
|
8
|
+
# @param renderer [Renderers::MustacheRenderer]
|
9
|
+
def initialize(renderer)
|
10
|
+
@renderer = renderer
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param file [Pathname]
|
14
|
+
# @return [Pathname]
|
15
|
+
# @raise [Errors::YamlError]
|
16
|
+
def process(file)
|
17
|
+
dest = dest_name(file)
|
18
|
+
context = read_context(file)
|
19
|
+
content = @renderer.render_file(file, context)
|
20
|
+
write_file(dest, content)
|
21
|
+
dest
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @param src [Pathname]
|
27
|
+
# @return [Pathname]
|
28
|
+
def dest_name(src)
|
29
|
+
dir = src.dirname
|
30
|
+
path = dir.sub(CONTENT_DIR, BUILD_DIR)
|
31
|
+
basename = src.basename(TEMPLATE_EXT)
|
32
|
+
|
33
|
+
path = path.join(basename) if basename.to_s != 'index'
|
34
|
+
|
35
|
+
path.join('index.html')
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param src [Pathname]
|
39
|
+
# @return [Hash]
|
40
|
+
# @raise [Errors::YamlError]
|
41
|
+
def read_context(src)
|
42
|
+
basename = src.basename(TEMPLATE_EXT)
|
43
|
+
path = "#{DATA_DIR}/#{basename}.yaml"
|
44
|
+
YAML.safe_load_file(path)
|
45
|
+
rescue IOError, Errno::ENOENT
|
46
|
+
{}
|
47
|
+
rescue Psych::SyntaxError => e
|
48
|
+
raise Errors::YamlError, "Encountered error while reading context file. #{e.message}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param path [Pathname]
|
52
|
+
# @param content [String]
|
53
|
+
def write_file(path, content)
|
54
|
+
dir = path.dirname
|
55
|
+
dir.mkpath
|
56
|
+
puts "Creating #{path}"
|
57
|
+
path.write(content)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'htmlbeautifier'
|
4
|
+
require 'mustache'
|
5
|
+
|
6
|
+
module GryphonNest
|
7
|
+
module Renderers
|
8
|
+
# Renders mustache templates to html
|
9
|
+
class MustacheRenderer < Mustache
|
10
|
+
# @param template [String]
|
11
|
+
# @param context [Hash]
|
12
|
+
# @return [String]
|
13
|
+
def render_file(template, context = {})
|
14
|
+
content = super
|
15
|
+
|
16
|
+
layout ||= read_layout_file
|
17
|
+
unless layout.empty?
|
18
|
+
context['yield'] = content
|
19
|
+
content = render(layout, context)
|
20
|
+
end
|
21
|
+
|
22
|
+
HtmlBeautifier.beautify(content)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param name [String]
|
26
|
+
# @return [String]
|
27
|
+
def partial(name)
|
28
|
+
File.read(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# @return [String]
|
34
|
+
def read_layout_file
|
35
|
+
layout_file = @options['layout_file']
|
36
|
+
File.read(layout_file)
|
37
|
+
rescue IOError, Errno::ENOENT
|
38
|
+
''
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/gryphon_nest/version.rb
CHANGED
data/lib/gryphon_nest.rb
CHANGED
@@ -1,39 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'fileutils'
|
4
3
|
require 'pathname'
|
5
4
|
require 'webrick'
|
6
|
-
require 'yaml'
|
7
5
|
|
8
6
|
module GryphonNest
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
7
|
+
autoload :Errors, 'gryphon_nest/errors'
|
8
|
+
autoload :Processors, 'gryphon_nest/processors'
|
9
|
+
autoload :Renderers, 'gryphon_nest/renderers'
|
12
10
|
autoload :VERSION, 'gryphon_nest/version'
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
LAYOUT_DIR = 'layouts'
|
20
|
-
TEMPLATE_EXT = '.mustache'
|
21
|
-
DEFAULT_LAYOUT = Pathname.new("#{LAYOUT_DIR}/main.mustache")
|
12
|
+
BUILD_DIR = '_site'
|
13
|
+
CONTENT_DIR = 'content'
|
14
|
+
DATA_DIR = 'data'
|
15
|
+
TEMPLATE_EXT = '.mustache'
|
16
|
+
LAYOUT_FILE = 'layouts/main.mustache'
|
22
17
|
|
23
|
-
|
18
|
+
class << self
|
19
|
+
# @raise [Errors::NotFoundError]
|
24
20
|
def build_website
|
25
|
-
raise NotFoundError, "Content directory doesn't exist" unless Dir.exist?(CONTENT_DIR)
|
21
|
+
raise Errors::NotFoundError, "Content directory doesn't exist in the current directory" unless Dir.exist?(CONTENT_DIR)
|
26
22
|
|
27
23
|
existing_files = []
|
28
24
|
if Dir.exist?(BUILD_DIR)
|
29
|
-
existing_files =
|
25
|
+
existing_files = glob("#{BUILD_DIR}/**/*")
|
30
26
|
else
|
31
27
|
Dir.mkdir(BUILD_DIR)
|
32
28
|
end
|
33
29
|
|
34
30
|
existing_files = existing_files.difference(process_content)
|
35
|
-
existing_files
|
36
|
-
FileUtil.delete(existing_files)
|
31
|
+
delete_files(existing_files)
|
37
32
|
end
|
38
33
|
|
39
34
|
# @param port [Integer]
|
@@ -47,121 +42,37 @@ module GryphonNest
|
|
47
42
|
|
48
43
|
private
|
49
44
|
|
50
|
-
# @
|
51
|
-
# @return [Pathname]
|
52
|
-
def get_output_name(source_file)
|
53
|
-
dir = source_file.dirname
|
54
|
-
basename = source_file.basename(TEMPLATE_EXT)
|
55
|
-
path = dir.sub(CONTENT_DIR, BUILD_DIR)
|
56
|
-
|
57
|
-
path = path.join(basename) if basename.to_s != 'index'
|
58
|
-
|
59
|
-
path.join('index.html')
|
60
|
-
end
|
61
|
-
|
62
|
-
# @param source_file [Pathname]
|
63
|
-
# @param dest_file [Pathname]
|
64
|
-
# @param context_file [Pathname, nil]
|
65
|
-
# @param layout_file [Pathname, nil]
|
66
|
-
# @return [Boolean]
|
67
|
-
def resources_updated?(source_file, dest_file, context_file, layout_file)
|
68
|
-
return true if FileUtil.file_newer?(source_file, dest_file)
|
69
|
-
|
70
|
-
return true if !context_file.nil? && FileUtil.file_newer?(context_file, dest_file)
|
71
|
-
|
72
|
-
!layout_file.nil? && FileUtil.file_newer?(layout_file, dest_file)
|
73
|
-
end
|
74
|
-
|
75
|
-
# @param name [String]
|
76
|
-
# @param context [Hash]
|
77
|
-
# @return [Pathname, nil]
|
78
|
-
# @raise [NotFoundError]
|
79
|
-
def get_layout_file(name, context)
|
80
|
-
path = Pathname.new(LAYOUT_DIR)
|
81
|
-
|
82
|
-
if context.key?('layout')
|
83
|
-
layout = context['layout']
|
84
|
-
path = path.join(layout)
|
85
|
-
|
86
|
-
raise NotFoundError, "#{name} requires layout file #{layout} but it doesn't exist or can't be read" unless path.exist?
|
87
|
-
|
88
|
-
return path
|
89
|
-
end
|
90
|
-
|
91
|
-
DEFAULT_LAYOUT if DEFAULT_LAYOUT.exist?
|
92
|
-
end
|
93
|
-
|
94
|
-
# @param path [Pathname, nil]
|
95
|
-
# @return [Hash]
|
96
|
-
def get_context(path)
|
97
|
-
return {} if path.nil?
|
98
|
-
|
99
|
-
File.open(path) do |yaml|
|
100
|
-
YAML.safe_load(yaml)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
# @param name [Pathname]
|
105
|
-
# @return [Pathname, nil]
|
106
|
-
def get_context_file(name)
|
107
|
-
basename = name.basename(TEMPLATE_EXT)
|
108
|
-
FileUtil.glob("#{DATA_DIR}/#{basename}.{yaml,yml}")[0]
|
109
|
-
end
|
110
|
-
|
111
|
-
# @param renderer [Renderer]
|
112
|
-
# @param source_file [Pathname]
|
113
|
-
# @param dest_file [Pathname]
|
114
|
-
# @raise [NotFoundError]
|
115
|
-
def process_file(renderer, source_file, dest_file)
|
116
|
-
context_file = get_context_file(source_file)
|
117
|
-
context = get_context(context_file)
|
118
|
-
layout_file = get_layout_file(source_file.basename, context)
|
119
|
-
context['layout'] = layout_file unless layout_file.nil?
|
120
|
-
|
121
|
-
return unless resources_updated?(source_file, dest_file, context_file, layout_file)
|
122
|
-
|
123
|
-
content = renderer.render_file(source_file, context)
|
124
|
-
FileUtil.write_file(dest_file, content)
|
125
|
-
end
|
126
|
-
|
127
|
-
# @return [Array]
|
128
|
-
# @raise [NotFoundError]
|
45
|
+
# @return [Array<Pathname>]
|
129
46
|
def process_content
|
130
|
-
|
47
|
+
processed_files = []
|
48
|
+
renderer = Renderers::MustacheRenderer.new({ 'layout_file' => LAYOUT_FILE })
|
49
|
+
asset_processor = Processors::AssetProcessor.new
|
131
50
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
warn "Skipping non template file #{source_file}"
|
136
|
-
next
|
137
|
-
end
|
51
|
+
processors = {
|
52
|
+
TEMPLATE_EXT => Processors::MustacheProcessor.new(renderer)
|
53
|
+
}
|
138
54
|
|
139
|
-
|
140
|
-
|
141
|
-
|
55
|
+
glob("#{CONTENT_DIR}/**/*").each do |source_file|
|
56
|
+
processor = processors.fetch(source_file.extname, asset_processor)
|
57
|
+
processed_files << processor.process(source_file)
|
142
58
|
end
|
143
59
|
|
144
|
-
|
60
|
+
processed_files
|
145
61
|
end
|
146
62
|
|
147
|
-
# @
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
FileUtil.glob("#{ASSETS_DIR}/**/*").each do |asset|
|
153
|
-
dest = asset.sub(ASSETS_DIR, BUILD_DIR)
|
154
|
-
copied_files << dest
|
155
|
-
|
156
|
-
next unless FileUtil.file_newer?(asset, dest)
|
63
|
+
# @params path [String]
|
64
|
+
# @return [Array<Pathname>]
|
65
|
+
def glob(path)
|
66
|
+
Pathname.glob(path).reject(&:directory?)
|
67
|
+
end
|
157
68
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
69
|
+
# @param junk_files [Array<Pathname>]
|
70
|
+
def delete_files(junk_files)
|
71
|
+
junk_files.each do |f|
|
72
|
+
puts "Deleting #{f}"
|
73
|
+
f.delete
|
162
74
|
end
|
163
|
-
|
164
|
-
copied_files
|
75
|
+
nil
|
165
76
|
end
|
166
77
|
end
|
167
78
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gryphon_nest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher Birmingham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: htmlbeautifier
|
@@ -39,19 +39,19 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: psych
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '3.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: webrick
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,9 +106,12 @@ files:
|
|
106
106
|
- LICENSE
|
107
107
|
- bin/nest
|
108
108
|
- lib/gryphon_nest.rb
|
109
|
-
- lib/gryphon_nest/
|
110
|
-
- lib/gryphon_nest/
|
111
|
-
- lib/gryphon_nest/
|
109
|
+
- lib/gryphon_nest/errors.rb
|
110
|
+
- lib/gryphon_nest/processors.rb
|
111
|
+
- lib/gryphon_nest/processors/asset_processor.rb
|
112
|
+
- lib/gryphon_nest/processors/mustache_processor.rb
|
113
|
+
- lib/gryphon_nest/renderers.rb
|
114
|
+
- lib/gryphon_nest/renderers/mustache_renderer.rb
|
112
115
|
- lib/gryphon_nest/version.rb
|
113
116
|
homepage: https://github.com/chrisBirmingham/gryphon_nest
|
114
117
|
licenses:
|
@@ -131,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
134
|
- !ruby/object:Gem::Version
|
132
135
|
version: '0'
|
133
136
|
requirements: []
|
134
|
-
rubygems_version: 3.
|
137
|
+
rubygems_version: 3.2.33
|
135
138
|
signing_key:
|
136
139
|
specification_version: 4
|
137
140
|
summary: Yet another static site generator
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'fileutils'
|
4
|
-
require 'pathname'
|
5
|
-
|
6
|
-
module GryphonNest
|
7
|
-
class FileUtil
|
8
|
-
# @params path [String]
|
9
|
-
# @return [Array]
|
10
|
-
def self.glob(path)
|
11
|
-
files = Dir.glob(path).reject do |p|
|
12
|
-
File.directory?(p)
|
13
|
-
end
|
14
|
-
|
15
|
-
files.map do |f|
|
16
|
-
Pathname.new(f)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# @param junk_files [Array]
|
21
|
-
def self.delete(junk_files)
|
22
|
-
junk_files.each do |f|
|
23
|
-
puts "Deleting #{f}"
|
24
|
-
FileUtils.remove_file(f)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# @param src [Pathname]
|
29
|
-
# @param dest [Pathname]
|
30
|
-
# @return [Boolean]
|
31
|
-
def self.file_newer?(src, dest)
|
32
|
-
return true unless dest.exist?
|
33
|
-
|
34
|
-
src.mtime > dest.mtime
|
35
|
-
end
|
36
|
-
|
37
|
-
# @param path [Pathname]
|
38
|
-
# @param content [String]
|
39
|
-
def self.write_file(path, content)
|
40
|
-
dir = path.dirname
|
41
|
-
|
42
|
-
unless dir.exist?
|
43
|
-
puts "Creating #{dir}"
|
44
|
-
dir.mkdir
|
45
|
-
end
|
46
|
-
|
47
|
-
puts "Creating #{path}"
|
48
|
-
path.write(content)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'htmlbeautifier'
|
4
|
-
require 'mustache'
|
5
|
-
|
6
|
-
module GryphonNest
|
7
|
-
# Renders mustache templates to html
|
8
|
-
class Renderer < Mustache
|
9
|
-
# @param options [Hash]
|
10
|
-
def initialize(options = {})
|
11
|
-
@layouts = {}
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
# @param template [String]
|
16
|
-
# @param context [Hash]
|
17
|
-
# @return [String]
|
18
|
-
def render_file(template, context = {})
|
19
|
-
content = super
|
20
|
-
|
21
|
-
if context.key?('layout')
|
22
|
-
context['yield'] = content
|
23
|
-
content = super(context['layout'], context)
|
24
|
-
end
|
25
|
-
|
26
|
-
HtmlBeautifier.beautify(content)
|
27
|
-
end
|
28
|
-
|
29
|
-
# @param name [String]
|
30
|
-
# @return [String]
|
31
|
-
def partial(name)
|
32
|
-
return @layouts[name] if @layouts.key?(name)
|
33
|
-
|
34
|
-
content = File.read(name)
|
35
|
-
@layouts[name] = content
|
36
|
-
content
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|