jekyll-dry 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.ruby-version +1 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +79 -0
- data/Rakefile +5 -0
- data/architecture.md +65 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/detailed_design.md +48 -0
- data/jekyll-dry.gemspec +31 -0
- data/lib/jekyll-dry.rb +116 -0
- data/lib/version.rb +5 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e999472a8eab6ae46d936e0455d0d1b036cc9b9dc0cd93d61dbfe3db9ca4a465
|
4
|
+
data.tar.gz: 2a6370165c59fbbf18049f19d3f5a51f6f0816a296d57ac9871f80696f6d7cec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '058246a2c85ef4efb7f483f46fb0f0b47071e9256f7256ede62bce018007c1d4279c64b51a82700d0b8c6dde8afa194659b8394a8e85d8b048a38e70a1632ed4'
|
7
|
+
data.tar.gz: 2a1a41e461641c5fd3ff5a10ad0fa7a293c0645d0768169d5c5f30e7296dca13633870b3ac995b03841e74ffb0e9d4d5d940ff4c6eda3f4bcca249854d50c87f
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6.5
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at vagiz.d@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Vagiz Duseev
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 vduseev
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# jekyll-dry
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/vduseev/jekyll-dry.svg?branch=master)](https://travis-ci.org/vduseev/jekyll-dry)
|
4
|
+
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f261f89a633e47e3964ecc37bb826d4f)](https://www.codacy.com/manual/vduseev/jekyll-dry?utm_source=github.com&utm_medium=referral&utm_content=vduseev/jekyll-dry&utm_campaign=Badge_Grade)
|
5
|
+
|
6
|
+
Jekyll plugin that helps you to implement the DRY (Don't Repeat Yourself) principle while writing documentation using Jekyll.
|
7
|
+
The plugin allows you to reuse any fragments of markdown multiple times across a single Jekyll website. The fragments can include Liquid syntax and tags.
|
8
|
+
|
9
|
+
## Table of contents
|
10
|
+
|
11
|
+
* User Guide:
|
12
|
+
* [Usage](#usage)
|
13
|
+
* [Installation](#installation)
|
14
|
+
* [Incompatibility with GitHub Pages](#incompatibility-with-github-pages)
|
15
|
+
* Implementation:
|
16
|
+
* [Plugin's architecture](architecture.md)
|
17
|
+
* [Detailed Design](detailed_design.md)
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Fragment's beginning tag: `{% frag ... %}`
|
22
|
+
|
23
|
+
`frag` tag marks the beginning of a reusable fragment and assigns a unique id to it. Everything between `frag` and `endfrag` tags with unique id is considered a unique reusable fragment that can be included to any page.
|
24
|
+
|
25
|
+
<a name="example-of-frag-usage">Example of usage</a>:
|
26
|
+
```
|
27
|
+
It was decided that the amount of dependencies of the executable will
|
28
|
+
be kept at minimum. For that reason {% frag dependencies %}the only
|
29
|
+
dependency is Python's `argparse` library{% endfrag dependencies %}.
|
30
|
+
```
|
31
|
+
|
32
|
+
As a result of the above markup the `dependencies` file will be generated and will contain following text:
|
33
|
+
```
|
34
|
+
the only dependency is Python's `argparse` library
|
35
|
+
```
|
36
|
+
|
37
|
+
The generated file is now available for inclusion at any page, including the page where it was initially declared. During generation process `frag` tag is replaced in page with an empty string.
|
38
|
+
|
39
|
+
### Fragment's end tag: `{% endfrag ... %}`
|
40
|
+
|
41
|
+
`endfrag` tag only marks the end of the fragment declared by `frag` tag and is only used by it. After Jekyll processes the page `endfrag` disappears.
|
42
|
+
|
43
|
+
If no matching `endfrag` is found for a given `frag`, then that `frag` will not be counted as a valid fragment and will not produce an include file.
|
44
|
+
|
45
|
+
<a name="example-of-endfrag-usage">Example of usage</a>: [see above](#example-of-frag-usage).
|
46
|
+
|
47
|
+
During generation process `endfrag` tag is replaced in page with an empty string.
|
48
|
+
|
49
|
+
### Fragment embedding tag: `{% > ... %}`
|
50
|
+
|
51
|
+
`>` inclusion tag that searches for an include file with a given unique id generated by `frag` tag. Works in exactly same way as traditional `include` tag, but does not use parameters.
|
52
|
+
|
53
|
+
<a name="example-of-inclusion-usage">Example of usage:</a>
|
54
|
+
```
|
55
|
+
Testing will require a test suite that covers a set of test cases
|
56
|
+
derived from initial use cases. However, since {% > dependencies %},
|
57
|
+
the testing will be entirely implemented using just the `py.test`
|
58
|
+
library.
|
59
|
+
```
|
60
|
+
|
61
|
+
Which will produce the following result:
|
62
|
+
|
63
|
+
Testing will require a test suite that covers a set of test cases derived from initial use cases. However, since the only
|
64
|
+
dependency is Python's `argparse` library, the testing will be entirely implemented using just the `py.test` library.
|
65
|
+
|
66
|
+
## Installation
|
67
|
+
|
68
|
+
Drop the `jekyll-dry.rb` file into the `_plugins` directory of the website as described in the first option of enabling a plugin in [Jekyll documentation](https://jekyllrb.com/docs/plugins/#installing-a-plugin):
|
69
|
+
```
|
70
|
+
In your site source root, make a _plugins directory. Place your plugins
|
71
|
+
here. Any file ending in *.rb inside this directory will be loaded
|
72
|
+
before Jekyll generates your site.
|
73
|
+
```
|
74
|
+
|
75
|
+
## Incompatibility with GitHub Pages
|
76
|
+
|
77
|
+
Since this is a custom plugin which is not included in the code set of GitHub pages plugins it will not work with GitHub's automatic website generation.
|
78
|
+
|
79
|
+
You will have to generate the website locally and then push the contents of the `_site` folder to GitHub in order to publish your website.
|
data/Rakefile
ADDED
data/architecture.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Architecture of jekyll-dry plugin
|
2
|
+
|
3
|
+
The plugin consists of three new Liquid tags: `frag`, `endfrag`, and `>`.
|
4
|
+
|
5
|
+
## Fragment's beginning tag: `frag`
|
6
|
+
|
7
|
+
`frag` tag is inherited from `Liquid::Block` and automatically detects `endfrag` tag with the same id that was used for `frag` tag itself. That means that the tag's class method must perform a text search on the page using regex expression with the starting position of the search equal to the position of the first character after the current `frag` tag.
|
8
|
+
|
9
|
+
If a corresponding `endfrag` tag is not found, then the `render()` method of the tag does not return anything.
|
10
|
+
|
11
|
+
However, if matching `endfrag` tag is found, then the whole text between the pair of `frag` and `endfrag` tags is captured and saved to a variable.
|
12
|
+
|
13
|
+
Any other `frag` and `endfrag` tags trapped in the captured text are removed.
|
14
|
+
|
15
|
+
After that, the text is saved to the file in the output directory. The saving path must be `output_directory_path/_fragments/`. And the name of the file must be equal to the unique id (*or variable as it is called in the source code of `include` tag*) provided with the tag.
|
16
|
+
|
17
|
+
If the file with the same name already exists in the `_fragments` directory, then an exception must be thrown from the plugin, saying:
|
18
|
+
```
|
19
|
+
The fragment with such name already exists: <fragment's name here>
|
20
|
+
```
|
21
|
+
|
22
|
+
## Fragment's end tag: `endfrag`
|
23
|
+
|
24
|
+
`endfrag` tag is only required for the correct work of the `frag` tag. It is replaced with empty string, or simply, not rendered at all when processed.
|
25
|
+
|
26
|
+
## Fragment's embedding tag: `>`
|
27
|
+
|
28
|
+
`>` embedding tag must have a lower priority than `frag` tag. This is required because all fragment files must be generated before `>` tag gets parsed and processed by Jekyll.
|
29
|
+
|
30
|
+
Embedding tag looks for the file specified in the variable to the tag in the `output_directory_path/_fragments/` directory.
|
31
|
+
|
32
|
+
If file is not found then nothing is rendered.
|
33
|
+
|
34
|
+
If, however, the file is found, the embedding tag reads it and resolves all internal inclusions completely (see a [note on recursive fragment embedding](#recursion)), regenerating the initial requested fragment while doing so.
|
35
|
+
|
36
|
+
If no regeneration is required and the fragment already has a final form, then the `>` tag embeds the contents of the file in place of itself on the page.
|
37
|
+
|
38
|
+
## <a name="recursion">Note on recursive fragment embedding and loops</a>
|
39
|
+
|
40
|
+
A fragment might contain an inclusion of another fragment inside itself. For example:
|
41
|
+
```
|
42
|
+
{% frag example_of_loop %}
|
43
|
+
This text illustrates that the fragment can contain another fragment
|
44
|
+
inside itself. {% > totally_different_fragment %} Such inclusion is
|
45
|
+
correctly resolved during generation process.
|
46
|
+
{% endfrag example_of_loop %}
|
47
|
+
```
|
48
|
+
|
49
|
+
In order to resolve this the `frag` tag first generates all fragment files, including `>` tag inside them left intact.
|
50
|
+
|
51
|
+
The example above will result in a following fragment generated.
|
52
|
+
|
53
|
+
`output_directory_path/_fragments/example_of_loop`:
|
54
|
+
```
|
55
|
+
This text illustrates that the fragment can contain another fragment
|
56
|
+
inside itself. {% > totally_different_fragment %} Such inclusion is
|
57
|
+
correctly resolved during generation process.
|
58
|
+
```
|
59
|
+
|
60
|
+
Later, when `>` tag is being processed, the first time it encounters the usage of the `example_of_loop` fragment in a page, it detects an internal inclusion of another fragment, in this case a fragment called `totally_different_fragment`. So, it goes and pulls up that fragment's file and replaces the `{% > totally_different_fragment %}` string in `example_of_loop` file with the actual content of `totally_different_fragment` file. Hence, it regenerates the `example_of_loop` file leaving it ready-to-use for all other cases, where this fragment will be included whether it's the same page or a different one. This action must be performed in a recursive fashion, or using a stack, until the fragment embedded in another fragment is static and has a determined content. In other words, when embedded fragment has no inclusions in it. As soon as this condition is satisfied the loop, or recursion, has to be stopped.
|
61
|
+
|
62
|
+
A special validity check must be included into the logic to make sure that under no circumstances a fragment contains an inclusion of itself. Or, in other words, that there is no loop in fragment embedding. Whenever this is detected, an exception must be thrown, saying:
|
63
|
+
```
|
64
|
+
A loop in fragment inclusion is detected: <looped fragment's name here>
|
65
|
+
```
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "jekyll/dry"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/detailed_design.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Detailed design of jekyll-dry plugin
|
2
|
+
|
3
|
+
## Fragment's beginning tag: `frag`
|
4
|
+
|
5
|
+
### Hierarchy note
|
6
|
+
|
7
|
+
Must be in module `Jekyll`. Must be in module `Tags`.
|
8
|
+
|
9
|
+
### Class `FragTag`
|
10
|
+
|
11
|
+
Class `FragTag` must be inherited from `Liquid::Tag`.
|
12
|
+
|
13
|
+
#### Methods of `FragTag`
|
14
|
+
|
15
|
+
##### `initialize` method
|
16
|
+
|
17
|
+
###### Arguments:
|
18
|
+
|
19
|
+
* tag_name
|
20
|
+
* markup
|
21
|
+
* tokens
|
22
|
+
|
23
|
+
###### Logic:
|
24
|
+
1. The `initialize method` should first make a call to the `super` method.
|
25
|
+
1. Then it should verify that markup string matches the predefined markup pattern similar to the one in the source code of the `include` tag. The regex below matches following example of markup:
|
26
|
+
```
|
27
|
+
file_path.ext param1="value1" param2='value2'
|
28
|
+
```
|
29
|
+
The regex that matches that string is:
|
30
|
+
```
|
31
|
+
# Capture group "fragment_id":
|
32
|
+
# - Starts with any amount of any symbols other than "{"
|
33
|
+
# - One or more untitled capture group:
|
34
|
+
# - Starts with "{{"
|
35
|
+
# - Followed by any number of whitespace characters
|
36
|
+
# - One or more: word, "-", or "."
|
37
|
+
# - Followed by any number of whitespace characters
|
38
|
+
# - Maybe one "|" followed by any amount of any symbols
|
39
|
+
# - Ends with "}}"
|
40
|
+
# - And any amount of symbols other than "\s", "{", or "}"
|
41
|
+
#
|
42
|
+
# Capture group "params":
|
43
|
+
# - Everything not captured by "fragment_id" group
|
44
|
+
|
45
|
+
(?<fragment_id>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
|
46
|
+
(?<params>.*)
|
47
|
+
```
|
48
|
+
1. If markup matched this regex, then a capture group named `fragment_id` must be extracted
|
data/jekyll-dry.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "jekyll-dry"
|
7
|
+
spec.version = Jekyll::JekyllDry::VERSION
|
8
|
+
spec.authors = ["vduseev"]
|
9
|
+
spec.email = ["vagiz.d@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Don't Repeat Yourself. Reuse text fragments.}
|
12
|
+
spec.description = %q{Jekyll plugin that helps you to implement the DRY (Don't Repeat Yourself) principle while writing documentation using Jekyll.}
|
13
|
+
spec.homepage = "https://github.com/vduseev/jekyll-dry"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/vduseev/jekyll-dry"
|
18
|
+
spec.metadata["changelog_uri"] = "https://raw.githubusercontent.com/vduseev/jekyll-dry/master/CHANGELOG.md"
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
|
+
end
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
end
|
data/lib/jekyll-dry.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# (The MIT License)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2018 Vagiz Duseev
|
4
|
+
|
5
|
+
module ContextExt
|
6
|
+
def find_variable(key)
|
7
|
+
variable = super(key)
|
8
|
+
unless variable
|
9
|
+
variable = registers[:site].fragments[key]
|
10
|
+
end
|
11
|
+
variable
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Jekyll::Hooks.register :site, :pre_render do |container, payload|
|
16
|
+
Liquid::Context.prepend(ContextExt)
|
17
|
+
end
|
18
|
+
|
19
|
+
module Jekyll
|
20
|
+
class FragmentGenerator < Generator
|
21
|
+
TagStart = /#{Liquid::TagStart}#{Liquid::WhitespaceControl}?\s*/o
|
22
|
+
TagEnd = /\s*#{Liquid::WhitespaceControl}?#{Liquid::TagEnd}/o
|
23
|
+
FragmentSubPattern = /#{TagStart}(?i:(end)?frag)\s*(.*?)#{TagEnd}\s*/om
|
24
|
+
FullFragmentPattern = %r/
|
25
|
+
#{TagStart}(?i:frag)\s*(?<id>.*?)#{TagEnd}
|
26
|
+
(?<body>.*)
|
27
|
+
#{TagStart}(?i:endfrag)\s*\k<id>#{TagEnd}
|
28
|
+
/xom
|
29
|
+
|
30
|
+
def generate(site)
|
31
|
+
# Initialize hash map that will store the fragments for rendering
|
32
|
+
class << site
|
33
|
+
attr_accessor :fragments
|
34
|
+
end
|
35
|
+
site.fragments = Hash.new
|
36
|
+
|
37
|
+
# Parse fragments on pages
|
38
|
+
site.pages.each do |page|
|
39
|
+
fragments = parse_page(page.content)
|
40
|
+
|
41
|
+
site.fragments.merge!(fragments) do |key, old_val, new_val|
|
42
|
+
# This section is invoked every time there is a duplicate key
|
43
|
+
# in site.fragments and parsed fragments
|
44
|
+
raise Liquid::SyntaxError.new(
|
45
|
+
"Duplicate fragment #{key} on page #{page.url}",
|
46
|
+
original_fragment: old_val,
|
47
|
+
duplicate_fragment: new_val
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_page(content)
|
54
|
+
fragments = Hash.new
|
55
|
+
|
56
|
+
pos = 0
|
57
|
+
while m = FullFragmentPattern.match(content, pos)
|
58
|
+
body = clean_fragment(m[:body])
|
59
|
+
fragments.store(m[:id], body)
|
60
|
+
puts("fragment #{m[:id]}:", body)
|
61
|
+
pos = m.begin(:id)
|
62
|
+
end
|
63
|
+
fragments
|
64
|
+
end
|
65
|
+
|
66
|
+
def clean_fragment(body)
|
67
|
+
pattern = /\s*#{FragmentSubPattern}\s*/om
|
68
|
+
clean_body = body.gsub(FragmentSubPattern, "")
|
69
|
+
clean_body.lstrip!
|
70
|
+
clean_body.rstrip!
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class FragmentBlockBody < Liquid::BlockBody
|
75
|
+
def initialize
|
76
|
+
super
|
77
|
+
end
|
78
|
+
|
79
|
+
def whitespace_handler(token, parse_context)
|
80
|
+
return unless token =~ Jekyll::FragmentGenerator::FragmentSubPattern
|
81
|
+
previous_token = @nodelist.last
|
82
|
+
if previous_token.is_a? String
|
83
|
+
# previous_token.rstrip!
|
84
|
+
end
|
85
|
+
parse_context.trim_whitespace = true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class FragmentBlock < Liquid::Block
|
90
|
+
def initialize(tag_name, markup, options)
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse(tokens)
|
95
|
+
@body = FragmentBlockBody.new
|
96
|
+
while parse_body(@body, tokens)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Fragment < FragmentBlock
|
102
|
+
def initialize(tag_name, markup, options)
|
103
|
+
super
|
104
|
+
@fragment_id, _ = markup.strip.split(%r!\s+!, 2)
|
105
|
+
end
|
106
|
+
|
107
|
+
def render(context)
|
108
|
+
output = super
|
109
|
+
#context[@fragment_id] = output
|
110
|
+
#context.resource_limits.assign_score += output.length
|
111
|
+
output
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
Liquid::Template.register_tag('frag', Jekyll::Fragment)
|
data/lib/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-dry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- vduseev
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-11-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Jekyll plugin that helps you to implement the DRY (Don't Repeat Yourself)
|
42
|
+
principle while writing documentation using Jekyll.
|
43
|
+
email:
|
44
|
+
- vagiz.d@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- ".ruby-version"
|
51
|
+
- ".travis.yml"
|
52
|
+
- CODE_OF_CONDUCT.md
|
53
|
+
- Gemfile
|
54
|
+
- Gemfile.lock
|
55
|
+
- LICENSE
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- _playground/about.md
|
60
|
+
- architecture.md
|
61
|
+
- bin/console
|
62
|
+
- bin/setup
|
63
|
+
- detailed_design.md
|
64
|
+
- jekyll-dry.gemspec
|
65
|
+
- lib/jekyll-dry.rb
|
66
|
+
- lib/version.rb
|
67
|
+
homepage: https://github.com/vduseev/jekyll-dry
|
68
|
+
licenses:
|
69
|
+
- MIT
|
70
|
+
metadata:
|
71
|
+
homepage_uri: https://github.com/vduseev/jekyll-dry
|
72
|
+
source_code_uri: https://github.com/vduseev/jekyll-dry
|
73
|
+
changelog_uri: https://raw.githubusercontent.com/vduseev/jekyll-dry/master/CHANGELOG.md
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubygems_version: 3.0.3
|
90
|
+
signing_key:
|
91
|
+
specification_version: 4
|
92
|
+
summary: Don't Repeat Yourself. Reuse text fragments.
|
93
|
+
test_files: []
|