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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72b699d93837e7f59c74c2782f38526c2f6bc869d6af875bc174d13e29951560
4
- data.tar.gz: 6150b1914e70d54e352d181c15750a57c524ae8271ce25de5bb976da4cb0bbf8
3
+ metadata.gz: 13f76380c2a03e716f50ee3d517a0af74a5423dcea83d563106984d524368e90
4
+ data.tar.gz: 3bfd1e61ab10066a5a5256eca4081e6e38933dc7b6346af970b638a1eb264efe
5
5
  SHA512:
6
- metadata.gz: b60a684cf031ca5d37f3fa111e5b552def7d2041b723a8cca940a497db385111d2b54751501d60e0c22c7ef9f74cd14cdd5effef11b713c92fe4c0e590624eeb
7
- data.tar.gz: 20e53c701a4bf0fa32d265d7fff95ece1a3c52eeb77439c347efd5d55a1401c2ee37ee1e2427cf575c70548220ff8640d193ef9ee5571178c06797233e3ae72b
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
- Sysexits.exit(:usage)
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 GryphonNest::NotFoundError => e
48
+ rescue StandardError => e
51
49
  warn e.message
52
- Sysexits.exit(:input_missing)
50
+ exit(EXIT_FAILURE)
53
51
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GryphonNest
4
+ module Errors
5
+ class NotFoundError < StandardError; end
6
+
7
+ class YamlError < StandardError; end
8
+ end
9
+ 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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GryphonNest
4
+ module Processors
5
+ autoload :AssetProcessor, 'gryphon_nest/processors/asset_processor'
6
+ autoload :MustacheProcessor, 'gryphon_nest/processors/mustache_processor'
7
+ end
8
+ 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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GryphonNest
4
+ module Renderers
5
+ autoload :MustacheRenderer, 'gryphon_nest/renderers/mustache_renderer'
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GryphonNest
4
- VERSION = '1.3.0'
4
+ VERSION = '2.0.0'
5
5
  end
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 :NotFoundError, 'gryphon_nest/not_found_error'
10
- autoload :Renderer, 'gryphon_nest/renderer'
11
- autoload :FileUtil, 'gryphon_nest/file_util'
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
- class << self
15
- BUILD_DIR = '_site'
16
- CONTENT_DIR = 'content'
17
- DATA_DIR = 'data'
18
- ASSETS_DIR = 'assets'
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
- # @raise [NotFoundError]
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 = FileUtil.glob("#{BUILD_DIR}/**/*")
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 = existing_files.difference(copy_assets)
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
- # @param source_file [Pathname]
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
- created_files = []
47
+ processed_files = []
48
+ renderer = Renderers::MustacheRenderer.new({ 'layout_file' => LAYOUT_FILE })
49
+ asset_processor = Processors::AssetProcessor.new
131
50
 
132
- renderer = Renderer.new
133
- FileUtil.glob("#{CONTENT_DIR}/**/*").each do |source_file|
134
- if source_file.extname != TEMPLATE_EXT
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
- dest_file = get_output_name(source_file)
140
- created_files << dest_file
141
- process_file(renderer, source_file, dest_file)
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
- created_files
60
+ processed_files
145
61
  end
146
62
 
147
- # @return [Array]
148
- def copy_assets
149
- return [] unless Dir.exist?(ASSETS_DIR)
150
-
151
- copied_files = []
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
- puts "Copying #{asset} to #{dest}"
159
- dest_dir = dest.dirname
160
- FileUtils.makedirs(dest_dir)
161
- FileUtils.copy_file(asset, dest)
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: 1.3.0
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: 2023-09-01 00:00:00.000000000 Z
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: sysexits
42
+ name: psych
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.2'
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: '1.2'
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/file_util.rb
110
- - lib/gryphon_nest/not_found_error.rb
111
- - lib/gryphon_nest/renderer.rb
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.3.7
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,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GryphonNest
4
- class NotFoundError < StandardError; end
5
- 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