jekyll_flexible_include 2.0.20 → 2.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -6
- data/CHANGELOG.md +44 -20
- data/README.md +106 -75
- data/jekyll_flexible_include_plugin.gemspec +2 -1
- data/lib/flexible_include/version.rb +1 -1
- data/lib/flexible_include.rb +86 -231
- data/lib/flexible_include_class.rb +52 -0
- data/lib/flexible_include_private_methods.rb +142 -0
- data/lib/{git_util.rb → git_file_reader.rb} +1 -7
- data/spec/flexible_include_spec.rb +2 -1
- data/spec/git_file_reader_spec.rb +27 -0
- data/spec/spec_helper.rb +2 -3
- data/spec/status_persistence.txt +7 -0
- metadata +26 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 000dda7b37378861311b6663e9e1e246da0479ddde8a115674a1001833655b0a
|
4
|
+
data.tar.gz: ea530cdba36b41a67e5d60eb6ba15b51ff051e5b13a109cda8f81d362bfc08b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 815da6cd2afecfbab624e4392d7127cbc7e238f203c44c7d24b5c98258cf39013ce90026cab5942f061dc894049f899c271b4252bcace82198a2f0abdf7d55cf
|
7
|
+
data.tar.gz: 07e0cf4ae181bd334aa9fc79a7cb396cad3cf9a2750ba6ca3d37d17782518cd01a1671beac0b83db2ee914b619f41e869ecf0595b6de2fb364fa2c9d380a5508
|
data/.rubocop.yml
CHANGED
@@ -8,11 +8,11 @@ require:
|
|
8
8
|
AllCops:
|
9
9
|
Exclude:
|
10
10
|
- demo/_site/**/*
|
11
|
+
- binstub/**/*
|
11
12
|
- exe/**/*
|
12
13
|
- vendor/**/*
|
13
14
|
- Gemfile*
|
14
15
|
NewCops: enable
|
15
|
-
TargetRubyVersion: 2.6
|
16
16
|
|
17
17
|
Gemspec/DeprecatedAttributeAssignment:
|
18
18
|
Enabled: false
|
@@ -20,6 +20,9 @@ Gemspec/DeprecatedAttributeAssignment:
|
|
20
20
|
Gemspec/RequireMFA:
|
21
21
|
Enabled: false
|
22
22
|
|
23
|
+
Gemspec/RequiredRubyVersion:
|
24
|
+
Enabled: false
|
25
|
+
|
23
26
|
Layout/InitialIndentation:
|
24
27
|
Exclude:
|
25
28
|
- README.md
|
@@ -39,7 +42,7 @@ Lint/RedundantCopDisableDirective:
|
|
39
42
|
- jekyll_flexible_include_plugin.gemspec
|
40
43
|
|
41
44
|
Metrics/AbcSize:
|
42
|
-
Max:
|
45
|
+
Max: 50
|
43
46
|
|
44
47
|
Metrics/BlockLength:
|
45
48
|
Exclude:
|
@@ -53,11 +56,14 @@ Metrics/ClassLength:
|
|
53
56
|
Metrics/CyclomaticComplexity:
|
54
57
|
Max: 20
|
55
58
|
|
59
|
+
Metrics/ModuleLength:
|
60
|
+
Max: 150
|
61
|
+
|
56
62
|
Metrics/MethodLength:
|
57
|
-
Max:
|
63
|
+
Max: 50
|
58
64
|
|
59
65
|
Metrics/PerceivedComplexity:
|
60
|
-
Max:
|
66
|
+
Max: 25
|
61
67
|
|
62
68
|
Naming/FileName:
|
63
69
|
Exclude:
|
@@ -88,8 +94,7 @@ Style/RegexpLiteral:
|
|
88
94
|
Enabled: false
|
89
95
|
|
90
96
|
Style/StringConcatenation:
|
91
|
-
|
92
|
-
- spec/**/*
|
97
|
+
Enabled: false
|
93
98
|
|
94
99
|
Style/StringLiterals:
|
95
100
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,38 +1,62 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## 2.0.21 / 2023-11-16
|
4
|
+
|
5
|
+
* Restructured code
|
6
|
+
* `die_on_file_error` and `die_on_path_denied` work again.
|
7
|
+
* Specifying the file name without using a name/value parameter works again.
|
8
|
+
|
9
|
+
|
1
10
|
## 2.0.20 / 2023-05-30
|
2
|
-
|
11
|
+
|
12
|
+
* Updated dependencies
|
13
|
+
|
3
14
|
|
4
15
|
## 2.0.19 / 2023-04-05
|
5
|
-
|
16
|
+
|
17
|
+
* Added attribution support
|
18
|
+
|
6
19
|
|
7
20
|
## 2.0.18 / 2023-03-24
|
8
|
-
|
9
|
-
|
21
|
+
|
22
|
+
* The following are now parsed property:
|
23
|
+
`die_on_file_error`, `die_on_path_denied`, `die_on_run_error`, `die_on_path_denied`, and `die_on_other_error`.
|
24
|
+
|
10
25
|
|
11
26
|
## 2.0.17 / 2023-03-22
|
12
|
-
|
27
|
+
|
28
|
+
* Added `repo` and `git_ref` parameters, so files can be retrieved from git repositories at a given commit or tag.
|
29
|
+
|
13
30
|
|
14
31
|
## 2.0.16 / 2023-02-19
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
32
|
+
|
33
|
+
* Replaced hard-coded CSS in `denied` method with `flexible_error` class in
|
34
|
+
`demo/assets/css/style.css`.
|
35
|
+
* Added `-e` and `-x` options to `demo/_bin/debug`.
|
36
|
+
* Added configuration section `flexible_include` with supported parameters
|
37
|
+
`die_on_file_error`, `die_on_path_denied`, `die_on_run_error`,
|
38
|
+
`die_on_path_denied` and `die_on_other_error`.
|
39
|
+
* Fixed `undefined method 'path'` that occurred when `FLEXIBLE_INCLUDE_PATHS` was specified.
|
40
|
+
|
22
41
|
|
23
42
|
## 2.0.15 / 2023-02-18
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
43
|
+
|
44
|
+
* Replaced dependency `key-value-parser` with `jekyll_plugin_support`.
|
45
|
+
* Added `demo` website.
|
46
|
+
* Improved the documentation.
|
47
|
+
* Updated Rubocop configuration.
|
48
|
+
* Added `strip` option.
|
49
|
+
|
29
50
|
|
30
51
|
## 2.0.14 / 2022-09-27
|
31
|
-
|
52
|
+
|
53
|
+
* Added `key-value-parser` as a dependency.
|
54
|
+
|
32
55
|
|
33
56
|
## 2.0.13 / 2022-04-24
|
34
|
-
|
35
|
-
|
57
|
+
|
58
|
+
* Added `highlight` regex option, for highlighting.
|
59
|
+
* Added `number` option, for numbered lines.
|
36
60
|
|
37
61
|
## 2.0.12 / 2022-04-22
|
38
62
|
* Exits with an error message if an environment variable included in the value
|
data/README.md
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
Jekyll `flexible_include` Plugin
|
2
|
-
[](https://badge.fury.io/rb/jekyll_flexible_include)
|
3
|
-
===========
|
1
|
+
# Jekyll `flexible_include` Plugin [](https://badge.fury.io/rb/jekyll_flexible_include)
|
4
2
|
|
5
3
|
`Flexible_include` is a Jekyll plugin that includes the contents of a file
|
6
4
|
or the result of a process into a generated page.
|
@@ -18,7 +16,7 @@ More information is available on my website about [my Jekyll plugins](https://ww
|
|
18
16
|
This plugin supports 4 types of includes:
|
19
17
|
|
20
18
|
|
21
|
-
|
19
|
+
## Include Types
|
22
20
|
|
23
21
|
1. Absolute filenames (recognized by filename paths that start with `/`).
|
24
22
|
|
@@ -30,18 +28,20 @@ This plugin supports 4 types of includes:
|
|
30
28
|
|
31
29
|
|
32
30
|
In addition, filenames that require environment expansion because they contain a
|
33
|
-
|
34
|
-
defined when
|
31
|
+
`$` character are expanded according to the environment variables
|
32
|
+
defined when `jekyll build` executes.
|
35
33
|
|
36
34
|
|
37
35
|
A file from a git repository can also be included.
|
38
36
|
Files can be retrieved from at a given commit or tag.
|
39
37
|
Two new options are provided for this purpose:
|
40
|
-
- `repo` - directory where git repo resides; environment variables are expanded; defaults to current directory.
|
41
|
-
- `git_ref` - Git ref of commit or tag to be examined for the file; defaults to `HEAD`.
|
42
38
|
|
39
|
+
- `repo` - directory where git repo resides; environment variables are expanded; defaults to current directory.
|
40
|
+
- `git_ref` - Git ref of commit or tag to be examined for the file; defaults to `HEAD`.
|
41
|
+
|
42
|
+
|
43
|
+
## Configuration
|
43
44
|
|
44
|
-
### Configuration
|
45
45
|
Configuration parameters can be added to a section in `_config.yml` called `flexible_include`, like this:
|
46
46
|
|
47
47
|
```yaml
|
@@ -55,17 +55,21 @@ flexible_include:
|
|
55
55
|
The default values for all of these parameters is `false`,
|
56
56
|
except for `die_on_other_error`, which defaults to `true`.
|
57
57
|
|
58
|
-
|
58
|
+
- If `die_on_file_error` is enabled, then an attempt to include a file that fails
|
59
|
+
will cause Jekyll to die with an error message.
|
59
60
|
|
60
|
-
|
61
|
+
- If `die_on_path_denied` is enabled (see [Restricting Directory Access](#restricting-directory-access)),
|
62
|
+
then an attempt to include a file that should be blocked will cause Jekyll to die with an error message.
|
61
63
|
|
62
|
-
|
64
|
+
- If `die_on_run_error` is enabled, then an attempt to run a process that fails will cause Jekyll to die with an error message.
|
63
65
|
|
64
|
-
|
66
|
+
- If `die_on_other_error` is enabled, then any other exception will cause Jekyll to die with an error message.
|
65
67
|
|
66
68
|
|
67
|
-
|
69
|
+
## Syntax
|
70
|
+
|
68
71
|
The following are all equivalent, however, the first two are recommended:
|
72
|
+
|
69
73
|
```html
|
70
74
|
{% flexible_include file="path" [ OPTIONS ] %}
|
71
75
|
{% flexible_include file='path' [ OPTIONS ] %}
|
@@ -78,55 +82,69 @@ Note that the [square brackets] merely indicate optional parameters
|
|
78
82
|
and are not intended to be written literally.
|
79
83
|
|
80
84
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
85
|
+
## Options
|
86
|
+
|
87
|
+
- `attribution` see [`jekyll_plugin_support`](https://github.com/mslinn/jekyll_plugin_support#subclass-attribution)
|
88
|
+
|
89
|
+
- `do_not_escape` keyword option caused the content to be included without HTML escaping it.
|
90
|
+
By default, the included file will escape characters `<`,
|
91
|
+
`{` and `}` unless the `do_not_escape` keyword option is specified.
|
92
|
+
|
93
|
+
- `from='regex'` specifies that the beginning of the output should discarded until the matching string or regex is encountered.
|
86
94
|
|
87
|
-
|
88
|
-
|
89
|
-
|
95
|
+
- `highlight='regex pattern here'` wraps content matching the regex pattern within a
|
96
|
+
`<span class='bg_yellow'></span>` tag.
|
97
|
+
Note that the pattern can simply consist of the exact text that you want to highlight.
|
90
98
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
99
|
+
- `pre` is a keyword option that causes the included file to be wrapped inside a
|
100
|
+
<pre></pre> tag; no label is generated.
|
101
|
+
The <pre></pre> tag has an `data-lt-active="false"` attribute, so
|
102
|
+
[LanguageTool](https://forum.languagetool.org/t/avoid-spell-check-on-certain-html-inputs-manually/3944)
|
103
|
+
will not attempt to check the spelling or grammar of the contents.
|
104
|
+
|
105
|
+
- `to='regex'` specifies that the output should discarded after the matching string or regex is encountered
|
106
|
+
(includes the matched line).
|
107
|
+
|
108
|
+
- `until='regex'` specifies that the output should discarded after the matching string or regex is encountered
|
109
|
+
(excludes the matched line).
|
96
110
|
|
97
111
|
The following options imply `pre`:
|
98
|
-
* `dark` keyword option applies the `dark` class to the generated <pre></pre> tag.
|
99
|
-
You can define the `dark` and `darkLabel` classes as desired.
|
100
|
-
[This CSS is a good starting point.](https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#pre_css)
|
101
112
|
|
102
|
-
|
103
|
-
|
104
|
-
|
113
|
+
- `dark` keyword option applies the `dark` class to the generated <pre></pre> tag.
|
114
|
+
You can define the `dark` and `darkLabel` classes as desired.
|
115
|
+
[This CSS is a good starting point.](https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#pre_css)
|
116
|
+
|
117
|
+
- `download` keyword option uses the name of the file as a label,
|
118
|
+
and displays it above the <pre></pre> tag.
|
119
|
+
Clicking the label causes the file to be downloaded.
|
105
120
|
|
106
|
-
|
107
|
-
|
121
|
+
- `copyButton` keyword option draws an icon at the top right of the <pre></pre>
|
122
|
+
tag that causes the included contents to be copied to the clipboard.
|
108
123
|
|
109
|
-
|
110
|
-
|
124
|
+
- `label` keyword option specifies that an automatically generated label be placed above the contents.
|
125
|
+
There is no need to specify this option if `download` or `copy_button` options are provided.
|
111
126
|
|
112
|
-
|
113
|
-
|
127
|
+
- `label="blah blah"` specifies a label for the contents; this value overrides the default label.
|
128
|
+
The value can be enclosed in single or double quotes.
|
114
129
|
|
115
130
|
|
116
|
-
|
117
|
-
|
131
|
+
## Restricting Directory Access
|
132
|
+
|
133
|
+
By default, `flexible_include` can read from all directories according to the permissions of the
|
134
|
+
user account that launched the `jekyll` process.
|
118
135
|
For security-conscience environments, the accessible paths can be restricted.
|
119
136
|
|
120
|
-
Defining an environment variable called `FLEXIBLE_INCLUDE_PATHS` prior to launching Jekyll will
|
137
|
+
Defining an environment variable called `FLEXIBLE_INCLUDE_PATHS` prior to launching Jekyll will
|
138
|
+
restrict the paths that `flexible_include` will be able to read from.
|
121
139
|
This environment variable consists of a colon-delimited set of
|
122
140
|
[file and directory glob patterns](https://docs.ruby-lang.org/en/2.7.0/Dir.html#method-c-glob).
|
123
141
|
For example, the following restricts access to only the files within:
|
124
142
|
|
125
|
-
|
143
|
+
1. The `~/my_dir` directory tree of the account of the user that launched Jekyll.
|
126
144
|
|
127
|
-
|
145
|
+
2. The directory tree rooted at `/var/files`.
|
128
146
|
|
129
|
-
|
147
|
+
3. The directory tree rooted at the expanded value of the `$work` environment variable.
|
130
148
|
|
131
149
|
```shell
|
132
150
|
export FLEXIBLE_INCLUDE_PATHS='~/.*:$sites/.*:$work/.*'
|
@@ -139,17 +157,21 @@ To just match visible files:
|
|
139
157
|
export FLEXIBLE_INCLUDE_PATHS='~/my_dir/**/*:/var/files/**/*:$work/**/*'
|
140
158
|
```
|
141
159
|
|
142
|
-
|
160
|
+
|
161
|
+
### Note
|
162
|
+
|
143
163
|
The specified directories are traversed when the plugin starts,
|
144
164
|
and the filenames are stored in memory.
|
145
165
|
Directories with lots of files might take a noticable amount of
|
146
166
|
time to enumerate the files.
|
147
167
|
|
148
168
|
|
149
|
-
|
169
|
+
## Restricting Arbitrary Processes
|
170
|
+
|
150
171
|
By default, `flexible_include` can execute any command.
|
151
172
|
You can disable that by setting the environment variable `DISABLE_FLEXIBLE_INCLUDE`
|
152
173
|
to any non-empty value.
|
174
|
+
|
153
175
|
```shell
|
154
176
|
export DISABLE_FLEXIBLE_INCLUDE=true
|
155
177
|
```
|
@@ -162,7 +184,9 @@ and a red error message will be logged on the console that says something like:
|
|
162
184
|
|
163
185
|
|
164
186
|
## Installation
|
187
|
+
|
165
188
|
1. Add the following to `Gemfile`, inside the `jekyll_plugins` group:
|
189
|
+
|
166
190
|
```ruby
|
167
191
|
group :jekyll_plugins do
|
168
192
|
gem 'jekyll_flexible_include', '~> 2.0.15'
|
@@ -172,6 +196,7 @@ and a red error message will be logged on the console that says something like:
|
|
172
196
|
2. Add the following to `_config.yml`.
|
173
197
|
This is necessary because the name of the plugin
|
174
198
|
does not match the name of the entry point file:
|
199
|
+
|
175
200
|
```yaml
|
176
201
|
plugins:
|
177
202
|
- flexible_include
|
@@ -180,27 +205,20 @@ and a red error message will be logged on the console that says something like:
|
|
180
205
|
3. Copy `demo/assets/images/clippy.svg` to a directory that resolves to
|
181
206
|
`assets/images/` in your Jekyll website.
|
182
207
|
|
183
|
-
4. Copy the CSS from `demo/assets/css/
|
184
|
-
```css
|
185
|
-
blah blah blah
|
186
|
-
|
187
|
-
/* START OF CSS TO COPY */
|
188
|
-
Copy this stuff
|
189
|
-
/* END OF CSS TO COPY */
|
208
|
+
4. Copy the CSS from `demo/assets/css/jekyll_flexible_include.css` to your Jekyll project's CSS file.
|
190
209
|
|
191
|
-
|
192
|
-
```
|
210
|
+
5. Install the `jekyll_flexible_include` Ruby gem and mark it as a dependency of your project:
|
193
211
|
|
194
|
-
5. Install the `jekyll_flexible_include` Ruby gem as usual:
|
195
212
|
```shell
|
196
|
-
$ bundle
|
213
|
+
$ bundle
|
197
214
|
```
|
198
215
|
|
199
216
|
|
200
217
|
## Examples
|
201
218
|
|
202
219
|
1. Include files, escaping any HTML markup, so it appears as written; all four types of includes are shown.
|
203
|
-
|
220
|
+
|
221
|
+
```html
|
204
222
|
{% flexible_include '../../folder/outside/jekyll/site/foo.html' %}
|
205
223
|
{% flexible_include 'folder/within/jekyll/site/bar.js' %}
|
206
224
|
{% flexible_include '/etc/passwd' %}
|
@@ -210,16 +228,20 @@ and a red error message will be logged on the console that says something like:
|
|
210
228
|
```
|
211
229
|
|
212
230
|
2. Include a JSON file (without escaping characters).
|
213
|
-
|
231
|
+
|
232
|
+
```html
|
214
233
|
{% flexible_include do_not_escape file='~/folder/under/home/directory/foo.html' %}
|
215
234
|
```
|
216
235
|
|
236
|
+
|
217
237
|
## Additional Information
|
238
|
+
|
218
239
|
More information is available on
|
219
240
|
[Mike Slinn’s website](https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html).
|
220
241
|
|
221
242
|
|
222
243
|
## GitHub Pages
|
244
|
+
|
223
245
|
GitHub Pages only allows [these plugins](https://pages.github.com/versions/).
|
224
246
|
That means `flexible_include` will not work on GitHub Pages.
|
225
247
|
Following is a workaround.
|
@@ -237,10 +259,12 @@ Following is a workaround.
|
|
237
259
|
|
238
260
|
6. Tell GitHub that you want the `generated_site` repository to hold your GitHub pages.
|
239
261
|
|
240
|
-
7. A moment later, your website will now be visible as GitHub Pages, with the included content,
|
262
|
+
7. A moment later, your website will now be visible as GitHub Pages, with the included content,
|
263
|
+
just as you saw it locally.
|
241
264
|
|
242
265
|
|
243
266
|
## Known Issues
|
267
|
+
|
244
268
|
If the plugin does not work:
|
245
269
|
|
246
270
|
1. Ensure `_config.yml` doesn't have `safe: true` set.
|
@@ -252,17 +276,22 @@ If the plugin does not work:
|
|
252
276
|
|
253
277
|
|
254
278
|
## Development
|
279
|
+
|
255
280
|
After checking out the repo, run `bin/setup` to install dependencies as binstubs in the `exe` directory.
|
256
281
|
|
257
282
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
258
283
|
|
284
|
+
|
259
285
|
### Build and Install Locally
|
286
|
+
|
260
287
|
To build and install this gem onto your local machine, run:
|
288
|
+
|
261
289
|
```shell
|
262
|
-
$ rake install
|
290
|
+
$ bundle exec rake install
|
263
291
|
```
|
264
292
|
|
265
293
|
Examine the newly built gem:
|
294
|
+
|
266
295
|
```shell
|
267
296
|
$ gem info jekyll_flexible_include
|
268
297
|
|
@@ -280,11 +309,13 @@ jekyll_flexible_include (2.0.4)
|
|
280
309
|
|
281
310
|
|
282
311
|
## Demo Website
|
312
|
+
|
283
313
|
A test/demo website is provided in the `demo` directory.
|
284
314
|
You can run it under a debugger, or let it run free.
|
285
315
|
|
286
316
|
The `demo/_bin/debug` script can set various parameters for the demo.
|
287
317
|
View the help information with the `-h` option:
|
318
|
+
|
288
319
|
```shell
|
289
320
|
$ demo/_bin/debug -h
|
290
321
|
|
@@ -309,45 +340,45 @@ Options:
|
|
309
340
|
|
310
341
|
|
311
342
|
### Debugging the Demo
|
343
|
+
|
312
344
|
To run under a debugger, for example Visual Studio Code:
|
313
|
-
1. Set breakpoints.
|
314
345
|
|
315
|
-
|
316
|
-
|
317
|
-
$ demo/bin/debug
|
318
|
-
```
|
346
|
+
1. Set breakpoints.
|
347
|
+
2. Initiate a debug session from the command line:
|
319
348
|
|
320
|
-
|
349
|
+
```shell
|
350
|
+
$ demo/bin/debug
|
351
|
+
```
|
321
352
|
|
322
|
-
|
353
|
+
3. Once the `Fast Debugger` signon appears, launch the Visual Studio Code launch configuration called `Attach rdbg`.
|
354
|
+
4. View the generated website at [`http://localhost:4444`](http://localhost:4444).
|
323
355
|
|
324
356
|
|
325
357
|
### Build and Push to RubyGems
|
358
|
+
|
326
359
|
To release a new version,
|
327
360
|
|
328
361
|
1. Update the version number in `version.rb`.
|
329
|
-
|
330
362
|
2. Add a comment to the top of `CHANGELOG.md`.
|
331
|
-
|
332
363
|
3. Commit all changes to git; if you don't the next step will fail.
|
333
|
-
|
334
364
|
4. Run the following:
|
365
|
+
|
335
366
|
```shell
|
336
367
|
$ bundle exec rake release
|
337
368
|
```
|
369
|
+
|
338
370
|
The above creates a git tag for the version, commits the created tag,
|
339
371
|
and pushes the new `.gem` file to [RubyGems.org](https://rubygems.org).
|
340
372
|
|
341
373
|
|
342
374
|
## Contributing
|
343
|
-
1. Fork the project
|
344
375
|
|
376
|
+
1. Fork the project
|
345
377
|
2. Create a descriptively named feature branch
|
346
|
-
|
347
378
|
3. Add your feature
|
348
|
-
|
349
379
|
4. Submit a pull request
|
350
380
|
|
351
381
|
|
352
382
|
## License
|
383
|
+
|
353
384
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
|
35
35
|
spec.test_files = spec.files.grep(%r!^(test|spec|features)/!)
|
36
36
|
spec.version = JekyllFlexibleIncludePluginVersion::VERSION
|
37
37
|
|
38
|
-
spec.add_dependency '
|
38
|
+
spec.add_dependency 'jekyll_from_to_until'
|
39
|
+
spec.add_dependency 'jekyll_plugin_support', '>= 0.7.3'
|
39
40
|
spec.add_dependency 'rugged'
|
40
41
|
end
|
data/lib/flexible_include.rb
CHANGED
@@ -2,248 +2,103 @@ require 'benchmark'
|
|
2
2
|
require 'jekyll_plugin_support'
|
3
3
|
require 'securerandom'
|
4
4
|
require_relative 'flexible_include/version'
|
5
|
+
require_relative 'flexible_include_class'
|
6
|
+
require_relative 'flexible_include_private_methods'
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
module FlexibleClassMethods
|
11
|
-
def access_allowed(path)
|
12
|
-
return true unless @read_regexes
|
13
|
-
|
14
|
-
@read_regexes.find { |regex| regex.match(normalize_path(path)) }
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.escape_html(string)
|
18
|
-
string.gsub("&", "&")
|
19
|
-
.gsub("{", "{")
|
20
|
-
.gsub("}", "}")
|
21
|
-
.gsub("<", "<")
|
22
|
-
end
|
23
|
-
|
24
|
-
def normalize_path(path)
|
25
|
-
JekyllPluginHelper.expand_env(path, die_if_undefined: true)
|
26
|
-
.gsub('~', Dir.home)
|
27
|
-
end
|
28
|
-
|
29
|
-
def number_content(content)
|
30
|
-
lines = content.split("\n")
|
31
|
-
digits = lines.length.to_s.length
|
32
|
-
i = 0
|
33
|
-
numbered_content = lines.map do |line|
|
34
|
-
i += 1
|
35
|
-
number = i.to_s.rjust(digits, ' ')
|
36
|
-
"<span class='unselectable numbered_line'> #{number}: </span>#{line}"
|
37
|
-
end
|
38
|
-
result = numbered_content.join "\n"
|
39
|
-
result += "\n" unless result.end_with? "\n"
|
40
|
-
result
|
41
|
-
end
|
42
|
-
|
43
|
-
# If FLEXIBLE_INCLUDE_PATHS='~/lib/.*:.*:$WORK/.*'
|
44
|
-
# Then @read_regexes will be set to regexes of ['/home/my_user_id/lib/.*', '/pwd/.*', '/work/envar/path/.*']
|
45
|
-
def security_check
|
46
|
-
@execution_denied = ENV.fetch('DISABLE_FLEXIBLE_INCLUDE', nil)
|
47
|
-
|
48
|
-
return if @read_regexes
|
49
|
-
|
50
|
-
flexible_include_paths = ENV.fetch('FLEXIBLE_INCLUDE_PATHS', nil)
|
51
|
-
read_paths = normalize_path(flexible_include_paths) if flexible_include_paths
|
52
|
-
return unless read_paths
|
53
|
-
|
54
|
-
@read_regexes = read_paths.split(':').map do |path|
|
55
|
-
abs_path = path.start_with?('/') ? path : (Pathname.new(Dir.pwd) + path).to_s
|
56
|
-
Regexp.new(abs_path)
|
57
|
-
end
|
8
|
+
class String
|
9
|
+
def squish
|
10
|
+
strip.gsub(/\s+/, ' ')
|
58
11
|
end
|
59
12
|
end
|
60
13
|
|
61
|
-
|
62
|
-
|
63
|
-
class FlexibleInclude < JekyllSupport::JekyllTag # rubocop: disable Metrics/ClassLength
|
64
|
-
include JekyllFlexibleIncludePluginVersion
|
65
|
-
|
66
|
-
def render_impl # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
67
|
-
setup
|
68
|
-
path = JekyllPluginHelper.expand_env(@filename)
|
69
|
-
case path
|
70
|
-
when /\A\// # Absolute path
|
71
|
-
return denied("Access to <code>#{path}</code> denied by <code>FLEXIBLE_INCLUDE_PATHS</code> value.") unless self.class.access_allowed(path)
|
72
|
-
|
73
|
-
@logger.debug { "Absolute path=#{path}, @filename=#{@filename}" }
|
74
|
-
when /\A~/ # Relative path to user's home directory
|
75
|
-
return denied("Access to <code>#{path}</code> denied by <code>FLEXIBLE_INCLUDE_PATHS</code> value.") unless self.class.access_allowed(path)
|
76
|
-
|
77
|
-
@logger.debug { "User home start @filename=#{@filename}, path=#{path}" }
|
78
|
-
@filename = @filename.delete_prefix '~/'
|
79
|
-
path = File.join(Dir.home, @filename)
|
80
|
-
@logger.debug { "User home end @filename=#{@filename}, path=#{path}" }
|
81
|
-
when /\A!/ # Run command and return response
|
82
|
-
return denied('Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.') if @execution_denied
|
83
|
-
|
84
|
-
@filename = JekyllPluginHelper.remove_quotes(@helper.argv.first) if @helper.argv.first
|
85
|
-
@filename = @filename.delete_prefix '!'
|
86
|
-
contents = run(@filename)
|
87
|
-
else # Relative path
|
88
|
-
source = File.expand_path(@site.config['source']) # website root directory
|
89
|
-
path = File.join(source, @filename) # Fully qualified path of include file from relative path
|
90
|
-
@relative = true
|
91
|
-
@logger.debug { "Relative end @filename=#{@filename}, path=#{path}" }
|
92
|
-
end
|
93
|
-
render_completion(path, contents)
|
94
|
-
rescue Errno::EACCES => e
|
95
|
-
msg = format_error_message e.message
|
96
|
-
@logger.error msg
|
97
|
-
raise FlexibleIncludeError, msg, [] if @die_on_file_error
|
98
|
-
|
99
|
-
"<span class='flexible_error'>FlexibleIncludeError: #{msg}</span>"
|
100
|
-
rescue Errno::ENOENT => e
|
101
|
-
msg = format_error_message e.message
|
102
|
-
@logger.error msg
|
103
|
-
raise FlexibleIncludeError, msg, [] if @die_on_path_denied
|
104
|
-
|
105
|
-
"<span class='flexible_error'>FlexibleIncludeError: #{msg}</span>"
|
106
|
-
rescue FlexibleIncludeError => e
|
107
|
-
@logger.error e.message
|
108
|
-
raise e
|
109
|
-
rescue StandardError => e
|
110
|
-
msg = format_error_message e.message
|
111
|
-
@logger.error msg
|
112
|
-
raise FlexibleIncludeError, msg, [] if @die_on_other_error
|
113
|
-
|
114
|
-
"<span class='flexible_error'>FlexibleIncludeError: #{msg}</span>"
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
class << self
|
120
|
-
include FlexibleClassMethods
|
121
|
-
end
|
122
|
-
|
123
|
-
def denied(msg)
|
124
|
-
msg_no_html = remove_html_tags(msg)
|
125
|
-
@logger.error("#{@page['path']} - #{msg_no_html}")
|
126
|
-
raise FlexibleIncludeError, "#{@page['path']} - #{msg_no_html.red}", [] if @die_on_path_denied
|
14
|
+
module FlexibleInclude
|
15
|
+
FlexibleIncludeError = JekyllSupport.define_error
|
127
16
|
|
128
|
-
|
129
|
-
end
|
130
|
-
|
131
|
-
def format_error_message(message)
|
132
|
-
"#{message} on line #{@line_number} (after front matter) of #{@page['path']}}"
|
133
|
-
end
|
134
|
-
|
135
|
-
def highlight(content, pattern)
|
136
|
-
content.gsub(Regexp.new(pattern), "<span class='bg_yellow'>\\0</span>")
|
137
|
-
end
|
138
|
-
|
139
|
-
def parse_args
|
140
|
-
@copy_button = @helper.parameter_specified? 'copyButton'
|
141
|
-
@dark = ' dark' if @helper.parameter_specified? 'dark'
|
142
|
-
@do_not_escape = @helper.parameter_specified? 'do_not_escape'
|
143
|
-
@download = @helper.parameter_specified? 'download'
|
144
|
-
@highlight_pattern = @helper.parameter_specified? 'highlight'
|
145
|
-
@label = @helper.parameter_specified? 'label'
|
146
|
-
@label_specified = @label
|
147
|
-
@number_lines = @helper.parameter_specified? 'number'
|
148
|
-
@strip = @helper.parameter_specified? 'strip'
|
149
|
-
|
150
|
-
# Download, dark, label or number implies pre
|
151
|
-
@pre = @helper.parameter_specified?('pre') || @copy_button || @dark || @download || @label_specified || @number_lines
|
152
|
-
|
153
|
-
@filename = @helper.parameter_specified? 'file'
|
154
|
-
@filename ||= @helper.params.first # Do this after all options have been checked for
|
155
|
-
@label ||= @filename
|
156
|
-
|
157
|
-
# If a label was specified, use it, otherwise concatenate any dangling parameters and use that as the label
|
158
|
-
@label ||= @helper.params[1..].join(' ')
|
159
|
-
|
160
|
-
@logger.debug("@filename=#{@filename}")
|
161
|
-
end
|
162
|
-
|
163
|
-
# Not used, delete
|
164
|
-
def realpath_prefixed_with?(path, dir)
|
165
|
-
File.exist?(path) && File.realpath(path).start_with?(dir)
|
166
|
-
rescue StandardError => _e
|
167
|
-
raise FlexibleIncludeError, remove_html_tags(e.message).red, [] if @die_on_file_error
|
168
|
-
end
|
169
|
-
|
170
|
-
def remove_html_tags(string)
|
171
|
-
string.gsub(/<[^>]*>/, '')
|
172
|
-
end
|
173
|
-
|
174
|
-
def render_completion(path, contents)
|
175
|
-
contents ||= File.read(path)
|
176
|
-
contents.strip! if @strip
|
177
|
-
contents2 = @do_not_escape ? contents : FlexibleClassMethods.escape_html(contents)
|
178
|
-
contents2 = highlight(contents2, @highlight_pattern) if @highlight_pattern
|
179
|
-
contents2 = FlexibleInclude.number_content(contents2) if @number_lines
|
180
|
-
result = @pre ? wrap_in_pre(path, contents2) : contents2
|
181
|
-
<<~END_OUTPUT
|
182
|
-
#{result}
|
183
|
-
#{@helper.attribute if @helper.attribution}
|
184
|
-
END_OUTPUT
|
185
|
-
end
|
17
|
+
PLUGIN_NAME = 'flexible_include'.freeze
|
186
18
|
|
187
|
-
|
188
|
-
|
189
|
-
msg = format_error_message 'FlexibleIncludeError: Empty command string'
|
190
|
-
@do_not_escape = true
|
191
|
-
return "<span class='flexible_error'>#{msg}</span>" unless @die_on_other_error
|
19
|
+
class FlexibleInclude < JekyllSupport::JekyllTag
|
20
|
+
include JekyllFlexibleIncludePluginVersion
|
192
21
|
|
193
|
-
|
22
|
+
class << self
|
23
|
+
include FlexibleClassMethods
|
194
24
|
end
|
195
25
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
@
|
217
|
-
|
218
|
-
@die_on_path_denied
|
219
|
-
|
26
|
+
def render_impl
|
27
|
+
setup
|
28
|
+
@path = JekyllPluginHelper.expand_env @filename
|
29
|
+
handle_path_types
|
30
|
+
render_completion
|
31
|
+
rescue Errno::EACCES => e
|
32
|
+
e.shorten_backtrace
|
33
|
+
msg = format_error_message e.message
|
34
|
+
@logger.error msg
|
35
|
+
|
36
|
+
if @die_on_file_error
|
37
|
+
e2 = Errno::EACCES.new msg
|
38
|
+
e2.set_backtrace e.backtrace
|
39
|
+
raise e2
|
40
|
+
end
|
41
|
+
|
42
|
+
"<div class='custom_error'>#{e.class} raised in #{self.class};\n#{msg}</div>"
|
43
|
+
rescue Errno::ENOENT => e
|
44
|
+
e.shorten_backtrace
|
45
|
+
msg = format_error_message e.message
|
46
|
+
@logger.error msg
|
47
|
+
|
48
|
+
if @die_on_path_denied
|
49
|
+
e2 = Errno::ENOENT.new msg
|
50
|
+
e2.set_backtrace e.backtrace
|
51
|
+
raise e2
|
52
|
+
end
|
53
|
+
|
54
|
+
"<div class='custom_error'>#{e.class} raised in #{self.class};\n#{msg}</div>"
|
55
|
+
rescue FlexibleIncludeError => e
|
56
|
+
e.shorten_backtrace
|
57
|
+
@logger.error e.message
|
58
|
+
raise e
|
220
59
|
end
|
221
60
|
|
222
|
-
|
223
|
-
|
61
|
+
private
|
62
|
+
|
63
|
+
include FlexiblePrivateMethods
|
64
|
+
|
65
|
+
# @return content if @path does not reference a file
|
66
|
+
def handle_path_types
|
67
|
+
case @path
|
68
|
+
when /\A\// # Absolute path
|
69
|
+
unless self.class.access_allowed(@path)
|
70
|
+
return denied("Access to <code>#{@path}</code> from line #{@line_number} (after front matter) " \
|
71
|
+
"of #{@page['name']} denied by <code>FLEXIBLE_INCLUDE_PATHS</code> value.")
|
72
|
+
end
|
73
|
+
|
74
|
+
@logger.debug { "Absolute @path=#{@path}, @filename=#{@filename}" }
|
75
|
+
when /\A~/ # Relative path to user's home directory
|
76
|
+
unless self.class.access_allowed(@path)
|
77
|
+
return denied("Access to <code>#{@path}</code> from line #{@line_number} (after front matter) " \
|
78
|
+
"of #{@page['name']} denied by <code>FLEXIBLE_INCLUDE_PATHS</code> value.")
|
79
|
+
end
|
80
|
+
|
81
|
+
@logger.debug { "User home start @filename=#{@filename}, @path=#{@path}" }
|
82
|
+
@filename = @filename.delete_prefix '~/'
|
83
|
+
@filename = File.join(Dir.home, @filename)
|
84
|
+
@path = @filename
|
85
|
+
@logger.debug { "User home end @filename=#{@filename}, @path=#{@path}" }
|
86
|
+
when /\A!/ # Run command and return response
|
87
|
+
if @execution_denied
|
88
|
+
return denied("Arbitrary command execution from line #{@line_number} (after front matter) " \
|
89
|
+
"of #{@page['name']} denied by DISABLE_FLEXIBLE_INCLUDE value.")
|
90
|
+
end
|
91
|
+
|
92
|
+
@filename = JekyllPluginHelper.remove_quotes(@helper.argv.first) if @helper.argv.first
|
93
|
+
@filename = @filename.delete_prefix '!'
|
94
|
+
@contents = run(@filename)
|
95
|
+
else # Relative path
|
96
|
+
@path = @filename
|
97
|
+
@relative = true
|
98
|
+
@logger.debug { "Relative end @filename=#{@filename}, @path=#{@path}" }
|
99
|
+
end
|
100
|
+
end
|
224
101
|
|
225
|
-
|
226
|
-
SUFFIX = "title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>".freeze
|
227
|
-
|
228
|
-
def wrap_in_pre(path, content)
|
229
|
-
basename = File.basename(path)
|
230
|
-
label_or_href = if @download
|
231
|
-
label = @label_specified ? @label : basename
|
232
|
-
<<~END_HREF
|
233
|
-
<a href='data:text/plain;charset=UTF-8,#{basename}' download='#{basename}'
|
234
|
-
title='Click on the file name to download the file'>#{label}</a>
|
235
|
-
END_HREF
|
236
|
-
else
|
237
|
-
@label_specified ? @label : basename
|
238
|
-
end
|
239
|
-
pre_id = "id#{SecureRandom.hex 6}"
|
240
|
-
copy_button = @copy_button ? "#{PREFIX}'##{pre_id}'#{SUFFIX}" : ''
|
241
|
-
dark_label = ' darkLabel' if @dark
|
242
|
-
<<~END_PRE
|
243
|
-
<div class="codeLabel#{dark_label}">#{label_or_href}</div>
|
244
|
-
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer#{@dark}" id="#{pre_id}">#{copy_button}#{content}</pre>
|
245
|
-
END_PRE
|
102
|
+
JekyllPluginHelper.register(self, 'flexible_include')
|
246
103
|
end
|
247
|
-
|
248
|
-
JekyllPluginHelper.register(self, 'flexible_include')
|
249
104
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module FlexibleInclude
|
2
|
+
module FlexibleClassMethods
|
3
|
+
def access_allowed(path)
|
4
|
+
return true unless @read_regexes
|
5
|
+
|
6
|
+
@read_regexes.find { |regex| regex.match(normalize_path(path)) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.escape_html(string)
|
10
|
+
string.gsub("&", "&")
|
11
|
+
.gsub("{", "{")
|
12
|
+
.gsub("}", "}")
|
13
|
+
.gsub("<", "<")
|
14
|
+
end
|
15
|
+
|
16
|
+
def normalize_path(path)
|
17
|
+
JekyllPluginHelper.expand_env(path, die_if_undefined: true)
|
18
|
+
.gsub('~', Dir.home)
|
19
|
+
end
|
20
|
+
|
21
|
+
def number_content(content)
|
22
|
+
lines = content.split("\n")
|
23
|
+
digits = lines.length.to_s.length
|
24
|
+
i = 0
|
25
|
+
numbered_content = lines.map do |line|
|
26
|
+
i += 1
|
27
|
+
number = i.to_s.rjust(digits, ' ')
|
28
|
+
"<span class='unselectable numbered_line'> #{number}: </span>#{line}"
|
29
|
+
end
|
30
|
+
result = numbered_content.join "\n"
|
31
|
+
result += "\n" unless result.end_with? "\n"
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
# If FLEXIBLE_INCLUDE_PATHS='~/lib/.*:.*:$WORK/.*'
|
36
|
+
# Then @read_regexes will be set to regexes of ['/home/my_user_id/lib/.*', '/pwd/.*', '/work/envar/path/.*']
|
37
|
+
def security_check
|
38
|
+
@execution_denied = ENV.fetch('DISABLE_FLEXIBLE_INCLUDE', nil)
|
39
|
+
|
40
|
+
return if @read_regexes
|
41
|
+
|
42
|
+
flexible_include_paths = ENV.fetch('FLEXIBLE_INCLUDE_PATHS', nil)
|
43
|
+
read_paths = normalize_path(flexible_include_paths) if flexible_include_paths
|
44
|
+
return unless read_paths
|
45
|
+
|
46
|
+
@read_regexes = read_paths.split(':').map do |path|
|
47
|
+
abs_path = path.start_with?('/') ? path : (Pathname.new(Dir.pwd) + path).to_s
|
48
|
+
Regexp.new(abs_path)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'jekyll_from_to_until'
|
3
|
+
|
4
|
+
module FlexibleInclude
|
5
|
+
module FlexiblePrivateMethods
|
6
|
+
def denied(msg)
|
7
|
+
msg_no_html = remove_html_tags(msg)
|
8
|
+
@logger.error("#{@page['path']} - #{msg_no_html}")
|
9
|
+
raise FlexibleIncludeError, "#{@page['path']} - #{msg_no_html}".red, [] if @die_on_path_denied
|
10
|
+
|
11
|
+
"<p class='flexible_error'>#{msg}</p>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def highlight(content, pattern)
|
15
|
+
raise FlexibleIncludeError, "content is a #{content.class}, not a String" unless content.instance_of? String
|
16
|
+
|
17
|
+
content.gsub(Regexp.new(pattern), "<span class='bg_yellow'>\\0</span>")
|
18
|
+
end
|
19
|
+
|
20
|
+
def maybe_raise_error(msg, throw_error: true)
|
21
|
+
fmsg = format_error_message msg
|
22
|
+
@logger.error fmsg
|
23
|
+
return "<span class='flexible_error'>#{fmsg}</span>" unless throw_error
|
24
|
+
|
25
|
+
raise FlexibleIncludeError, msg, []
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_args
|
29
|
+
@copy_button = @helper.parameter_specified? 'copyButton'
|
30
|
+
@dark = ' dark' if @helper.parameter_specified? 'dark'
|
31
|
+
@do_not_escape = @helper.parameter_specified? 'do_not_escape'
|
32
|
+
@download = @helper.parameter_specified? 'download'
|
33
|
+
@from = @helper.parameter_specified? 'from'
|
34
|
+
@highlight_pattern = @helper.parameter_specified? 'highlight'
|
35
|
+
@label = @helper.parameter_specified? 'label'
|
36
|
+
@label_specified = @label
|
37
|
+
@number_lines = @helper.parameter_specified? 'number'
|
38
|
+
@strip = @helper.parameter_specified? 'strip'
|
39
|
+
@to = @helper.parameter_specified? 'to'
|
40
|
+
@until = @helper.parameter_specified? 'until'
|
41
|
+
|
42
|
+
# Download, dark, label or number implies pre
|
43
|
+
@pre = @helper.parameter_specified?('pre') || @copy_button || @dark || @download || @label_specified || @number_lines
|
44
|
+
|
45
|
+
@filename = @helper.parameter_specified? 'file'
|
46
|
+
|
47
|
+
unless @filename # Do this after all other options have been checked for
|
48
|
+
@filename = @helper.params.first.first
|
49
|
+
@helper.delete_parameter @helper.params.first
|
50
|
+
end
|
51
|
+
raise StandardError, "@filename (#{@filename}) is not a string", [] unless @filename.instance_of? String
|
52
|
+
|
53
|
+
@label ||= @filename
|
54
|
+
|
55
|
+
@logger.debug("@filename=#{@filename}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove_html_tags(string)
|
59
|
+
string.gsub(/<[^>]*>/, '')
|
60
|
+
end
|
61
|
+
|
62
|
+
def render_completion
|
63
|
+
unless @path.start_with? '!'
|
64
|
+
maybe_raise_error("#{@path} does not exist", throw_error: @die_on_file_error) unless File.exist? @path
|
65
|
+
maybe_raise_error("#{@path} is not readable", throw_error: @die_on_file_error) unless Pathname.new(@path).readable?
|
66
|
+
|
67
|
+
@contents = File.read @path
|
68
|
+
unless @contents.instance_of? String
|
69
|
+
maybe_raise_error("contents has type a #{@contents.class}, not a String",
|
70
|
+
throw_error: @die_on_file_error)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@contents = FromToUntil.from(@contents, @from) if @from
|
74
|
+
@contents = FromToUntil.to(@contents, @to) if @to
|
75
|
+
@contents = FromToUntil.until(@contents, @until) if @until
|
76
|
+
contents2 = @do_not_escape ? @contents : FlexibleClassMethods.escape_html(@contents)
|
77
|
+
contents2.strip! if @strip
|
78
|
+
maybe_raise_error("contents2 is a #{contents2.class}, not a String", throw_error: @die_on_file_error) unless contents2.instance_of? String
|
79
|
+
|
80
|
+
contents2 = highlight(contents2, @highlight_pattern) if @highlight_pattern
|
81
|
+
contents2 = FlexibleInclude.number_content(contents2) if @number_lines
|
82
|
+
result = @pre ? wrap_in_pre(@path, contents2) : contents2
|
83
|
+
<<~END_OUTPUT
|
84
|
+
#{result}
|
85
|
+
#{@helper.attribute if @helper.attribution}
|
86
|
+
END_OUTPUT
|
87
|
+
end
|
88
|
+
|
89
|
+
def run(cmd)
|
90
|
+
if cmd.empty?
|
91
|
+
@do_not_escape = true
|
92
|
+
return maybe_raise_error('FlexibleIncludeError: Empty command string', throw_error: @die_on_other_error)
|
93
|
+
end
|
94
|
+
|
95
|
+
@logger.debug { "Executing #{cmd}" }
|
96
|
+
%x[#{cmd}].chomp
|
97
|
+
rescue FlexibleIncludeError => e
|
98
|
+
raise e
|
99
|
+
rescue StandardError => e
|
100
|
+
@do_not_escape = true
|
101
|
+
maybe_reraise_error(e, throw_error: @die_on_run_error)
|
102
|
+
end
|
103
|
+
|
104
|
+
def setup
|
105
|
+
@helper.gem_file __FILE__ # Enables attribution
|
106
|
+
self.class.security_check
|
107
|
+
|
108
|
+
config = @config[PLUGIN_NAME]
|
109
|
+
if config
|
110
|
+
@die_on_file_error = config['die_on_file_error'] == true
|
111
|
+
@die_on_other_error = config['die_on_other_error'] == true
|
112
|
+
@die_on_path_denied = config['die_on_path_denied'] == true
|
113
|
+
@die_on_run_error = config['die_on_run_error'] == true
|
114
|
+
end
|
115
|
+
|
116
|
+
parse_args
|
117
|
+
end
|
118
|
+
|
119
|
+
PREFIX = "<button class='copyBtn' data-clipboard-target=".freeze
|
120
|
+
SUFFIX = "title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>".freeze
|
121
|
+
|
122
|
+
def wrap_in_pre(path, content)
|
123
|
+
basename = File.basename(path)
|
124
|
+
label_or_href = if @download
|
125
|
+
label = @label_specified ? @label : basename
|
126
|
+
<<~END_HREF
|
127
|
+
<a href='data:text/plain;charset=UTF-8,#{basename}' download='#{basename}'
|
128
|
+
title='Click on the file name to download the file'>#{label}</a>
|
129
|
+
END_HREF
|
130
|
+
else
|
131
|
+
@label_specified ? @label : basename
|
132
|
+
end
|
133
|
+
pre_id = "id#{SecureRandom.hex 6}"
|
134
|
+
copy_button = @copy_button ? "#{PREFIX}'##{pre_id}'#{SUFFIX}" : ''
|
135
|
+
dark_label = ' darkLabel' if @dark
|
136
|
+
<<~END_PRE
|
137
|
+
<div class="codeLabel#{dark_label}">#{label_or_href}</div>
|
138
|
+
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer#{@dark}" id="#{pre_id}">#{copy_button}#{content}</pre>
|
139
|
+
END_PRE
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rugged'
|
2
2
|
|
3
|
+
# TODO: Not used yet. Either implement git-related functionality or delete this file and its tests
|
3
4
|
class GitFileReader
|
4
5
|
def initialize(repo_dir = '.')
|
5
6
|
@repo = Rugged::Repository.new repo_dir
|
@@ -32,10 +33,3 @@ class GitFileReader
|
|
32
33
|
object.data # String
|
33
34
|
end
|
34
35
|
end
|
35
|
-
|
36
|
-
if $PROGRAM_NAME == __FILE__
|
37
|
-
puts GitFileReader.new('.').blob_at('HEAD~2', 'README.md').content
|
38
|
-
# puts GitFileReader.new('.').commit_for_ref('HEAD^').contents('README.md')
|
39
|
-
# puts GitFileReader.new('.').commit_for_ref('HEAD').contents('README.md')
|
40
|
-
# puts GitFileReader.new('.').commit_for_ref('refs/heads/master').contents('README.md')
|
41
|
-
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'jekyll_plugin_support'
|
1
2
|
require_relative '../lib/flexible_include'
|
2
3
|
|
3
4
|
RSpec.describe(FlexibleInclude) do
|
@@ -10,7 +11,7 @@ RSpec.describe(FlexibleInclude) do
|
|
10
11
|
|
11
12
|
expect(described_class.access_allowed('~/.mem_settings.yaml')).to be_truthy
|
12
13
|
|
13
|
-
home_file =
|
14
|
+
home_file = JekyllPluginHelper.expand_env('$HOME/.mem_settings.yaml')
|
14
15
|
expect(described_class.access_allowed(home_file)).to be_truthy
|
15
16
|
|
16
17
|
expect(described_class.access_allowed('/asdf')).to be_falsey
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'jekyll_plugin_support'
|
2
|
+
require_relative '../lib/git_file_reader'
|
3
|
+
|
4
|
+
RSpec.describe(GitFileReader) do
|
5
|
+
it 'reads a file at a tag' do
|
6
|
+
content = described_class.new('.').blob_at('v2.0.20', 'README.md').content
|
7
|
+
expect(content).to include('Added `highlight` regex option')
|
8
|
+
|
9
|
+
content = described_class.new('.').blob_at('v2.0.19', 'README.md').content
|
10
|
+
expect(content).not_to include('Added `highlight` regex option')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'reads a file at HEAD^' do
|
14
|
+
content = described_class.new('.').blob_at('HEAD^', 'README.md').content
|
15
|
+
expect(content).to be_truthy
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'reads a file at HEAD' do
|
19
|
+
content = described_class.new('.').blob_at('HEAD', 'README.md').content
|
20
|
+
expect(content).to be_truthy
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'reads a file at refs/heads/master' do
|
24
|
+
content = described_class.new('.').blob_at('refs/heads/master', 'README.md').content
|
25
|
+
expect(content).to be_truthy
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,9 +5,8 @@ require_relative '../lib/flexible_include'
|
|
5
5
|
Jekyll.logger.log_level = :info
|
6
6
|
|
7
7
|
RSpec.configure do |config|
|
8
|
-
config.
|
9
|
-
config.
|
10
|
-
config.run_all_when_everything_filtered = true
|
8
|
+
# config.order = 'random'
|
9
|
+
config.filter_run_when_matching focus: true
|
11
10
|
|
12
11
|
# See https://relishapp.com/rspec/rspec-core/docs/command-line/only-failures
|
13
12
|
config.example_status_persistence_file_path = 'spec/status_persistence.txt'
|
@@ -0,0 +1,7 @@
|
|
1
|
+
example_id | status | run_time |
|
2
|
+
------------------------------------ | ------ | --------------- |
|
3
|
+
./spec/flexible_include_spec.rb[1:1] | passed | 0.00164 seconds |
|
4
|
+
./spec/git_file_reader_spec.rb[1:1] | failed | 0.04395 seconds |
|
5
|
+
./spec/git_file_reader_spec.rb[1:2] | passed | 0.0411 seconds |
|
6
|
+
./spec/git_file_reader_spec.rb[1:3] | passed | 0.03189 seconds |
|
7
|
+
./spec/git_file_reader_spec.rb[1:4] | passed | 0.02975 seconds |
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll_flexible_include
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.21
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Slinn
|
@@ -10,22 +10,36 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-11-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: jekyll_from_to_until
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
15
29
|
- !ruby/object:Gem::Dependency
|
16
30
|
name: jekyll_plugin_support
|
17
31
|
requirement: !ruby/object:Gem::Requirement
|
18
32
|
requirements:
|
19
33
|
- - ">="
|
20
34
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.7.
|
35
|
+
version: 0.7.3
|
22
36
|
type: :runtime
|
23
37
|
prerelease: false
|
24
38
|
version_requirements: !ruby/object:Gem::Requirement
|
25
39
|
requirements:
|
26
40
|
- - ">="
|
27
41
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.7.
|
42
|
+
version: 0.7.3
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: rugged
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,9 +73,13 @@ files:
|
|
59
73
|
- jekyll_flexible_include_plugin.gemspec
|
60
74
|
- lib/flexible_include.rb
|
61
75
|
- lib/flexible_include/version.rb
|
62
|
-
- lib/
|
76
|
+
- lib/flexible_include_class.rb
|
77
|
+
- lib/flexible_include_private_methods.rb
|
78
|
+
- lib/git_file_reader.rb
|
63
79
|
- spec/flexible_include_spec.rb
|
80
|
+
- spec/git_file_reader_spec.rb
|
64
81
|
- spec/spec_helper.rb
|
82
|
+
- spec/status_persistence.txt
|
65
83
|
homepage: https://www.mslinn.com/jekyll_plugins/jekyll_flexible_include.html
|
66
84
|
licenses:
|
67
85
|
- MIT
|
@@ -89,12 +107,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
107
|
- !ruby/object:Gem::Version
|
90
108
|
version: '0'
|
91
109
|
requirements: []
|
92
|
-
rubygems_version: 3.3.
|
110
|
+
rubygems_version: 3.3.7
|
93
111
|
signing_key:
|
94
112
|
specification_version: 4
|
95
113
|
summary: Jekyll plugin supports various ways to include content into the generated
|
96
114
|
site.
|
97
115
|
test_files:
|
98
116
|
- spec/flexible_include_spec.rb
|
117
|
+
- spec/git_file_reader_spec.rb
|
99
118
|
- spec/spec_helper.rb
|
119
|
+
- spec/status_persistence.txt
|
100
120
|
...
|