bunto 3.2.1 → 3.4.5

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +21 -4
  3. data/LICENSE +1 -1
  4. data/README.markdown +20 -25
  5. data/exe/bunto +1 -1
  6. data/lib/bunto.rb +10 -4
  7. data/lib/bunto/collection.rb +11 -4
  8. data/lib/bunto/commands/build.rb +17 -2
  9. data/lib/bunto/commands/doctor.rb +1 -1
  10. data/lib/bunto/commands/new.rb +35 -5
  11. data/lib/bunto/commands/new_theme.rb +4 -2
  12. data/lib/bunto/commands/serve.rb +45 -15
  13. data/lib/bunto/commands/serve/servlet.rb +1 -1
  14. data/lib/bunto/configuration.rb +9 -7
  15. data/lib/bunto/converters/markdown/kramdown_parser.rb +2 -2
  16. data/lib/bunto/converters/markdown/redcarpet_parser.rb +1 -1
  17. data/lib/bunto/convertible.rb +21 -82
  18. data/lib/bunto/desktop.ini +1 -1
  19. data/lib/bunto/document.rb +118 -81
  20. data/lib/bunto/drops/bunto_drop.rb +1 -1
  21. data/lib/bunto/drops/static_file_drop.rb +11 -0
  22. data/lib/bunto/drops/url_drop.rb +5 -0
  23. data/lib/bunto/entry_filter.rb +9 -10
  24. data/lib/bunto/excerpt.rb +2 -3
  25. data/lib/bunto/external.rb +1 -1
  26. data/lib/bunto/filters.rb +10 -32
  27. data/lib/bunto/filters/grouping_filters.rb +63 -0
  28. data/lib/bunto/filters/url_filters.rb +40 -0
  29. data/lib/bunto/frontmatter_defaults.rb +1 -1
  30. data/lib/bunto/hooks.rb +9 -9
  31. data/lib/bunto/log_adapter.rb +1 -1
  32. data/lib/bunto/page.rb +8 -4
  33. data/lib/bunto/plugin.rb +1 -1
  34. data/lib/bunto/reader.rb +2 -1
  35. data/lib/bunto/readers/data_reader.rb +9 -10
  36. data/lib/bunto/readers/post_reader.rb +1 -1
  37. data/lib/bunto/readers/theme_assets_reader.rb +47 -0
  38. data/lib/bunto/regenerator.rb +1 -1
  39. data/lib/bunto/related_posts.rb +3 -9
  40. data/lib/bunto/renderer.rb +26 -6
  41. data/lib/bunto/site.rb +12 -7
  42. data/lib/bunto/static_file.rb +20 -9
  43. data/lib/bunto/tags/highlight.rb +3 -3
  44. data/lib/bunto/tags/include.rb +9 -5
  45. data/lib/bunto/tags/link.rb +4 -2
  46. data/lib/bunto/tags/post_url.rb +4 -2
  47. data/lib/bunto/theme.rb +8 -4
  48. data/lib/bunto/theme_builder.rb +2 -2
  49. data/lib/bunto/url.rb +31 -8
  50. data/lib/bunto/utils.rb +16 -2
  51. data/lib/bunto/utils/ansi.rb +1 -1
  52. data/lib/bunto/utils/exec.rb +25 -0
  53. data/lib/bunto/utils/platforms.rb +52 -2
  54. data/lib/bunto/utils/win_tz.rb +73 -0
  55. data/lib/bunto/version.rb +1 -1
  56. data/lib/site_template/_config.yml +8 -3
  57. data/lib/site_template/_posts/0000-00-00-welcome-to-bunto.markdown.erb +4 -4
  58. data/lib/site_template/about.md +1 -1
  59. data/lib/site_template/index.md +6 -0
  60. data/lib/theme_template/LICENSE.txt.erb +1 -1
  61. data/lib/theme_template/README.md.erb +4 -4
  62. data/lib/theme_template/gitignore.erb +1 -0
  63. data/lib/theme_template/theme.gemspec.erb +3 -2
  64. metadata +55 -40
  65. data/lib/site_template/css/main.scss +0 -39
  66. data/lib/site_template/feed.xml +0 -30
  67. data/lib/site_template/index.html +0 -23
@@ -6,7 +6,7 @@ module Bunto
6
6
  class Servlet < WEBrick::HTTPServlet::FileHandler
7
7
  DEFAULTS = {
8
8
  "Cache-Control" => "private, max-age=0, proxy-revalidate, " \
9
- "no-store, no-cache, must-revalidate"
9
+ "no-store, no-cache, must-revalidate",
10
10
  }.freeze
