jekyll 4.2.1 → 4.2.2

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