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 +4 -4
- data/bin/nest +4 -6
- data/lib/gryphon_nest/errors.rb +9 -0
- data/lib/gryphon_nest/processors/asset_processor.rb +40 -0
- data/lib/gryphon_nest/processors/mustache_processor.rb +61 -0
- data/lib/gryphon_nest/processors.rb +8 -0
- data/lib/gryphon_nest/renderers/mustache_renderer.rb +42 -0
- data/lib/gryphon_nest/renderers.rb +7 -0
- data/lib/gryphon_nest/version.rb +1 -1
- data/lib/gryphon_nest.rb +32 -170
- metadata +15 -11
- data/lib/gryphon_nest/not_found_error.rb +0 -5
- data/lib/gryphon_nest/renderer.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13f76380c2a03e716f50ee3d517a0af74a5423dcea83d563106984d524368e90
|
4
|
+
data.tar.gz: 3bfd1e61ab10066a5a5256eca4081e6e38933dc7b6346af970b638a1eb264efe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
48
|
+
rescue StandardError => e
|
51
49
|
warn e.message
|
52
|
-
|
50
|
+
exit(EXIT_FAILURE)
|
53
51
|
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,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
|
data/lib/gryphon_nest/version.rb
CHANGED
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 :
|
10
|
-
autoload :
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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 =
|
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
|
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
|
-
# @
|
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
|
-
|
155
|
-
renderer =
|
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
|
-
|
169
|
-
|
51
|
+
processors = {
|
52
|
+
TEMPLATE_EXT => Processors::MustacheProcessor.new(renderer)
|
53
|
+
}
|
170
54
|
|
171
|
-
|
172
|
-
|
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
|
-
|
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
|
-
# @
|
188
|
-
|
189
|
-
|
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
|
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
|
-
|
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:
|
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:
|
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:
|
42
|
+
name: psych
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
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: '
|
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/
|
110
|
-
- lib/gryphon_nest/
|
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.
|
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,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
|