jekyll 3.9.5 → 4.0.0.pre.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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 +75 -48
  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