jekyll 4.2.1 → 4.3.2

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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +474 -350
  3. data/LICENSE +21 -21
  4. data/README.markdown +83 -86
  5. data/exe/jekyll +57 -57
  6. data/lib/blank_template/_config.yml +3 -3
  7. data/lib/blank_template/_layouts/default.html +12 -12
  8. data/lib/blank_template/_sass/{main.scss → base.scss} +9 -9
  9. data/lib/blank_template/assets/css/main.scss +4 -4
  10. data/lib/blank_template/index.md +8 -8
  11. data/lib/jekyll/cache.rb +186 -190
  12. data/lib/jekyll/cleaner.rb +111 -111
  13. data/lib/jekyll/collection.rb +310 -309
  14. data/lib/jekyll/command.rb +105 -105
  15. data/lib/jekyll/commands/build.rb +82 -93
  16. data/lib/jekyll/commands/clean.rb +44 -45
  17. data/lib/jekyll/commands/doctor.rb +177 -177
  18. data/lib/jekyll/commands/help.rb +34 -34
  19. data/lib/jekyll/commands/new.rb +168 -169
  20. data/lib/jekyll/commands/new_theme.rb +39 -40
  21. data/lib/jekyll/commands/serve/live_reload_reactor.rb +119 -122
  22. data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -1183
  23. data/lib/jekyll/commands/serve/mime_types_charset.json +71 -0
  24. data/lib/jekyll/commands/serve/servlet.rb +206 -202
  25. data/lib/jekyll/commands/serve/websockets.rb +81 -81
  26. data/lib/jekyll/commands/serve.rb +367 -362
  27. data/lib/jekyll/configuration.rb +313 -313
  28. data/lib/jekyll/converter.rb +54 -54
  29. data/lib/jekyll/converters/identity.rb +41 -41
  30. data/lib/jekyll/converters/markdown/kramdown_parser.rb +197 -199
  31. data/lib/jekyll/converters/markdown.rb +113 -113
  32. data/lib/jekyll/converters/smartypants.rb +70 -70
  33. data/lib/jekyll/convertible.rb +257 -257
  34. data/lib/jekyll/deprecator.rb +50 -50
  35. data/lib/jekyll/document.rb +543 -544
  36. data/lib/jekyll/drops/collection_drop.rb +20 -20
  37. data/lib/jekyll/drops/document_drop.rb +74 -70
  38. data/lib/jekyll/drops/drop.rb +293 -293
  39. data/lib/jekyll/drops/excerpt_drop.rb +23 -19
  40. data/lib/jekyll/drops/jekyll_drop.rb +32 -32
  41. data/lib/jekyll/drops/site_drop.rb +66 -66
  42. data/lib/jekyll/drops/static_file_drop.rb +14 -14
  43. data/lib/jekyll/drops/theme_drop.rb +36 -0
  44. data/lib/jekyll/drops/unified_payload_drop.rb +30 -26
  45. data/lib/jekyll/drops/url_drop.rb +140 -140
  46. data/lib/jekyll/entry_filter.rb +117 -121
  47. data/lib/jekyll/errors.rb +20 -20
  48. data/lib/jekyll/excerpt.rb +200 -201
  49. data/lib/jekyll/external.rb +75 -79
  50. data/lib/jekyll/filters/date_filters.rb +110 -110
  51. data/lib/jekyll/filters/grouping_filters.rb +64 -64
  52. data/lib/jekyll/filters/url_filters.rb +98 -98
  53. data/lib/jekyll/filters.rb +532 -535
  54. data/lib/jekyll/frontmatter_defaults.rb +238 -240
  55. data/lib/jekyll/generator.rb +5 -5
  56. data/lib/jekyll/hooks.rb +107 -107
  57. data/lib/jekyll/inclusion.rb +32 -32
  58. data/lib/jekyll/layout.rb +55 -67
  59. data/lib/jekyll/liquid_extensions.rb +22 -22
  60. data/lib/jekyll/liquid_renderer/file.rb +77 -77
  61. data/lib/jekyll/liquid_renderer/table.rb +45 -55
  62. data/lib/jekyll/liquid_renderer.rb +80 -80
  63. data/lib/jekyll/log_adapter.rb +151 -151
  64. data/lib/jekyll/mime.types +939 -866
  65. data/lib/jekyll/page.rb +215 -217
  66. data/lib/jekyll/page_excerpt.rb +25 -25
  67. data/lib/jekyll/page_without_a_file.rb +14 -14
  68. data/lib/jekyll/path_manager.rb +74 -74
  69. data/lib/jekyll/plugin.rb +92 -92
  70. data/lib/jekyll/plugin_manager.rb +123 -115
  71. data/lib/jekyll/profiler.rb +51 -58
  72. data/lib/jekyll/publisher.rb +23 -23
  73. data/lib/jekyll/reader.rb +209 -192
  74. data/lib/jekyll/readers/collection_reader.rb +23 -23
  75. data/lib/jekyll/readers/data_reader.rb +113 -79
  76. data/lib/jekyll/readers/layout_reader.rb +62 -62
  77. data/lib/jekyll/readers/page_reader.rb +25 -25
  78. data/lib/jekyll/readers/post_reader.rb +85 -85
  79. data/lib/jekyll/readers/static_file_reader.rb +25 -25
  80. data/lib/jekyll/readers/theme_assets_reader.rb +52 -52
  81. data/lib/jekyll/regenerator.rb +195 -195
  82. data/lib/jekyll/related_posts.rb +52 -52
  83. data/lib/jekyll/renderer.rb +263 -265
  84. data/lib/jekyll/site.rb +576 -551
  85. data/lib/jekyll/static_file.rb +205 -208
  86. data/lib/jekyll/stevenson.rb +60 -60
  87. data/lib/jekyll/tags/highlight.rb +114 -110
  88. data/lib/jekyll/tags/include.rb +275 -275
  89. data/lib/jekyll/tags/link.rb +42 -42
  90. data/lib/jekyll/tags/post_url.rb +106 -106
  91. data/lib/jekyll/theme.rb +90 -86
  92. data/lib/jekyll/theme_builder.rb +121 -121
  93. data/lib/jekyll/url.rb +167 -167
  94. data/lib/jekyll/utils/ansi.rb +57 -57
  95. data/lib/jekyll/utils/exec.rb +26 -26
  96. data/lib/jekyll/utils/internet.rb +37 -37
  97. data/lib/jekyll/utils/platforms.rb +67 -67
  98. data/lib/jekyll/utils/thread_event.rb +31 -31
  99. data/lib/jekyll/utils/win_tz.rb +46 -75
  100. data/lib/jekyll/utils.rb +371 -367
  101. data/lib/jekyll/version.rb +5 -5
  102. data/lib/jekyll.rb +195 -195
  103. data/lib/site_template/.gitignore +5 -5
  104. data/lib/site_template/404.html +25 -25
  105. data/lib/site_template/_config.yml +55 -55
  106. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
  107. data/lib/site_template/about.markdown +18 -18
  108. data/lib/site_template/index.markdown +6 -6
  109. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
  110. data/lib/theme_template/Gemfile +4 -4
  111. data/lib/theme_template/LICENSE.txt.erb +21 -21
  112. data/lib/theme_template/README.md.erb +50 -52
  113. data/lib/theme_template/_layouts/default.html +1 -1
  114. data/lib/theme_template/_layouts/page.html +5 -5
  115. data/lib/theme_template/_layouts/post.html +5 -5
  116. data/lib/theme_template/example/_config.yml.erb +1 -1
  117. data/lib/theme_template/example/_post.md +12 -12
  118. data/lib/theme_template/example/index.html +14 -14
  119. data/lib/theme_template/example/style.scss +7 -7
  120. data/lib/theme_template/gitignore.erb +6 -6
  121. data/lib/theme_template/theme.gemspec.erb +16 -16
  122. data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
  123. data/rubocop/jekyll/no_p_allowed.rb +23 -23
  124. data/rubocop/jekyll/no_puts_allowed.rb +23 -23
  125. data/rubocop/jekyll.rb +5 -5
  126. metadata +64 -18
