jekyll_plugin_support 0.7.2 → 0.8.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +60 -50
- data/CHANGELOG.md +14 -0
- data/README.md +306 -132
- data/jekyll_plugin_support.gemspec +3 -2
- data/lib/jekyll_plugin_error_handling.rb +48 -0
- data/lib/jekyll_plugin_helper.rb +20 -134
- data/lib/jekyll_plugin_helper_attribution.rb +72 -0
- data/lib/jekyll_plugin_helper_class.rb +33 -0
- data/lib/jekyll_plugin_support/version.rb +1 -1
- data/lib/jekyll_plugin_support.rb +5 -202
- data/lib/jekyll_plugin_support_block.rb +77 -0
- data/lib/jekyll_plugin_support_block_noarg.rb +30 -0
- data/lib/jekyll_plugin_support_class.rb +86 -0
- data/lib/jekyll_plugin_support_tag.rb +75 -0
- data/lib/jekyll_plugin_support_tag_noarg.rb +26 -0
- data/spec/liquid_variable_parsing_spec.rb +34 -0
- data/spec/spec_helper.rb +1 -2
- data/spec/status_persistence.txt +7 -0
- metadata +28 -6
- data/lib/call_chain.rb +0 -54
- data/lib/gem_support.rb +0 -19
@@ -0,0 +1,86 @@
|
|
1
|
+
# Monkey patch StandardError so a new method called shorten_backtrace is added.
|
2
|
+
class StandardError
|
3
|
+
def shorten_backtrace(backtrace_element_count = 3)
|
4
|
+
# self.backtrace = backtrace[0..backtrace_element_count].map do |x|
|
5
|
+
# raise JekyllPluginSupportError, "backtrace contains a #{x.class} with value '#{x}'." unless x.instance_of? String
|
6
|
+
|
7
|
+
# x.gsub(Dir.pwd + '/', './')
|
8
|
+
# end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module JekyllSupport
|
13
|
+
DISPLAYED_CALLS = 8
|
14
|
+
|
15
|
+
def self.error_short_trace(logger, error)
|
16
|
+
error.set_backtrace error.backtrace[0..DISPLAYED_CALLS]
|
17
|
+
logger.error { error }
|
18
|
+
error
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return a new StandardError subclass containing the shorten_backtrace method
|
22
|
+
def define_error
|
23
|
+
Class.new StandardError
|
24
|
+
end
|
25
|
+
module_function :define_error
|
26
|
+
|
27
|
+
JekyllPluginSupportError = define_error
|
28
|
+
|
29
|
+
def self.dump_vars(_logger, liquid_context)
|
30
|
+
page = liquid_context.registers[:page]
|
31
|
+
vars = liquid_context.scopes.map do |scope|
|
32
|
+
scope.map { |name, value| " #{name} = #{value}" }.join("\n")
|
33
|
+
end.join("\n")
|
34
|
+
puts <<~END_MSG
|
35
|
+
#{page['name']} variables after injecting any defined in _config.yml:
|
36
|
+
#{vars}
|
37
|
+
END_MSG
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add variable definitions from _config.yml to liquid_context
|
41
|
+
# Modifies liquid_context in the caller (call by reference)
|
42
|
+
# @return modified liquid_context
|
43
|
+
# See README.md#configuration-variable-definitions
|
44
|
+
# See demo/variables.html
|
45
|
+
def self.inject_vars(_logger, liquid_context)
|
46
|
+
site = liquid_context.registers[:site]
|
47
|
+
|
48
|
+
plugin_variables = site.config['liquid_vars']
|
49
|
+
return liquid_context unless plugin_variables
|
50
|
+
|
51
|
+
scope = liquid_context.scopes.last
|
52
|
+
|
53
|
+
env = site.config['env']
|
54
|
+
mode = env&.key?('JEKYLL_ENV') ? env['JEKYLL_ENV'] : 'development'
|
55
|
+
|
56
|
+
# Set default values
|
57
|
+
plugin_variables&.each do |name, value|
|
58
|
+
scope[name] = value if value.instance_of? String
|
59
|
+
end
|
60
|
+
|
61
|
+
# Override with environment-specific values
|
62
|
+
plugin_variables[mode]&.each do |name, value|
|
63
|
+
scope[name] = value if value.instance_of? String
|
64
|
+
end
|
65
|
+
|
66
|
+
liquid_context
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.lookup_liquid_variables(liquid_context, markup)
|
70
|
+
liquid_context.scopes.each do |scope|
|
71
|
+
scope.each do |name, value|
|
72
|
+
markup = markup.gsub("{{#{name}}}", value.to_s)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
markup
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.warn_short_trace(logger, error)
|
79
|
+
remaining = error.backtrace.length - DISPLAYED_CALLS
|
80
|
+
logger.warn do
|
81
|
+
error.msg + "\n" +
|
82
|
+
error.backtrace.take(DISPLAYED_CALLS).join("\n") +
|
83
|
+
"\n...Remaining #{remaining} call sites elided.\n"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require_relative 'jekyll_plugin_error_handling'
|
3
|
+
|
4
|
+
module JekyllSupport
|
5
|
+
# Base class for Jekyll tags
|
6
|
+
class JekyllTag < Liquid::Tag
|
7
|
+
attr_reader :argument_string, :helper, :line_number, :logger, :page, :site
|
8
|
+
|
9
|
+
include JekyllSupportErrorHandling
|
10
|
+
extend JekyllSupportErrorHandling
|
11
|
+
|
12
|
+
# See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
|
13
|
+
# @param tag_name [String] the name of the tag, which we usually know.
|
14
|
+
# @param argument_string [String] the arguments passed to the tag, as a single string.
|
15
|
+
# @param parse_context [Liquid::ParseContext] hash that stores Liquid options.
|
16
|
+
# By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
|
17
|
+
# a boolean parameter that determines if error messages should display the line number the error occurred.
|
18
|
+
# This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
|
19
|
+
# See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
|
20
|
+
# @return [void]
|
21
|
+
def initialize(tag_name, markup, parse_context)
|
22
|
+
super
|
23
|
+
@tag_name = tag_name
|
24
|
+
raise JekyllPluginSupportError, "markup is a #{markup.class} with value '#{markup}'." unless markup.instance_of? String
|
25
|
+
|
26
|
+
@argument_string = markup
|
27
|
+
@logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
|
28
|
+
@logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
|
29
|
+
@helper = JekyllPluginHelper.new(tag_name, @argument_string, @logger, respond_to?(:no_arg_parsing))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Method prescribed by the Jekyll plugin lifecycle.
|
33
|
+
def render(liquid_context)
|
34
|
+
return if @helper.excerpt_caller
|
35
|
+
|
36
|
+
@helper.liquid_context = JekyllSupport.inject_vars @logger, liquid_context
|
37
|
+
|
38
|
+
@envs = liquid_context.environments.first
|
39
|
+
@page = liquid_context.registers[:page]
|
40
|
+
@scopes = liquid_context.scopes
|
41
|
+
@site = liquid_context.registers[:site]
|
42
|
+
|
43
|
+
@config = @site.config
|
44
|
+
@tag_config = @config[@tag_name]
|
45
|
+
@jps = @config['jekyll_plugin_support']
|
46
|
+
@pry_on_standard_error = @jps['pry_on_standard_error'] || false if @jps
|
47
|
+
|
48
|
+
# @envs.keys are :content, :highlighter_prefix, :highlighter_suffix, :jekyll, :layout, :page, :paginator, :site, :theme
|
49
|
+
@layout = @envs[:layout]
|
50
|
+
@paginator = @envs[:paginator]
|
51
|
+
@theme = @envs[:theme]
|
52
|
+
|
53
|
+
@mode = @config['env']&.key?('JEKYLL_ENV') ? @config['env']['JEKYLL_ENV'] : 'development'
|
54
|
+
|
55
|
+
@helper.reinitialize JekyllSupport.lookup_liquid_variables liquid_context, @argument_string
|
56
|
+
|
57
|
+
render_impl
|
58
|
+
rescue StandardError => e
|
59
|
+
e.shorten_backtrace
|
60
|
+
msg = format_error_message e.full_message
|
61
|
+
@logger.error msg
|
62
|
+
binding.pry if @pry_on_standard_error # rubocop:disable Lint/Debugger
|
63
|
+
raise e if @die_on_standard_error
|
64
|
+
|
65
|
+
"<div class='standard_error'>#{e.class}: #{msg}</div>"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Jekyll plugins must override this method, not render, so their plugin can be tested more easily
|
69
|
+
# The following variables are predefined:
|
70
|
+
# @argument_string, @config, @envs, @helper, @layout, @logger, @mode, @page, @paginator, @site, @tag_name and @theme
|
71
|
+
def render_impl
|
72
|
+
abort "#{self.class}.render_impl for tag #{@tag_name} must be overridden, but it was not."
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'jekyll_plugin_error_handling'
|
2
|
+
|
3
|
+
module JekyllSupport
|
4
|
+
class JekyllTagNoArgParsing < JekyllTag
|
5
|
+
attr_reader :argument_string, :helper, :line_number, :logger, :page, :site
|
6
|
+
|
7
|
+
include JekyllSupportErrorHandling
|
8
|
+
extend JekyllSupportErrorHandling
|
9
|
+
|
10
|
+
def initialize(tag_name, argument_string, parse_context)
|
11
|
+
class << self
|
12
|
+
include NoArgParsing
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
@logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Jekyll plugins must override this method, not render, so their plugin can be tested more easily
|
20
|
+
# The following variables are predefined:
|
21
|
+
# @argument_string, @config, @envs, @helper, @layout, @logger, @mode, @page, @paginator, @site, @tag_name and @theme
|
22
|
+
def render_impl
|
23
|
+
abort "#{self.class}.render_impl for tag #{@tag_name} must be overridden, but it was not."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'jekyll_plugin_logger'
|
2
|
+
require 'rspec/match_ignoring_whitespace'
|
3
|
+
require_relative '../lib/jekyll_plugin_support'
|
4
|
+
require_relative '../lib/jekyll_plugin_support_spec_support'
|
5
|
+
|
6
|
+
class LiquidVariableParsing
|
7
|
+
# @return copy of str with references to defined variables replaced by the values of the variables
|
8
|
+
def variable_replace(str, scopes)
|
9
|
+
result = str.clone
|
10
|
+
match_data_list = str.to_enum(:scan, /{{[a-z_][a-zA-Z_0-9]*}}/).map { Regexp.last_match }.reverse
|
11
|
+
match_data_list.each do |md|
|
12
|
+
from = md.begin(0)
|
13
|
+
to = md.end(0) - 1
|
14
|
+
ref = str[from..to]
|
15
|
+
name = ref[2..-3]
|
16
|
+
scopes.each do |scope|
|
17
|
+
value = scope.key?(name) ? scope[name] : ref
|
18
|
+
# puts "str=#{str}; from=#{from}; to=#{to}; name=#{name} value=#{value}"
|
19
|
+
result[from..to] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec.describe JekyllPluginHelper do
|
26
|
+
it 'substitutes variable references for values without recursion' do
|
27
|
+
scopes = [{ 'a' => '{{', 'b' => 'asdf', 'c' => '}}' }]
|
28
|
+
str = '{{a}}{{b}}{{c}} This should be unchanged: {{d}}'
|
29
|
+
new_str = variable_replace(str, scopes)
|
30
|
+
expect(str).to start_with('{{a}}')
|
31
|
+
expect(new_str).to be('{{asdf}} This should be unchanged: {{d}}')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,9 +2,8 @@ require 'jekyll'
|
|
2
2
|
require_relative '../lib/jekyll_plugin_support'
|
3
3
|
|
4
4
|
RSpec.configure do |config|
|
5
|
-
config.
|
5
|
+
config.filter_run_when_matching focus: true
|
6
6
|
# config.order = 'random'
|
7
|
-
config.run_all_when_everything_filtered = true
|
8
7
|
|
9
8
|
# See https://relishapp.com/rspec/rspec-core/docs/command-line/only-failures
|
10
9
|
config.example_status_persistence_file_path = 'spec/status_persistence.txt'
|
data/spec/status_persistence.txt
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
example_id | status | run_time |
|
2
|
+
------------------------------------------------ | ------ | --------------- |
|
3
|
+
./spec/jekyll_plugin_helper_options_spec.rb[1:1] | failed | 0.00009 seconds |
|
4
|
+
./spec/jekyll_plugin_helper_options_spec.rb[1:2] | failed | 0.00004 seconds |
|
5
|
+
./spec/jekyll_plugin_helper_options_spec.rb[1:3] | failed | 0.00002 seconds |
|
6
|
+
./spec/jekyll_plugin_helper_options_spec.rb[1:4] | failed | 0.00002 seconds |
|
7
|
+
./spec/liquid_variable_parsing_spec.rb[1:1] | failed | 0.00003 seconds |
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll_plugin_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Slinn
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: facets
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description:
|
70
84
|
email:
|
71
85
|
- mslinn@mslinn.com
|
@@ -80,13 +94,20 @@ files:
|
|
80
94
|
- README.md
|
81
95
|
- Rakefile
|
82
96
|
- jekyll_plugin_support.gemspec
|
83
|
-
- lib/
|
84
|
-
- lib/gem_support.rb
|
97
|
+
- lib/jekyll_plugin_error_handling.rb
|
85
98
|
- lib/jekyll_plugin_helper.rb
|
99
|
+
- lib/jekyll_plugin_helper_attribution.rb
|
100
|
+
- lib/jekyll_plugin_helper_class.rb
|
86
101
|
- lib/jekyll_plugin_support.rb
|
87
102
|
- lib/jekyll_plugin_support/version.rb
|
103
|
+
- lib/jekyll_plugin_support_block.rb
|
104
|
+
- lib/jekyll_plugin_support_block_noarg.rb
|
105
|
+
- lib/jekyll_plugin_support_class.rb
|
88
106
|
- lib/jekyll_plugin_support_spec_support.rb
|
107
|
+
- lib/jekyll_plugin_support_tag.rb
|
108
|
+
- lib/jekyll_plugin_support_tag_noarg.rb
|
89
109
|
- spec/jekyll_plugin_helper_options_spec.rb
|
110
|
+
- spec/liquid_variable_parsing_spec.rb
|
90
111
|
- spec/spec_helper.rb
|
91
112
|
- spec/status_persistence.txt
|
92
113
|
homepage: https://www.mslinn.com/jekyll_plugins/jekyll_plugin_support.html
|
@@ -116,12 +137,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
137
|
- !ruby/object:Gem::Version
|
117
138
|
version: '0'
|
118
139
|
requirements: []
|
119
|
-
rubygems_version: 3.3.
|
140
|
+
rubygems_version: 3.3.7
|
120
141
|
signing_key:
|
121
142
|
specification_version: 4
|
122
|
-
summary: Provides
|
143
|
+
summary: Provides a framework for writing and testing Jekyll plugins
|
123
144
|
test_files:
|
124
145
|
- spec/jekyll_plugin_helper_options_spec.rb
|
146
|
+
- spec/liquid_variable_parsing_spec.rb
|
125
147
|
- spec/spec_helper.rb
|
126
148
|
- spec/status_persistence.txt
|
127
149
|
...
|
data/lib/call_chain.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
# TODO: File not used, delete
|
2
|
-
|
3
|
-
# See https://stackoverflow.com/a/23363883/553865
|
4
|
-
module CallChain
|
5
|
-
ACaller = Struct.new(:filepath, :line, :method_name)
|
6
|
-
|
7
|
-
def self.caller_method(depth = 1)
|
8
|
-
parse_caller(caller(depth + 1).first).method_name
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.parse_caller(at)
|
12
|
-
return unless /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
|
13
|
-
|
14
|
-
last_match = Regexp.last_match.to_a
|
15
|
-
ACaller.new(
|
16
|
-
last_match[1],
|
17
|
-
last_match[2].to_i,
|
18
|
-
last_match[3]
|
19
|
-
)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.excerpt_caller
|
23
|
-
call_sequence = caller
|
24
|
-
call_sequence.each do |caller_|
|
25
|
-
parsed_caller = parse_caller caller_
|
26
|
-
filepath = parsed_caller.filepath
|
27
|
-
excerpt = filepath.match? %r{jekyll[.0-9-]*/lib/jekyll/excerpt.rb\z}
|
28
|
-
puts "excerpt matched #{filepath}" if excerpt
|
29
|
-
return true if excerpt
|
30
|
-
end
|
31
|
-
false
|
32
|
-
end
|
33
|
-
|
34
|
-
# Return ACaller prior to jekyll_plugin_support
|
35
|
-
def self.jpsh_subclass_caller
|
36
|
-
state = :nothing_found
|
37
|
-
call_sequence = caller
|
38
|
-
call_sequence.each do |caller_|
|
39
|
-
parsed_caller = parse_caller caller_
|
40
|
-
filepath = parsed_caller.filepath
|
41
|
-
dirname = File.dirname filepath
|
42
|
-
jpsh = dirname.match? %r{jekyll_plugin_support[.0-9-]*/lib\z}
|
43
|
-
liquid = dirname.match? %r{liquid[.0-9-]*/lib/\z}
|
44
|
-
case state
|
45
|
-
when :nothing_found
|
46
|
-
state = :jpsh_found if jpsh
|
47
|
-
when :jpsh_found
|
48
|
-
# puts "Called from #{parsed_caller.filepath}, on line #{parsed_caller.line}, by method '#{parsed_caller.method_name}'" unless jpsh
|
49
|
-
return parsed_caller unless jpsh || liquid
|
50
|
-
end
|
51
|
-
end
|
52
|
-
nil
|
53
|
-
end
|
54
|
-
end
|
data/lib/gem_support.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# See https://stackoverflow.com/a/75890279/553865
|
2
|
-
module GemSupport
|
3
|
-
# @param file must be a fully qualified file name that points to a file within a gem.
|
4
|
-
# @return Gem::Specification of gem that file points into, or nil if not called from a gem
|
5
|
-
def self.current_spec(file)
|
6
|
-
abort 'GemSupport::current_spec: file is nil' if file.nil?
|
7
|
-
return nil unless File.exist?(file)
|
8
|
-
|
9
|
-
searcher = if Gem::Specification.respond_to?(:find)
|
10
|
-
Gem::Specification
|
11
|
-
elsif Gem.respond_to?(:searcher)
|
12
|
-
Gem.searcher.init_gemspecs
|
13
|
-
end
|
14
|
-
|
15
|
-
searcher&.find do |spec|
|
16
|
-
file.start_with? spec.full_gem_path
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|