gryphon_nest 1.2.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: c6d453e21e5e415707640be17773b49c68e9cc89051e7f4414b1339a6ffc8ced
4
- data.tar.gz: 62c65f5a605cee0c62b1abba8f638afe6fa7cc8f7e3f118787d2f0dcb6aa379c
3
+ metadata.gz: 13f76380c2a03e716f50ee3d517a0af74a5423dcea83d563106984d524368e90
4
+ data.tar.gz: 3bfd1e61ab10066a5a5256eca4081e6e38933dc7b6346af970b638a1eb264efe
5
5
  SHA512:
6
- metadata.gz: 56bf077e811a4ab6bf7a6836f42a929fea615a1e89078d1e6c3df63d753e08ff3e9f8d82d91b1588ac26648543702aa7543d95b212b0455f55cf78a44e95c4ec
7
- data.tar.gz: 73ffb7df7f94d890f6c46e82687010722e7989e3cd5e0c8dad7c5c4a878bb288cfe0b03fd4e5d449a006cd32012153675ec665442fd37508dd25e8f681b7bf46
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.2.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/gryphon_nest.rb CHANGED
@@ -1,36 +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'
7
+ autoload :Errors, 'gryphon_nest/errors'
8
+ autoload :Processors, 'gryphon_nest/processors'
9
+ autoload :Renderers, 'gryphon_nest/renderers'
11
10
  autoload :VERSION, 'gryphon_nest/version'
12
11
 
13
- class << self
14
- BUILD_DIR = '_site'
15
- CONTENT_DIR = 'content'
16
- DATA_DIR = 'data'
17
- ASSETS_DIR = 'assets'
18
- LAYOUT_DIR = 'layouts'
19
- TEMPLATE_EXT = '.mustache'
12
+ BUILD_DIR = '_site'
13
+ CONTENT_DIR = 'content'
14
+ DATA_DIR = 'data'
15
+ TEMPLATE_EXT = '.mustache'
16
+ LAYOUT_FILE = 'layouts/main.mustache'
20
17
 
18
+ class << self
19
+ # @raise [Errors::NotFoundError]
21
20
  def build_website
22
- 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)
23
22
 
24
23
  existing_files = []
25
24
  if Dir.exist?(BUILD_DIR)
26
- existing_files = filter_glob("#{BUILD_DIR}/**/*")
25
+ existing_files = glob("#{BUILD_DIR}/**/*")
27
26
  else
28
27
  Dir.mkdir(BUILD_DIR)
29
28
  end
30
29
 
31
30
  existing_files = existing_files.difference(process_content)
32
- existing_files = existing_files.difference(copy_assets)
33
- cleanup(existing_files)
31
+ delete_files(existing_files)
34
32
  end
35
33
 
36
34
  # @param port [Integer]
@@ -44,173 +42,37 @@ module GryphonNest
44
42
 
45
43
  private
46
44
 
47
- # @param template_name [String]
48
- # @return [String]
49
- def get_output_name(template_name)
50
- dir = File.dirname(template_name)
51
- basename = File.basename(template_name, TEMPLATE_EXT)
52
-
53
- path = Pathname.new(dir)
54
- path = path.sub(CONTENT_DIR, BUILD_DIR)
55
-
56
- path = path.join(basename) if basename != 'index'
57
-
58
- path = path.join('index.html')
59
- path.to_s
60
- end
61
-
62
- # @params path [String]
63
- # @return [Array]
64
- def filter_glob(path)
65
- Dir.glob(path).reject do |p|
66
- File.directory?(p)
67
- end
68
- end
69
-
70
- # @param path [String]
71
- # @param content [String]
72
- def save_html_file(path, content)
73
- dir = File.dirname(path)
74
-
75
- unless Dir.exist?(dir)
76
- puts "Creating #{dir}"
77
- Dir.mkdir(dir)
78
- end
79
-
80
- puts "Creating #{path}"
81
- File.write(path, content)
82
- end
83
-
84
- # @param src [String]
85
- # @param dest [String]
86
- # @param layout_file [String]
87
- # @param context_file [String]
88
- # @return [Boolean]
89
- def can_create_html_file?(src, dest, layout_file, context_file)
90
- return true unless File.exist?(dest)
91
-
92
- src_mtime = File.mtime(src)
93
- dest_mtime = File.mtime(dest)
94
-
95
- return true if src_mtime > dest_mtime
96
-
97
- if File.exist?(layout_file)
98
- layout_mtime = File.mtime(layout_file)
99
- return true if layout_mtime > dest_mtime
100
- end
101
-
102
- if File.exist?(context_file)
103
- context_mtime = File.mtime(context_file)
104
- return true if context_mtime > dest_mtime
105
- end
106
-
107
- false
108
- end
109
-
110
- # @param name [String]
111
- # @param context [Hash]
112
- # @return [String]
113
- def get_layout_file(name, context)
114
- path = Pathname.new(LAYOUT_DIR)
115
-
116
- if context.key?('layout')
117
- layout = context['layout']
118
- path = path.join(layout)
119
-
120
- raise NotFoundError, "#{name} requires layout file #{layout} but it doesn't exist or can't be read" unless File.exist?(path)
121
-
122
- return path.to_s
123
- end
124
-
125
- path.join('main.mustache').to_s
126
- end
127
-
128
- # @param path [String]
129
- # @return [Hash]
130
- def read_context_file(path)
131
- return {} if path == ''
132
-
133
- return {} unless File.exist?(path)
134
-
135
- File.open(path) do |yaml|
136
- YAML.safe_load(yaml)
137
- end
138
- end
139
-
140
- # @param name [String]
141
- # @return [String]
142
- def get_context_file(name)
143
- basename = File.basename(name, TEMPLATE_EXT)
144
-
145
- Dir.glob("#{DATA_DIR}/#{basename}.{yaml,yml}") do |f|
146
- return f
147
- end
148
-
149
- ''
150
- end
151
-
152
- # @return [Array]
45
+ # @return [Array<Pathname>]
153
46
  def process_content