11
11
 
12
12
  def initialize(server, root, callbacks)
@@ -17,7 +17,9 @@ module Bunto
17
17
  # Handling Reading
18
18
  "safe" => false,
19
19
  "include" => [".htaccess"],
20
- "exclude" => [],
20
+ "exclude" => %w(
21
+ node_modules vendor/bundle/ vendor/cache/ vendor/gems/ vendor/ruby/
22
+ ),
21
23
  "keep_files" => [".git", ".svn"],
22
24
  "encoding" => "utf-8",
23
25
  "markdown_ext" => "markdown,mkdown,mkdn,mkd,md",
@@ -43,7 +45,7 @@ module Bunto
43
45
  "detach" => false, # default to not detaching the server
44
46
  "port" => "4000",
45
47
  "host" => "127.0.0.1",
46
- "baseurl" => "",
48
+ "baseurl" => nil, # this mounts at /, i.e. no subdirectory
47
49
  "show_dir_listing" => false,
48
50
 
49
51
  # Output Configuration
@@ -56,15 +58,15 @@ module Bunto
56
58
  "defaults" => [],
57
59
 
58
60
  "liquid" => {
59
- "error_mode" => "warn"
61
+ "error_mode" => "warn",
60
62
  },
61
63
 
62
64
  "rdiscount" => {
63
- "extensions" => []
65
+ "extensions" => [],
64
66
  },
65
67
 
66
68
  "redcarpet" => {
67
- "extensions" => []
69
+ "extensions" => [],
68
70
  },
69
71
 
70
72
  "kramdown" => {
@@ -74,8 +76,8 @@ module Bunto
74
76
  "smart_quotes" => "lsquo,rsquo,ldquo,rdquo",
75
77
  "input" => "GFM",
76
78
  "hard_wrap" => false,
77
- "footnote_nr" => 1
78
- }
79
+ "footnote_nr" => 1,
80
+ },
79
81
  }.map { |k, v| [k, v.freeze] }].freeze
80
82
 
81
83
  class << self
@@ -11,7 +11,7 @@ module Bunto
11
11
  "line_numbers" => "inline",
12
12
  "line_number_start" => 1,
13
13
  "tab_width" => 4,
14
- "wrap" => "div"
14
+ "wrap" => "div",
15
15
  }.freeze
16
16
 
17
17
  def initialize(config)
@@ -104,7 +104,7 @@ module Bunto
104
104
 
105
105
  private
106
106
  def modernize_coderay_config
107
- if highlighter == "coderay"
107
+ unless @config["coderay"].empty?
108
108
  Bunto::Deprecator.deprecation_message(
109
109
  "You are using 'kramdown.coderay' in your configuration, " \
110
110
  "please use 'syntax_highlighter_opts' instead."
@@ -21,7 +21,7 @@ class Bunto::Converters::Markdown::RedcarpetParser
21
21
  code,
22
22
  {
23
23
  :lexer => lang,
24
- :options => { :encoding => "utf-8" }
24
+ :options => { :encoding => "utf-8" },
25
25
  }
26
26
  ),
27
27
  lang
@@ -35,6 +35,7 @@ module Bunto
35
35
  # opts - optional parameter to File.read, default at site configs
36
36
  #
37
37
  # Returns nothing.
38
+ # rubocop:disable Metrics/AbcSize
38
39
  def read_yaml(base, name, opts = {})
39
40
  filename = File.join(base, name)
40
41
 
@@ -58,6 +59,7 @@ module Bunto
58
59
 
59
60
  self.data
60
61
  end
62
+ # rubocop:enable Metrics/AbcSize
61
63
 
62
64
  def validate_data!(filename)
63
65
  unless self.data.is_a?(Hash)
@@ -76,18 +78,7 @@ module Bunto
76
78
  #
77
79
  # Returns the transformed contents.
78
80
  def transform
79
- converters.reduce(content) do |output, converter|
80
- begin
81
- converter.convert output
82
- rescue => e
83
- Bunto.logger.error(
84
- "Conversion error:",
85
- "#{converter.class} encountered an error while converting '#{path}':"
86
- )
87
- Bunto.logger.error("", e.to_s)
88
- raise e
89
- end
90
- end
81
+ _renderer.transform
91
82
  end
92
83
 
93
84
  # Determine the extension depending on content_type.
@@ -95,7 +86,7 @@ module Bunto
95
86
  # Returns the String extension for the output file.
96
87
  # e.g. ".html" for an HTML output file.
97
88
  def output_ext
