jekyll_plugin_support 1.0.3 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4f3097e166568e130edb33f7dac7b366f0813bd9d21bc8e5e4687b7f8e9fbe2
4
- data.tar.gz: d5523a0a88be86c56091eb906376507cd4b6d5e42e1722e90b5c568959ff03f6
3
+ metadata.gz: b54badeb565b69fb6839140b7ce7b340d6a6663c226a3f7cb8728df2e0f61b2e
4
+ data.tar.gz: f74acdbe3c4d99561dead976deb36c4ff477f06dac7783a0ef918df901a71a53
5
5
  SHA512:
6
- metadata.gz: b4d7f3863be9bf780ecc5da65141be8ddda6b920ee354a4065b8f43e05d1532083b36537c8112494ef83e18ce0cf47b25ee45f25a3ba7af149240ba40ebe1f75
7
- data.tar.gz: d5ba448bfd2b4bdbf01208e00d1b932b76293a7c6e78e7fe57f706c2dc8f9b3cd48af7867c0da38347dabdbda7e0f4420f739889f1e0aeda747b8f92bbee3b39
6
+ metadata.gz: d3deaa658d05e5f9cf629ba10a337f9b864465462a362d1d4670eaa3afff32c78b04cbc974a17fb9afd9fbac1392a2e4c58c1e0fbbd40a145ac9c4076b5a741a
7
+ data.tar.gz: 910ca35d862324baac1eb5c627662f4f4dac01e5966a06ad9c071bf1354800458ff81f16e260375680845d7062ee9dc7c41c63ab994f4d2630ad56eacbe8bb51
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.1.0 / 2025-02-07
4
+
5
+ * Now replaces include and layout variables with their values
6
+ * Renamed `@helper.markup` to `@helper.argument_string`
7
+ * Improved `nil` handling throughout
8
+ * Added the **Debug Demo production** launch configuration to debug the demo website in production mode
9
+ * Added the `bin/release_port` script that kills hung processes being debugged
10
+ * Added the `cleanDemo` task for the **Debug Demo production** and **Debug Demo development** tasks
11
+ * Many minor improvements to the demo.
12
+ * `JekyllSupport::JekyllPluginHelper::register` now accepts a `quiet` option, to suppress the signon message.
13
+ Use it like this:
14
+
15
+ ```ruby
16
+ JekyllPluginHelper.register(self, 'tag_name', quiet: true)
17
+ ```
18
+
3
19
 
4
20
  ## 1.0.3 / 2024-08-19
5
21
 
data/README.md CHANGED
@@ -1,13 +1,17 @@
1
1
  # `jekyll_plugin_support` [![Gem Version](https://badge.fury.io/rb/jekyll_plugin_support.svg)](https://badge.fury.io/rb/jekyll_plugin_support)
2
2
 
3
- `Jekyll_plugin_support` is a Ruby gem that provides a framework for writing and testing Jekyll plugins.
3
+ After writing over two dozen Jekyll plugins, I distilled the common code into `Jekyll_plugin_support`.
4
+ This F/OSS Ruby gem facilitates writing and testing Jekyll plugins and handles the standard housekeeping that every Jekyll
5
+ inline and block tag plugin requires.
6
+ Logging, parsing arguments, obtaining references to site and page objects, etc. are all handled.
7
+ The result is faster Jekyll plugin writing with fewer bugs.
4
8
 
5
9
  `Jekyll_plugin_support` can be used to create simple Jekyll plugins in
6
10
  the `_plugins/` directory of your Jekyll project, or gem-based Jekyll plugins.
7
11
 
8
12
  At present, only Jekyll tags and blocks are supported.
9
13
 
10
- Plugins that use `jekyll_plugin_support` include:
14
+ Public plugins that use `jekyll_plugin_support` include:
11
15
 
12
16
  <ul style="columns: 2">
13
17
  <li><a href='https://www.mslinn.com/jekyll_plugins/jekyll_all_collections'><code>jekyll_all_collections</code></a></li>
@@ -33,51 +37,70 @@ Jekyll plugin tags created from `jekyll_plugin_support` framework automatically
33
37
  1. Boilerplate is removed, so you can focus on the required logic and output.
34
38
  2. Arguments are parsed for keywords and name/value parameters.
35
39
  3. Single or double quotes can be used for arguments and parameters.
36
- 4. Important variables are defined.
40
+ 4. Important variables are predefined.
37
41
  5. Error handling is standardized, and includes an automatically defined error type
38
42
  and corresponding CSS tag for each Jekyll tag.
39
- 6. Liquid variables can be passed as parameters to tags, and used in the body of block tags.
40
- 7. Registration is automatic, and important configuration details are reported during registration.
43
+ 6. Jekyll and Liquid variables, including `layout`, `page` and `include` variables,
44
+ can be passed as parameters to tags, and used in the body of block tags.
45
+ 7. Plugin registration is integrated, and important configuration details are reported during registration.
41
46
  8. A custom logger is created for each tag, independent of the default Jekyll logger.
42
47
  9. Variables can be defined in `_config.yml`, and optionally have different values for debug mode,
43
48
  production mode and test mode.
44
49
  10. An attribution message is available.
45
50
  11. Draft pages are automatically detected.
