jekyll 4.2.1 → 4.3.0

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 (128) 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 +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/data_entry.rb +83 -0
  35. data/lib/jekyll/data_hash.rb +61 -0
  36. data/lib/jekyll/deprecator.rb +50 -50
  37. data/lib/jekyll/document.rb +543 -544
  38. data/lib/jekyll/drops/collection_drop.rb +20 -20
  39. data/lib/jekyll/drops/document_drop.rb +71 -70
  40. data/lib/jekyll/drops/drop.rb +293 -293
  41. data/lib/jekyll/drops/excerpt_drop.rb +23 -19
  42. data/lib/jekyll/drops/jekyll_drop.rb +32 -32
  43. data/lib/jekyll/drops/site_drop.rb +71 -66
  44. data/lib/jekyll/drops/static_file_drop.rb +14 -14
  45. data/lib/jekyll/drops/theme_drop.rb +36 -0
  46. data/lib/jekyll/drops/unified_payload_drop.rb +30 -26
  47. data/lib/jekyll/drops/url_drop.rb +140 -140
  48. data/lib/jekyll/entry_filter.rb +117 -121
  49. data/lib/jekyll/errors.rb +20 -20
  50. data/lib/jekyll/excerpt.rb +200 -201
  51. data/lib/jekyll/external.rb +75 -79
  52. data/lib/jekyll/filters/date_filters.rb +110 -110
  53. data/lib/jekyll/filters/grouping_filters.rb +64 -64
  54. data/lib/jekyll/filters/url_filters.rb +98 -98
  55. data/lib/jekyll/filters.rb +532 -535
  56. data/lib/jekyll/frontmatter_defaults.rb +238 -240
  57. data/lib/jekyll/generator.rb +5 -5
  58. data/lib/jekyll/hooks.rb +107 -107
  59. data/lib/jekyll/inclusion.rb +32 -32
  60. data/lib/jekyll/layout.rb +55 -67
  61. data/lib/jekyll/liquid_extensions.rb +22 -22
  62. data/lib/jekyll/liquid_renderer/file.rb +77 -77
  63. data/lib/jekyll/liquid_renderer/table.rb +55 -55
  64. data/lib/jekyll/liquid_renderer.rb +80 -80
  65. data/lib/jekyll/log_adapter.rb +151 -151
  66. data/lib/jekyll/mime.types +939 -866
  67. data/lib/jekyll/page.rb +215 -217
  68. data/lib/jekyll/page_excerpt.rb +25 -25
  69. data/lib/jekyll/page_without_a_file.rb +14 -14
  70. data/lib/jekyll/path_manager.rb +74 -74
  71. data/lib/jekyll/plugin.rb +92 -92
  72. data/lib/jekyll/plugin_manager.rb +123 -115
  73. data/lib/jekyll/profiler.rb +55 -58
  74. data/lib/jekyll/publisher.rb +23 -23
  75. data/lib/jekyll/reader.rb +209 -192
  76. data/lib/jekyll/readers/collection_reader.rb +23 -23
  77. data/lib/jekyll/readers/data_reader.rb +116 -79
  78. data/lib/jekyll/readers/layout_reader.rb +62 -62
  79. data/lib/jekyll/readers/page_reader.rb +25 -25
  80. data/lib/jekyll/readers/post_reader.rb +85 -85
  81. data/lib/jekyll/readers/static_file_reader.rb +25 -25
  82. data/lib/jekyll/readers/theme_assets_reader.rb +52 -52
  83. data/lib/jekyll/regenerator.rb +195 -195
  84. data/lib/jekyll/related_posts.rb +52 -52
  85. data/lib/jekyll/renderer.rb +263 -265
  86. data/lib/jekyll/site.rb +582 -551
  87. data/lib/jekyll/static_file.rb +205 -208
  88. data/lib/jekyll/stevenson.rb +60 -60
  89. data/lib/jekyll/tags/highlight.rb +114 -110
  90. data/lib/jekyll/tags/include.rb +275 -275
  91. data/lib/jekyll/tags/link.rb +42 -42
  92. data/lib/jekyll/tags/post_url.rb +106 -106
  93. data/lib/jekyll/theme.rb +90 -86
  94. data/lib/jekyll/theme_builder.rb +121 -121
  95. data/lib/jekyll/url.rb +167 -167
  96. data/lib/jekyll/utils/ansi.rb +57 -57
  97. data/lib/jekyll/utils/exec.rb +26 -26
  98. data/lib/jekyll/utils/internet.rb +37 -37
  99. data/lib/jekyll/utils/platforms.rb +67 -67
  100. data/lib/jekyll/utils/thread_event.rb +31 -31
  101. data/lib/jekyll/utils/win_tz.rb +46 -75
  102. data/lib/jekyll/utils.rb +378 -367
  103. data/lib/jekyll/version.rb +5 -5
  104. data/lib/jekyll.rb +197 -195
  105. data/lib/site_template/.gitignore +5 -5
  106. data/lib/site_template/404.html +25 -25
  107. data/lib/site_template/_config.yml +55 -55
  108. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
  109. data/lib/site_template/about.markdown +18 -18
  110. data/lib/site_template/index.markdown +6 -6
  111. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
  112. data/lib/theme_template/Gemfile +4 -4
  113. data/lib/theme_template/LICENSE.txt.erb +21 -21
  114. data/lib/theme_template/README.md.erb +50 -52
  115. data/lib/theme_template/_layouts/default.html +1 -1
  116. data/lib/theme_template/_layouts/page.html +5 -5
  117. data/lib/theme_template/_layouts/post.html +5 -5
  118. data/lib/theme_template/example/_config.yml.erb +1 -1
  119. data/lib/theme_template/example/_post.md +12 -12
  120. data/lib/theme_template/example/index.html +14 -14
  121. data/lib/theme_template/example/style.scss +7 -7
  122. data/lib/theme_template/gitignore.erb +6 -6
  123. data/lib/theme_template/theme.gemspec.erb +16 -16
  124. data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
  125. data/rubocop/jekyll/no_p_allowed.rb +23 -23
  126. data/rubocop/jekyll/no_puts_allowed.rb +23 -23
  127. data/rubocop/jekyll.rb +5 -5
  128. metadata +62 -14
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,116 @@
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 = DataHash.new
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
+ # rubocop:disable Metrics/AbcSize
28
+
29
+ # Read and parse all .yaml, .yml, .json, .csv and .tsv
30
+ # files under <dir> and add them to the <data> variable.
31
+ #
32
+ # dir - The string absolute path of the directory to read.
33
+ # data - The variable to which data will be added.
34
+ #
35
+ # Returns nothing
36
+ def read_data_to(dir, data)
37
+ return unless File.directory?(dir) && !@entry_filter.symlink?(dir)
38
+
39
+ entries = Dir.chdir(dir) do
40
+ Dir["*.{yaml,yml,json,csv,tsv}"] + Dir["*"].select { |fn| File.directory?(fn) }
41
+ end
42
+
43
+ entries.each do |entry|
44
+ path = @in_source_dir.call(dir, entry)
45
+ next if @entry_filter.symlink?(path)
46
+
47
+ if File.directory?(path)
48
+ read_data_to(path, data[sanitize_filename(entry)] = DataHash.new)
49
+ else
50
+ key = sanitize_filename(File.basename(entry, ".*"))
51
+ data[key] = DataEntry.new(site, path, read_data_file(path))
52
+ end
53
+ end
54
+ end
55
+ # rubocop:enable Metrics/AbcSize
56
+
57
+ # Determines how to read a data file.
58
+ #
59
+ # Returns the contents of the data file.
60
+ def read_data_file(path)
61
+ Jekyll.logger.debug "Reading:", path.sub(@source_dir, "")
62
+
63
+ case File.extname(path).downcase
64
+ when ".csv"
65
+ CSV.read(path, **csv_config).map { |row| convert_row(row) }
66
+ when ".tsv"
67
+ CSV.read(path, **tsv_config).map { |row| convert_row(row) }
68
+ else
69
+ SafeYAML.load_file(path)
70
+ end
71
+ end
72
+
73
+ def sanitize_filename(name)
74
+ name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "")
75
+ .gsub(%r!\s+!, "_")
76
+ end
77
+
78
+ private
79
+
80
+ # @return [Hash]
81
+ def csv_config
82
+ @csv_config ||= read_config("csv_reader")
83
+ end
84
+
85
+ # @return [Hash]
86
+ def tsv_config
87
+ @tsv_config ||= read_config("tsv_reader", { :col_sep => "\t" })
88
+ end
89
+
90
+ # @param config_key [String]
91
+ # @param overrides [Hash]
92
+ # @return [Hash]
93
+ # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/csv/rdoc/CSV.html#Converters
94
+ def read_config(config_key, overrides = {})
95
+ reader_config = config[config_key] || {}
96
+
97
+ defaults = {
98
+ :converters => reader_config.fetch("csv_converters", []).map(&:to_sym),
99
+ :headers => reader_config.fetch("headers", true),
100
+ :encoding => reader_config.fetch("encoding", config["encoding"]),
101
+ }
102
+
103
+ defaults.merge(overrides)
104
+ end
105
+
106
+ def config
107
+ @config ||= site.config
108
+ end
109
+
110
+ # @param row [Array, CSV::Row]
111
+ # @return [Array, Hash]
112
+ def convert_row(row)
113
+ row.instance_of?(CSV::Row) ? row.to_hash : row
114
+ end
115
+ end
116
+ end