jekyll 3.4.5 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jekyll might be problematic. Click here for more details.

@@ -3,10 +3,13 @@
3
3
  module Jekyll
4
4
  class Document
5
5
  include Comparable
6
+ extend Forwardable
6
7
 
7
8
  attr_reader :path, :site, :extname, :collection
8
9
  attr_accessor :content, :output
9
10
 
11
+ def_delegator :self, :read_post_data, :post_read
12
+
10
13
  YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
11
14
  DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!
12
15
  DATE_FILENAME_MATCHER = %r!^(?:.+/)*(\d{2,4}-\d{1,2}-\d{1,2})-(.*)(\.[^.]+)$!
@@ -147,7 +150,7 @@ module Jekyll
147
150
  #
148
151
  # Returns true if extname == .coffee, false otherwise.
149
152
  def coffeescript_file?
150
- ".coffee" == extname
153
+ extname == ".coffee"
151
154
  end
152
155
 
153
156
  # Determine whether the file should be rendered with Liquid.
@@ -158,12 +161,19 @@ module Jekyll
158
161
  !(coffeescript_file? || yaml_file?)
159
162
  end
160
163
 
164
+ # Determine whether the file should be rendered with a layout.
165
+ #
166
+ # Returns true if the Front Matter specifies that `layout` is set to `none`.
167
+ def no_layout?
168
+ data["layout"] == "none"
169
+ end
170
+
161
171
  # Determine whether the file should be placed into layouts.
162
172
  #
163
- # Returns false if the document is either an asset file or a yaml file,
164
- # true otherwise.
173
+ # Returns false if the document is set to `layouts: none`, or is either an
174
+ # asset file or a yaml file. Returns true otherwise.
165
175
  def place_in_layout?
166
- !(asset_file? || yaml_file?)
176
+ !(asset_file? || yaml_file? || no_layout?)
167
177
  end
168
178
 
169
179
  # The URL template where the document would be accessible.
@@ -256,11 +266,8 @@ module Jekyll
256
266
  merge_defaults
257
267
  read_content(opts)
258
268
  read_post_data
259
- rescue SyntaxError => e
260
- Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{e.message}"
261
269
  rescue => e
262
- raise e if e.is_a? Jekyll::Errors::FatalException
263
- Jekyll.logger.error "Error:", "could not read file #{path}: #{e.message}"
270
+ handle_read_error(e)
264
271
  end
265
272
  end
266
273
  end
@@ -364,7 +371,7 @@ module Jekyll
364
371
  if data.key?(method.to_s)
365
372
  Jekyll::Deprecator.deprecation_message "Document##{method} is now a key "\
366
373
  "in the #data hash."
367
- Jekyll::Deprecator.deprecation_message "Called by #{caller.first}."
374
+ Jekyll::Deprecator.deprecation_message "Called by #{caller(0..0)}."
368
375
  data[method.to_s]
369
376
  else
370
377
  super
@@ -375,6 +382,38 @@ module Jekyll
375
382
  data.key?(method.to_s) || super
376
383
  end
377
384
 
385
+ # Add superdirectories of the special_dir to categories.
386
+ # In the case of es/_posts, 'es' is added as a category.
387
+ # In the case of _posts/es, 'es' is NOT added as a category.
388
+ #
389
+ # Returns nothing.
390
+ def categories_from_path(special_dir)
391
+ superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
392
+ .split(File::SEPARATOR)
393
+ .reject do |c|
394
+ c.empty? || c == special_dir || c == basename
395
+ end
396
+ merge_data!({ "categories" => superdirs }, :source => "file path")
397
+ end
398
+
399
+ def populate_categories
400
+ merge_data!({
401
+ "categories" => (
402
+ Array(data["categories"]) + Utils.pluralized_array_from_hash(
403
+ data,
404
+ "category",
405
+ "categories"
406
+ )
407
+ ).map(&:to_s).flatten.uniq,
408
+ })
409
+ end
410
+
411
+ def populate_tags
412
+ merge_data!({
413
+ "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten,
414
+ })
415
+ end
416
+
378
417
  private
