jekyll_flexible_include 2.0.20 → 2.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -6
- data/CHANGELOG.md +49 -20
- data/README.md +106 -75
- data/jekyll_flexible_include_plugin.gemspec +3 -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 +144 -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 +40 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a510c3980d0d804087271c3508dc35e7986783db434d4ee8c15dd0e0ccb6bd0
|
4
|
+
data.tar.gz: e8e0f13c44c64bd4098372a9d7a8651341e9879e4dc9e602f23d99ec0020419b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f7e88a3d0537fd18e41f921f577fa016a2cd2fecf49609c32ccc1fd9be4f78296b760f8316f4ae3836f2edb6813183a26cedfabb69b244b8c26eed610a6aef2
|
7
|
+
data.tar.gz: c774673d92470e3355f3c22fe69c31186a9c57b5919f0f070706fb5a10ea6b5506f9ed39be6cc10fa85e9f74abac4eda869facda90676005618e99e2b8802f0b
|
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,67 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## 2.0.22 / 2024-02-27
|
4
|
+
|
5
|
+
* Improved error reporting
|
6
|
+
|
7
|
+
|
8
|
+
## 2.0.21 / 2023-11-16
|
9
|
+
|
10
|
+
* Restructured code
|
11
|
+
* `die_on_file_error` and `die_on_path_denied` work again.
|
12
|
+
* Specifying the file name without using a name/value parameter works again.
|
13
|
+
|
14
|
+
|
1
15
|
## 2.0.20 / 2023-05-30
|
2
|
-
|
16
|
+
|
17
|
+
* Updated dependencies
|
18
|
+
|
3
19
|
|
4
20
|
## 2.0.19 / 2023-04-05
|
5
|
-
|
21
|
+
|
22
|
+
* Added attribution support
|
23
|
+
|
6
24
|
|
7
25
|
## 2.0.18 / 2023-03-24
|
8
|
-
|
9
|
-
|
26
|
+
|
27
|
+
* The following are now parsed property:
|
28
|
+
`die_on_file_error`, `die_on_path_denied`, `die_on_run_error`, `die_on_path_denied`, and `die_on_other_error`.
|
29
|
+
|
10
30
|
|
11
31
|
## 2.0.17 / 2023-03-22
|
12
|
-
|
32
|
+
|
33
|
+
* Added `repo` and `git_ref` parameters, so files can be retrieved from git repositories at a given commit or tag.
|
34
|
+
|
13
35
|
|
14
36
|
## 2.0.16 / 2023-02-19
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
37
|
+
|
38
|
+
* Replaced hard-coded CSS in `denied` method with `flexible_error` class in
|
39
|
+
`demo/assets/css/style.css`.
|
40
|
+
* Added `-e` and `-x` options to `demo/_bin/debug`.
|
41
|
+
* Added configuration section `flexible_include` with supported parameters
|
42
|
+
`die_on_file_error`, `die_on_path_denied`, `die_on_run_error`,
|
43
|
+
`die_on_path_denied` and `die_on_other_error`.
|
44
|
+
* Fixed `undefined method 'path'` that occurred when `FLEXIBLE_INCLUDE_PATHS` was specified.
|
45
|
+
|
22
46
|
|
23
47
|
## 2.0.15 / 2023-02-18
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
48
|
+
|
49
|
+
* Replaced dependency `key-value-parser` with `jekyll_plugin_support`.
|
50
|
+
* Added `demo` website.
|
51
|
+
* Improved the documentation.
|
52
|
+
* Updated Rubocop configuration.
|
53
|
+
* Added `strip` option.
|
54
|
+
|
29
55
|
|
30
56
|
## 2.0.14 / 2022-09-27
|
31
|
-
|
57
|
+
|
58
|
+
* Added `key-value-parser` as a dependency.
|
59
|
+
|
32
60
|
|
33
61
|
## 2.0.13 / 2022-04-24
|
34
|
-
|
35
|
-
|
62
|
+
|
63
|
+
* Added `highlight` regex option, for highlighting.
|
64
|
+
* Added `number` option, for numbered lines.
|
36
65
|
|
37
66
|
## 2.0.12 / 2022-04-22
|
38
67
|
* 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
|
-
[![Gem Version](https://badge.fury.io/rb/jekyll_flexible_include.svg)](https://badge.fury.io/rb/jekyll_flexible_include)
|
3
|
-
===========
|
1
|
+
# Jekyll `flexible_include` Plugin [![Gem Version](https://badge.fury.io/rb/jekyll_flexible_include.svg)](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,8 @@ 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.8.4'
|
40
|
+
spec.add_dependency 'jekyll-sass-converter', '= 2.2.0'
|
39
41
|
spec.add_dependency 'rugged'
|
40
42
|
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, @logger
|
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,144 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'English'
|
3
|
+
require 'jekyll_from_to_until'
|
4
|
+
|
5
|
+
module FlexibleInclude
|
6
|
+
module FlexiblePrivateMethods
|
7
|
+
def denied(msg)
|
8
|
+
msg_no_html = remove_html_tags(msg)
|
9
|
+
@logger.error("#{@page['path']} - #{msg_no_html}")
|
10
|
+
raise FlexibleIncludeError, "#{@page['path']} - #{msg_no_html}".red, [] if @die_on_path_denied
|
11
|
+
|
12
|
+
"<p class='flexible_error'>#{msg}</p>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def highlight(content, pattern)
|
16
|
+
raise FlexibleIncludeError, "content is a #{content.class}, not a String" unless content.instance_of? String
|
17
|
+
|
18
|
+
content.gsub(Regexp.new(pattern), "<span class='bg_yellow'>\\0</span>")
|
19
|
+
end
|
20
|
+
|
21
|
+
def maybe_raise_error(msg, throw_error: true)
|
22
|
+
fmsg = format_error_message msg
|
23
|
+
@logger.error fmsg
|
24
|
+
return "<span class='flexible_error'>#{fmsg}</span>" unless throw_error
|
25
|
+
|
26
|
+
raise FlexibleIncludeError, msg, []
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_args
|
30
|
+
@copy_button = @helper.parameter_specified? 'copyButton'
|
31
|
+
@dark = ' dark' if @helper.parameter_specified? 'dark'
|
32
|
+
@do_not_escape = @helper.parameter_specified? 'do_not_escape'
|
33
|
+
@download = @helper.parameter_specified? 'download'
|
34
|
+
@from = @helper.parameter_specified? 'from'
|
35
|
+
@highlight_pattern = @helper.parameter_specified? 'highlight'
|
36
|
+
@label = @helper.parameter_specified? 'label'
|
37
|
+
@label_specified = @label
|
38
|
+
@number_lines = @helper.parameter_specified? 'number'
|
39
|
+
@strip = @helper.parameter_specified? 'strip'
|
40
|
+
@to = @helper.parameter_specified? 'to'
|
41
|
+
@until = @helper.parameter_specified? 'until'
|
42
|
+
|
43
|
+
# Download, dark, label or number implies pre
|
44
|
+
@pre = @helper.parameter_specified?('pre') || @copy_button || @dark || @download || @label_specified || @number_lines
|
45
|
+
|
46
|
+
@filename = @helper.parameter_specified? 'file'
|
47
|
+
|
48
|
+
unless @filename # Do this after all other options have been checked for
|
49
|
+
@filename = @helper.params.first.first
|
50
|
+
@helper.delete_parameter @helper.params.first
|
51
|
+
end
|
52
|
+
raise StandardError, "@filename (#{@filename}) is not a string", [] unless @filename.instance_of? String
|
53
|
+
|
54
|
+
@label ||= @filename
|
55
|
+
|
56
|
+
@logger.debug("@filename=#{@filename}")
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_html_tags(string)
|
60
|
+
string.gsub(/<[^>]*>/, '')
|
61
|
+
end
|
62
|
+
|
63
|
+
def render_completion
|
64
|
+
unless @path.start_with? '!'
|
65
|
+
maybe_raise_error("#{@path} does not exist", throw_error: @die_on_file_error) unless File.exist? @path
|
66
|
+
maybe_raise_error("#{@path} is not readable", throw_error: @die_on_file_error) unless Pathname.new(@path).readable?
|
67
|
+
|
68
|
+
@contents = File.read @path
|
69
|
+
unless @contents.instance_of? String
|
70
|
+
maybe_raise_error("contents has type a #{@contents.class}, not a String",
|
71
|
+
throw_error: @die_on_file_error)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
@contents = FromToUntil.from(@contents, @from) if @from
|
75
|
+
@contents = FromToUntil.to(@contents, @to) if @to
|
76
|
+
@contents = FromToUntil.until(@contents, @until) if @until
|
77
|
+
contents2 = @do_not_escape ? @contents : FlexibleClassMethods.escape_html(@contents)
|
78
|
+
contents2.strip! if @strip
|
79
|
+
maybe_raise_error("contents2 is a #{contents2.class}, not a String", throw_error: @die_on_file_error) unless contents2.instance_of? String
|
80
|
+
|
81
|
+
contents2 = highlight(contents2, @highlight_pattern) if @highlight_pattern
|
82
|
+
contents2 = FlexibleInclude.number_content(contents2) if @number_lines
|
83
|
+
result = @pre ? wrap_in_pre(@path, contents2) : contents2
|
84
|
+
<<~END_OUTPUT
|
85
|
+
#{result}
|
86
|
+
#{@helper.attribute if @helper.attribution}
|
87
|
+
END_OUTPUT
|
88
|
+
end
|
89
|
+
|
90
|
+
def run(cmd)
|
91
|
+
if cmd.empty?
|
92
|
+
@do_not_escape = true
|
93
|
+
return maybe_raise_error('FlexibleIncludeError: Empty command string', throw_error: @die_on_other_error)
|
94
|
+
end
|
95
|
+
|
96
|
+
@logger.debug { "Executing #{cmd}" }
|
97
|
+
%x[#{cmd}].chomp
|
98
|
+
rescue FlexibleIncludeError => e
|
99
|
+
raise e
|
100
|
+
rescue StandardError => e
|
101
|
+
@do_not_escape = true
|
102
|
+
e = e.exception "'#{e.message}' while executing '#{cmd}'"
|
103
|
+
maybe_reraise_error(e, throw_error: @die_on_run_error)
|
104
|
+
end
|
105
|
+
|
106
|
+
def setup
|
107
|
+
@helper.gem_file __FILE__ # Enables attribution
|
108
|
+
self.class.security_check
|
109
|
+
|
110
|
+
config = @config[PLUGIN_NAME]
|
111
|
+
if config
|
112
|
+
@die_on_file_error = config['die_on_file_error'] == true
|
113
|
+
@die_on_other_error = config['die_on_other_error'] == true
|
114
|
+
@die_on_path_denied = config['die_on_path_denied'] == true
|
115
|
+
@die_on_run_error = config['die_on_run_error'] == true
|
116
|
+
end
|
117
|
+
|
118
|
+
parse_args
|
119
|
+
end
|
120
|
+
|
121
|
+
PREFIX = "<button class='copyBtn' data-clipboard-target=".freeze
|
122
|
+
SUFFIX = "title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>".freeze
|
123
|
+
|
124
|
+
def wrap_in_pre(path, content)
|
125
|
+
basename = File.basename(path)
|
126
|
+
label_or_href = if @download
|
127
|
+
label = @label_specified ? @label : basename
|
128
|
+
<<~END_HREF
|
129
|
+
<a href='data:text/plain;charset=UTF-8,#{basename}' download='#{basename}'
|
130
|
+
title='Click on the file name to download the file'>#{label}</a>
|
131
|
+
END_HREF
|
132
|
+
else
|
133
|
+
@label_specified ? @label : basename
|
134
|
+
end
|
135
|
+
pre_id = "id#{SecureRandom.hex 6}"
|
136
|
+
copy_button = @copy_button ? "#{PREFIX}'##{pre_id}'#{SUFFIX}" : ''
|
137
|
+
dark_label = ' darkLabel' if @dark
|
138
|
+
<<~END_PRE
|
139
|
+
<div class="codeLabel#{dark_label}">#{label_or_href}</div>
|
140
|
+
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer#{@dark}" id="#{pre_id}">#{copy_button}#{content}</pre>
|
141
|
+
END_PRE
|
142
|
+
end
|
143
|
+
end
|
144
|
+
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.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Slinn
|
@@ -10,22 +10,50 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2024-02-27 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.
|
35
|
+
version: 0.8.4
|
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.
|
42
|
+
version: 0.8.4
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: jekyll-sass-converter
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - '='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 2.2.0
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - '='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 2.2.0
|
29
57
|
- !ruby/object:Gem::Dependency
|
30
58
|
name: rugged
|
31
59
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,9 +87,13 @@ files:
|
|
59
87
|
- jekyll_flexible_include_plugin.gemspec
|
60
88
|
- lib/flexible_include.rb
|
61
89
|
- lib/flexible_include/version.rb
|
62
|
-
- lib/
|
90
|
+
- lib/flexible_include_class.rb
|
91
|
+
- lib/flexible_include_private_methods.rb
|
92
|
+
- lib/git_file_reader.rb
|
63
93
|
- spec/flexible_include_spec.rb
|
94
|
+
- spec/git_file_reader_spec.rb
|
64
95
|
- spec/spec_helper.rb
|
96
|
+
- spec/status_persistence.txt
|
65
97
|
homepage: https://www.mslinn.com/jekyll_plugins/jekyll_flexible_include.html
|
66
98
|
licenses:
|
67
99
|
- MIT
|
@@ -89,12 +121,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
121
|
- !ruby/object:Gem::Version
|
90
122
|
version: '0'
|
91
123
|
requirements: []
|
92
|
-
rubygems_version: 3.
|
124
|
+
rubygems_version: 3.5.6
|
93
125
|
signing_key:
|
94
126
|
specification_version: 4
|
95
127
|
summary: Jekyll plugin supports various ways to include content into the generated
|
96
128
|
site.
|
97
129
|
test_files:
|
98
130
|
- spec/flexible_include_spec.rb
|
131
|
+
- spec/git_file_reader_spec.rb
|
99
132
|
- spec/spec_helper.rb
|
133
|
+
- spec/status_persistence.txt
|
100
134
|
...
|