flutterby 0.0.13 → 0.0.14
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/lib/flutterby.rb +3 -2
- data/lib/flutterby/cli.rb +5 -2
- data/lib/flutterby/exporter.rb +31 -0
- data/lib/flutterby/filters.rb +20 -23
- data/lib/flutterby/node.rb +83 -98
- data/lib/flutterby/server.rb +12 -31
- data/lib/flutterby/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98bf6013901069664a2281251125dbc4f2b82bf1
|
4
|
+
data.tar.gz: 677647c5e11088fbf5091fe7aa3e5a121a48c37a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd149d0e3efa73a1692fc9b940f09dbaef6379864721c396b6f7103d624973477da40fcdab67e600051b7d5c53b5cc9445a871b4f37136a60506c5c2929410a1
|
7
|
+
data.tar.gz: 94648d86b9e989b8dc523a345aa4b655e54c13e1faf6fa58766a2174faffde2eeefb9458968e3496cc69207f462ea8328256fefaa38f1f4d9946b50594088dd4
|
data/lib/flutterby.rb
CHANGED
@@ -10,7 +10,6 @@ require "flutterby/version"
|
|
10
10
|
require "flutterby/node"
|
11
11
|
require "flutterby/filters"
|
12
12
|
require "flutterby/view"
|
13
|
-
require "flutterby/server"
|
14
13
|
|
15
14
|
|
16
15
|
module Flutterby
|
@@ -22,7 +21,9 @@ module Flutterby
|
|
22
21
|
name ||= ::File.basename(fs_path)
|
23
22
|
|
24
23
|
if ::File.exist?(fs_path)
|
25
|
-
Node.new(name, fs_path: fs_path, parent: parent)
|
24
|
+
Node.new(name, fs_path: fs_path, parent: parent).tap do |node|
|
25
|
+
node.preprocess!
|
26
|
+
end
|
26
27
|
else
|
27
28
|
raise "Path #{fs_path} could not be found."
|
28
29
|
end
|
data/lib/flutterby/cli.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
require 'commander'
|
2
1
|
require 'flutterby'
|
2
|
+
require 'flutterby/exporter'
|
3
|
+
require "flutterby/server"
|
4
|
+
|
5
|
+
require 'commander'
|
3
6
|
require 'benchmark'
|
4
7
|
|
5
8
|
Flutterby.logger.level = Logger::INFO
|
@@ -32,7 +35,7 @@ Commander.configure do
|
|
32
35
|
|
33
36
|
# Export site
|
34
37
|
say color("💾 Exporting site...", :bold)
|
35
|
-
root.export(into: options.out)
|
38
|
+
Flutterby::Exporter.new(root).export!(into: options.out)
|
36
39
|
end
|
37
40
|
|
38
41
|
say color("✅ Done. (took #{sprintf "%.2f", time}s)", :green, :bold)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Flutterby
|
2
|
+
class Exporter
|
3
|
+
def initialize(root)
|
4
|
+
@root = root
|
5
|
+
end
|
6
|
+
|
7
|
+
def export!(into:)
|
8
|
+
@root.paths.each do |path, node|
|
9
|
+
if node.should_publish?
|
10
|
+
path = ::File.expand_path(::File.join(into, node.url))
|
11
|
+
|
12
|
+
if node.file?
|
13
|
+
# Make sure directory exists
|
14
|
+
FileUtils.mkdir_p(::File.dirname(path))
|
15
|
+
|
16
|
+
# Write file
|
17
|
+
::File.write(path, node.render)
|
18
|
+
logger.info "Exported #{node.url}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def logger
|
28
|
+
@logger ||= Flutterby.logger
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/flutterby/filters.rb
CHANGED
@@ -1,43 +1,40 @@
|
|
1
1
|
module Flutterby
|
2
2
|
module Filters
|
3
|
-
def apply!(
|
4
|
-
body =
|
3
|
+
def apply!(node)
|
4
|
+
node.body = node.source
|
5
5
|
|
6
6
|
# Apply all filters
|
7
|
-
|
8
|
-
meth = "process_#{filter}"
|
7
|
+
node.filters.each do |filter|
|
8
|
+
meth = "process_#{filter}!"
|
9
9
|
|
10
|
-
# Set the file's extension to the requested filter. The filter
|
11
|
-
# itself can, of course, override this (eg. the "md" filter can default
|
12
|
-
# the extension to "html".)
|
13
|
-
#
|
14
|
-
file.ext = filter
|
15
|
-
|
16
|
-
# Now apply the actual filter!
|
17
|
-
#
|
18
10
|
if Filters.respond_to?(meth)
|
19
|
-
|
11
|
+
Filters.send(meth, node)
|
20
12
|
end
|
21
13
|
end
|
14
|
+
end
|
22
15
|
|
23
|
-
|
16
|
+
def process_rb!(node)
|
17
|
+
# extend the node
|
18
|
+
mod = Module.new
|
19
|
+
mod.class_eval(node.body)
|
20
|
+
node.extend(mod)
|
21
|
+
node.filter! if node.respond_to?(:filter!)
|
24
22
|
end
|
25
23
|
|
26
|
-
def process_erb(
|
27
|
-
tilt("erb",
|
24
|
+
def process_erb!(node)
|
25
|
+
node.body = tilt("erb", node.body).render(node.view)
|
28
26
|
end
|
29
27
|
|
30
|
-
def process_slim(
|
31
|
-
tilt("slim",
|
28
|
+
def process_slim!(node)
|
29
|
+
node.body = tilt("slim", node.body).render(node.view)
|
32
30
|
end
|
33
31
|
|
34
|
-
def process_md(
|
35
|
-
|
36
|
-
Slodown::Formatter.new(input).complete.to_s
|
32
|
+
def process_md!(node)
|
33
|
+
node.body = Slodown::Formatter.new(node.body).complete.to_s
|
37
34
|
end
|
38
35
|
|
39
|
-
def process_scss(
|
40
|
-
Sass::Engine.new(
|
36
|
+
def process_scss!(node)
|
37
|
+
node.body = Sass::Engine.new(node.body, syntax: :scss).render
|
41
38
|
end
|
42
39
|
|
43
40
|
def tilt(format, body)
|
data/lib/flutterby/node.rb
CHANGED
@@ -3,26 +3,22 @@ require 'benchmark'
|
|
3
3
|
module Flutterby
|
4
4
|
class Node
|
5
5
|
attr_accessor :parent, :ext, :source, :body
|
6
|
-
attr_reader :name, :filters, :fs_path, :data, :children
|
6
|
+
attr_reader :name, :filters, :fs_path, :data, :children, :paths
|
7
7
|
|
8
|
-
def initialize(name, parent: nil, fs_path: nil)
|
9
|
-
@
|
10
|
-
@
|
8
|
+
def initialize(name, parent: nil, fs_path: nil, source: nil)
|
9
|
+
@fs_path = fs_path ? ::File.expand_path(fs_path) : nil
|
10
|
+
@source = source
|
11
11
|
|
12
12
|
# Extract name, extension, and filters from given name
|
13
13
|
parts = name.split(".")
|
14
14
|
@name = parts.shift
|
15
|
+
@ext = parts.shift
|
15
16
|
@filters = parts.reverse
|
16
17
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@ext = @filters.last
|
22
|
-
|
23
|
-
# If a filesystem path was given, read the node from disk
|
24
|
-
if fs_path
|
25
|
-
@fs_path = ::File.expand_path(fs_path)
|
18
|
+
# Register this node with its parent
|
19
|
+
if parent
|
20
|
+
@parent = parent
|
21
|
+
parent.children << self
|
26
22
|
end
|
27
23
|
|
28
24
|
reload!
|
@@ -32,13 +28,10 @@ module Flutterby
|
|
32
28
|
# Children
|
33
29
|
#
|
34
30
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
def add_child(node)
|
40
|
-
node.parent = self
|
41
|
-
children << node
|
31
|
+
def register!
|
32
|
+
if file?
|
33
|
+
root.paths[url] = self
|
34
|
+
end
|
42
35
|
end
|
43
36
|
|
44
37
|
def find_child(name)
|
@@ -145,51 +138,71 @@ module Flutterby
|
|
145
138
|
#
|
146
139
|
|
147
140
|
def reload!
|
148
|
-
@body
|
149
|
-
|
141
|
+
@body = nil
|
142
|
+
@data = {}
|
143
|
+
@children = []
|
144
|
+
@paths = {}
|
145
|
+
|
146
|
+
load_from_filesystem! if @fs_path
|
147
|
+
end
|
148
|
+
|
149
|
+
def preprocess!
|
150
|
+
# First of all, we want to make sure all nodes have their
|
151
|
+
# available extensions loaded.
|
152
|
+
#
|
153
|
+
walk_tree do |node|
|
154
|
+
node.load_extension! if node.should_publish?
|
155
|
+
end
|
150
156
|
|
151
|
-
#
|
157
|
+
# Now do another pass, prerendering stuff where necessary,
|
158
|
+
# extracting data, registering URLs to be exported, etc.
|
152
159
|
#
|
160
|
+
walk_tree do |node|
|
161
|
+
node.render_body! if node.should_prerender?
|
162
|
+
node.extract_data!
|
163
|
+
node.register! if node.should_publish?
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def should_prerender?
|
168
|
+
should_publish? && !folder? &&
|
169
|
+
(["json", "yaml", "rb"] & filters).any?
|
170
|
+
end
|
171
|
+
|
172
|
+
def load_from_filesystem!
|
153
173
|
if @fs_path
|
154
174
|
if ::File.directory?(fs_path)
|
155
175
|
Dir[::File.join(fs_path, "*")].each do |entry|
|
156
|
-
|
157
|
-
|
158
|
-
end
|
176
|
+
name = ::File.basename(entry)
|
177
|
+
Flutterby::Node.new(name, parent: self, fs_path: entry)
|
159
178
|
end
|
160
179
|
else
|
161
180
|
@source = ::File.read(fs_path)
|
162
|
-
|
163
|
-
# Extract date from name
|
164
|
-
if name =~ %r{^(\d\d\d\d\-\d\d?\-\d\d?)\-}
|
165
|
-
@data['date'] = Time.parse($1)
|
166
|
-
end
|
167
|
-
|
168
|
-
# Read remaining data from frontmatter. Data in frontmatter
|
169
|
-
# will always have precedence!
|
170
|
-
@data.merge! parse_frontmatter
|
171
|
-
|
172
|
-
# Do some extra processing depending on extension
|
173
|
-
meth = "read_#{ext}"
|
174
|
-
send(meth) if respond_to?(meth)
|
175
181
|
end
|
176
182
|
end
|
177
|
-
|
178
|
-
# If this node is the root node, perform some preprocessing
|
179
|
-
if root?
|
180
|
-
preprocess!
|
181
|
-
end
|
182
183
|
end
|
183
184
|
|
184
|
-
def
|
185
|
-
|
186
|
-
|
185
|
+
def extract_data!
|
186
|
+
# Extract date from name
|
187
|
+
if name =~ %r{^(\d\d\d\d\-\d\d?\-\d\d?)\-}
|
188
|
+
data['date'] = Time.parse($1)
|
187
189
|
end
|
190
|
+
|
191
|
+
# Read remaining data from frontmatter. Data in frontmatter
|
192
|
+
# will always have precedence!
|
193
|
+
parse_frontmatter!
|
194
|
+
|
195
|
+
# Do some extra processing depending on extension
|
196
|
+
meth = "read_#{ext}!"
|
197
|
+
send(meth) if respond_to?(meth)
|
188
198
|
end
|
189
199
|
|
190
|
-
def
|
191
|
-
|
192
|
-
|
200
|
+
def load_extension!
|
201
|
+
if extension = sibling("_node.rb")
|
202
|
+
mod = Module.new
|
203
|
+
mod.class_eval(extension.body)
|
204
|
+
extend mod
|
205
|
+
end
|
193
206
|
end
|
194
207
|
|
195
208
|
#
|
@@ -234,66 +247,30 @@ module Flutterby
|
|
234
247
|
|
235
248
|
|
236
249
|
|
237
|
-
#
|
238
|
-
# Exporting
|
239
|
-
#
|
240
|
-
|
241
|
-
def export(into:)
|
242
|
-
if should_publish?
|
243
|
-
time = Benchmark.realtime do
|
244
|
-
write_static(into: into)
|
245
|
-
end
|
246
|
-
|
247
|
-
logger.info "Exported #{url}"
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
def write_static(into:)
|
252
|
-
if folder?
|
253
|
-
# write children, acting as a directory
|
254
|
-
|
255
|
-
path = full_fs_path(base: into)
|
256
|
-
Dir.mkdir(path) unless ::File.exists?(path)
|
257
|
-
|
258
|
-
children.each do |child|
|
259
|
-
child.export(into: path)
|
260
|
-
end
|
261
|
-
else
|
262
|
-
# write a file
|
263
|
-
::File.write(full_fs_path(base: into), render)
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def should_publish?
|
268
|
-
!name.start_with?("_")
|
269
|
-
end
|
270
|
-
|
271
250
|
|
272
251
|
#
|
273
252
|
# Front Matter Parsing
|
274
253
|
#
|
275
254
|
|
276
|
-
def parse_frontmatter
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
255
|
+
def parse_frontmatter!
|
256
|
+
if @source
|
257
|
+
# YAML Front Matter
|
258
|
+
if @source.sub!(/\A\-\-\-\n(.+)\n\-\-\-\n/m, "")
|
259
|
+
data.merge! YAML.load($1)
|
260
|
+
end
|
283
261
|
|
284
|
-
|
285
|
-
|
286
|
-
|
262
|
+
# TOML Front Matter
|
263
|
+
if @source.sub!(/\A\+\+\+\n(.+)\n\+\+\+\n/m, "")
|
264
|
+
data.merge! TOML.parse($1)
|
265
|
+
end
|
287
266
|
end
|
288
|
-
|
289
|
-
data
|
290
267
|
end
|
291
268
|
|
292
|
-
def read_json
|
269
|
+
def read_json!
|
293
270
|
data.merge!(JSON.parse(body))
|
294
271
|
end
|
295
272
|
|
296
|
-
def read_yaml
|
273
|
+
def read_yaml!
|
297
274
|
data.merge!(YAML.load(body))
|
298
275
|
end
|
299
276
|
|
@@ -314,10 +291,18 @@ module Flutterby
|
|
314
291
|
children.any?
|
315
292
|
end
|
316
293
|
|
294
|
+
def file?
|
295
|
+
!folder?
|
296
|
+
end
|
297
|
+
|
317
298
|
def page?
|
318
299
|
!folder? && ext == "html"
|
319
300
|
end
|
320
301
|
|
302
|
+
def should_publish?
|
303
|
+
!name.start_with?("_")
|
304
|
+
end
|
305
|
+
|
321
306
|
def logger
|
322
307
|
Flutterby.logger
|
323
308
|
end
|
data/lib/flutterby/server.rb
CHANGED
@@ -37,14 +37,15 @@ module Flutterby
|
|
37
37
|
req = Rack::Request.new(env)
|
38
38
|
res = Rack::Response.new([], 200, {})
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
40
|
+
# Look for target node in path registry
|
41
|
+
if node = find_node_for_path(req.path)
|
42
|
+
# Determine MIME type
|
43
|
+
mime_type = MIME::Types.type_for(node.ext) || "text/plain"
|
45
44
|
|
46
|
-
|
47
|
-
|
45
|
+
# Build response
|
46
|
+
res.headers["Content-Type"] = mime_type
|
47
|
+
res.body = [node.render]
|
48
|
+
else
|
48
49
|
res.status = 404
|
49
50
|
res.headers["Content-Type"] = "text/html"
|
50
51
|
res.body = ["404"]
|
@@ -53,30 +54,10 @@ module Flutterby
|
|
53
54
|
res
|
54
55
|
end
|
55
56
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# If there are parts left, find them and delegate to the next
|
61
|
-
# node in the chain; otherwise, render this specific node.
|
62
|
-
#
|
63
|
-
if parts.any?
|
64
|
-
if child = node.find(parts.shift)
|
65
|
-
serve(child, parts, req, res)
|
66
|
-
else
|
67
|
-
throw :halt, :error_404
|
68
|
-
end
|
69
|
-
elsif child = node.find("index")
|
70
|
-
serve(child, parts, req, res)
|
71
|
-
else
|
72
|
-
# Determine MIME type
|
73
|
-
mime_type = MIME::Types.type_for(node.ext) || "text/plain"
|
74
|
-
|
75
|
-
# Build response
|
76
|
-
res.headers["Content-Type"] = mime_type
|
77
|
-
res.body = [node.render]
|
78
|
-
throw :halt
|
79
|
-
end
|
57
|
+
def find_node_for_path(path)
|
58
|
+
@root.paths[path] ||
|
59
|
+
@root.paths[path + ".html"] ||
|
60
|
+
@root.paths[::File.join(path, "index.html")]
|
80
61
|
end
|
81
62
|
end
|
82
63
|
end
|
data/lib/flutterby/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flutterby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hendrik Mans
|
@@ -243,6 +243,7 @@ files:
|
|
243
243
|
- flutterby.gemspec
|
244
244
|
- lib/flutterby.rb
|
245
245
|
- lib/flutterby/cli.rb
|
246
|
+
- lib/flutterby/exporter.rb
|
246
247
|
- lib/flutterby/filters.rb
|
247
248
|
- lib/flutterby/node.rb
|
248
249
|
- lib/flutterby/server.rb
|