98
- Bunto::Renderer.new(site, self).output_ext
89
+ _renderer.output_ext
99
90
  end
100
91
 
101
92
  # Determine which converter to use based on this convertible's
@@ -103,7 +94,7 @@ module Bunto
103
94
  #
104
95
  # Returns the Converter instance.
105
96
  def converters
106
- @converters ||= site.converters.select { |c| c.matches(ext) }.sort
97
+ _renderer.converters
107
98
  end
108
99
 
109
100
  # Render Liquid in the content
@@ -114,17 +105,7 @@ module Bunto
114
105
  #
115
106
  # Returns the converted content
116
107
  def render_liquid(content, payload, info, path)
117
- template = site.liquid_renderer.file(path).parse(content)
118
- template.warnings.each do |e|
119
- Bunto.logger.warn "Liquid Warning:",
120
- LiquidRenderer.format_error(e, path || self.path)
121
- end
122
- template.render!(payload, info)
123
- # rubocop: disable RescueException
124
- rescue Exception => e
125
- Bunto.logger.error "Liquid Exception:",
126
- LiquidRenderer.format_error(e, path || self.path)
127
- raise e
108
+ _renderer.render_liquid(content, payload, info, path)
128
109
  end
129
110
  # rubocop: enable RescueException
130
111
 
@@ -211,40 +192,10 @@ module Bunto
211
192
  #
212
193
  # Returns nothing
213
194
  def render_all_layouts(layouts, payload, info)
214
- # recursively render layouts
215
- layout = layouts[data["layout"]]
216
-
217
- Bunto.logger.warn(
218
- "Build Warning:",
219
- "Layout '#{data["layout"]}' requested in #{path} does not exist."
220
- ) if invalid_layout? layout
221
-
222
- used = Set.new([layout])
223
-
224
- # Reset the payload layout data to ensure it starts fresh for each page.
225
- payload["layout"] = nil
226
-
227
- while layout
228
- Bunto.logger.debug "Rendering Layout:", path
229
- payload["content"] = output
230
- payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
231
-
232
- self.output = render_liquid(layout.content,
233
- payload,
234
- info,
235
- layout.relative_path)
236
-
237
- # Add layout to dependency tree
238
- site.regenerator.add_dependency(
239
- site.in_source_dir(path),
240
- site.in_source_dir(layout.path)
241
- )
242
-
243
- if (layout = layouts[layout.data["layout"]])
244
- break if used.include?(layout)
245
- used << layout
246
- end
247
- end
195
+ _renderer.layouts = layouts
196
+ self.output = _renderer.place_in_layouts(output, payload, info)
197
+ ensure
198
+ @_renderer = nil # this will allow the modifications above to disappear
248
199
  end
249
200
 
250
201
  # Add any necessary layouts to this convertible document.
@@ -254,32 +205,15 @@ module Bunto
254
205
  #
255
206
  # Returns nothing.
256
207
  def do_layout(payload, layouts)
257
- Bunto.logger.debug "Rendering:", self.relative_path
258
-
259
- Bunto.logger.debug "Pre-Render Hooks:", self.relative_path
260
- Bunto::Hooks.trigger hook_owner, :pre_render, self, payload
261
- info = {
262
- :filters => [Bunto::Filters],
263
- :registers => { :site => site, :page => payload["page"] }
264
- }
208
+ self.output = _renderer.tap do |renderer|
209
+ renderer.layouts = layouts
210
+ renderer.payload = payload
211
+ end.run
265
212
 
266
- # render and transform content (this becomes the final content of the object)
267
- payload["highlighter_prefix"] = converters.first.highlighter_prefix
268
- payload["highlighter_suffix"] = converters.first.highlighter_suffix
269
-
270
- if render_with_liquid?
271
- Bunto.logger.debug "Rendering Liquid:", self.relative_path
272
- self.content = render_liquid(content, payload, info, path)
273
- end
274
- Bunto.logger.debug "Rendering Markup:", self.relative_path
275
- self.content = transform
276
-
277
- # output keeps track of what will finally be written
278
- self.output = content
279
-
280
- render_all_layouts(layouts, payload, info) if place_in_layout?
281
213
  Bunto.logger.debug "Post-Render Hooks:", self.relative_path
282
214
  Bunto::Hooks.trigger hook_owner, :post_render, self
215
+ ensure
216
+ @_renderer = nil # this will allow the modifications above to disappear
283
217
  end
284
218
 
285
219
  # Write the generated page file to the destination directory.
@@ -306,5 +240,10 @@ module Bunto
306
240
  data[property]
307
241
  end