379
418
  def merge_categories!(other)
380
419
  if other.key?("categories") && !other["categories"].nil?
@@ -422,6 +461,19 @@ module Jekyll
422
461
  generate_excerpt
423
462
  end
424
463
 
464
+ private
465
+ def handle_read_error(error)
466
+ if error.is_a? SyntaxError
467
+ Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{error.message}"
468
+ else
469
+ Jekyll.logger.error "Error:", "could not read file #{path}: #{error.message}"
470
+ end
471
+
472
+ if site.config["strict_front_matter"] || error.is_a?(Jekyll::Errors::FatalException)
473
+ raise error
474
+ end
475
+ end
476
+
425
477
  private
426
478
  def populate_title
427
479
  if relative_path =~ DATE_FILENAME_MATCHER
@@ -445,41 +497,6 @@ module Jekyll
445
497
  end
446
498
  end
447
499
 
448
- # Add superdirectories of the special_dir to categories.
449
- # In the case of es/_posts, 'es' is added as a category.
450
- # In the case of _posts/es, 'es' is NOT added as a category.
451
- #
452
- # Returns nothing.
453
- private
454
- def categories_from_path(special_dir)
455
- superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
456
- .split(File::SEPARATOR)
457
- .reject do |c|
458
- c.empty? || c == special_dir || c == basename
459
- end
460
- merge_data!({ "categories" => superdirs }, :source => "file path")
461
- end
462
-
463
- private
464
- def populate_categories
465
- merge_data!({
466
- "categories" => (
467
- Array(data["categories"]) + Utils.pluralized_array_from_hash(
468
- data,
469
- "category",
470
- "categories"
471
- )
472
- ).map(&:to_s).flatten.uniq,
473
- })
474
- end
475
-
476
- private
477
- def populate_tags
478
- merge_data!({
479
- "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten,
480
- })
481
- end
482
-
483
500
  private
484
501
  def generate_excerpt
485
502
  if generate_excerpt?
@@ -19,6 +19,10 @@ module Jekyll
19
19
  end
20
20
  end
21
21
 
22
+ def key?(key)
23
+ (@obj.collections.key?(key) && key != "posts") || super
24
+ end
25
+
22
26
  def posts
23
27
  @site_posts ||= @obj.posts.docs.sort { |a, b| b <=> a }
24
28
  end
@@ -36,7 +36,8 @@ module Jekyll
36
36
  end
37
37
 
38
38
  def included?(entry)
39
- glob_include?(site.include, entry)
39
+ glob_include?(site.include, entry) ||
40
+ glob_include?(site.include, File.basename(entry))
40
41
  end
41
42
 
42
43
  def special?(entry)
@@ -9,9 +9,10 @@ module Jekyll
9
9
  InvalidYAMLFrontMatterError = Class.new(FatalException)
10
10
  MissingDependencyException = Class.new(FatalException)
11
11
 
12
- InvalidDateError = Class.new(FatalException)
13
- InvalidPostNameError = Class.new(FatalException)
14
- PostURLError = Class.new(FatalException)
15
- InvalidURLError = Class.new(FatalException)
12
+ InvalidDateError = Class.new(FatalException)
13
+ InvalidPostNameError = Class.new(FatalException)
14
+ PostURLError = Class.new(FatalException)
15
+ InvalidURLError = Class.new(FatalException)
16
+ InvalidConfigurationError = Class.new(FatalException)
16
17
  end
17
18
  end
@@ -23,12 +23,25 @@ module Jekyll
23
23
  require name
24
24
  rescue LoadError
25
25
  Jekyll.logger.debug "Couldn't load #{name}. Skipping."
26
- yield(name) if block_given?
26
+ yield(name, version_constraint(name)) if block_given?
27
27
  false
