bunto 1.0.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.markdown +59 -0
- data/bin/bunto +51 -0
- data/lib/bunto.rb +179 -0
- data/lib/bunto/cleaner.rb +105 -0
- data/lib/bunto/collection.rb +205 -0
- data/lib/bunto/command.rb +65 -0
- data/lib/bunto/commands/build.rb +77 -0
- data/lib/bunto/commands/clean.rb +42 -0
- data/lib/bunto/commands/doctor.rb +114 -0
- data/lib/bunto/commands/help.rb +31 -0
- data/lib/bunto/commands/new.rb +82 -0
- data/lib/bunto/commands/serve.rb +204 -0
- data/lib/bunto/commands/serve/servlet.rb +61 -0
- data/lib/bunto/configuration.rb +323 -0
- data/lib/bunto/converter.rb +48 -0
- data/lib/bunto/converters/identity.rb +21 -0
- data/lib/bunto/converters/markdown.rb +92 -0
- data/lib/bunto/converters/markdown/kramdown_parser.rb +117 -0
- data/lib/bunto/converters/markdown/rdiscount_parser.rb +33 -0
- data/lib/bunto/converters/markdown/redcarpet_parser.rb +102 -0
- data/lib/bunto/converters/smartypants.rb +34 -0
- data/lib/bunto/convertible.rb +297 -0
- data/lib/bunto/deprecator.rb +46 -0
- data/lib/bunto/document.rb +444 -0
- data/lib/bunto/drops/bunto_drop.rb +21 -0
- data/lib/bunto/drops/collection_drop.rb +22 -0
- data/lib/bunto/drops/document_drop.rb +27 -0
- data/lib/bunto/drops/drop.rb +176 -0
- data/lib/bunto/drops/site_drop.rb +38 -0
- data/lib/bunto/drops/unified_payload_drop.rb +25 -0
- data/lib/bunto/drops/url_drop.rb +83 -0
- data/lib/bunto/entry_filter.rb +72 -0
- data/lib/bunto/errors.rb +10 -0
- data/lib/bunto/excerpt.rb +127 -0
- data/lib/bunto/external.rb +59 -0
- data/lib/bunto/filters.rb +367 -0
- data/lib/bunto/frontmatter_defaults.rb +188 -0
- data/lib/bunto/generator.rb +3 -0
- data/lib/bunto/hooks.rb +101 -0
- data/lib/bunto/layout.rb +49 -0
- data/lib/bunto/liquid_extensions.rb +22 -0
- data/lib/bunto/liquid_renderer.rb +39 -0
- data/lib/bunto/liquid_renderer/file.rb +50 -0
- data/lib/bunto/liquid_renderer/table.rb +94 -0
- data/lib/bunto/log_adapter.rb +115 -0
- data/lib/bunto/mime.types +800 -0
- data/lib/bunto/page.rb +180 -0
- data/lib/bunto/plugin.rb +96 -0
- data/lib/bunto/plugin_manager.rb +95 -0
- data/lib/bunto/post.rb +329 -0
- data/lib/bunto/publisher.rb +21 -0
- data/lib/bunto/reader.rb +126 -0
- data/lib/bunto/readers/collection_reader.rb +20 -0
- data/lib/bunto/readers/data_reader.rb +69 -0
- data/lib/bunto/readers/layout_reader.rb +53 -0
- data/lib/bunto/readers/page_reader.rb +21 -0
- data/lib/bunto/readers/post_reader.rb +62 -0
- data/lib/bunto/readers/static_file_reader.rb +21 -0
- data/lib/bunto/regenerator.rb +175 -0
- data/lib/bunto/related_posts.rb +56 -0
- data/lib/bunto/renderer.rb +191 -0
- data/lib/bunto/site.rb +391 -0
- data/lib/bunto/static_file.rb +141 -0
- data/lib/bunto/stevenson.rb +58 -0
- data/lib/bunto/tags/highlight.rb +122 -0
- data/lib/bunto/tags/include.rb +190 -0
- data/lib/bunto/tags/post_url.rb +88 -0
- data/lib/bunto/url.rb +136 -0
- data/lib/bunto/utils.rb +287 -0
- data/lib/bunto/utils/ansi.rb +59 -0
- data/lib/bunto/utils/platforms.rb +30 -0
- data/lib/bunto/version.rb +3 -0
- data/lib/site_template/.gitignore +3 -0
- data/lib/site_template/_config.yml +21 -0
- data/lib/site_template/_includes/footer.html +38 -0
- data/lib/site_template/_includes/head.html +12 -0
- data/lib/site_template/_includes/header.html +27 -0
- data/lib/site_template/_includes/icon-github.html +1 -0
- data/lib/site_template/_includes/icon-github.svg +1 -0
- data/lib/site_template/_includes/icon-twitter.html +1 -0
- data/lib/site_template/_includes/icon-twitter.svg +1 -0
- data/lib/site_template/_layouts/default.html +20 -0
- data/lib/site_template/_layouts/page.html +14 -0
- data/lib/site_template/_layouts/post.html +15 -0
- data/lib/site_template/_posts/0000-00-00-welcome-to-bunto.markdown.erb +25 -0
- data/lib/site_template/_sass/_base.scss +206 -0
- data/lib/site_template/_sass/_layout.scss +242 -0
- data/lib/site_template/_sass/_syntax-highlighting.scss +71 -0
- data/lib/site_template/about.md +15 -0
- data/lib/site_template/css/main.scss +53 -0
- data/lib/site_template/feed.xml +30 -0
- data/lib/site_template/index.html +23 -0
- metadata +252 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
module Bunto
|
2
|
+
# This class handles custom defaults for YAML frontmatter settings.
|
3
|
+
# These are set in _config.yml and apply both to internal use (e.g. layout)
|
4
|
+
# and the data available to liquid.
|
5
|
+
#
|
6
|
+
# It is exposed via the frontmatter_defaults method on the site class.
|
7
|
+
class FrontmatterDefaults
|
8
|
+
# Initializes a new instance.
|
9
|
+
def initialize(site)
|
10
|
+
@site = site
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_deprecated_types(set)
|
14
|
+
return set unless set.key?('scope') && set['scope'].key?('type')
|
15
|
+
|
16
|
+
set['scope']['type'] =
|
17
|
+
case set['scope']['type']
|
18
|
+
when 'page'
|
19
|
+
Deprecator.defaults_deprecate_type('page', 'pages')
|
20
|
+
'pages'
|
21
|
+
when 'post'
|
22
|
+
Deprecator.defaults_deprecate_type('post', 'posts')
|
23
|
+
'posts'
|
24
|
+
when 'draft'
|
25
|
+
Deprecator.defaults_deprecate_type('draft', 'drafts')
|
26
|
+
'drafts'
|
27
|
+
else
|
28
|
+
set['scope']['type']
|
29
|
+
end
|
30
|
+
|
31
|
+
set
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_time!(set)
|
35
|
+
return set unless set.key?('values') && set['values'].key?('date')
|
36
|
+
return set if set['values']['date'].is_a?(Time)
|
37
|
+
set['values']['date'] = Utils.parse_date(set['values']['date'], "An invalid date format was found in a front-matter default set: #{set}")
|
38
|
+
set
|
39
|
+
end
|
40
|
+
|
41
|
+
# Finds a default value for a given setting, filtered by path and type
|
42
|
+
#
|
43
|
+
# path - the path (relative to the source) of the page, post or :draft the default is used in
|
44
|
+
# type - a symbol indicating whether a :page, a :post or a :draft calls this method
|
45
|
+
#
|
46
|
+
# Returns the default value or nil if none was found
|
47
|
+
def find(path, type, setting)
|
48
|
+
value = nil
|
49
|
+
old_scope = nil
|
50
|
+
|
51
|
+
matching_sets(path, type).each do |set|
|
52
|
+
if set['values'].key?(setting) && has_precedence?(old_scope, set['scope'])
|
53
|
+
value = set['values'][setting]
|
54
|
+
old_scope = set['scope']
|
55
|
+
end
|
56
|
+
end
|
57
|
+
value
|
58
|
+
end
|
59
|
+
|
60
|
+
# Collects a hash with all default values for a page or post
|
61
|
+
#
|
62
|
+
# path - the relative path of the page or post
|
63
|
+
# type - a symbol indicating the type (:post, :page or :draft)
|
64
|
+
#
|
65
|
+
# Returns a hash with all default values (an empty hash if there are none)
|
66
|
+
def all(path, type)
|
67
|
+
defaults = {}
|
68
|
+
old_scope = nil
|
69
|
+
matching_sets(path, type).each do |set|
|
70
|
+
if has_precedence?(old_scope, set['scope'])
|
71
|
+
defaults = Utils.deep_merge_hashes(defaults, set['values'])
|
72
|
+
old_scope = set['scope']
|
73
|
+
else
|
74
|
+
defaults = Utils.deep_merge_hashes(set['values'], defaults)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
defaults
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Checks if a given default setting scope matches the given path and type
|
83
|
+
#
|
84
|
+
# scope - the hash indicating the scope, as defined in _config.yml
|
85
|
+
# path - the path to check for
|
86
|
+
# type - the type (:post, :page or :draft) to check for
|
87
|
+
#
|
88
|
+
# Returns true if the scope applies to the given path and type
|
89
|
+
def applies?(scope, path, type)
|
90
|
+
applies_path?(scope, path) && applies_type?(scope, type)
|
91
|
+
end
|
92
|
+
|
93
|
+
def applies_path?(scope, path)
|
94
|
+
return true if !scope.key?('path') || scope['path'].empty?
|
95
|
+
|
96
|
+
scope_path = Pathname.new(scope['path'])
|
97
|
+
Pathname.new(sanitize_path(path)).ascend do |path|
|
98
|
+
if path.to_s == scope_path.to_s
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Determines whether the scope applies to type.
|
105
|
+
# The scope applies to the type if:
|
106
|
+
# 1. no 'type' is specified
|
107
|
+
# 2. the 'type' in the scope is the same as the type asked about
|
108
|
+
#
|
109
|
+
# scope - the Hash defaults set being asked about application
|
110
|
+
# type - the type of the document being processed / asked about
|
111
|
+
# its defaults.
|
112
|
+
#
|
113
|
+
# Returns true if either of the above conditions are satisfied,
|
114
|
+
# otherwise returns false
|
115
|
+
def applies_type?(scope, type)
|
116
|
+
!scope.key?('type') || scope['type'].eql?(type.to_s)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Checks if a given set of default values is valid
|
120
|
+
#
|
121
|
+
# set - the default value hash, as defined in _config.yml
|
122
|
+
#
|
123
|
+
# Returns true if the set is valid and can be used in this class
|
124
|
+
def valid?(set)
|
125
|
+
set.is_a?(Hash) && set['values'].is_a?(Hash)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Determines if a new scope has precedence over an old one
|
129
|
+
#
|
130
|
+
# old_scope - the old scope hash, or nil if there's none
|
131
|
+
# new_scope - the new scope hash
|
132
|
+
#
|
133
|
+
# Returns true if the new scope has precedence over the older
|
134
|
+
def has_precedence?(old_scope, new_scope)
|
135
|
+
return true if old_scope.nil?
|
136
|
+
|
137
|
+
new_path = sanitize_path(new_scope['path'])
|
138
|
+
old_path = sanitize_path(old_scope['path'])
|
139
|
+
|
140
|
+
if new_path.length != old_path.length
|
141
|
+
new_path.length >= old_path.length
|
142
|
+
elsif new_scope.key? 'type'
|
143
|
+
true
|
144
|
+
else
|
145
|
+
!old_scope.key? 'type'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Collects a list of sets that match the given path and type
|
150
|
+
#
|
151
|
+
# Returns an array of hashes
|
152
|
+
def matching_sets(path, type)
|
153
|
+
valid_sets.select do |set|
|
154
|
+
!set.key?('scope') || applies?(set['scope'], path, type)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns a list of valid sets
|
159
|
+
#
|
160
|
+
# This is not cached to allow plugins to modify the configuration
|
161
|
+
# and have their changes take effect
|
162
|
+
#
|
163
|
+
# Returns an array of hashes
|
164
|
+
def valid_sets
|
165
|
+
sets = @site.config['defaults']
|
166
|
+
return [] unless sets.is_a?(Array)
|
167
|
+
|
168
|
+
sets.map do |set|
|
169
|
+
if valid?(set)
|
170
|
+
ensure_time!(update_deprecated_types(set))
|
171
|
+
else
|
172
|
+
Bunto.logger.warn "Defaults:", "An invalid front-matter default set was found:"
|
173
|
+
Bunto.logger.warn "#{set}"
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
end.compact
|
177
|
+
end
|
178
|
+
|
179
|
+
# Sanitizes the given path by removing a leading and adding a trailing slash
|
180
|
+
def sanitize_path(path)
|
181
|
+
if path.nil? || path.empty?
|
182
|
+
""
|
183
|
+
else
|
184
|
+
path.gsub(/\A\//, '').gsub(/([^\/])\z/, '\1')
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/bunto/hooks.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
module Bunto
|
2
|
+
module Hooks
|
3
|
+
DEFAULT_PRIORITY = 20
|
4
|
+
|
5
|
+
# compatibility layer for octopress-hooks users
|
6
|
+
PRIORITY_MAP = {
|
7
|
+
:low => 10,
|
8
|
+
:normal => 20,
|
9
|
+
:high => 30
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
# initial empty hooks
|
13
|
+
@registry = {
|
14
|
+
:site => {
|
15
|
+
:after_reset => [],
|
16
|
+
:post_read => [],
|
17
|
+
:pre_render => [],
|
18
|
+
:post_render => [],
|
19
|
+
:post_write => []
|
20
|
+
},
|
21
|
+
:pages => {
|
22
|
+
:post_init => [],
|
23
|
+
:pre_render => [],
|
24
|
+
:post_render => [],
|
25
|
+
:post_write => []
|
26
|
+
},
|
27
|
+
:posts => {
|
28
|
+
:post_init => [],
|
29
|
+
:pre_render => [],
|
30
|
+
:post_render => [],
|
31
|
+
:post_write => []
|
32
|
+
},
|
33
|
+
:documents => {
|
34
|
+
:post_init => [],
|
35
|
+
:pre_render => [],
|
36
|
+
:post_render => [],
|
37
|
+
:post_write => []
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
# map of all hooks and their priorities
|
42
|
+
@hook_priority = {}
|
43
|
+
|
44
|
+
NotAvailable = Class.new(RuntimeError)
|
45
|
+
Uncallable = Class.new(RuntimeError)
|
46
|
+
|
47
|
+
# register hook(s) to be called later, public API
|
48
|
+
def self.register(owners, event, priority: DEFAULT_PRIORITY, &block)
|
49
|
+
Array(owners).each do |owner|
|
50
|
+
register_one(owner, event, priority_value(priority), &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Ensure the priority is a Fixnum
|
55
|
+
def self.priority_value(priority)
|
56
|
+
return priority if priority.is_a?(Fixnum)
|
57
|
+
PRIORITY_MAP[priority] || DEFAULT_PRIORITY
|
58
|
+
end
|
59
|
+
|
60
|
+
# register a single hook to be called later, internal API
|
61
|
+
def self.register_one(owner, event, priority, &block)
|
62
|
+
@registry[owner] ||={
|
63
|
+
:post_init => [],
|
64
|
+
:pre_render => [],
|
65
|
+
:post_render => [],
|
66
|
+
:post_write => []
|
67
|
+
}
|
68
|
+
|
69
|
+
unless @registry[owner][event]
|
70
|
+
raise NotAvailable, "Invalid hook. #{owner} supports only the " \
|
71
|
+
"following hooks #{@registry[owner].keys.inspect}"
|
72
|
+
end
|
73
|
+
|
74
|
+
unless block.respond_to? :call
|
75
|
+
raise Uncallable, "Hooks must respond to :call"
|
76
|
+
end
|
77
|
+
|
78
|
+
insert_hook owner, event, priority, &block
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.insert_hook(owner, event, priority, &block)
|
82
|
+
@hook_priority[block] = "#{priority}.#{@hook_priority.size}".to_f
|
83
|
+
@registry[owner][event] << block
|
84
|
+
end
|
85
|
+
|
86
|
+
# interface for Bunto core components to trigger hooks
|
87
|
+
def self.trigger(owner, event, *args)
|
88
|
+
# proceed only if there are hooks to call
|
89
|
+
return unless @registry[owner]
|
90
|
+
return unless @registry[owner][event]
|
91
|
+
|
92
|
+
# hooks to call for this owner and event
|
93
|
+
hooks = @registry[owner][event]
|
94
|
+
|
95
|
+
# sort and call hooks according to priority and load order
|
96
|
+
hooks.sort_by { |h| @hook_priority[h] }.each do |hook|
|
97
|
+
hook.call(*args)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/bunto/layout.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Bunto
|
2
|
+
class Layout
|
3
|
+
include Convertible
|
4
|
+
|
5
|
+
# Gets the Site object.
|
6
|
+
attr_reader :site
|
7
|
+
|
8
|
+
# Gets the name of this layout.
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# Gets the path to this layout.
|
12
|
+
attr_reader :path
|
13
|
+
|
14
|
+
# Gets/Sets the extension of this layout.
|
15
|
+
attr_accessor :ext
|
16
|
+
|
17
|
+
# Gets/Sets the Hash that holds the metadata for this layout.
|
18
|
+
attr_accessor :data
|
19
|
+
|
20
|
+
# Gets/Sets the content of this layout.
|
21
|
+
attr_accessor :content
|
22
|
+
|
23
|
+
# Initialize a new Layout.
|
24
|
+
#
|
25
|
+
# site - The Site.
|
26
|
+
# base - The String path to the source.
|
27
|
+
# name - The String filename of the post file.
|
28
|
+
def initialize(site, base, name)
|
29
|
+
@site = site
|
30
|
+
@base = base
|
31
|
+
@name = name
|
32
|
+
@path = site.in_source_dir(base, name)
|
33
|
+
|
34
|
+
self.data = {}
|
35
|
+
|
36
|
+
process(name)
|
37
|
+
read_yaml(base, name)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Extract information from the layout filename.
|
41
|
+
#
|
42
|
+
# name - The String filename of the layout file.
|
43
|
+
#
|
44
|
+
# Returns nothing.
|
45
|
+
def process(name)
|
46
|
+
self.ext = File.extname(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Bunto
|
2
|
+
module LiquidExtensions
|
3
|
+
|
4
|
+
# Lookup a Liquid variable in the given context.
|
5
|
+
#
|
6
|
+
# context - the Liquid context in question.
|
7
|
+
# variable - the variable name, as a string.
|
8
|
+
#
|
9
|
+
# Returns the value of the variable in the context
|
10
|
+
# or the variable name if not found.
|
11
|
+
def lookup_variable(context, variable)
|
12
|
+
lookup = context
|
13
|
+
|
14
|
+
variable.split(".").each do |value|
|
15
|
+
lookup = lookup[value]
|
16
|
+
end
|
17
|
+
|
18
|
+
lookup || variable
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'bunto/liquid_renderer/file'
|
2
|
+
require 'bunto/liquid_renderer/table'
|
3
|
+
|
4
|
+
module Bunto
|
5
|
+
class LiquidRenderer
|
6
|
+
def initialize(site)
|
7
|
+
@site = site
|
8
|
+
reset
|
9
|
+
end
|
10
|
+
|
11
|
+
def reset
|
12
|
+
@stats = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def file(filename)
|
16
|
+
filename = @site.in_source_dir(filename).sub(/\A#{Regexp.escape(@site.source)}\//, '')
|
17
|
+
|
18
|
+
LiquidRenderer::File.new(self, filename).tap do
|
19
|
+
@stats[filename] ||= {}
|
20
|
+
@stats[filename][:count] ||= 0
|
21
|
+
@stats[filename][:count] += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def increment_bytes(filename, bytes)
|
26
|
+
@stats[filename][:bytes] ||= 0
|
27
|
+
@stats[filename][:bytes] += bytes
|
28
|
+
end
|
29
|
+
|
30
|
+
def increment_time(filename, time)
|
31
|
+
@stats[filename][:time] ||= 0.0
|
32
|
+
@stats[filename][:time] += time
|
33
|
+
end
|
34
|
+
|
35
|
+
def stats_table(n = 50)
|
36
|
+
LiquidRenderer::Table.new(@stats).to_s(n)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Bunto
|
2
|
+
class LiquidRenderer
|
3
|
+
class File
|
4
|
+
def initialize(renderer, filename)
|
5
|
+
@renderer = renderer
|
6
|
+
@filename = filename
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(content)
|
10
|
+
measure_time do
|
11
|
+
@template = Liquid::Template.parse(content, line_numbers: true)
|
12
|
+
end
|
13
|
+
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def render(*args)
|
18
|
+
measure_time do
|
19
|
+
measure_bytes do
|
20
|
+
@template.render(*args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def render!(*args)
|
26
|
+
measure_time do
|
27
|
+
measure_bytes do
|
28
|
+
@template.render!(*args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def measure_bytes
|
36
|
+
yield.tap do |str|
|
37
|
+
@renderer.increment_bytes(@filename, str.bytesize)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def measure_time
|
42
|
+
before = Time.now
|
43
|
+
yield
|
44
|
+
ensure
|
45
|
+
after = Time.now
|
46
|
+
@renderer.increment_time(@filename, after - before)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|