jekyll 3.2.1 → 3.3.0.pre.rc1

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.

@@ -51,19 +51,9 @@ module Jekyll
51
51
  #
52
52
  # Returns the merged data.
53
53
  def merge_data!(other, source: "YAML front matter")
54
- if other.key?("categories") && !other["categories"].nil?
55
- if other["categories"].is_a?(String)
56
- other["categories"] = other["categories"].split(" ").map(&:strip)
57
- end
58
- other["categories"] = (data["categories"] || []) | other["categories"]
59
- end
54
+ merge_categories!(other)
60
55
  Utils.deep_merge_hashes!(data, other)
61
- if data.key?("date") && !data["date"].is_a?(Time)
62
- data["date"] = Utils.parse_date(
63
- data["date"].to_s,
64
- "Document '#{relative_path}' does not have a valid date in the #{source}."
65
- )
66
- end
56
+ merge_date!(source)
67
57
  data
68
58
  end
69
59
 
@@ -90,8 +80,7 @@ module Jekyll
90
80
  # Returns a String path which represents the relative path
91
81
  # from the site source to this document
92
82
  def relative_path
93
- @relative_path ||= Pathname.new(path)
94
- .relative_path_from(Pathname.new(site.source)).to_s
83
+ @relative_path ||= Pathutil.new(path).relative_path_from(site.source).to_s
95
84
  end
96
85
 
97
86
  # The output extension of the document.
@@ -264,20 +253,9 @@ module Jekyll
264
253
  @data = SafeYAML.load_file(path)
265
254
  else
266
255
  begin
267
- defaults = @site.frontmatter_defaults.all(
268
- relative_path,
269
- collection.label.to_sym
270
- )
271
- merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
272
-
273
- self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
274
- if content =~ YAML_FRONT_MATTER_REGEXP
275
- self.content = $POSTMATCH
276
- data_file = SafeYAML.load(Regexp.last_match(1))
277
- merge_data!(data_file, :source => "YAML front matter") if data_file
278
- end
279
-
280
- post_read
256
+ merge_defaults
257
+ read_content(opts)
258
+ read_post_data
281
259
  rescue SyntaxError => e
282
260
  Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{e.message}"
283
261
  rescue => e
@@ -287,56 +265,6 @@ module Jekyll
287
265
  end
288
266
  end
289
267
 
290
- def post_read
291
- if relative_path =~ DATE_FILENAME_MATCHER
292
- date, slug, ext = Regexp.last_match.captures
293
- if !data["date"] || data["date"].to_i == site.time.to_i
294
- merge_data!({ "date" => date }, :source => "filename")
295
- end
296
- elsif relative_path =~ DATELESS_FILENAME_MATCHER
297
- slug, ext = Regexp.last_match.captures
298
- end
299
-
300
- # Try to ensure the user gets a title.
301
- data["title"] ||= Utils.titleize_slug(slug)
302
- # Only overwrite slug & ext if they aren't specified.
303
- data["slug"] ||= slug
304
- data["ext"] ||= ext
305
-
306
- populate_categories
307
- populate_tags
308
- generate_excerpt
309
- end
310
-
311
- # Add superdirectories of the special_dir to categories.
312
- # In the case of es/_posts, 'es' is added as a category.
313
- # In the case of _posts/es, 'es' is NOT added as a category.
314
- #
315
- # Returns nothing.
316
- def categories_from_path(special_dir)
317
- superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
318
- .split(File::SEPARATOR)
319
- .reject do |c|
320
- c.empty? || c.eql?(special_dir) || c.eql?(basename)
321
- end
322
- merge_data!({ "categories" => superdirs }, :source => "file path")
323
- end
324
-
325
- def populate_categories
326
- merge_data!({
327
- "categories" => (
328
- Array(data["categories"]) +
329
- Utils.pluralized_array_from_hash(data, "category", "categories")
330
- ).map(&:to_s).flatten.uniq
331
- })
332
- end
333
-
334
- def populate_tags
335
- merge_data!({
336
- "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
337
- })
338
- end
339
-
340
268
  # Create a Liquid-understandable version of this Document.
341
269
  #
342
270
  # Returns a Hash representing this Document's data.
@@ -443,7 +371,116 @@ module Jekyll
443
371
  end
444
372
  end
445
373
 