46
-
47
- In addition, a demonstration website is provided for easy testing of your plugins.
51
+ 12. A demonstration website is provided for easy testing of every plugin.
52
+ 13. Visual Studio Code debugging is set up for the plugin code and the demo website.
53
+ 14. Plugins can be subclassed.
54
+ 15. [`Nugem`](https://mslinn.com/ruby/6800-nugem.html) can create working scaffolding for new plugins built
55
+ using `jekyll_plugin_support`.
48
56
 
49
57
 
50
58
  ## Installation
51
59
 
60
+ ### For A Jekyll Website
61
+
52
62
  `Jekyll_plugin_support` is packaged as a Ruby gem.
53
- If your project is a custom plugin that will reside in a Jekyll project’s `_plugins` directory,
63
+ If you want to write a custom Jekyll plugin that will reside in a Jekyll project’s `_plugins` directory,
54
64
  add the following line to your Jekyll plugin’s `Gemfile`.
55
65
 
56
66
  ```ruby
57
67
  group :jekyll_plugins do
58
68
  # ...
59
- gem 'jekyll_plugin_support', '>= 0.8.0'
69
+ gem 'jekyll_plugin_support', '>= 1.1.0'
60
70
  # ...
61
71
  end
62
72
  ```
63
73
 
64
- Otherwise, if your custom plugin will be packaged into a gem, add the following to your plugin’s `.gemspec`:
74
+ Run the standard `jekyll_plugin_support` setup procedure:
75
+
76
+ ```shell
77
+ $ bin/setup
78
+ ```
79
+
80
+
81
+ ### As a Gem Dependency
82
+
83
+ If your custom plugin will be packaged into a gem, add the following to your plugin’s `.gemspec`:
65
84
 
66
85
  ```ruby
67
86
  Gem::Specification.new do |spec|
68
87
  # ...
69
- spec.add_dependency 'jekyll_plugin_support', '>= 0.8.0'
88
+ spec.add_dependency 'jekyll_plugin_support', '>= 1.1.0'
70
89
  # ...
71
90
  end
72
91
  ```
73
92
 
74
- Install the `jekyll_plugin_support` Ruby gem and mark it as a dependency of your project by typing:
93
+ Install the `jekyll_plugin_support` gem into your plugin project in the usual manner:
94
+
95
+ ```shell
96
+ $ bundle
97
+ ```
98
+
99
+ Copy the CSS classes from
100
+ [`demo/assets/css/jekyll_plugin_support.css`](demo/assets/css/jekyll_plugin_support.css)
101
+ to your Jekyll project&rsquo;s CSS file.
75
102
 
76
- ```shell
77
- $ bundle
78
- ```
79
103
 
80
- Copy the CSS classes from `demo/assets/css/jekyll_plugin_support.css` to your Jekyll project&rsquo;s CSS file.
81
104
 
82
105
 
83
106
  ## About `jekyll_plugin_support`
@@ -104,13 +127,21 @@ Instead, define a method called `render_impl`.
104
127
  For inline tags, `render_impl` does not accept any parameters.
105
128
  For block tags, a single parameter is required, which contains text passed from your block in the page.
106
129
 
107
- Your implementation of render_impl can parse parameters passed to the tag / block tag, as described in
130
+ Your implementation of render_impl can parse parameters passed to your tag, as described in
108
131
  [Tag Parameter Parsing](http://mslinn.com/jekyll/10100-jekyll-plugin-background.html#params).
109
132
 
133
+ In addition, within <code>render_impl</code>,
134
+ the arguments passed to the tag will have been tokenized and parsed,
135
+ with Jekyll and Liquid variables substituted for their values,
136
+ and all the public Jekyll variables will be available as instance variables.
137
+ Error handling will also have been set up,
138
+ and access to your tag's entry within <code>_config.yml</code> will have been set up.
139
+
140
+
110
141
 
111
142
  ## General Usage
112
143
 
113
- Please see the [`demo/`](demo/) project for a well-documented set of demonstration Jekyll plugins that are built from `jekyll_plugin_support`.
144
+ Please see the [`demo/`](demo/) project for a well-documented set of demonstration Jekyll plugins built from `jekyll_plugin_support`.
114
145
  Additional information is available [here](https://mslinn.com/jekyll/10200-jekyll-plugin-background.html) and the
115
146
  [`jekyll_plugin_support`](https://www.mslinn.com/jekyll_plugins/jekyll_plugin_support.html) documentation.
116
147
 
@@ -141,7 +172,7 @@ because both `JekyllSupport` classes define this method.
141
172
 
142
173
  Instead, define a method called `render_impl`.
143
174
  For inline tags, `render_impl` does not accept any parameters.
144
- For block tags, a single parameter is required, which contains any text enclosed within your block.
175
+ For block tags, a single parameter is required, which receives any text enclosed within your block by the website author.
145
176
 
146
177
 
147
178
  ## Predefined Plugin Variables
@@ -150,10 +181,12 @@ For block tags, a single parameter is required, which contains any text enclosed
150
181
 
151
182
  * `@argument_string` Unparsed markup passed as a parameter to your block tag and inline tag.
152
183
 
184
+ * `@argv` returns any remaining tokens after `parameter_specified?` has been invoked.
185
+
153
186
  * [`@attribution`](#subclass-attribution) Attribution markup
154
187
 
155
188
  * [`@config`](https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/about-github-pages-and-jekyll#configuring-jekyll-in-your-github-pages-site)
156
- [YAML](https://yaml.org/) Jekyll site configuration file
189
+ The [YAML](https://yaml.org/) Jekyll site configuration file
157
190
 
158
191
  * [`@helper`](https://github.com/mslinn/jekyll_plugin_support/blob/master/lib/jekyll_plugin_helper.rb)
159
192
  `JekyllPluginHelper` instance for your plugin.
@@ -165,7 +198,7 @@ For block tags, a single parameter is required, which contains any text enclosed
165
198
  * [`@mode`](https://jekyllrb.com/docs/configuration/environments/)
166
199
  Indicates `production`, `test` or `development` mode.
167
200
 
168
- * [`@page`](https://jekyllrb.com/docs/variables/#page-variables) Page variables
201
+ * [`@page`](https://jekyllrb.com/docs/variables/#page-variables) `Jekyll::Page` variables
169
202
 
170
203
  * [`@paginator`](https://jekyllrb.com/docs/variables/#page-variables) Pagination variables
171
204
 
@@ -322,7 +355,7 @@ call `@helper.remaining_markup` to obtain the remaining markup that was passed t
322
355
 
323
356
  `jekyll_plugin_support` provides support for
324
357
  [Liquid variables](https://shopify.github.io/liquid/tags/variable/)
325
- to be defined in `_config.yml`, in a section called `liquid-vars`.
358
+ to be defined in `_config.yml`, in a section called `liquid_vars`.
326
359
  These variables behave exactly like Liquid variables defined by `assign` and `capture` expressions,
327
360
  except they are global in scope; these variables are available in every Jekyll web page.
328
361
 
@@ -339,7 +372,7 @@ Liquid variables defined in this manner are intended to be embedded in a webpage
339
372
  They are can be used like any other Liquid variable.
340
373
 
341
374
 
342
- ### Variable Expansion
375
+ ## Variable Expansion
343
376
 
344
377
  Jekyll expands Liquid variable references during the page rendering process.
345
378
  Jekyll does not expand Liquid variable references passes as parameters to tag and block plugins, however.
@@ -444,7 +477,27 @@ Similarly, the letters `y` and `z` are pronounced {{y}} and {{z}}.
444
477
  ```
445
478
 
446
479
 
447
- ### Automatically Created Error Classes
480
+ ### Evaluating Include Variables
481
+
482
+ This information is only useful if a plugin might be executed from within an included file.
483
+
484
+ While Liquid handles regular variables, Jekyll has special handling for variables defined by include parameters.
485
+ For example, the following defines a variable in the `include` scope called `var1`
486
+ when processing the body of an included file:
487
+
488
+ ```html
489
+ {% include myfile.html var1='value1' %}
490
+ ```
491
+
492
+ You can obtain the value of this variable from the `render_impl` method of a
493
+ `JekyllSupport::JekyllTag` or `JekyllSupport::JekyllBlock` subclass as follows:
494
+
495
+ ```ruby
496
+ @var1 = @scopes.first['include']&.[]('var1')
497
+ ```
498
+
499
+
500
+ ## Automatically Created Error Classes
448
501
 
449
502
  `JekyllSupport::JekyllBlock` and `JekyllSupport::JekyllTag` subclasses
450
503
  automatically create error classes, named after the subclass.
@@ -452,7 +505,8 @@ automatically create error classes, named after the subclass.
452
505
  For example, if you create a `JekyllSupport::JekyllBlock` subclass called `DemoBlockTag`,
453
506
  the automatically generated error class will be called `DemoBlockTagError`.
454
507
 
455
- Although you could use it as you would any other error class, `JekyllPluginSupport` provides some helper methods.
508
+ Although you could use it as you would any other error class, `JekyllPluginSupport`
509
+ provides additional helper methods.
456
510
  These methods fill in the page path and line number that caused the error, shorten the stack trace,
457
511
  log an error message, and can be used to return an HTML-friendly version of the message to the web page.
458
512
 
@@ -482,7 +536,7 @@ Error class methods have been provided for standardized and convenient error han
482
536
  * `html_message` - The same as `logger_message`, but constructed with HTML.
483
537
 
484
538
 
485
- ### Self-Reporting Upon Registration
539
+ ## Self-Reporting Upon Registration
486
540
 
487
541
  When each tag is registered, it self-reports, for example:
488
542
 
@@ -514,6 +568,83 @@ If your tag or block plugin only needs access to the raw arguments passed from t
514
568
  without tokenization, and you expect that the plugin might be invoked with large amounts of text,
515
569
  derive your plugin from `JekyllBlockNoArgParsing` or `JekyllTagNoArgParsing`.
516
570
 
571
+ ## Writing Plugins
572
+
573
+ The following minimal examples define `VERSION`,
574
+ which is important because `JekyllPluginHelper.register` logs that value when registering the plugin.
575
+
576
+ This is how you would define plugins in the `_plugins` directory
577
+
578
+ **Boilerplate for an inline tag plugin**
579
+
580
+ ```ruby
581
+ require 'jekyll_plugin_support'
582
+
583
+ module Jekyll
584
+ class DemoTag < JekyllSupport::JekyllTag
585
+ VERSION = '0.1.0'.freeze
586
+
587
+ def render_impl
588
+ @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem
589
+ # Your Jekyll plugin logic goes here
590
+ end
591
+
592
+ JekyllPluginHelper.register(self, 'demo_tag')
593
+ end
594
+ end
595
+ ```
596
+
597
+ **Boilerplate for a tag block plugin**
598
+
599
+ ```ruby
600
+ require 'jekyll_plugin_support'
601
+
602
+ module Jekyll
603
+ class DemoBlock < JekyllSupport::JekyllBlock
604
+ VERSION = '0.1.0'.freeze
605
+
606
+ def render_impl(text)
607
+ @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem
608
+ # Your Jekyll plugin logic goes here
609
+ end
610
+
611
+ JekyllPluginHelper.register(self, 'demo_block')
612
+ end
613
+ end
614
+ ```
615
+
616
+ If your plugin is packaged as a gem, then you might need to include `version.rb` into the plugin class.
617
+ For example, if your version module looks like this:
618
+
619
+ **lib/my_plugin/version.rb**:
620
+
621
+ ```ruby
622
+ module MyPluginVersion
623
+ VERSION = '0.5.0'.freeze
624
+ end
625
+ ```
626
+
627
+ Then your plugin can incorporate the VERSION constant into your plugin like this:
628
+
629
+ **lib/my_plugin.rb**:
630
+
631
+ ```ruby
632
+ require 'jekyll_plugin_support'
633
+ require_relative 'my_plugin/version'
634
+
635
+ module Jekyll
636
+ class MyBlock < JekyllSupport::JekyllBlock
637
+ include MyPluginVersion
638
+
639
+ def render_impl(text)
640
+ @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem
641
+ # Your code here
642
+ end
643
+
644
+ JekyllPluginHelper.register(self, 'demo_block')
645
+ end
646
+ end
647
+ ```
517
648
 
518
649
  ## Attribution
519
650
 
@@ -583,7 +714,7 @@ An alternative attribution string can be specified properties can be output usin
583
714
  {% my_tag attribution="Generated by the #{name} #{version} Jekyll plugin, written by #{author} #{date}" %}
584
715
  ```
585
716
 
586
- ## Subclassing
717
+ ## Subclassing Plugins
587
718
 
588
719
  Jekyll plugins created using `jekyll_plugin_support` are implemented as Ruby classes.
589
720
  If you would like to create a version of an existing Jekyll plugin, you will need to subclass the plugin.
@@ -647,9 +778,203 @@ blah:
647
778
 
648
779
  ## Demonstration Plugins and Website
649
780
 
650
- A demo / test website is provided in the `demo` directory.
781
+ A demo / test website is provided in the [`demo`](demo) directory.
651
782
  It can be used to debug the plugin or to run freely.
652
783
 
784
+ ### Examining the Demo Plugins
785
+
786
+ The following example plugins use
787
+ [Ruby’s squiggly heredoc operator](https://ruby-doc.org/core-2.5.0/doc/syntax/literals_rdoc.html#label-Here+Documents) (`<<~`).
788
+ The squiggly heredoc operator removes the outermost indentation.
789
+ This provides easy-to-read multiline text literals.
790
+
791
+ **demo/_plugins/demo_tag.rb**:
792
+
793
+ ```ruby
794
+ require 'jekyll_plugin_support'
795
+
796
+ # Use the JekyllSupport module namespace so the self methods are automajically found
797
+ module JekyllSupport
798
+ DemoInlineTagError = JekyllSupport.define_error
799
+
800
+ class DemoTag < JekyllTag
801
+ VERSION = '0.1.2'.freeze
802
+ # JekyllSupport.redef_without_warning 'VERSION', '0.1.2'.freeze
803
+
804
+ def render_impl
805
+ @demo_tag_error = @helper.parameter_specified? 'raise_demo_tag_error'
806
+ @keyword1 = @helper.parameter_specified? 'keyword1'
807
+ @keyword2 = @helper.parameter_specified? 'keyword2'
808
+ @name1 = @helper.parameter_specified? 'name1'
809
+ @name2 = @helper.parameter_specified? 'name2'
810
+ @standard_error = @helper.parameter_specified? 'raise_standard_error'
811
+
812
+ if @tag_config
813
+ @die_on_demo_tag_error = @tag_config['die_on_demo_tag_error'] == true
814
+ @die_on_standard_error = @tag_config['die_on_standard_error'] == true
815
+ end
816
+
817
+ raise DemoInlineTagError, 'This DemoInlineTagError error is expected.' if @demo_tag_error
818
+ raise StandardError, 'This StandardError error is expected.' if @standard_error
819
+
820
+ # _infinity = 1 / 0 if @standard_error # Not required
821
+
822
+ output
823
+ rescue DemoInlineTagError => e # jekyll_plugin_support handles StandardError
824
+ @logger.error { e.logger_message }
825
+ exit! 1 if @die_on_demo_tag_error
826
+
827
+ e.html_message
828
+ end
829
+
830
+ private
831
+
832
+ def output
833
+ <<~END_OUTPUT
834
+ <pre># jekyll_plugin_support becomes able to perform variable substitution after this variable is defined.
835
+ # The value could be updated at a later stage, but no need to add that complexity unless there is a use case.
836
+ @argument_string="#{@argument_string}"
837
+
838
+ @helper.argv=
839
+ #{@helper.argv&.join("\n ")}
840
+
841
+ # Liquid variable name/value pairs
842
+ @helper.params=
843
+ #{@helper.params&.map { |k, v| "#{k}=#{v}" }&.join("\n ")}
844
+
845
+ # The keys_values property serves no purpose any more, consider it deprecated
846
+ @helper.keys_values=
847
+ #{(@helper.keys_values&.map { |k, v| "#{k}=#{v}" })&.join("\n ")}
848
+
849
+ @layout='#{@layout}'
850
+ @page.keys='#{@page.keys}'
851
+
852
+ remaining_markup='#{@helper.remaining_markup}'
853
+
854
+ @keyword1='#{@keyword1}'
855
+ @keyword2='#{@keyword2}'
856
+ @name1='#{@name1}'
857
+ @name2='#{@name2}'</pre>
858
+ END_OUTPUT
859
+ end
860
+
861
+ JekyllPluginHelper.register(self, 'demo_inline_tag')
862
+ end
863
+ end
864
+ ```
865
+
866
+ **demo/_plugins/demo_block.rb**:
867
+
868
+ ```ruby
869
+ require 'cgi'
870
+ require 'jekyll_plugin_support'
871
+
872
+ # Use the JekyllSupport module namespace so the self methods are automajically found
873
+ module JekyllSupport
874
+ DemoBlockError = JekyllSupport.define_error
875
+
876
+ class DemoBlock < JekyllBlock
877
+ VERSION = '0.1.2'.freeze
878
+
879
+ def render_impl(text)
880
+ @demo_block_error = @helper.parameter_specified? 'raise_demo_block_error'
881
+ @keyword1 = @helper.parameter_specified? 'keyword1'
882
+ @keyword2 = @helper.parameter_specified? 'keyword2'
883
+ @name1 = @helper.parameter_specified? 'name1'
884
+ @name2 = @helper.parameter_specified? 'name2'
885
+ @standard_error = @helper.parameter_specified? 'raise_standard_error'
886
+
887
+ if @tag_config
888
+ @die_on_demo_block_error = @tag_config['die_on_demo_block_error'] == true
889
+ @die_on_standard_error = @tag_config['die_on_standard_error'] == true
890
+ end
891
+
892
+ raise DemoBlockTagError, 'This DemoBlockTagError error is expected.' if @demo_block_error
893
+ raise StandardError, 'This StandardError error is expected.' if @standard_error
894
+
895
+ # _infinity = 1 / 0 if @standard_error # Not required
896
+
897
+ output text
898
+ rescue DemoBlockTagError => e # jekyll_plugin_support handles StandardError
899
+ @logger.error { e.logger_message }
900
+ exit! 1 if @die_on_demo_block_error
901
+
902
+ e.html_message
903
+ end
904
+
905
+ private
906
+
907
+ def output(text)
908
+ <<~END_OUTPUT
909
+ <pre>@helper.tag_name=#{@helper.tag_name}
910
+
911
+ @mode=#{@mode}
912
+
913
+ # jekyll_plugin_support becomes able to perform variable substitution after this variable is defined.
914
+ # The value could be updated at a later stage, but no need to add that complexity unless there is a use case.
915
+ @argument_string="#{@argument_string}"
916
+
917
+ @helper.argv=
918
+ #{@helper.argv&.join("\n ")}
919
+
920
+ # Liquid variable name/value pairs
921
+ @helper.params=
922
+ #{@helper.params&.map { |k, v| "#{k}=#{v}" }&.join("\n ")}
923
+
924
+ # The keys_values property serves no purpose any more, consider it deprecated
925
+ @helper.keys_values=
926
+ #{(@helper.keys_values&.map { |k, v| "#{k}=#{v}" })&.join("\n ")}
927
+
928
+ @helper.remaining_markup='#{@helper.remaining_markup}'
929
+
930
+ @envs=#{@envs.keys.sort.join(', ')}
931
+
932
+ @config['url']='#{@config['url']}'
933
+
934
+ @site.collection_names=#{@site.collection_names&.sort&.join(', ')}
935
+
936
+ @page['description']=#{@page['description']}
937
+
938
+ @page['path']=#{@page['path']}
939
+
940
+ @keyword1=#{@keyword1}
941
+
942
+ @keyword2=#{@keyword2}
943
+
944
+ @name1=#{@name1}
945
+
946
+ @name2=#{@name2}
947
+
948
+ text=#{text}</pre>
949
+ END_OUTPUT
950
+ end
951
+
952
+ JekyllPluginHelper.register(self, 'demo_block_tag')
953
+ end
954
+ end
955
+ ```
956
+
957
+ The following is an example of no_arg_parsing optimization.
958
+
959
+ ```ruby
960
+ require 'jekyll_plugin_support'
961
+
962
+ # Use the JekyllSupport module namespace so the self methods are automajically found
963
+ module JekyllSupport
964
+ class DemoTagNoArgs < JekyllTagNoArgParsing
965
+ VERSION = '0.1.0'.freeze
966
+
967
+ def render_impl
968
+ <<~END_OUTPUT
969
+ The raw arguments passed to this <code>DemoTagNoArgs</code> instance are:<br>
970
+ <code>#{@argument_string}</code>
971
+ END_OUTPUT
972
+ end
973
+
974
+ JekyllPluginHelper.register(self, 'demo_inline_tag_no_arg')
975
+ end
976
+ end
977
+ ```
653
978
 
654
979
  ### Run Freely
655
980
 
@@ -668,6 +993,10 @@ It can be used to debug the plugin or to run freely.
668
993
 
669
994
  1. Set breakpoints in Visual Studio Code.
670
995
 
996
+ 2. Run the **Debug Demo development** or **Debug Demo production** launch configuration.
997
+
998
+ Alternatively, you can:
999
+
671
1000
  2. Initiate a debug session from the command line by running the `demo/_bin/debug` script:
672
1001
 
673
1002
  ```shell
@@ -694,7 +1023,7 @@ It can be used to debug the plugin or to run freely.
694
1023
  ```
695
1024
 
696
1025
  3. Once the `DEBUGGER: wait for debugger connection...` message appears,
697
- run the Visual Studio Code launch configuration called `Attach with rdbg`.
1026
+ run the Visual Studio Code launch configuration called **Attach with rdbg**.
698
1027
 
699
1028
  4. View the generated website,
700
1029
  which might be at [`http://localhost:4444`](http://localhost:4444),
@@ -3,12 +3,12 @@ require_relative 'lib/jekyll_plugin_support/version'
3
3
  Gem::Specification.new do |spec|
4
4
  github = 'https://github.com/mslinn/jekyll_plugin_support'
5
5
 
6
- spec.bindir = 'exe'
7
- spec.authors = ['Mike Slinn']
8
- spec.email = ['mslinn@mslinn.com']
9
- spec.files = Dir['.rubocop.yml', 'LICENSE.*', 'Rakefile', '{lib,spec}/**/*', '*.gemspec', '*.md']
6
+ spec.bindir = 'exe'
7
+ spec.authors = ['Mike Slinn']
8
+ spec.email = ['mslinn@mslinn.com']
9
+ spec.files = Dir['.rubocop.yml', 'LICENSE.*', 'Rakefile', '{lib,spec}/**/*', '*.gemspec', '*.md']
10
10
  spec.homepage = 'https://www.mslinn.com/jekyll_plugins/jekyll_plugin_support.html'
11
- spec.license = 'MIT'
11
+ spec.license = 'MIT'
12
12
  spec.metadata = {
13
13
  'allowed_push_host' => 'https://rubygems.org',
14
14
  'bug_tracker_uri' => "#{github}/issues",
@@ -16,17 +16,18 @@ Gem::Specification.new do |spec|
16
16
  'homepage_uri' => spec.homepage,
17
17
  'source_code_uri' => github,
18
18
  }
19
- spec.name = 'jekyll_plugin_support'
19
+ spec.name = 'jekyll_plugin_support'
20
+ spec.platform = Gem::Platform::RUBY
20
21
  spec.post_install_message = <<~END_MESSAGE
21
22
 
22
23
  Thanks for installing #{spec.name}!
23
24
 
24
25
  END_MESSAGE
25
- spec.require_paths = ['lib']
26
+ spec.require_paths = ['lib']
26
27
  spec.required_ruby_version = '>= 2.6.0'
27
- spec.summary = 'Provides a framework for writing and testing Jekyll plugins'
28
- spec.test_files = spec.files.grep %r{^(test|spec|features)/}
29
- spec.version = JekyllPluginSupportVersion::VERSION
28
+ spec.summary = 'Provides a framework for writing and testing Jekyll plugins'
29
+ spec.test_files = spec.files.grep %r{^(test|spec|features)/}
30
+ spec.version = JekyllPluginSupportVersion::VERSION
30
31
 
31
32
  spec.add_dependency 'facets'
32
33
  spec.add_dependency 'jekyll', '>= 3.5.0'
@@ -8,16 +8,28 @@ module JekyllSupport
8
8
  # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
9
9
  # @param tag_name [String] the name of the tag, which we usually know.
10
10
  # @param argument_string [String] the arguments passed to the tag, as a single string.
11
- # @param parse_context [Liquid::ParseContext] hash that stores Liquid options.
12
- # By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
13
- # a boolean parameter that determines if error messages should display the line number the error occurred.
14
- # This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
15
- # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
11
+ # @param parse_context [Liquid::ParseContext] contains the following attributes:
12
+ # @depth might have the value 0
13
+ # @error_mode might have the value `:strict`
14
+ # @line_number duplicates @ptions[:line_number]
15
+ # @locale duplicates @ptions[:locale]
16
+ # @options is a hash with the following two keys that holds Liquid options:
17
+ # :locale is a Liquid::I18n object, used to display localized error messages on Liquid built-in tags and filters.
18
+ # :line_number is the line number containing the plugin invocation.
19
+ # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
20
+ # @partial Boolean, unclear what this indicates
21
+ # @template_options Replicates @options
22
+ # @trim_whitespace might have the value `false`
23
+ # @warnings array
16
24
  # @return [void]
17
25
  def initialize(tag_name, markup, parse_context)
18
26
  super
19
27
  @tag_name = tag_name
20
- @argument_string = markup.to_s # Vars in plugin parameters cannot be replaced yet
28
+ raise JekyllPluginSupportError, "markup is a #{markup.class} with value '#{markup}'." unless markup.instance_of? String
29
+
30
+ # Lookup variable names with values in markup in render because site and config are not available here
31
+ @argument_string = markup # Replace variable names with values in markup in render because site and config are not available here
32
+
21
33
  @logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
22
34
  @logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
23
35
  @helper = JekyllPluginHelper.new tag_name, markup, @logger, respond_to?(:no_arg_parsing)
@@ -36,11 +48,11 @@ module JekyllSupport
36
48
  # Defines @config, @envs, @mode, @page and @site
37
49
  # @return [String]
38
50
  def render(liquid_context)
39
- @helper.liquid_context = JekyllSupport.inject_vars @logger, liquid_context
51
+ @helper.liquid_context = JekyllSupport.inject_config_vars liquid_context # modifies liquid_context
40
52
  text = super # Liquid variable values in content are looked up and substituted
41
53
 
42
54
  @envs = liquid_context.environments.first
43
- @page = liquid_context.registers[:page] # hash
55
+ @page = liquid_context.registers[:page]
44
56
  @scopes = liquid_context.scopes
45
57
  @site = liquid_context.registers[:site]
46
58
 
@@ -51,6 +63,7 @@ module JekyllSupport
51
63
 
52
64
  set_error_context
53
65
 
66
+ # @envs.keys are :content, :highlighter_prefix, :highlighter_suffix, :jekyll, :layout, :page, :paginator, :site, :theme
54
67
  @layout = @envs[:layout]
55
68
  @paginator = @envs[:paginator]
56
69
  @theme = @envs[:theme]
@@ -58,18 +71,23 @@ module JekyllSupport
58
71
  env = @config['env']
59
72
  @mode = env&.key?('JEKYLL_ENV') ? env['JEKYLL_ENV'] : 'development'
60
73
 
61
- @helper.reinitialize @markup.strip
74
+ @argument_string = JekyllSupport.lookup_liquid_variables @logger, @helper.liquid_context, @argument_string.to_s.strip
75
+ @helper.reinitialize @argument_string.to_s.strip
62
76
 
63
77
  @attribution = @helper.parameter_specified?('attribution') || false unless @no_arg_parsing
64
78
  @logger.debug { "@keys_values='#{@keys_values}'" }
65
79
 
66
- markup = JekyllSupport.lookup_liquid_variables liquid_context, @argument_string
67
- @helper.reinitialize markup
80
+ # @argument_string = JekyllSupport.lookup_liquid_variables @logger, liquid_context, @argument_string # Is this redundant?
81
+ # @argument_string.strip! # Is this redundant?
82
+ # @helper.reinitialize @argument_string # Is this redundant?
68
83
 
69
84
  render_impl(text)
70
85
  rescue StandardError => e
71
86
  e.shorten_backtrace
72
- @logger.error { "#{e.class} on line #{@line_number} of #{e.backtrace[0].split(':').first} by #{@tag_name} - #{e.message}" }
87
+ file_name = e.backtrace[0]&.split(':')&.first
88
+ in_file_name = "in '#{file_name}' " if file_name
89
+ of_page = "of '#{@page['path']}'" if @page
90
+ @logger.error { "#{e.class} on line #{@line_number} #{of_page} while processing #{tag_name} #{in_file_name}- #{e.message}" }
73
91
  binding.pry if @pry_on_standard_error # rubocop:disable Lint/Debugger
74
92
  raise e if @die_on_standard_error
75
93
 
@@ -23,7 +23,7 @@ module JekyllSupport
23
23
  @tag_name = tag_name
24
24
  @logger = logger
25
25
  @no_arg_parsing = no_arg_parsing
26
- @markup = markup
26
+ @argument_string = markup
27
27
  rescue StandardError => e
28
28
  e.shorten_backtrace
29
29
  @logger.error { e.message }
@@ -42,7 +42,7 @@ module JekyllSupport
42
42
 
43
43
  def reinitialize(markup)
44
44
  # @keys_values was a Hash[Symbol, String|Boolean] but now it is Hash[String, String|Boolean]
45
- @markup = markup
45
+ @argument_string = markup
46
46
  if @no_arg_parsing
47
47
  define_singleton_method(:argv) { warn_fetch :argv }
48
48
  define_singleton_method(:keys_values) { warn_fetch :keys_values }
@@ -55,7 +55,10 @@ module JekyllSupport
55
55
  END_MSG
56
56
  end
57
57
 
58
- def self.register(klass, tag_name)
58
+ # @param klass [Class] class instance to register
59
+ # @param tag_name [String] name of plugin defined by klass to register as
60
+ # @param quiet [Boolean] suppress registration message if truthy
61
+ def self.register(klass, tag_name, quiet: false)
59
62
  abort("Error: The #{tag_name} plugin does not define VERSION") \
60
63
  unless klass.const_defined?(:VERSION)
61
64
 
@@ -67,6 +70,8 @@ module JekyllSupport
67
70
  klass.ancestors.include?(JekyllSupport::JekyllTag))
68
71
 
69
72
  Liquid::Template.register_tag(tag_name, klass)
73
+ return if quiet
74
+
70
75
  msg = generate_message(klass, tag_name, version)
71
76
  PluginMetaLogger.instance.info { msg }
72
77
  end
@@ -39,23 +39,21 @@ module JekyllSupport
39
39
  END_MSG
40
40
  end
41
41
 
42
- # Add variable definitions from _config.yml to liquid_context
43
- # Modifies liquid_context in the caller
42
+ # Inject variable definitions from _config.yml into liquid_context
43
+ # Modifies liquid_context.scopes in the caller
44
44
  # (call by object reference, see https://stackoverflow.com/a/1872159/553865)
45
45
  # @return modified liquid_context
46
46
  # See README.md#configuration-variable-definitions
47
47
  # See demo/variables.html
48
- def self.inject_vars(_logger, liquid_context)
49
- # TODO: Modify a deep clone? Do I dare?
48
+ def self.inject_config_vars(liquid_context)
50
49
  site = liquid_context.registers[:site]
51
50
 
52
51
  plugin_variables = site.config['liquid_vars']
53
- return liquid_context unless plugin_variables
54
52
 
55
53
  scope = liquid_context.scopes.last
56
54
 
57
55
  env = site.config['env']
58
- mode = env&.key?('JEKYLL_ENV') ? env['JEKYLL_ENV'] : 'development'
56
+ @mode = env&.key?('JEKYLL_ENV') ? env['JEKYLL_ENV'] : 'development'
59
57
 
60
58
  # Set default values
61
59
  plugin_variables&.each do |name, value|
@@ -63,7 +61,7 @@ module JekyllSupport
63
61
  end
64
62
 
65
63
  # Override with environment-specific values
66
- plugin_variables[mode]&.each do |name, value|
64
+ plugin_variables&.[](@mode)&.each do |name, value|
67
65
  scope[name] = value if value.instance_of? String
68
66
  end
69
67
 
@@ -85,34 +83,71 @@ module JekyllSupport
85
83
  # Modifies a clone of markup_original so variable references are replaced by their values
86
84
  # @param markup_original to be cloned
87
85
  # @return modified markup_original
88
- def self.lookup_liquid_variables(liquid_context, markup_original)
86
+ def self.lookup_liquid_variables(logger, liquid_context, markup_original)
89
87
  markup = markup_original.clone
90
- page = liquid_context.registers[:page]
88
+ page = liquid_context.registers[:page]
91
89
  envs = liquid_context.environments.first
92
90
  layout = envs[:layout]
93
91
 
94
- # process layout variables
92
+ markup = process_layout_variables logger, layout, markup
93
+ markup = process_page_variables logger, page, markup
94
+ liquid_context.scopes&.each do |scope|
95
+ markup = process_included_variables logger, scope, markup
96
+ markup = process_liquid_variables logger, scope, markup
97
+ end
98
+ markup
99
+ rescue StandardError => e
100
+ logger.error { e.full_message }
101
+ end
102
+
103
+ def self.process_included_variables(logger, scope, markup)
104
+ scope['include']&.each do |name, value|
105
+ if value.nil?
106
+ value = ''
107
+ logger.warn { "include.#{name} is undefined." }
108
+ end
109
+ markup.gsub!("{{include.#{name}}}", value)
110
+ end
111
+ markup
112
+ rescue StandardError => e
113
+ logger.error { e.full_message }
114
+ end
115
+
116
+ def self.process_layout_variables(logger, layout, markup)
95
117
  layout&.each do |name, value|
118
+ if value.nil?
119
+ value = ''
120
+ logger.warn { "layout.#{value} is undefined." }
121
+ end
96
122
  markup.gsub!("{{layout.#{name}}}", value.to_s)
97
123
  end
124
+ markup
125
+ rescue StandardError => e
126
+ logger.error { e.full_message }
127
+ end
98
128
 
99
- # process page variables
100
- # puts "\nStarting page variable processing of #{page['path']}; stack has #{caller.length} elements".green
101
- keys = page.keys
102
- %w[excerpt output].each { |key| keys.delete key }
103
- # puts " Filtered keys: #{keys.join ' '}"
104
- # keys.each { |key| puts " #{key}: #{page[key]}" }
105
- keys&.each do |key|
106
- markup.gsub!("{{page.#{key}}}", page[key].to_s)
129
+ # Process assigned, captured and injected variables
130
+ def self.process_liquid_variables(logger, scope, markup)
131
+ scope&.each do |name, value|
132
+ next if name.nil?
133
+
134
+ value = '' if value.nil?
135
+ markup.gsub!("{{#{name}}}", value&.to_s)
107
136
  end
137
+ markup
138
+ rescue StandardError => e
139
+ logger.error { e.full_message }
140
+ end
108
141
 
109
- # Process assigned, captured and injected variables
110
- liquid_context.scopes.each do |scope|
111
- scope&.each do |name, value|
112
- markup.gsub!("{{#{name}}}", value.to_s)
113
- end
142
+ def self.process_page_variables(logger, page, markup)
143
+ page&.each_key do |key|
144
+ next if %w[content excerpt next previous output].include? key # Skip problem attributes
145
+
146
+ markup.gsub!("{{page.#{key}}}", page[key].to_s)
114
147
  end
115
148
  markup
149
+ rescue StandardError => e
150
+ logger.error { e.full_message }
116
151
  end
117
152
 
118
153
  def self.warn_short_trace(logger, error)
@@ -1,3 +1,3 @@
1
1
  module JekyllPluginSupportVersion
2
- VERSION = '1.0.3'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -3,7 +3,7 @@ require 'jekyll'
3
3
  require 'jekyll_plugin_logger'
4
4
 
5
5
  def require_directory(dir)
6
- Dir[File.join(dir, '*.rb')].sort.each do |file|
6
+ Dir[File.join(dir, '*.rb')]&.sort&.each do |file|
7
7
  require file unless file == __FILE__
8
8
  end
9
9
  end
@@ -9,18 +9,28 @@ module JekyllSupport
9
9
  # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
10
10
  # @param tag_name [String] the name of the tag, which we usually know.
11
11
  # @param argument_string [String] the arguments passed to the tag, as a single string.
12
- # @param parse_context [Liquid::ParseContext] hash that stores Liquid options.
13
- # By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
14
- # a boolean parameter that determines if error messages should display the line number the error occurred.
15
- # This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
16
- # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
12
+ # @param parse_context [Liquid::ParseContext] contains the following attributes:
13
+ # @depth might have the value 0
14
+ # @error_mode might have the value `:strict`
15
+ # @line_number duplicates @ptions[:line_number]
16
+ # @locale duplicates @ptions[:locale]
17
+ # @options is a hash with the following two keys that holds Liquid options:
18
+ # :locale is a Liquid::I18n object, used to display localized error messages on Liquid built-in tags and filters.
19
+ # :line_number is the line number containing the plugin invocation.
20
+ # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
21
+ # @partial Boolean, unclear what this indicates
22
+ # @template_options Replicates @options
23
+ # @trim_whitespace might have the value `false`
24
+ # @warnings array
17
25
  # @return [void]
18
26
  def initialize(tag_name, markup, parse_context)
19
27
  super
20
28
  @tag_name = tag_name
21
29
  raise JekyllPluginSupportError, "markup is a #{markup.class} with value '#{markup}'." unless markup.instance_of? String
22
30
 
23
- @argument_string = markup
31
+ # Vars in plugin parameters cannot be replaced yet
32
+ @argument_string = markup.to_s # Lookup variable names with values in markup in render because site and config are not available here
33
+
24
34
  @logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
25
35
  @logger.debug { "#{self.class}: respond_to?(:no_arg_parsing) #{respond_to?(:no_arg_parsing) ? 'yes' : 'no'}." }
26
36
  @helper = JekyllPluginHelper.new(tag_name, @argument_string, @logger, respond_to?(:no_arg_parsing))
@@ -33,7 +43,7 @@ module JekyllSupport
33
43
  def render(liquid_context)
34
44
  return if @helper.excerpt_caller
35
45
 
36
- @helper.liquid_context = JekyllSupport.inject_vars @logger, liquid_context
46
+ @helper.liquid_context = JekyllSupport.inject_config_vars liquid_context # modifies liquid_context
37
47
 
38
48
  @envs = liquid_context.environments.first
39
49
  @page = liquid_context.registers[:page]
@@ -55,8 +65,12 @@ module JekyllSupport
55
65
  env = @config['env']
56
66
  @mode = env&.key?('JEKYLL_ENV') ? env['JEKYLL_ENV'] : 'development'
57
67
 
58
- markup = JekyllSupport.lookup_liquid_variables liquid_context, @argument_string
59
- @helper.reinitialize markup.strip
68
+ @argument_string = JekyllSupport.lookup_liquid_variables @logger, @helper.liquid_context, @argument_string.to_s.strip
69
+ @helper.reinitialize @argument_string.to_s.strip
70
+
71
+ # @argument_string = JekyllSupport.lookup_liquid_variables @logger, liquid_context, @argument_string # Is this redundant?
72
+ # @argument_string.strip! # Is this redundant?
73
+ # @helper.reinitialize @argument_string # Is this redundant?
60
74
 
61
75
  render_impl
62
76
  rescue StandardError => e
@@ -64,13 +78,13 @@ module JekyllSupport
64
78
  file_name = e.backtrace[0]&.split(':')&.first
65
79
  in_file_name = "in '#{file_name}' " if file_name
66
80
  of_page = "of '#{@page['path']}'" if @page
67
- @logger.error { "#{e.class} on line #{@line_number} #{of_page}while processing #{tag_name} #{in_file_name}- #{e.message}" }
81
+ @logger.error { "#{e.class} on line #{@line_number} #{of_page} while processing #{tag_name} #{in_file_name}- #{e.message}" }
68
82
  binding.pry if @pry_on_standard_error # rubocop:disable Lint/Debugger
69
83
  raise e if @die_on_standard_error
70
84
 
71
85
  <<~END_MSG
72
86
  <div class='standard_error'>
73
- #{e.class} on line #{@line_number} #{of_page}while processing #{tag_name} #{in_file_name} - #{e.message}
87
+ #{e.class} on line #{@line_number} #{of_page} while processing #{tag_name} #{in_file_name} - #{JekyllPluginHelper.remove_html_tags e.message}
74
88
  </div>
75
89
  END_MSG
76
90
  end
@@ -8,12 +8,12 @@ class LiquidVariableParsing
8
8
  def variable_replace(str, scopes)
9
9
  result = str.clone
10
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|
11
+ match_data_list&.each do |md|
12
12
  from = md.begin(0)
13
13
  to = md.end(0) - 1
14
14
  ref = str[from..to]
15
15
  name = ref[2..-3]
16
- scopes.each do |scope|
16
+ scopes&.each do |scope|
17
17
  value = scope.key?(name) ? scope[name] : ref
18
18
  # puts "str=#{str}; from=#{from}; to=#{to}; name=#{name} value=#{value}"
19
19
  result[from..to] = value
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll_plugin_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Slinn
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-08-20 00:00:00.000000000 Z
10
+ date: 2025-02-07 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: facets
@@ -80,7 +79,6 @@ dependencies:
80
79
  - - ">="
81
80
  - !ruby/object:Gem::Version
82
81
  version: '0'
83
- description:
84
82
  email:
85
83
  - mslinn@mslinn.com
86
84
  executables: []
@@ -140,8 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
138
  - !ruby/object:Gem::Version
141
139
  version: '0'
142
140
  requirements: []
143
- rubygems_version: 3.5.17
144
- signing_key:
141
+ rubygems_version: 3.6.3
145
142
  specification_version: 4
146
143
  summary: Provides a framework for writing and testing Jekyll plugins
147
144
  test_files: