jekyll 3.10.0 → 4.0.0.pre.alpha1

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +27 -50
  3. data/LICENSE +1 -1
  4. data/README.markdown +46 -17
  5. data/lib/blank_template/_config.yml +3 -0
  6. data/lib/blank_template/_layouts/default.html +12 -0
  7. data/lib/blank_template/_sass/main.scss +9 -0
  8. data/lib/blank_template/assets/css/main.scss +4 -0
  9. data/lib/blank_template/index.md +8 -0
  10. data/lib/jekyll/cache.rb +183 -0
  11. data/lib/jekyll/cleaner.rb +2 -1
  12. data/lib/jekyll/collection.rb +78 -8
  13. data/lib/jekyll/command.rb +31 -6
  14. data/lib/jekyll/commands/build.rb +11 -20
  15. data/lib/jekyll/commands/clean.rb +2 -0
  16. data/lib/jekyll/commands/doctor.rb +15 -8
  17. data/lib/jekyll/commands/help.rb +1 -1
  18. data/lib/jekyll/commands/new.rb +37 -42
  19. data/lib/jekyll/commands/new_theme.rb +30 -28
  20. data/lib/jekyll/commands/serve/live_reload_reactor.rb +6 -10
  21. data/lib/jekyll/commands/serve/servlet.rb +15 -19
  22. data/lib/jekyll/commands/serve.rb +46 -86
  23. data/lib/jekyll/configuration.rb +26 -26
  24. data/lib/jekyll/converters/identity.rb +18 -0
  25. data/lib/jekyll/converters/markdown/kramdown_parser.rb +1 -10
  26. data/lib/jekyll/converters/markdown.rb +49 -40
  27. data/lib/jekyll/converters/smartypants.rb +34 -14
  28. data/lib/jekyll/convertible.rb +11 -13
  29. data/lib/jekyll/deprecator.rb +1 -3
  30. data/lib/jekyll/document.rb +44 -41
  31. data/lib/jekyll/drops/collection_drop.rb +2 -3
  32. data/lib/jekyll/drops/document_drop.rb +2 -1
  33. data/lib/jekyll/drops/drop.rb +3 -6
  34. data/lib/jekyll/drops/excerpt_drop.rb +4 -0
  35. data/lib/jekyll/drops/site_drop.rb +4 -13
  36. data/lib/jekyll/drops/unified_payload_drop.rb +1 -0
  37. data/lib/jekyll/drops/url_drop.rb +1 -0
  38. data/lib/jekyll/entry_filter.rb +2 -1
  39. data/lib/jekyll/excerpt.rb +45 -34
  40. data/lib/jekyll/external.rb +10 -5
  41. data/lib/jekyll/filters/date_filters.rb +6 -3
  42. data/lib/jekyll/filters/grouping_filters.rb +1 -2
  43. data/lib/jekyll/filters/url_filters.rb +6 -1
  44. data/lib/jekyll/filters.rb +72 -31
  45. data/lib/jekyll/frontmatter_defaults.rb +35 -19
  46. data/lib/jekyll/hooks.rb +2 -3
  47. data/lib/jekyll/liquid_extensions.rb +0 -2
  48. data/lib/jekyll/liquid_renderer/file.rb +14 -3
  49. data/lib/jekyll/liquid_renderer/table.rb +67 -65
  50. data/lib/jekyll/liquid_renderer.rb +13 -1
  51. data/lib/jekyll/log_adapter.rb +5 -1
  52. data/lib/jekyll/mime.types +80 -195
  53. data/lib/jekyll/page.rb +10 -26
  54. data/lib/jekyll/page_without_a_file.rb +0 -4
  55. data/lib/jekyll/plugin.rb +5 -11
  56. data/lib/jekyll/plugin_manager.rb +2 -0
  57. data/lib/jekyll/reader.rb +38 -8
  58. data/lib/jekyll/readers/data_reader.rb +5 -5
  59. data/lib/jekyll/readers/layout_reader.rb +2 -12
  60. data/lib/jekyll/readers/post_reader.rb +29 -17
  61. data/lib/jekyll/readers/static_file_reader.rb +1 -1
  62. data/lib/jekyll/readers/theme_assets_reader.rb +7 -5
  63. data/lib/jekyll/regenerator.rb +4 -12
  64. data/lib/jekyll/renderer.rb +14 -25
  65. data/lib/jekyll/site.rb +78 -34
  66. data/lib/jekyll/static_file.rb +47 -11
  67. data/lib/jekyll/stevenson.rb +7 -5
  68. data/lib/jekyll/tags/highlight.rb +22 -52
  69. data/lib/jekyll/tags/include.rb +27 -48
  70. data/lib/jekyll/tags/link.rb +11 -7
  71. data/lib/jekyll/tags/post_url.rb +17 -16
  72. data/lib/jekyll/theme.rb +12 -23
  73. data/lib/jekyll/theme_builder.rb +91 -89
  74. data/lib/jekyll/url.rb +3 -2
  75. data/lib/jekyll/utils/ansi.rb +1 -1
  76. data/lib/jekyll/utils/exec.rb +0 -1
  77. data/lib/jekyll/utils/internet.rb +2 -4
  78. data/lib/jekyll/utils/platforms.rb +8 -8
  79. data/lib/jekyll/utils/thread_event.rb +1 -5
  80. data/lib/jekyll/utils/win_tz.rb +47 -18
  81. data/lib/jekyll/utils.rb +5 -4
  82. data/lib/jekyll/version.rb +1 -1
  83. data/lib/jekyll.rb +5 -0
  84. data/lib/site_template/.gitignore +2 -0
  85. data/lib/site_template/404.html +1 -0
  86. data/lib/site_template/_config.yml +17 -5
  87. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
  88. data/lib/theme_template/gitignore.erb +1 -0
  89. data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -0
  90. metadata +70 -71
  91. data/lib/jekyll/commands/serve/mime_types_charset.json +0 -71
  92. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
  93. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
  94. data/lib/jekyll/utils/rouge.rb +0 -22
  95. /data/lib/site_template/{about.md → about.markdown} +0 -0
  96. /data/lib/site_template/{index.md → index.markdown} +0 -0
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Jekyll
4
4
  module Converters
