gryphon 1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: aec8dfc0ffa76193cd8528f8375b6eea622aed8499d3b340945f38a1dce1bb31
4
+ data.tar.gz: 030fbf23c1515be0a8a207184c5f574bf8cc2d2a4574e7f92d239be6271437ac
5
+ SHA512:
6
+ metadata.gz: 6b30add9bae44b30943a4bdb0ad03da9c95c09edc80b1f8881c0fb728de6cec3efd3f911094356eff0b2a3c060d4ba5c64e84709fa0d1ec7f62b61003303bf4d
7
+ data.tar.gz: 7083d7c2b16b3d0b19a074dc8adc954e4b7329b3b9787dc751362c8c10a3af1a7d51d06d0aac59c5ad6b91b192be31ed2bb650058145bace57e5ebdd5e2a87eb
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/bin/gryphon ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'gryphon'
5
+
6
+ ARGV << '-h' if ARGV.empty?
7
+
8
+ cli = Gryphon::Cli.new
9
+ cli.run
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+
5
+ module Gryphon
6
+ # Command line parsing and execution class
7
+ class Cli
8
+ COMMANDS = %w[
9
+ build
10
+ clean
11
+ serve
12
+ ].freeze
13
+
14
+ def run
15
+ options = {
16
+ compress: false,
17
+ force: false,
18
+ port: 8000,
19
+ watch: false
20
+ }
21
+
22
+ parse!(options)
23
+ execute(ARGV[0], options)
24
+ rescue Errors::GryphonError => e
25
+ warn e.message
26
+ exit false
27
+ end
28
+
29
+ private
30
+
31
+ # @param command [String]
32
+ # @param options [Hash]
33
+ # @raise [Errors::GryphonError]
34
+ def execute(command, options)
35
+ raise Errors::GryphonError, to_usage_error("Unknown command #{command}") unless COMMANDS.include?(command)
36
+
37
+ nest = Gryphon::Nest.new(
38
+ Processors.create,
39
+ options[:compress] ? Compressors.create : [],
40
+ options[:force]
41
+ )
42
+
43
+ if command == 'clean'
44
+ nest.clean
45
+ else
46
+ nest.build
47
+
48
+ nest.serve(options[:port], options[:watch]) if command == 'serve'
49
+ end
50
+ end
51
+
52
+ # @param options [Hash]
53
+ # @raise [Errors::GryphonError]
54
+ def parse!(options)
55
+ OptionParser.new do |opts|
56
+ opts.banner = 'Usage: gryphon [build|serve|clean] [options]
57
+ Yet another static website builder using mustache and sass'
58
+
59
+ opts.separator ''
60
+
61
+ opts.on('-c', '--compress', 'Create compressed versions of each file')
62
+
63
+ opts.on('-f', '--force', 'Force (re)build all files')
64
+
65
+ opts.on('-p', '--port [PORT]', Integer, 'Port to run dev server on')
66
+
67
+ opts.on('-w', '--watch', 'Watch for file changes when running the local server')
68
+
69
+ opts.on('-h', '--help', 'Show this message') do
70
+ puts opts
71
+ exit
72
+ end
73
+
74
+ opts.on('-v', '--version', 'Print version') do
75
+ puts Gryphon::VERSION
76
+ exit
77
+ end
78
+ end.parse!(into: options)
79
+ rescue OptionParser::ParseError => e
80
+ raise Errors::GryphonError, to_usage_error(e.message)
81
+ end
82
+
83
+ # @param msg [String]
84
+ # @return [String]
85
+ def to_usage_error(msg) = "gryphon: #{msg}\nTry 'gryphon -h' for more information"
86
+ end
87
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
4
+ module Compressors
5
+ # Class for compressing files using brotli
6
+ class BrotliCompressor
7
+ # @return [String]
8
+ def extname = '.br'
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(File.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 Gryphon
6
+ module Compressors
7
+ # Class for compressing files using zlib
8
+ class GzipCompressor
9
+ # @return [String]
10
+ def extname = '.gz'
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(File.binread(file))
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
4
+ module Compressors
5
+ autoload :BrotliCompressor, 'gryphon/compressors/brotli_compressor'
6
+ autoload :GzipCompressor, 'gryphon/compressors/gzip_compressor'
7
+
8
+ class << self
9
+ COMPRESSABLE = %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 compressable?(file) = file.size >= 40 && COMPRESSABLE.include?(file.extname)
26
+
27
+ # return [Array<Object>]
28
+ def create
29
+ compressors = [GzipCompressor.new]
30
+
31
+ begin
32
+ require 'brotli'
33
+ compressors << BrotliCompressor.new
34
+ rescue LoadError
35
+ # Do nothing
36
+ end
37
+
38
+ compressors
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
4
+ module Errors
5
+ class GryphonError < StandardError; end
6
+
7
+ class NotFoundError < GryphonError; end
8
+
9
+ class ParseError < GryphonError; end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
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? = @path.exist?
15
+
16
+ # @return [Time]
17
+ def mtime = @path.mtime
18
+
19
+ # @return [String]
20
+ def content
21
+ mod_time = mtime
22
+
23
+ if @content.nil? || mod_time > @last_mtime
24
+ @content = @path.read
25
+ @last_mtime = mod_time
26
+ end
27
+
28
+ @content
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Gryphon
6
+ # Mixin class for setting up log creation and logging
7
+ module Logging
8
+ include Kernel
9
+
10
+ # @param msg [String]
11
+ # @param lvl [Integer]
12
+ def log(msg, lvl = Logger::INFO)
13
+ @logger ||= create
14
+ @logger.add(lvl, msg)
15
+ end
16
+
17
+ private
18
+
19
+ # @return [Logger]
20
+ def create
21
+ logger = Logger.new($stdout)
22
+
23
+ # Create formatter that matches WebBricks log messages
24
+ logger.formatter = proc do |severity, datetime, _progname, msg|
25
+ date_format = datetime.strftime('%Y-%m-%d %H:%M:%S')
26
+ "[#{date_format}] #{severity.ljust(5)} #{msg}\n"
27
+ end
28
+
29
+ logger
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module Gryphon
6
+ module Processors
7
+ # Default file processor. Moves files from source to destination
8
+ class AssetProcessor
9
+ # @param src [Pathname]
10
+ # @param dest [Pathname]
11
+ def process(src, dest)
12
+ dest.dirname.mkpath
13
+ FileUtils.copy_file(src, dest)
14
+ end
15
+
16
+ # @param src [Pathname]
17
+ # @return [Pathname]
18
+ def dest_name(src) = src.sub(CONTENT_DIR, BUILD_DIR)
19
+
20
+ # @param src [Pathname]
21
+ # @param des [Pathname]
22
+ # @return [Boolean]
23
+ def file_modified?(src, dest)
24
+ return true unless dest.exist?
25
+
26
+ src.mtime > dest.mtime
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'htmlbeautifier'
4
+
5
+ module Gryphon
6
+ module Processors
7
+ # Renders a Mustache template into a html file
8
+ class MustacheProcessor
9
+ # @param renderer [Renderers::MustacheRenderer]
10
+ # @param layout_file [LayoutFile]
11
+ def initialize(renderer, layout_file)
12
+ @renderer = renderer
13
+ @layout_file = layout_file
14
+ end
15
+
16
+ # @param src [Pathname]
17
+ # @param dest [Pathname]
18
+ # @raise [Errors::ParseError]
19
+ def process(src, dest)
20
+ content = build_output(src)
21
+ write_file(dest, content)
22
+ end
23
+
24
+ # @param src [Pathname]
25
+ # @return [Pathname]
26
+ def dest_name(src)
27
+ dir = src.dirname
28
+ path = dir.sub(CONTENT_DIR, BUILD_DIR)
29
+ basename = src.basename(TEMPLATE_EXT)
30
+
31
+ path = path.join(basename) if basename.to_s != 'index'
32
+
33
+ path.join('index.html')
34
+ end
35
+
36
+ # @param src [Pathname]
37
+ # @param dest [Pathname]
38
+ # @return [Boolean]
39
+ def file_modified?(src, dest)
40
+ return true unless dest.exist?
41
+
42
+ mod_time = dest.mtime
43
+ return true if src.mtime > mod_time
44
+
45
+ return false unless @layout_file.exist?
46
+
47
+ @layout_file.mtime > mod_time
48
+ end
49
+
50
+ private
51
+
52
+ # @param file [Pathname]
53
+ # @return [String]
54
+ # @raise [Errors::ParseError]
55
+ def build_output(file)
56
+ content =
57
+ if @layout_file.exist?
58
+ @renderer.render(@layout_file.content, { yield: file.basename(TEMPLATE_EXT) })
59
+ else
60
+ @renderer.render_file(file)
61
+ end
62
+
63
+ HtmlBeautifier.beautify(content, stop_on_errors: true)
64
+ rescue Mustache::Parser::SyntaxError, Psych::SyntaxError => e
65
+ raise Errors::ParseError, "Failed to process mustache template #{file}. Reason: #{e.message}"
66
+ rescue RuntimeError => e
67
+ raise Errors::ParseError, "Failed to beautify template output #{file}. Reason: #{e.message}"
68
+ end
69
+
70
+ # @param path [Pathname]
71
+ # @param content [String]
72
+ def write_file(path, content)
73
+ path.dirname.mkpath
74
+ path.write(content)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
4
+ module Processors
5
+ # Renders a sass file into a css file
6
+ class SassProcessor
7
+ # @param src [Pathname]
8
+ # @param dest [Pathname]
9
+ # @raise [Errors::ParseError]
10
+ def process(src, dest)
11
+ result = Sass.compile(src)
12
+ File.write(dest, result.css)
13
+ rescue Sass::CompileError => e
14
+ raise Errors::ParseError, "Failed to process sass style sheet #{src}. Reason: #{e.full_message}"
15
+ end
16
+
17
+ # @param src [Pathname]
18
+ # @return [Pathname]
19
+ def dest_name(src) = src.sub(CONTENT_DIR, BUILD_DIR).sub_ext('.css')
20
+
21
+ # @param src [Pathname]
22
+ # @param des [Pathname]
23
+ # @return [Boolean]
24
+ def file_modified?(src, dest)
25
+ return true unless dest.exist?
26
+
27
+ src.mtime > dest.mtime
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
4
+ module Processors
5
+ autoload :AssetProcessor, 'gryphon/processors/asset_processor'
6
+ autoload :MustacheProcessor, 'gryphon/processors/mustache_processor'
7
+ autoload :SassProcessor, 'gryphon/processors/sass_processor.rb'
8
+
9
+ class << self
10
+ # @return [Array<Object>]
11
+ def create
12
+ processors = Hash.new(AssetProcessor.new)
13
+
14
+ layout_file = LayoutFile.new(Pathname(LAYOUT_FILE))
15
+ renderer = Renderers::MustacheRenderer.new
16
+ renderer.template_path = CONTENT_DIR
17
+ processors[TEMPLATE_EXT] = Processors::MustacheProcessor.new(renderer, layout_file)
18
+
19
+ begin
20
+ require 'sass-embedded'
21
+ sass = Processors::SassProcessor.new
22
+ processors['.scss'] = sass
23
+ processors['.sass'] = sass
24
+ rescue LoadError
25
+ # Do nothing
26
+ end
27
+
28
+ processors
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mustache'
4
+ require 'yaml'
5
+
6
+ module Gryphon
7
+ module Renderers
8
+ # Class to override default Mustache behavior
9
+ class MustacheRenderer < Mustache
10
+ # @param _name [String]
11
+ # @return [String]
12
+ # @raise [Psych::SyntaxError]
13
+ def partial(_name)
14
+ name = context[:yield]
15
+ path = "#{template_path}/#{name}.#{template_extension}"
16
+ docs = YAML.safe_load_stream(File.read(path), filename: name)
17
+ content = docs[1] || docs[0]
18
+
19
+ context.push(docs[0]) unless docs[1].nil?
20
+
21
+ content
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
4
+ module Renderers
5
+ autoload :MustacheRenderer, 'gryphon/renderers/mustache_renderer'
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gryphon
4
+ VERSION = '1.0.0'
5
+ end
data/lib/gryphon.rb ADDED
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'listen'
5
+ require 'pathname'
6
+ require 'webrick'
7
+
8
+ module Gryphon
9
+ autoload :Cli, 'gryphon/cli'
10
+ autoload :Compressors, 'gryphon/compressors'
11
+ autoload :Errors, 'gryphon/errors'
12
+ autoload :LayoutFile, 'gryphon/layout_file'
13
+ autoload :Logging, 'gryphon/logging'
14
+ autoload :Processors, 'gryphon/processors'
15
+ autoload :Renderers, 'gryphon/renderers'
16
+ autoload :VERSION, 'gryphon/version'
17
+
18
+ BUILD_DIR = '_site'
19
+ CONTENT_DIR = 'content'
20
+ TEMPLATE_EXT = '.mustache'
21
+ LAYOUT_FILE = 'layout.mustache'
22
+
23
+ class Nest
24
+ include Logging
25
+
26
+ # @param processors [Array<Object>]
27
+ # @param compressors [Array<Object>]
28
+ # @param force [Boolean]
29
+ def initialize(processors, compressors, force)
30
+ @processors = processors
31
+ @compressors = compressors
32
+ @force = force
33
+ @modifications = 0
34
+ end
35
+
36
+ # @raise [Errors::GryphonError]
37
+ def build
38
+ unless Dir.exist?(CONTENT_DIR)
39
+ raise Errors::NotFoundError, "Content directory doesn't exist in the current directory"
40
+ end
41
+
42
+ FileUtils.mkdir_p(BUILD_DIR)
43
+ existing_files = glob(BUILD_DIR, '{!.gz,!.br}')
44
+ content_files = glob(CONTENT_DIR)
45
+ processed_files = content_files.collect { |src| process_file(src) }
46
+ files_to_delete = existing_files.difference(processed_files)
47
+ files_to_delete.each { |file| delete_file(file) }
48
+
49
+ log('No changes detected') if @modifications.zero? && files_to_delete.empty?
50
+ end
51
+
52
+ def clean
53
+ FileUtils.remove_dir(BUILD_DIR, true)
54
+ log('Removed build dir')
55
+ end
56
+
57
+ # @param port [Integer]
58
+ # @param monitor [Boolean]
59
+ def serve(port, monitor)
60
+ watch if monitor
61
+
62
+ log("Running local server on #{port}")
63
+ server = WEBrick::HTTPServer.new(Port: port, DocumentRoot: BUILD_DIR, AccessLog: [])
64
+ # Trap ctrl c so we don't get the horrible stack trace
65
+ trap('INT') { server.shutdown }
66
+ server.start
67
+ end
68
+
69
+ private
70
+
71
+ def watch
72
+ log('Watching for content changes')
73
+
74
+ # Bypass modification checks, we already know the files been changed
75
+ @force = true
76
+
77
+ only = [/^#{CONTENT_DIR}/, /^#{LAYOUT_FILE}$/]
78
+ Listen.to('.', relative: true, only: only) do |modified, added, removed|
79
+ modified.union(added).each { |file| process_changes(file) }
80
+
81
+ removed.each { |file| process_changes(file, removal: true) }
82
+ end.start
83
+ end
84
+
85
+ # @param src [Pathname]
86
+ # @return [Pathname]
87
+ # @raise [Errors::GryphonError]
88
+ def process_file(src)
89
+ processor = @processors[src.extname]
90
+ dest = processor.dest_name(src)
91
+
92
+ if @force || processor.file_modified?(src, dest)
93
+ @modifications += 1
94
+ msg = File.exist?(dest) ? 'Recreating' : 'Creating'
95
+ log("#{msg} #{dest}")
96
+ processor.process(src, dest)
97
+ compress_file(dest)
98
+ end
99
+
100
+ dest
101
+ end
102
+
103
+ # @param file [Pathname]
104
+ def compress_file(file)
105
+ return if @compressors.empty?
106
+
107
+ return unless Compressors.compressable?(file)
108
+
109
+ log("Compressing #{file}")
110
+ @compressors.each { |compressor| compressor.compress(file) }
111
+ end
112
+
113
+ # @param src [String]
114
+ # @param removal [Boolean]
115
+ def process_changes(src, removal: false)
116
+ if src == LAYOUT_FILE
117
+ glob(CONTENT_DIR, TEMPLATE_EXT).each { |file| process_file(file) }
118
+ else
119
+ path = Pathname(src)
120
+
121
+ if removal
122
+ path = @processors[path.extname].dest_name(path)
123
+ delete_file(path)
124
+ else
125
+ process_file(path)
126
+ end
127
+ end
128
+ rescue Errors::GryphonError => e
129
+ log(e.message, Logger::ERROR)
130
+ end
131
+
132
+ # @params base [String]
133
+ # @params match [String]
134
+ # @return [Array<Pathname>]
135
+ def glob(base, match = '') = Pathname.glob("#{base}/**/*#{match}").reject(&:directory?)
136
+
137
+ # @param file [Pathname]
138
+ def delete_file(file)
139
+ log("Deleting #{file}")
140
+ file.delete
141
+
142
+ @compressors.each do |compressor|
143
+ compressed_file = Pathname("#{file}#{compressor.extname}")
144
+ next unless compressed_file.exist?
145
+
146
+ log("Deleting #{compressed_file}")
147
+ compressed_file.delete
148
+ end
149
+ end
150
+ end
151
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gryphon
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Christopher Birmingham
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: htmlbeautifier
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: listen
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mustache
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: psych
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webrick
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '13.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '13.0'
111
+ description: A slightly opinionated static website generator for those who like working
112
+ in html and mustache
113
+ email:
114
+ - chris.birmingham@hotmail.co.uk
115
+ executables:
116
+ - gryphon
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - LICENSE
121
+ - bin/gryphon
122
+ - lib/gryphon.rb
123
+ - lib/gryphon/cli.rb
124
+ - lib/gryphon/compressors.rb
125
+ - lib/gryphon/compressors/brotli_compressor.rb
126
+ - lib/gryphon/compressors/gzip_compressor.rb
127
+ - lib/gryphon/errors.rb
128
+ - lib/gryphon/layout_file.rb
129
+ - lib/gryphon/logging.rb
130
+ - lib/gryphon/processors.rb
131
+ - lib/gryphon/processors/asset_processor.rb
132
+ - lib/gryphon/processors/mustache_processor.rb
133
+ - lib/gryphon/processors/sass_processor.rb
134
+ - lib/gryphon/renderers.rb
135
+ - lib/gryphon/renderers/mustache_renderer.rb
136
+ - lib/gryphon/version.rb
137
+ homepage: https://github.com/chrisBirmingham/gryphon
138
+ licenses:
139
+ - Unlicense
140
+ metadata:
141
+ homepage_uri: https://github.com/chrisBirmingham/gryphon
142
+ source_code_uri: https://github.com/chrisBirmingham/gryphon
143
+ rubygems_mfa_required: 'true'
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 3.0.0
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubygems_version: 3.4.20
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: Yet another static site generator
163
+ test_files: []