Class: JekyllSupport::JekyllPluginHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/helper/jekyll_plugin_helper_class.rb,
lib/helper/jekyll_plugin_helper.rb,
lib/helper/jekyll_plugin_helper_attribution.rb

Overview

Attribution aspect of JekyllPluginHelper

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tag_name, markup, logger, no_arg_parsing: false) ⇒ void

Parameters:

  • tag_name (String)

    the name of the tag, which we already know.

  • markup (String)

    the arguments from the tag, as a single string.

  • logger (Jekyll::Logger)

    logger instance to use for logging messages.

  • no_arg_parsing (Boolean) (defaults to: false)

    if true, argument parsing is suppressed.



17
18
19
20
21
22
23
24
25
26
# File 'lib/helper/jekyll_plugin_helper.rb', line 17

def initialize(tag_name, markup, logger, no_arg_parsing: false)
  @tag_name = tag_name
  @logger = logger
  @markup = markup
  @argument_string = markup
  @no_arg_parsing = no_arg_parsing
rescue StandardError => e
  e.shorten_backtrace
  @logger.error { e.message }
end

Instance Attribute Details

#argument_stringObject (readonly)

Returns the value of attribute argument_string.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def argument_string
  @argument_string
end

#argvObject (readonly)

Returns the value of attribute argv.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def argv
  @argv
end

#argv_originalObject (readonly)

Returns the value of attribute argv_original.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def argv_original
  @argv_original
end

#attributionObject (readonly)

Returns the value of attribute attribution.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def attribution
  @attribution
end

#excerpt_callerObject (readonly)

Returns the value of attribute excerpt_caller.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def excerpt_caller
  @excerpt_caller
end

#jpsh_subclass_callerObject (readonly)

Returns the value of attribute jpsh_subclass_caller.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def jpsh_subclass_caller
  @jpsh_subclass_caller
end

#keys_valuesObject (readonly)

Returns the value of attribute keys_values.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def keys_values
  @keys_values
end

#keys_values_originalObject (readonly)

Returns the value of attribute keys_values_original.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def keys_values_original
  @keys_values_original
end

#liquid_contextObject

Returns the value of attribute liquid_context.



7
8
9
# File 'lib/helper/jekyll_plugin_helper.rb', line 7

def liquid_context
  @liquid_context
end

#loggerObject (readonly)

Returns the value of attribute logger.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def logger
  @logger
end

#markupObject (readonly)

Returns the value of attribute markup.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def markup
  @markup
end

#no_arg_parsingObject (readonly)

Returns the value of attribute no_arg_parsing.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def no_arg_parsing
  @no_arg_parsing
end

#paramsObject (readonly)

Returns the value of attribute params.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def params
  @params
end

#params_originalObject (readonly)

Returns the value of attribute params_original.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def params_original
  @params_original
end

#tag_nameObject (readonly)

Returns the value of attribute tag_name.



8
9
10
# File 'lib/helper/jekyll_plugin_helper.rb', line 8

def tag_name
  @tag_name
end

Class Method Details

.current_spec(file) ⇒ Object

Parameters:

  • file

    must be a fully qualified file name that points to a file within a gem.

Returns:

  • Gem::Specification of gem that file points into, or nil if not called from a gem



7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/helper/jekyll_plugin_helper_attribution.rb', line 7

def self.current_spec(file)
  abort 'JekyllPluginHelper::current_spec: file is nil' if file.nil?
  return nil unless File.exist?(file)

  searcher = if Gem::Specification.respond_to?(:find)
               Gem::Specification
             elsif Gem.respond_to?(:searcher)
               Gem.searcher.init_gemspecs
             end

  searcher&.find do |spec|
    file.start_with? spec.full_gem_path
  end
end

.env_var_case_insensitive(name) ⇒ Object

Case-insensitive search for a Bash environment variable name # @param name [String] name of environment variable to search for

Returns:

  • matching variable value, or nil if not found



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 11

def self.env_var_case_insensitive(name)
  candidate = ENV.fetch(name, nil) # exact match first
  return candidate if candidate

  candidates = ENV.select do |key, _value| # case-insensitive search
    key.casecmp?(name)
  end.keys
  case candidates.size
  when 0
    msg = "Environment variable #{name} is undefined, even with a case-insensitive search."
    if Jekyll.logger
      Jekyll.logger.warn msg
    else
      puts "jekyll_plugin_support warning: #{msg}".red
    end
    nil
  when 1
    candidates.first
  else
    msg = "Multiple case-insensitive matches found for environment variable #{name}: #{candidates.join(', ')}"
    raise JekyllPluginSupportError, msg.red, []
  end
end

.env_var_expand_bash(str, logger = nil, die_if_undefined: false) ⇒ Object

If a Windows-style env var is evaluated on a non-Windows machine, then a Bash environment variable of the same name is searched for and used, if found.

  • If an exact case-sensitive match is found, it is used. A debug-level log message is emitted stating what happened.

  • If a case-insensitive match is found, it is used, and a warning is issued.

  • If more than one case-insensitive match is found, Jekyll is shut down.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 50