308
242
  end
243
+
244
+ private
245
+ def _renderer
246
+ @_renderer ||= Bunto::Renderer.new(site, self)
247
+ end
309
248
  end
310
249
  end
@@ -1,2 +1,2 @@
1
1
  [.ShellClassInfo]
2
- LocalizedResourceName=bunto
2
+ LocalizedResourceName=jekl
@@ -9,7 +9,7 @@ module Bunto
9
9
 
10
10
  YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
11
11
  DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!
12
- DATE_FILENAME_MATCHER = %r!^(?:.+/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$!
12
+ DATE_FILENAME_MATCHER = %r!^(?:.+/)*(\d{2,4}-\d{1,2}-\d{1,2})-(.*)(\.[^.]+)$!
13
13
 
14
14
  # Create a new Document.
15
15
  #
@@ -51,19 +51,9 @@ module Bunto
51
51
  #
52
52
  # Returns the merged data.
53
53
  def merge_data!(other, source: "YAML front matter")
54
- if other.key?("categories") && !other["categories"].nil?
55
- if other["categories"].is_a?(String)
56
- other["categories"] = other["categories"].split(" ").map(&:strip)
57
- end
58
- other["categories"] = (data["categories"] || []) | other["categories"]
59
- end
54
+ merge_categories!(other)
60
55
  Utils.deep_merge_hashes!(data, other)
61
- if data.key?("date") && !data["date"].is_a?(Time)
62
- data["date"] = Utils.parse_date(
63
- data["date"].to_s,
64
- "Document '#{relative_path}' does not have a valid date in the #{source}."
65
- )
66
- end
56
+ merge_date!(source)
67
57
  data
68
58
  end
69
59
 
@@ -90,8 +80,7 @@ module Bunto
90
80
  # Returns a String path which represents the relative path
91
81
  # from the site source to this document
92
82
  def relative_path
93
- @relative_path ||= Pathname.new(path)
94
- .relative_path_from(Pathname.new(site.source)).to_s
83
+ @relative_path ||= Pathutil.new(path).relative_path_from(site.source).to_s
95
84
  end
96
85
 
97
86
  # The output extension of the document.
@@ -207,7 +196,7 @@ module Bunto
207
196
  @url = URL.new({
208
197
  :template => url_template,
209
198
  :placeholders => url_placeholders,
210
- :permalink => permalink
199
+ :permalink => permalink,
211
200
  }).to_s
212
201
  end
213
202
 
@@ -264,20 +253,9 @@ module Bunto
264
253
  @data = SafeYAML.load_file(path)
265
254
  else
266
255
  begin
267
- defaults = @site.frontmatter_defaults.all(
268
- relative_path,
269
- collection.label.to_sym
270
- )
271
- merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
272
-
273
- self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
274
- if content =~ YAML_FRONT_MATTER_REGEXP
275
- self.content = $POSTMATCH
276
- data_file = SafeYAML.load(Regexp.last_match(1))
277
- merge_data!(data_file, :source => "YAML front matter") if data_file
278
- end
279
-
280
- post_read
256
+ merge_defaults
257
+ read_content(opts)
258
+ read_post_data
281
259
  rescue SyntaxError => e
282
260
  Bunto.logger.error "Error:", "YAML Exception reading #{path}: #{e.message}"
283
261
  rescue => e
@@ -287,56 +265,6 @@ module Bunto
287
265
  end
288
266
  end
289
267
 
290
- def post_read
291
- if relative_path =~ DATE_FILENAME_MATCHER
292
- date, slug, ext = Regexp.last_match.captures
293
- if !data["date"] || data["date"].to_i == site.time.to_i
294
- merge_data!({ "date" => date }, :source => "filename")
295
- end
296
- elsif relative_path =~ DATELESS_FILENAME_MATCHER
297
- slug, ext = Regexp.last_match.captures
298
- end
299
-
300
- # Try to ensure the user gets a title.
301
- data["title"] ||= Utils.titleize_slug(slug)
302
- # Only overwrite slug & ext if they aren't specified.
303
- data["slug"] ||= slug
304
- data["ext"] ||= ext
305
-
306
- populate_categories
307
- populate_tags
308
- generate_excerpt
309
- end
310
-
311
- # Add superdirectories of the special_dir to categories.
312
- # In the case of es/_posts, 'es' is added as a category.
313
- # In the case of _posts/es, 'es' is NOT added as a category.
314
- #
315
- # Returns nothing.
316
- def categories_from_path(special_dir)
317
- superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
318
- .split(File::SEPARATOR)
319
- .reject do |c|
320
- c.empty? || c.eql?(special_dir) || c.eql?(basename)
321
- end
322
- merge_data!({ "categories" => superdirs }, :source => "file path")
323
- end
324
-
325
- def populate_categories
326
- merge_data!({
327
- "categories" => (
328
- Array(data["categories"]) +
329
- Utils.pluralized_array_from_hash(data, "category", "categories")
330
- ).map(&:to_s).flatten.uniq
331
- })
332
- end
333
-
334
- def populate_tags
335
- merge_data!({
336
- "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
337
- })
338
- end
339
-
340
268
  # Create a Liquid-understandable version of this Document.
341
269
  #
342
270
  # Returns a Hash representing this Document's data.
@@ -443,7 +371,116 @@ module Bunto
443
371
  end
444
372
  end
445
373
 
446
- private # :nodoc:
374
+ def respond_to_missing?(method, *)
375
+ data.key?(method.to_s) || super
376
+ end
377
+
378
+ private
379
+ def merge_categories!(other)
380
+ if other.key?("categories") && !other["categories"].nil?
381
+ if other["categories"].is_a?(String)
382
+ other["categories"] = other["categories"].split(%r!\s+!).map(&:strip)
383
+ end
384
+ other["categories"] = (data["categories"] || []) | other["categories"]
385
+ end
386
+ end
387
+
388
+ private
389
+ def merge_date!(source)
390
+ if data.key?("date") && !data["date"].is_a?(Time)
391
+ data["date"] = Utils.parse_date(
392
+ data["date"].to_s,
393
+ "Document '#{relative_path}' does not have a valid date in the #{source}."
394
+ )
395
+ end
396
+ end
397
+
398
+ private
399
+ def merge_defaults
400
+ defaults = @site.frontmatter_defaults.all(
401
+ relative_path,
402
+ collection.label.to_sym
403
+ )
404
+ merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
405
+ end
406
+
407
+ private
408
+ def read_content(opts)
409
+ self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
410
+ if content =~ YAML_FRONT_MATTER_REGEXP
411
+ self.content = $POSTMATCH
412
+ data_file = SafeYAML.load(Regexp.last_match(1))
413
+ merge_data!(data_file, :source => "YAML front matter") if data_file
414
+ end
415
+ end
416
+
417
+ private
418
+ def read_post_data
419
+ populate_title
420
+ populate_categories
421
+ populate_tags
422
+ generate_excerpt
423
+ end
424
+
425
+ private
426
+ def populate_title
427
+ if relative_path =~ DATE_FILENAME_MATCHER
428
+ date, slug, ext = Regexp.last_match.captures
429
+ modify_date(date)
430
+ elsif relative_path =~ DATELESS_FILENAME_MATCHER
431
+ slug, ext = Regexp.last_match.captures
432
+ end
433
+
434
+ # Try to ensure the user gets a title.
435
+ data["title"] ||= Utils.titleize_slug(slug)
436
+ # Only overwrite slug & ext if they aren't specified.
437
+ data["slug"] ||= slug
438
+ data["ext"] ||= ext
439
+ end
440
+
441
+ private
442
+ def modify_date(date)
443
+ if !data["date"] || data["date"].to_i == site.time.to_i
444
+ merge_data!({ "date" => date }, :source => "filename")
445
+ end
446
+ end
447
+
448
+ # Add superdirectories of the special_dir to categories.
449
+ # In the case of es/_posts, 'es' is added as a category.
450
+ # In the case of _posts/es, 'es' is NOT added as a category.
451
+ #
452
+ # Returns nothing.
453
+ private
454
+ def categories_from_path(special_dir)
455
+ superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
456
+ .split(File::SEPARATOR)
457
+ .reject do |c|
458
+ c.empty? || c == special_dir || c == basename
459
+ end
460
+ merge_data!({ "categories" => superdirs }, :source => "file path")
461
+ end
462
+
463
+ private
464
+ def populate_categories
465
+ merge_data!({
466
+ "categories" => (
467
+ Array(data["categories"]) + Utils.pluralized_array_from_hash(
468
+ data,
469
+ "category",
470
+ "categories"
471
+ )
472
+ ).map(&:to_s).flatten.uniq,
473
+ })
474
+ end
475
+
476
+ private
477
+ def populate_tags
478
+ merge_data!({
479
+ "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten,
480
+ })
481
+ end
482
+
483
+ private
447
484
  def generate_excerpt
448
485
  if generate_excerpt?
449
486
  data["excerpt"] ||= Bunto::Excerpt.new(self)