28
28
  end
29
29
  end
30
30
  end
31
31
 
32
+ #
33
+ # The version constraint required to activate a given gem.
34
+ # Usually the gem version requirement is "> 0," because any version
35
+ # will do. In the case of jekyll-docs, however, we require the exact
36
+ # same version as Jekyll.
37
+ #
38
+ # Returns a String version constraint in a parseable form for
39
+ # RubyGems.
40
+ def version_constraint(gem_name)
41
+ return "= #{Jekyll::VERSION}" if gem_name.to_s.eql?("jekyll-docs")
42
+ "> 0"
43
+ end
44
+
32
45
  #
33
46
  # Require a gem or gems. If it's not present, show a very nice error
34
47
  # message that explains everything and is much more helpful than the
@@ -80,6 +80,7 @@ module Jekyll
80
80
  #
81
81
  # Returns the formatted String.
82
82
  def date_to_long_string(date)
83
+ return date if date.to_s.empty?
83
84
  time(date).strftime("%d %B %Y")
84
85
  end
85
86
 
@@ -94,6 +95,7 @@ module Jekyll
94
95
  #
95
96
  # Returns the formatted String.
96
97
  def date_to_xmlschema(date)
98
+ return date if date.to_s.empty?
97
99
  time(date).xmlschema
98
100
  end
99
101
 
@@ -108,6 +110,7 @@ module Jekyll
108
110
  #
109
111
  # Returns the formatted String.
110
112
  def date_to_rfc822(date)
113
+ return date if date.to_s.empty?
111
114
  time(date).rfc822
112
115
  end
113
116
 
@@ -280,10 +283,11 @@ module Jekyll
280
283
  end
281
284
  end
282
285
 
283
- def pop(array, input = 1)
286
+ def pop(array, num = 1)
284
287
  return array unless array.is_a?(Array)
288
+ num = Liquid::Utils.to_integer(num)
285
289
  new_ary = array.dup
286
- new_ary.pop(input.to_i || 1)
290
+ new_ary.pop(num)
287
291
  new_ary
288
292
  end
289
293
 
@@ -294,10 +298,11 @@ module Jekyll
294
298
  new_ary
295
299
  end
296
300
 
297
- def shift(array, input = 1)
301
+ def shift(array, num = 1)
298
302
  return array unless array.is_a?(Array)
303
+ num = Liquid::Utils.to_integer(num)
299
304
  new_ary = array.dup
300
- new_ary.shift(input.to_i || 1)
305
+ new_ary.shift(num)
301
306
  new_ary
302
307
  end
303
308
 
@@ -310,11 +315,11 @@ module Jekyll
310
315
 
311
316
  def sample(input, num = 1)
312
317
  return input unless input.respond_to?(:sample)
313
- n = num.to_i rescue 1
314
- if n == 1
318
+ num = Liquid::Utils.to_integer(num) rescue 1
319
+ if num == 1
315
320
  input.sample
316
321
  else
317
- input.sample(n)
322
+ input.sample(num)
318
323
  end
319
324
  end
320
325
 
@@ -345,25 +350,20 @@ module Jekyll
345
350
 
346
351
  private
347
352
  def time(input)
348
- case input
349
- when Time
350
- input.clone
351
- when Date
352
- input.to_time
353
- when String
354
- Time.parse(input) rescue Time.at(input.to_i)
355
- when Numeric
356
- Time.at(input)
357
- else
353
+ date = Liquid::Utils.to_date(input)
354
+ unless date.respond_to?(:to_time)
358
355
  raise Errors::InvalidDateError,
359
356
  "Invalid Date: '#{input.inspect}' is not a valid datetime."
360
- end.localtime
357
+ end
358
+ date.to_time.dup.localtime
361
359
  end
362
360
 
363
361
  private
364
362
  def item_property(item, property)
365
363
  if item.respond_to?(:to_liquid)
