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.
@@ -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.filter_run :focus
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'
@@ -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.7.2
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-08-14 00:00:00.000000000 Z
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/call_chain.rb
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.3
140
+ rubygems_version: 3.3.7
120
141
  signing_key:
121
142
  specification_version: 4
122
- summary: Provides support for writing Jekyll plugins.
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