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,88 @@
|
|
1
|
+
module Bunto
|
2
|
+
module Tags
|
3
|
+
class PostComparer
|
4
|
+
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)$/
|
5
|
+
|
6
|
+
attr_reader :path, :date, :slug, :name
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
all, @path, @date, @slug = *name.sub(/^\//, "").match(MATCHER)
|
11
|
+
raise ArgumentError.new("'#{name}' does not contain valid date and/or title.") unless all
|
12
|
+
|
13
|
+
@name_regex = /^#{path}#{date}-#{slug}\.[^.]+/
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
other.basename.match(@name_regex)
|
18
|
+
end
|
19
|
+
|
20
|
+
def deprecated_equality(other)
|
21
|
+
date = Utils.parse_date(name, "'#{name}' does not contain valid date and/or title.")
|
22
|
+
slug == post_slug(other) &&
|
23
|
+
date.year == other.date.year &&
|
24
|
+
date.month == other.date.month &&
|
25
|
+
date.day == other.date.day
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
# Construct the directory-aware post slug for a Bunto::Post
|
30
|
+
#
|
31
|
+
# other - the Bunto::Post
|
32
|
+
#
|
33
|
+
# Returns the post slug with the subdirectory (relative to _posts)
|
34
|
+
def post_slug(other)
|
35
|
+
path = other.basename.split("/")[0...-1].join("/")
|
36
|
+
if path.nil? || path == ""
|
37
|
+
other.data['slug']
|
38
|
+
else
|
39
|
+
path + '/' + other.data['slug']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class PostUrl < Liquid::Tag
|
45
|
+
def initialize(tag_name, post, tokens)
|
46
|
+
super
|
47
|
+
@orig_post = post.strip
|
48
|
+
begin
|
49
|
+
@post = PostComparer.new(@orig_post)
|
50
|
+
rescue
|
51
|
+
raise ArgumentError.new <<-eos
|
52
|
+
Could not parse name of post "#{@orig_post}" in tag 'post_url'.
|
53
|
+
|
54
|
+
Make sure the post exists and the name is correct.
|
55
|
+
eos
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def render(context)
|
60
|
+
site = context.registers[:site]
|
61
|
+
|
62
|
+
site.posts.docs.each do |p|
|
63
|
+
return p.url if @post == p
|
64
|
+
end
|
65
|
+
|
66
|
+
# New matching method did not match, fall back to old method
|
67
|
+
# with deprecation warning if this matches
|
68
|
+
|
69
|
+
site.posts.docs.each do |p|
|
70
|
+
next unless @post.deprecated_equality p
|
71
|
+
Bunto::Deprecator.deprecation_message "A call to '{{ post_url #{@post.name} }}' did not match " \
|
72
|
+
"a post using the new matching method of checking name " \
|
73
|
+
"(path-date-slug) equality. Please make sure that you " \
|
74
|
+
"change this tag to match the post's name exactly."
|
75
|
+
return p.url
|
76
|
+
end
|
77
|
+
|
78
|
+
raise ArgumentError.new <<-eos
|
79
|
+
Could not find post "#{@orig_post}" in tag 'post_url'.
|
80
|
+
|
81
|
+
Make sure the post exists and the name is correct.
|
82
|
+
eos
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
Liquid::Template.register_tag('post_url', Bunto::Tags::PostUrl)
|
data/lib/bunto/url.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
# Public: Methods that generate a URL for a resource such as a Post or a Page.
|
4
|
+
#
|
5
|
+
# Examples
|
6
|
+
#
|
7
|
+
# URL.new({
|
8
|
+
# :template => /:categories/:title.html",
|
9
|
+
# :placeholders => {:categories => "ruby", :title => "something"}
|
10
|
+
# }).to_s
|
11
|
+
#
|
12
|
+
module Bunto
|
13
|
+
class URL
|
14
|
+
# options - One of :permalink or :template must be supplied.
|
15
|
+
# :template - The String used as template for URL generation,
|
16
|
+
# for example "/:path/:basename:output_ext", where
|
17
|
+
# a placeholder is prefixed with a colon.
|
18
|
+
# :placeholders - A hash containing the placeholders which will be
|
19
|
+
# replaced when used inside the template. E.g.
|
20
|
+
# { "year" => Time.now.strftime("%Y") } would replace
|
21
|
+
# the placeholder ":year" with the current year.
|
22
|
+
# :permalink - If supplied, no URL will be generated from the
|
23
|
+
# template. Instead, the given permalink will be
|
24
|
+
# used as URL.
|
25
|
+
def initialize(options)
|
26
|
+
@template = options[:template]
|
27
|
+
@placeholders = options[:placeholders] || {}
|
28
|
+
@permalink = options[:permalink]
|
29
|
+
|
30
|
+
if (@template || @permalink).nil?
|
31
|
+
raise ArgumentError, "One of :template or :permalink must be supplied."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# The generated relative URL of the resource
|
36
|
+
#
|
37
|
+
# Returns the String URL
|
38
|
+
def to_s
|
39
|
+
sanitize_url(generated_permalink || generated_url)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generates a URL from the permalink
|
43
|
+
#
|
44
|
+
# Returns the _unsanitized String URL
|
45
|
+
def generated_permalink
|
46
|
+
(@generated_permalink ||= generate_url(@permalink)) if @permalink
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generates a URL from the template
|
50
|
+
#
|
51
|
+
# Returns the unsanitized String URL
|
52
|
+
def generated_url
|
53
|
+
@generated_url ||= generate_url(@template)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Internal: Generate the URL by replacing all placeholders with their
|
57
|
+
# respective values in the given template
|
58
|
+
#
|
59
|
+
# Returns the unsanitized String URL
|
60
|
+
def generate_url(template)
|
61
|
+
if @placeholders.is_a? Drops::UrlDrop
|
62
|
+
generate_url_from_drop(template)
|
63
|
+
else
|
64
|
+
generate_url_from_hash(template)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_url_from_hash(template)
|
69
|
+
@placeholders.inject(template) do |result, token|
|
70
|
+
break result if result.index(':').nil?
|
71
|
+
if token.last.nil?
|
72
|
+
# Remove leading '/' to avoid generating urls with `//`
|
73
|
+
result.gsub(/\/:#{token.first}/, '')
|
74
|
+
else
|
75
|
+
result.gsub(/:#{token.first}/, self.class.escape_path(token.last))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_url_from_drop(template)
|
81
|
+
template.gsub(/:([a-z_]+)/.freeze) do |match|
|
82
|
+
replacement = @placeholders.public_send(match.sub(':'.freeze, ''.freeze))
|
83
|
+
if replacement.nil?
|
84
|
+
''.freeze
|
85
|
+
else
|
86
|
+
self.class.escape_path(replacement)
|
87
|
+
end
|
88
|
+
end.gsub(/\/\//.freeze, '/'.freeze)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a sanitized String URL, stripping "../../" and multiples of "/",
|
92
|
+
# as well as the beginning "/" so we can enforce and ensure it.
|
93
|
+
|
94
|
+
def sanitize_url(str)
|
95
|
+
"/" + str.gsub(/\/{2,}/, "/").gsub(/\.+\/|\A\/+/, "")
|
96
|
+
end
|
97
|
+
|
98
|
+
# Escapes a path to be a valid URL path segment
|
99
|
+
#
|
100
|
+
# path - The path to be escaped.
|
101
|
+
#
|
102
|
+
# Examples:
|
103
|
+
#
|
104
|
+
# URL.escape_path("/a b")
|
105
|
+
# # => "/a%20b"
|
106
|
+
#
|
107
|
+
# Returns the escaped path.
|
108
|
+
def self.escape_path(path)
|
109
|
+
# Because URI.escape doesn't escape '?', '[' and ']' by default,
|
110
|
+
# specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
|
111
|
+
#
|
112
|
+
# URI path segment is defined in RFC 3986 as follows:
|
113
|
+
# segment = *pchar
|
114
|
+
# pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
115
|
+
# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
116
|
+
# pct-encoded = "%" HEXDIG HEXDIG
|
117
|
+
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
118
|
+
# / "*" / "+" / "," / ";" / "="
|
119
|
+
URI.escape(path, /[^a-zA-Z\d\-._~!$&'()*+,;=:@\/]/).encode('utf-8')
|
120
|
+
end
|
121
|
+
|
122
|
+
# Unescapes a URL path segment
|
123
|
+
#
|
124
|
+
# path - The path to be unescaped.
|
125
|
+
#
|
126
|
+
# Examples:
|
127
|
+
#
|
128
|
+
# URL.unescape_path("/a%20b")
|
129
|
+
# # => "/a b"
|
130
|
+
#
|
131
|
+
# Returns the unescaped path.
|
132
|
+
def self.unescape_path(path)
|
133
|
+
URI.unescape(path.encode('utf-8'))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/bunto/utils.rb
ADDED
@@ -0,0 +1,287 @@
|
|
1
|
+
module Bunto
|
2
|
+
module Utils
|
3
|
+
extend self
|
4
|
+
autoload :Platforms, 'bunto/utils/platforms'
|
5
|
+
autoload :Ansi, "bunto/utils/ansi"
|
6
|
+
|
7
|
+
# Constants for use in #slugify
|
8
|
+
SLUGIFY_MODES = %w(raw default pretty)
|
9
|
+
SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
|
10
|
+
SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze
|
11
|
+
SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
|
12
|
+
|
13
|
+
# Takes an indented string and removes the preceding spaces on each line
|
14
|
+
|
15
|
+
def strip_heredoc(str)
|
16
|
+
str.gsub(/^[ \t]{#{(str.scan(/^[ \t]*(?=\S)/).min || "").size}}/, "")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Takes a slug and turns it into a simple title.
|
20
|
+
|
21
|
+
def titleize_slug(slug)
|
22
|
+
slug.split("-").map! do |val|
|
23
|
+
val.capitalize!
|
24
|
+
end.join(" ")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Non-destructive version of deep_merge_hashes! See that method.
|
28
|
+
#
|
29
|
+
# Returns the merged hashes.
|
30
|
+
def deep_merge_hashes(master_hash, other_hash)
|
31
|
+
deep_merge_hashes!(master_hash.dup, other_hash)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Merges a master hash with another hash, recursively.
|
35
|
+
#
|
36
|
+
# master_hash - the "parent" hash whose values will be overridden
|
37
|
+
# other_hash - the other hash whose values will be persisted after the merge
|
38
|
+
#
|
39
|
+
# This code was lovingly stolen from some random gem:
|
40
|
+
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
41
|
+
#
|
42
|
+
# Thanks to whoever made it.
|
43
|
+
def deep_merge_hashes!(target, overwrite)
|
44
|
+
target.merge!(overwrite) do |key, old_val, new_val|
|
45
|
+
if new_val.nil?
|
46
|
+
old_val
|
47
|
+
else
|
48
|
+
mergable?(old_val) && mergable?(new_val) ? deep_merge_hashes(old_val, new_val) : new_val
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if target.respond_to?(:default_proc) && overwrite.respond_to?(:default_proc) && target.default_proc.nil?
|
53
|
+
target.default_proc = overwrite.default_proc
|
54
|
+
end
|
55
|
+
|
56
|
+
target
|
57
|
+
end
|
58
|
+
|
59
|
+
def mergable?(value)
|
60
|
+
value.is_a?(Hash) || value.is_a?(Drops::Drop)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Read array from the supplied hash favouring the singular key
|
64
|
+
# and then the plural key, and handling any nil entries.
|
65
|
+
#
|
66
|
+
# hash - the hash to read from
|
67
|
+
# singular_key - the singular key
|
68
|
+
# plural_key - the plural key
|
69
|
+
#
|
70
|
+
# Returns an array
|
71
|
+
def pluralized_array_from_hash(hash, singular_key, plural_key)
|
72
|
+
[].tap do |array|
|
73
|
+
array << (value_from_singular_key(hash, singular_key) || value_from_plural_key(hash, plural_key))
|
74
|
+
end.flatten.compact
|
75
|
+
end
|
76
|
+
|
77
|
+
def value_from_singular_key(hash, key)
|
78
|
+
hash[key] if hash.key?(key) || (hash.default_proc && hash[key])
|
79
|
+
end
|
80
|
+
|
81
|
+
def value_from_plural_key(hash, key)
|
82
|
+
if hash.key?(key) || (hash.default_proc && hash[key])
|
83
|
+
val = hash[key]
|
84
|
+
case val
|
85
|
+
when String
|
86
|
+
val.split
|
87
|
+
when Array
|
88
|
+
val.compact
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def transform_keys(hash)
|
94
|
+
result = {}
|
95
|
+
hash.each_key do |key|
|
96
|
+
result[yield(key)] = hash[key]
|
97
|
+
end
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
# Apply #to_sym to all keys in the hash
|
102
|
+
#
|
103
|
+
# hash - the hash to which to apply this transformation
|
104
|
+
#
|
105
|
+
# Returns a new hash with symbolized keys
|
106
|
+
def symbolize_hash_keys(hash)
|
107
|
+
transform_keys(hash) { |key| key.to_sym rescue key }
|
108
|
+
end
|
109
|
+
|
110
|
+
# Apply #to_s to all keys in the Hash
|
111
|
+
#
|
112
|
+
# hash - the hash to which to apply this transformation
|
113
|
+
#
|
114
|
+
# Returns a new hash with stringified keys
|
115
|
+
def stringify_hash_keys(hash)
|
116
|
+
transform_keys(hash) { |key| key.to_s rescue key }
|
117
|
+
end
|
118
|
+
|
119
|
+
# Parse a date/time and throw an error if invalid
|
120
|
+
#
|
121
|
+
# input - the date/time to parse
|
122
|
+
# msg - (optional) the error message to show the user
|
123
|
+
#
|
124
|
+
# Returns the parsed date if successful, throws a FatalException
|
125
|
+
# if not
|
126
|
+
def parse_date(input, msg = "Input could not be parsed.")
|
127
|
+
Time.parse(input).localtime
|
128
|
+
rescue ArgumentError
|
129
|
+
raise Errors::FatalException.new("Invalid date '#{input}': " + msg)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Determines whether a given file has
|
133
|
+
#
|
134
|
+
# Returns true if the YAML front matter is present.
|
135
|
+
def has_yaml_header?(file)
|
136
|
+
!!(File.open(file, 'rb') { |f| f.readline } =~ /\A---\s*\r?\n/)
|
137
|
+
rescue EOFError
|
138
|
+
false
|
139
|
+
end
|
140
|
+
|
141
|
+
# Slugify a filename or title.
|
142
|
+
#
|
143
|
+
# string - the filename or title to slugify
|
144
|
+
# mode - how string is slugified
|
145
|
+
# cased - whether to replace all uppercase letters with their
|
146
|
+
# lowercase counterparts
|
147
|
+
#
|
148
|
+
# When mode is "none", return the given string.
|
149
|
+
#
|
150
|
+
# When mode is "raw", return the given string,
|
151
|
+
# with every sequence of spaces characters replaced with a hyphen.
|
152
|
+
#
|
153
|
+
# When mode is "default" or nil, non-alphabetic characters are
|
154
|
+
# replaced with a hyphen too.
|
155
|
+
#
|
156
|
+
# When mode is "pretty", some non-alphabetic characters (._~!$&'()+,;=@)
|
157
|
+
# are not replaced with hyphen.
|
158
|
+
#
|
159
|
+
# If cased is true, all uppercase letters in the result string are
|
160
|
+
# replaced with their lowercase counterparts.
|
161
|
+
#
|
162
|
+
# Examples:
|
163
|
+
# slugify("The _config.yml file")
|
164
|
+
# # => "the-config-yml-file"
|
165
|
+
#
|
166
|
+
# slugify("The _config.yml file", "pretty")
|
167
|
+
# # => "the-_config.yml-file"
|
168
|
+
#
|
169
|
+
# slugify("The _config.yml file", "pretty", true)
|
170
|
+
# # => "The-_config.yml file"
|
171
|
+
#
|
172
|
+
# Returns the slugified string.
|
173
|
+
def slugify(string, mode: nil, cased: false)
|
174
|
+
mode ||= 'default'
|
175
|
+
return nil if string.nil?
|
176
|
+
|
177
|
+
unless SLUGIFY_MODES.include?(mode)
|
178
|
+
return cased ? string : string.downcase
|
179
|
+
end
|
180
|
+
|
181
|
+
# Replace each character sequence with a hyphen
|
182
|
+
re =
|
183
|
+
case mode
|
184
|
+
when 'raw'
|
185
|
+
SLUGIFY_RAW_REGEXP
|
186
|
+
when 'default'
|
187
|
+
SLUGIFY_DEFAULT_REGEXP
|
188
|
+
when 'pretty'
|
189
|
+
# "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL
|
190
|
+
# and is allowed in both extN and NTFS.
|
191
|
+
SLUGIFY_PRETTY_REGEXP
|
192
|
+
end
|
193
|
+
|
194
|
+
# Strip according to the mode
|
195
|
+
slug = string.gsub(re, '-')
|
196
|
+
|
197
|
+
# Remove leading/trailing hyphen
|
198
|
+
slug.gsub!(/^\-|\-$/i, '')
|
199
|
+
|
200
|
+
slug.downcase! unless cased
|
201
|
+
slug
|
202
|
+
end
|
203
|
+
|
204
|
+
# Add an appropriate suffix to template so that it matches the specified
|
205
|
+
# permalink style.
|
206
|
+
#
|
207
|
+
# template - permalink template without trailing slash or file extension
|
208
|
+
# permalink_style - permalink style, either built-in or custom
|
209
|
+
#
|
210
|
+
# The returned permalink template will use the same ending style as
|
211
|
+
# specified in permalink_style. For example, if permalink_style contains a
|
212
|
+
# trailing slash (or is :pretty, which indirectly has a trailing slash),
|
213
|
+
# then so will the returned template. If permalink_style has a trailing
|
214
|
+
# ":output_ext" (or is :none, :date, or :ordinal) then so will the returned
|
215
|
+
# template. Otherwise, template will be returned without modification.
|
216
|
+
#
|
217
|
+
# Examples:
|
218
|
+
# add_permalink_suffix("/:basename", :pretty)
|
219
|
+
# # => "/:basename/"
|
220
|
+
#
|
221
|
+
# add_permalink_suffix("/:basename", :date)
|
222
|
+
# # => "/:basename:output_ext"
|
223
|
+
#
|
224
|
+
# add_permalink_suffix("/:basename", "/:year/:month/:title/")
|
225
|
+
# # => "/:basename/"
|
226
|
+
#
|
227
|
+
# add_permalink_suffix("/:basename", "/:year/:month/:title")
|
228
|
+
# # => "/:basename"
|
229
|
+
#
|
230
|
+
# Returns the updated permalink template
|
231
|
+
def add_permalink_suffix(template, permalink_style)
|
232
|
+
case permalink_style
|
233
|
+
when :pretty
|
234
|
+
template << "/"
|
235
|
+
when :date, :ordinal, :none
|
236
|
+
template << ":output_ext"
|
237
|
+
else
|
238
|
+
template << "/" if permalink_style.to_s.end_with?("/")
|
239
|
+
template << ":output_ext" if permalink_style.to_s.end_with?(":output_ext")
|
240
|
+
end
|
241
|
+
template
|
242
|
+
end
|
243
|
+
|
244
|
+
# Work the same way as Dir.glob but seperating the input into two parts
|
245
|
+
# ('dir' + '/' + 'pattern') to make sure the first part('dir') does not act
|
246
|
+
# as a pattern.
|
247
|
+
#
|
248
|
+
# For example, Dir.glob("path[/*") always returns an empty array,
|
249
|
+
# because the method fails to find the closing pattern to '[' which is ']'
|
250
|
+
#
|
251
|
+
# Examples:
|
252
|
+
# safe_glob("path[", "*")
|
253
|
+
# # => ["path[/file1", "path[/file2"]
|
254
|
+
#
|
255
|
+
# safe_glob("path", "*", File::FNM_DOTMATCH)
|
256
|
+
# # => ["path/.", "path/..", "path/file1"]
|
257
|
+
#
|
258
|
+
# safe_glob("path", ["**", "*"])
|
259
|
+
# # => ["path[/file1", "path[/folder/file2"]
|
260
|
+
#
|
261
|
+
# dir - the dir where glob will be executed under
|
262
|
+
# (the dir will be included to each result)
|
263
|
+
# patterns - the patterns (or the pattern) which will be applied under the dir
|
264
|
+
# flags - the flags which will be applied to the pattern
|
265
|
+
#
|
266
|
+
# Returns matched pathes
|
267
|
+
def safe_glob(dir, patterns, flags = 0)
|
268
|
+
return [] unless Dir.exist?(dir)
|
269
|
+
pattern = File.join(Array patterns)
|
270
|
+
return [dir] if pattern.empty?
|
271
|
+
Dir.chdir(dir) do
|
272
|
+
Dir.glob(pattern, flags).map { |f| File.join(dir, f) }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Returns merged option hash for File.read of self.site (if exists)
|
277
|
+
# and a given param
|
278
|
+
def merged_file_read_opts(site, opts)
|
279
|
+
merged = (site ? site.file_read_opts : {}).merge(opts)
|
280
|
+
if merged["encoding"] && !merged["encoding"].start_with?("bom|")
|
281
|
+
merged["encoding"].insert(0, "bom|")
|
282
|
+
end
|
283
|
+
merged
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
end
|