flutterby 0.0.13 → 0.0.14

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
  SHA1:
3
- metadata.gz: 2c87a6c4e67963aa5d66c2225d60b9e228a2d852
4
- data.tar.gz: 896782e01a481af93bc5da7d9a277bc9252d9f29
3
+ metadata.gz: 98bf6013901069664a2281251125dbc4f2b82bf1
4
+ data.tar.gz: 677647c5e11088fbf5091fe7aa3e5a121a48c37a
5
5
  SHA512:
6
- metadata.gz: 52dcaab598ca23c831ff348a3fdeb56124a4ce663c45c36ba47d4211370d602f8b1e0389de27fc9ec830b79abe0761dcbfe5c64a47834de46f17b75418424e72
7
- data.tar.gz: 760b7537767012d5b6d044c537d461bfd52514c2feeb5bf114d4213de1805add17e09eaff1225cb2af0381076f1c6f4f2f99201a644a638dce558ebee55d5392
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
@@ -1,43 +1,40 @@
1
1
  module Flutterby
2
2
  module Filters
3
- def apply!(file)
4
- body = file.source
3
+ def apply!(node)
4
+ node.body = node.source
5
5
 
6
6
  # Apply all filters
7
- file.filters.each do |filter|
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
- body = Filters.send(meth, body, file)
11
+ Filters.send(meth, node)
20
12
  end
21
13
  end
14
+ end
22
15
 
23
- file.body = body
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(input, file)
27
- tilt("erb", input).render(file.view)
24
+ def process_erb!(node)
25
+ node.body = tilt("erb", node.body).render(node.view)
28
26
  end
29
27
 
30
- def process_slim(input, file)
31
- tilt("slim", input).render(file.view)
28
+ def process_slim!(node)
29
+ node.body = tilt("slim", node.body).render(node.view)
32
30
  end
33
31
 
34
- def process_md(input, file)
35
- file.ext = "html"
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(input, file)
40
- Sass::Engine.new(input, syntax: :scss).render
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)
@@ -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
- @parent = parent
10
- @data = {}
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
- # We're assuming the extension is the name of the final filter
18
- # that will be applied. This may not be always correct, since filters
19
- # can also change a file's extension.
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 reset_children!
36
- @children = []
37
- end
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 = nil
149
- reset_children!
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
- # Load contents from filesystem
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
- if node = Flutterby.from(entry, parent: self)
157
- add_child(node)
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 preprocess!
185
- walk_tree do |node|
186
- node.render_body! if node.should_preprocess?
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 should_preprocess?
191
- should_publish? && !folder? &&
192
- (filters.include?("json") || filters.include?("yaml"))
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
- data = {}
278
-
279
- # YAML Front Matter
280
- if @source.sub!(/\A\-\-\-\n(.+)\n\-\-\-\n/m, "")
281
- data.merge! YAML.load($1)
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
- # TOML Front Matter
285
- if @source.sub!(/\A\+\+\+\n(.+)\n\+\+\+\n/m, "")
286
- data.merge! TOML.parse($1)
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
@@ -37,14 +37,15 @@ module Flutterby
37
37
  req = Rack::Request.new(env)
38
38
  res = Rack::Response.new([], 200, {})
39
39
 
40
- parts = req.path.split("/").reject(&:empty?)
41
-
42
- result = catch :halt do
43
- serve(@root, parts, req, res)
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
- case result
47
- when :error_404 then
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 serve(node, parts, req, res)
57
- # halt if we're not supposed to serve current node
58
- throw :halt, :error_404 unless node.should_publish?
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
@@ -1,3 +1,3 @@
1
1
  module Flutterby
2
- VERSION = "0.0.13"
2
+ VERSION = "0.0.14"
3
3
  end
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.13
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