gryphon_nest 3.1.0 → 4.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: 9a15200889f6f72f775094aed126ad46ef5dae7546395b4ebef80f830e389505
4
- data.tar.gz: c582fb185b2663a96f959823bf769a079aeca856b8303fe83f99088078339c78
3
+ metadata.gz: 11bbaedf3fbca0e216d16bd03a8452ee6ffc57930dac0ab8a30c3d02fef15f2e
4
+ data.tar.gz: 63ed7c032ef8478697ee0372ff569c54f3408a92dc68728f892c7a938e7e4955
5
5
  SHA512:
6
- metadata.gz: 1ce6e28ae987f0bf357f3035d34734e09589cf6060b9d5e9c60b327d8ed84fb9616e00994fc70e93cfc7b95a29a82cc42b1ac85704e71cf32eccbe7dcad586ef
7
- data.tar.gz: d069c1bb1f27bcc1dca9ca4817e3d8fa06db8aeb5d0fc7aa28a1d877940ed0ece4ae720e42d2821eedf382ee14a4a2bce03c3454e8d642a310869cfdd82b034a
6
+ metadata.gz: 865a4ed653820f359cd8d608e38e7f1c9c1697def2f4958ea18b11e4f4df84b701663571b10bf03eb06a2a1f9cd530a0c1f7579c613b710611b7014cd93c9851
7
+ data.tar.gz: 16838330ada34079d02f4ef629a878c2f6afdd55fa288e180a44e17b9fc8425fe3c6c5eaa27883ebb7ec900d30ce0ae2e82dd84525384e7dbbe464732f976fa6
data/bin/nest CHANGED
@@ -8,7 +8,8 @@ DEFAULT_PORT = 8000
8
8
  EXIT_FAILURE = 1
9
9
 
10
10
  options = {
11
- port: DEFAULT_PORT
11
+ port: DEFAULT_PORT,
12
+ watch: false
12
13
  }
13
14
 
14
15
  # @param msg [String]
@@ -24,6 +25,8 @@ begin
24
25
 
25
26
  opts.on('-p', '--port [PORT]', Integer, 'Port to run dev server on')
26
27
 
28
+ opts.on('-w', '--watch', 'Watch for file changes when running the local server')
29
+
27
30
  opts.on('-h', '--help', 'Show this message') do
28
31
  puts opts
29
32
  exit
@@ -41,8 +44,14 @@ begin
41
44
 
42
45
  usage_error("Unknown command #{command}", parser) unless %w[build serve].include?(command)
43
46
 
44
- GryphonNest.build_website
45
- GryphonNest.serve_website(options[:port]) if command == 'serve'
47
+ nest = GryphonNest::Nest.new
48
+ nest.build
49
+
50
+ if command == 'serve'
51
+ nest.watch if options[:watch]
52
+
53
+ nest.serve(options[:port])
54
+ end
46
55
  rescue OptionParser::ParseError => e
47
56
  usage_error(e.message, parser)
48
57
  rescue StandardError => e
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module GryphonNest
6
+ # Mixing module used for logging messages to the console
7
+ module Logging
8
+ class << self
9
+ # @return [Logger]
10
+ def create
11
+ logger = Logger.new($stdout)
12
+
13
+ # Create formatter that matches WebBricks log messages
14
+ logger.formatter = proc do |severity, datetime, _progname, msg|
15
+ date_format = datetime.strftime('%Y-%m-%d %H:%M:%S')
16
+ "[#{date_format}] #{severity.ljust(5)} #{msg}\n"
17
+ end
18
+
19
+ logger
20
+ end
21
+ end
22
+ end
23
+ end
@@ -6,22 +6,13 @@ module GryphonNest
6
6
  module Processors
7
7
  # Default file processor. Moves files from source to destination
8
8
  class AssetProcessor
9
- # @param file [Pathname]
10
- # @return [Pathname]
11
- def process(file)
12
- dest = dest_name(file)
13
-
14
- if file_modified?(file, dest)
15
- puts "Copying #{file} to #{dest}"
16
- dest.dirname.mkpath
17
- FileUtils.copy_file(file, dest)
18
- end
19
-
20
- dest
9
+ # @param src [Pathname]
10
+ # @param dest [Pathname]
11
+ def process(src, dest)
12
+ dest.dirname.mkpath
13
+ FileUtils.copy_file(src, dest)
21
14
  end