366
- item.to_liquid[property.to_s]
364
+ property.to_s.split(".").reduce(item.to_liquid) do |subvalue, attribute|
365
+ subvalue[attribute]
366
+ end
367
367
  elsif item.respond_to?(:data)
368
368
  item.data[property.to_s]
369
369
  else
@@ -402,9 +402,11 @@ module Jekyll
402
402
  operator = parser.consume?(:comparison)
403
403
  condition =
404
404
  if operator
405
- Liquid::Condition.new(left_expr, operator, parser.expression)
405
+ Liquid::Condition.new(Liquid::Expression.parse(left_expr),
406
+ operator,
407
+ Liquid::Expression.parse(parser.expression))
406
408
  else
407
- Liquid::Condition.new(left_expr)
409
+ Liquid::Condition.new(Liquid::Expression.parse(left_expr))
408
410
  end
409
411
  parser.consume(:end_of_string)
410
412
 
@@ -40,7 +40,7 @@ module Jekyll
40
40
 
41
41
  private
42
42
  def parse_expression(str)
43
- Liquid::Variable.new(str, {})
43
+ Liquid::Variable.new(str, Liquid::ParseContext.new)
44
44
  end
45
45
 
46
46
  private
@@ -10,7 +10,7 @@ module Jekyll
10
10
  # Returns the absolute URL as a String.
11
11
  def absolute_url(input)
12
12
  return if input.nil?
13
- site = @context.registers[:site]
13
+ return input if Addressable::URI.parse(input).absolute?
14
14
  return relative_url(input).to_s if site.config["url"].nil?
15
15
  Addressable::URI.parse(site.config["url"] + relative_url(input)).normalize.to_s
16
16
  end
@@ -22,14 +22,32 @@ module Jekyll
22
22
  # Returns a URL relative to the domain root as a String.
23
23
  def relative_url(input)
24
24
  return if input.nil?
25
- site = @context.registers[:site]
26
- parts = [site.config["baseurl"], input]
25
+ return ensure_leading_slash(input.to_s) if sanitized_baseurl.nil?
27
26
  Addressable::URI.parse(
28
- parts.compact.map { |part| ensure_leading_slash(part.to_s) }.join
27
+ ensure_leading_slash(sanitized_baseurl) + ensure_leading_slash(input.to_s)
29
28
  ).normalize.to_s
30
29
  end
31
30
 
31
+ # Strips trailing `/index.html` from URLs to create pretty permalinks
32
+ #
33
+ # input - the URL with a possible `/index.html`
34
+ #
35
+ # Returns a URL with the trailing `/index.html` removed
36
+ def strip_index(input)
37
+ return if input.nil? || input.to_s.empty?
38
+ input.sub(%r!/index\.html?$!, "/")
39
+ end
40
+
32
41
  private
42
+
43
+ def site
44
+ @context.registers[:site]
45
+ end
46
+
47
+ def sanitized_baseurl
48
+ site.config["baseurl"].to_s.chomp("/")
49
+ end
50
+
33
51
  def ensure_leading_slash(input)
34
52
  return input if input.nil? || input.empty? || input.start_with?("/")
35
53
  "/#{input}"
@@ -60,7 +60,7 @@ module Jekyll
60
60
 
61
61
  # register a single hook to be called later, internal API
62
62
  def self.register_one(owner, event, priority, &block)
