bunto 2.0.0.pre → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +80 -0
- data/README.markdown +15 -19
- data/bin/bunto +1 -1
- data/lib/bunto.rb +7 -5
- data/lib/bunto/cleaner.rb +3 -2
- data/lib/bunto/collection.rb +3 -3
- data/lib/bunto/commands/clean.rb +10 -12
- data/lib/bunto/commands/doctor.rb +1 -1
- data/lib/bunto/commands/new.rb +29 -0
- data/lib/bunto/commands/serve.rb +17 -15
- data/lib/bunto/configuration.rb +4 -3
- data/lib/bunto/converter.rb +6 -2
- data/lib/bunto/converters/markdown.rb +1 -1
- data/lib/bunto/converters/markdown/kramdown_parser.rb +1 -0
- data/lib/bunto/convertible.rb +5 -5
- data/lib/bunto/deprecator.rb +1 -1
- data/lib/bunto/document.rb +11 -4
- data/lib/bunto/drops/document_drop.rb +7 -0
- data/lib/bunto/entry_filter.rb +6 -2
- data/lib/bunto/errors.rb +4 -0
- data/lib/bunto/external.rb +1 -1
- data/lib/bunto/filters.rb +49 -8
- data/lib/bunto/frontmatter_defaults.rb +2 -2
- data/lib/bunto/hooks.rb +1 -0
- data/lib/bunto/layout.rb +16 -1
- data/lib/bunto/mime.types +1 -1
- data/lib/bunto/page.rb +1 -0
- data/lib/bunto/plugin.rb +1 -1
- data/lib/bunto/plugin_manager.rb +4 -4
- data/lib/bunto/publisher.rb +4 -4
- data/lib/bunto/readers/data_reader.rb +3 -2
- data/lib/bunto/readers/layout_reader.rb +19 -3
- data/lib/bunto/readers/post_reader.rb +5 -1
- data/lib/bunto/regenerator.rb +5 -3
- data/lib/bunto/renderer.rb +3 -2
- data/lib/bunto/site.rb +47 -17
- data/lib/bunto/static_file.rb +5 -1
- data/lib/bunto/tags/include.rb +33 -31
- data/lib/bunto/tags/link.rb +26 -0
- data/lib/bunto/tags/post_url.rb +18 -8
- data/lib/bunto/theme.rb +56 -0
- data/lib/bunto/utils.rb +3 -2
- data/lib/bunto/version.rb +1 -1
- data/lib/site_template/_config.yml +8 -2
- data/lib/site_template/_includes/footer.html +3 -3
- data/lib/site_template/_includes/head.html +2 -2
- data/lib/site_template/_includes/header.html +3 -3
- data/lib/site_template/_layouts/default.html +2 -2
- data/lib/site_template/_layouts/page.html +1 -1
- data/lib/site_template/_layouts/post.html +1 -1
- data/lib/site_template/_posts/0000-00-00-welcome-to-bunto.markdown.erb +1 -1
- data/lib/site_template/_sass/_base.scss +11 -17
- data/lib/site_template/about.md +5 -1
- data/lib/site_template/index.html +1 -1
- metadata +32 -29
data/lib/bunto/converter.rb
CHANGED
@@ -8,7 +8,9 @@ module Bunto
|
|
8
8
|
#
|
9
9
|
# Returns the String prefix.
|
10
10
|
def self.highlighter_prefix(highlighter_prefix = nil)
|
11
|
-
@highlighter_prefix
|
11
|
+
if !defined?(@highlighter_prefix) || !highlighter_prefix.nil?
|
12
|
+
@highlighter_prefix = highlighter_prefix
|
13
|
+
end
|
12
14
|
@highlighter_prefix
|
13
15
|
end
|
14
16
|
|
@@ -20,7 +22,9 @@ module Bunto
|
|
20
22
|
#
|
21
23
|
# Returns the String suffix.
|
22
24
|
def self.highlighter_suffix(highlighter_suffix = nil)
|
23
|
-
@highlighter_suffix
|
25
|
+
if !defined?(@highlighter_suffix) || !highlighter_suffix.nil?
|
26
|
+
@highlighter_suffix = highlighter_suffix
|
27
|
+
end
|
24
28
|
@highlighter_suffix
|
25
29
|
end
|
26
30
|
|
@@ -6,7 +6,7 @@ module Bunto
|
|
6
6
|
safe true
|
7
7
|
|
8
8
|
def setup
|
9
|
-
return if @setup
|
9
|
+
return if @setup ||= false
|
10
10
|
unless (@parser = get_processor)
|
11
11
|
Bunto.logger.error "Invalid Markdown processor given:", @config["markdown"]
|
12
12
|
Bunto.logger.info "", "Custom processors are not loaded in safe mode" if @config["safe"]
|
data/lib/bunto/convertible.rb
CHANGED
@@ -39,9 +39,9 @@ module Bunto
|
|
39
39
|
filename = File.join(base, name)
|
40
40
|
|
41
41
|
begin
|
42
|
-
self.content = File.read(site.in_source_dir(base, name),
|
42
|
+
self.content = File.read(@path || site.in_source_dir(base, name),
|
43
43
|
Utils.merged_file_read_opts(site, opts))
|
44
|
-
if content =~
|
44
|
+
if content =~ Document::YAML_FRONT_MATTER_REGEXP
|
45
45
|
self.content = $POSTMATCH
|
46
46
|
self.data = SafeYAML.load(Regexp.last_match(1))
|
47
47
|
end
|
@@ -215,9 +215,9 @@ module Bunto
|
|
215
215
|
payload["layout"] = Utils.deep_merge_hashes(payload["layout"] || {}, layout.data)
|
216
216
|
|
217
217
|
self.output = render_liquid(layout.content,
|
218
|
-
|
219
|
-
|
220
|
-
|
218
|
+
payload,
|
219
|
+
info,
|
220
|
+
layout.relative_path)
|
221
221
|
|
222
222
|
# Add layout to dependency tree
|
223
223
|
site.regenerator.add_dependency(
|
data/lib/bunto/deprecator.rb
CHANGED
@@ -22,7 +22,7 @@ module Bunto
|
|
22
22
|
|
23
23
|
def no_subcommand(args)
|
24
24
|
if args.size > 0 && args.first =~ /^--/ && !%w(--help --version).include?(args.first)
|
25
|
-
deprecation_message "Bunto now uses subcommands instead of just switches. Run `bunto
|
25
|
+
deprecation_message "Bunto now uses subcommands instead of just switches. Run `bunto help` to find out more."
|
26
26
|
abort
|
27
27
|
end
|
28
28
|
end
|
data/lib/bunto/document.rb
CHANGED
@@ -68,7 +68,11 @@ module Bunto
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def date
|
71
|
-
data['date'] ||= site.time
|
71
|
+
data['date'] ||= (draft? ? source_file_mtime : site.time)
|
72
|
+
end
|
73
|
+
|
74
|
+
def source_file_mtime
|
75
|
+
@source_file_mtime ||= File.mtime(path)
|
72
76
|
end
|
73
77
|
|
74
78
|
# Returns whether the document is a draft. This is only the case if
|
@@ -217,8 +221,11 @@ module Bunto
|
|
217
221
|
def destination(base_directory)
|
218
222
|
dest = site.in_dest_dir(base_directory)
|
219
223
|
path = site.in_dest_dir(dest, URL.unescape_path(url))
|
220
|
-
|
221
|
-
|
224
|
+
if url.end_with? "/"
|
225
|
+
path = File.join(path, "index.html")
|
226
|
+
else
|
227
|
+
path << output_ext unless path.end_with? output_ext
|
228
|
+
end
|
222
229
|
path
|
223
230
|
end
|
224
231
|
|
@@ -256,7 +263,7 @@ module Bunto
|
|
256
263
|
@data = SafeYAML.load_file(path)
|
257
264
|
else
|
258
265
|
begin
|
259
|
-
defaults = @site.frontmatter_defaults.all(
|
266
|
+
defaults = @site.frontmatter_defaults.all(relative_path, collection.label.to_sym)
|
260
267
|
merge_data!(defaults, source: "front matter defaults") unless defaults.empty?
|
261
268
|
|
262
269
|
self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
|
@@ -20,6 +20,13 @@ module Bunto
|
|
20
20
|
fallback_data['excerpt'].to_s
|
21
21
|
end
|
22
22
|
|
23
|
+
def <=>(other)
|
24
|
+
return nil unless other.is_a? DocumentDrop
|
25
|
+
cmp = self['date'] <=> other['date']
|
26
|
+
cmp = self['path'] <=> other['path'] if cmp.nil? || cmp == 0
|
27
|
+
cmp
|
28
|
+
end
|
29
|
+
|
23
30
|
private
|
24
31
|
def_delegator :@obj, :data, :fallback_data
|
25
32
|
end
|
data/lib/bunto/entry_filter.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Bunto
|
2
2
|
class EntryFilter
|
3
|
-
SPECIAL_LEADING_CHARACTERS = ['.', '_', '#'].freeze
|
3
|
+
SPECIAL_LEADING_CHARACTERS = ['.', '_', '#', '~'].freeze
|
4
4
|
|
5
5
|
attr_reader :site
|
6
6
|
|
@@ -52,7 +52,11 @@ module Bunto
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def symlink?(entry)
|
55
|
-
File.symlink?(entry) &&
|
55
|
+
site.safe && File.symlink?(entry) && symlink_outside_site_source?(entry)
|
56
|
+
end
|
57
|
+
|
58
|
+
def symlink_outside_site_source?(entry)
|
59
|
+
! File.realpath(entry).start_with?(File.realpath(@site.source))
|
56
60
|
end
|
57
61
|
|
58
62
|
def ensure_leading_slash(path)
|
data/lib/bunto/errors.rb
CHANGED
@@ -6,5 +6,9 @@ module Bunto
|
|
6
6
|
InvalidPermalinkError = Class.new(FatalException)
|
7
7
|
InvalidYAMLFrontMatterError = Class.new(FatalException)
|
8
8
|
MissingDependencyException = Class.new(FatalException)
|
9
|
+
|
10
|
+
InvalidDateError = Class.new(FatalException)
|
11
|
+
InvalidPostNameError = Class.new(FatalException)
|
12
|
+
PostURLError = Class.new(FatalException)
|
9
13
|
end
|
10
14
|
end
|
data/lib/bunto/external.rb
CHANGED
@@ -48,7 +48,7 @@ In order to use Bunto as currently configured, you'll need to install this gem.
|
|
48
48
|
|
49
49
|
The full error message from Ruby is: '#{e.message}'
|
50
50
|
|
51
|
-
If you run into trouble, you can find helpful resources at
|
51
|
+
If you run into trouble, you can find helpful resources at http://bunto.github.io/help/!
|
52
52
|
MSG
|
53
53
|
raise Bunto::Errors::MissingDependencyException.new(name)
|
54
54
|
end
|
data/lib/bunto/filters.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'json'
|
3
3
|
require 'date'
|
4
|
+
require 'liquid'
|
4
5
|
|
5
6
|
module Bunto
|
6
7
|
module Filters
|
@@ -15,11 +16,11 @@ module Bunto
|
|
15
16
|
converter.convert(input)
|
16
17
|
end
|
17
18
|
|
18
|
-
# Convert
|
19
|
+
# Convert quotes into smart quotes.
|
19
20
|
#
|
20
|
-
# input - The
|
21
|
+
# input - The String to convert.
|
21
22
|
#
|
22
|
-
# Returns the
|
23
|
+
# Returns the smart-quotified String.
|
23
24
|
def smartify(input)
|
24
25
|
site = @context.registers[:site]
|
25
26
|
converter = site.find_converter_instance(Bunto::Converters::SmartyPants)
|
@@ -117,7 +118,7 @@ module Bunto
|
|
117
118
|
#
|
118
119
|
# Returns the escaped String.
|
119
120
|
def xml_escape(input)
|
120
|
-
|
121
|
+
input.to_s.encode(:xml => :attr).gsub(/\A"|"\Z/, "")
|
121
122
|
end
|
122
123
|
|
123
124
|
# CGI escape a string for use in a URL. Replaces any special characters
|
@@ -205,7 +206,7 @@ module Bunto
|
|
205
206
|
input.group_by do |item|
|
206
207
|
item_property(item, property).to_s
|
207
208
|
end.inject([]) do |memo, i|
|
208
|
-
memo << { "name" => i.first, "items" => i.last }
|
209
|
+
memo << { "name" => i.first, "items" => i.last, "size" => i.last.size }
|
209
210
|
end
|
210
211
|
else
|
211
212
|
input
|
@@ -222,7 +223,27 @@ module Bunto
|
|
222
223
|
def where(input, property, value)
|
223
224
|
return input unless input.is_a?(Enumerable)
|
224
225
|
input = input.values if input.is_a?(Hash)
|
225
|
-
input.select { |object| item_property(object, property).to_s
|
226
|
+
input.select { |object| Array(item_property(object, property)).map(&:to_s).include?(value.to_s) }
|
227
|
+
end
|
228
|
+
|
229
|
+
# Filters an array of objects against an expression
|
230
|
+
#
|
231
|
+
# input - the object array
|
232
|
+
# variable - the variable to assign each item to in the expression
|
233
|
+
# expression - a Liquid comparison expression passed in as a string
|
234
|
+
#
|
235
|
+
# Returns the filtered array of objects
|
236
|
+
def where_exp(input, variable, expression)
|
237
|
+
return input unless input.is_a?(Enumerable)
|
238
|
+
input = input.values if input.is_a?(Hash) # FIXME
|
239
|
+
|
240
|
+
condition = parse_condition(expression)
|
241
|
+
@context.stack do
|
242
|
+
input.select do |object|
|
243
|
+
@context[variable] = object
|
244
|
+
condition.evaluate(@context)
|
245
|
+
end
|
246
|
+
end
|
226
247
|
end
|
227
248
|
|
228
249
|
# Sort an array of objects
|
@@ -308,14 +329,14 @@ module Bunto
|
|
308
329
|
#
|
309
330
|
# Returns a String representation of the object.
|
310
331
|
def inspect(input)
|
311
|
-
|
332
|
+
xml_escape(input.inspect)
|
312
333
|
end
|
313
334
|
|
314
335
|
private
|
315
336
|
def time(input)
|
316
337
|
case input
|
317
338
|
when Time
|
318
|
-
input
|
339
|
+
input.clone
|
319
340
|
when Date
|
320
341
|
input.to_time
|
321
342
|
when String
|
@@ -363,5 +384,25 @@ module Bunto
|
|
363
384
|
end
|
364
385
|
end
|
365
386
|
end
|
387
|
+
|
388
|
+
# Parse a string to a Liquid Condition
|
389
|
+
def parse_condition(exp)
|
390
|
+
parser = Liquid::Parser.new(exp)
|
391
|
+
left_expr = parser.expression
|
392
|
+
operator = parser.consume?(:comparison)
|
393
|
+
condition =
|
394
|
+
if operator
|
395
|
+
Liquid::Condition.new(left_expr, operator, parser.expression)
|
396
|
+
else
|
397
|
+
Liquid::Condition.new(left_expr)
|
398
|
+
end
|
399
|
+
parser.consume(:end_of_string)
|
400
|
+
|
401
|
+
condition
|
402
|
+
end
|
366
403
|
end
|
367
404
|
end
|
405
|
+
|
406
|
+
Liquid::Template.register_filter(
|
407
|
+
Bunto::Filters
|
408
|
+
)
|
@@ -94,8 +94,8 @@ module Bunto
|
|
94
94
|
return true if !scope.key?('path') || scope['path'].empty?
|
95
95
|
|
96
96
|
scope_path = Pathname.new(scope['path'])
|
97
|
-
Pathname.new(sanitize_path(path)).ascend do |
|
98
|
-
if
|
97
|
+
Pathname.new(sanitize_path(path)).ascend do |ascended_path|
|
98
|
+
if ascended_path.to_s == scope_path.to_s
|
99
99
|
return true
|
100
100
|
end
|
101
101
|
end
|
data/lib/bunto/hooks.rb
CHANGED
data/lib/bunto/layout.rb
CHANGED
@@ -29,7 +29,14 @@ module Bunto
|
|
29
29
|
@site = site
|
30
30
|
@base = base
|
31
31
|
@name = name
|
32
|
-
|
32
|
+
|
33
|
+
if site.theme && site.theme.layouts_path.eql?(base)
|
34
|
+
@base_dir = site.theme.root
|
35
|
+
@path = site.in_theme_dir(base, name)
|
36
|
+
else
|
37
|
+
@base_dir = site.source
|
38
|
+
@path = site.in_source_dir(base, name)
|
39
|
+
end
|
33
40
|
|
34
41
|
self.data = {}
|
35
42
|
|
@@ -45,5 +52,13 @@ module Bunto
|
|
45
52
|
def process(name)
|
46
53
|
self.ext = File.extname(name)
|
47
54
|
end
|
55
|
+
|
56
|
+
# The path to the layout, relative to the site source.
|
57
|
+
#
|
58
|
+
# Returns a String path which represents the relative path
|
59
|
+
# from the site source to this layout
|
60
|
+
def relative_path
|
61
|
+
@relative_path ||= Pathname.new(path).relative_path_from(Pathname.new(@base_dir)).to_s
|
62
|
+
end
|
48
63
|
end
|
49
64
|
end
|
data/lib/bunto/mime.types
CHANGED
data/lib/bunto/page.rb
CHANGED
data/lib/bunto/plugin.rb
CHANGED
data/lib/bunto/plugin_manager.rb
CHANGED
@@ -30,16 +30,16 @@ module Bunto
|
|
30
30
|
def self.require_from_bundler
|
31
31
|
if !ENV["BUNTO_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
|
32
32
|
require "bundler"
|
33
|
-
|
34
|
-
|
33
|
+
|
34
|
+
Bundler.setup
|
35
|
+
required_gems = Bundler.require(:bunto_plugins)
|
35
36
|
Bunto.logger.debug("PluginManager:", "Required #{required_gems.map(&:name).join(', ')}")
|
36
37
|
ENV["BUNTO_NO_BUNDLER_REQUIRE"] = "true"
|
38
|
+
|
37
39
|
true
|
38
40
|
else
|
39
41
|
false
|
40
42
|
end
|
41
|
-
rescue LoadError, Bundler::GemfileNotFound
|
42
|
-
false
|
43
43
|
end
|
44
44
|
|
45
45
|
# Check whether a gem plugin is allowed to be used during this build.
|
data/lib/bunto/publisher.rb
CHANGED
@@ -8,14 +8,14 @@ module Bunto
|
|
8
8
|
can_be_published?(thing) && !hidden_in_the_future?(thing)
|
9
9
|
end
|
10
10
|
|
11
|
+
def hidden_in_the_future?(thing)
|
12
|
+
thing.respond_to?(:date) && !@site.future && thing.date.to_i > @site.time.to_i
|
13
|
+
end
|
14
|
+
|
11
15
|
private
|
12
16
|
|
13
17
|
def can_be_published?(thing)
|
14
18
|
thing.data.fetch('published', true) || @site.unpublished
|
15
19
|
end
|
16
|
-
|
17
|
-
def hidden_in_the_future?(thing)
|
18
|
-
thing.respond_to?(:date) && !@site.future && thing.date.to_i > @site.time.to_i
|
19
|
-
end
|
20
20
|
end
|
21
21
|
end
|
@@ -4,6 +4,7 @@ module Bunto
|
|
4
4
|
def initialize(site)
|
5
5
|
@site = site
|
6
6
|
@content = {}
|
7
|
+
@entry_filter = EntryFilter.new(site)
|
7
8
|
end
|
8
9
|
|
9
10
|
# Read all the files in <source>/<dir>/_drafts and create a new Draft
|
@@ -26,7 +27,7 @@ module Bunto
|
|
26
27
|
#
|
27
28
|
# Returns nothing
|
28
29
|
def read_data_to(dir, data)
|
29
|
-
return unless File.directory?(dir) &&
|
30
|
+
return unless File.directory?(dir) && !@entry_filter.symlink?(dir)
|
30
31
|
|
31
32
|
entries = Dir.chdir(dir) do
|
32
33
|
Dir['*.{yaml,yml,json,csv}'] + Dir['*'].select { |fn| File.directory?(fn) }
|
@@ -34,7 +35,7 @@ module Bunto
|
|
34
35
|
|
35
36
|
entries.each do |entry|
|
36
37
|
path = @site.in_source_dir(dir, entry)
|
37
|
-
next if
|
38
|
+
next if @entry_filter.symlink?(path)
|
38
39
|
|
39
40
|
key = sanitize_filename(File.basename(entry, '.*'))
|
40
41
|
if File.directory?(path)
|
@@ -7,8 +7,12 @@ module Bunto
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def read
|
10
|
-
layout_entries.each do |
|
11
|
-
@layouts[layout_name(
|
10
|
+
layout_entries.each do |layout_file|
|
11
|
+
@layouts[layout_name(layout_file)] = Layout.new(site, layout_directory, layout_file)
|
12
|
+
end
|
13
|
+
|
14
|
+
theme_layout_entries.each do |layout_file|
|
15
|
+
@layouts[layout_name(layout_file)] ||= Layout.new(site, theme_layout_directory, layout_file)
|
12
16
|
end
|
13
17
|
|
14
18
|
@layouts
|
@@ -18,11 +22,23 @@ module Bunto
|
|
18
22
|
@layout_directory ||= (layout_directory_in_cwd || layout_directory_inside_source)
|
19
23
|
end
|
20
24
|
|
25
|
+
def theme_layout_directory
|
26
|
+
@theme_layout_directory ||= site.theme.layouts_path if site.theme
|
27
|
+
end
|
28
|
+
|
21
29
|
private
|
22
30
|
|
23
31
|
def layout_entries
|
32
|
+
entries_in layout_directory
|
33
|
+
end
|
34
|
+
|
35
|
+
def theme_layout_entries
|
36
|
+
theme_layout_directory ? entries_in(theme_layout_directory) : []
|
37
|
+
end
|
38
|
+
|
39
|
+
def entries_in(dir)
|
24
40
|
entries = []
|
25
|
-
within(
|
41
|
+
within(dir) do
|
26
42
|
entries = EntryFilter.new(site).filter(Dir['**/*.*'])
|
27
43
|
end
|
28
44
|
entries
|