446
- private # :nodoc:
374
+ def respond_to_missing?(method, *)
375
+ data.key?(method.to_s) || super
376
+ end
377
+
378
+ private
379
+ def merge_categories!(other)
380
+ if other.key?("categories") && !other["categories"].nil?
381
+ if other["categories"].is_a?(String)
382
+ other["categories"] = other["categories"].split(%r!\s+!).map(&:strip)
383
+ end
384
+ other["categories"] = (data["categories"] || []) | other["categories"]
385
+ end
386
+ end
387
+
388
+ private
389
+ def merge_date!(source)
390
+ if data.key?("date") && !data["date"].is_a?(Time)
391
+ data["date"] = Utils.parse_date(
392
+ data["date"].to_s,
393
+ "Document '#{relative_path}' does not have a valid date in the #{source}."
394
+ )
395
+ end
396
+ end
397
+
398
+ private
399
+ def merge_defaults
400
+ defaults = @site.frontmatter_defaults.all(
401
+ relative_path,
402
+ collection.label.to_sym
403
+ )
404
+ merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
405
+ end
406
+
407
+ private
408
+ def read_content(opts)
409
+ self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
410
+ if content =~ YAML_FRONT_MATTER_REGEXP
411
+ self.content = $POSTMATCH
412
+ data_file = SafeYAML.load(Regexp.last_match(1))
413
+ merge_data!(data_file, :source => "YAML front matter") if data_file
414
+ end
415
+ end
416
+
417
+ private
418
+ def read_post_data
419
+ populate_title
420
+ populate_categories
421
+ populate_tags
422
+ generate_excerpt
423
+ end
424
+
425
+ private
426
+ def populate_title
427
+ if relative_path =~ DATE_FILENAME_MATCHER
428
+ date, slug, ext = Regexp.last_match.captures
429
+ modify_date(date)
430
+ elsif relative_path =~ DATELESS_FILENAME_MATCHER
431
+ slug, ext = Regexp.last_match.captures
432
+ end
433
+
434
+ # Try to ensure the user gets a title.
435
+ data["title"] ||= Utils.titleize_slug(slug)
436
+ # Only overwrite slug & ext if they aren't specified.
437
+ data["slug"] ||= slug
438
+ data["ext"] ||= ext
439
+ end
440
+
441
+ private
442
+ def modify_date(date)
443
+ if !data["date"] || data["date"].to_i == site.time.to_i
444
+ merge_data!({ "date" => date }, :source => "filename")
445
+ end
446
+ end
447
+
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
+ private
447
484
  def generate_excerpt
448
485
  if generate_excerpt?
449
486
  data["excerpt"] ||= Jekyll::Excerpt.new(self)
@@ -118,7 +118,7 @@ module Jekyll
118
118
  if tail.empty?
119
119
  head
120
120
  else
121
- "" << head << "\n\n" << tail.scan(%r!^\[[^\]]+\]:.+$!).join("\n")
121
+ "" << head << "\n\n" << tail.scan(%r!^ {0,3}\[[^\]]+\]:.+$!).join("\n")
122
122
  end
123
123
  end
124
124
  end
@@ -3,8 +3,11 @@ require "json"
3
3
  require "date"
4
4
  require "liquid"
5
5
 
6
+ require_all "jekyll/filters"
7
+
6
8
  module Jekyll
7
9
  module Filters
10
+ include URLFilters
8
11
  # Convert a Markdown string into HTML output.
9
12
  #
10
13
  # input - The Markdown String to convert.
@@ -0,0 +1,40 @@
1
+ require "addressable/uri"
2
+
3
+ module Jekyll
4
+ module Filters
5
+ module URLFilters
6
+ # Produces an absolute URL based on site.url and site.baseurl.
7
+ #
8
+ # input - the URL to make absolute.
9
+ #
10
+ # Returns the absolute URL as a String.
11
+ def absolute_url(input)
12
+ return if input.nil?
13
+ site = @context.registers[:site]
14
+ return relative_url(input).to_s if site.config["url"].nil?
15
+ Addressable::URI.parse(site.config["url"] + relative_url(input)).normalize.to_s
16
+ end
17
+
18
+ # Produces a URL relative to the domain root based on site.baseurl.
19
+ #
20
+ # input - the URL to make relative to the domain root
21
+ #
22
+ # Returns a URL relative to the domain root as a String.
23
+ def relative_url(input)
24
+ return if input.nil?
25
+ site = @context.registers[:site]
26
+ return ensure_leading_slash(input.to_s) if site.config["baseurl"].nil?
27
+ Addressable::URI.parse(
28
+ ensure_leading_slash(site.config["baseurl"]) + ensure_leading_slash(input.to_s)
29
+ ).normalize.to_s
30
+ end
31
+
32
+ private
33
+ def ensure_leading_slash(input)
34
+ return input if input.nil? || input.empty? || input.start_with?("/")
35
+ "/#{input}"
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -54,7 +54,7 @@ module Jekyll
54
54
 
55
55
  # Ensure the priority is a Fixnum
56
56
  def self.priority_value(priority)
57
- return priority if priority.is_a?(Fixnum)
57
+ return priority if priority.is_a?(Integer)
58
58
  PRIORITY_MAP[priority] || DEFAULT_PRIORITY
59
59
  end
60
60
 
@@ -80,7 +80,7 @@ module Jekyll
80
80
  end
81
81
 
82
82
  def self.insert_hook(owner, event, priority, &block)
83
- @hook_priority[block] = "#{priority}.#{@hook_priority.size}".to_f
83
+ @hook_priority[block] = [-priority, @hook_priority.size]
84
84
  @registry[owner][event] << block
85
85
  end
86
86
 
@@ -40,7 +40,11 @@ module Jekyll
40
40
  @base = base
41
41
  @dir = dir
42
42
  @name = name