5
+ # Markdown converter.
6
+ # For more info on converters see https://jekyllrb.com/docs/plugins/converters/
5
7
  class Markdown < Converter
6
8
  highlighter_prefix "\n"
7
9
  highlighter_suffix "\n"
@@ -9,80 +11,91 @@ module Jekyll
9
11
 
10
12
  def setup
11
13
  return if @setup ||= false
14
+
12
15
  unless (@parser = get_processor)
13
- Jekyll.logger.error "Invalid Markdown processor given:", @config["markdown"]
14
16
  if @config["safe"]
15
- Jekyll.logger.info "", "Custom processors are not loaded in safe mode"
17
+ Jekyll.logger.warn "Build Warning:", "Custom processors are not loaded in safe mode"
16
18
  end
17
- Jekyll.logger.error(
18
- "",
19
- "Available processors are: #{valid_processors.join(", ")}"
20
- )
21
- raise Errors::FatalException, "Bailing out; invalid Markdown processor."
19
+
20
+ Jekyll.logger.error "Markdown processor:",
21
+ "#{@config["markdown"].inspect} is not a valid Markdown processor."
22
+ Jekyll.logger.error "", "Available processors are: #{valid_processors.join(", ")}"
23
+ Jekyll.logger.error ""
24
+ raise Errors::FatalException, "Invalid Markdown processor given: #{@config["markdown"]}"
22
25
  end
23
26
 
27
+ @cache = Jekyll::Cache.new("Jekyll::Converters::Markdown")
24
28
  @setup = true
25
29
  end
26
30
 
27
- # Rubocop does not allow reader methods to have names starting with `get_`
31
+ # RuboCop does not allow reader methods to have names starting with `get_`
28
32
  # To ensure compatibility, this check has been disabled on this method
29
33
  #
30
34
  # rubocop:disable Naming/AccessorMethodName
31
35
  def get_processor
32
36
  case @config["markdown"].downcase
33
- when "redcarpet" then return RedcarpetParser.new(@config)
34
- when "kramdown" then return KramdownParser.new(@config)
35
- when "rdiscount" then return RDiscountParser.new(@config)
37
+ when "kramdown" then KramdownParser.new(@config)
36
38
  else
37
39
  custom_processor
38
40
  end
39
41
  end
40
42
  # rubocop:enable Naming/AccessorMethodName
41
43
 
