jekyll_plugin_support 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,45 +1,14 @@
1
1
  require 'facets/string/interpolate'
2
2
  require 'key_value_parser'
3
3
  require 'shellwords'
4
- require_relative 'gem_support'
4
+ require_relative 'jekyll_plugin_helper_class'
5
+ require_relative 'jekyll_plugin_helper_attribution'
5
6
 
6
- # Base class for all types of Jekyll plugin helpers
7
- class JekyllPluginHelper # rubocop:disable Metrics/ClassLength
7
+ class JekyllPluginHelper
8
8
  attr_accessor :liquid_context
9
9
  attr_reader :argv, :attribution, :keys_values, :logger, :markup, :no_arg_parsing, :params, :tag_name,
10
10
  :argv_original, :excerpt_caller, :keys_values_original, :params_original, :jpsh_subclass_caller
11
11
 
12
- # Expand an environment variable reference
13
- def self.expand_env(str, die_if_undefined: false)
14
- str.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do
15
- envar = Regexp.last_match(1)
16
- raise HrefError, "jekyll_href error: #{envar} is undefined".red, [] \
17
- if !ENV.key?(envar) && die_if_undefined # Suppress stack trace
18
-
19
- ENV.fetch(envar, nil)
20
- end
21
- end
22
-
23
- def self.register(klass, name)
24
- abort("Error: The #{name} plugin does not define VERSION") \
25
- unless klass.const_defined?(:VERSION)
26
-
27
- version = klass.const_get(:VERSION)
28
-
29
- abort("Error: The #{name} plugin is not an instance of JekyllSupport::JekyllBlock or JekyllSupport::JekyllTag") \
30
- unless klass.instance_of?(Class) &&
31
- (klass.ancestors.include?(JekyllSupport::JekyllBlock) ||
32
- klass.ancestors.include?(JekyllSupport::JekyllTag))
33
-
34
- Liquid::Template.register_tag(name, klass)
35
- PluginMetaLogger.instance.info { "Loaded #{name} v#{version} plugin." }
36
- end
37
-
38
- # strip leading and trailing quotes if present
39
- def self.remove_quotes(string)
40
- string.strip.gsub(/\A'|\A"|'\Z|"\Z/, '').strip if string
41
- end
42
-
43
12
  # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
44
13
  # @param tag_name [String] the name of the tag, which we already know.
45
14
  # @param argument_string [String] the arguments from the tag, as a single string.
@@ -53,51 +22,18 @@ class JekyllPluginHelper # rubocop:disable Metrics/ClassLength
53
22
  @tag_name = tag_name
54
23
  @logger = logger
55
24
  @no_arg_parsing = no_arg_parsing
56
- reinitialize(markup.strip)
57
-
58
- @attribution = parameter_specified?('attribution') || false unless no_arg_parsing
59
- @logger.debug { "@keys_values='#{@keys_values}'" }
25
+ @markup = markup
60
26
  rescue StandardError => e
61
- @logger.error { "#{self.class} died with a #{e.full_message}" }
62
- end
63
-
64
- def attribute
65
- return unless @current_gem
66
-
67
- <<~END_OUTPUT
68
- <div id="jps_attribute_#{rand(999_999)}" class="jps_attribute">
69
- <div>
70
- <a href="#{@homepage}" target="_blank" rel="nofollow">
71
- #{attribution_string}
72
- </a>
73
- </div>
74
- </div>
75
- END_OUTPUT
76
- end
77
-
78
- def default_attribution
79
- authors = @authors&.join(', ')
80
- result = "Generated by the \#{@name} v\#{@version} Jekyll plugin"
81
- result << ", written by #{authors}" if authors
82
- result << " \#{@published_date}" if @published_date
83
- result << '.'
84
- result
85
- end
86
-
87
- # Sets @current_gem if file points at a uniquely named file within a gem.
88
- # @param file must be a fully qualified file name in a gem, for example: __FILE__
89
- def gem_file(file)
90
- @current_gem = GemSupport.current_spec file
91
- @logger.debug "No gem found for '#{file} was found." unless @current_gem
92
- annotate_globals if @attribution && @current_gem
27
+ e.shorten_backtrace
28
+ @logger.error { e.msg }
93
29
  end
94
30
 
95
31
  # @return undefined if parameter was specified, removes it from the available tokens and returns value
96
32
  def parameter_specified?(name, delete_param: true)
97
- return if @keys_values.empty?
33
+ return false if @keys_values.to_s.empty?
98
34
 
99
35
  key = name
100
- key = name.to_sym if @keys_values.first.first.instance_of?(Symbol)
36
+ key = name.to_sym if @keys_values&.first&.first.instance_of?(Symbol)
101
37
  value = @keys_values[key]
102
38
  delete_parameter(name) if delete_param
103
39
  value
@@ -112,18 +48,21 @@ class JekyllPluginHelper # rubocop:disable Metrics/ClassLength
112
48
  define_singleton_method(:params) { warn_fetch :params }
113
49
  define_singleton_method(:parameter_specified?) { |_name| warn_parse(:parameter_specified?) }
114
50
  define_singleton_method(:delete_parameter) { |_name| warn_parse(:delete_parameter) }
51
+ @attribution = false
115
52
  else
116
53
  parse markup
54
+ @attribution = parameter_specified?('attribution') || false
55
+ @logger.debug { "@keys_values='#{@keys_values}'" }
117
56
  end
118
57
  end
119
58
 
120
59
  # Call this method to return the remaining markup after `parameter_specified?` has been invoked.
121
60
  def remaining_markup
122
- @argv.join(' ')
61
+ @argv&.join(' ')
123
62
  end
124
63
 
125
64
  def remaining_markup_original
126
- @argv_original.join(' ')
65
+ @argv_original&.join(' ')
127
66
  end
128
67
 
129
68
  def warn_fetch(variable)
@@ -134,27 +73,6 @@ class JekyllPluginHelper # rubocop:disable Metrics/ClassLength
134
73
  abort "Error: Argument parsing was suppressed, but an attempt to invoke #{meth} was made"
135
74
  end
136
75
 
137
- private
138
-
139
- def attribution_string
140
- string = if @attribution == true
141
- default_attribution
142
- else
143
- @attribution
144
- end
145
- String.interpolate { string }
146
- end
147
-
148
- def annotate_globals
149
- return unless @current_gem
150
-
151
- @name = @current_gem.name
152
- @authors = @current_gem.authors
153
- @homepage = @current_gem.homepage
154
- @published_date = @current_gem.date.to_date.to_s
155
- @version = @current_gem.version
156
- end
157
-
158
76
  def delete_parameter(key)
159
77
  return if @keys_values.empty? || @params.nil?
160
78
 
@@ -167,58 +85,26 @@ class JekyllPluginHelper # rubocop:disable Metrics/ClassLength
167
85
  @keys_values_original.delete(key)
168
86
  end
169
87
 
170
- PREDEFINED_SCOPE_KEYS = %i[include page].freeze
171
-
172
- # Finds variables defined in an invoking include, or maybe somewhere else
173
- # @return variable value or nil
174
- def dereference_include_variable(name)
175
- @liquid_context.scopes.each do |scope|
176
- next if PREDEFINED_SCOPE_KEYS.include? scope.keys.first
177
-
178
- value = scope[name]
179
- return value if value
180
- end
181
- nil
182
- end
183
-
184
- # @return value of variable, or the empty string
185
- def dereference_variable(name)
186
- value = @liquid_context[name] # Finds variables named like 'include.my_variable', found in @liquid_context.scopes.first
187
- value ||= @page[name] if @page # Finds variables named like 'page.my_variable'
188
- value ||= dereference_include_variable(name)
189
- value ||= ''
190
- value
191
- end
192
-
193
- def lookup_variable(symbol)
194
- string = symbol.to_s
195
- return string unless string.start_with?('{{') && string.end_with?('}}')
196
-
197
- dereference_variable(string.delete_prefix('{{').delete_suffix('}}'))
198
- end
88
+ private
199
89
 
200
90
  def page
201
91
  @liquid_context.registers[:page]
202
92
  end
203
93
 
204
- # rubocop:disable Style/IfUnlessModifier
205
94
  def parse(markup)
206
95
  @argv_original = Shellwords.split(markup)
207
96
  @keys_values_original = KeyValueParser
208
- .new({}, { array_values: false, normalize_keys: false, separator: /=/ })
209
- .parse(@argv_original)
210
- unless respond_to?(:no_arg_parsing) && no_arg_parsing
211
- @params_original = @keys_values_original.map { |k, _v| lookup_variable(k) }
212
- end
97
+ .new({}, { array_values: false, normalize_keys: false, separator: /=/ })
98
+ .parse(@argv_original)
99
+ @params_original = @keys_values_original unless respond_to?(:no_arg_parsing) && no_arg_parsing
213
100
 
214
101
  @argv = Shellwords.split(self.class.expand_env(markup))
215
102
  @keys_values = KeyValueParser
216
- .new({}, { array_values: false, normalize_keys: false, separator: /=/ })
217
- .parse(@argv)
103
+ .new({}, { array_values: false, normalize_keys: false, separator: /=/ })
104
+ .parse(@argv)
218
105
 
219
106
  return if respond_to?(:no_arg_parsing) && no_arg_parsing
220
107
 
221
- @params = @keys_values.map { |k, _v| lookup_variable(k) }
108
+ @params = @keys_values # TODO: @keys_values should be deleted
222
109
  end
223
- # rubocop:enable Style/IfUnlessModifier
224
110
  end
@@ -0,0 +1,72 @@
1
+ # Attribution aspect of JekyllPluginHelper
2
+ class JekyllPluginHelper
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
+ # See https://stackoverflow.com/a/75890279/553865
6
+ def self.current_spec(file)
7
+ abort 'JekyllPluginHelper::current_spec: file is nil' if file.nil?
8
+ return nil unless File.exist?(file)
9
+
10
+ searcher = if Gem::Specification.respond_to?(:find)
11
+ Gem::Specification
12
+ elsif Gem.respond_to?(:searcher)
13
+ Gem.searcher.init_gemspecs
14
+ end
15
+
16
+ searcher&.find do |spec|
17
+ file.start_with? spec.full_gem_path
18
+ end
19
+ end
20
+
21
+ def attribute
22
+ return unless @current_gem
23
+
24
+ <<~END_OUTPUT
25
+ <div id="jps_attribute_#{rand(999_999)}" class="jps_attribute">
26
+ <div>
27
+ <a href="#{@homepage}" target="_blank" rel="nofollow">
28
+ #{attribution_string}
29
+ </a>
30
+ </div>
31
+ </div>
32
+ END_OUTPUT
33
+ end
34
+
35
+ def default_attribution
36
+ authors = @authors&.join(', ')
37
+ result = "Generated by the \#{@name} v\#{@version} Jekyll plugin"
38
+ result << ", written by #{authors}" if authors
39
+ result << " \#{@published_date}" if @published_date
40
+ result << '.'
41
+ result
42
+ end
43
+
44
+ # Sets @current_gem if file points at a uniquely named file within a gem.
45
+ # @param file must be a fully qualified file name in a gem, for example: __FILE__
46
+ def gem_file(file)
47
+ @current_gem = JekyllPluginHelper.current_spec file
48
+ @logger.debug "No gem found for '#{file} was found." unless @current_gem
49
+ annotate_globals if @attribution && @current_gem
50
+ end
51
+
52
+ private
53
+
54
+ def annotate_globals
55
+ return unless @current_gem
56
+
57
+ @name = @current_gem.name
58
+ @authors = @current_gem.authors
59
+ @homepage = @current_gem.homepage
60
+ @published_date = @current_gem.date.to_date.to_s
61
+ @version = @current_gem.version
62
+ end
63
+
64
+ def attribution_string
65
+ string = if @attribution == true
66
+ default_attribution
67
+ else
68
+ @attribution
69
+ end
70
+ String.interpolate { string }
71
+ end
72
+ end
@@ -0,0 +1,33 @@
1
+ # Class methods for JekyllPluginHelper
2
+ class JekyllPluginHelper
3
+ # Expand an environment variable reference
4
+ def self.expand_env(str, die_if_undefined: false)
5
+ str&.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do
6
+ envar = Regexp.last_match(1)
7
+ raise JekyllPluginSupportError, "jekyll_plugin_support error: #{envar} is undefined".red, [] \
8
+ if !ENV.key?(envar) && die_if_undefined # Suppress stack trace
9
+
10
+ ENV.fetch(envar, nil)
11
+ end
12
+ end
13
+
14
+ def self.register(klass, name)
15
+ abort("Error: The #{name} plugin does not define VERSION") \
16
+ unless klass.const_defined?(:VERSION)
17
+
18
+ version = klass.const_get(:VERSION)
19
+
20
+ abort("Error: The #{name} plugin is not an instance of JekyllSupport::JekyllBlock or JekyllSupport::JekyllTag") \
21
+ unless klass.instance_of?(Class) &&
22
+ (klass.ancestors.include?(JekyllSupport::JekyllBlock) ||
23
+ klass.ancestors.include?(JekyllSupport::JekyllTag))
24
+
25
+ Liquid::Template.register_tag(name, klass)
26
+ PluginMetaLogger.instance.info { "Loaded #{name} v#{version} plugin." }
27
+ end
28
+
29
+ # strip leading and trailing quotes if present
30
+ def self.remove_quotes(string)
31
+ string.strip.gsub(/\A'|\A"|'\Z|"\Z/, '').strip if string
32
+ end
33
+ end
@@ -1,3 +1,3 @@
1
1
  module JekyllPluginSupportVersion
2
- VERSION = '0.7.2'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
@@ -4,211 +4,14 @@ require 'jekyll_plugin_logger'
4
4
  require_relative 'jekyll_plugin_helper'
5
5
  require_relative 'jekyll_plugin_support/version'
6
6
 
7
- # @author Copyright 2022 Michael Slinn
8
- # @license SPDX-License-Identifier: Apache-2.0``
9
7
  module NoArgParsing
10
8
  attr_accessor :no_arg_parsing
11
9
 
12
10
  @no_arg_parsing = true
13
11
  end
14
12
 
15
- module JekyllSupport
16
- DISPLAYED_CALLS = 8
17
-
18
- def self.error_short_trace(logger, error)
19
- remaining = error.backtrace.length - DISPLAYED_CALLS
20
- logger.error do
21
- error.message + "\n" + # rubocop:disable Style/StringConcatenation
22
- error.backtrace.take(DISPLAYED_CALLS).join("\n") +
23
- "\n...Remaining #{remaining} call sites elided.\n"
24
- end
25
- end
26
-
27
- def self.warn_short_trace(logger, error)
28
- remaining = error.backtrace.length - DISPLAYED_CALLS
29
- logger.warn do
30
- error.message + "\n" + # rubocop:disable Style/StringConcatenation
31
- error.backtrace.take(DISPLAYED_CALLS).join("\n") +
32
- "\n...Remaining #{remaining} call sites elided.\n"
33
- end
34
- end
35
-
36
- # Base class for Jekyll block tags
37
- class JekyllBlock < Liquid::Block
38
- attr_reader :argument_string, :helper, :line_number, :logger, :page, :site, :text
39
-
40
- # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
41
- # @param tag_name [String] the name of the tag, which we usually know.
42
- # @param argument_string [String] the arguments passed to the tag, as a single string.
43
- # @param parse_context [Liquid::ParseContext] hash that stores Liquid options.
44
- # By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
45
- # a boolean parameter that determines if error messages should display the line number the error occurred.
46
- # This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
47
- # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
48
- # @return [void]
49
- def initialize(tag_name, argument_string, parse_context)
50
- super
51
- @tag_name = tag_name
52
- @argument_string = argument_string
53
- @logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
54
- @logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
55
- @helper = JekyllPluginHelper.new tag_name, argument_string, @logger, respond_to?(:no_arg_parsing)
56
- end
57
-
58
- # @return line number where tag or block was found, relative to the start of the page
59
- def jekyll_line_number
60
- @page['front_matter'].count("\n") + @line_number
61
- end
62
-
63
- # Method prescribed by the Jekyll plugin lifecycle.
64
- # Defines @config, @envs, @mode, @page and @site
65
- # @return [String]
66
- def render(liquid_context)
67
- text = super
68
- @helper.liquid_context = liquid_context
69
-
70
- @page = liquid_context.registers[:page] # hash
71
- @site = liquid_context.registers[:site]
72
- @config = @site.config
73
- @envs = liquid_context.environments.first
74
-
75
- @layout = @envs[:layout]
76
- @paginator = @envs[:paginator]
77
- @theme = @envs[:theme]
78
-
79
- @mode = @config['env']&.key?('JEKYLL_ENV') ? @config['env']['JEKYLL_ENV'] : 'development'
80
-
81
- render_impl text
82
- rescue StandardError => e
83
- @logger.error { "#{self.class} died with a #{e.full_message}" }
84
- JekyllSupport.error_short_trace(@logger, e)
85
- end
86
-
87
- # Jekyll plugins should override this method, not render,
88
- # so they can be tested more easily.
89
- # The following variables are predefined:
90
- # @argument_string, @config, @envs, @helper, @layout, @logger, @mode, @page, @paginator, @site, @tag_name and @theme
91
- # @return [String] The result to be rendered to the invoking page
92
- def render_impl(text)
93
- text
94
- end
95
-
96
- def warn_short_trace(error)
97
- JekyllSupport.warn_short_trace(@logger, error)
98
- end
99
- end
100
-
101
- class JekyllBlockNoArgParsing < JekyllBlock
102
- def initialize(tag_name, argument_string, parse_context)
103
- class << self
104
- include NoArgParsing
105
- end
106
-
107
- super
108
- @logger.debug { "#{self.class}: respond_to?(:o_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
109
- rescue StandardError => e
110
- @logger.error { "#{self.class} died with a #{e.full_message}" }
111
- JekyllSupport.error_short_trace(@logger, e)
112
- end
113
-
114
- def warn_short_trace(error)
115
- JekyllSupport.warn_short_trace(@logger, error)
116
- end
117
- end
118
-
119
- # Base class for Jekyll tags
120
- class JekyllTag < Liquid::Tag
121
- attr_reader :argument_string, :helper, :line_number, :logger, :page, :site
122
-
123
- # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
124
- # @param tag_name [String] the name of the tag, which we usually know.
125
- # @param argument_string [String] the arguments passed to the tag, as a single string.
126
- # @param parse_context [Liquid::ParseContext] hash that stores Liquid options.
127
- # By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
128
- # a boolean parameter that determines if error messages should display the line number the error occurred.
129
- # This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
130
- # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
131
- # @return [void]
132
- def initialize(tag_name, argument_string, parse_context)
133
- super
134
- @tag_name = tag_name
135
- @argument_string = argument_string
136
- @logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
137
- @logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
138
- @helper = JekyllPluginHelper.new(tag_name, argument_string, @logger, respond_to?(:no_arg_parsing))
139
- end
140
-
141
- # If a Jekyll plugin needs to crash exit, and stop Jekyll, call this method.
142
- # It does not generate a stack trace.
143
- # This method does not return because the process is abruptly terminated.
144
- #
145
- # @param error StandardError or a subclass of StandardError is required
146
- #
147
- # Do not raise the error before calling this method, just create it via 'new', like this:
148
- # exit_without_stack_trace StandardError.new('This is my error message')
149
- #
150
- # If you want to call this method from a handler method, the default index for the backtrace array must be specified.
151
- # The default backtrace index is 1, which means the calling method.
152
- # To specify the calling method's caller, pass in 2, like this:
153
- # exit_without_stack_trace StandardError.new('This is my error message'), 2
154
- def exit_without_stack_trace(error, caller_index = 1)
155
- raise error
156
- rescue StandardError => e
157
- file, line_number, caller = e.backtrace[caller_index].split(':')
158
- caller = caller.tr('`', "'")
159
- warn "#{self.class} died with a '#{error.message}' #{caller} on line #{line_number} of #{file}".red
160
- # Process.kill('HUP', Process.pid) # generates huge stack trace
161
- exec "echo ''"
162
- end
163
-
164
- # @return line number where tag or block was found, relative to the start of the page
165
- def jekyll_line_number
166
- @page['front_matter'].count("\n") + @line_number
167
- end
168
-
169
- # Method prescribed by the Jekyll plugin lifecycle.
170
- def render(liquid_context)
171
- return if @helper.excerpt_caller
172
-
173
- @helper.liquid_context = liquid_context
174
-
175
- @envs = liquid_context.environments.first
176
-
177
- @layout = @envs[:layout]
178
- @paginator = @envs[:paginator]
179
- @theme = @envs[:theme]
180
-
181
- @page = liquid_context.registers[:page]
182
- @site = liquid_context.registers[:site]
183
-
184
- @config = @site.config
185
- @mode = @config['env']&.key?('JEKYLL_ENV') ? @config['env']['JEKYLL_ENV'] : 'development'
186
-
187
- render_impl
188
- rescue StandardError => e
189
- JekyllSupport.error_short_trace(@logger, e)
190
- end
191
-
192
- # Jekyll plugins must override this method, not render, so their plugin can be tested more easily
193
- # The following variables are predefined:
194
- # @argument_string, @config, @envs, @helper, @layout, @logger, @mode, @page, @paginator, @site, @tag_name and @theme
195
- def render_impl
196
- abort "#{self.class}.render_impl for tag #{@tag_name} must be overridden, but it was not."
197
- end
198
-
199
- def warn_short_trace(error)
200
- JekyllSupport.warn_short_trace(@logger, error)
201
- end
202
- end
203
-
204
- class JekyllTagNoArgParsing < JekyllTag
205
- def initialize(tag_name, argument_string, parse_context)
206
- class << self
207
- include NoArgParsing
208
- end
209
-
210
- super
211
- @logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
212
- end
213
- end
214
- end
13
+ require_relative 'jekyll_plugin_support_class'
14
+ require_relative 'jekyll_plugin_support_block'
15
+ require_relative 'jekyll_plugin_support_block_noarg'
16
+ require_relative 'jekyll_plugin_support_tag'
17
+ require_relative 'jekyll_plugin_support_tag_noarg'
@@ -0,0 +1,77 @@
1
+ require_relative 'jekyll_plugin_error_handling'
2
+
3
+ module JekyllSupport
4
+ # Base class for Jekyll block tags
5
+ class JekyllBlock < Liquid::Block
6
+ attr_reader :argument_string, :helper, :line_number, :logger, :page, :site, :text
7
+
8
+ include JekyllSupportErrorHandling
9
+ extend JekyllSupportErrorHandling
10
+
11
+ # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
12
+ # @param tag_name [String] the name of the tag, which we usually know.
13
+ # @param argument_string [String] the arguments passed to the tag, as a single string.
14
+ # @param parse_context [Liquid::ParseContext] hash that stores Liquid options.
15
+ # By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
16
+ # a boolean parameter that determines if error messages should display the line number the error occurred.
17
+ # This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
18
+ # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
19
+ # @return [void]
20
+ def initialize(tag_name, markup, parse_context)
21
+ super
22
+ @tag_name = tag_name
23
+ @argument_string = markup.to_s # Vars in plugin parameters cannot be replaced yet
24
+ @logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
25
+ @logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
26
+ @helper = JekyllPluginHelper.new tag_name, markup, @logger, respond_to?(:no_arg_parsing)
27
+ end
28
+
29
+ # Method prescribed by the Jekyll plugin lifecycle.
30
+ # Defines @config, @envs, @mode, @page and @site
31
+ # @return [String]
32
+ def render(liquid_context)
33
+ @helper.liquid_context = JekyllSupport.inject_vars @logger, liquid_context
34
+ text = super # Liquid variable values in content are looked up and substituted
35
+
36
+ @envs = liquid_context.environments.first
37
+ @page = liquid_context.registers[:page] # hash
38
+ @scopes = liquid_context.scopes
39
+ @site = liquid_context.registers[:site]
40
+
41
+ @config = @site.config
42
+ @tag_config = @config[@tag_name]
43
+
44
+ @layout = @envs[:layout]
45
+ @paginator = @envs[:paginator]
46
+ @theme = @envs[:theme]
47
+
48
+ @mode = @config['env']&.key?('JEKYLL_ENV') ? @config['env']['JEKYLL_ENV'] : 'development'
49
+
50
+ @helper.reinitialize(@markup.strip)
51
+
52
+ @attribution = @helper.parameter_specified?('attribution') || false unless @no_arg_parsing
53
+ @logger.debug { "@keys_values='#{@keys_values}'" }
54
+
55
+ markup = JekyllSupport.lookup_liquid_variables liquid_context, @argument_string
56
+ @helper.reinitialize markup
57
+
58
+ render_impl text
59
+ rescue StandardError => e
60
+ e.shorten_backtrace
61
+ msg = format_error_message e.full_message
62
+ @logger.error msg
63
+ raise e if @die_on_standard_error
64
+
65
+ "<div class='standard_error'>#{e.class}: #{msg}</div>"
66
+ end
67
+
68
+ # Jekyll plugins should override this method, not render,
69
+ # so they can be tested more easily.
70
+ # The following variables are predefined:
71
+ # @argument_string, @config, @envs, @helper, @layout, @logger, @mode, @page, @paginator, @site, @tag_name and @theme
72
+ # @return [String] The result to be rendered to the invoking page
73
+ def render_impl(text)
74
+ text
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'jekyll_plugin_error_handling'
2
+
3
+ module JekyllSupport
4
+ class JekyllBlockNoArgParsing < JekyllBlock
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?(:o_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
17
+ rescue StandardError => e
18
+ e.shorten_backtrace
19
+ @logger.error { e.full_message }
20
+ JekyllSupport.error_short_trace(@logger, e)
21
+ end
22
+
23
+ # Jekyll plugins must override this method, not render, so their plugin can be tested more easily
24
+ # The following variables are predefined:
25
+ # @argument_string, @config, @envs, @helper, @layout, @logger, @mode, @page, @paginator, @site, @tag_name and @theme
26
+ def render_impl
27
+ abort "#{self.class}.render_impl for tag #{@tag_name} must be overridden, but it was not."
28
+ end
29
+ end
30
+ end