jekyll 4.2.2 → 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +150 -26
  3. data/README.markdown +16 -19
  4. data/lib/jekyll/cache.rb +3 -7
  5. data/lib/jekyll/cleaner.rb +1 -1
  6. data/lib/jekyll/collection.rb +1 -0
  7. data/lib/jekyll/commands/build.rb +2 -13
  8. data/lib/jekyll/commands/clean.rb +1 -2
  9. data/lib/jekyll/commands/doctor.rb +13 -13
  10. data/lib/jekyll/commands/new.rb +5 -9
  11. data/lib/jekyll/commands/new_theme.rb +3 -4
  12. data/lib/jekyll/commands/serve/live_reload_reactor.rb +3 -6
  13. data/lib/jekyll/commands/serve/mime_types_charset.json +71 -0
  14. data/lib/jekyll/commands/serve/servlet.rb +13 -9
  15. data/lib/jekyll/commands/serve.rb +23 -18
  16. data/lib/jekyll/configuration.rb +2 -2
  17. data/lib/jekyll/converters/markdown/kramdown_parser.rb +13 -15
  18. data/lib/jekyll/deprecator.rb +1 -1
  19. data/lib/jekyll/document.rb +2 -3
  20. data/lib/jekyll/drops/document_drop.rb +4 -0
  21. data/lib/jekyll/drops/excerpt_drop.rb +4 -0
  22. data/lib/jekyll/drops/theme_drop.rb +36 -0
  23. data/lib/jekyll/drops/unified_payload_drop.rb +6 -2
  24. data/lib/jekyll/entry_filter.rb +2 -6
  25. data/lib/jekyll/excerpt.rb +5 -6
  26. data/lib/jekyll/external.rb +17 -21
  27. data/lib/jekyll/filters.rb +11 -14
  28. data/lib/jekyll/frontmatter_defaults.rb +2 -4
  29. data/lib/jekyll/hooks.rb +2 -2
  30. data/lib/jekyll/layout.rb +8 -20
  31. data/lib/jekyll/mime.types +146 -73
  32. data/lib/jekyll/page.rb +2 -4
  33. data/lib/jekyll/path_manager.rb +7 -7
  34. data/lib/jekyll/plugin_manager.rb +12 -4
  35. data/lib/jekyll/profiler.rb +0 -3
  36. data/lib/jekyll/reader.rb +18 -1
  37. data/lib/jekyll/readers/data_reader.rb +45 -11
  38. data/lib/jekyll/renderer.rb +8 -10
  39. data/lib/jekyll/site.rb +52 -21
  40. data/lib/jekyll/static_file.rb +6 -9
  41. data/lib/jekyll/tags/highlight.rb +13 -9
  42. data/lib/jekyll/tags/include.rb +4 -4
  43. data/lib/jekyll/tags/post_url.rb +5 -5
  44. data/lib/jekyll/theme.rb +6 -2
  45. data/lib/jekyll/theme_builder.rb +1 -1
  46. data/lib/jekyll/url.rb +1 -1
  47. data/lib/jekyll/utils/ansi.rb +1 -1
  48. data/lib/jekyll/utils/win_tz.rb +18 -47
  49. data/lib/jekyll/utils.rb +10 -6
  50. data/lib/jekyll/version.rb +1 -1
  51. data/lib/jekyll.rb +1 -1
  52. data/lib/site_template/_config.yml +1 -1
  53. data/lib/theme_template/README.md.erb +1 -3
  54. metadata +59 -13
data/lib/jekyll/page.rb CHANGED
@@ -5,9 +5,7 @@ module Jekyll
5
5
  include Convertible
6
6
 
7
7
  attr_writer :dir
8
- attr_accessor :site, :pager
9
- attr_accessor :name, :ext, :basename
10
- attr_accessor :data, :content, :output
8
+ attr_accessor :basename, :content, :data, :ext, :name, :output, :pager, :site
11
9
 
12
10
  alias_method :extname, :ext
13
11
 
@@ -144,7 +142,7 @@ module Jekyll
144
142
 
145
143
  # The path to the page source file, relative to the site source
146
144
  def relative_path
147
- @relative_path ||= PathManager.join(@dir, @name).sub(%r!\A/!, "")
145
+ @relative_path ||= PathManager.join(@dir, @name).delete_prefix("/")
148
146
  end
149
147
 
150
148
  # Obtain destination path.
@@ -36,13 +36,13 @@ module Jekyll
36
36
  def sanitized_path(base_directory, questionable_path)
37
37
  @sanitized_path ||= {}
38
38
  @sanitized_path[base_directory] ||= {}
39
- @sanitized_path[base_directory][questionable_path] ||= begin
40
- if questionable_path.nil?
41
- base_directory.freeze
42
- else
43
- sanitize_and_join(base_directory, questionable_path).freeze
44
- end
45
- end
39
+ @sanitized_path[base_directory][questionable_path] ||= if questionable_path.nil?
40
+ base_directory.freeze
41
+ else
42
+ sanitize_and_join(
43
+ base_directory, questionable_path
44
+ ).freeze
45
+ end
46
46
  end
47
47
 
48
48
  private
@@ -46,7 +46,7 @@ module Jekyll
46
46
  end
47
47
 
48
48
  def self.require_from_bundler
49
- if !ENV["JEKYLL_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
49
+ if !ENV["JEKYLL_NO_BUNDLER_REQUIRE"] && gemfile_exists?
50
50
  require "bundler"
51
51
 
52
52
  Bundler.setup
@@ -61,6 +61,13 @@ module Jekyll
61
61
  end
62
62
  end
63
63
 
64
+ # Check for the existence of a Gemfile.
65
+ #
66
+ # Returns true if a Gemfile exists in the places bundler will look
67
+ def self.gemfile_exists?
68
+ File.file?("Gemfile") || (ENV["BUNDLE_GEMFILE"] && File.file?(ENV["BUNDLE_GEMFILE"]))
69
+ end
70
+
64
71
  # Check whether a gem plugin is allowed to be used during this build.
65
72
  #
66
73
  # plugin_name - the name of the plugin
@@ -106,9 +113,10 @@ module Jekyll
106
113
  pagination_included = (site.config["plugins"] || []).include?("jekyll-paginate") ||
107
114
  defined?(Jekyll::Paginate)
108
115
  if site.config["paginate"] && !pagination_included
109
- Jekyll::Deprecator.deprecation_message "You appear to have pagination " \
110
- "turned on, but you haven't included the `jekyll-paginate` gem. " \
111
- "Ensure you have `plugins: [jekyll-paginate]` in your configuration file."
116
+ Jekyll::Deprecator.deprecation_message <<~MSG
117
+ You appear to have pagination turned on, but you haven't included the `jekyll-paginate`
118
+ gem. Ensure you have `plugins: [jekyll-paginate]` in your configuration file.
119
+ MSG
112
120
  end
113
121
  end
114
122
  end
@@ -14,15 +14,12 @@ module Jekyll
14
14
 
15
15
  rows = table_rows.dup
16
16
  header = rows.shift
17
- footer = rows.pop
18
17
  output = +"\n"
19
18
 
20
19
  table = Terminal::Table.new do |t|
21
20
  t << header
22
21
  t << :separator
23
22
  rows.each { |row| t << row }
24
- t << :separator
25
- t << footer
26
23
  t.style = TERMINAL_TABLE_STYLES
27
24
  t.align_column(0, :left)
28
25
  end
data/lib/jekyll/reader.rb CHANGED
@@ -16,9 +16,26 @@ module Jekyll
16
16
  read_directories
17
17
  read_included_excludes
18
18
  sort_files!
19
- @site.data = DataReader.new(site).read(site.config["data_dir"])
20
19
  CollectionReader.new(site).read
21
20
  ThemeAssetsReader.new(site).read
21
+ read_data
22
+ end
23
+
24
+ # Read and merge the data files.
25
+ # If a theme is specified and it contains data, it will be read.
26
+ # Site data will overwrite theme data with the same key using the
27
+ # semantics of Utils.deep_merge_hashes.
28
+ #
29
+ # Returns nothing.
30
+ def read_data
31
+ @site.data = DataReader.new(site).read(site.config["data_dir"])
32
+ return unless site.theme&.data_path
33
+
34
+ theme_data = DataReader.new(
35
+ site,
36
+ :in_source_dir => site.method(:in_theme_dir)
37
+ ).read(site.theme.data_path)
38
+ @site.data = Jekyll::Utils.deep_merge_hashes(theme_data, @site.data)
22
39
  end
23
40
 
24
41
  # Sorts posts, pages, and static files.
@@ -4,11 +4,12 @@ module Jekyll
4
4
  class DataReader
5
5
  attr_reader :site, :content
6
6
 
7
- def initialize(site)
7
+ def initialize(site, in_source_dir: nil)
8
8
  @site = site
9
9
  @content = {}
10
10
  @entry_filter = EntryFilter.new(site)
11
- @source_dir = site.in_source_dir("/")
11
+ @in_source_dir = in_source_dir || @site.method(:in_source_dir)
12
+ @source_dir = @in_source_dir.call("/")
12
13
  end
13
14
 
14
15
  # Read all the files in <dir> and adds them to @content
@@ -18,7 +19,7 @@ module Jekyll
18
19
  # Returns @content, a Hash of the .yaml, .yml,
19
20
  # .json, and .csv files in the base directory
20
21
  def read(dir)
21
- base = site.in_source_dir(dir)
22
+ base = @in_source_dir.call(dir)
22
23
  read_data_to(base, @content)
23
24
  @content
24
25
  end
@@ -38,7 +39,7 @@ module Jekyll
38
39
  end
39
40
 
40
41
  entries.each do |entry|
41
- path = @site.in_source_dir(dir, entry)
42
+ path = @in_source_dir.call(dir, entry)
42
43
  next if @entry_filter.symlink?(path)
43
44
 
44
45
  if File.directory?(path)
@@ -58,14 +59,9 @@ module Jekyll
58
59
 
59
60
  case File.extname(path).downcase
60
61
  when ".csv"
61
- CSV.read(path,
62
- :headers => true,
63
- :encoding => site.config["encoding"]).map(&:to_hash)
62
+ CSV.read(path, **csv_config).map { |row| convert_row(row) }
64
63
  when ".tsv"
65
- CSV.read(path,
66
- :col_sep => "\t",
67
- :headers => true,
68
- :encoding => site.config["encoding"]).map(&:to_hash)
64
+ CSV.read(path, **tsv_config).map { |row| convert_row(row) }
69
65
  else
70
66
  SafeYAML.load_file(path)
71
67
  end
@@ -75,5 +71,43 @@ module Jekyll
75
71
  name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "")
76
72
  .gsub(%r!\s+!, "_")
77
73
  end
74
+
75
+ private
76
+
77
+ # @return [Hash]
78
+ def csv_config
79
+ @csv_config ||= read_config("csv_reader")
80
+ end
81
+
82
+ # @return [Hash]
83
+ def tsv_config
84
+ @tsv_config ||= read_config("tsv_reader", { :col_sep => "\t" })
85
+ end
86
+
87
+ # @param config_key [String]
88
+ # @param overrides [Hash]
89
+ # @return [Hash]
90
+ # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/csv/rdoc/CSV.html#Converters
91
+ def read_config(config_key, overrides = {})
92
+ reader_config = config[config_key] || {}
93
+
94
+ defaults = {
95
+ :converters => reader_config.fetch("csv_converters", []).map(&:to_sym),
96
+ :headers => reader_config.fetch("headers", true),
97
+ :encoding => reader_config.fetch("encoding", config["encoding"]),
98
+ }
99
+
100
+ defaults.merge(overrides)
101
+ end
102
+
103
+ def config
104
+ @config ||= site.config
105
+ end
106
+
107
+ # @param row [Array, CSV::Row]
108
+ # @return [Array, Hash]
109
+ def convert_row(row)
110
+ row.instance_of?(CSV::Row) ? row.to_hash : row
111
+ end
78
112
  end
79
113
  end
@@ -102,15 +102,13 @@ module Jekyll
102
102
  # Returns String the converted content.
103
103
  def convert(content)
104
104
  converters.reduce(content) do |output, converter|
105
- begin
106
- converter.convert output
107
- rescue StandardError => e
108
- Jekyll.logger.error "Conversion error:",
109
- "#{converter.class} encountered an error while "\
110
- "converting '#{document.relative_path}':"
111
- Jekyll.logger.error("", e.to_s)
112
- raise e
113
- end
105
+ converter.convert output
106
+ rescue StandardError => e
107
+ Jekyll.logger.error "Conversion error:",
108
+ "#{converter.class} encountered an error while " \
109
+ "converting '#{document.relative_path}':"
110
+ Jekyll.logger.error("", e.to_s)
111
+ raise e
114
112
  end