42
- # Public: Provides you with a list of processors, the ones we
43
- # support internally and the ones that you have provided to us (if you
44
- # are not in safe mode.)
45
-
44
+ # Public: Provides you with a list of processors comprised of the ones we support internally
45
+ # and the ones that you have provided to us (if they're whitelisted for use in safe mode).
46
+ #
47
+ # Returns an array of symbols.
46
48
  def valid_processors
47
- %w(rdiscount kramdown redcarpet) + third_party_processors
49
+ [:kramdown] + third_party_processors
48
50
  end
49
51
 
50
52
  # Public: A list of processors that you provide via plugins.
51
- # This is really only available if you are not in safe mode, if you are
52
- # in safe mode (re: GitHub) then there will be none.
53
-
53
+ #
54
+ # Returns an array of symbols
54
55
  def third_party_processors
55
- self.class.constants - \
56
- %w(KramdownParser RDiscountParser RedcarpetParser PRIORITIES).map(
57
- &:to_sym
58
- )
59
- end
60
-
61
- def extname_list
62
- @extname_list ||= @config["markdown_ext"].split(",").map do |e|
63
- ".#{e.downcase}"
64
- end
56
+ self.class.constants - [:KramdownParser, :PRIORITIES]
65
57
  end
66
58
 
59
+ # Does the given extension match this converter's list of acceptable extensions?
60
+ # Takes one argument: the file's extension (including the dot).
61
+ #
62
+ # ext - The String extension to check.
63
+ #
64
+ # Returns true if it matches, false otherwise.
67
65
  def matches(ext)
68
66
  extname_list.include?(ext.downcase)
69
67
  end
70
68
 
69
+ # Public: The extension to be given to the output file (including the dot).
70
+ #
71
+ # ext - The String extension or original file.
72
+ #
73
+ # Returns The String output file extension.
71
74
  def output_ext(_ext)
72
75
  ".html"
73
76
  end
74
77
 
78
+ # Logic to do the content conversion.
79
+ #
80
+ # content - String content of file (without front matter).
81
+ #
82
+ # Returns a String of the converted content.
75
83
  def convert(content)
76
84
  setup
77
- @parser.convert(content)
85
+ @cache.getset(content) do
86
+ @parser.convert(content)
87
+ end
88
+ end
89
+
90
+ def extname_list
91
+ @extname_list ||= @config["markdown_ext"].split(",").map! { |e| ".#{e.downcase}" }
78
92
  end
79
93
 
80
94
  private
95
+
81
96
  def custom_processor
82
97
  converter_name = @config["markdown"]
83
- if custom_class_allowed?(converter_name)
84
- self.class.const_get(converter_name).new(@config)
85
- end
98
+ self.class.const_get(converter_name).new(@config) if custom_class_allowed?(converter_name)
86
99
  end
87
100
 
88
101
  # Private: Determine whether a class name is an allowed custom
@@ -90,14 +103,10 @@ module Jekyll
90
103
  #
91
104
  # parser_name - the name of the parser class
92
105
  #
93
- # Returns true if the parser name contains only alphanumeric
94
- # characters and is defined within Jekyll::Converters::Markdown
95
-
96
- private
106
+ # Returns true if the parser name contains only alphanumeric characters and is defined
107
+ # within Jekyll::Converters::Markdown
97
108
  def custom_class_allowed?(parser_name)
98
- parser_name !~ %r![^A-Za-z0-9_]! && self.class.constants.include?(
99
- parser_name.to_sym
100
- )
109
+ parser_name !~ %r![^A-Za-z0-9_]! && self.class.constants.include?(parser_name.to_sym)
101
110
  end
102
111
  end
103
112
  end
@@ -1,40 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Kramdown::Parser::SmartyPants < Kramdown::Parser::Kramdown
4
- def initialize(source, options)
5
- super
6
- @block_parsers = [:block_html, :content]
7
- @span_parsers = [:smart_quotes, :html_entity, :typographic_syms, :span_html]
8
- end
3
+ module Kramdown
4
+ module Parser
5
+ class SmartyPants < Kramdown::Parser::Kramdown
6
+ def initialize(source, options)
7
+ super
8
+ @block_parsers = [:block_html, :content]
9
+ @span_parsers = [:smart_quotes, :html_entity, :typographic_syms, :span_html]
10
+ end
9
11
 
10
- def parse_content
11
- add_text @src.scan(%r!\A.*\n!)
12
+ def parse_content
13
+ add_text @src.scan(%r!\A.*\n!)
14
+ end
15
+ define_parser(:content, %r!\A!)
16
+ end
12
17
  end
