jekyll 3.10.0 → 4.0.0.pre.alpha1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +27 -50
- data/LICENSE +1 -1
- data/README.markdown +46 -17
- data/lib/blank_template/_config.yml +3 -0
- data/lib/blank_template/_layouts/default.html +12 -0
- data/lib/blank_template/_sass/main.scss +9 -0
- data/lib/blank_template/assets/css/main.scss +4 -0
- data/lib/blank_template/index.md +8 -0
- data/lib/jekyll/cache.rb +183 -0
- data/lib/jekyll/cleaner.rb +2 -1
- data/lib/jekyll/collection.rb +78 -8
- data/lib/jekyll/command.rb +31 -6
- data/lib/jekyll/commands/build.rb +11 -20
- data/lib/jekyll/commands/clean.rb +2 -0
- data/lib/jekyll/commands/doctor.rb +15 -8
- data/lib/jekyll/commands/help.rb +1 -1
- data/lib/jekyll/commands/new.rb +37 -42
- data/lib/jekyll/commands/new_theme.rb +30 -28
- data/lib/jekyll/commands/serve/live_reload_reactor.rb +6 -10
- data/lib/jekyll/commands/serve/servlet.rb +15 -19
- data/lib/jekyll/commands/serve.rb +46 -86
- data/lib/jekyll/configuration.rb +26 -26
- data/lib/jekyll/converters/identity.rb +18 -0
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +1 -10
- data/lib/jekyll/converters/markdown.rb +49 -40
- data/lib/jekyll/converters/smartypants.rb +34 -14
- data/lib/jekyll/convertible.rb +11 -13
- data/lib/jekyll/deprecator.rb +1 -3
- data/lib/jekyll/document.rb +44 -41
- data/lib/jekyll/drops/collection_drop.rb +2 -3
- data/lib/jekyll/drops/document_drop.rb +2 -1
- data/lib/jekyll/drops/drop.rb +3 -6
- data/lib/jekyll/drops/excerpt_drop.rb +4 -0
- data/lib/jekyll/drops/site_drop.rb +4 -13
- data/lib/jekyll/drops/unified_payload_drop.rb +1 -0
- data/lib/jekyll/drops/url_drop.rb +1 -0
- data/lib/jekyll/entry_filter.rb +2 -1
- data/lib/jekyll/excerpt.rb +45 -34
- data/lib/jekyll/external.rb +10 -5
- data/lib/jekyll/filters/date_filters.rb +6 -3
- data/lib/jekyll/filters/grouping_filters.rb +1 -2
- data/lib/jekyll/filters/url_filters.rb +6 -1
- data/lib/jekyll/filters.rb +72 -31
- data/lib/jekyll/frontmatter_defaults.rb +35 -19
- data/lib/jekyll/hooks.rb +2 -3
- data/lib/jekyll/liquid_extensions.rb +0 -2
- data/lib/jekyll/liquid_renderer/file.rb +14 -3
- data/lib/jekyll/liquid_renderer/table.rb +67 -65
- data/lib/jekyll/liquid_renderer.rb +13 -1
- data/lib/jekyll/log_adapter.rb +5 -1
- data/lib/jekyll/mime.types +80 -195
- data/lib/jekyll/page.rb +10 -26
- data/lib/jekyll/page_without_a_file.rb +0 -4
- data/lib/jekyll/plugin.rb +5 -11
- data/lib/jekyll/plugin_manager.rb +2 -0
- data/lib/jekyll/reader.rb +38 -8
- data/lib/jekyll/readers/data_reader.rb +5 -5
- data/lib/jekyll/readers/layout_reader.rb +2 -12
- data/lib/jekyll/readers/post_reader.rb +29 -17
- data/lib/jekyll/readers/static_file_reader.rb +1 -1
- data/lib/jekyll/readers/theme_assets_reader.rb +7 -5
- data/lib/jekyll/regenerator.rb +4 -12
- data/lib/jekyll/renderer.rb +14 -25
- data/lib/jekyll/site.rb +78 -34
- data/lib/jekyll/static_file.rb +47 -11
- data/lib/jekyll/stevenson.rb +7 -5
- data/lib/jekyll/tags/highlight.rb +22 -52
- data/lib/jekyll/tags/include.rb +27 -48
- data/lib/jekyll/tags/link.rb +11 -7
- data/lib/jekyll/tags/post_url.rb +17 -16
- data/lib/jekyll/theme.rb +12 -23
- data/lib/jekyll/theme_builder.rb +91 -89
- data/lib/jekyll/url.rb +3 -2
- data/lib/jekyll/utils/ansi.rb +1 -1
- data/lib/jekyll/utils/exec.rb +0 -1
- data/lib/jekyll/utils/internet.rb +2 -4
- data/lib/jekyll/utils/platforms.rb +8 -8
- data/lib/jekyll/utils/thread_event.rb +1 -5
- data/lib/jekyll/utils/win_tz.rb +47 -18
- data/lib/jekyll/utils.rb +5 -4
- data/lib/jekyll/version.rb +1 -1
- data/lib/jekyll.rb +5 -0
- data/lib/site_template/.gitignore +2 -0
- data/lib/site_template/404.html +1 -0
- data/lib/site_template/_config.yml +17 -5
- data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
- data/lib/theme_template/gitignore.erb +1 -0
- data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -0
- metadata +70 -71
- data/lib/jekyll/commands/serve/mime_types_charset.json +0 -71
- data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
- data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
- data/lib/jekyll/utils/rouge.rb +0 -22
- /data/lib/site_template/{about.md → about.markdown} +0 -0
- /data/lib/site_template/{index.md → index.markdown} +0 -0
@@ -8,12 +8,12 @@ module Jekyll
|
|
8
8
|
mutable false
|
9
9
|
|
10
10
|
def_delegator :@obj, :site_data, :data
|
11
|
-
def_delegators :@obj, :time, :pages, :static_files, :tags, :categories
|
11
|
+
def_delegators :@obj, :time, :pages, :static_files, :documents, :tags, :categories
|
12
12
|
|
13
13
|
private def_delegator :@obj, :config, :fallback_data
|
14
14
|
|
15
15
|
def [](key)
|
16
|
-
if @obj.collections.key?(key)
|
16
|
+
if key != "posts" && @obj.collections.key?(key)
|
17
17
|
@obj.collections[key].docs
|
18
18
|
else
|
19
19
|
super(key)
|
@@ -21,7 +21,7 @@ module Jekyll
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def key?(key)
|
24
|
-
(@obj.collections.key?(key)
|
24
|
+
(key != "posts" && @obj.collections.key?(key)) || super
|
25
25
|
end
|
26
26
|
|
27
27
|
def posts
|
@@ -38,22 +38,13 @@ module Jekyll
|
|
38
38
|
@site_collections ||= @obj.collections.values.sort_by(&:label).map(&:to_liquid)
|
39
39
|
end
|
40
40
|
|
41
|
-
# `Site#documents` cannot be memoized so that `Site#docs_to_write` can access the
|
42
|
-
# latest state of the attribute.
|
43
|
-
#
|
44
|
-
# Since this method will be called after `Site#pre_render` hook,
|
45
|
-
# the `Site#documents` array shouldn't thereafter change and can therefore be
|
46
|
-
# safely memoized to prevent additional computation of `Site#documents`.
|
47
|
-
def documents
|
48
|
-
@documents ||= @obj.documents
|
49
|
-
end
|
50
|
-
|
51
41
|
# `{{ site.related_posts }}` is how posts can get posts related to
|
52
42
|
# them, either through LSI if it's enabled, or through the most
|
53
43
|
# recent posts.
|
54
44
|
# We should remove this in 4.0 and switch to `{{ post.related_posts }}`.
|
55
45
|
def related_posts
|
56
46
|
return nil unless @current_document.is_a?(Jekyll::Document)
|
47
|
+
|
57
48
|
@current_document.related_posts
|
58
49
|
end
|
59
50
|
attr_writer :current_document
|
data/lib/jekyll/entry_filter.rb
CHANGED
@@ -35,6 +35,7 @@ module Jekyll
|
|
35
35
|
next true if symlink?(e)
|
36
36
|
# Do not reject this entry if it is included.
|
37
37
|
next false if included?(e)
|
38
|
+
|
38
39
|
# Reject this entry if it is special, a backup file, or excluded.
|
39
40
|
special?(e) || backup?(e) || excluded?(e)
|
40
41
|
end
|
@@ -55,7 +56,7 @@ module Jekyll
|
|
55
56
|
end
|
56
57
|
|
57
58
|
def excluded?(entry)
|
58
|
-
glob_include?(site.exclude, relative_to_source(entry)).tap do |excluded|
|
59
|
+
glob_include?(site.exclude - site.include, relative_to_source(entry)).tap do |excluded|
|
59
60
|
if excluded
|
60
61
|
Jekyll.logger.debug(
|
61
62
|
"EntryFilter:",
|
data/lib/jekyll/excerpt.rb
CHANGED
@@ -8,10 +8,11 @@ module Jekyll
|
|
8
8
|
attr_accessor :content, :ext
|
9
9
|
attr_writer :output
|
10
10
|
|
11
|
-
def_delegators :@doc,
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def_delegators :@doc,
|
12
|
+
:site, :name, :ext, :extname,
|
13
|
+
:collection, :related_posts,
|
14
|
+
:coffeescript_file?, :yaml_file?,
|
15
|
+
:url, :next_doc, :previous_doc
|
15
16
|
|
16
17
|
private :coffeescript_file?, :yaml_file?
|
17
18
|
|
@@ -48,14 +49,14 @@ module Jekyll
|
|
48
49
|
#
|
49
50
|
# Returns the relative_path for the doc this excerpt belongs to with #excerpt appended
|
50
51
|
def relative_path
|
51
|
-
File.join(doc.relative_path, "#excerpt")
|
52
|
+
@relative_path ||= File.join(doc.relative_path, "#excerpt")
|
52
53
|
end
|
53
54
|
|
54
55
|
# Check if excerpt includes a string
|
55
56
|
#
|
56
57
|
# Returns true if the string passed in
|
57
58
|
def include?(something)
|
58
|
-
(output
|
59
|
+
(output&.include?(something)) || content.include?(something)
|
59
60
|
end
|
60
61
|
|
61
62
|
# The UID for this doc (useful in feeds).
|
@@ -76,7 +77,7 @@ module Jekyll
|
|
76
77
|
|
77
78
|
# Returns the shorthand String identifier of this doc.
|
78
79
|
def inspect
|
79
|
-
"
|
80
|
+
"<#{self.class} id=#{id}>"
|
80
81
|
end
|
81
82
|
|
82
83
|
def output
|
@@ -88,6 +89,8 @@ module Jekyll
|
|
88
89
|
end
|
89
90
|
|
90
91
|
def render_with_liquid?
|
92
|
+
return false if data["render_with_liquid"] == false
|
93
|
+
|
91
94
|
!(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content))
|
92
95
|
end
|
93
96
|
|
@@ -128,36 +131,47 @@ module Jekyll
|
|
128
131
|
#
|
129
132
|
# Returns excerpt String
|
130
133
|
|
131
|
-
LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m
|
132
|
-
MKDWN_LINK_REF_REGEX = %r!^ {0,3}\[[^\]]+\]
|
134
|
+
LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m.freeze
|
135
|
+
MKDWN_LINK_REF_REGEX = %r!^ {0,3}(?:(\[[^\]]+\])(:.+))$!.freeze
|
133
136
|
|
134
137
|
def extract_excerpt(doc_content)
|
135
138
|
head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
|
139
|
+
return head if tail.empty?
|
136
140
|
|
137
|
-
|
138
|
-
|
139
|
-
|
141
|
+
head = sanctify_liquid_tags(head) if head.include?("{%")
|
142
|
+
definitions = extract_markdown_link_reference_defintions(head, tail)
|
143
|
+
return head if definitions.empty?
|
140
144
|
|
141
|
-
|
142
|
-
|
143
|
-
tag_names = head.scan(LIQUID_TAG_REGEX)
|
144
|
-
tag_names.flatten!
|
145
|
-
tag_names.reverse_each do |tag_name|
|
146
|
-
next unless liquid_block?(tag_name)
|
147
|
-
next if head =~ endtag_regex_stash(tag_name)
|
145
|
+
head << "\n\n" << definitions.join("\n")
|
146
|
+
end
|
148
147
|
|
149
|
-
|
150
|
-
head << "\n{% end#{tag_name} %}"
|
151
|
-
end
|
152
|
-
print_build_warning if modified
|
153
|
-
end
|
148
|
+
private
|
154
149
|
|
155
|
-
|
150
|
+
# append appropriate closing tag(s) (for each Liquid block), to the `head` if the
|
151
|
+
# partitioning resulted in leaving the closing tag somewhere in the `tail` partition.
|
152
|
+
def sanctify_liquid_tags(head)
|
153
|
+
modified = false
|
154
|
+
tag_names = head.scan(LIQUID_TAG_REGEX)
|
155
|
+
tag_names.flatten!
|
156
|
+
tag_names.reverse_each do |tag_name|
|
157
|
+
next unless liquid_block?(tag_name)
|
158
|
+
next if head =~ endtag_regex_stash(tag_name)
|
159
|
+
|
160
|
+
modified = true
|
161
|
+
head << "\n{% end#{tag_name} %}"
|
162
|
+
end
|
156
163
|
|
157
|
-
|
164
|
+
print_build_warning if modified
|
165
|
+
head
|
158
166
|
end
|
159
167
|
|
160
|
-
|
168
|
+
def extract_markdown_link_reference_defintions(head, tail)
|
169
|
+
[].tap do |definitions|
|
170
|
+
tail.scan(MKDWN_LINK_REF_REGEX).each do |segments|
|
171
|
+
definitions << segments.join if head.include?(segments[0])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
161
175
|
|
162
176
|
def endtag_regex_stash(tag_name)
|
163
177
|
@endtag_regex_stash ||= {}
|
@@ -171,8 +185,7 @@ module Jekyll
|
|
171
185
|
Liquid::Template.tags[tag_name].ancestors.include?(Liquid::Block)
|
172
186
|
rescue NoMethodError
|
173
187
|
Jekyll.logger.error "Error:",
|
174
|
-
|
175
|
-
"parsed."
|
188
|
+
"A Liquid tag in the excerpt of #{doc.relative_path} couldn't be parsed."
|
176
189
|
raise
|
177
190
|
end
|
178
191
|
|
@@ -180,11 +193,9 @@ module Jekyll
|
|
180
193
|
Jekyll.logger.warn "Warning:", "Excerpt modified in #{doc.relative_path}!"
|
181
194
|
Jekyll.logger.warn "", "Found a Liquid block containing the excerpt separator" \
|
182
195
|
" #{doc.excerpt_separator.inspect}. "
|
183
|
-
Jekyll.logger.warn "", "The block has been modified with the appropriate"
|
184
|
-
|
185
|
-
Jekyll.logger.warn "", "
|
186
|
-
" excerpt_separator in the document's front matter" \
|
187
|
-
" if the generated excerpt is unsatisfactory."
|
196
|
+
Jekyll.logger.warn "", "The block has been modified with the appropriate closing tag."
|
197
|
+
Jekyll.logger.warn "", "Feel free to define a custom excerpt or excerpt_separator in the"
|
198
|
+
Jekyll.logger.warn "", "document's Front Matter if the generated excerpt is unsatisfactory."
|
188
199
|
end
|
189
200
|
end
|
190
201
|
end
|
data/lib/jekyll/external.rb
CHANGED
@@ -9,6 +9,7 @@ module Jekyll
|
|
9
9
|
#
|
10
10
|
def blessed_gems
|
11
11
|
%w(
|
12
|
+
jekyll-compose
|
12
13
|
jekyll-docs
|
13
14
|
jekyll-import
|
14
15
|
)
|
@@ -41,6 +42,7 @@ module Jekyll
|
|
41
42
|
# RubyGems.
|
42
43
|
def version_constraint(gem_name)
|
43
44
|
return "= #{Jekyll::VERSION}" if gem_name.to_s.eql?("jekyll-docs")
|
45
|
+
|
44
46
|
"> 0"
|
45
47
|
end
|
46
48
|
|
@@ -57,13 +59,16 @@ module Jekyll
|
|
57
59
|
Jekyll.logger.debug "Requiring:", name.to_s
|
58
60
|
require name
|
59
61
|
rescue LoadError => e
|
60
|
-
Jekyll.logger.error "Dependency Error:",
|
61
|
-
Yikes! It looks like you don't have #{name} or one of its dependencies installed.
|
62
|
-
In order to use Jekyll as currently configured, you'll need to install this gem.
|
62
|
+
Jekyll.logger.error "Dependency Error:", <<~MSG
|
63
|
+
Yikes! It looks like you don't have #{name} or one of its dependencies installed.
|
64
|
+
In order to use Jekyll as currently configured, you'll need to install this gem.
|
65
|
+
|
66
|
+
If you've run Jekyll with `bundle exec`, ensure that you have included the #{name}
|
67
|
+
gem in your Gemfile as well.
|
63
68
|
|
64
|
-
The full error message from Ruby is: '#{e.message}'
|
69
|
+
The full error message from Ruby is: '#{e.message}'
|
65
70
|
|
66
|
-
If you run into trouble, you can find helpful resources at https://jekyllrb.com/help/!
|
71
|
+
If you run into trouble, you can find helpful resources at https://jekyllrb.com/help/!
|
67
72
|
MSG
|
68
73
|
raise Jekyll::Errors::MissingDependencyException, name
|
69
74
|
end
|
@@ -45,6 +45,7 @@ module Jekyll
|
|
45
45
|
# Returns the formatted String.
|
46
46
|
def date_to_xmlschema(date)
|
47
47
|
return date if date.to_s.empty?
|
48
|
+
|
48
49
|
time(date).xmlschema
|
49
50
|
end
|
50
51
|
|
@@ -60,10 +61,12 @@ module Jekyll
|
|
60
61
|
# Returns the formatted String.
|
61
62
|
def date_to_rfc822(date)
|
62
63
|
return date if date.to_s.empty?
|
64
|
+
|
63
65
|
time(date).rfc822
|
64
66
|
end
|
65
67
|
|
66
68
|
private
|
69
|
+
|
67
70
|
# month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B")
|
68
71
|
# type: nil (default) or "ordinal"
|
69
72
|
# style: nil (default) or "US"
|
@@ -71,17 +74,18 @@ module Jekyll
|
|
71
74
|
# Returns a stringified date or the empty input.
|
72
75
|
def stringify_date(date, month_type, type = nil, style = nil)
|
73
76
|
return date if date.to_s.empty?
|
77
|
+
|
74
78
|
time = time(date)
|
75
79
|
if type == "ordinal"
|
76
80
|
day = time.day
|
77
81
|
ordinal_day = "#{day}#{ordinal(day)}"
|
78
82
|
return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US"
|
83
|
+
|
79
84
|
return time.strftime("#{ordinal_day} #{month_type} %Y")
|
80
85
|
end
|
81
86
|
time.strftime("%d #{month_type} %Y")
|
82
87
|
end
|
83
88
|
|
84
|
-
private
|
85
89
|
def ordinal(number)
|
86
90
|
return "th" if (11..13).cover?(number)
|
87
91
|
|
@@ -93,12 +97,11 @@ module Jekyll
|
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
96
|
-
private
|
97
100
|
def time(input)
|
98
101
|
date = Liquid::Utils.to_date(input)
|
99
102
|
unless date.respond_to?(:to_time)
|
100
103
|
raise Errors::InvalidDateError,
|
101
|
-
|
104
|
+
"Invalid Date: '#{input.inspect}' is not a valid datetime."
|
102
105
|
end
|
103
106
|
date.to_time.dup.localtime
|
104
107
|
end
|
@@ -41,16 +41,15 @@ module Jekyll
|
|
41
41
|
end
|
42
42
|
|
43
43
|
private
|
44
|
+
|
44
45
|
def parse_expression(str)
|
45
46
|
Liquid::Variable.new(str, Liquid::ParseContext.new)
|
46
47
|
end
|
47
48
|
|
48
|
-
private
|
49
49
|
def groupable?(element)
|
50
50
|
element.respond_to?(:group_by)
|
51
51
|
end
|
52
52
|
|
53
|
-
private
|
54
53
|
def grouped_array(groups)
|
55
54
|
groups.each_with_object([]) do |item, array|
|
56
55
|
array << {
|
@@ -10,10 +10,13 @@ module Jekyll
|
|
10
10
|
# Returns the absolute URL as a String.
|
11
11
|
def absolute_url(input)
|
12
12
|
return if input.nil?
|
13
|
+
|
13
14
|
input = input.url if input.respond_to?(:url)
|
14
15
|
return input if Addressable::URI.parse(input.to_s).absolute?
|
16
|
+
|
15
17
|
site = @context.registers[:site]
|
16
18
|
return relative_url(input) if site.config["url"].nil?
|
19
|
+
|
17
20
|
Addressable::URI.parse(
|
18
21
|
site.config["url"].to_s + relative_url(input)
|
19
22
|
).normalize.to_s
|
@@ -27,6 +30,7 @@ module Jekyll
|
|
27
30
|
# Returns a URL relative to the domain root as a String.
|
28
31
|
def relative_url(input)
|
29
32
|
return if input.nil?
|
33
|
+
|
30
34
|
input = input.url if input.respond_to?(:url)
|
31
35
|
return input if Addressable::URI.parse(input.to_s).absolute?
|
32
36
|
|
@@ -43,6 +47,7 @@ module Jekyll
|
|
43
47
|
# Returns a URL with the trailing `/index.html` removed
|
44
48
|
def strip_index(input)
|
45
49
|
return if input.nil? || input.to_s.empty?
|
50
|
+
|
46
51
|
input.sub(%r!/index\.html?$!, "/")
|
47
52
|
end
|
48
53
|
|
@@ -55,9 +60,9 @@ module Jekyll
|
|
55
60
|
|
56
61
|
def ensure_leading_slash(input)
|
57
62
|
return input if input.nil? || input.empty? || input.start_with?("/")
|
63
|
+
|
58
64
|
"/#{input}"
|
59
65
|
end
|
60
|
-
|
61
66
|
end
|
62
67
|
end
|
63
68
|
end
|
data/lib/jekyll/filters.rb
CHANGED
@@ -169,6 +169,7 @@ module Jekyll
|
|
169
169
|
def where(input, property, value)
|
170
170
|
return input if property.nil? || value.nil?
|
171
171
|
return input unless input.respond_to?(:select)
|
172
|
+
|
172
173
|
input = input.values if input.is_a?(Hash)
|
173
174
|
input_id = input.hash
|
174
175
|
|
@@ -195,6 +196,7 @@ module Jekyll
|
|
195
196
|
# Returns the filtered array of objects
|
196
197
|
def where_exp(input, variable, expression)
|
197
198
|
return input unless input.respond_to?(:select)
|
199
|
+
|
198
200
|
input = input.values if input.is_a?(Hash) # FIXME
|
199
201
|
|
200
202
|
condition = parse_condition(expression)
|
@@ -214,6 +216,7 @@ module Jekyll
|
|
214
216
|
def to_integer(input)
|
215
217
|
return 1 if input == true
|
216
218
|
return 0 if input == false
|
219
|
+
|
217
220
|
input.to_i
|
218
221
|
end
|
219
222
|
|
@@ -225,9 +228,8 @@ module Jekyll
|
|
225
228
|
#
|
226
229
|
# Returns the filtered array of objects
|
227
230
|
def sort(input, property = nil, nils = "first")
|
228
|
-
if input.nil?
|
229
|
-
|
230
|
-
end
|
231
|
+
raise ArgumentError, "Cannot sort a null object." if input.nil?
|
232
|
+
|
231
233
|
if property.nil?
|
232
234
|
input.sort
|
233
235
|
else
|
@@ -246,6 +248,7 @@ module Jekyll
|
|
246
248
|
|
247
249
|
def pop(array, num = 1)
|
248
250
|
return array unless array.is_a?(Array)
|
251
|
+
|
249
252
|
num = Liquid::Utils.to_integer(num)
|
250
253
|
new_ary = array.dup
|
251
254
|
new_ary.pop(num)
|
@@ -254,6 +257,7 @@ module Jekyll
|
|
254
257
|
|
255
258
|
def push(array, input)
|
256
259
|
return array unless array.is_a?(Array)
|
260
|
+
|
257
261
|
new_ary = array.dup
|
258
262
|
new_ary.push(input)
|
259
263
|
new_ary
|
@@ -261,6 +265,7 @@ module Jekyll
|
|
261
265
|
|
262
266
|
def shift(array, num = 1)
|
263
267
|
return array unless array.is_a?(Array)
|
268
|
+
|
264
269
|
num = Liquid::Utils.to_integer(num)
|
265
270
|
new_ary = array.dup
|
266
271
|
new_ary.shift(num)
|
@@ -269,6 +274,7 @@ module Jekyll
|
|
269
274
|
|
270
275
|
def unshift(array, input)
|
271
276
|
return array unless array.is_a?(Array)
|
277
|
+
|
272
278
|
new_ary = array.dup
|
273
279
|
new_ary.unshift(input)
|
274
280
|
new_ary
|
@@ -276,6 +282,7 @@ module Jekyll
|
|
276
282
|
|
277
283
|
def sample(input, num = 1)
|
278
284
|
return input unless input.respond_to?(:sample)
|
285
|
+
|
279
286
|
num = Liquid::Utils.to_integer(num) rescue 1
|
280
287
|
if num == 1
|
281
288
|
input.sample
|
@@ -301,35 +308,45 @@ module Jekyll
|
|
301
308
|
# We also utilize the Schwartzian transform to make this more efficient.
|
302
309
|
def sort_input(input, property, order)
|
303
310
|
input.map { |item| [item_property(item, property), item] }
|
304
|
-
.sort! do |
|
305
|
-
|
306
|
-
|
311
|
+
.sort! do |a_info, b_info|
|
312
|
+
a_property = a_info.first
|
313
|
+
b_property = b_info.first
|
307
314
|
|
308
|
-
if !
|
315
|
+
if !a_property.nil? && b_property.nil?
|
309
316
|
- order
|
310
|
-
elsif
|
317
|
+
elsif a_property.nil? && !b_property.nil?
|
311
318
|
+ order
|
312
319
|
else
|
313
|
-
|
320
|
+
a_property <=> b_property || a_property.to_s <=> b_property.to_s
|
314
321
|
end
|
315
322
|
end
|
316
323
|
.map!(&:last)
|
317
324
|
end
|
318
325
|
|
319
|
-
private
|
320
326
|
def item_property(item, property)
|
321
|
-
|
322
|
-
|
323
|
-
|
327
|
+
@item_property_cache ||= {}
|
328
|
+
@item_property_cache[property] ||= {}
|
329
|
+
@item_property_cache[property][item] ||= begin
|
330
|
+
if item.respond_to?(:to_liquid)
|
331
|
+
property.to_s.split(".").reduce(item.to_liquid) do |subvalue, attribute|
|
332
|
+
parse_sort_input(subvalue[attribute])
|
333
|
+
end
|
334
|
+
elsif item.respond_to?(:data)
|
335
|
+
parse_sort_input(item.data[property.to_s])
|
336
|
+
else
|
337
|
+
parse_sort_input(item[property.to_s])
|
324
338
|
end
|
325
|
-
elsif item.respond_to?(:data)
|
326
|
-
item.data[property.to_s]
|
327
|
-
else
|
328
|
-
item[property.to_s]
|
329
339
|
end
|
330
340
|
end
|
331
341
|
|
332
|
-
|
342
|
+
# return numeric values as numbers for proper sorting
|
343
|
+
def parse_sort_input(property)
|
344
|
+
number_like = %r!\A\s*-?(?:\d+\.?\d*|\.\d+)\s*\Z!
|
345
|
+
return property.to_f if property =~ number_like
|
346
|
+
|
347
|
+
property
|
348
|
+
end
|
349
|
+
|
333
350
|
def as_liquid(item)
|
334
351
|
case item
|
335
352
|
when Hash
|
@@ -352,25 +369,49 @@ module Jekyll
|
|
352
369
|
end
|
353
370
|
end
|
354
371
|
|
372
|
+
# ----------- The following set of code was *adapted* from Liquid::If
|
373
|
+
# ----------- ref: https://git.io/vp6K6
|
374
|
+
|
355
375
|
# Parse a string to a Liquid Condition
|
356
|
-
private
|
357
376
|
def parse_condition(exp)
|
358
|
-
parser
|
359
|
-
|
360
|
-
operator = parser.consume?(:comparison)
|
361
|
-
condition =
|
362
|
-
if operator
|
363
|
-
Liquid::Condition.new(Liquid::Expression.parse(left_expr),
|
364
|
-
operator,
|
365
|
-
Liquid::Expression.parse(parser.expression))
|
366
|
-
else
|
367
|
-
Liquid::Condition.new(Liquid::Expression.parse(left_expr))
|
368
|
-
end
|
369
|
-
parser.consume(:end_of_string)
|
377
|
+
parser = Liquid::Parser.new(exp)
|
378
|
+
condition = parse_binary_comparison(parser)
|
370
379
|
|
380
|
+
parser.consume(:end_of_string)
|
371
381
|
condition
|
372
382
|
end
|
373
383
|
|
384
|
+
# Generate a Liquid::Condition object from a Liquid::Parser object additionally processing
|
385
|
+
# the parsed expression based on whether the expression consists of binary operations with
|
386
|
+
# Liquid operators `and` or `or`
|
387
|
+
#
|
388
|
+
# - parser: an instance of Liquid::Parser
|
389
|
+
#
|
390
|
+
# Returns an instance of Liquid::Condition
|
391
|
+
def parse_binary_comparison(parser)
|
392
|
+
parse_comparison(parser).tap do |condition|
|
393
|
+
binary_operator = parser.id?("and") || parser.id?("or")
|
394
|
+
condition.send(binary_operator, parse_comparison(parser)) if binary_operator
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed
|
399
|
+
# expression involves a "comparison" operator (e.g. <, ==, >, !=, etc)
|
400
|
+
#
|
401
|
+
# - parser: an instance of Liquid::Parser
|
402
|
+
#
|
403
|
+
# Returns an instance of Liquid::Condition
|
404
|
+
def parse_comparison(parser)
|
405
|
+
left_operand = Liquid::Expression.parse(parser.expression)
|
406
|
+
operator = parser.consume?(:comparison)
|
407
|
+
|
408
|
+
# No comparison-operator detected. Initialize a Liquid::Condition using only left operand
|
409
|
+
return Liquid::Condition.new(left_operand) unless operator
|
410
|
+
|
411
|
+
# Parse what remained after extracting the left operand and the `:comparison` operator
|
412
|
+
# and initialize a Liquid::Condition object using the operands and the comparison-operator
|
413
|
+
Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression))
|
414
|
+
end
|
374
415
|
end
|
375
416
|
end
|
376
417
|
|
@@ -10,6 +10,11 @@ module Jekyll
|
|
10
10
|
# Initializes a new instance.
|
11
11
|
def initialize(site)
|
12
12
|
@site = site
|
13
|
+
reset
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset
|
17
|
+
@glob_cache = {}
|
13
18
|
end
|
14
19
|
|
15
20
|
def update_deprecated_types(set)
|
@@ -36,6 +41,7 @@ module Jekyll
|
|
36
41
|
def ensure_time!(set)
|
37
42
|
return set unless set.key?("values") && set["values"].key?("date")
|
38
43
|
return set if set["values"]["date"].is_a?(Time)
|
44
|
+
|
39
45
|
set["values"]["date"] = Utils.parse_date(
|
40
46
|
set["values"]["date"],
|
41
47
|
"An invalid date format was found in a front-matter default set: #{set}"
|
@@ -92,39 +98,44 @@ module Jekyll
|
|
92
98
|
# path - the path to check for
|
93
99
|
# type - the type (:post, :page or :draft) to check for
|
94
100
|
#
|
95
|
-
# Returns true if the scope applies to the given
|
101
|
+
# Returns true if the scope applies to the given type and path
|
96
102
|
def applies?(scope, path, type)
|
97
|
-
|
103
|
+
applies_type?(scope, type) && applies_path?(scope, path)
|
98
104
|
end
|
99
105
|
|
100
|
-
# rubocop:disable Metrics/AbcSize
|
101
106
|
def applies_path?(scope, path)
|
102
107
|
return true if !scope.key?("path") || scope["path"].empty?
|
103
108
|
|
104
109
|
sanitized_path = Pathname.new(sanitize_path(path))
|
105
|
-
site_path = Pathname.new(@site.source)
|
106
110
|
rel_scope_path = Pathname.new(scope["path"])
|
107
|
-
abs_scope_path = File.join(@site.source, rel_scope_path)
|
108
111
|
|
109
112
|
if scope["path"].to_s.include?("*")
|
110
|
-
|
111
|
-
scope_path = Pathname.new(scope_path).relative_path_from(site_path)
|
112
|
-
scope_path = strip_collections_dir(scope_path)
|
113
|
-
Jekyll.logger.debug "Globbed Scope Path:", scope_path
|
114
|
-
return true if path_is_subpath?(sanitized_path, scope_path)
|
115
|
-
end
|
116
|
-
false
|
113
|
+
glob_scope(sanitized_path, rel_scope_path)
|
117
114
|
else
|
118
115
|
path_is_subpath?(sanitized_path, strip_collections_dir(rel_scope_path))
|
119
116
|
end
|
120
117
|
end
|
121
|
-
|
118
|
+
|
119
|
+
def glob_scope(sanitized_path, rel_scope_path)
|
120
|
+
site_source = Pathname.new(@site.source)
|
121
|
+
abs_scope_path = site_source.join(rel_scope_path).to_s
|
122
|
+
|
123
|
+
glob_cache(abs_scope_path).each do |scope_path|
|
124
|
+
scope_path = Pathname.new(scope_path).relative_path_from(site_source)
|
125
|
+
scope_path = strip_collections_dir(scope_path)
|
126
|
+
Jekyll.logger.debug "Globbed Scope Path:", scope_path
|
127
|
+
return true if path_is_subpath?(sanitized_path, scope_path)
|
128
|
+
end
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
def glob_cache(path)
|
133
|
+
@glob_cache[path] ||= Dir.glob(path)
|
134
|
+
end
|
122
135
|
|
123
136
|
def path_is_subpath?(path, parent_path)
|
124
137
|
path.ascend do |ascended_path|
|
125
|
-
if ascended_path.to_s == parent_path.to_s
|
126
|
-
return true
|
127
|
-
end
|
138
|
+
return true if ascended_path.to_s == parent_path.to_s
|
128
139
|
end
|
129
140
|
|
130
141
|
false
|
@@ -134,6 +145,7 @@ module Jekyll
|
|
134
145
|
collections_dir = @site.config["collections_dir"]
|
135
146
|
slashed_coll_dir = "#{collections_dir}/"
|
136
147
|
return path if collections_dir.empty? || !path.to_s.start_with?(slashed_coll_dir)
|
148
|
+
|
137
149
|
path.sub(slashed_coll_dir, "")
|
138
150
|
end
|
139
151
|
|
@@ -188,8 +200,12 @@ module Jekyll
|
|
188
200
|
#
|
189
201
|
# Returns an array of hashes
|
190
202
|
def matching_sets(path, type)
|
191
|
-
|
192
|
-
|
203
|
+
@matched_set_cache ||= {}
|
204
|
+
@matched_set_cache[path] ||= {}
|
205
|
+
@matched_set_cache[path][type] ||= begin
|
206
|
+
valid_sets.select do |set|
|
207
|
+
!set.key?("scope") || applies?(set["scope"], path, type)
|
208
|
+
end
|
193
209
|
end
|
194
210
|
end
|
195
211
|
|
@@ -216,7 +232,7 @@ module Jekyll
|
|
216
232
|
|
217
233
|
# Sanitizes the given path by removing a leading and adding a trailing slash
|
218
234
|
|
219
|
-
SANITIZATION_REGEX = %r!\A/|(?<=[^/])\z
|
235
|
+
SANITIZATION_REGEX = %r!\A/|(?<=[^/])\z!.freeze
|
220
236
|
|
221
237
|
def sanitize_path(path)
|
222
238
|
if path.nil? || path.empty?
|