def self.env_var_expand_bash(str, logger = nil, die_if_undefined: false)
  str&.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}/) do
    envar = Regexp.last_match 1
    unless ENV.key? envar
      msg = "jekyll_plugin_support error: environment variable #{envar} is undefined"
      raise JekyllPluginSupportError, msg.red, [] if die_if_undefined

      if logger
        logger.warn msg
      else
        puts msg.red
      end
    end
    ENV.fetch(envar, nil)
  end
end

.env_var_expand_windows(str, logger = nil, die_if_undefined: false, use_wslvar: true) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 91

def self.env_var_expand_windows(str, logger = nil, die_if_undefined: false, use_wslvar: true)
  # Only expand %VAR% if str is not nil and contains a %
  if str&.include?('%')
    str.gsub(/%([a-zA-Z_][a-zA-Z0-9_]*)%|{\g<1>}/) do
      envar = Regexp.last_match 1
      value = find_windows_envar(envar, use_wslvar: use_wslvar)
      if value.to_s.empty?
        msg = "jekyll_plugin_support error: Windows environment variable %#{envar}% is undefined"
        raise JekyllPluginSupportError, msg.red, [] if die_if_undefined

        if logger
          logger.warn msg
        else
          puts msg.red
        end
      end
      value
    end
  else
    str
  end
end

.expand_env(str, logger = nil, die_if_undefined: false) ⇒ Object

Expand an environment variable reference;

- expand bash environment variables ($VAR or ${VAR})

Note: Windows environment variables (%VAR%) are NOT expanded here because text content often contains percent pairs that aren’t environment variables. Use env_var_expand_windows() directly if Windows environment variable expansion is needed.



40
41
42
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 40

def self.expand_env(str, logger = nil, die_if_undefined: false)
  JekyllPluginHelper.env_var_expand_bash(str, logger, die_if_undefined: die_if_undefined)
end

.find_windows_envar(envar, use_wslvar: true) ⇒ Object

Called for Linux, MacOS, and Windows (with WSL)



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 69

def self.find_windows_envar(envar, use_wslvar: true)
  if use_wslvar
    wslvar_path = `which wslvar 2> /dev/null`.chomp
    if wslvar_path.empty?
      warn "jekyll_plugin_support warning: wslvar not found in PATH; will attempt to find $#{envar} in the bash environment variables."
      return env_var_case_insensitive(envar)
    end

    result = `wslvar #{envar} &2> /dev/null`.chomp
    if result.empty?
      warn "jekyll_plugin_support warning: wslvar did not find $#{envar}; will attempt to find it in the bash environment variables."
      return env_var_case_insensitive(envar)
    end

    return result
  end
  env_var_case_insensitive(envar)
end

.generate_message(klass, tag_name, version) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 114

def self.generate_message(klass, tag_name, version)
  error_name_stub = klass.name.include?('::') ? klass.name.split('::')[1] : klass.name
  error_ruby_class_name = "#{error_name_stub.camelcase(:upper)}Error"
  config_die_key = "die_on_#{error_ruby_class_name.snakecase}"
  error_css_class_name = error_ruby_class_name.split('::').last.snakecase
  config = YAML.load_file('_config.yml')
  tag_config = config[tag_name]
  tag_config_msg = if tag_config.nil?
                     "                       _config.yml does not contain configuration information for this plugin.\n                         You could add a section containing default values by specifying a section for the tag name,\n                         and an entry whose name starts with `die_on_`, followed by a snake_case version of the error name.\n\n                           \#{tag_name}:\n                             \#{config_die_key}: false\n                     END_MSG\n                   else\n                     <<~END_MSG\n                       _config.yml contains the following configuration for this plugin:\n                         \#{tag_config}\n                     END_MSG\n                   end\n\n  <<~END_MSG\n    Loaded plugin \#{tag_name} v\#{version}. It has:\n      Error class: \#{error_ruby_class_name}\n      CSS class for error messages: \#{error_css_class_name}\n\n      \#{tag_config_msg}\n  END_MSG\nend\n"

.register(klass, tag_name, quiet: false) ⇒ Object

Parameters:

  • klass (Class)

    class instance to register

  • tag_name (String)

    name of plugin defined by klass to register as

  • quiet (Boolean) (defaults to: false)

    suppress registration message if truthy



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 149

def self.register(klass, tag_name, quiet: false)
  abort("Error: The #{tag_name} plugin does not define VERSION") \
    unless klass.const_defined?(:VERSION)

  version = klass.const_get(:VERSION)

  abort("Error: The #{tag_name} plugin is not an instance of JekyllSupport::JekyllBlock or JekyllSupport::JekyllTag") \
    unless klass.instance_of?(Class) &&
           (klass.ancestors.include?(::JekyllSupport::JekyllBlock) ||
            klass.ancestors.include?(::JekyllSupport::JekyllTag))

  Liquid::Template.register_tag(tag_name, klass)
  return if quiet

  msg = generate_message(klass, tag_name, version)
  PluginMetaLogger.instance.info { msg }