115
113
  end
116
114
 
@@ -181,7 +179,7 @@ module Jekyll
181
179
  return unless invalid_layout?(layout)
182
180
 
183
181
  Jekyll.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
184
- "in #{document.relative_path} does not exist."
182
+ "in #{document.relative_path} does not exist."
185
183
  end
186
184
 
187
185
  # Render layout content into document.output
data/lib/jekyll/site.rb CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  module Jekyll
4
4
  class Site
5
- attr_reader :source, :dest, :cache_dir, :config
6
- attr_accessor :layouts, :pages, :static_files, :drafts, :inclusions,
7
- :exclude, :include, :lsi, :highlighter, :permalink_style,
8
- :time, :future, :unpublished, :safe, :plugins, :limit_posts,
9
- :show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
10
- :gems, :plugin_manager, :theme
5
+ attr_accessor :baseurl, :converters, :data, :drafts, :exclude,
6
+ :file_read_opts, :future, :gems, :generators, :highlighter,
7
+ :include, :inclusions, :keep_files, :layouts, :limit_posts,
8
+ :lsi, :pages, :permalink_style, :plugin_manager, :plugins,
9
+ :reader, :safe, :show_drafts, :static_files, :theme, :time,
10
+ :unpublished
11
11
 
12
- attr_accessor :converters, :generators, :reader
13
- attr_reader :regenerator, :liquid_renderer, :includes_load_paths, :filter_cache, :profiler
12
+ attr_reader :cache_dir, :config, :dest, :filter_cache, :includes_load_paths,
13
+ :liquid_renderer, :profiler, :regenerator, :source
14
14
 
15
15
  # Public: Initialize a new Site.
16
16
  #
@@ -304,10 +304,10 @@ module Jekyll
304
304
  # klass - The Class of the Converter to fetch.
305
305
  def find_converter_instance(klass)
306
306
  @find_converter_instance ||= {}
307
- @find_converter_instance[klass] ||= begin
308
- converters.find { |converter| converter.instance_of?(klass) } || \
309
- raise("No Converters found for #{klass}")
310
- end
307
+ @find_converter_instance[klass] ||= converters.find do |converter|
308
+ converter.instance_of?(klass)
309
+ end || \
310
+ raise("No Converters found for #{klass}")
311
311
  end
312
312
 
313
313
  # klass - class or module containing the subclasses.
@@ -328,11 +328,11 @@ module Jekyll
328
328
  # Returns
329
329
  def relative_permalinks_are_deprecated
330
330
  if config["relative_permalinks"]
331
- Jekyll.logger.abort_with "Since v3.0, permalinks for pages" \
332
- " in subfolders must be relative to the" \
333
- " site source directory, not the parent" \
334
- " directory. Check https://jekyllrb.com/docs/upgrading/"\
335
- " for more info."
331
+ Jekyll.logger.abort_with "Since v3.0, permalinks for pages " \
332
+ "in subfolders must be relative to the " \
333
+ "site source directory, not the parent " \
334
+ "directory. Check https://jekyllrb.com/docs/upgrading/ " \
335
+ "for more info."
336
336
  end
337
337
  end
338
338
 
@@ -343,6 +343,13 @@ module Jekyll
343
343
  documents.select(&:write?)
344
344
  end
345
345
 
346
+ # Get the to be written static files
347
+ #
348
+ # Returns an Array of StaticFiles which should be written
349
+ def static_files_to_write
350
+ static_files.select(&:write?)
351
+ end
352
+
346
353
  # Get all the documents
347
354
  #
348
355
  # Returns an Array of all Documents
@@ -353,9 +360,13 @@ module Jekyll
353
360
  end
354
361
 
355
362
  def each_site_file
356
- %w(pages static_files docs_to_write).each do |type|
363
+ seen_files = []
364
+ %w(pages static_files_to_write docs_to_write).each do |type|
357
365
  send(type).each do |item|
366
+ next if seen_files.include?(item)
367
+
358
368
  yield item
369
+ seen_files << item
359
370
  end
360
371
  end
361
372
  end