154
- created_files = []
155
- renderer = Renderer.new
156
-
157
- filter_glob("#{CONTENT_DIR}/**/*").each do |template|
158
- if File.extname(template) != TEMPLATE_EXT
159
- puts "Skipping non template file #{template}"
160
- next
161
- end
162
-
163
- dest_file = get_output_name(template)
164
- context_file = get_context_file(template)
165
- context = read_context_file(context_file)
166
- layout_file = get_layout_file(template, context)
47
+ processed_files = []
48
+ renderer = Renderers::MustacheRenderer.new({ 'layout_file' => LAYOUT_FILE })
49
+ asset_processor = Processors::AssetProcessor.new
167
50
 
168
- created_files << dest_file
169
- next unless can_create_html_file?(template, dest_file, layout_file, context_file)
51
+ processors = {
52
+ TEMPLATE_EXT => Processors::MustacheProcessor.new(renderer)
53
+ }
170
54
 
171
- content = renderer.render(template, layout_file, context)
172
- save_html_file(dest_file, content)
55
+ glob("#{CONTENT_DIR}/**/*").each do |source_file|
56
+ processor = processors.fetch(source_file.extname, asset_processor)
57
+ processed_files << processor.process(source_file)
173
58
  end
174
59
 
175
- created_files
176
- end
177
-
178
- # @param src [String]
179
- # @param dest [String]
180
- # @return [Boolean]
181
- def can_copy_asset?(src, dest)
182
- return true unless File.exist?(dest)
183
-
184
- File.mtime(src) > File.mtime(dest)
60
+ processed_files
185
61
  end
186
62
 
187
- # @return [Array]
188
- def copy_assets
189
- return [] unless Dir.exist?(ASSETS_DIR)
190
-
191
- copied_files = []
192
- filter_glob("#{ASSETS_DIR}/**/*").each do |asset|
193
- dest = Pathname.new(asset)
194
- dest = dest.sub(ASSETS_DIR, BUILD_DIR)
195
- copied_files << dest.to_s
196
-
197
- next unless can_copy_asset?(asset, dest)
198
-
199
- puts "Copying #{asset} to #{dest}"
200
- dest_dir = File.dirname(dest)
201
- FileUtils.makedirs(dest_dir)
202
- FileUtils.copy_file(asset, dest)
203
- end
204
-
205
- copied_files
63
+ # @params path [String]
64
+ # @return [Array<Pathname>]
65
+ def glob(path)
66
+ Pathname.glob(path).reject(&:directory?)
206
67
  end
207
68
 
208
- # @param junk_files [Array]
209
- def cleanup(junk_files)
69
+ # @param junk_files [Array<Pathname>]
70
+ def delete_files(junk_files)
210
71
  junk_files.each do |f|
211
72
  puts "Deleting #{f}"
212
- FileUtils.remove_file(f)
73
+ f.delete
213
74
  end
75
+ nil
214
76
  end
215
77
  end
216
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.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Birmingham
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-28 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,8 +106,12 @@ files:
106
106
  - LICENSE
107
107
  - bin/nest
108
108
  - lib/gryphon_nest.rb
109
- - lib/gryphon_nest/not_found_error.rb
110
- - 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
111
115
  - lib/gryphon_nest/version.rb
112
116
  homepage: https://github.com/chrisBirmingham/gryphon_nest
113
117
  licenses:
@@ -115,7 +119,7 @@ licenses:
115
119
  metadata:
116
120
  homepage_uri: https://github.com/chrisBirmingham/gryphon_nest
117
121
  source_code_uri: https://github.com/chrisBirmingham/gryphon_nest
118
- post_install_message:
122
+ post_install_message:
119
123
  rdoc_options: []
120
124
  require_paths:
121
125
  - lib
@@ -130,8 +134,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
134
  - !ruby/object:Gem::Version
131
135
  version: '0'
132
136
  requirements: []
133
- rubygems_version: 3.1.2
134
- signing_key:
137
+ rubygems_version: 3.2.33
138
+ signing_key:
135
139
  specification_version: 4
136
140
  summary: Yet another static site generator
137
141
  test_files: []
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GryphonNest
4
- class NotFoundError < StandardError; end
5
- end
@@ -1,55 +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
9
- # @param layouts [Array]
10
- def initialize
11
- @layouts = {}
12
- end
13
-
14
- # @param template [String]
15
- # @param layout [String]
16
- # @param context [Hash]
17
- # @return [String]
18
- def render(template, layout, context)
19
- File.open(template) do |f|
20
- content = render_template(f.read, context)
21
- layout = get_template_layout(layout)
22
-
23
- unless layout.nil?
24
- context[:yield] = content
25
- content = render_template(layout, context)
26
- end
27
-
28
- HtmlBeautifier.beautify(content)
29
- end
30
- end
31
-
32
- private
33
-
34
- # @param content [String]
35
- # @param context [Hash]
36
- # @return [String]
37
- def render_template(content, context)
38
- Mustache.render(content, context)
39
- end
40
-
41
- # @param path [String]
42
- # @return [String|null]
43
- def get_template_layout(path)
44
- return @layouts[path] if @layouts.key?(path)
45
-
46
- return unless File.exist?(path)
47
-
48
- File.open(path) do |file|
49
- content = file.read
50
- @layouts[path] = content
51
- return content
52
- end
53
- end
54
- end
55
- end