22
15
 
23
- private
24
-
25
16
  # @param src [Pathname]
26
17
  # @return [Pathname]
27
18
  def dest_name(src)
@@ -12,25 +12,18 @@ module GryphonNest
12
12
  @renderer = renderer
13
13
  end
14
14
 
15
- # @param file [Pathname]
16
- # @return [Pathname]
15
+ # @param src [Pathname]
16
+ # @param dest [Pathname]
17
17
  # @raise [Errors::YamlError]
18
18
  # @raise [Errors::ParseError]
19
- def process(file)
20
- dest = dest_name(file)
21
- msg = File.exist?(dest) ? 'Recreating' : 'Creating'
22
- puts "#{msg} #{dest}"
23
-
19
+ def process(src, dest)
24
20
  @layout ||= read_layout_file
25
21
 
26
- context = read_context(file)
27
- content = build_output(file, context)
22
+ context = read_context(src)
23
+ content = build_output(src, context)
28
24
  write_file(dest, content)
29
- dest
30
25
  end
31
26
 
32
- private
33
-
34
27
  # @param src [Pathname]
35
28
  # @return [Pathname]
36
29
  def dest_name(src)
@@ -43,12 +36,20 @@ module GryphonNest
43
36
  path.join('index.html')
44
37
  end
45
38
 
39
+ # @param _src [Pathname]
40
+ # @param _dest [Pathname]
41
+ # @return [Boolean]
42
+ def file_modified?(_src, _dest)
43
+ true
44
+ end
45
+
46
+ private
47
+
46
48
  # @param src [Pathname]
47
49
  # @return [Hash]
48
50
  # @raise [Errors::YamlError]
49
51
  def read_context(src)
50
- basename = src.basename(TEMPLATE_EXT)
51
- path = "#{DATA_DIR}/#{basename}.yaml"
52
+ path = src.sub(CONTENT_DIR, DATA_DIR).sub_ext('.yaml')
52
53
  YAML.safe_load_file(path, symbolize_names: true)
53
54
  rescue IOError, Errno::ENOENT
54
55
  {}
@@ -61,16 +62,17 @@ module GryphonNest
61
62
  # @return [String]
62
63
  # @raise [Errors::ParseError]
63
64
  def build_output(file, context)
64
- if @layout.empty?
65
- content = @renderer.render_file(file, context)
66
- else
67
- context[:yield] = file.basename(TEMPLATE_EXT)
68
- content = @renderer.render(@layout, context)
69
- end
65
+ content =
66
+ if @layout.empty?
67
+ @renderer.render_file(file, context)
68
+ else
69
+ context[:yield] = file.basename(TEMPLATE_EXT)
70
+ @renderer.render(@layout, context)
71
+ end
70
72
 
71
73
  HtmlBeautifier.beautify(content, stop_on_errors: true)
72
74
  rescue Mustache::Parser::SyntaxError => e
73
- raise Errors::ParseError, "Failed to process mustache template #{file}.\nReason: #{e}"
75
+ raise Errors::ParseError, "Failed to process mustache template #{file}. Reason:\n#{e}"
74
76
  rescue RuntimeError => e
75
77
  raise Errors::ParseError, "Failed to beautify template output #{file}. Reason: #{e.message}"
76
78
  end
@@ -78,8 +80,7 @@ module GryphonNest
78
80
  # @param path [Pathname]
79
81
  # @param content [String]
80
82
  def write_file(path, content)
81
- dir = path.dirname
82
- dir.mkpath
83
+ path.dirname.mkpath
83
84
  path.write(content)
84
85
  end
