jekyll-relationships 0.1.0.alpha
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 +7 -0
- data/lib/jekyll-relationships/configuration/debug_setting.rb +117 -0
- data/lib/jekyll-relationships/configuration/defaults.rb +54 -0
- data/lib/jekyll-relationships/configuration/frontmatter.rb +102 -0
- data/lib/jekyll-relationships/configuration/hash_utilities.rb +55 -0
- data/lib/jekyll-relationships/configuration/multiple_settings.rb +101 -0
- data/lib/jekyll-relationships/configuration/parser.rb +551 -0
- data/lib/jekyll-relationships/configuration/prune_rule_settings.rb +101 -0
- data/lib/jekyll-relationships/configuration/prune_settings.rb +82 -0
- data/lib/jekyll-relationships/configuration/tree_frontmatter.rb +244 -0
- data/lib/jekyll-relationships/configuration/tree_settings.rb +74 -0
- data/lib/jekyll-relationships/configuration.rb +250 -0
- data/lib/jekyll-relationships/debug_logger.rb +104 -0
- data/lib/jekyll-relationships/definitions/configured_relationship.rb +61 -0
- data/lib/jekyll-relationships/definitions/normal_relationship.rb +51 -0
- data/lib/jekyll-relationships/definitions/prune_rule.rb +67 -0
- data/lib/jekyll-relationships/definitions/tree_relationship.rb +48 -0
- data/lib/jekyll-relationships/documents/registry.rb +136 -0
- data/lib/jekyll-relationships/engine/raw_path_state.rb +217 -0
- data/lib/jekyll-relationships/engine/relationship_state.rb +251 -0
- data/lib/jekyll-relationships/engine/session.rb +187 -0
- data/lib/jekyll-relationships/engine/write_back.rb +154 -0
- data/lib/jekyll-relationships/engine.rb +228 -0
- data/lib/jekyll-relationships/errors.rb +30 -0
- data/lib/jekyll-relationships/generators/relationships.rb +27 -0
- data/lib/jekyll-relationships/pruning/normal_graph.rb +119 -0
- data/lib/jekyll-relationships/pruning/rule_pruner.rb +67 -0
- data/lib/jekyll-relationships/pruning/tree_phase.rb +351 -0
- data/lib/jekyll-relationships/pruning/tree_provenance.rb +32 -0
- data/lib/jekyll-relationships/references/accumulator.rb +185 -0
- data/lib/jekyll-relationships/references/template.rb +222 -0
- data/lib/jekyll-relationships/resolvers/base.rb +207 -0
- data/lib/jekyll-relationships/run_logger.rb +101 -0
- data/lib/jekyll-relationships/support/data_path.rb +194 -0
- data/lib/jekyll-relationships/support/frontmatter_path.rb +217 -0
- data/lib/jekyll-relationships/support/hash_deep_merge.rb +63 -0
- data/lib/jekyll-relationships/support/placeholders.rb +21 -0
- data/lib/jekyll-relationships/support/string_array.rb +157 -0
- data/lib/jekyll-relationships/trees/edge_builder.rb +196 -0
- data/lib/jekyll-relationships/trees/graph.rb +454 -0
- data/lib/jekyll-relationships/version.rb +11 -0
- data/lib/jekyll-relationships.rb +34 -0
- data/readme.md +509 -0
- metadata +189 -0
data/readme.md
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
# Jekyll Relationships
|
|
2
|
+
|
|
3
|
+
Plugin for Jekyll that allows collection documents to specify their relationships with each other, via many-to-many links. These can form trees, graphs or relational-database-like structures. Exposes the relationships in each document's frontmatter.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
How documents link to each other is specified by configuration.
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
relationships:
|
|
11
|
+
enabled: true # set to false for global disable
|
|
12
|
+
|
|
13
|
+
# The relationships that exist on this site
|
|
14
|
+
relationships: # array, see Relationships below
|
|
15
|
+
|
|
16
|
+
# The frontmatter keys that specify relationships - global defaults
|
|
17
|
+
frontmatter: # see Frontmatter below
|
|
18
|
+
base: relationships # prepend to other keys
|
|
19
|
+
primary: nil # use Document path as primary key
|
|
20
|
+
foreign: <collection> # because of base, treated as `relationships.<foreign>`
|
|
21
|
+
output: nil # see Output below
|
|
22
|
+
|
|
23
|
+
# Modify the shape of the foreign reference object
|
|
24
|
+
references: # see References below
|
|
25
|
+
id: <key>
|
|
26
|
+
collection: <collection>
|
|
27
|
+
page: <page>
|
|
28
|
+
|
|
29
|
+
# Settings for tree relationships
|
|
30
|
+
tree: # see Trees below
|
|
31
|
+
frontmatter:
|
|
32
|
+
parent: parent # which keys specify a single parent
|
|
33
|
+
child: child # which keys specify a single child
|
|
34
|
+
parents: parents # which keys specify multiple parents
|
|
35
|
+
children: children # which keys specify multiple children
|
|
36
|
+
ancestors: ancestors # key on which to set ancestors
|
|
37
|
+
descendants: descendants # key on which to set descendants
|
|
38
|
+
max:
|
|
39
|
+
parents: -1 # max parents per item (-1 = unlimited)
|
|
40
|
+
children: -1 # max children per item
|
|
41
|
+
url: false # infer parent by item url
|
|
42
|
+
|
|
43
|
+
# Global pruning settings
|
|
44
|
+
prune: # see Pruning below
|
|
45
|
+
combine: true
|
|
46
|
+
iterations: 10
|
|
47
|
+
tree:
|
|
48
|
+
orphans: grandparents
|
|
49
|
+
|
|
50
|
+
# What should happen to multiple links to the same thing?
|
|
51
|
+
multiple: drop # see Multiple Links below
|
|
52
|
+
|
|
53
|
+
# Override keywords in case they are a clash with your collection/frontmatter names
|
|
54
|
+
keywords: # see Keywords below
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The values shown above are the built-in defaults that will apply even if you don't specify these settings at all.
|
|
58
|
+
|
|
59
|
+
Throughout the config, any item which can be an array may also be given as a single string (implicitly a one-item array) or a comma-delimited string (will be split to an array).
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Relationships
|
|
63
|
+
|
|
64
|
+
`relationships` is an array specifying the relationships on this site. Example:
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
relationships:
|
|
68
|
+
# categories and tags are each in a tree
|
|
69
|
+
- from: categories, tags
|
|
70
|
+
to: self
|
|
71
|
+
mode: parent
|
|
72
|
+
# products
|
|
73
|
+
- from: products
|
|
74
|
+
to: categories, tags
|
|
75
|
+
frontmatter: # override for this relationship
|
|
76
|
+
base: data # override `base` for this `frontmatter`
|
|
77
|
+
foreign: <collection>, links # find references at `data.categories`, `data.tags` and `data.links`.
|
|
78
|
+
- from: products
|
|
79
|
+
to: # array form
|
|
80
|
+
- collection: products # hash form
|
|
81
|
+
frontmatter: # override frontmatter for this `to` target
|
|
82
|
+
foreign: body.subproducts
|
|
83
|
+
- services # regular string form
|
|
84
|
+
- collection: product-types
|
|
85
|
+
mode: tree # override `mode` for this `to` target
|
|
86
|
+
mode: bidirectional
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Each array item has:
|
|
90
|
+
|
|
91
|
+
* `from` (required): one or more collection labels.
|
|
92
|
+
* `to` (rquired): one or more collection labels.
|
|
93
|
+
* Each item in `from` is related to each item in `to`.
|
|
94
|
+
* Instead of a collection label a keyword can be used:
|
|
95
|
+
* `self`: each item in `from` relates "to itself". E.g. if an item in `from` is `products` then `self` means `products` for that item i.e. products can relate to other products.
|
|
96
|
+
* `others`: each item in `from` relates to any of the other items besides itself
|
|
97
|
+
* `all`: each item in `from` relates to any of those items including itself
|
|
98
|
+
* Instead of a string a hash can be used:
|
|
99
|
+
* `collection`: the string as before
|
|
100
|
+
* Hash can also specify a local `frontmatter` and/or `mode` override
|
|
101
|
+
* `frontmatter` (optional): the values set in global config apply to all relationships by default, but you can override specific properties of it per relationship by setting them here.
|
|
102
|
+
* `mode`
|
|
103
|
+
* `link` (default): `from` links to `to`.
|
|
104
|
+
* `parent`/`child`: items in `from` can have items in `to` as their parent/child (which also implies the reverse relationship). See [Trees](#trees) below.
|
|
105
|
+
* `bidirectional`: like `link`, but whenever a link is added, it is also added to the target in the reverse direction automatically.
|
|
106
|
+
* `prune` (optional): remove documents whose finally-resolved relationship count for this entry is too low. See [Pruning](#pruning) below.
|
|
107
|
+
|
|
108
|
+
Duplicate or clashing relationship definitions will throw an error. A bidirectional relationship "uses up" the reverse definition, so "from A to B, bidirectional" followed by a "from B to A, link/bidirectional" definition is a duplicate and throws an error.
|
|
109
|
+
|
|
110
|
+
The defined relationships will be processed and resolved. This will:
|
|
111
|
+
|
|
112
|
+
* Ensure arrays of references are complete
|
|
113
|
+
* Upgrade the references between documents to richer objects
|
|
114
|
+
* Protect against invalid or duplicate references (A can't link to B twice in the same array)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
## Frontmatter
|
|
118
|
+
|
|
119
|
+
Relationships between documents are specified by values in each document's frontmatter. Somewhere in the frontmatter of documents you must have:
|
|
120
|
+
|
|
121
|
+
* A "Primary Key": a value that identifies this document
|
|
122
|
+
* A "Foreign Key": a value that identifies another document's primary key
|
|
123
|
+
|
|
124
|
+
You specify where in the frontmatter these primary/foreign keys exist, using:
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
frontmatter:
|
|
128
|
+
primary: id # the frontmatter key `id` holds the primary key value
|
|
129
|
+
foreign: <collection> # <collection> is replaced by the label of a `to` collection. The frontmatter key that matches this label holds a reference to a document in that collection.
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
* Both keys can be specified with dot-notation to access deeply nested keys, e.g. `meta.details.relationships.id`.
|
|
133
|
+
* `<collection>` is valid only within `foreign`. Example:
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
relationships:
|
|
137
|
+
- from: products
|
|
138
|
+
to: categories, tags
|
|
139
|
+
frontmatter:
|
|
140
|
+
foreign: links.<collection>_links
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
In `products` documents, this would find links to `categories` at `links.categories_links` and links to `tags` at `links.tags_links`.
|
|
144
|
+
* `foreign` can be an array of frontmatter locations where references will be read. These will all be accumulated.
|
|
145
|
+
|
|
146
|
+
### Output
|
|
147
|
+
|
|
148
|
+
Relationships are read from the keys you give, and processed. Processing may modify the relationships, add/removing some. This will be written back into the frontmatter at the first location defined in `foreign`. However, if you want to leave this alone, or if the foreign location spans across an array and so can't be written back to, a separate key `output` gives the frontmatter location where you want the final set of relationships to be written. It may contain `<collection>`. Example:
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
relationships:
|
|
152
|
+
frontmatter:
|
|
153
|
+
base: ''
|
|
154
|
+
primary: meta.id
|
|
155
|
+
foreign: data.<collection>
|
|
156
|
+
output: meta.relationships.<collection>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Base
|
|
160
|
+
|
|
161
|
+
Any definition of `frontmatter` may specify a `base` which will be prepended to both the `primary` and `foreign` keys within that `frontmatter` definition.
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
frontmatter:
|
|
165
|
+
base: links
|
|
166
|
+
primary: id # treated as `links.id`
|
|
167
|
+
foreign: <collection> # treated as `links.<collection>`
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The default `base` is `relationships` i.e. all information about relationships is by default read from and stored under a `relationships` key on any document in the site.
|
|
171
|
+
|
|
172
|
+
You can unset `base` with an empty string.
|
|
173
|
+
|
|
174
|
+
### Defaults and Overrides
|
|
175
|
+
|
|
176
|
+
`frontmatter` settings for any given relationship is determined by a series of overrides:
|
|
177
|
+
|
|
178
|
+
1. Built-in defaults
|
|
179
|
+
2. Global config
|
|
180
|
+
3. Override at relationship level
|
|
181
|
+
4. Override at `to` level
|
|
182
|
+
|
|
183
|
+
For instance if `base` is defined higher in the chain, it will apply by default to any `frontmatter` given lower down, unless it is unset at that level.
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
## References
|
|
187
|
+
|
|
188
|
+
In a document's frontmatter, a link from one document to another is a "reference". The references in frontmatter can be either:
|
|
189
|
+
|
|
190
|
+
* A string of the foreign key
|
|
191
|
+
* A reference hash where some property gives the foreign key
|
|
192
|
+
* An array of either of the above, or mixing the above.
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
# superbrand-shoes.md
|
|
198
|
+
title: SuperBrand Shoes
|
|
199
|
+
relationships:
|
|
200
|
+
categories:
|
|
201
|
+
- Shoes
|
|
202
|
+
- id: Trainers
|
|
203
|
+
- Clothing
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The `references` config lets you modify the shape of reference hashes. Its keys are arbitrary, and its values can be:
|
|
207
|
+
|
|
208
|
+
* `<key>` exactly (required): this key gives the foreign key, and must be present.
|
|
209
|
+
* `<collection>`: this key gives the collection in which the foreign document exists.
|
|
210
|
+
* `<page>`: this key will be set to the actual `Jekyll::Document` instance for the foreign document.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
# _config.yml
|
|
216
|
+
relationships:
|
|
217
|
+
references:
|
|
218
|
+
link_to: <key> # foreign key is now stored on the property 'link_to'
|
|
219
|
+
# collection is removed
|
|
220
|
+
page: <page>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
When relationships are processed, references are read loosely: strings are foreign keys, hashes look at the foreign key/collection keys only, ignoring others. Following resolution, all references are upgraded to the defined hash form (merging over any other keys on an existing hash).
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
## Primary Keys
|
|
227
|
+
|
|
228
|
+
**Primary keys must be unique** within a collection. However, if you set things up so that primary keys are unique across *all* collections, then you no longer need to know the collection of a document when linking to it: just the foreign key is needed.
|
|
229
|
+
|
|
230
|
+
If the collection isn't given by the reference, all collections will be searched to find the foreign key. If non-unique keys are found this will raise an error.
|
|
231
|
+
|
|
232
|
+
When the `primary` config is `nil`, (which is the default), the document "path" is used as the primary key. This guarantees uniqueness across the whole site. Document path is `Document#relative_path` with leading `_` and trailing `Document.extname` removed, giving strings like `products/shoes` for `shoes.md` in the `products` collection.
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
## Trees
|
|
236
|
+
|
|
237
|
+
In Tree mode, documents can be the parents or children of other documents. This type of relationship has special handling and helpers.
|
|
238
|
+
|
|
239
|
+
Tree mode is activated on a relationship with `mode` set to:
|
|
240
|
+
* `parent`: `to` items can be parents of `from` items
|
|
241
|
+
* `child`: `to` items can be children of `from` items
|
|
242
|
+
* `parent/child` or `child/parent`: both `to` and `from` items can be parents of each other
|
|
243
|
+
|
|
244
|
+
Note that the `parent`/`child` relationship is symmetric. If A is a parent of B, then B is a child of A. As soon as one is set one way, the other is implied.
|
|
245
|
+
|
|
246
|
+
Tree mode can be configured under `tree`:
|
|
247
|
+
|
|
248
|
+
* `frontmatter`: set the keys that will be used for single/multiple parents/children and ancestors/descendants.
|
|
249
|
+
* `max`: set max allowed parents/children (default `-1` i.e. unlimited).
|
|
250
|
+
* `url`: interpret URLs as ancestry information (default false).
|
|
251
|
+
|
|
252
|
+
Tree relationships must be declared in `relationships.relationships` with `mode: `. `from`, `to` and whether mode is `parent` or `child` define the relationship's directionality. Swapping `from`/`to` and `mode` would define the same thing.
|
|
253
|
+
|
|
254
|
+
Tree relationships are processed as follows:
|
|
255
|
+
|
|
256
|
+
* Only for declared relationships, look for parents/children in valid directions on documents:
|
|
257
|
+
* By inspecting:
|
|
258
|
+
* The `#url`s of the documents, if config `url: true`. See [URL mode](#url-mode) below.
|
|
259
|
+
* References found in the frontmatter keys on the item (by default, `relationships.parent(s)` and `relationships.child(ren)`).
|
|
260
|
+
* In each case, the value can be a reference directly, or an array of references.
|
|
261
|
+
* All parents/children specified by either method, and by any relationship, are accumulated.
|
|
262
|
+
* This happens across all items, in an initial pass, building a complete graph.
|
|
263
|
+
* Documents can choose whether to specify parents, children, both, or neither. The graph will be built up using the information given in either direction. E.g. if A can be a parent of B, then it will look for B-children of A, and A-parents of B.
|
|
264
|
+
* Protections against loops (including the 0-length case of self-reference) are built-in. If an ancestor/descendant chain attempts to make a link that forms a loop, the chain is broken before adding that link and the attempted link is deleted. A warning is issued but processing otherwise continues.
|
|
265
|
+
* Having built the graph, the tree links are filled in on each document:
|
|
266
|
+
* `parents` and `children` are set as arrays of the immediate ancestors/descendants as reference hashes.
|
|
267
|
+
* `ancestors` and `descendants` are set as arrays where each element is a reference hash. In this situation the hash gains the property `distance`, which is 0 for self, 1 for immediate parent/child, 2 for grandparent/grandchild, etc. The arrays are in ascending distance order. If an item can be reached by multiple paths, the shortest path gives the distance.
|
|
268
|
+
|
|
269
|
+
### Max
|
|
270
|
+
|
|
271
|
+
The `max` config lets you specify a maximum number of allowed parents/children in a relationship.
|
|
272
|
+
|
|
273
|
+
If more than the max number of parents/children are encountered, only the first up to max are used, and all other attempts to add parent/child fail and that reference is deleted. A warning is issued but processing otherwise continues.
|
|
274
|
+
|
|
275
|
+
For each of `max.parents`/`max.children`, if the value is `1`, the singular `parent`/`child` keys will be used to read the parents/children (otherwise all keys are used) and to set the `parent`/`child` values (which will be the reference directly, not in an array).
|
|
276
|
+
|
|
277
|
+
### URL mode
|
|
278
|
+
|
|
279
|
+
In URL mode the `url` property of documents are inspected to find parent relationships. On each document, URLs are normalised (no leading/trailing slash, `index.html` removed) and split on `/`, to give a set of URL parts. To find tree relationships in the target collection:
|
|
280
|
+
|
|
281
|
+
* A parent has the same URL parts minus the last one
|
|
282
|
+
* A child has the same URL parts plus one more part.
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
## Resolvers
|
|
286
|
+
|
|
287
|
+
You can insert special logic that modifies how the links from X to Y are resolved, by defining and registering one or more Resolver classes for a relationship.
|
|
288
|
+
|
|
289
|
+
Resolvers run for normal relationships (`link` or `bidirectional`), not Tree relationships.
|
|
290
|
+
|
|
291
|
+
Resolvers must be placed in `Jekyll::Plugins::Relationships::Resolvers` and inherit from `Jekyll::Plugins::Relationships::Resolvers::Base`. They could be defined by files in your `_plugins` folder, for instance.
|
|
292
|
+
|
|
293
|
+
Each Resolver must specify the relationship it applies to with the `from` and `to` directives. These have the same syntax and interpretation as `from` and `to` in a relationship definition. The Resolver will run for any implied pair of collections.
|
|
294
|
+
|
|
295
|
+
```ruby
|
|
296
|
+
class ProductsAndCategories < Jekyll::Plugins::Relationships::Resolvers::Base
|
|
297
|
+
from 'products, categories'
|
|
298
|
+
to 'self'
|
|
299
|
+
|
|
300
|
+
# Called for each item in the collection
|
|
301
|
+
def resolve
|
|
302
|
+
# use link(reference) and unlink(reference) to modify links on this document from `from` to `to`
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
end
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The Resolver class is instantiated each time that type of relationship is being resolved for some document. The `resolve` method is called, and this has a chance to modify that document, and the documents it links to.
|
|
309
|
+
|
|
310
|
+
The instance has these variables already set:
|
|
311
|
+
|
|
312
|
+
* `@key`: primary key of the document currently being resolved
|
|
313
|
+
* `@from`: label of the collection this item belongs to
|
|
314
|
+
* `@to`: label of the target collection, links to which are currently being resolved
|
|
315
|
+
* `@site`: the `Jekyll::Site`
|
|
316
|
+
|
|
317
|
+
This document's links are modified with the `link` and `unlink` methods:
|
|
318
|
+
|
|
319
|
+
* `link(reference)`
|
|
320
|
+
* `reference` gives the document to link to, from this document. It must be in the `@to` collection.
|
|
321
|
+
* `reference:` (optional named parameter): gives a hash that the created reference hash will merge over, providing arbitrary addititional properties to the hash.
|
|
322
|
+
* Adding a duplicate link has no effect.
|
|
323
|
+
* `unlink(reference)`
|
|
324
|
+
* Remove the link to the `reference`d document.
|
|
325
|
+
* `unlink` (no reference) removes all links on this document to the `@to` collection.
|
|
326
|
+
|
|
327
|
+
Because the class extends `Resolvers::Base` it has access to these helpers:
|
|
328
|
+
|
|
329
|
+
* `relationships(reference, to: collection)`
|
|
330
|
+
* Gets an array of reference hashes for the relationships from the `reference`d document to the collection passed as `to`.
|
|
331
|
+
* This causes the requested relationships to be resolved immediately on that foreign document, creating a recursion. Cyclic dependencies are detected and throw an error.
|
|
332
|
+
* If `to` is omitted, the value of `@to` is used.
|
|
333
|
+
* You can get those relationships on *this* document to the target collection: this will return current links. You can modify from there.
|
|
334
|
+
* Tree-traversal methods:
|
|
335
|
+
* `parents(reference)`: shorthand for `ancestors(reference, max: 1)`.
|
|
336
|
+
* `ancestors(reference)`
|
|
337
|
+
* Returns the array of ancestors of the `reference`d document, where each array item is a reference hash with `distance`.
|
|
338
|
+
* `max` (optional, default `-1`): the max `distance` to search/return. `-1` means no limit.
|
|
339
|
+
* `min` (optional, default `1`): the min `distance` to search/return. `0` would include the item itself.
|
|
340
|
+
* `children(reference)`: shorthand for `descendants(reference, max: 1)`.
|
|
341
|
+
* `descendants(reference)`: equivalent to `ancestors` but looking in the opposite direction.
|
|
342
|
+
* `document(reference)`: retrieves the `reference`d document.
|
|
343
|
+
* Only valid for items participating in relationships.
|
|
344
|
+
* If that document itself has defined relationships, it recurses to resolve those first, unless that document is this document.
|
|
345
|
+
* Returns the actual `Jekyll::Document`.
|
|
346
|
+
|
|
347
|
+
The `reference` parameter in all the above:
|
|
348
|
+
|
|
349
|
+
* Can be a reference (i.e. a key string or reference hash). If optional `from` is passed, or the reference hash has collection info, the key lookup is constrained to that collection.
|
|
350
|
+
* Can be a `Jekyll::Document` object. Its primary key and collection are read from the object.
|
|
351
|
+
* Can be omitted in the helpers, in which case the method operates on *this* document.
|
|
352
|
+
|
|
353
|
+
### Example
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
class ProjectServices < Jekyll::Plugins::Relationships::Resolvers::Base
|
|
357
|
+
from 'projects'
|
|
358
|
+
to 'services'
|
|
359
|
+
|
|
360
|
+
# Services on this project are determined by looking at
|
|
361
|
+
# its deliverables and all of their ancestors
|
|
362
|
+
# and the services they link to
|
|
363
|
+
def resolve
|
|
364
|
+
relationships(to: 'deliverables').each do |deliverable|
|
|
365
|
+
ancestors(deliverable, min: 0).each do |ancestor|
|
|
366
|
+
relationships(ancestor, to: 'services').each do |service|
|
|
367
|
+
link(service, reference: { distance: distance(ancestor) + distance(service) })
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Helper to add distance info to added links
|
|
374
|
+
def distance(reference)
|
|
375
|
+
if reference.has_key?('distance')
|
|
376
|
+
reference['distance']
|
|
377
|
+
else
|
|
378
|
+
0
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
end
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Multiple Links
|
|
386
|
+
|
|
387
|
+
By default, A can only link to B once. If the same link is given again, further links are ignored. You can control this behaviour with `relationships.multiple`. This can be:
|
|
388
|
+
|
|
389
|
+
* A string:
|
|
390
|
+
* `drop`: default behaviour, no multiple links
|
|
391
|
+
* `keep`: allow multiple links to the same thing
|
|
392
|
+
* `count`: collapse multiple links, but keep a count of how many times it was linked, on the reference hash
|
|
393
|
+
* A hash where:
|
|
394
|
+
* `mode` is one of the above
|
|
395
|
+
* `sort` is `asc` or `desc`. Sort is only applicable to `mode: count`. Links will be sorted by the number of times that they were linked, in ascending/descending order.
|
|
396
|
+
|
|
397
|
+
In `count` mode, the reference hash additionally has a `count` key. You can change where this key appears with:
|
|
398
|
+
|
|
399
|
+
```yaml
|
|
400
|
+
references:
|
|
401
|
+
my_key: <key>
|
|
402
|
+
my_collection: <collection>
|
|
403
|
+
my_page: <page>
|
|
404
|
+
my_count: <count>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
## Pruning
|
|
409
|
+
|
|
410
|
+
You can remove certain pages from your site ("prune" them) according to the number of finally-resolved relationships on them. This is controlled with a `prune` key which you add to the relationship definition:
|
|
411
|
+
|
|
412
|
+
```yaml
|
|
413
|
+
relationships:
|
|
414
|
+
# Normal relationship definitions
|
|
415
|
+
relationships:
|
|
416
|
+
- from: projects
|
|
417
|
+
to: categories, tags
|
|
418
|
+
prune:
|
|
419
|
+
min: 1 # prune a project if it links to fewer than 1 category/tag total
|
|
420
|
+
- from: products
|
|
421
|
+
to: categories
|
|
422
|
+
prune:
|
|
423
|
+
mode: inverse
|
|
424
|
+
min: 1 # prune a category if fewer than 1 products link to it
|
|
425
|
+
- from: categories
|
|
426
|
+
to: self
|
|
427
|
+
mode: parent
|
|
428
|
+
prune:
|
|
429
|
+
min: 2 # prune a category if it has fewer than 2 parents
|
|
430
|
+
depth: -1 # only prune non-root nodes
|
|
431
|
+
- from: products
|
|
432
|
+
mode: parent
|
|
433
|
+
to:
|
|
434
|
+
- collection: self
|
|
435
|
+
prune:
|
|
436
|
+
mode: inverse
|
|
437
|
+
min: 2 # prune a product if it has fewer than 2 children
|
|
438
|
+
depth: 1 # only prune root notes
|
|
439
|
+
prune:
|
|
440
|
+
combine: true # default
|
|
441
|
+
iterations: 10 # default
|
|
442
|
+
tree:
|
|
443
|
+
orphans: grandparents # default
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`prune` on each relationship/target entry allows:
|
|
447
|
+
|
|
448
|
+
* `min` (required): the minimum number of related documents needed to survive.
|
|
449
|
+
* `mode: inverse` (optional): prune the `to` side instead of the `from` side.
|
|
450
|
+
* `depth` (required if pruning a tree): only for tree relationships, determines which tree nodes can be pruned:
|
|
451
|
+
* `1` selects roots, `2` would be roots and their chlidren, etc.
|
|
452
|
+
* `-1` and negative integers are the negation of their positive counterparts. So `-2` means all but the first two levels of the tree are eligible for pruning.
|
|
453
|
+
|
|
454
|
+
If pruning removes a node from a tree, any children that lose all parents become orphans. `relationships.prune.tree.orphans` controls what happens:
|
|
455
|
+
|
|
456
|
+
* `grandparents` (default): reconnect to the pruned node's original grandparents, if any.
|
|
457
|
+
* `grandparents required`: as above, but also remove the orphan if there is no grandparent to connec to.
|
|
458
|
+
* `prune`: prune all orphans recursively.
|
|
459
|
+
* `orphan`: leave them parentless.
|
|
460
|
+
|
|
461
|
+
`prune` can also be `false` to disable pruning at that level, or an integer as a shortcut for `prune: min: int`.
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
## Keywords
|
|
465
|
+
|
|
466
|
+
In many places, certain keyword strings have special meaning. In case these strings clash with a collection name or frontmatter key, you can change them with the `keywords` config.
|
|
467
|
+
|
|
468
|
+
```yaml
|
|
469
|
+
keywords:
|
|
470
|
+
base: prepend
|
|
471
|
+
self: itself
|
|
472
|
+
#etc for all reserved tokens that can be used in a context where collection names or frontmatter keys can also be given
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
## Examples
|
|
477
|
+
|
|
478
|
+
```yml
|
|
479
|
+
# Portfolio site
|
|
480
|
+
relationships:
|
|
481
|
+
frontmatter:
|
|
482
|
+
base: ''
|
|
483
|
+
primary: meta.id
|
|
484
|
+
foreign: data.<collection>
|
|
485
|
+
|
|
486
|
+
relationships:
|
|
487
|
+
# Clients have an organisation type, industry and location
|
|
488
|
+
- from: clients
|
|
489
|
+
to: org_types, industries, locations
|
|
490
|
+
# Deliverables, industries and locations can nest inside themselves
|
|
491
|
+
- from: deliverables, industries, locations
|
|
492
|
+
to: self
|
|
493
|
+
mode: parent
|
|
494
|
+
- from: deliverables
|
|
495
|
+
to: services
|
|
496
|
+
- from: projects
|
|
497
|
+
to:
|
|
498
|
+
- services
|
|
499
|
+
- collection: deliverables
|
|
500
|
+
frontmatter:
|
|
501
|
+
foreign: data.deliverables, data.body.details.deliverables
|
|
502
|
+
- collection: clients
|
|
503
|
+
frontmatter:
|
|
504
|
+
foreign: data.client
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Notes
|
|
508
|
+
|
|
509
|
+
**This gem is in an alpha release.** Breaking changes may occur between 0.x minor versions, and the gem overall has not been fully tested. If you encounter any issues please report them.
|
metadata
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jekyll-relationships
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0.alpha
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Convincible
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-24 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: jekyll
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 3.8.5
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '5.0'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 3.8.5
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.0'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: jekyll-test-harness
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - '='
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.1.0.alpha
|
|
40
|
+
type: :development
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - '='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.1.0.alpha
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: pry
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.13'
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 0.13.1
|
|
57
|
+
type: :development
|
|
58
|
+
prerelease: false
|
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - "~>"
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '0.13'
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: 0.13.1
|
|
67
|
+
- !ruby/object:Gem::Dependency
|
|
68
|
+
name: pry-byebug
|
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '3.9'
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: 3.9.0
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - "~>"
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '3.9'
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: 3.9.0
|
|
87
|
+
- !ruby/object:Gem::Dependency
|
|
88
|
+
name: rspec
|
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - "~>"
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '3.10'
|
|
94
|
+
type: :development
|
|
95
|
+
prerelease: false
|
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - "~>"
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '3.10'
|
|
101
|
+
- !ruby/object:Gem::Dependency
|
|
102
|
+
name: rake
|
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - "~>"
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '12.3'
|
|
108
|
+
type: :development
|
|
109
|
+
prerelease: false
|
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - "~>"
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '12.3'
|
|
115
|
+
description: Collection documents can specify many-to-many relationships with each
|
|
116
|
+
other in their frontmatter, forming trees, graphs or relational-database-like structures.
|
|
117
|
+
Parses the relationships and writes them back to frontmatter.
|
|
118
|
+
email:
|
|
119
|
+
- development@convincible.media
|
|
120
|
+
executables: []
|
|
121
|
+
extensions: []
|
|
122
|
+
extra_rdoc_files: []
|
|
123
|
+
files:
|
|
124
|
+
- lib/jekyll-relationships.rb
|
|
125
|
+
- lib/jekyll-relationships/configuration.rb
|
|
126
|
+
- lib/jekyll-relationships/configuration/debug_setting.rb
|
|
127
|
+
- lib/jekyll-relationships/configuration/defaults.rb
|
|
128
|
+
- lib/jekyll-relationships/configuration/frontmatter.rb
|
|
129
|
+
- lib/jekyll-relationships/configuration/hash_utilities.rb
|
|
130
|
+
- lib/jekyll-relationships/configuration/multiple_settings.rb
|
|
131
|
+
- lib/jekyll-relationships/configuration/parser.rb
|
|
132
|
+
- lib/jekyll-relationships/configuration/prune_rule_settings.rb
|
|
133
|
+
- lib/jekyll-relationships/configuration/prune_settings.rb
|
|
134
|
+
- lib/jekyll-relationships/configuration/tree_frontmatter.rb
|
|
135
|
+
- lib/jekyll-relationships/configuration/tree_settings.rb
|
|
136
|
+
- lib/jekyll-relationships/debug_logger.rb
|
|
137
|
+
- lib/jekyll-relationships/definitions/configured_relationship.rb
|
|
138
|
+
- lib/jekyll-relationships/definitions/normal_relationship.rb
|
|
139
|
+
- lib/jekyll-relationships/definitions/prune_rule.rb
|
|
140
|
+
- lib/jekyll-relationships/definitions/tree_relationship.rb
|
|
141
|
+
- lib/jekyll-relationships/documents/registry.rb
|
|
142
|
+
- lib/jekyll-relationships/engine.rb
|
|
143
|
+
- lib/jekyll-relationships/engine/raw_path_state.rb
|
|
144
|
+
- lib/jekyll-relationships/engine/relationship_state.rb
|
|
145
|
+
- lib/jekyll-relationships/engine/session.rb
|
|
146
|
+
- lib/jekyll-relationships/engine/write_back.rb
|
|
147
|
+
- lib/jekyll-relationships/errors.rb
|
|
148
|
+
- lib/jekyll-relationships/generators/relationships.rb
|
|
149
|
+
- lib/jekyll-relationships/pruning/normal_graph.rb
|
|
150
|
+
- lib/jekyll-relationships/pruning/rule_pruner.rb
|
|
151
|
+
- lib/jekyll-relationships/pruning/tree_phase.rb
|
|
152
|
+
- lib/jekyll-relationships/pruning/tree_provenance.rb
|
|
153
|
+
- lib/jekyll-relationships/references/accumulator.rb
|
|
154
|
+
- lib/jekyll-relationships/references/template.rb
|
|
155
|
+
- lib/jekyll-relationships/resolvers/base.rb
|
|
156
|
+
- lib/jekyll-relationships/run_logger.rb
|
|
157
|
+
- lib/jekyll-relationships/support/data_path.rb
|
|
158
|
+
- lib/jekyll-relationships/support/frontmatter_path.rb
|
|
159
|
+
- lib/jekyll-relationships/support/hash_deep_merge.rb
|
|
160
|
+
- lib/jekyll-relationships/support/placeholders.rb
|
|
161
|
+
- lib/jekyll-relationships/support/string_array.rb
|
|
162
|
+
- lib/jekyll-relationships/trees/edge_builder.rb
|
|
163
|
+
- lib/jekyll-relationships/trees/graph.rb
|
|
164
|
+
- lib/jekyll-relationships/version.rb
|
|
165
|
+
- readme.md
|
|
166
|
+
homepage: https://github.com/ConvincibleMedia/jekyll-relationships
|
|
167
|
+
licenses:
|
|
168
|
+
- LGPL-3.0-or-later
|
|
169
|
+
metadata: {}
|
|
170
|
+
post_install_message:
|
|
171
|
+
rdoc_options: []
|
|
172
|
+
require_paths:
|
|
173
|
+
- lib
|
|
174
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - ">="
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: 2.4.4
|
|
179
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
181
|
+
- - ">"
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: 1.3.1
|
|
184
|
+
requirements: []
|
|
185
|
+
rubygems_version: 3.2.3
|
|
186
|
+
signing_key:
|
|
187
|
+
specification_version: 4
|
|
188
|
+
summary: Define and traverse relationships between Jekyll documents.
|
|
189
|
+
test_files: []
|