gryphon_nest 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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