@@ -469,7 +480,11 @@ module Jekyll
469
480
  theme_config.delete_if { |key, _| Configuration::DEFAULTS.key?(key) }
470
481
 
471
482
  # Override theme_config with existing config and return the result.
483
+ # Additionally ensure we return a `Jekyll::Configuration` instance instead of a Hash.
472
484
  Utils.deep_merge_hashes(theme_config, config)
485
+ .each_with_object(Jekyll::Configuration.new) do |(key, value), conf|
486
+ conf[key] = value
487
+ end
473
488
  end
474
489
 
475
490
  # Limits the current posts; removes the posts which exceed the limit_posts
@@ -490,10 +505,26 @@ module Jekyll
490
505
  @site_cleaner ||= Cleaner.new(self)
491
506
  end
492
507
 
508
+ def hide_cache_dir_from_git
509
+ @cache_gitignore_path ||= in_source_dir(config["cache_dir"], ".gitignore")
510
+ return if File.exist?(@cache_gitignore_path)
511
+
512
+ cache_dir_path = in_source_dir(config["cache_dir"])
513
+ FileUtils.mkdir_p(cache_dir_path) unless File.directory?(cache_dir_path)
514
+
515
+ File.open(@cache_gitignore_path, "wb") do |file|
516
+ file.puts("# ignore everything in this directory\n*")
517
+ end
518
+ end
519
+
493
520
  # Disable Marshaling cache to disk in Safe Mode
494
521
  def configure_cache
495
522
  Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
496
- Jekyll::Cache.disable_disk_cache! if safe || config["disable_disk_cache"]
523
+ if safe || config["disable_disk_cache"]
524
+ Jekyll::Cache.disable_disk_cache!
525
+ else
526
+ hide_cache_dir_from_git
527
+ end
497
528
  end
498
529
 
499
530
  def configure_plugins
@@ -509,8 +540,8 @@ module Jekyll
509
540
  if config["theme"].is_a?(String)
510
541
  Jekyll::Theme.new(config["theme"])
511
542
  else
512
- Jekyll.logger.warn "Theme:", "value of 'theme' in config should be " \
513
- "String to use gem-based themes, but got #{config["theme"].class}"
543
+ Jekyll.logger.warn "Theme:", "value of 'theme' in config should be String to use " \
544
+ "gem-based themes, but got #{config["theme"].class}"
514
545
  nil
515
546
  end
516
547
  end
@@ -39,14 +39,11 @@ module Jekyll
39
39
 
40
40
  # Returns source file path.
41
41
  def path
42
- @path ||= begin
43
- # Static file is from a collection inside custom collections directory
44
- if !@collection.nil? && !@site.config["collections_dir"].empty?
45
- File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
46
- else
47
- File.join(*[@base, @dir, @name].compact)
48
- end
49
- end
42
+ @path ||= if !@collection.nil? && !@site.config["collections_dir"].empty?
43
+ File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
44
+ else
45
+ File.join(*[@base, @dir, @name].compact)
46
+ end
50
47
  end
51
48
 
52
49
  # Obtain destination path.
@@ -159,7 +156,7 @@ module Jekyll
159
156
 
160
157
  # Applies a similar URL-building technique as Jekyll::Document that takes
161
158
  # the collection's URL template into account. The default URL template can
162
- # be overriden in the collection's configuration in _config.yml.
159
+ # be overridden in the collection's configuration in _config.yml.
163
160
  def url
164
161
  @url ||= begin
165
162
  base = if @collection.nil?
@@ -80,13 +80,17 @@ module Jekyll
80
80
 
81
81
  def render_rouge(code)
82
82
  require "rouge"
83
- formatter = ::Rouge::Formatters::HTMLLegacy.new(
84
- :line_numbers => @highlight_options[:linenos],
85
- :wrap => false,
86
- :css_class => "highlight",
87
- :gutter_class => "gutter",
88
- :code_class => "code"
89
- )
83
+ formatter = ::Rouge::Formatters::HTML.new
84
+ if @highlight_options[:linenos]
85
+ formatter = ::Rouge::Formatters::HTMLTable.new(
86
+ formatter,
87
+ {
88
+ :css_class => "highlight",
89
+ :gutter_class => "gutter",
90
+ :code_class => "code",
91
+ }
92
+ )
93
+ end
90
94
  lexer = ::Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