63
- @registry[owner] ||={
63
+ @registry[owner] ||= {
64
64
  :post_init => [],
65
65
  :pre_render => [],
66
66
  :post_render => [],
@@ -32,7 +32,7 @@ module Jekyll
32
32
 
33
33
  row_data.each_index do |cell_index|
34
34
  str << "-" * widths[cell_index]
35
- str << "-+-" unless cell_index == row_data.length-1
35
+ str << "-+-" unless cell_index == row_data.length - 1
36
36
  end
37
37
 
38
38
  str << "\n"
@@ -49,7 +49,7 @@ module Jekyll
49
49
  cell_data.rjust(widths[cell_index], " ")
50
50
  end
51
51
 
52
- str << " | " unless cell_index == row_data.length-1
52
+ str << " | " unless cell_index == row_data.length - 1
53
53
  end
54
54
 
55
55
  str << "\n"
@@ -127,7 +127,7 @@ module Jekyll
127
127
  # layouts - The Hash of {"name" => "layout"}.
128
128
  # site_payload - The site payload Hash.
129
129
  #
130
- # Returns nothing.
130
+ # Returns String rendered page.
131
131
  def render(layouts, site_payload)
132
132
  site_payload["page"] = to_liquid
133
133
  site_payload["paginator"] = pager.to_liquid
@@ -60,7 +60,7 @@ module Jekyll
60
60
  #
61
61
  # Returns the safety Boolean.
62
62
  def self.safe(safe = nil)
63
- if !defined?(@safe) || !safe.nil?
63
+ unless defined?(@safe) && safe.nil?
64
64
  @safe = safe
65
65
  end
66
66
  @safe || false
@@ -15,6 +15,7 @@ module Jekyll
15
15
  #
16
16
  # Returns nothing
17
17
  def conscientious_require
18
+ require_theme_deps if site.theme
18
19
  require_plugin_files
19
20
  require_gems
20
21
  deprecation_checks
@@ -25,10 +26,21 @@ module Jekyll
25
26
  # Returns nothing.
26
27
  def require_gems
27
28
  Jekyll::External.require_with_graceful_fail(
28
- site.gems.select { |gem| plugin_allowed?(gem) }
29
+ site.gems.select { |plugin| plugin_allowed?(plugin) }
29
30
  )
30
31
  end
31
32
 
33
+ # Require each of the runtime_dependencies specified by the theme's gemspec.
34
+ #
35
+ # Returns false only if no dependencies have been specified, otherwise nothing.
36
+ def require_theme_deps
37
+ return false unless site.theme.runtime_dependencies
38
+ site.theme.runtime_dependencies.each do |dep|
39
+ next if dep.name == "jekyll"
40
+ External.require_with_graceful_fail(dep.name) if plugin_allowed?(dep.name)
41
+ end
42
+ end
43
+
32
44
  def self.require_from_bundler
33
45
  if !ENV["JEKYLL_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
34
46
  require "bundler"
@@ -47,12 +59,12 @@ module Jekyll
47
59
 
48
60
  # Check whether a gem plugin is allowed to be used during this build.
49
61
  #
50
- # gem_name - the name of the gem
62
+ # plugin_name - the name of the plugin
51
63
  #
52
- # Returns true if the gem name is in the whitelist or if the site is not
64
+ # Returns true if the plugin name is in the whitelist or if the site is not
53
65
  # in safe mode.
54
- def plugin_allowed?(gem_name)
55
- !site.safe || whitelist.include?(gem_name)
66
+ def plugin_allowed?(plugin_name)
67
+ !site.safe || whitelist.include?(plugin_name)
56
68
  end
57
69
 
58
70
  # Build an array of allowed plugin gem names.
@@ -87,12 +99,12 @@ module Jekyll
87
99
  end
88
100
 
89
101
  def deprecation_checks
90
- pagination_included = (site.config["gems"] || []).include?("jekyll-paginate") ||
102
+ pagination_included = (site.config["plugins"] || []).include?("jekyll-paginate") ||
91
103
  defined?(Jekyll::Paginate)
92
104
  if site.config["paginate"] && !pagination_included
93
105
  Jekyll::Deprecator.deprecation_message "You appear to have pagination " \
94
106
  "turned on, but you haven't included the `jekyll-paginate` gem. " \
95
- "Ensure you have `gems: [jekyll-paginate]` in your configuration file."
107
+ "Ensure you have `plugins: [jekyll-paginate]` in your configuration file."
96
108
  end
97
109
  end
98
110
  end