13
- define_parser(:content, %r!\A!)
14
18
  end
15
19
 
16
20
  module Jekyll
17
21
  module Converters
22
+ # SmartyPants converter.
23
+ # For more info on converters see https://jekyllrb.com/docs/plugins/converters/
18
24
  class SmartyPants < Converter
19
25
  safe true
20
26
  priority :low
21
27
 
22
28
  def initialize(config)
23
- unless defined?(Kramdown)
24
- Jekyll::External.require_with_graceful_fail "kramdown"
25
- end
29
+ Jekyll::External.require_with_graceful_fail "kramdown" unless defined?(Kramdown)
26
30
  @config = config["kramdown"].dup || {}
27
31
  @config[:input] = :SmartyPants
28
32
  end
29
33
 
30
- def matches(_)
34
+ # Does the given extension match this converter's list of acceptable extensions?
35
+ # Takes one argument: the file's extension (including the dot).
36
+ #
37
+ # ext - The String extension to check.
38
+ #
39
+ # Returns true if it matches, false otherwise.
40
+ def matches(_ext)
31
41
  false
32
42
  end
33
43
 
34
- def output_ext(_)
44
+ # Public: The extension to be given to the output file (including the dot).
45
+ #
46
+ # ext - The String extension or original file.
47
+ #
48
+ # Returns The String output file extension.
49
+ def output_ext(_ext)
35
50
  nil
36
51
  end
37
52
 
53
+ # Logic to do the content conversion.
54
+ #
55
+ # content - String content of file (without front matter).
56
+ #
57
+ # Returns a String of the converted content.
38
58
  def convert(content)
39
59
  document = Kramdown::Document.new(content, @config)
40
60
  html_output = document.to_html.chomp
@@ -39,17 +39,17 @@ module Jekyll
39
39
 
40
40
  begin
41
41
  self.content = File.read(@path || site.in_source_dir(base, name),
42
- **Utils.merged_file_read_opts(site, opts))
42
+ Utils.merged_file_read_opts(site, opts))
43
43
  if content =~ Document::YAML_FRONT_MATTER_REGEXP
44
44
  self.content = $POSTMATCH
45
45
  self.data = SafeYAML.load(Regexp.last_match(1))
46
46
  end
47
47
  rescue Psych::SyntaxError => e
48
48
  Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}"
49
- raise e if self.site.config["strict_front_matter"]
49
+ raise e if site.config["strict_front_matter"]
50
50
  rescue StandardError => e
51
51
  Jekyll.logger.warn "Error reading file #{filename}: #{e.message}"
52
- raise e if self.site.config["strict_front_matter"]
52
+ raise e if site.config["strict_front_matter"]
53
53
  end
54
54
 
55
55
  self.data ||= {}
@@ -64,12 +64,12 @@ module Jekyll
64
64
  def validate_data!(filename)
65
65
  unless self.data.is_a?(Hash)
66
66
  raise Errors::InvalidYAMLFrontMatterError,
67
- "Invalid YAML front matter in #{filename}"
67
+ "Invalid YAML front matter in #{filename}"
68
68
  end
69
69
  end
70
70
 
71
71
  def validate_permalink!(filename)
72
- if self.data["permalink"] && self.data["permalink"].to_s.empty?
72
+ if self.data["permalink"]&.to_s&.empty?
73
73
  raise Errors::InvalidPermalinkError, "Invalid permalink in #{filename}"
74
74
  end
75
75
  end
@@ -125,16 +125,12 @@ module Jekyll
125
125
  #
126
126
  # Returns the type of self.
127
127
  def type
128
- if is_a?(Page)
129
- :pages
130
- end
128
+ :pages if is_a?(Page)
131
129
  end
132
130
 
133
131
  # returns the owner symbol for hook triggering
134
132
  def hook_owner
135
- if is_a?(Page)
136
- :pages
137
- end
133
+ :pages if is_a?(Page)
138
134
  end
139
135
 
140
136
  # Determine whether the document is an asset file.
@@ -164,6 +160,8 @@ module Jekyll
164
160
  #
165
161
  # Returns true if the file has Liquid Tags or Variables, false otherwise.
166
162
  def render_with_liquid?
163
+ return false if data["render_with_liquid"] == false
164
+
167
165
  Jekyll::Utils.has_liquid_construct?(content)
168
166
  end
