bunto 3.2.1 → 3.4.5

Sign up to get free protection for your applications and to get access to all the features.
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)