ngage 0.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 +22 -0
- data/exe/ngage +55 -0
- data/lib/ngage.rb +3 -0
- data/lib/ngage/jekyll.rb +204 -0
- data/lib/ngage/jekyll/cleaner.rb +111 -0
- data/lib/ngage/jekyll/collection.rb +235 -0
- data/lib/ngage/jekyll/command.rb +103 -0
- data/lib/ngage/jekyll/commands/build.rb +93 -0
- data/lib/ngage/jekyll/commands/clean.rb +45 -0
- data/lib/ngage/jekyll/commands/doctor.rb +173 -0
- data/lib/ngage/jekyll/commands/help.rb +34 -0
- data/lib/ngage/jekyll/commands/new.rb +157 -0
- data/lib/ngage/jekyll/commands/new_theme.rb +42 -0
- data/lib/ngage/jekyll/commands/serve.rb +354 -0
- data/lib/ngage/jekyll/commands/serve/live_reload_reactor.rb +122 -0
- data/lib/ngage/jekyll/commands/serve/livereload_assets/livereload.js +1183 -0
- data/lib/ngage/jekyll/commands/serve/servlet.rb +203 -0
- data/lib/ngage/jekyll/commands/serve/websockets.rb +81 -0
- data/lib/ngage/jekyll/configuration.rb +391 -0
- data/lib/ngage/jekyll/converter.rb +54 -0
- data/lib/ngage/jekyll/converters/identity.rb +41 -0
- data/lib/ngage/jekyll/converters/markdown.rb +116 -0
- data/lib/ngage/jekyll/converters/markdown/kramdown_parser.rb +122 -0
- data/lib/ngage/jekyll/converters/smartypants.rb +70 -0
- data/lib/ngage/jekyll/convertible.rb +253 -0
- data/lib/ngage/jekyll/deprecator.rb +50 -0
- data/lib/ngage/jekyll/document.rb +503 -0
- data/lib/ngage/jekyll/drops/collection_drop.rb +20 -0
- data/lib/ngage/jekyll/drops/document_drop.rb +69 -0
- data/lib/ngage/jekyll/drops/drop.rb +209 -0
- data/lib/ngage/jekyll/drops/excerpt_drop.rb +15 -0
- data/lib/ngage/jekyll/drops/jekyll_drop.rb +32 -0
- data/lib/ngage/jekyll/drops/site_drop.rb +56 -0
- data/lib/ngage/jekyll/drops/static_file_drop.rb +14 -0
- data/lib/ngage/jekyll/drops/unified_payload_drop.rb +26 -0
- data/lib/ngage/jekyll/drops/url_drop.rb +89 -0
- data/lib/ngage/jekyll/entry_filter.rb +127 -0
- data/lib/ngage/jekyll/errors.rb +20 -0
- data/lib/ngage/jekyll/excerpt.rb +180 -0
- data/lib/ngage/jekyll/external.rb +76 -0
- data/lib/ngage/jekyll/filters.rb +390 -0
- data/lib/ngage/jekyll/filters/date_filters.rb +110 -0
- data/lib/ngage/jekyll/filters/grouping_filters.rb +64 -0
- data/lib/ngage/jekyll/filters/url_filters.rb +68 -0
- data/lib/ngage/jekyll/frontmatter_defaults.rb +233 -0
- data/lib/ngage/jekyll/generator.rb +5 -0
- data/lib/ngage/jekyll/hooks.rb +106 -0
- data/lib/ngage/jekyll/layout.rb +62 -0
- data/lib/ngage/jekyll/liquid_extensions.rb +22 -0
- data/lib/ngage/jekyll/liquid_renderer.rb +63 -0
- data/lib/ngage/jekyll/liquid_renderer/file.rb +56 -0
- data/lib/ngage/jekyll/liquid_renderer/table.rb +98 -0
- data/lib/ngage/jekyll/log_adapter.rb +151 -0
- data/lib/ngage/jekyll/mime.types +825 -0
- data/lib/ngage/jekyll/page.rb +185 -0
- data/lib/ngage/jekyll/page_without_a_file.rb +14 -0
- data/lib/ngage/jekyll/plugin.rb +92 -0
- data/lib/ngage/jekyll/plugin_manager.rb +115 -0
- data/lib/ngage/jekyll/publisher.rb +23 -0
- data/lib/ngage/jekyll/reader.rb +154 -0
- data/lib/ngage/jekyll/readers/collection_reader.rb +22 -0
- data/lib/ngage/jekyll/readers/data_reader.rb +75 -0
- data/lib/ngage/jekyll/readers/layout_reader.rb +70 -0
- data/lib/ngage/jekyll/readers/page_reader.rb +25 -0
- data/lib/ngage/jekyll/readers/post_reader.rb +72 -0
- data/lib/ngage/jekyll/readers/static_file_reader.rb +25 -0
- data/lib/ngage/jekyll/readers/theme_assets_reader.rb +51 -0
- data/lib/ngage/jekyll/regenerator.rb +195 -0
- data/lib/ngage/jekyll/related_posts.rb +52 -0
- data/lib/ngage/jekyll/renderer.rb +266 -0
- data/lib/ngage/jekyll/site.rb +476 -0
- data/lib/ngage/jekyll/static_file.rb +169 -0
- data/lib/ngage/jekyll/stevenson.rb +60 -0
- data/lib/ngage/jekyll/tags/highlight.rb +108 -0
- data/lib/ngage/jekyll/tags/include.rb +226 -0
- data/lib/ngage/jekyll/tags/link.rb +40 -0
- data/lib/ngage/jekyll/tags/post_url.rb +104 -0
- data/lib/ngage/jekyll/theme.rb +73 -0
- data/lib/ngage/jekyll/theme_builder.rb +121 -0
- data/lib/ngage/jekyll/url.rb +160 -0
- data/lib/ngage/jekyll/utils.rb +370 -0
- data/lib/ngage/jekyll/utils/ansi.rb +57 -0
- data/lib/ngage/jekyll/utils/exec.rb +26 -0
- data/lib/ngage/jekyll/utils/internet.rb +37 -0
- data/lib/ngage/jekyll/utils/platforms.rb +82 -0
- data/lib/ngage/jekyll/utils/thread_event.rb +31 -0
- data/lib/ngage/jekyll/utils/win_tz.rb +75 -0
- data/lib/ngage/site_template/.gitignore +5 -0
- data/lib/ngage/site_template/404.html +25 -0
- data/lib/ngage/site_template/_config.yml +47 -0
- data/lib/ngage/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -0
- data/lib/ngage/site_template/about.markdown +18 -0
- data/lib/ngage/site_template/index.markdown +6 -0
- data/lib/ngage/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
- data/lib/ngage/theme_template/Gemfile +4 -0
- data/lib/ngage/theme_template/LICENSE.txt.erb +21 -0
- data/lib/ngage/theme_template/README.md.erb +52 -0
- data/lib/ngage/theme_template/_layouts/default.html +1 -0
- data/lib/ngage/theme_template/_layouts/page.html +5 -0
- data/lib/ngage/theme_template/_layouts/post.html +5 -0
- data/lib/ngage/theme_template/example/_config.yml.erb +1 -0
- data/lib/ngage/theme_template/example/_post.md +12 -0
- data/lib/ngage/theme_template/example/index.html +14 -0
- data/lib/ngage/theme_template/example/style.scss +7 -0
- data/lib/ngage/theme_template/gitignore.erb +6 -0
- data/lib/ngage/theme_template/theme.gemspec.erb +19 -0
- data/lib/ngage/version.rb +5 -0
- metadata +328 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Utils
|
|
5
|
+
extend self
|
|
6
|
+
autoload :Ansi, "jekyll/utils/ansi"
|
|
7
|
+
autoload :Exec, "jekyll/utils/exec"
|
|
8
|
+
autoload :Internet, "jekyll/utils/internet"
|
|
9
|
+
autoload :Platforms, "jekyll/utils/platforms"
|
|
10
|
+
autoload :ThreadEvent, "jekyll/utils/thread_event"
|
|
11
|
+
autoload :WinTZ, "jekyll/utils/win_tz"
|
|
12
|
+
|
|
13
|
+
# Constants for use in #slugify
|
|
14
|
+
SLUGIFY_MODES = %w(raw default pretty ascii latin).freeze
|
|
15
|
+
SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
|
|
16
|
+
SLUGIFY_DEFAULT_REGEXP = Regexp.new("[^[:alnum:]]+").freeze
|
|
17
|
+
SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
|
|
18
|
+
SLUGIFY_ASCII_REGEXP = Regexp.new("[^[A-Za-z0-9]]+").freeze
|
|
19
|
+
|
|
20
|
+
# Takes an indented string and removes the preceding spaces on each line
|
|
21
|
+
|
|
22
|
+
def strip_heredoc(str)
|
|
23
|
+
str.gsub(%r!^[ \t]{#{(str.scan(%r!^[ \t]*(?=\S)!).min || "").size}}!, "")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Takes a slug and turns it into a simple title.
|
|
27
|
+
|
|
28
|
+
def titleize_slug(slug)
|
|
29
|
+
slug.split("-").map!(&:capitalize).join(" ")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Non-destructive version of deep_merge_hashes! See that method.
|
|
33
|
+
#
|
|
34
|
+
# Returns the merged hashes.
|
|
35
|
+
def deep_merge_hashes(master_hash, other_hash)
|
|
36
|
+
deep_merge_hashes!(master_hash.dup, other_hash)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Merges a master hash with another hash, recursively.
|
|
40
|
+
#
|
|
41
|
+
# master_hash - the "parent" hash whose values will be overridden
|
|
42
|
+
# other_hash - the other hash whose values will be persisted after the merge
|
|
43
|
+
#
|
|
44
|
+
# This code was lovingly stolen from some random gem:
|
|
45
|
+
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
|
46
|
+
#
|
|
47
|
+
# Thanks to whoever made it.
|
|
48
|
+
def deep_merge_hashes!(target, overwrite)
|
|
49
|
+
merge_values(target, overwrite)
|
|
50
|
+
merge_default_proc(target, overwrite)
|
|
51
|
+
duplicate_frozen_values(target)
|
|
52
|
+
|
|
53
|
+
target
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def mergable?(value)
|
|
57
|
+
value.is_a?(Hash) || value.is_a?(Drops::Drop)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def duplicable?(obj)
|
|
61
|
+
case obj
|
|
62
|
+
when nil, false, true, Symbol, Numeric
|
|
63
|
+
false
|
|
64
|
+
else
|
|
65
|
+
true
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Read array from the supplied hash favouring the singular key
|
|
70
|
+
# and then the plural key, and handling any nil entries.
|
|
71
|
+
#
|
|
72
|
+
# hash - the hash to read from
|
|
73
|
+
# singular_key - the singular key
|
|
74
|
+
# plural_key - the plural key
|
|
75
|
+
#
|
|
76
|
+
# Returns an array
|
|
77
|
+
def pluralized_array_from_hash(hash, singular_key, plural_key)
|
|
78
|
+
[].tap do |array|
|
|
79
|
+
value = value_from_singular_key(hash, singular_key)
|
|
80
|
+
value ||= value_from_plural_key(hash, plural_key)
|
|
81
|
+
array << value
|
|
82
|
+
end.flatten.compact
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def value_from_singular_key(hash, key)
|
|
86
|
+
hash[key] if hash.key?(key) || (hash.default_proc && hash[key])
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def value_from_plural_key(hash, key)
|
|
90
|
+
if hash.key?(key) || (hash.default_proc && hash[key])
|
|
91
|
+
val = hash[key]
|
|
92
|
+
case val
|
|
93
|
+
when String
|
|
94
|
+
val.split
|
|
95
|
+
when Array
|
|
96
|
+
val.compact
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def transform_keys(hash)
|
|
102
|
+
result = {}
|
|
103
|
+
hash.each_key do |key|
|
|
104
|
+
result[yield(key)] = hash[key]
|
|
105
|
+
end
|
|
106
|
+
result
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Apply #to_sym to all keys in the hash
|
|
110
|
+
#
|
|
111
|
+
# hash - the hash to which to apply this transformation
|
|
112
|
+
#
|
|
113
|
+
# Returns a new hash with symbolized keys
|
|
114
|
+
def symbolize_hash_keys(hash)
|
|
115
|
+
transform_keys(hash) { |key| key.to_sym rescue key }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Apply #to_s to all keys in the Hash
|
|
119
|
+
#
|
|
120
|
+
# hash - the hash to which to apply this transformation
|
|
121
|
+
#
|
|
122
|
+
# Returns a new hash with stringified keys
|
|
123
|
+
def stringify_hash_keys(hash)
|
|
124
|
+
transform_keys(hash) { |key| key.to_s rescue key }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Parse a date/time and throw an error if invalid
|
|
128
|
+
#
|
|
129
|
+
# input - the date/time to parse
|
|
130
|
+
# msg - (optional) the error message to show the user
|
|
131
|
+
#
|
|
132
|
+
# Returns the parsed date if successful, throws a FatalException
|
|
133
|
+
# if not
|
|
134
|
+
def parse_date(input, msg = "Input could not be parsed.")
|
|
135
|
+
Time.parse(input).localtime
|
|
136
|
+
rescue ArgumentError
|
|
137
|
+
raise Errors::InvalidDateError, "Invalid date '#{input}': #{msg}"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Determines whether a given file has
|
|
141
|
+
#
|
|
142
|
+
# Returns true if the YAML front matter is present.
|
|
143
|
+
# rubocop: disable PredicateName
|
|
144
|
+
def has_yaml_header?(file)
|
|
145
|
+
!!(File.open(file, "rb", &:readline) =~ %r!\A---\s*\r?\n!)
|
|
146
|
+
rescue EOFError
|
|
147
|
+
false
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Determine whether the given content string contains Liquid Tags or Vaiables
|
|
151
|
+
#
|
|
152
|
+
# Returns true is the string contains sequences of `{%` or `{{`
|
|
153
|
+
def has_liquid_construct?(content)
|
|
154
|
+
return false if content.nil? || content.empty?
|
|
155
|
+
|
|
156
|
+
content.include?("{%") || content.include?("{{")
|
|
157
|
+
end
|
|
158
|
+
# rubocop: enable PredicateName
|
|
159
|
+
|
|
160
|
+
# Slugify a filename or title.
|
|
161
|
+
#
|
|
162
|
+
# string - the filename or title to slugify
|
|
163
|
+
# mode - how string is slugified
|
|
164
|
+
# cased - whether to replace all uppercase letters with their
|
|
165
|
+
# lowercase counterparts
|
|
166
|
+
#
|
|
167
|
+
# When mode is "none", return the given string.
|
|
168
|
+
#
|
|
169
|
+
# When mode is "raw", return the given string,
|
|
170
|
+
# with every sequence of spaces characters replaced with a hyphen.
|
|
171
|
+
#
|
|
172
|
+
# When mode is "default" or nil, non-alphabetic characters are
|
|
173
|
+
# replaced with a hyphen too.
|
|
174
|
+
#
|
|
175
|
+
# When mode is "pretty", some non-alphabetic characters (._~!$&'()+,;=@)
|
|
176
|
+
# are not replaced with hyphen.
|
|
177
|
+
#
|
|
178
|
+
# When mode is "ascii", some everything else except ASCII characters
|
|
179
|
+
# a-z (lowercase), A-Z (uppercase) and 0-9 (numbers) are not replaced with hyphen.
|
|
180
|
+
#
|
|
181
|
+
# When mode is "latin", the input string is first preprocessed so that
|
|
182
|
+
# any letters with accents are replaced with the plain letter. Afterwards,
|
|
183
|
+
# it follows the "default" mode of operation.
|
|
184
|
+
#
|
|
185
|
+
# If cased is true, all uppercase letters in the result string are
|
|
186
|
+
# replaced with their lowercase counterparts.
|
|
187
|
+
#
|
|
188
|
+
# Examples:
|
|
189
|
+
# slugify("The _config.yml file")
|
|
190
|
+
# # => "the-config-yml-file"
|
|
191
|
+
#
|
|
192
|
+
# slugify("The _config.yml file", "pretty")
|
|
193
|
+
# # => "the-_config.yml-file"
|
|
194
|
+
#
|
|
195
|
+
# slugify("The _config.yml file", "pretty", true)
|
|
196
|
+
# # => "The-_config.yml file"
|
|
197
|
+
#
|
|
198
|
+
# slugify("The _config.yml file", "ascii")
|
|
199
|
+
# # => "the-config-yml-file"
|
|
200
|
+
#
|
|
201
|
+
# slugify("The _config.yml file", "latin")
|
|
202
|
+
# # => "the-config-yml-file"
|
|
203
|
+
#
|
|
204
|
+
# Returns the slugified string.
|
|
205
|
+
def slugify(string, mode: nil, cased: false)
|
|
206
|
+
mode ||= "default"
|
|
207
|
+
return nil if string.nil?
|
|
208
|
+
|
|
209
|
+
unless SLUGIFY_MODES.include?(mode)
|
|
210
|
+
return cased ? string : string.downcase
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Drop accent marks from latin characters. Everything else turns to ?
|
|
214
|
+
if mode == "latin"
|
|
215
|
+
I18n.config.available_locales = :en if I18n.config.available_locales.empty?
|
|
216
|
+
string = I18n.transliterate(string)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
slug = replace_character_sequence_with_hyphen(string, :mode => mode)
|
|
220
|
+
|
|
221
|
+
# Remove leading/trailing hyphen
|
|
222
|
+
slug.gsub!(%r!^\-|\-$!i, "")
|
|
223
|
+
|
|
224
|
+
slug.downcase! unless cased
|
|
225
|
+
slug
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Add an appropriate suffix to template so that it matches the specified
|
|
229
|
+
# permalink style.
|
|
230
|
+
#
|
|
231
|
+
# template - permalink template without trailing slash or file extension
|
|
232
|
+
# permalink_style - permalink style, either built-in or custom
|
|
233
|
+
#
|
|
234
|
+
# The returned permalink template will use the same ending style as
|
|
235
|
+
# specified in permalink_style. For example, if permalink_style contains a
|
|
236
|
+
# trailing slash (or is :pretty, which indirectly has a trailing slash),
|
|
237
|
+
# then so will the returned template. If permalink_style has a trailing
|
|
238
|
+
# ":output_ext" (or is :none, :date, or :ordinal) then so will the returned
|
|
239
|
+
# template. Otherwise, template will be returned without modification.
|
|
240
|
+
#
|
|
241
|
+
# Examples:
|
|
242
|
+
# add_permalink_suffix("/:basename", :pretty)
|
|
243
|
+
# # => "/:basename/"
|
|
244
|
+
#
|
|
245
|
+
# add_permalink_suffix("/:basename", :date)
|
|
246
|
+
# # => "/:basename:output_ext"
|
|
247
|
+
#
|
|
248
|
+
# add_permalink_suffix("/:basename", "/:year/:month/:title/")
|
|
249
|
+
# # => "/:basename/"
|
|
250
|
+
#
|
|
251
|
+
# add_permalink_suffix("/:basename", "/:year/:month/:title")
|
|
252
|
+
# # => "/:basename"
|
|
253
|
+
#
|
|
254
|
+
# Returns the updated permalink template
|
|
255
|
+
def add_permalink_suffix(template, permalink_style)
|
|
256
|
+
template = template.dup
|
|
257
|
+
|
|
258
|
+
case permalink_style
|
|
259
|
+
when :pretty
|
|
260
|
+
template << "/"
|
|
261
|
+
when :date, :ordinal, :none
|
|
262
|
+
template << ":output_ext"
|
|
263
|
+
else
|
|
264
|
+
template << "/" if permalink_style.to_s.end_with?("/")
|
|
265
|
+
template << ":output_ext" if permalink_style.to_s.end_with?(":output_ext")
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
template
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Work the same way as Dir.glob but seperating the input into two parts
|
|
272
|
+
# ('dir' + '/' + 'pattern') to make sure the first part('dir') does not act
|
|
273
|
+
# as a pattern.
|
|
274
|
+
#
|
|
275
|
+
# For example, Dir.glob("path[/*") always returns an empty array,
|
|
276
|
+
# because the method fails to find the closing pattern to '[' which is ']'
|
|
277
|
+
#
|
|
278
|
+
# Examples:
|
|
279
|
+
# safe_glob("path[", "*")
|
|
280
|
+
# # => ["path[/file1", "path[/file2"]
|
|
281
|
+
#
|
|
282
|
+
# safe_glob("path", "*", File::FNM_DOTMATCH)
|
|
283
|
+
# # => ["path/.", "path/..", "path/file1"]
|
|
284
|
+
#
|
|
285
|
+
# safe_glob("path", ["**", "*"])
|
|
286
|
+
# # => ["path[/file1", "path[/folder/file2"]
|
|
287
|
+
#
|
|
288
|
+
# dir - the dir where glob will be executed under
|
|
289
|
+
# (the dir will be included to each result)
|
|
290
|
+
# patterns - the patterns (or the pattern) which will be applied under the dir
|
|
291
|
+
# flags - the flags which will be applied to the pattern
|
|
292
|
+
#
|
|
293
|
+
# Returns matched pathes
|
|
294
|
+
def safe_glob(dir, patterns, flags = 0)
|
|
295
|
+
return [] unless Dir.exist?(dir)
|
|
296
|
+
|
|
297
|
+
pattern = File.join(Array(patterns))
|
|
298
|
+
return [dir] if pattern.empty?
|
|
299
|
+
|
|
300
|
+
Dir.chdir(dir) do
|
|
301
|
+
Dir.glob(pattern, flags).map { |f| File.join(dir, f) }
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Returns merged option hash for File.read of self.site (if exists)
|
|
306
|
+
# and a given param
|
|
307
|
+
def merged_file_read_opts(site, opts)
|
|
308
|
+
merged = (site ? site.file_read_opts : {}).merge(opts)
|
|
309
|
+
if merged[:encoding] && !merged[:encoding].start_with?("bom|")
|
|
310
|
+
merged[:encoding] = "bom|#{merged[:encoding]}"
|
|
311
|
+
end
|
|
312
|
+
if merged["encoding"] && !merged["encoding"].start_with?("bom|")
|
|
313
|
+
merged["encoding"] = "bom|#{merged["encoding"]}"
|
|
314
|
+
end
|
|
315
|
+
merged
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
private
|
|
319
|
+
|
|
320
|
+
def merge_values(target, overwrite)
|
|
321
|
+
target.merge!(overwrite) do |_key, old_val, new_val|
|
|
322
|
+
if new_val.nil?
|
|
323
|
+
old_val
|
|
324
|
+
elsif mergable?(old_val) && mergable?(new_val)
|
|
325
|
+
deep_merge_hashes(old_val, new_val)
|
|
326
|
+
else
|
|
327
|
+
new_val
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def merge_default_proc(target, overwrite)
|
|
333
|
+
if target.is_a?(Hash) && overwrite.is_a?(Hash) && target.default_proc.nil?
|
|
334
|
+
target.default_proc = overwrite.default_proc
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def duplicate_frozen_values(target)
|
|
339
|
+
target.each do |key, val|
|
|
340
|
+
target[key] = val.dup if val.frozen? && duplicable?(val)
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Replace each character sequence with a hyphen.
|
|
345
|
+
#
|
|
346
|
+
# See Utils#slugify for a description of the character sequence specified
|
|
347
|
+
# by each mode.
|
|
348
|
+
def replace_character_sequence_with_hyphen(string, mode: "default")
|
|
349
|
+
replaceable_char =
|
|
350
|
+
case mode
|
|
351
|
+
when "raw"
|
|
352
|
+
SLUGIFY_RAW_REGEXP
|
|
353
|
+
when "pretty"
|
|
354
|
+
# "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL
|
|
355
|
+
# and is allowed in both extN and NTFS.
|
|
356
|
+
SLUGIFY_PRETTY_REGEXP
|
|
357
|
+
when "ascii"
|
|
358
|
+
# For web servers not being able to handle Unicode, the safe
|
|
359
|
+
# method is to ditch anything else but latin letters and numeric
|
|
360
|
+
# digits.
|
|
361
|
+
SLUGIFY_ASCII_REGEXP
|
|
362
|
+
else
|
|
363
|
+
SLUGIFY_DEFAULT_REGEXP
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Strip according to the mode
|
|
367
|
+
string.gsub(replaceable_char, "-")
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Utils
|
|
5
|
+
module Ansi
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
ESCAPE = format("%c", 27)
|
|
9
|
+
MATCH = %r!#{ESCAPE}\[(?:\d+)(?:;\d+)*(j|k|m|s|u|A|B|G)|\e\(B\e\[m!ix
|
|
10
|
+
COLORS = {
|
|
11
|
+
:red => 31,
|
|
12
|
+
:green => 32,
|
|
13
|
+
:black => 30,
|
|
14
|
+
:magenta => 35,
|
|
15
|
+
:yellow => 33,
|
|
16
|
+
:white => 37,
|
|
17
|
+
:blue => 34,
|
|
18
|
+
:cyan => 36,
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
# Strip ANSI from the current string. It also strips cursor stuff,
|
|
22
|
+
# well some of it, and it also strips some other stuff that a lot of
|
|
23
|
+
# the other ANSI strippers don't.
|
|
24
|
+
|
|
25
|
+
def strip(str)
|
|
26
|
+
str.gsub MATCH, ""
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
|
|
31
|
+
def has?(str)
|
|
32
|
+
!!(str =~ MATCH)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Reset the color back to the default color so that you do not leak any
|
|
36
|
+
# colors when you move onto the next line. This is probably normally
|
|
37
|
+
# used as part of a wrapper so that we don't leak colors.
|
|
38
|
+
|
|
39
|
+
def reset(str = "")
|
|
40
|
+
@ansi_reset ||= format("%c[0m", 27)
|
|
41
|
+
"#{@ansi_reset}#{str}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# SEE: `self::COLORS` for a list of methods. They are mostly
|
|
45
|
+
# standard base colors supported by pretty much any xterm-color, we do
|
|
46
|
+
# not need more than the base colors so we do not include them.
|
|
47
|
+
# Actually... if I'm honest we don't even need most of the
|
|
48
|
+
# base colors.
|
|
49
|
+
|
|
50
|
+
COLORS.each do |color, num|
|
|
51
|
+
define_method color do |str|
|
|
52
|
+
"#{format("%c", 27)}[#{num}m#{str}#{reset}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module Utils
|
|
7
|
+
module Exec
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
# Runs a program in a sub-shell.
|
|
11
|
+
#
|
|
12
|
+
# *args - a list of strings containing the program name and arguments
|
|
13
|
+
#
|
|
14
|
+
# Returns a Process::Status and a String of output in an array in
|
|
15
|
+
# that order.
|
|
16
|
+
def run(*args)
|
|
17
|
+
stdin, stdout, stderr, process = Open3.popen3(*args)
|
|
18
|
+
out = stdout.read.strip
|
|
19
|
+
err = stderr.read.strip
|
|
20
|
+
|
|
21
|
+
[stdin, stdout, stderr].each(&:close)
|
|
22
|
+
[process.value, out + err]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|