169
167
 
@@ -181,7 +179,7 @@ module Jekyll
181
179
  #
182
180
  # Returns true if the layout is invalid, false if otherwise
183
181
  def invalid_layout?(layout)
184
- !data["layout"].nil? && layout.nil? && !(self.is_a? Jekyll::Excerpt)
182
+ !data["layout"].nil? && layout.nil? && !(is_a? Jekyll::Excerpt)
185
183
  end
186
184
 
187
185
  # Recursively render layouts
@@ -210,7 +208,7 @@ module Jekyll
210
208
  renderer.payload = payload
211
209
  end.run
212
210
 
213
- Jekyll.logger.debug "Post-Render Hooks:", self.relative_path
211
+ Jekyll.logger.debug "Post-Render Hooks:", relative_path
214
212
  Jekyll::Hooks.trigger hook_owner, :post_render, self
215
213
  ensure
216
214
  @_renderer = nil # this will allow the modifications above to disappear
@@ -34,9 +34,7 @@ module Jekyll
34
34
  end
35
35
 
36
36
  def arg_is_present?(args, deprecated_argument, message)
37
- if args.include?(deprecated_argument)
38
- deprecation_message(message)
39
- end
37
+ deprecation_message(message) if args.include?(deprecated_argument)
40
38
  end
41
39
 
42
40
  def deprecation_message(message)
@@ -10,9 +10,9 @@ module Jekyll
10
10
 
11
11
  def_delegator :self, :read_post_data, :post_read
12
12
 
13
- YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
14
- DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!
15
- DATE_FILENAME_MATCHER = %r!^(?:.+/)*(\d{2,4}-\d{1,2}-\d{1,2})-(.*)(\.[^.]+)$!
13
+ YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze
14
+ DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!.freeze
15
+ DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze
16
16
 
17
17
  # Create a new Document.
18
18
  #
@@ -60,12 +60,19 @@ module Jekyll
60
60
  data
61
61
  end
62
62
 
63
+ # Returns the document date. If metadata is not present then calculates it
64
+ # based on Jekyll::Site#time or the document file modification time.
65
+ #
66
+ # Return document date string.
63
67
  def date
64
68
  data["date"] ||= (draft? ? source_file_mtime : site.time)
65
69
  end
66
70
 
71
+ # Return document file modification time in the form of a Time object.
72
+ #
73
+ # Return document file modification Time object.
67
74
  def source_file_mtime
68
- @source_file_mtime ||= File.mtime(path)
75
+ File.mtime(path)
69
76
  end
70
77
 
71
78
  # Returns whether the document is a draft. This is only the case if
@@ -112,15 +119,19 @@ module Jekyll
112
119
  # and with the collection's directory removed as well.
113
120
  # This method is useful when building the URL of the document.
114
121
  #
122
+ # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`)
123
+ #
115
124
  # Examples:
116
- # When relative_path is "_methods/site/generate.md":
125
+ # When relative_path is "_methods/site/generate...md":
117
126
  # cleaned_relative_path
118
127
  # # => "/site/generate"
119
128
  #
120
129
  # Returns the cleaned relative path of the document.
121
130
  def cleaned_relative_path
122
131
  @cleaned_relative_path ||=
123
- relative_path[0..-extname.length - 1].sub(collection.relative_directory, "")
132
+ relative_path[0..-extname.length - 1]
133
+ .sub(collection.relative_directory, "")
134
+ .gsub(%r!\.*\z!, "")
124
135
  end
125
136
 
126
137
  # Determine whether the document is a YAML file.
@@ -159,6 +170,8 @@ module Jekyll
159
170
  # or if the document doesn't contain any Liquid Tags or Variables,
160
171
  # true otherwise.
161
172
  def render_with_liquid?
173
+ return false if data["render_with_liquid"] == false
174
+
162
175
  !(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content))
163
176
  end
164
177
 
@@ -204,11 +217,11 @@ module Jekyll
204
217
  #
205
218
  # Returns the computed URL for the document.
206
219
  def url
207
- @url ||= URL.new({
220
+ @url ||= URL.new(
208
221
  :template => url_template,
209
222
  :placeholders => url_placeholders,
210
- :permalink => permalink,
211
- }).to_s
223
+ :permalink => permalink
224
+ ).to_s
212
225
  end
213
226
 
214
227
  def [](key)
@@ -266,7 +279,7 @@ module Jekyll
266
279
  else
