gryphon_nest 4.1.0 → 4.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.
- checksums.yaml +4 -4
- data/bin/nest +1 -2
- data/lib/gryphon_nest/compressors/brotli_compressor.rb +22 -0
- data/lib/gryphon_nest/compressors/gzip_compressor.rb +24 -0
- data/lib/gryphon_nest/compressors.rb +41 -0
- data/lib/gryphon_nest/layout_file.rb +35 -0
- data/lib/gryphon_nest/processors/mustache_processor.rb +28 -20
- data/lib/gryphon_nest/processors.rb +2 -1
- data/lib/gryphon_nest/version.rb +1 -1
- data/lib/gryphon_nest.rb +57 -40
- metadata +6 -3
- data/lib/gryphon_nest/gzip_compressor.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dd5e55ab64ee1c053587293099a0423628f1d9075e4c73e6257fc029feefd47
|
4
|
+
data.tar.gz: b151fc9e7d09114ea9d47c1118f6b275658296f970cd0982a6145ff8c00b0c24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee54f2a45e67a122f2fa2416e2c26500a852f9e729060d7adff553a6e44b0bd897232f09aaef6224f44f102ac861918ced09aecdea0c4084604a867aa669dbb1
|
7
|
+
data.tar.gz: 2cd88219d657d3cd7d8e8d526e8d02a094ea89c684129e644c139d9dce0f2003eaa37b208d86f83086cfa67f65589bded6b71ec0ae8511bd323de1100b98cea6
|
data/bin/nest
CHANGED
@@ -48,8 +48,7 @@ begin
|
|
48
48
|
|
49
49
|
command = ARGV.fetch(0, 'build')
|
50
50
|
|
51
|
-
nest = GryphonNest::Nest.new(options[:force])
|
52
|
-
nest.compressor = GryphonNest::GzipCompressor.new if options[:compress]
|
51
|
+
nest = GryphonNest::Nest.new(options[:compress], options[:force])
|
53
52
|
|
54
53
|
case command
|
55
54
|
when 'clean'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#frozen_string_literal: true
|
2
|
+
|
3
|
+
module GryphonNest
|
4
|
+
module Compressors
|
5
|
+
class BrotliCompressor
|
6
|
+
def extname
|
7
|
+
'.br'
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param file [Pathname]
|
11
|
+
def compress(file)
|
12
|
+
compressed = "#{file}#{extname}"
|
13
|
+
|
14
|
+
File.open(compressed, 'wb') do |br|
|
15
|
+
writer = Brotli::Writer.new(br)
|
16
|
+
writer.write(IO.binread(file))
|
17
|
+
writer.close
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'zlib'
|
4
|
+
|
5
|
+
module GryphonNest
|
6
|
+
module Compressors
|
7
|
+
class GzipCompressor
|
8
|
+
def extname
|
9
|
+
'.gz'
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param file [Pathname]
|
13
|
+
def compress(file)
|
14
|
+
compressed = "#{file}#{extname}"
|
15
|
+
|
16
|
+
Zlib::GzipWriter.open(compressed, Zlib::BEST_COMPRESSION) do |gz|
|
17
|
+
gz.mtime = file.mtime
|
18
|
+
gz.orig_name = file.to_s
|
19
|
+
gz.write IO.binread(file)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GryphonNest
|
4
|
+
module Compressors
|
5
|
+
autoload :BrotliCompressor, 'gryphon_nest/compressors/brotli_compressor'
|
6
|
+
autoload :GzipCompressor, 'gryphon_nest/compressors/gzip_compressor'
|
7
|
+
|
8
|
+
class << self
|
9
|
+
COMPRESSABLE_FILETYPES = %w[
|
10
|
+
.html
|
11
|
+
.htm
|
12
|
+
.xhtml
|
13
|
+
.txt
|
14
|
+
.csv
|
15
|
+
.css
|
16
|
+
.js
|
17
|
+
.mjs
|
18
|
+
.md
|
19
|
+
.xml
|
20
|
+
.svg
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
# @param file [Pathname]
|
24
|
+
# @return [Boolean]
|
25
|
+
def can_compress?(file)
|
26
|
+
file.size >= 20 && COMPRESSABLE_FILETYPES.include?(file.extname)
|
27
|
+
end
|
28
|
+
|
29
|
+
# return [Array<Object>]
|
30
|
+
def create
|
31
|
+
compressors = [GzipCompressor.new]
|
32
|
+
|
33
|
+
require 'brotli'
|
34
|
+
compressors.append(BrotliCompressor.new)
|
35
|
+
compressors
|
36
|
+
rescue LoadError
|
37
|
+
compressors
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GryphonNest
|
4
|
+
# Wrapper class for operations performed on the layout.yaml file
|
5
|
+
class LayoutFile
|
6
|
+
# @param path [Pathname]
|
7
|
+
def initialize(path)
|
8
|
+
@path = path
|
9
|
+
@content = nil
|
10
|
+
@last_mtime = Time.now
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Boolean]
|
14
|
+
def exist?
|
15
|
+
@path.exist?
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Time]
|
19
|
+
def mtime
|
20
|
+
@path.mtime
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [String]
|
24
|
+
def content
|
25
|
+
mod_time = mtime
|
26
|
+
|
27
|
+
if @content.nil? || mod_time > @last_mtime
|
28
|
+
@content = @path.read
|
29
|
+
@last_mtime = mod_time
|
30
|
+
end
|
31
|
+
|
32
|
+
@content
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -8,8 +8,10 @@ module GryphonNest
|
|
8
8
|
# Renders a Mustache template into a html file
|
9
9
|
class MustacheProcessor
|
10
10
|
# @param renderer [Renderers::MustacheRenderer]
|
11
|
-
|
11
|
+
# @param layout_file [LayoutFile]
|
12
|
+
def initialize(renderer, layout_file)
|
12
13
|
@renderer = renderer
|
14
|
+
@layout_file = layout_file
|
13
15
|
end
|
14
16
|
|
15
17
|
# @param src [Pathname]
|
@@ -17,8 +19,6 @@ module GryphonNest
|
|
17
19
|
# @raise [Errors::YamlError]
|
18
20
|
# @raise [Errors::ParseError]
|
19
21
|
def process(src, dest)
|
20
|
-
@layout ||= read_layout_file
|
21
|
-
|
22
22
|
context = read_context(src)
|
23
23
|
content = build_output(src, context)
|
24
24
|
write_file(dest, content)
|
@@ -36,11 +36,21 @@ module GryphonNest
|
|
36
36
|
path.join('index.html')
|
37
37
|
end
|
38
38
|
|
39
|
-
# @param
|
40
|
-
# @param
|
39
|
+
# @param src [Pathname]
|
40
|
+
# @param dest [Pathname]
|
41
41
|
# @return [Boolean]
|
42
|
-
def file_modified?(
|
43
|
-
true
|
42
|
+
def file_modified?(src, dest)
|
43
|
+
return true unless dest.exist?
|
44
|
+
|
45
|
+
mod_time = dest.mtime
|
46
|
+
return true if src.mtime > mod_time
|
47
|
+
|
48
|
+
path = context_file_name(src)
|
49
|
+
return true if path.exist? && path.mtime > mod_time
|
50
|
+
|
51
|
+
return false unless @layout_file.exist?
|
52
|
+
|
53
|
+
@layout_file.mtime > mod_time
|
44
54
|
end
|
45
55
|
|
46
56
|
private
|
@@ -49,25 +59,30 @@ module GryphonNest
|
|
49
59
|
# @return [Hash]
|
50
60
|
# @raise [Errors::YamlError]
|
51
61
|
def read_context(src)
|
52
|
-
|
53
|
-
YAML.safe_load_file(path, symbolize_names: true)
|
62
|
+
YAML.safe_load_file(context_file_name(src), symbolize_names: true)
|
54
63
|
rescue IOError, Errno::ENOENT
|
55
64
|
{}
|
56
65
|
rescue Psych::SyntaxError => e
|
57
66
|
raise Errors::YamlError, "Encountered error while reading context file. Reason: #{e.message}"
|
58
67
|
end
|
59
68
|
|
69
|
+
# @param src [Pathname]
|
70
|
+
# @return [Pathname]
|
71
|
+
def context_file_name(src)
|
72
|
+
src.sub(CONTENT_DIR, DATA_DIR).sub_ext('.yaml')
|
73
|
+
end
|
74
|
+
|
60
75
|
# @param file [Pathname]
|
61
76
|
# @param context [Hash]
|
62
77
|
# @return [String]
|
63
78
|
# @raise [Errors::ParseError]
|
64
79
|
def build_output(file, context)
|
65
80
|
content =
|
66
|
-
if @
|
67
|
-
@renderer.render_file(file, context)
|
68
|
-
else
|
81
|
+
if @layout_file.exist?
|
69
82
|
context[:yield] = file.basename(TEMPLATE_EXT)
|
70
|
-
@renderer.render(@
|
83
|
+
@renderer.render(@layout_file.content, context)
|
84
|
+
else
|
85
|
+
@renderer.render_file(file, context)
|
71
86
|
end
|
72
87
|
|
73
88
|
HtmlBeautifier.beautify(content, stop_on_errors: true)
|
@@ -83,13 +98,6 @@ module GryphonNest
|
|
83
98
|
path.dirname.mkpath
|
84
99
|
path.write(content)
|
85
100
|
end
|
86
|
-
|
87
|
-
# @return [String]
|
88
|
-
def read_layout_file
|
89
|
-
File.read(LAYOUT_FILE)
|
90
|
-
rescue IOError, Errno::ENOENT
|
91
|
-
''
|
92
|
-
end
|
93
101
|
end
|
94
102
|
end
|
95
103
|
end
|
@@ -12,9 +12,10 @@ module GryphonNest
|
|
12
12
|
def create
|
13
13
|
ProcessorRegistry.new do |reg|
|
14
14
|
reg[TEMPLATE_EXT] = proc {
|
15
|
+
layout_file = LayoutFile.new(Pathname(LAYOUT_FILE))
|
15
16
|
renderer = Renderers::MustacheRenderer.new
|
16
17
|
renderer.template_path = CONTENT_DIR
|
17
|
-
Processors::MustacheProcessor.new(renderer)
|
18
|
+
Processors::MustacheProcessor.new(renderer, layout_file)
|
18
19
|
}
|
19
20
|
|
20
21
|
sass_proc = proc { Processors::SassProcessor.new }
|
data/lib/gryphon_nest/version.rb
CHANGED
data/lib/gryphon_nest.rb
CHANGED
@@ -6,8 +6,9 @@ require 'pathname'
|
|
6
6
|
require 'webrick'
|
7
7
|
|
8
8
|
module GryphonNest
|
9
|
+
autoload :Compressors, 'gryphon_nest/compressors'
|
9
10
|
autoload :Errors, 'gryphon_nest/errors'
|
10
|
-
autoload :
|
11
|
+
autoload :LayoutFile, 'gryphon_nest/layout_file'
|
11
12
|
autoload :Logging, 'gryphon_nest/logging'
|
12
13
|
autoload :Processors, 'gryphon_nest/processors'
|
13
14
|
autoload :Renderers, 'gryphon_nest/renderers'
|
@@ -20,27 +21,31 @@ module GryphonNest
|
|
20
21
|
LAYOUT_FILE = 'layout.mustache'
|
21
22
|
|
22
23
|
class Nest
|
23
|
-
# @
|
24
|
-
attr_writer :compressor
|
25
|
-
|
24
|
+
# @param compress [Boolean]
|
26
25
|
# @param force [Boolean]
|
27
|
-
def initialize(force)
|
26
|
+
def initialize(compress, force)
|
28
27
|
@processors = Processors.create
|
29
28
|
@logger = Logging.create
|
30
29
|
@force = force
|
31
|
-
@
|
30
|
+
@compressors = compress ? Compressors.create : []
|
31
|
+
@modifications = 0
|
32
32
|
end
|
33
33
|
|
34
34
|
# @raise [Errors::NotFoundError]
|
35
35
|
def build
|
36
|
-
|
36
|
+
unless Dir.exist?(CONTENT_DIR)
|
37
|
+
raise Errors::NotFoundError, "Content directory doesn't exist in the current directory"
|
38
|
+
end
|
37
39
|
|
38
40
|
Dir.mkdir(BUILD_DIR) unless Dir.exist?(BUILD_DIR)
|
39
41
|
|
40
|
-
existing_files = glob(
|
41
|
-
content_files = glob(
|
42
|
+
existing_files = glob(BUILD_DIR, '{!.gz,!.br}')
|
43
|
+
content_files = glob(CONTENT_DIR)
|
42
44
|
processed_files = content_files.collect { |src| process_file(src) }
|
43
|
-
existing_files.difference(processed_files)
|
45
|
+
files_to_delete = existing_files.difference(processed_files)
|
46
|
+
files_to_delete.each { |file| delete_file(file) }
|
47
|
+
|
48
|
+
@logger.info('No changes detected') if @modifications.zero? && files_to_delete.empty?
|
44
49
|
end
|
45
50
|
|
46
51
|
def clean
|
@@ -50,24 +55,15 @@ module GryphonNest
|
|
50
55
|
|
51
56
|
def watch
|
52
57
|
@logger.info('Watching for content changes')
|
53
|
-
Listen.to(CONTENT_DIR, relative: true) do |modified, added, removed|
|
54
|
-
modified.union(added).each do |file|
|
55
|
-
path = Pathname(file)
|
56
|
-
process_file(path)
|
57
|
-
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
path = @processors[path.extname].dest_name(path)
|
62
|
-
delete_file(path)
|
63
|
-
end
|
64
|
-
end.start
|
59
|
+
# Bypass modification checks, we already know the files been changed
|
60
|
+
@force = true
|
65
61
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
62
|
+
only = [/^#{CONTENT_DIR}/, /^#{DATA_DIR}/, /^#{LAYOUT_FILE}$/]
|
63
|
+
Listen.to('.', relative: true, only: only) do |modified, added, removed|
|
64
|
+
modified.union(added).each { |file| process_changes(file) }
|
65
|
+
|
66
|
+
removed.each { |file| process_changes(file, removal: true) }
|
71
67
|
end.start
|
72
68
|
end
|
73
69
|
|
@@ -89,6 +85,7 @@ module GryphonNest
|
|
89
85
|
dest = processor.dest_name(src)
|
90
86
|
|
91
87
|
if @force || processor.file_modified?(src, dest)
|
88
|
+
@modifications += 1
|
92
89
|
msg = File.exist?(dest) ? 'Recreating' : 'Creating'
|
93
90
|
@logger.info("#{msg} #{dest}")
|
94
91
|
processor.process(src, dest)
|
@@ -100,15 +97,33 @@ module GryphonNest
|
|
100
97
|
|
101
98
|
# @param file [Pathname]
|
102
99
|
def compress_file(file)
|
103
|
-
return
|
100
|
+
return if @compressors.empty?
|
101
|
+
|
102
|
+
return unless Compressors.can_compress?(file)
|
104
103
|
|
105
104
|
@logger.info("Compressing #{file}")
|
106
|
-
|
107
|
-
|
108
|
-
return
|
109
|
-
end
|
105
|
+
@compressors.each { |compressor| compressor.compress(file) }
|
106
|
+
end
|
110
107
|
|
111
|
-
|
108
|
+
# @param src [String]
|
109
|
+
# @param removal [Boolean]
|
110
|
+
def process_changes(src, removal: false)
|
111
|
+
if src == LAYOUT_FILE
|
112
|
+
glob(CONTENT_DIR, TEMPLATE_EXT).each { |file| process_file(file) }
|
113
|
+
else
|
114
|
+
path = Pathname(src)
|
115
|
+
|
116
|
+
if src.start_with?(DATA_DIR)
|
117
|
+
process_data_file(path)
|
118
|
+
elsif removal
|
119
|
+
path = @processors[path.extname].dest_name(path)
|
120
|
+
delete_file(path)
|
121
|
+
else
|
122
|
+
process_file(path)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
rescue StandardError => e
|
126
|
+
@logger.error(e.message)
|
112
127
|
end
|
113
128
|
|
114
129
|
# @param src [Pathname]
|
@@ -121,10 +136,11 @@ module GryphonNest
|
|
121
136
|
process_file(src)
|
122
137
|
end
|
123
138
|
|
124
|
-
# @params
|
139
|
+
# @params base [String]
|
140
|
+
# @params match [String]
|
125
141
|
# @return [Array<Pathname>]
|
126
|
-
def glob(
|
127
|
-
Pathname.glob(
|
142
|
+
def glob(base, match = '')
|
143
|
+
Pathname.glob("#{base}/**/*#{match}").reject(&:directory?)
|
128
144
|
end
|
129
145
|
|
130
146
|
# @param file [Pathname]
|
@@ -132,12 +148,13 @@ module GryphonNest
|
|
132
148
|
@logger.info("Deleting #{file}")
|
133
149
|
file.delete
|
134
150
|
|
135
|
-
|
136
|
-
|
137
|
-
|
151
|
+
@compressors.each do |compressor|
|
152
|
+
compressed_file = "#{file}#{compressor.extname}"
|
153
|
+
next unless compressed_file.exist?
|
138
154
|
|
139
|
-
|
140
|
-
|
155
|
+
@logger.info("Deleting #{compressed_file}")
|
156
|
+
compressed_file.delete
|
157
|
+
end
|
141
158
|
end
|
142
159
|
end
|
143
160
|
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.1
|
4
|
+
version: 4.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher Birmingham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: htmlbeautifier
|
@@ -120,8 +120,11 @@ files:
|
|
120
120
|
- LICENSE
|
121
121
|
- bin/nest
|
122
122
|
- lib/gryphon_nest.rb
|
123
|
+
- lib/gryphon_nest/compressors.rb
|
124
|
+
- lib/gryphon_nest/compressors/brotli_compressor.rb
|
125
|
+
- lib/gryphon_nest/compressors/gzip_compressor.rb
|
123
126
|
- lib/gryphon_nest/errors.rb
|
124
|
-
- lib/gryphon_nest/
|
127
|
+
- lib/gryphon_nest/layout_file.rb
|
125
128
|
- lib/gryphon_nest/logging.rb
|
126
129
|
- lib/gryphon_nest/processors.rb
|
127
130
|
- lib/gryphon_nest/processors/asset_processor.rb
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'zlib'
|
4
|
-
|
5
|
-
module GryphonNest
|
6
|
-
class GzipCompressor
|
7
|
-
# @param file [Pathname]
|
8
|
-
# @return [Boolean]
|
9
|
-
def can_compress?(file)
|
10
|
-
file.size >= 20
|
11
|
-
end
|
12
|
-
|
13
|
-
# @param file [Pathname]
|
14
|
-
def compress(file)
|
15
|
-
compressed = "#{file}.gz"
|
16
|
-
|
17
|
-
Zlib::GzipWriter.open(compressed, Zlib::BEST_COMPRESSION) do |gz|
|
18
|
-
gz.mtime = file.mtime
|
19
|
-
gz.orig_name = file.to_s
|
20
|
-
gz.write IO.binread(file)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|