85
86
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GryphonNest
4
+ module Processors
5
+ class ProcessorRegistry
6
+ def initialize
7
+ @asset_processor = AssetProcessor.new
8
+ @processors = Hash.new(@asset_processor)
9
+ yield self
10
+ end
11
+
12
+ # @param key [String]
13
+ # @param value [Proc]
14
+ def []=(key, value)
15
+ @processors[key] = value
16
+ end
17
+
18
+ # @param key [String]
19
+ # @return [Object]
20
+ def [](key)
21
+ processor = @processors[key]
22
+
23
+ if processor.is_a?(Proc)
24
+ processor = processor.call
25
+ @processors[key] = processor
26
+ end
27
+
28
+ processor
29
+ rescue LoadError
30
+ @processors[key] = nil
31
+ @asset_processor
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sass-embedded'
4
+
5
+ module GryphonNest
6
+ module Processors
7
+ class SassProcessor
8
+ # @param src [Pathname]
9
+ # @param dest [Pathname]
10
+ # @raise [Errors::ParseError]
11
+ def process(src, dest)
12
+ result = Sass.compile(src)
13
+ File.write(dest, result.css)
14
+ rescue Sass::CompileError => e
15
+ raise Errors::ParseError, "Failed to process sass style sheet #{src}. Reason:\n#{e.full_message}"
16
+ end
17
+
18
+ # @param src [Pathname]
19
+ # @return [Pathname]
20
+ def dest_name(src)
21
+ src.sub(CONTENT_DIR, BUILD_DIR).sub_ext('.css')
22
+ end
23
+
24
+ # @param src [Pathname]
25
+ # @param des [Pathname]
26
+ # @return [Boolean]
27
+ def file_modified?(src, dest)
28
+ return true unless dest.exist?
29
+
30
+ src.mtime > dest.mtime
31
+ end
32
+ end
33
+ end
34
+ end
@@ -4,5 +4,25 @@ module GryphonNest
4
4
  module Processors
5
5
  autoload :AssetProcessor, 'gryphon_nest/processors/asset_processor'
6
6
  autoload :MustacheProcessor, 'gryphon_nest/processors/mustache_processor'
7
+ autoload :ProcessorRegistry, 'gryphon_nest/processors/processor_registry'
8
+ autoload :SassProcessor, 'gryphon_nest/processors/sass_processor.rb'
9
+
10
+ class << self
11
+ # @return [ProcessorRegistry]
12
+ def create
13
+ ProcessorRegistry.new do |reg|
14
+ reg[TEMPLATE_EXT] = proc {
15
+ renderer = Renderers::MustacheRenderer.new
16
+ renderer.template_path = CONTENT_DIR
17
+ Processors::MustacheProcessor.new(renderer)
18
+ }
19
+
20
+ sass_proc = proc { Processors::SassProcessor.new }
21
+
22
+ reg['.scss'] = sass_proc
23
+ reg['.sass'] = sass_proc
24
+ end
25
+ end
26
+ end
7
27
  end
8
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GryphonNest
4
- VERSION = '3.1.0'
4
+ VERSION = '4.0.0'
5
5
  end
data/lib/gryphon_nest.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'listen'
3
4
  require 'pathname'
4
5
  require 'webrick'
5
6
 
6
7
  module GryphonNest
7
8
  autoload :Errors, 'gryphon_nest/errors'
9
+ autoload :Logging, 'gryphon_nest/logging'
8
10
  autoload :Processors, 'gryphon_nest/processors'
9
11
  autoload :Renderers, 'gryphon_nest/renderers'
10
12
  autoload :VERSION, 'gryphon_nest/version'
@@ -15,21 +17,55 @@ module GryphonNest
15
17
  TEMPLATE_EXT = '.mustache'
16
18
  LAYOUT_FILE = 'layout.mustache'
17
19
 
18
- class << self
20
+ class Nest
21
+ def initialize
22
+ @processors = Processors.create
23
+ @logger = Logging.create
24
+ end
25
+
19
26
  # @raise [Errors::NotFoundError]
20
- def build_website
27
+ def build
21
28
  raise Errors::NotFoundError, "Content directory doesn't exist in the current directory" unless Dir.exist?(CONTENT_DIR)
22
29
 
23
30
  Dir.mkdir(BUILD_DIR) unless Dir.exist?(BUILD_DIR)
24
31
 
25
32
  existing_files = glob("#{BUILD_DIR}/**/*")
26
- existing_files = existing_files.difference(process_content)
27
- delete_files(existing_files)
33
+ content_files = glob("#{CONTENT_DIR}/**/*")
34
+ processed_files = content_files.collect { |src| process_file(src) }
35
+ existing_files.difference(processed_files).each { |file| delete_file(file) }
36
+ end
37
+
38
+ def watch
39
+ @logger.info('Watching for content changes')
40
+ listener = Listen.to(CONTENT_DIR, DATA_DIR, relative: true) do |modified, added, removed|
41
+ modified.union(added).each do |file|
42
+ path = Pathname(file)
43
+
44
+ if file.start_with?(DATA_DIR)
45
+ process_data_file(path)
46
+ else
47
+ process_file(path)
48
+ end
49
+ end
50
+
51
+ removed.each do |file|
52
+ path = Pathname(file)
53
+
54
+ if file.start_with?(DATA_DIR)
55
+ process_data_file(path)
56
+ else
57
+ path = @processors[path.extname].dest_name(path)
58
+ delete_file(path)
59
+ end
60
+ end
61
+ end
62
+
63
+ listener.start
28
64
  end
