jekyll_href 1.1.0 → 1.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +74 -12
- data/CHANGELOG.md +15 -0
- data/README.md +153 -16
- data/Rakefile +2 -1
- data/jekyll_href.gemspec +4 -6
- data/lib/hash_array.rb +32 -0
- data/lib/href_summary_tag.rb +79 -0
- data/lib/href_tag.rb +214 -0
- data/lib/jekyll_href/version.rb +1 -1
- data/lib/jekyll_href.rb +6 -174
- data/spec/hash_array_spec.rb +43 -0
- data/spec/href_spec.rb +68 -123
- data/spec/spec_helper.rb +68 -3
- data/spec/status_persistence.txt +27 -13
- metadata +24 -36
- data/lib/jekyll_tag_helper2.rb +0 -94
data/lib/href_tag.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'jekyll_all_collections'
|
2
|
+
require 'jekyll_plugin_logger'
|
3
|
+
require 'jekyll_plugin_support'
|
4
|
+
require 'liquid'
|
5
|
+
require_relative 'jekyll_href/version'
|
6
|
+
require_relative 'hash_array'
|
7
|
+
|
8
|
+
# @author Copyright 2020 Michael Slinn
|
9
|
+
# @license SPDX-License-Identifier: Apache-2.0
|
10
|
+
# Generates an href.
|
11
|
+
|
12
|
+
module HrefTag
|
13
|
+
MiniHref = Struct.new(:follow, :html, :link, :line_number, :link_save, :path, :summary_exclude, :summary_href, keyword_init: true)
|
14
|
+
|
15
|
+
# Implements href Jekyll tag
|
16
|
+
class HrefTag < JekyllSupport::JekyllTag # rubocop:disable Metrics/ClassLength
|
17
|
+
attr_reader :follow, :helper, :line_number, :link_save, :match, :page, :path, :site,
|
18
|
+
:summary, :summary_exclude, :summary_href, :target, :text, :url
|
19
|
+
attr_accessor :link
|
20
|
+
|
21
|
+
include JekyllHrefVersion
|
22
|
+
include HashArray
|
23
|
+
include Comparable
|
24
|
+
|
25
|
+
def <=>(other)
|
26
|
+
return nil unless other.is_a?(self.class)
|
27
|
+
|
28
|
+
[follow, match, path, target, text] <=> [other.follow, other.match, other.path, other.target, other.text]
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"On line #{line_number} of #{path}: #{follow} #{match} #{target} #{link} => '#{text}'"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Method prescribed by the Jekyll plugin lifecycle.
|
36
|
+
# @param liquid_context [Liquid::Context]
|
37
|
+
# @return [String]
|
38
|
+
def render_impl
|
39
|
+
globals_initial
|
40
|
+
linkk = compute_linkk
|
41
|
+
linkk = replace_vars(linkk)
|
42
|
+
linkk.delete_prefix('./') # normalize relative links
|
43
|
+
@url = linkk if @url
|
44
|
+
@link_save = linkk
|
45
|
+
@helper_save = @helper.clone
|
46
|
+
globals_update(@helper.argv, linkk) # Sets @link and @text, might clear @follow and @target
|
47
|
+
handle_match if @match
|
48
|
+
save_summary
|
49
|
+
"<a href='#{@link}'#{@target}#{@follow}>#{@text}</a>"
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def save_summary
|
55
|
+
return if @summary_exclude || @link_save.start_with?('mailto:') || @link_save.start_with?('#')
|
56
|
+
|
57
|
+
@summary = @summary.to_s.empty? ? @text : @summary.to_s
|
58
|
+
@summary = @summary[0].upcase + @summary[1..]
|
59
|
+
@summary_href = "<a href='#{@link_save}'#{@target}#{@follow}>#{@summary}</a>"
|
60
|
+
mini_href = MiniHref.new(
|
61
|
+
follow: @follow,
|
62
|
+
html: @summary_href,
|
63
|
+
line_number: @line_number,
|
64
|
+
link: @link_save,
|
65
|
+
link_save: @link_save,
|
66
|
+
path: @path,
|
67
|
+
summary_exclude: @summary_exclude,
|
68
|
+
summary_href: @summary_href
|
69
|
+
)
|
70
|
+
if @link_save.start_with? 'http'
|
71
|
+
add_global_link_for_page mini_href
|
72
|
+
else
|
73
|
+
add_local_link_for_page mini_href
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Does not look at or compute @link
|
78
|
+
def compute_linkk
|
79
|
+
return @link if @link
|
80
|
+
|
81
|
+
linkk = @url
|
82
|
+
if linkk.nil? || !linkk
|
83
|
+
linkk = @helper.argv&.shift
|
84
|
+
@helper.params&.shift
|
85
|
+
@helper.keys_values&.delete(linkk)
|
86
|
+
dump_linkk_relations(linkk) if linkk.nil?
|
87
|
+
elsif @url.to_s.empty?
|
88
|
+
dump_linkk_relations(linkk)
|
89
|
+
end
|
90
|
+
linkk
|
91
|
+
end
|
92
|
+
|
93
|
+
def dump_linkk_relations(linkk)
|
94
|
+
msg = <<~END_MESSAGE
|
95
|
+
jekyll_href error: no url was provided on #{@path}:#{@line_number}.
|
96
|
+
@helper.markup=#{@helper.markup}
|
97
|
+
@helper.argv='#{@helper.argv}'
|
98
|
+
linkk='#{linkk}'
|
99
|
+
@match='#{@match}'
|
100
|
+
@url='#{@url}'
|
101
|
+
@follow='#{@follow}
|
102
|
+
@target='#{@target}'
|
103
|
+
END_MESSAGE
|
104
|
+
abort msg.red
|
105
|
+
end
|
106
|
+
|
107
|
+
# Sets @follow, @helper, @match, @path, @shy, @target, @url, @wbr
|
108
|
+
def globals_initial
|
109
|
+
@path = @page['path']
|
110
|
+
AllCollectionsHooks.compute(@site)
|
111
|
+
|
112
|
+
@follow = @helper.parameter_specified?('follow') ? '' : " rel='nofollow'"
|
113
|
+
@match = @helper.parameter_specified? 'match'
|
114
|
+
@blank = @helper.parameter_specified? 'blank'
|
115
|
+
@summary_exclude = @helper.parameter_specified? 'summary_exclude'
|
116
|
+
@shy = @helper.parameter_specified? 'shy'
|
117
|
+
@summary = @helper.parameter_specified? 'summary'
|
118
|
+
@target = @blank ? " target='_blank'" : nil
|
119
|
+
@target ||= @helper.parameter_specified?('notarget') ? '' : " target='_blank'"
|
120
|
+
@url = @helper.parameter_specified? 'url'
|
121
|
+
@wbr = @helper.parameter_specified? 'wbr'
|
122
|
+
end
|
123
|
+
|
124
|
+
# Might set @follow, @linkk, @target, and @text
|
125
|
+
def globals_update(tokens, linkk)
|
126
|
+
if linkk.start_with? 'mailto:'
|
127
|
+
@link = linkk
|
128
|
+
@target = @follow = ''
|
129
|
+
@text = @helper.argv.join(' ')
|
130
|
+
if @text.empty?
|
131
|
+
text = linkk.delete_prefix('mailto:')
|
132
|
+
@text = "<code>#{text}</code>"
|
133
|
+
end
|
134
|
+
return
|
135
|
+
else
|
136
|
+
@text = tokens.join(' ').strip
|
137
|
+
if @text.to_s.empty?
|
138
|
+
text = linkk
|
139
|
+
text = linkk.gsub('/', '/­') if @shy
|
140
|
+
text = linkk.gsub('/', '/<wbr>') if @wbr
|
141
|
+
@text = "<code>#{text}</code>"
|
142
|
+
@link = linkk.start_with?('http') ? linkk : "https://#{linkk}"
|
143
|
+
else
|
144
|
+
@link = if @shy
|
145
|
+
linkk.gsub('/', '/­')
|
146
|
+
elsif @wbr
|
147
|
+
linkk.gsub('/', '/<wbr>')
|
148
|
+
else
|
149
|
+
linkk
|
150
|
+
end
|
151
|
+
end
|
152
|
+
@link_save = @link
|
153
|
+
end
|
154
|
+
|
155
|
+
return if @link.start_with? 'http'
|
156
|
+
|
157
|
+
@follow = ''
|
158
|
+
@target = '' unless @blank
|
159
|
+
end
|
160
|
+
|
161
|
+
def handle_match
|
162
|
+
match_post
|
163
|
+
@follow = ''
|
164
|
+
@target = '' unless @blank
|
165
|
+
end
|
166
|
+
|
167
|
+
# Might set @link and @text
|
168
|
+
def match_post
|
169
|
+
config = @site.config['href']
|
170
|
+
@die_if_nomatch = !config.nil? && config['nomatch'] && config['nomatch'] == 'fatal'
|
171
|
+
@path, @fragment = @link.split('#')
|
172
|
+
|
173
|
+
@logger.debug do
|
174
|
+
<<~END_DEBUG
|
175
|
+
@link=#{@link}
|
176
|
+
@site.posts.docs[0].url = #{@site.posts.docs[0].url}
|
177
|
+
@site.posts.docs[0].path = #{@site.posts.docs[0].path}
|
178
|
+
END_DEBUG
|
179
|
+
end
|
180
|
+
|
181
|
+
all_urls = @site.all_collections.map(&:url)
|
182
|
+
compute_link_and_text(all_urls)
|
183
|
+
end
|
184
|
+
|
185
|
+
def compute_link_and_text(all_urls)
|
186
|
+
url_matches = all_urls.select { |url| url&.include? @path }
|
187
|
+
case url_matches.length
|
188
|
+
when 0
|
189
|
+
abort "href error: No url matches '#{@link}'" if @die_if_nomatch
|
190
|
+
@link_save = @link = '#'
|
191
|
+
@text = "<i>#{@link} is not available</i>"
|
192
|
+
when 1
|
193
|
+
@link = url_matches.first
|
194
|
+
@link = "#{@link}##{@fragment}" if @fragment
|
195
|
+
@link_save = @link
|
196
|
+
else
|
197
|
+
abort "Error: More than one url matched '#{@path}': #{url_matches.join(', ')}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Replace names in plugin-vars with values
|
202
|
+
def replace_vars(text)
|
203
|
+
variables = @site.config['plugin-vars']
|
204
|
+
return text unless variables
|
205
|
+
|
206
|
+
variables.each do |name, value|
|
207
|
+
text = text.gsub "{{#{name}}}", value
|
208
|
+
end
|
209
|
+
text
|
210
|
+
end
|
211
|
+
|
212
|
+
JekyllPluginHelper.register(self, 'href')
|
213
|
+
end
|
214
|
+
end
|
data/lib/jekyll_href/version.rb
CHANGED
data/lib/jekyll_href.rb
CHANGED
@@ -1,177 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'liquid'
|
4
|
-
require_relative 'jekyll_href/version'
|
5
|
-
require_relative './jekyll_tag_helper2'
|
1
|
+
require_relative 'href_tag'
|
2
|
+
require_relative 'href_summary_tag'
|
6
3
|
|
7
|
-
|
8
|
-
# @license SPDX-License-Identifier: Apache-2.0
|
9
|
-
# Generates an href.
|
4
|
+
HrefError = Class.new(Liquid::Error)
|
10
5
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
attr_accessor :link
|
15
|
-
|
16
|
-
# @param tag_name [String] is the name of the tag, which we already know.
|
17
|
-
# @param markup [String] the arguments from the web page.
|
18
|
-
# @param _tokens [Liquid::ParseContext] tokenized command line
|
19
|
-
# By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
|
20
|
-
# a boolean parameter that determines if error messages should display the line number the error occurred.
|
21
|
-
# This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
|
22
|
-
# See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
|
23
|
-
# @return [void]
|
24
|
-
def initialize(tag_name, markup, _tokens)
|
25
|
-
super
|
26
|
-
markup = '' if markup.nil?
|
27
|
-
markup.strip!
|
28
|
-
|
29
|
-
@logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
|
30
|
-
@helper = JekyllTagHelper2.new(tag_name, markup, @logger)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Method prescribed by the Jekyll plugin lifecycle.
|
34
|
-
# @param liquid_context [Liquid::Context]
|
35
|
-
# @return [String]
|
36
|
-
def render(liquid_context)
|
37
|
-
super
|
38
|
-
globals_initial(liquid_context)
|
39
|
-
linkk = compute_linkk
|
40
|
-
linkk = replace_vars(linkk)
|
41
|
-
@link_save = linkk
|
42
|
-
@helper_save = @helper.clone
|
43
|
-
globals_update(@helper.argv, linkk) # Sets @link and @text, might clear @follow and @target
|
44
|
-
handle_match if @match
|
45
|
-
"<a href='#{@link}'#{@target}#{@follow}>#{@text}</a>"
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
# Does not look at or compute @link
|
51
|
-
def compute_linkk
|
52
|
-
return @link if @link
|
53
|
-
|
54
|
-
linkk = @url
|
55
|
-
if linkk.nil? || !linkk
|
56
|
-
linkk = @helper.argv&.shift
|
57
|
-
@helper.params&.shift
|
58
|
-
@helper.keys_values&.delete(linkk)
|
59
|
-
dump_linkk_relations(linkk) if linkk.nil?
|
60
|
-
elsif @url.to_s.empty?
|
61
|
-
dump_linkk_relations(linkk)
|
62
|
-
end
|
63
|
-
linkk
|
64
|
-
end
|
65
|
-
|
66
|
-
def dump_linkk_relations(linkk)
|
67
|
-
msg = <<~END_MESSAGE
|
68
|
-
jekyll_href error: no url was provided on #{@path}:#{@line_number}.
|
69
|
-
@helper.markup=#{@helper.markup}
|
70
|
-
@helper.argv='#{@helper.argv}'
|
71
|
-
linkk='#{linkk}'
|
72
|
-
@match='#{@match}'
|
73
|
-
@url='#{@url}'
|
74
|
-
@follow='#{@follow}
|
75
|
-
@target='#{@target}'
|
76
|
-
END_MESSAGE
|
77
|
-
abort msg.red
|
78
|
-
end
|
79
|
-
|
80
|
-
def globals_initial(liquid_context)
|
81
|
-
# Sets @follow, @helper, @match, @page, @path, @site, @target, @url
|
82
|
-
@helper.liquid_context = liquid_context
|
83
|
-
|
84
|
-
@page = liquid_context.registers[:page]
|
85
|
-
@path = @page['path']
|
86
|
-
@site = liquid_context.registers[:site]
|
87
|
-
AllCollectionsHooks.compute(@site)
|
88
|
-
|
89
|
-
@follow = @helper.parameter_specified?('follow') ? '' : " rel='nofollow'"
|
90
|
-
@match = @helper.parameter_specified?('match')
|
91
|
-
@blank = @helper.parameter_specified?('blank')
|
92
|
-
@target = @blank ? " target='_blank'" : nil
|
93
|
-
@target ||= @helper.parameter_specified?('notarget') ? '' : " target='_blank'"
|
94
|
-
@url = @helper.parameter_specified?('url')
|
95
|
-
end
|
96
|
-
|
97
|
-
# Might set @follow, @linkk, @target, and @text
|
98
|
-
def globals_update(tokens, linkk)
|
99
|
-
if linkk.start_with? 'mailto:'
|
100
|
-
@link = linkk
|
101
|
-
@target = @follow = ''
|
102
|
-
@text = @helper.argv.join(' ')
|
103
|
-
if @text.empty?
|
104
|
-
text = linkk.delete_prefix('mailto:')
|
105
|
-
@text = "<code>#{text}</code>"
|
106
|
-
end
|
107
|
-
return
|
108
|
-
else
|
109
|
-
@text = tokens.join(' ').strip
|
110
|
-
if @text.to_s.empty?
|
111
|
-
@text = "<code>#{linkk}</code>"
|
112
|
-
@link = "https://#{linkk}"
|
113
|
-
else
|
114
|
-
@link = linkk
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
return if @link.start_with? 'http'
|
119
|
-
|
120
|
-
@follow = ''
|
121
|
-
@target = '' unless @blank
|
122
|
-
end
|
123
|
-
|
124
|
-
def handle_match
|
125
|
-
match_post
|
126
|
-
@follow = ''
|
127
|
-
@target = '' unless @blank
|
128
|
-
end
|
129
|
-
|
130
|
-
# Might set @link and @text
|
131
|
-
def match_post
|
132
|
-
config = @site.config['href']
|
133
|
-
@die_if_nomatch = !config.nil? && config['nomatch'] && config['nomatch'] == 'fatal'
|
134
|
-
@path, @fragment = @link.split('#')
|
135
|
-
|
136
|
-
@logger.debug do
|
137
|
-
<<~END_DEBUG
|
138
|
-
@link=#{@link}
|
139
|
-
@site.posts.docs[0].url = #{@site.posts.docs[0].url}
|
140
|
-
@site.posts.docs[0].path = #{@site.posts.docs[0].path}
|
141
|
-
END_DEBUG
|
142
|
-
end
|
143
|
-
|
144
|
-
all_urls = @site.all_collections.map(&:url)
|
145
|
-
compute_link_and_text(all_urls)
|
146
|
-
end
|
147
|
-
|
148
|
-
def compute_link_and_text(all_urls)
|
149
|
-
url_matches = all_urls.select { |url| url&.include? @path }
|
150
|
-
case url_matches.length
|
151
|
-
when 0
|
152
|
-
abort "href error: No url matches '#{@link}'" if @die_if_nomatch
|
153
|
-
@link = '#'
|
154
|
-
@text = "<i>#{@link} is not available</i>"
|
155
|
-
when 1
|
156
|
-
@link = url_matches.first
|
157
|
-
@link = "#{@link}\##{@fragment}" if @fragment
|
158
|
-
else
|
159
|
-
abort "Error: More than one url matched '#{@path}': #{url_matches.join(', ')}"
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Replace names in plugin-vars with values
|
164
|
-
def replace_vars(text)
|
165
|
-
variables = @site.config['plugin-vars']
|
166
|
-
return text unless variables
|
167
|
-
|
168
|
-
variables.each do |name, value|
|
169
|
-
text = text.gsub "{{#{name}}}", value
|
170
|
-
end
|
171
|
-
@logger.debug { "@link=#{@link}" }
|
172
|
-
text
|
173
|
-
end
|
6
|
+
module JekyllHrefModule
|
7
|
+
include HrefTag
|
8
|
+
include HrefSummaryTag
|
174
9
|
end
|
175
|
-
|
176
|
-
PluginMetaLogger.instance.info { "Loaded jekyll_href v#{JekyllHrefVersion::VERSION} plugin." }
|
177
|
-
Liquid::Template.register_tag('href', ExternalHref)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HashArraySpec
|
2
|
+
require_relative '../lib/hash_array'
|
3
|
+
|
4
|
+
RSpec.describe HashArray do
|
5
|
+
let(:logger) do
|
6
|
+
PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:parse_context) { TestParseContext.new }
|
10
|
+
|
11
|
+
[1, 2, 3, 4, 11, 22, 33, 44].each do |x|
|
12
|
+
let("href#{x}".to_sym) do
|
13
|
+
domain, where = if x.even?
|
14
|
+
['https://domain.com/', 'External']
|
15
|
+
else
|
16
|
+
['', 'Internal']
|
17
|
+
end
|
18
|
+
href = HrefTag::HrefTag.send(
|
19
|
+
:new,
|
20
|
+
'href',
|
21
|
+
+ "#{domain}path/page#{x}.html #{where} link text #{x}",
|
22
|
+
parse_context
|
23
|
+
)
|
24
|
+
href.render(TestLiquidContext.new)
|
25
|
+
href
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'accumulates arrays of href_tag' do
|
30
|
+
described_class.reset
|
31
|
+
[href1, href3, href11, href33].each { |x| described_class.add_local_link_for_page x }
|
32
|
+
[href2, href4, href22, href44].each { |x| described_class.add_global_link_for_page x }
|
33
|
+
|
34
|
+
local_hrefs = described_class.instance_variable_get :@local_hrefs
|
35
|
+
value = { 'index.html' => [href1, href3, href11, href33] }
|
36
|
+
expect(local_hrefs).to match_array(value)
|
37
|
+
|
38
|
+
global_hrefs = described_class.instance_variable_get :@global_hrefs
|
39
|
+
value = { 'index.html' => [href2, href4, href22, href44] }
|
40
|
+
expect(global_hrefs).to match_array(value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|