91
95
  formatter.format(lexer.lex(code))
92
96
  end
@@ -100,8 +104,8 @@ module Jekyll
100
104
  "class=\"language-#{@lang.to_s.tr("+", "-")}\"",
101
105
  "data-lang=\"#{@lang}\"",
102
106
  ].join(" ")
103
- "<figure class=\"highlight\"><pre><code #{code_attributes}>"\
104
- "#{code.chomp}</code></pre></figure>"
107
+ "<figure class=\"highlight\"><pre><code #{code_attributes}>" \
108
+ "#{code.chomp}</code></pre></figure>"
105
109
  end
106
110
  end
107
111
  end
@@ -13,7 +13,7 @@ module Jekyll
13
13
  !mx.freeze
14
14
 
15
15
  FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!.freeze
16
- VALID_FILENAME_CHARS = %r!^[\w/.-]+$!.freeze
16
+ VALID_FILENAME_CHARS = %r!^[\w/.\-()+~\#@]+$!.freeze
17
17
  INVALID_SEQUENCES = %r![./]{2,}!.freeze
18
18
 
19
19
  def initialize(tag_name, markup, tokens)
@@ -179,8 +179,8 @@ module Jekyll
179
179
  private
180
180
 
181
181
  def could_not_locate_message(file, includes_dirs, safe)
182
- message = "Could not locate the included file '#{file}' in any of "\
183
- "#{includes_dirs}. Ensure it exists in one of those directories and"
182
+ message = "Could not locate the included file '#{file}' in any of #{includes_dirs}. " \
183
+ "Ensure it exists in one of those directories and"
184
184
  message + if safe
185
185
  " is not a symlink as those are not allowed in safe mode."
186
186
  else
@@ -265,7 +265,7 @@ module Jekyll
265
265
  def resource_path(page, site)
266
266
  path = page["path"]
267
267
  path = File.join(site.config["collections_dir"], path) if page["collection"]
268
- path.sub(%r!/#excerpt\z!, "")
268
+ path.delete_suffix("/#excerpt")
269
269
  end
270
270
  end
271
271
  end
@@ -86,11 +86,11 @@ module Jekyll
86
86
  site.posts.docs.each do |document|
87
87
  next unless @post.deprecated_equality document
88
88
 
89
- Jekyll::Deprecator.deprecation_message "A call to "\
90
- "'{% post_url #{@post.name} %}' did not match " \
91
- "a post using the new matching method of checking name " \
92
- "(path-date-slug) equality. Please make sure that you " \
93
- "change this tag to match the post's name exactly."
89
+ Jekyll::Deprecator.deprecation_message(
90
+ "A call to '{% post_url #{@post.name} %}' did not match a post using the new " \
91
+ "matching method of checking name (path-date-slug) equality. Please make sure " \
92
+ "that you change this tag to match the post's name exactly."
93
+ )
94
94
  return relative_url(document)
95
95
  end
96
96
 
data/lib/jekyll/theme.rb CHANGED
@@ -18,8 +18,8 @@ module Jekyll
18
18
  # Otherwise, Jekyll.sanitized path with prepend the unresolved root
19
19
  @root ||= File.realpath(gemspec.full_gem_path)
20
20
  rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
21
- raise "Path #{gemspec.full_gem_path} does not exist, is not accessible "\
22
- "or includes a symbolic link loop"
21
+ raise "Path #{gemspec.full_gem_path} does not exist, is not accessible or includes " \
22
+ "a symbolic link loop"
23
23
  end
24
24
 
25
25
  # The name of theme directory
@@ -43,6 +43,10 @@ module Jekyll
43
43
  @assets_path ||= path_for "assets"
44
44
  end
45
45
 
46
+ def data_path
47
+ @data_path ||= path_for "_data"
48
+ end
49
+
46
50
  def runtime_dependencies
47
51
  gemspec.runtime_dependencies
48
52
  end
@@ -3,7 +3,7 @@
3
3
  module Jekyll
4
4
  class ThemeBuilder
5
5
  SCAFFOLD_DIRECTORIES = %w(
6
- assets _layouts _includes _sass
6
+ assets _data _layouts _includes _sass
7
7
  ).freeze
8
8
 
9
9
  attr_reader :name, :path, :code_of_conduct
data/lib/jekyll/url.rb CHANGED
@@ -100,7 +100,7 @@ module Jekyll
100
100
  winner = pool.find { |key| @placeholders.key?(key) }
101
101
  if winner.nil?
102
102
  raise NoMethodError,
103
- "The URL template doesn't have #{pool.join(" or ")} keys. "\
103
+ "The URL template doesn't have #{pool.join(" or ")} keys. " \
104
104
  "Check your permalink template!"
105
105
  end
106
106
 
@@ -1,4 +1,4 @@
1
- # Frozen-string-literal: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jekyll
4
4
  module Utils
@@ -11,64 +11,35 @@ module Jekyll
11
11
  # timezone - the IANA Time Zone specified in "_config.yml"
12
12
  #
13
13
  # Returns a string that ultimately re-defines ENV["TZ"] in Windows
14
- def calculate(timezone)
14
+ def calculate(timezone, now = Time.now)
15
15
  External.require_with_graceful_fail("tzinfo") unless defined?(TZInfo)
16
16
  tz = TZInfo::Timezone.get(timezone)
17
- difference = Time.now.to_i - tz.now.to_i
17
+
18
+ #
19
+ # Use period_for_utc and utc_total_offset instead of
20
+ # period_for and observed_utc_offset for compatibility with tzinfo v1.
21
+ offset = tz.period_for_utc(now.getutc).utc_total_offset
22
+
18
23
  #
19
24
  # POSIX style definition reverses the offset sign.
20
25
  # e.g. Eastern Standard Time (EST) that is 5Hrs. to the 'west' of Prime Meridian
21
26
  # is denoted as:
22
27
  # EST+5 (or) EST+05:00
23
- # Reference: http://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
24
- sign = difference.negative? ? "-" : "+"
25
- offset = sign == "-" ? "+" : "-" unless difference.zero?
26
- #
27
- # convert the difference (in seconds) to hours, as a rational number, and perform
28
- # a modulo operation on it.
29
- modulo = modulo_of(rational_hour(difference))
28
+ # Reference: https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
29
+ sign = offset.positive? ? "-" : "+"
30
+
31
+ rational_hours = offset.abs.to_r / 3600
32
+ hours = rational_hours.to_i
33
+ minutes = ((rational_hours - hours) * 60).to_i
34
+
30
35
  #
31
- # Format the hour as a two-digit number.
32
- # Establish the minutes based on modulo expression.
33
- hh = format("%<hour>02d", :hour => absolute_hour(difference).ceil)
34
- mm = modulo.zero? ? "00" : "30"
36
+ # Format the hours and minutes as two-digit numbers.
37
+ time = format("%<hours>02d:%<minutes>02d", :hours => hours, :minutes => minutes)
35
38
 
36
- Jekyll.logger.debug "Timezone:", "#{timezone} #{offset}#{hh}:#{mm}"
39
+ Jekyll.logger.debug "Timezone:", "#{timezone} #{sign}#{time}"
37
40
  #
38
41
  # Note: The 3-letter-word below doesn't have a particular significance.
39
- "WTZ#{sign}#{hh}:#{mm}"
40
- end
41
-
42
- private
43
-
44
- # Private: Convert given seconds to an hour as a rational number.
45
- #
46
- # seconds - supplied as an integer, it is converted to a rational number.
47
- # 3600 - no. of seconds in an hour.
48
- #
49
- # Returns a rational number.
50
- def rational_hour(seconds)
51
- seconds.to_r / 3600
52
- end
53
-
54
- # Private: Convert given seconds to an hour as an absolute number.
55
- #
56
- # seconds - supplied as an integer, it is converted to its absolute.
57
- # 3600 - no. of seconds in an hour.
58
- #
59
- # Returns an integer.
60
- def absolute_hour(seconds)
61
- seconds.abs / 3600
62
- end
63
-
64
- # Private: Perform a modulo operation on a given fraction.
65
- #
66
- # fraction - supplied as a rational number, its numerator is divided
67
- # by its denominator and the remainder returned.
68
- #
69
- # Returns an integer.
70
- def modulo_of(fraction)
71
- fraction.numerator % fraction.denominator
42
+ "WTZ#{sign}#{time}"
72
43
  end
73
44
  end
74
45
  end