267
280
  begin
268
281
  merge_defaults
269
- read_content(**opts)
282
+ read_content(opts)
270
283
  read_post_data
271
284
  rescue StandardError => e
272
285
  handle_read_error(e)
@@ -286,7 +299,7 @@ module Jekyll
286
299
  #
287
300
  # Returns the inspect string for this document.
288
301
  def inspect
289
- "#<Jekyll::Document #{relative_path} collection=#{collection.label}>"
302
+ "#<#{self.class} #{relative_path} collection=#{collection.label}>"
290
303
  end
291
304
 
292
305
  # The string representation for this document.
@@ -303,6 +316,7 @@ module Jekyll
303
316
  # equal or greater than the other doc's path. See String#<=> for more details.
304
317
  def <=>(other)
305
318
  return nil unless other.respond_to?(:data)
319
+
306
320
  cmp = data["date"] <=> other.data["date"]
307
321
  cmp = path <=> other.path if cmp.nil? || cmp.zero?
308
322
  cmp
@@ -315,7 +329,7 @@ module Jekyll
315
329
  # method returns true, and if the site's Publisher will publish the document.
316
330
  # False otherwise.
317
331
  def write?
318
- collection && collection.write? && site.publisher.publish?(self)
332
+ collection&.write? && site.publisher.publish?(self)
319
333
  end
320
334
 
321
335
  # The Document excerpt_separator, from the YAML Front-Matter or site
@@ -323,7 +337,7 @@ module Jekyll
323
337
  #
324
338
  # Returns the document excerpt_separator
325
339
  def excerpt_separator
326
- (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
340
+ @excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
327
341
  end
328
342
 
329
343
  # Whether to generate an excerpt
@@ -335,16 +349,12 @@ module Jekyll
335
349
 
336
350
  def next_doc
337
351
  pos = collection.docs.index { |post| post.equal?(self) }
338
- if pos && pos < collection.docs.length - 1
339
- collection.docs[pos + 1]
340
- end
352
+ collection.docs[pos + 1] if pos && pos < collection.docs.length - 1
341
353
  end
342
354
 
343
355
  def previous_doc
344
356
  pos = collection.docs.index { |post| post.equal?(self) }
345
- if pos && pos > 0
346
- collection.docs[pos - 1]
347
- end
357
+ collection.docs[pos - 1] if pos && pos.positive?
348
358
  end
349
359
 
350
360
  def trigger_hooks(hook_name, *args)
@@ -400,32 +410,30 @@ module Jekyll
400
410
  end
401
411
 
402
412
  def populate_categories
403
- merge_data!({
413
+ merge_data!(
404
414
  "categories" => (
405
415
  Array(data["categories"]) + Utils.pluralized_array_from_hash(
406
416
  data, "category", "categories"
407
417
  )
408
- ).map(&:to_s).flatten.uniq,
409
- })
418
+ ).map(&:to_s).flatten.uniq
419
+ )
410
420
  end
411
421
 
412
422
  def populate_tags
413
- merge_data!({
414
- "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten,
415
- })
423
+ merge_data!(
424
+ "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
425
+ )
416
426
  end
417
427
 
418
428
  private
429
+
419
430
  def merge_categories!(other)
420
431
  if other.key?("categories") && !other["categories"].nil?
421
- if other["categories"].is_a?(String)
422
- other["categories"] = other["categories"].split
423
- end
432
+ other["categories"] = other["categories"].split if other["categories"].is_a?(String)
424
433
  other["categories"] = (data["categories"] || []) | other["categories"]
425
434
  end
426
435
  end
427
436
 
428
- private
429
437
  def merge_date!(source)
430
438
  if data.key?("date")