data/lib/jekyll/reader.rb CHANGED
@@ -1,192 +1,209 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- class Reader
5
- attr_reader :site
6
-
7
- def initialize(site)
8
- @site = site
9
- end
10
-
11
- # Read Site data from disk and load it into internal data structures.
12
- #
13
- # Returns nothing.
14
- def read
15
- @site.layouts = LayoutReader.new(site).read
16
- read_directories
17
- read_included_excludes
18
- sort_files!
19
- @site.data = DataReader.new(site).read(site.config["data_dir"])
20
- CollectionReader.new(site).read
21
- ThemeAssetsReader.new(site).read
22
- end
23
-
24
- # Sorts posts, pages, and static files.
25
- def sort_files!
26
- site.collections.each_value { |c| c.docs.sort! }
27
- site.pages.sort_by!(&:name)
28
- site.static_files.sort_by!(&:relative_path)
29
- end
30
-
31
- # Recursively traverse directories to find pages and static files
32
- # that will become part of the site according to the rules in
33
- # filter_entries.
34
- #
35
- # dir - The String relative path of the directory to read. Default: ''.
36
- #
37
- # Returns nothing.
38
- def read_directories(dir = "")
39
- base = site.in_source_dir(dir)
40
-
41
- return unless File.directory?(base)
42
-
43
- dot_dirs = []
44
- dot_pages = []
45
- dot_static_files = []
46
-
47
- dot = Dir.chdir(base) { filter_entries(Dir.entries("."), base) }
48
- dot.each do |entry|
49
- file_path = @site.in_source_dir(base, entry)
50
- if File.directory?(file_path)
51
- dot_dirs << entry
52
- elsif Utils.has_yaml_header?(file_path)
53
- dot_pages << entry
54
- else
55
- dot_static_files << entry
56
- end
57
- end
58
-
59
- retrieve_posts(dir)
60
- retrieve_dirs(base, dir, dot_dirs)
61
- retrieve_pages(dir, dot_pages)
62
- retrieve_static_files(dir, dot_static_files)
63
- end
64
-
65
- # Retrieves all the posts(posts/drafts) from the given directory
66
- # and add them to the site and sort them.
67
- #
68
- # dir - The String representing the directory to retrieve the posts from.
69
- #
70
- # Returns nothing.
71
- def retrieve_posts(dir)
72
- return if outside_configured_directory?(dir)
73
-
74
- site.posts.docs.concat(post_reader.read_posts(dir))
75
- site.posts.docs.concat(post_reader.read_drafts(dir)) if site.show_drafts
76
- end
77
-
78
- # Recursively traverse directories with the read_directories function.
79
- #
80
- # base - The String representing the site's base directory.
81
- # dir - The String representing the directory to traverse down.
82
- # dot_dirs - The Array of subdirectories in the dir.
83
- #
84
- # Returns nothing.
85
- def retrieve_dirs(_base, dir, dot_dirs)
86
- dot_dirs.each do |file|
87
- dir_path = site.in_source_dir(dir, file)
88
- rel_path = PathManager.join(dir, file)
89
- @site.reader.read_directories(rel_path) unless @site.dest.chomp("/") == dir_path
90
- end
91
- end
92
-
93
- # Retrieve all the pages from the current directory,
94
- # add them to the site and sort them.
95
- #
96
- # dir - The String representing the directory retrieve the pages from.
97
- # dot_pages - The Array of pages in the dir.
98
- #
99
- # Returns nothing.
100
- def retrieve_pages(dir, dot_pages)
101
- site.pages.concat(PageReader.new(site, dir).read(dot_pages))
102
- end
103
-
104
- # Retrieve all the static files from the current directory,
105
- # add them to the site and sort them.
106
- #
107
- # dir - The directory retrieve the static files from.
108
- # dot_static_files - The static files in the dir.
109
- #
110
- # Returns nothing.
111
- def retrieve_static_files(dir, dot_static_files)
112
- site.static_files.concat(StaticFileReader.new(site, dir).read(dot_static_files))
113
- end
114
-
115
- # Filter out any files/directories that are hidden or backup files (start
116
- # with "." or "#" or end with "~"), or contain site content (start with "_"),
117
- # or are excluded in the site configuration, unless they are web server
118
- # files such as '.htaccess'.
119
- #
120
- # entries - The Array of String file/directory entries to filter.
121
- # base_directory - The string representing the optional base directory.
122
- #
123
- # Returns the Array of filtered entries.
124
- def filter_entries(entries, base_directory = nil)
125
- EntryFilter.new(site, base_directory).filter(entries)
126
- end
127
-
128
- # Read the entries from a particular directory for processing
129
- #
130
- # dir - The String representing the relative path of the directory to read.
131
- # subfolder - The String representing the directory to read.
132
- #
133
- # Returns the list of entries to process
134
- def get_entries(dir, subfolder)
135
- base = site.in_source_dir(dir, subfolder)
136
- return [] unless File.exist?(base)
137
-
138
- entries = Dir.chdir(base) { filter_entries(Dir["**/*"], base) }
139
- entries.delete_if { |e| File.directory?(site.in_source_dir(base, e)) }
140
- end
141
-
142
- private
143
-
144
- # Internal
145
- #
146
- # Determine if the directory is supposed to contain posts and drafts.
147
- # If the user has defined a custom collections_dir, then attempt to read
148
- # posts and drafts only from within that directory.
149
- #
150
- # Returns true if a custom collections_dir has been set but current directory lies
151
- # outside that directory.
152
- def outside_configured_directory?(dir)
153
- collections_dir = site.config["collections_dir"]
154
- !collections_dir.empty? && !dir.start_with?("/#{collections_dir}")
155
- end
156
-
157
- # Create a single PostReader instance to retrieve drafts and posts from all valid
158
- # directories in current site.
159
- def post_reader
160
- @post_reader ||= PostReader.new(site)
161
- end
162
-
163
- def read_included_excludes
164
- entry_filter = EntryFilter.new(site)
165
-
166
- site.include.each do |entry|
167
- entry_path = site.in_source_dir(entry)
168
- next if File.directory?(entry_path)
169
- next if entry_filter.symlink?(entry_path)
170
-
171
- read_included_file(entry_path) if File.file?(entry_path)
172
- end
173
- end
174
-
175
- def read_included_file(entry_path)
176
- if Utils.has_yaml_header?(entry_path)
177
- conditionally_generate_entry(entry_path, site.pages, PageReader)
178
- else
179
- conditionally_generate_entry(entry_path, site.static_files, StaticFileReader)
180
- end
181
- end
182
-
183
- def conditionally_generate_entry(entry_path, container, reader)
184
- return if container.find { |item| site.in_source_dir(item.relative_path) == entry_path }
185
-
186
- dir, files = File.split(entry_path)
187
- dir.sub!(site.source, "")
188
- files = Array(files)
189
- container.concat(reader.new(site, dir).read(files))
190
- end
191
- end
192
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class Reader
5
+ attr_reader :site
6
+
7
+ def initialize(site)
8
+ @site = site
9
+ end
10
+
11
+ # Read Site data from disk and load it into internal data structures.
12
+ #
13
+ # Returns nothing.
14
+ def read
15
+ @site.layouts = LayoutReader.new(site).read
16
+ read_directories
17
+ read_included_excludes
18
+ sort_files!
19
+ CollectionReader.new(site).read
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)
39
+ end
40
+
41
+ # Sorts posts, pages, and static files.
42
+ def sort_files!
43
+ site.collections.each_value { |c| c.docs.sort! }
44
+ site.pages.sort_by!(&:name)
45
+ site.static_files.sort_by!(&:relative_path)
46
+ end
47
+
48
+ # Recursively traverse directories to find pages and static files
49
+ # that will become part of the site according to the rules in
50
+ # filter_entries.
51
+ #
52
+ # dir - The String relative path of the directory to read. Default: ''.
53
+ #
54
+ # Returns nothing.
55
+ def read_directories(dir = "")
56
+ base = site.in_source_dir(dir)
57
+
58
+ return unless File.directory?(base)
59
+
60
+ dot_dirs = []
61
+ dot_pages = []
62
+ dot_static_files = []
63
+
64
+ dot = Dir.chdir(base) { filter_entries(Dir.entries("."), base) }
65
+ dot.each do |entry|
66
+ file_path = @site.in_source_dir(base, entry)
67
+ if File.directory?(file_path)
68
+ dot_dirs << entry
69
+ elsif Utils.has_yaml_header?(file_path)
70
+ dot_pages << entry
71
+ else
72
+ dot_static_files << entry
73
+ end
74
+ end
75
+
76
+ retrieve_posts(dir)
77
+ retrieve_dirs(base, dir, dot_dirs)
78
+ retrieve_pages(dir, dot_pages)
79
+ retrieve_static_files(dir, dot_static_files)
80
+ end
81
+
82
+ # Retrieves all the posts(posts/drafts) from the given directory
83
+ # and add them to the site and sort them.
84
+ #
85
+ # dir - The String representing the directory to retrieve the posts from.
86
+ #
87
+ # Returns nothing.
88
+ def retrieve_posts(dir)
89
+ return if outside_configured_directory?(dir)
90
+
91
+ site.posts.docs.concat(post_reader.read_posts(dir))
92
+ site.posts.docs.concat(post_reader.read_drafts(dir)) if site.show_drafts
93
+ end
94
+
95
+ # Recursively traverse directories with the read_directories function.
96
+ #
97
+ # base - The String representing the site's base directory.
98
+ # dir - The String representing the directory to traverse down.
99
+ # dot_dirs - The Array of subdirectories in the dir.
100
+ #
101
+ # Returns nothing.
102
+ def retrieve_dirs(_base, dir, dot_dirs)
103
+ dot_dirs.each do |file|
104
+ dir_path = site.in_source_dir(dir, file)
105
+ rel_path = PathManager.join(dir, file)
106
+ @site.reader.read_directories(rel_path) unless @site.dest.chomp("/") == dir_path
107
+ end
108
+ end
109
+
110
+ # Retrieve all the pages from the current directory,
111
+ # add them to the site and sort them.
112
+ #
113
+ # dir - The String representing the directory retrieve the pages from.
114
+ # dot_pages - The Array of pages in the dir.
115
+ #
116
+ # Returns nothing.
117
+ def retrieve_pages(dir, dot_pages)
118
+ site.pages.concat(PageReader.new(site, dir).read(dot_pages))
119
+ end
120
+
121
+ # Retrieve all the static files from the current directory,
122
+ # add them to the site and sort them.
123
+ #
124
+ # dir - The directory retrieve the static files from.
125
+ # dot_static_files - The static files in the dir.
126
+ #
127
+ # Returns nothing.
128
+ def retrieve_static_files(dir, dot_static_files)
129
+ site.static_files.concat(StaticFileReader.new(site, dir).read(dot_static_files))
130
+ end
131
+
132
+ # Filter out any files/directories that are hidden or backup files (start
133
+ # with "." or "#" or end with "~"), or contain site content (start with "_"),
134
+ # or are excluded in the site configuration, unless they are web server
135
+ # files such as '.htaccess'.
136
+ #
137
+ # entries - The Array of String file/directory entries to filter.
138
+ # base_directory - The string representing the optional base directory.
139
+ #
140
+ # Returns the Array of filtered entries.
141
+ def filter_entries(entries, base_directory = nil)
142
+ EntryFilter.new(site, base_directory).filter(entries)
143
+ end
144
+
145
+ # Read the entries from a particular directory for processing
146
+ #
147
+ # dir - The String representing the relative path of the directory to read.
148
+ # subfolder - The String representing the directory to read.
149
+ #
150
+ # Returns the list of entries to process
151
+ def get_entries(dir, subfolder)
152
+ base = site.in_source_dir(dir, subfolder)
153
+ return [] unless File.exist?(base)
154
+
155
+ entries = Dir.chdir(base) { filter_entries(Dir["**/*"], base) }
156
+ entries.delete_if { |e| File.directory?(site.in_source_dir(base, e)) }
157
+ end
158
+
159
+ private
160
+
161
+ # Internal
162
+ #
163
+ # Determine if the directory is supposed to contain posts and drafts.
164
+ # If the user has defined a custom collections_dir, then attempt to read
165
+ # posts and drafts only from within that directory.
166
+ #
167
+ # Returns true if a custom collections_dir has been set but current directory lies
168
+ # outside that directory.
169
+ def outside_configured_directory?(dir)
170
+ collections_dir = site.config["collections_dir"]
171
+ !collections_dir.empty? && !dir.start_with?("/#{collections_dir}")
172
+ end
173
+
174
+ # Create a single PostReader instance to retrieve drafts and posts from all valid
175
+ # directories in current site.
176
+ def post_reader
177
+ @post_reader ||= PostReader.new(site)
178
+ end
179
+
180
+ def read_included_excludes
181
+ entry_filter = EntryFilter.new(site)
182
+
183
+ site.include.each do |entry|
184
+ entry_path = site.in_source_dir(entry)
185
+ next if File.directory?(entry_path)
186
+ next if entry_filter.symlink?(entry_path)
187
+
188
+ read_included_file(entry_path) if File.file?(entry_path)
189
+ end
190
+ end
191
+
192
+ def read_included_file(entry_path)
193
+ if Utils.has_yaml_header?(entry_path)
194
+ conditionally_generate_entry(entry_path, site.pages, PageReader)
195
+ else
196
+ conditionally_generate_entry(entry_path, site.static_files, StaticFileReader)
197
+ end
198
+ end
199
+
200
+ def conditionally_generate_entry(entry_path, container, reader)
201
+ return if container.find { |item| site.in_source_dir(item.relative_path) == entry_path }
202
+
203
+ dir, files = File.split(entry_path)
204
+ dir.sub!(site.source, "")
205
+ files = Array(files)
206
+ container.concat(reader.new(site, dir).read(files))
207
+ end
208
+ end
209
+ end
@@ -1,23 +1,23 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- class CollectionReader
5
- SPECIAL_COLLECTIONS = %w(posts data).freeze
6
-
7
- attr_reader :site, :content
8
-
9
- def initialize(site)
10
- @site = site
11
- @content = {}
12
- end
13
-
14
- # Read in all collections specified in the configuration
15
- #
16
- # Returns nothing.
17
- def read
18
- site.collections.each_value do |collection|
19
- collection.read unless SPECIAL_COLLECTIONS.include?(collection.label)
20
- end
21
- end
22
- end
23
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class CollectionReader
5
+ SPECIAL_COLLECTIONS = %w(posts data).freeze
6
+
7
+ attr_reader :site, :content
8
+
9
+ def initialize(site)
10
+ @site = site
11
+ @content = {}
12
+ end
13
+
14
+ # Read in all collections specified in the configuration
15
+ #
16
+ # Returns nothing.
17
+ def read
18
+ site.collections.each_value do |collection|
19
+ collection.read unless SPECIAL_COLLECTIONS.include?(collection.label)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,79 +1,113 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- class DataReader
5
- attr_reader :site, :content
6
-
7
- def initialize(site)
8
- @site = site
9
- @content = {}
10
- @entry_filter = EntryFilter.new(site)
11
- @source_dir = site.in_source_dir("/")
12
- end
13
-
14
- # Read all the files in <dir> and adds them to @content
15
- #
16
- # dir - The String relative path of the directory to read.
17
- #
18
- # Returns @content, a Hash of the .yaml, .yml,
19
- # .json, and .csv files in the base directory
20
- def read(dir)
21
- base = site.in_source_dir(dir)
22
- read_data_to(base, @content)
23
- @content
24
- end
25
-
26
- # Read and parse all .yaml, .yml, .json, .csv and .tsv
27
- # files under <dir> and add them to the <data> variable.
28
- #
29
- # dir - The string absolute path of the directory to read.
30
- # data - The variable to which data will be added.
31
- #
32
- # Returns nothing
33
- def read_data_to(dir, data)
34
- return unless File.directory?(dir) && !@entry_filter.symlink?(dir)
35
-
36
- entries = Dir.chdir(dir) do
37
- Dir["*.{yaml,yml,json,csv,tsv}"] + Dir["*"].select { |fn| File.directory?(fn) }
38
- end
39
-
40
- entries.each do |entry|
41
- path = @site.in_source_dir(dir, entry)
42
- next if @entry_filter.symlink?(path)
43
-
44
- if File.directory?(path)
45
- read_data_to(path, data[sanitize_filename(entry)] = {})
46
- else
47
- key = sanitize_filename(File.basename(entry, ".*"))
48
- data[key] = read_data_file(path)
49
- end
50
- end
51
- end
52
-
53
- # Determines how to read a data file.
54
- #
55
- # Returns the contents of the data file.
56
- def read_data_file(path)
57
- Jekyll.logger.debug "Reading:", path.sub(@source_dir, "")
58
-
59
- case File.extname(path).downcase
60
- when ".csv"
61
- CSV.read(path,
62
- :headers => true,
63
- :encoding => site.config["encoding"]).map(&:to_hash)
64
- when ".tsv"
65
- CSV.read(path,
66
- :col_sep => "\t",
67
- :headers => true,
68
- :encoding => site.config["encoding"]).map(&:to_hash)
69
- else
70
- SafeYAML.load_file(path)
71
- end
72
- end
73
-
74
- def sanitize_filename(name)
75
- name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "")
76
- .gsub(%r!\s+!, "_")
77
- end
78
- end
79
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class DataReader
5
+ attr_reader :site, :content
6
+
7
+ def initialize(site, in_source_dir: nil)
8
+ @site = site
9
+ @content = {}
10
+ @entry_filter = EntryFilter.new(site)
11
+ @in_source_dir = in_source_dir || @site.method(:in_source_dir)
12
+ @source_dir = @in_source_dir.call("/")
13
+ end
14
+
15
+ # Read all the files in <dir> and adds them to @content
16
+ #
17
+ # dir - The String relative path of the directory to read.
18
+ #
19
+ # Returns @content, a Hash of the .yaml, .yml,
20
+ # .json, and .csv files in the base directory
21
+ def read(dir)
22
+ base = @in_source_dir.call(dir)
23
+ read_data_to(base, @content)
24
+ @content
25
+ end
26
+
27
+ # Read and parse all .yaml, .yml, .json, .csv and .tsv
28
+ # files under <dir> and add them to the <data> variable.
29
+ #
30
+ # dir - The string absolute path of the directory to read.
31
+ # data - The variable to which data will be added.
32
+ #
33
+ # Returns nothing
34
+ def read_data_to(dir, data)
35
+ return unless File.directory?(dir) && !@entry_filter.symlink?(dir)
36
+
37
+ entries = Dir.chdir(dir) do
38
+ Dir["*.{yaml,yml,json,csv,tsv}"] + Dir["*"].select { |fn| File.directory?(fn) }
39
+ end
40
+
41
+ entries.each do |entry|
42
+ path = @in_source_dir.call(dir, entry)
43
+ next if @entry_filter.symlink?(path)
44
+
45
+ if File.directory?(path)
46
+ read_data_to(path, data[sanitize_filename(entry)] = {})
47
+ else
48
+ key = sanitize_filename(File.basename(entry, ".*"))
49
+ data[key] = read_data_file(path)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Determines how to read a data file.
55
+ #
56
+ # Returns the contents of the data file.
57
+ def read_data_file(path)
58
+ Jekyll.logger.debug "Reading:", path.sub(@source_dir, "")
59
+
60
+ case File.extname(path).downcase
61
+ when ".csv"
62
+ CSV.read(path, **csv_config).map { |row| convert_row(row) }
63
+ when ".tsv"
64
+ CSV.read(path, **tsv_config).map { |row| convert_row(row) }
65
+ else
66
+ SafeYAML.load_file(path)
67
+ end
68
+ end
69
+
70
+ def sanitize_filename(name)
71
+ name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "")
72
+ .gsub(%r!\s+!, "_")
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
112
+ end
113
+ end