jekyll_href 1.1.0 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|