29
65
 
30
66
  # @param port [Integer]
31
- def serve_website(port)
32
- puts "Running local server on #{port}"
67
+ def serve(port)
68
+ @logger.info("Running local server on #{port}")
33
69
  server = WEBrick::HTTPServer.new(Port: port, DocumentRoot: BUILD_DIR)
34
70
  # Trap ctrl c so we don't get the horrible stack trace
35
71
  trap('INT') { server.shutdown }
@@ -38,20 +74,29 @@ module GryphonNest
38
74
 
39
75
  private
40
76
 
41
- # @return [Array<Pathname>]
42
- def process_content
43
- renderer = Renderers::MustacheRenderer.new
44
- renderer.template_path = CONTENT_DIR
45
- asset_processor = Processors::AssetProcessor.new
46
-
47
- processors = {
48
- TEMPLATE_EXT => Processors::MustacheProcessor.new(renderer)
49
- }
50
-
51
- glob("#{CONTENT_DIR}/**/*").map do |source_file|
52
- processor = processors.fetch(source_file.extname, asset_processor)
53
- processor.process(source_file)
77
+ # @param src [Pathname]
78
+ # @return [Pathname]
79
+ def process_file(src)
80
+ processor = @processors[src.extname]
81
+ dest = processor.dest_name(src)
82
+
83
+ if processor.file_modified?(src, dest)
84
+ msg = File.exist?(dest) ? 'Recreating' : 'Creating'
85
+ @logger.info("#{msg} #{dest}")
86
+ processor.process(src, dest)
54
87
  end
88
+
89
+ dest
90
+ end
91
+
92
+ # @param src [Pathname]
93
+ # @return [Pathname]
94
+ def process_data_file(src)
95
+ src = src.sub(DATA_DIR, CONTENT_DIR).sub_ext(TEMPLATE_EXT)
96
+
97
+ return unless src.exist?
98
+
99
+ process_file(src)
55
100
  end
56
101
 
57
102
  # @params path [String]
@@ -60,13 +105,10 @@ module GryphonNest
60
105
  Pathname.glob(path).reject(&:directory?)
61
106
  end
62
107
 
63
- # @param junk_files [Array<Pathname>]
64
- def delete_files(junk_files)
65
- junk_files.each do |f|
66
- puts "Deleting #{f}"
67
- f.delete
68
- end
69
- nil
108
+ # @param file [Pathname]
109
+ def delete_file(file)
110
+ @logger.info("Deleting #{file}")
111
+ file.delete
70
112
  end
71
113
  end
72
114
  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: 3.1.0
4
+ version: 4.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: 2025-04-26 00:00:00.000000000 Z
11
+ date: 2025-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: htmlbeautifier
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
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'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: mustache
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -44,28 +58,28 @@ dependencies:
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '3.3'
61
+ version: '5.2'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '3.3'
68
+ version: '5.2'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: webrick
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '1.8'
75
+ version: '1.9'
62
76
  type: :runtime
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '1.8'
82
+ version: '1.9'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -107,9 +121,12 @@ files:
107
121
  - bin/nest
108
122
  - lib/gryphon_nest.rb
109
123
  - lib/gryphon_nest/errors.rb
124
+ - lib/gryphon_nest/logging.rb
110
125
  - lib/gryphon_nest/processors.rb
111
126
  - lib/gryphon_nest/processors/asset_processor.rb
112
127
  - lib/gryphon_nest/processors/mustache_processor.rb
128
+ - lib/gryphon_nest/processors/processor_registry.rb
129
+ - lib/gryphon_nest/processors/sass_processor.rb
113
130
  - lib/gryphon_nest/renderers.rb
114
131
  - lib/gryphon_nest/renderers/mustache_renderer.rb
115
132
  - lib/gryphon_nest/version.rb