43
- @path = site.in_source_dir(base, dir, name)
43
+ @path = if site.in_theme_dir(base) == base # we're in a theme
44
+ site.in_theme_dir(base, dir, name)
45
+ else
46
+ site.in_source_dir(base, dir, name)
47
+ end
44
48
 
45
49
  process(name)
46
50
  read_yaml(File.join(base, dir), name)
@@ -18,6 +18,7 @@ module Jekyll
18
18
  sort_files!
19
19
  @site.data = DataReader.new(site).read(site.config["data_dir"])
20
20
  CollectionReader.new(site).read
21
+ ThemeAssetsReader.new(site).read
21
22
  end
22
23
 
23
24
  # Sorts posts, pages, and static files.
@@ -0,0 +1,47 @@
1
+ module Jekyll
2
+ class ThemeAssetsReader
3
+ attr_reader :site
4
+ def initialize(site)
5
+ @site = site
6
+ end
7
+
8
+ def read
9
+ return unless site.theme && site.theme.assets_path
10
+
11
+ Find.find(site.theme.assets_path) do |path|
12
+ next if File.directory?(path)
13
+ if File.symlink?(path)
14
+ Jekyll.logger.warn "Theme reader:", "Ignored symlinked asset: #{path}"
15
+ else
16
+ read_theme_asset(path)
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+ def read_theme_asset(path)
23
+ base = site.theme.root
24
+ dir = File.dirname(path.sub("#{site.theme.root}/", ""))
25
+ name = File.basename(path)
26
+
27
+ if Utils.has_yaml_header?(path)
28
+ append_unless_exists site.pages,
29
+ Jekyll::Page.new(site, base, dir, name)
30
+ else
31
+ append_unless_exists site.static_files,
32
+ Jekyll::StaticFile.new(site, base, dir, name)
33
+ end
34
+ end
35
+
36
+ def append_unless_exists(haystack, new_item)
37
+ if haystack.any? { |file| file.relative_path == new_item.relative_path }
38
+ Jekyll.logger.debug "Theme:",
39
+ "Ignoring #{new_item.relative_path} in theme due to existing file " \
40
+ "with that path in site."
41
+ return
42
+ end
43
+
44
+ haystack << new_item
45
+ end
46
+ end
47
+ end
@@ -2,12 +2,32 @@
2
2
 
3
3
  module Jekyll
4
4
  class Renderer
5
- attr_reader :document, :site, :payload
5
+ attr_reader :document, :site
6
+ attr_writer :layouts, :payload
6
7
 
7
8
  def initialize(site, document, site_payload = nil)
8
9
  @site = site
9
10
  @document = document
10
- @payload = site_payload || site.site_payload
11
+ @payload = site_payload
12
+ end
13
+
14
+ # Fetches the payload used in Liquid rendering.
15
+ # It can be written with #payload=(new_payload)
16
+ # Falls back to site.site_payload if no payload is set.
17
+ #
18
+ # Returns a Jekyll::Drops::UnifiedPayloadDrop
19
+ def payload
20
+ @payload ||= site.site_payload
21
+ end
22
+
23
+ # The list of layouts registered for this Renderer.
24
+ # It can be written with #layouts=(new_layouts)
25
+ # Falls back to site.layouts if no layouts are registered.
26
+ #
27
+ # Returns a Hash of String => Jekyll::Layout identified
28
+ # as basename without the extension name.
29
+ def layouts
30
+ @layouts || site.layouts
11
31
  end
12
32
 
13
33
  # Determine which converters to use based on this document's
@@ -15,7 +35,7 @@ module Jekyll
15
35
  #
16
36
  # Returns an array of Converter instances.
17
37
  def converters
18
- @converters ||= site.converters.select { |c| c.matches(document.extname) }
38
+ @converters ||= site.converters.select { |c| c.matches(document.extname) }.sort
19
39
  end
20
40
 
21
41
  # Determine the extname the outputted file should have
@@ -126,7 +146,7 @@ module Jekyll
126
146
  #
127
147
  # Returns true if the layout is invalid, false if otherwise
128
148
  def invalid_layout?(layout)
129
- !document.data["layout"].nil? && layout.nil?
149
+ !document.data["layout"].nil? && layout.nil? && !(document.is_a? Jekyll::Excerpt)
130
150
  end
131
151
 
132
152
  # Render layouts and place given content inside.
@@ -137,7 +157,7 @@ module Jekyll
137
157
  # Returns the content placed in the Liquid-rendered layouts
138
158
  def place_in_layouts(content, payload, info)
139
159
  output = content.dup
140
- layout = site.layouts[document.data["layout"]]
160
+ layout = layouts[document.data["layout"]]
141
161
 
142
162
  Jekyll.logger.warn(
143
163
  "Build Warning:",
@@ -167,7 +187,7 @@ module Jekyll
167
187
  site.in_source_dir(layout.path)
168
188
  ) if document.write?
169
189
 
170
- if (layout = site.layouts[layout.data["layout"]])
190
+ if (layout = layouts[layout.data["layout"]])
171
191
  break if used.include?(layout)
172
192
  used << layout
173
193
  end