431
439
  data["date"] = Utils.parse_date(
@@ -435,7 +443,6 @@ module Jekyll
435
443
  end
436
444
  end
437
445
 
438
- private
439
446
  def merge_defaults
440
447
  defaults = @site.frontmatter_defaults.all(
441
448
  relative_path,
@@ -444,9 +451,8 @@ module Jekyll
444
451
  merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
445
452
  end
446
453
 
447
- private
448
- def read_content(**opts)
449
- self.content = File.read(path, **Utils.merged_file_read_opts(site, opts))
454
+ def read_content(opts)
455
+ self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
450
456
  if content =~ YAML_FRONT_MATTER_REGEXP
451
457
  self.content = $POSTMATCH
452
458
  data_file = SafeYAML.load(Regexp.last_match(1))
@@ -454,7 +460,6 @@ module Jekyll
454
460
  end
455
461
  end
456
462
 
457
- private
458
463
  def read_post_data
459
464
  populate_title
460
465
  populate_categories
@@ -462,7 +467,6 @@ module Jekyll
462
467
  generate_excerpt
463
468
  end
464
469
 
465
- private
466
470
  def handle_read_error(error)
467
471
  if error.is_a? Psych::SyntaxError
468
472
  Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{error.message}"
@@ -475,7 +479,6 @@ module Jekyll
475
479
  end
476
480
  end
477
481
 
478
- private
479
482
  def populate_title
480
483
  if relative_path =~ DATE_FILENAME_MATCHER
481
484
  date, slug, ext = Regexp.last_match.captures
@@ -484,6 +487,10 @@ module Jekyll
484
487
  slug, ext = Regexp.last_match.captures
485
488
  end
486
489
 
490
+ # slugs shouldn't end with a period
491
+ # `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`)
492
+ slug.gsub!(%r!\.*\z!, "")
493
+
487
494
  # Try to ensure the user gets a title.
488
495
  data["title"] ||= Utils.titleize_slug(slug)
489
496
  # Only overwrite slug & ext if they aren't specified.
@@ -491,18 +498,14 @@ module Jekyll
491
498
  data["ext"] ||= ext
492
499
  end
493
500
 
494
- private
495
501
  def modify_date(date)
496
502
  if !data["date"] || data["date"].to_i == site.time.to_i
497
503
  merge_data!({ "date" => date }, :source => "filename")
498
504
  end
499
505
  end
500
506
 
501
- private
502
507
  def generate_excerpt
503
- if generate_excerpt?
504
- data["excerpt"] ||= Jekyll::Excerpt.new(self)
505
- end
508
+ data["excerpt"] ||= Jekyll::Excerpt.new(self) if generate_excerpt?
506
509
  end
507
510
  end
508
511
  end
@@ -7,9 +7,8 @@ module Jekyll
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegator :@obj, :write?, :output
11
- def_delegators :@obj, :label, :docs, :files, :directory,
12
- :relative_directory
10
+ def_delegator :@obj, :write?, :output
11
+ def_delegators :@obj, :label, :docs, :files, :directory, :relative_directory
13
12
 
14
13
  private def_delegator :@obj, :metadata, :fallback_data
15
14
 
@@ -12,7 +12,7 @@ module Jekyll
12
12
  mutable false
13
13
 
14
14
  def_delegator :@obj, :relative_path, :path
15
- def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
15
+ def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url, :date
16
16
 
17
17
  private def_delegator :@obj, :data, :fallback_data
18
18
 
@@ -26,6 +26,7 @@ module Jekyll
26
26
 
27
27
  def <=>(other)
28
28
  return nil unless other.is_a? DocumentDrop
29
+
29
30
  cmp = self["date"] <=> other["date"]
30
31
  cmp = self["path"] <=> other["path"] if cmp.nil? || cmp.zero?
31
32
  cmp
@@ -15,11 +15,7 @@ module Jekyll
15
15
  #
16
16
  # Returns the mutability of the class
17
17
  def self.mutable(is_mutable = nil)
18
- @is_mutable = if is_mutable
19
- is_mutable
20
- else
21
- false
22
- end
18
+ @is_mutable = is_mutable || false
23
19
  end
24
20
 
25
21
  def self.mutable?
@@ -105,6 +101,7 @@ module Jekyll
105
101
  def key?(key)
106
102
  return false if key.nil?
107
103
  return true if self.class.mutable? && @mutations.key?(key)
104
+
108
105
  respond_to?(key) || fallback_data.key?(key)
109
106
  end
110
107
 
@@ -173,7 +170,7 @@ module Jekyll
173
170
  end
174
171
 
175
172
  def merge(other, &block)
176
- self.dup.tap do |me|
173
+ dup.tap do |me|
177
174
  if block.nil?
178
175
  me.merge!(other)
179
176
  else
@@ -7,6 +7,10 @@ module Jekyll
7
7
  @obj.doc.data["layout"]
8
8
  end
9
9
 
10
+ def date
11
+ @obj.doc.date
12
+ end
13
+
10
14
  def excerpt
11
15
  nil
12
16
  end