end

.remove_html_tags(string) ⇒ Object



167
168
169
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 167

def self.remove_html_tags(string)
  string.gsub(/<[^>]*>/, '').strip
end

.remove_quotes(string) ⇒ Object

strip leading and trailing quotes if present



172
173
174
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 172

def self.remove_quotes(string)
  string.strip.gsub(/\A'|\A"|'\Z|"\Z/, '').strip if string
end

.wsl_detected?Boolean

Detect if Jekyll is running under WSL

Returns:

  • (Boolean)


89
# File 'lib/helper/jekyll_plugin_helper_class.rb', line 89

def self.wsl_detected? = File.read('/proc/version').include?('-WSL')

Instance Method Details

#attributeObject



22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/helper/jekyll_plugin_helper_attribution.rb', line 22

def attribute
  return unless @current_gem

  "    <div id=\"jps_attribute_\#{rand(999_999)}\" class=\"jps_attribute\">\n      <div>\n        <a href=\"\#{@homepage}\" target=\"_blank\" rel=\"nofollow\">\n          \#{attribution_string}\n        </a>\n      </div>\n    </div>\n  END_OUTPUT\nend\n"

#default_attributionObject



36
37
38
39
40
41
42
43
# File 'lib/helper/jekyll_plugin_helper_attribution.rb', line 36

def default_attribution
  authors = @authors&.join(', ')
  result = "Generated by the \#{@name} v\#{@version} Jekyll plugin"
  result << ", written by #{authors}" if authors
  result << " \#{@published_date}" if @published_date
  result << '.'
  result
end

#delete_parameter(key) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/helper/jekyll_plugin_helper.rb', line 73

def delete_parameter(key)
  return if @keys_values.empty? || @params.nil?

  @params.delete(key)
  @argv.delete_if { |x| x == key or x.start_with?("#{key}=") }
  @keys_values.delete(key)

  @params_original.delete(key)
  @argv_original.delete_if { |x| x == key or x.start_with?("#{key}=") }
  @keys_values_original.delete(key)
end

#gem_file(file) ⇒ Object

Sets @current_gem if file points at a uniquely named file within a gem.

Parameters:

  • file

    must be a fully qualified file name in a gem, for example: __FILE__



47
48
49
50
51
# File 'lib/helper/jekyll_plugin_helper_attribution.rb', line 47

def gem_file(file)
  @current_gem = JekyllPluginHelper.current_spec file
  @logger.debug "No gem found for '#{file} was found." unless @current_gem
  annotate_globals if @attribution && @current_gem
end

#parameter_specified?(name, delete_param: true) ⇒ Boolean

Returns undefined if parameter was specified, removes it from the available tokens and returns value.

Returns:

  • (Boolean)

    undefined if parameter was specified, removes it from the available tokens and returns value



29
30
31
32
33
34
35
36
37
# File 'lib/helper/jekyll_plugin_helper.rb', line 29

def parameter_specified?(name, delete_param: true)
  return false if @keys_values.to_s.empty?

  key = name
  key = name.to_sym if @keys_values&.first&.first.instance_of?(Symbol)
  value = @keys_values[key]
  delete_parameter(name) if delete_param
  value
end

#reinitialize(markup) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/helper/jekyll_plugin_helper.rb', line 39

def reinitialize(markup)
  # @keys_values was a Hash[Symbol, String|Boolean] but now it is Hash[String, String|Boolean]
  @argument_string = markup
  if @no_arg_parsing
    define_singleton_method(:argv) { warn_fetch :argv }
    define_singleton_method(:keys_values) { warn_fetch :keys_values }
    define_singleton_method(:params) { warn_fetch :params }
    define_singleton_method(:parameter_specified?) { |_name| warn_parse(:parameter_specified?) }
    define_singleton_method(:delete_parameter) { |_name| warn_parse(:delete_parameter) }
    @attribution = false
  else
    parse markup
    @attribution = parameter_specified?('attribution') || false
    @logger.debug { "@keys_values='#{@keys_values}'" }
  end
end

#remaining_markupObject

Call this method to return the remaining markup after ‘parameter_specified?` has been invoked.



57
58
59
# File 'lib/helper/jekyll_plugin_helper.rb', line 57

def remaining_markup
  @argv&.join(' ')
end

#remaining_markup_originalObject



61
62
63
# File 'lib/helper/jekyll_plugin_helper.rb', line 61

def remaining_markup_original
  @argv_original&.join(' ')
end

#warn_fetch(variable) ⇒ Object



65
66
67
# File 'lib/helper/jekyll_plugin_helper.rb', line 65

def warn_fetch(variable)
  abort "Error: Argument parsing was suppressed, but an attempt to obtain the value of #{variable} was made"
end

#warn_parse(meth) ⇒ Object



69
70
71
# File 'lib/helper/jekyll_plugin_helper.rb', line 69

def warn_parse(meth)
  abort "Error: Argument parsing was suppressed, but an attempt to invoke #{meth} was made"
end