gryphon_nest 1.2.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: 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