relaton-render 0.4.2 → 3.0.0.pre.alpha.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/rake.yml +6 -1
- data/.github/workflows/release.yml +24 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +2 -2
- data/Gemfile +17 -1
- data/README.adoc +13 -258
- data/docs/citation-style-pack.adoc +370 -0
- data/lib/relaton/render/general.rb +305 -0
- data/lib/relaton/render/iso690/element.rb +153 -0
- data/lib/relaton/render/iso690/elements/access.rb +44 -0
- data/lib/relaton/render/iso690/elements/authorizer.rb +30 -0
- data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
- data/lib/relaton/render/iso690/elements/component_part.rb +99 -0
- data/lib/relaton/render/iso690/elements/creator.rb +258 -0
- data/lib/relaton/render/iso690/elements/edition.rb +92 -0
- data/lib/relaton/render/iso690/elements/extent.rb +71 -0
- data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
- data/lib/relaton/render/iso690/elements/location.rb +73 -0
- data/lib/relaton/render/iso690/elements/medium.rb +32 -0
- data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
- data/lib/relaton/render/iso690/elements/production.rb +92 -0
- data/lib/relaton/render/iso690/elements/pub_date.rb +56 -0
- data/lib/relaton/render/iso690/elements/series.rb +66 -0
- data/lib/relaton/render/iso690/elements/size.rb +41 -0
- data/lib/relaton/render/iso690/elements/status.rb +30 -0
- data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
- data/lib/relaton/render/iso690/elements/title.rb +56 -0
- data/lib/relaton/render/iso690/elements/updated.rb +38 -0
- data/lib/relaton/render/iso690/elements.rb +67 -0
- data/lib/relaton/render/iso690/fields.rb +142 -0
- data/lib/relaton/render/iso690/i18n/en.yml +47 -0
- data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
- data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
- data/lib/relaton/render/iso690/i18n.rb +83 -0
- data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
- data/lib/relaton/render/iso690/index_renderer.rb +57 -0
- data/lib/relaton/render/iso690/kinds.rb +53 -0
- data/lib/relaton/render/iso690/renderer.rb +148 -0
- data/lib/relaton/render/iso690/rules.rb +36 -0
- data/lib/relaton/render/iso690/style/locale.rb +53 -0
- data/lib/relaton/render/iso690/style/name_form.rb +87 -0
- data/lib/relaton/render/iso690/style/scheme.rb +33 -0
- data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
- data/lib/relaton/render/iso690/style/template_map.rb +40 -0
- data/lib/relaton/render/iso690/style/type_template.rb +37 -0
- data/lib/relaton/render/iso690/style.rb +222 -0
- data/lib/relaton/render/iso690/styles/apa.yml +45 -0
- data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
- data/lib/relaton/render/iso690/styles/chicago.yml +48 -0
- data/lib/relaton/render/iso690/styles/ieee-sa.yml +45 -0
- data/lib/relaton/render/iso690/styles/lncs.yml +54 -0
- data/lib/relaton/render/iso690/template.rb +248 -0
- data/lib/relaton/render/iso690.rb +27 -0
- data/lib/relaton/render/version.rb +1 -1
- data/lib/relaton-render.rb +11 -5
- data/relaton-render.gemspec +6 -13
- metadata +61 -116
- data/lib/isodoc/i18n.rb +0 -33
- data/lib/isodoc-yaml/i18n-ar.yaml +0 -44
- data/lib/isodoc-yaml/i18n-de.yaml +0 -43
- data/lib/isodoc-yaml/i18n-en.yaml +0 -40
- data/lib/isodoc-yaml/i18n-es.yaml +0 -47
- data/lib/isodoc-yaml/i18n-fr.yaml +0 -48
- data/lib/isodoc-yaml/i18n-ru.yaml +0 -51
- data/lib/isodoc-yaml/i18n-zh-Hans.yaml +0 -40
- data/lib/relaton/render/fields/date.rb +0 -45
- data/lib/relaton/render/fields/fields.rb +0 -245
- data/lib/relaton/render/general/citations.rb +0 -96
- data/lib/relaton/render/general/config.yml +0 -79
- data/lib/relaton/render/general/render.rb +0 -185
- data/lib/relaton/render/general/render_classes.rb +0 -129
- data/lib/relaton/render/parse/parse.rb +0 -64
- data/lib/relaton/render/parse/parse_contributors.rb +0 -132
- data/lib/relaton/render/parse/parse_extract.rb +0 -180
- data/lib/relaton/render/template/liquid.rb +0 -15
- data/lib/relaton/render/template/template.rb +0 -188
- data/lib/relaton/render/utils/utils.rb +0 -27
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
= Defining a Citation Style: the Pack Format
|
|
2
|
+
|
|
3
|
+
This document is the authoritative authoring specification for a
|
|
4
|
+
CitationStyle pack: the one file that fully describes a citation style.
|
|
5
|
+
It is the contract between three parties — style authors, the rendering
|
|
6
|
+
engine, and the relaton-models citation schema.
|
|
7
|
+
|
|
8
|
+
Status markers: **[impl]** shipped on this engine line; **[spec]** the
|
|
9
|
+
committed design this engine line converges to (tracked in the
|
|
10
|
+
promotion backlog, section 12).
|
|
11
|
+
|
|
12
|
+
== 1. The laws
|
|
13
|
+
|
|
14
|
+
1. **Models are ISO 690.** The relaton-models citation module
|
|
15
|
+
(`Relaton::Bib::Item` and friends) is the single data substrate for
|
|
16
|
+
every style. There are no LNCS models, no Chicago models. A
|
|
17
|
+
bibliographic item is parsed, stored and exchanged once.
|
|
18
|
+
2. **A citation style is one encapsulated data pack.** Everything a
|
|
19
|
+
style knows lives in the pack: the scheme (name forms, citation
|
|
20
|
+
system), the locale (labels, punctuation), the presentation
|
|
21
|
+
templates (citation, reference, per-type patterns), and the
|
|
22
|
+
presentation-of-models (how each 690 model element presents).
|
|
23
|
+
Nothing lives in flavor Ruby.
|
|
24
|
+
3. **The pack's field vocabulary is a superset of CSL-JSON.** Every
|
|
25
|
+
CSL-JSON term has a home; the standards-specific extensions CSL
|
|
26
|
+
lacks (designations/docids, typed stages, host relations, ordinal
|
|
27
|
+
editions, bilingual and CJK forms) extend it. The same is true of
|
|
28
|
+
the logic vocabulary (section 8): it supersets CSL's condition
|
|
29
|
+
grammar, not a general template engine.
|
|
30
|
+
4. **Engines hold zero styles.** The engine is a generic evaluator of
|
|
31
|
+
packs. Canonical packs (the ISO 690 default, LNCS, Chicago,
|
|
32
|
+
IEEE-SA) ship beside the engine while the vocabulary co-evolves and
|
|
33
|
+
extract to a data repository once it stabilises — the CSL precedent
|
|
34
|
+
being a separate styles distribution consumed by many processors.
|
|
35
|
+
5. **No Liquid.** The template language is the chunk grammar
|
|
36
|
+
(section 8): declarative, schema-validated, sandboxed, identical in
|
|
37
|
+
Ruby and relaton-ts. A fenced `procedural:` escape may exist for
|
|
38
|
+
experimental packs; canonical styles never reference it.
|
|
39
|
+
|
|
40
|
+
== 2. The pack file
|
|
41
|
+
|
|
42
|
+
One YAML document. **[impl]** Loaded and validated by the
|
|
43
|
+
CitationStyle model (`Relaton::Render::Iso690::Style.load` accepts a
|
|
44
|
+
path or a hash; unknown keys are rejected). **[spec]** The model
|
|
45
|
+
itself is owned by relaton-models' citation module; this engine loads
|
|
46
|
+
it, and relaton-ts loads its mirror.
|
|
47
|
+
|
|
48
|
+
Skeleton — every key, in order:
|
|
49
|
+
|
|
50
|
+
....
|
|
51
|
+
name: "ISO 690 (metanorma-iso flavour)"
|
|
52
|
+
family: iso690 # [spec] iso690 | lncs | chicago | ieee-sa
|
|
53
|
+
extends: null # [spec] name of a base pack this deltas
|
|
54
|
+
requires: # [spec] capability gates this pack needs
|
|
55
|
+
- extent_group_separator
|
|
56
|
+
|
|
57
|
+
scheme:
|
|
58
|
+
system: name-date # name-date | numeric | designation
|
|
59
|
+
shortFromReference: true
|
|
60
|
+
homeDocidType: [ISO, IEC] # docid types that mark a "home" standard
|
|
61
|
+
defaultKind: "" # kind for types the taxonomy leaves unmapped
|
|
62
|
+
nameForm: { ... } # section 7
|
|
63
|
+
locale: { ... } # section 6
|
|
64
|
+
|
|
65
|
+
templates: # style-wide templates, section 5
|
|
66
|
+
titleOpen: "_"
|
|
67
|
+
titleClose: "_"
|
|
68
|
+
citation: "{{surname}}, {{date}}"
|
|
69
|
+
reference: "{{creator}}. {{title}}. ..."
|
|
70
|
+
name: "{{surname}}, {{givenNames}}"
|
|
71
|
+
series: ""
|
|
72
|
+
creators: ""
|
|
73
|
+
uri: "<fmt-link target='{{uri}}'>{{uri}}</fmt-link>"
|
|
74
|
+
uriShort: ""
|
|
75
|
+
identifierMode: all # all | first
|
|
76
|
+
|
|
77
|
+
types: # [spec] section 4: the resource-type taxonomy
|
|
78
|
+
article-journal: { alias: continuing }
|
|
79
|
+
standard: { alias: report }
|
|
80
|
+
|
|
81
|
+
perType: # section 5: the presentation patterns
|
|
82
|
+
- type: report
|
|
83
|
+
home: true
|
|
84
|
+
template: "{{title}}"
|
|
85
|
+
title: "<span class='stddocTitle'>{{title}}</span>"
|
|
86
|
+
short: "{{title}}, {{extent}}"
|
|
87
|
+
- type: monograph
|
|
88
|
+
noPlace: true
|
|
89
|
+
fallbacks: { date: "n.d" }
|
|
90
|
+
template: "{{creator}}{{role}}. {{title}}. ..."
|
|
91
|
+
|
|
92
|
+
sortKey: # index sorting
|
|
93
|
+
- attribute: creator
|
|
94
|
+
- attribute: date
|
|
95
|
+
|
|
96
|
+
rules: # [spec] section 9: presentation-of-models
|
|
97
|
+
identifier: itu # named engine rule selections
|
|
98
|
+
creator: nist_series_publisher
|
|
99
|
+
|
|
100
|
+
postProcess: # [spec] ordered gsub rule chain
|
|
101
|
+
- pattern: "。+"
|
|
102
|
+
replace: "。"
|
|
103
|
+
....
|
|
104
|
+
|
|
105
|
+
== 3. Lifecycle
|
|
106
|
+
|
|
107
|
+
* **Adopt** — a flavor points its `bibrenderer` at a named canonical
|
|
108
|
+
pack. Zero code, zero YAML. The target state for new and generic
|
|
109
|
+
flavors.
|
|
110
|
+
* **Inherit-and-override** — **[spec]** `extends: <pack>` plus a delta.
|
|
111
|
+
The merge semantics are part of this specification (section 11):
|
|
112
|
+
`perType`/`types` merge per type key (a delta re-declaring `article`
|
|
113
|
+
never drops the base's `monograph`); `labels`, `punct`, `fallbacks`
|
|
114
|
+
and rule selections merge per key; scheme scalars replace. The
|
|
115
|
+
merge is deterministic and identical in Ruby and TS.
|
|
116
|
+
* **Own pack** — a fully self-contained instance. Last resort; the
|
|
117
|
+
existing flavor YAMLs are this, and most should collapse into deltas
|
|
118
|
+
over a canonical base.
|
|
119
|
+
|
|
120
|
+
**[spec] `requires:`** names engine capabilities, so a resolver fails
|
|
121
|
+
loudly (`pack requires extent_group_separator`) instead of silently
|
|
122
|
+
mis-rendering. Capability names are frozen per engine minor line.
|
|
123
|
+
|
|
124
|
+
== 4. Info-resource-types
|
|
125
|
+
|
|
126
|
+
Different information resource types present with different patterns
|
|
127
|
+
in nearly every citation style. The pack's type taxonomy is the axis
|
|
128
|
+
those patterns hang on.
|
|
129
|
+
|
|
130
|
+
**[impl]** The engine normalises a bibliographic item's wire `type`
|
|
131
|
+
value to an ISO 690 clause-8 resource *kind* through the Kinds table;
|
|
132
|
+
perType entries select on kinds. **[spec]** The taxonomy becomes pack
|
|
133
|
+
data (`types:`), aligned name-for-name with the CSL resource types —
|
|
134
|
+
the same superset law as the fields — with the standards extensions
|
|
135
|
+
CSL lacks.
|
|
136
|
+
|
|
137
|
+
.Kind taxonomy (ISO 690 clause 8) and its wire-type synonyms [impl]
|
|
138
|
+
[cols="1,3"]
|
|
139
|
+
|===
|
|
140
|
+
|Kind |Wire types
|
|
141
|
+
|
|
142
|
+
|`monograph` |book, booklet, manual, proceedings, thesis
|
|
143
|
+
|`report` |standard, techreport, report *(the clause 8.11 fallback)*
|
|
144
|
+
|`patent` |patent
|
|
145
|
+
|`continuing` |journal, serial, article-journal, article-magazine
|
|
146
|
+
|`component_part` |inbook, incollection, inproceedings
|
|
147
|
+
|`serial_part` |article
|
|
148
|
+
|`online` |software, unpublished, dataset, webpage, online
|
|
149
|
+
|`webdoc` |website, webresource
|
|
150
|
+
|===
|
|
151
|
+
|
|
152
|
+
**[spec]** The full recommended taxonomy carries every CSL type
|
|
153
|
+
(`article-journal`, `article-magazine`, `article-newspaper`, `bill`,
|
|
154
|
+
`book`, `broadcast`, `chapter`, `classic`, `collection`, `dataset`,
|
|
155
|
+
`document`, `entry`, `entry-dictionary`, `entry-encyclopedia`,
|
|
156
|
+
`event`, `figure`, `graphic`, `hearing`, `interview`, `legal_case`,
|
|
157
|
+
`legislation`, `manuscript`, `map`, `motion_picture`, `musical_score`,
|
|
158
|
+
`pamphlet`, `paper-conference`, `patent`, `performance`, `periodical`,
|
|
159
|
+
`personal_communication`, `post`, `post-weblog`, `regulation`,
|
|
160
|
+
`report`, `review`, `review-book`, `software`, `song`, `speech`,
|
|
161
|
+
`standard`, `statute`, `thesis`, `treaty`, `webpage`) plus the
|
|
162
|
+
relaton extensions (`techreport`, `inbook`, `incollection`,
|
|
163
|
+
`inproceedings`, `webresource`, `webdoc`, `online`). Rules of the
|
|
164
|
+
taxonomy:
|
|
165
|
+
|
|
166
|
+
* **Complete, explicit coverage.** Every type resolves to a declared
|
|
167
|
+
pattern — through an entry, an `alias`, or the declared `fallthrough`
|
|
168
|
+
(`report`). Nothing inherits silently.
|
|
169
|
+
* **Aliasing is declared**, never engine-hardcoded: `booklet: book`.
|
|
170
|
+
* **Home/external splits sit on top** — for standards-producing
|
|
171
|
+
bodies, the *same* type carries two patterns, selected by whether
|
|
172
|
+
the item bears one of `scheme.homeDocidType`'s designations.
|
|
173
|
+
IEEE-SA's `standard` pattern is *only* designation + title; a
|
|
174
|
+
Chicago `standard` is cited differently again — same type, different
|
|
175
|
+
packs, different patterns.
|
|
176
|
+
|
|
177
|
+
== 5. Presentation patterns (`perType`)
|
|
178
|
+
|
|
179
|
+
The pattern — not the template — is the unit. A journal-article
|
|
180
|
+
pattern is not a reordered book template; it is its own arrangement
|
|
181
|
+
(quoted title form, italic container, vol/no/pp placement,
|
|
182
|
+
month-abbreviated dates) *plus* its own short cite, fallbacks and
|
|
183
|
+
terminator behaviour. The perType entry carries all of it:
|
|
184
|
+
|
|
185
|
+
[cols="1,3"]
|
|
186
|
+
|===
|
|
187
|
+
|Field |Meaning
|
|
188
|
+
|
|
189
|
+
|`type` |the kind (or **[spec]** taxonomy type) this pattern serves
|
|
190
|
+
|`template` |the reference (bibliography entry) arrangement
|
|
191
|
+
|`title`, `series` |per-slot form overrides for this pattern
|
|
192
|
+
|`short` |the short-cite template (falls back to the reference in short mode)
|
|
193
|
+
|`home` |`true` marks the pattern serving items bearing a home docid; two entries of one type split on it
|
|
194
|
+
|`fallbacks` |absent-slot replacement texts, per slot (`date: "n.d"`, `dategroup: "(n.d.)"`)
|
|
195
|
+
|`noPlace` |render the locale's `no_place` label when production lacks a place
|
|
196
|
+
|===
|
|
197
|
+
|
|
198
|
+
Multiple entries of one `type` resolve: home-flagged variants split by
|
|
199
|
+
the home test; otherwise the first match wins; unmatched kinds fall to
|
|
200
|
+
`templates.reference`.
|
|
201
|
+
|
|
202
|
+
== 6. Locale, labels and punctuation
|
|
203
|
+
|
|
204
|
+
**[impl]** Packs overlay the engine language packs (`i18n/<lang>.yml`)
|
|
205
|
+
per language; the pack wins. Adding a language is a new language pack,
|
|
206
|
+
never code.
|
|
207
|
+
|
|
208
|
+
* `locale.and|others|noDate|noAuthor|in|at|availableAt` — connective
|
|
209
|
+
strings.
|
|
210
|
+
* `locale.labels` — arbitrary label overrides merged verbatim:
|
|
211
|
+
`and`, `oxford_comma`, `list_separator`, `series_no`, `no_place`,
|
|
212
|
+
`available_from`, `ed`/`eds`, `edition_ordinal`/`edition_cardinal`/
|
|
213
|
+
`version_cardinal` (locale templates, `{{ var1 }}` with the
|
|
214
|
+
`ordinal_word` filter), extent unit templates (`volume: "vol. %"`,
|
|
215
|
+
`page: "%頁"` — the `%` carries the value, so a CJK locale places
|
|
216
|
+
its unit after the number), `extent_group_separator`, month names.
|
|
217
|
+
* `locale.punct` — typographic punctuation: `production_sep`,
|
|
218
|
+
`production_order` (`publisher_first` for LNCS-style orders),
|
|
219
|
+
`open-paren`/`close-paren` (ASCII vs fullwidth), `cjk-latin-separator`,
|
|
220
|
+
`size-join`, `marker-space`.
|
|
221
|
+
* **[spec] `postProcess:`** — the CJK spacing rules that flavors
|
|
222
|
+
carry as Ruby today become ordered `pattern/replace` rule chains in
|
|
223
|
+
the pack, so bilingual typography travels with the style.
|
|
224
|
+
|
|
225
|
+
== 7. Name forms (`scheme.nameForm`) [impl]
|
|
226
|
+
|
|
227
|
+
Every knob of creator presentation:
|
|
228
|
+
|
|
229
|
+
[cols="1,3"]
|
|
230
|
+
|===
|
|
231
|
+
|Knob |Effect
|
|
232
|
+
|
|
233
|
+
|`initials`, `initialsSeparator`, `initialsPeriod` |initials vs given names, "A. B." vs "AB"
|
|
234
|
+
|`invertedAll`, `givenNameFirst` |surname-first for all names / direct order
|
|
235
|
+
|`surnameUpcase`, `subsequentSurnameUpcase`, `subsequentInitials` |caps and second-name forms
|
|
236
|
+
|`completenameUpcase`, `orgUpcase` |verbatim names and organisations
|
|
237
|
+
|`editorsMarked`, `loneEditorMarked`, `rolePlacement` |"(eds.)" markers and their placement
|
|
238
|
+
|`listStyle`, `creatorFallback` |join style; fallback source when no personal creator (OGC: `publisher_abbrev`)
|
|
239
|
+
|`etalCount`, `etalDisplay` |bibliography et-al truncation
|
|
240
|
+
|`inTextEtalCount`, `inTextEtalDisplay` |in-text et-al truncation
|
|
241
|
+
|===
|
|
242
|
+
|
|
243
|
+
== 8. The template language
|
|
244
|
+
|
|
245
|
+
**[impl]** Templates are chunk strings: `{{slot}}` fields and literal
|
|
246
|
+
text. The evaluator carries the segment-join semantics — a literal
|
|
247
|
+
renders when it separates two present fields, so absent elements
|
|
248
|
+
swallow their separators; leading and trailing literals drop with
|
|
249
|
+
their fields; parenthetical literals (`({{series}})`, `({{series}})`)
|
|
250
|
+
pair as groups that drop whole when the field is absent, balanced on
|
|
251
|
+
the template's literal parens alone; stacked separators collapse;
|
|
252
|
+
trailing terminators apply per style. The short-cite evaluator splits
|
|
253
|
+
after the leading field run at the first-biblio delimiter, which the
|
|
254
|
+
presentation layer turns into the cross-reference capsule.
|
|
255
|
+
|
|
256
|
+
Slots resolve to the 690 model presentations: `creator`, `title`,
|
|
257
|
+
`edition`, `medium`, `series`, `production`, `date`, `numeration`,
|
|
258
|
+
`component_part`, `identifier`, `location`, `size`, `extent`,
|
|
259
|
+
`access`, `stddoc`, `status`, `citeid`, `authorizer`, `updated`;
|
|
260
|
+
derived fields `role`, `dategroup` (the parenthesised date group),
|
|
261
|
+
`disambiguateddate` (the batch-suffixed date, "2022a"); and the name
|
|
262
|
+
fields (`surname`, `givenNames`, ...).
|
|
263
|
+
|
|
264
|
+
**[spec]** The logic vocabulary supersets CSL's condition grammar:
|
|
265
|
+
|
|
266
|
+
* **Conditionals** — `choose`/`if`/`else-if` over `type`, field
|
|
267
|
+
presence, `home`, and `disambiguated` ( CSL `position`-adjacent ),
|
|
268
|
+
as declarative blocks inside `perType` entries and templates.
|
|
269
|
+
* **Fallback chains** — `creator: [authors, publisher_abbrev]`:
|
|
270
|
+
first present source wins, replacing today's single `creatorFallback`
|
|
271
|
+
knob and OGC's hack.
|
|
272
|
+
* No loops, no arbitrary filters, no string mutation — those are
|
|
273
|
+
`postProcess` rules or named rules, never template logic.
|
|
274
|
+
|
|
275
|
+
== 9. Model presentation templates (`rules`) — presentation-of-models
|
|
276
|
+
|
|
277
|
+
Each 690 model element has a declarative presentation vocabulary; the
|
|
278
|
+
pack selects per-slot rule implementations by name. **[impl]** the
|
|
279
|
+
declarative surface (extent unit `%`-templates, production orders,
|
|
280
|
+
group separators, locale templates, fallbacks); **[spec]** the
|
|
281
|
+
registry: engine implements each named rule once, conformance-tested
|
|
282
|
+
per rule, mirrored in relaton-ts, and addressable as pack data —
|
|
283
|
+
`identifier: itu`, `creator: nist_series_publisher`.
|
|
284
|
+
|
|
285
|
+
The promotion backlog — every flavor element override still standing
|
|
286
|
+
is a rule to promote:
|
|
287
|
+
|
|
288
|
+
[cols="2,2"]
|
|
289
|
+
|===
|
|
290
|
+
|Flavor override |Becomes rule
|
|
291
|
+
|
|
292
|
+
|`ItuIdentifier` |`identifier: itu`
|
|
293
|
+
|`NistIdentifier`, `NistCreator`, `NistDate`, `SerialDate`, `NistSeries`, `NistComponentPart`, `NistPublisher`, `Draft` |the NIST author-year rule set
|
|
294
|
+
|`IeeeIdentifier`, `IeeeComponentPart`, `IeeeAccess`, `IeeeMedium` |the IEEE-SA rule set (designation-first standards)
|
|
295
|
+
|`BipmCreator`, `BipmComponentPart`, `BipmProduction`, `BipmExtent`, `BipmMedium`, `BipmEdition`, `BipmVolume` |the BIPM rule set
|
|
296
|
+
|`IhoEdition`, `IhoCreator` |affiliation-composite creators
|
|
297
|
+
|`JisComponentPart`, `JisSeries`, `JisExtent` |the JIS host-citation rules
|
|
298
|
+
|`BsiStatus` |status presentation
|
|
299
|
+
|===
|
|
300
|
+
|
|
301
|
+
== 10. Citation forms
|
|
302
|
+
|
|
303
|
+
**[impl]** The renderings capsule every pack produces (the isodoc
|
|
304
|
+
contract): `author`, `date`, `citation{full, default, short,
|
|
305
|
+
author_date, author_date_br, author, date, reference_tag, title,
|
|
306
|
+
title_reference_tag}`, `formattedref`. `templates.citation` is the
|
|
307
|
+
in-text form; `shortFromReference` takes the reference form with the
|
|
308
|
+
first-biblio marker; batch disambiguation suffixes derive from
|
|
309
|
+
author+date collisions; `identifierMode` governs `default` (first vs
|
|
310
|
+
all designations). **[spec]** `system: numeric | designation` selects
|
|
311
|
+
numeric `[1]`/`[B4]` and designation-led citation systems — the
|
|
312
|
+
IEEE-SA and JIS/BSI homes — as first-class engines, not author-date
|
|
313
|
+
approximations.
|
|
314
|
+
|
|
315
|
+
== 11. Deltas and the flavor triangle
|
|
316
|
+
|
|
317
|
+
**[spec]** `extends` resolves base-then-delta at load, on model
|
|
318
|
+
instances (never ad-hoc hash merges):
|
|
319
|
+
|
|
320
|
+
* `perType`, `types`: merge **per type key** — a delta's `article`
|
|
321
|
+
entry overlays the base's; the base's other entries survive.
|
|
322
|
+
* `labels`, `punct`, `fallbacks`: per-key merge.
|
|
323
|
+
* `nameForm` knobs, `templates.*`, rule selections: replace — with
|
|
324
|
+
the caveat that a knob left at its model default counts as unset and
|
|
325
|
+
inherits (a Lutaml model cannot distinguish an explicit default from
|
|
326
|
+
an absent key).
|
|
327
|
+
* `sortKey`, `requires`: union.
|
|
328
|
+
|
|
329
|
+
The forcing test: PLATEAU's four near-copy styles (document-language
|
|
330
|
+
x item-language quadrants) collapse to `extends: jis` plus two
|
|
331
|
+
punct/label deltas. If the merge cannot express that, the merge is
|
|
332
|
+
wrong.
|
|
333
|
+
|
|
334
|
+
== 12. Validation, versioning, conformance
|
|
335
|
+
|
|
336
|
+
* **Schema** — the CitationStyle model validates every pack; unknown
|
|
337
|
+
keys fail at load, not at render. [impl]
|
|
338
|
+
* **`requires:`** — capability gating, frozen names per engine minor
|
|
339
|
+
line. [spec]
|
|
340
|
+
* **Conformance corpora** — every canonical pack ships a corpus of
|
|
341
|
+
examples with expected renderings, in the iso690-test tradition:
|
|
342
|
+
LNCS from Springer's authors' instructions; Chicago from CMOS;
|
|
343
|
+
IEEE-SA from the Standards Style Manual clause 17 (`[B1] ASME
|
|
344
|
+
BPVC-I-2004, Title.` designation-only standards; Chicago-derived
|
|
345
|
+
books/articles with only the first name inverted). CI renders the
|
|
346
|
+
matrix: every declared type, every pattern, both engine ports.
|
|
347
|
+
* **Portability & safety** — packs are data: they validate, sandbox
|
|
348
|
+
and render identically server-side (the CF Workers API) and in
|
|
349
|
+
relaton-ts. No interpreters, no Liquid, no flavor Ruby.
|
|
350
|
+
|
|
351
|
+
== 13. Authoring walkthrough (LNCS, abridged)
|
|
352
|
+
|
|
353
|
+
. Declare the pack: `family: lncs`, `extends: null` (canonical),
|
|
354
|
+
`requires: [production_order, extent_units]`.
|
|
355
|
+
. Name forms: initials without periods, `invertedAll`, direct-order
|
|
356
|
+
subsequent names, `creatorFallback: [publisher_abbrev]` for
|
|
357
|
+
standards with no personal authors.
|
|
358
|
+
. Locale: `production_order: publisher_first`, `production_sep: ", "`,
|
|
359
|
+
year trailing in parentheses (the `dategroup` form).
|
|
360
|
+
. Types and patterns: `paper-conference` ("`{{creator}}: {{title}}.
|
|
361
|
+
In: {{componentpart}} {{extent}} {{production}} {{dategroup}}.`"),
|
|
362
|
+
`chapter`, `article-journal`, `book`, `thesis`, `webpage` — one
|
|
363
|
+
entry each, own `title`/`series` forms, own `short` cites.
|
|
364
|
+
. Rules: `identifier: lncs-series` (LNCS vol./pp. forms).
|
|
365
|
+
. Conformance: the Springer instruction examples become the corpus;
|
|
366
|
+
the pack merges only when the matrix is green.
|
|
367
|
+
|
|
368
|
+
The same walk with CMOS examples yields Chicago; with the IEEE-SA
|
|
369
|
+
manual, `family: ieee-sa` — whose `standard` pattern is two fields
|
|
370
|
+
long.
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
# Compatibility entry point (relaton-render#90).
|
|
6
|
+
#
|
|
7
|
+
# The v2 engine replaced the liquid/config/i18n machinery with the
|
|
8
|
+
# template evaluator over CitationStyle instances
|
|
9
|
+
# (Relaton::Render::Iso690). isodoc main's render-isodoc subclasses
|
|
10
|
+
# this constant, so it must exist for all-git-main stacks to boot.
|
|
11
|
+
# This facade delegates rendering to the v2 engine; the retired
|
|
12
|
+
# options (config:, template overrides in liquid syntax) are ignored
|
|
13
|
+
# with one warning — migrate to a CitationStyle instance, which
|
|
14
|
+
# expresses everything the old config.yml did (see the citation
|
|
15
|
+
# module in relaton-models).
|
|
16
|
+
class General
|
|
17
|
+
# The eref `style=` vocabulary metanorma-standoc validates against
|
|
18
|
+
# (cleanup/xref.rb reads citetemplate.template_raw's keys) — the
|
|
19
|
+
# 1.x default config's top-level citation styles.
|
|
20
|
+
CITE_STYLES = %i[
|
|
21
|
+
author_date author_date_br author date reference_tag title
|
|
22
|
+
title_reference_tag short
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
25
|
+
CiteTemplate = Struct.new(:template_raw)
|
|
26
|
+
|
|
27
|
+
def initialize(options = {})
|
|
28
|
+
options = deep_symbolize(options)
|
|
29
|
+
@lang = options[:language] || options[:lang] || "en"
|
|
30
|
+
renderer_opts = { lang: @lang, script: options[:script] || "Latn",
|
|
31
|
+
labels: options[:i18nhash] || {} }
|
|
32
|
+
# A flavor names its own CitationStyle instance (name or YAML path)
|
|
33
|
+
options[:style] and renderer_opts[:style] = options[:style]
|
|
34
|
+
@renderer = Iso690::Renderer.new(**renderer_opts)
|
|
35
|
+
@renderer_opts = renderer_opts
|
|
36
|
+
@renderers_by_lang = {}
|
|
37
|
+
warn_general_config if options[:config]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# A collection's items may each declare their own language; the
|
|
41
|
+
# renderer localizes by language, so a reference renders in its
|
|
42
|
+
# own language within a document of another (the 1.x plateau
|
|
43
|
+
# per-reference behaviour)
|
|
44
|
+
def renderer_for(lang)
|
|
45
|
+
return @renderer if lang.nil? || lang == @lang
|
|
46
|
+
|
|
47
|
+
@renderers_by_lang[lang] ||= Iso690::Renderer.new(
|
|
48
|
+
**@renderer_opts.merge(lang: lang,
|
|
49
|
+
script: %w(ja ko zh).include?(lang) ? nil : "Latn")
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render(model, embedded: false, **opts)
|
|
54
|
+
model = to_model(model)
|
|
55
|
+
text =
|
|
56
|
+
if model.respond_to?(:formattedref) && model.formattedref
|
|
57
|
+
model.formattedref.content
|
|
58
|
+
else
|
|
59
|
+
@renderer.render(model, **opts)
|
|
60
|
+
end
|
|
61
|
+
embedded ? text : "<formattedref>#{text}</formattedref>"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def citation(model, **opts)
|
|
65
|
+
@renderer.citation(to_model(model), **opts)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# isodoc's references rendering: renderings[id][:formattedref]
|
|
69
|
+
# feeds the <formattedref> of every bibitem (isodoc
|
|
70
|
+
# presentation_function/refs.rb).
|
|
71
|
+
def render_all(bib, type: "author-date")
|
|
72
|
+
items = facade_bibitems(bib) or return nil
|
|
73
|
+
disambiguators = date_disambiguators(items)
|
|
74
|
+
items.each_with_object({}).with_index do |(item, m), i|
|
|
75
|
+
renderer = renderer_for(Array(item.language).first)
|
|
76
|
+
ref = begin
|
|
77
|
+
renderer.render(item, disambiguator: disambiguators[item.id])
|
|
78
|
+
rescue ::Relaton::Render::Unrenderable
|
|
79
|
+
next
|
|
80
|
+
end
|
|
81
|
+
m[item.id] = {
|
|
82
|
+
author: renderer.in_text_author(item),
|
|
83
|
+
date: renderer.disambiguated_date(item,
|
|
84
|
+
disambiguator:
|
|
85
|
+
disambiguators[item.id]),
|
|
86
|
+
citation: citation_renderings(item, ref, renderer,
|
|
87
|
+
type: type,
|
|
88
|
+
disambiguator:
|
|
89
|
+
disambiguators[item.id]),
|
|
90
|
+
formattedref: terminate_reference(ref, item),
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# isodoc's styled citations consume these keys: the default is the
|
|
96
|
+
# authoritative identifier; the short cite is the style's citation,
|
|
97
|
+
# falling back to the reference rendering (the 1.x short-cite)
|
|
98
|
+
FIRST_DELIM = '<span class="fmt-first-biblio-delim"/>'
|
|
99
|
+
|
|
100
|
+
# A creator list alone (flavors' document-history name forms)
|
|
101
|
+
def creator_names(item)
|
|
102
|
+
@renderer.render_creators(to_model(item))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# render_all feeds the bibliography list: a reference not ending
|
|
106
|
+
# in the biblio terminator takes one (the 1.x render1 behaviour;
|
|
107
|
+
# single-item render stays verbatim)
|
|
108
|
+
# The batch disambiguation suffixes ("2022a"): items sharing a
|
|
109
|
+
# principal-creator surname and a date take alphabetical
|
|
110
|
+
# suffixes in bibliography order (the 1.x
|
|
111
|
+
# disambig_author_date_citations)
|
|
112
|
+
def date_disambiguators(items)
|
|
113
|
+
keys = items.filter_map do |item|
|
|
114
|
+
begin
|
|
115
|
+
[item.id, @renderer.author_key(item),
|
|
116
|
+
@renderer.citation(item)]
|
|
117
|
+
rescue ::Relaton::Render::Unrenderable
|
|
118
|
+
nil
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
groups = keys.group_by { |_, author, cite| [author, cite] }
|
|
122
|
+
suffixes = {}
|
|
123
|
+
groups.each_value do |group|
|
|
124
|
+
next if group.size < 2
|
|
125
|
+
|
|
126
|
+
group.each_with_index do |(id, _author, _cite), i|
|
|
127
|
+
suffixes[id] = ("a".ord + i).chr
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
suffixes
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The bibliography terminator; flavors override to suppress it
|
|
134
|
+
# (1.x use_terminator?), receiving the item it terminates
|
|
135
|
+
def terminate_reference(ref, _item = nil)
|
|
136
|
+
return ref if ref.empty? || ref.rstrip.end_with?(".")
|
|
137
|
+
|
|
138
|
+
"#{ref}."
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# The style-keyed capsule ("citation" => { author_date: "..." })
|
|
142
|
+
# is what isodoc's styled erefs consume (their style attribute
|
|
143
|
+
# names the key)
|
|
144
|
+
def citation_renderings(item, ref, renderer, type: "author-date",
|
|
145
|
+
disambiguator: nil)
|
|
146
|
+
# A style declaring shortFromReference takes the reference form
|
|
147
|
+
# with the first-biblio marker (the 1.x citetemplate short)
|
|
148
|
+
short = if renderer.short_from_reference?
|
|
149
|
+
renderer.render_short(item, FIRST_DELIM)
|
|
150
|
+
else
|
|
151
|
+
short = renderer.citation(item)
|
|
152
|
+
short.empty? ? renderer.render_short(item, FIRST_DELIM) :
|
|
153
|
+
short
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
title = renderer.title_citation(item)
|
|
157
|
+
|
|
158
|
+
{
|
|
159
|
+
full: ref,
|
|
160
|
+
default: item.docidentifier.first&.content.to_s,
|
|
161
|
+
short: short,
|
|
162
|
+
author_date: renderer.citation(item,
|
|
163
|
+
disambiguator: disambiguator),
|
|
164
|
+
author_date_br: renderer.bracketed_date_citation(
|
|
165
|
+
item, disambiguator: disambiguator,
|
|
166
|
+
),
|
|
167
|
+
author: renderer.in_text_author(item),
|
|
168
|
+
date: renderer.disambiguated_date(item),
|
|
169
|
+
# reference_tag: no rendering — isodoc falls back to the
|
|
170
|
+
# biblio-tag anchor (the 1.x nil)
|
|
171
|
+
title: title,
|
|
172
|
+
title_reference_tag: title,
|
|
173
|
+
}
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# isodoc's pref_ref_code_parse reads
|
|
177
|
+
# data[:authoritative_identifier] to build biblio-tag identifiers
|
|
178
|
+
# (presentation_function/docid.rb). Returns [data, self] as the
|
|
179
|
+
# 1.x signature did.
|
|
180
|
+
def parse(doc)
|
|
181
|
+
[{ authoritative_identifier: facade_docids(doc) }, self]
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def citetemplate
|
|
185
|
+
CiteTemplate.new(CITE_STYLES.to_h { |s| [s, ""] })
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
# 1.x accepted a <references> XML string (isodoc strips namespaces
|
|
191
|
+
# before building it) or a ready array of models.
|
|
192
|
+
def facade_bibitems(bib)
|
|
193
|
+
case bib
|
|
194
|
+
when Array then bib
|
|
195
|
+
when String
|
|
196
|
+
require "moxml"
|
|
197
|
+
root = Moxml.parse(bib).root
|
|
198
|
+
return nil unless root&.name == "references"
|
|
199
|
+
|
|
200
|
+
klass = facade_bibitem_class
|
|
201
|
+
root.xpath("./bibitem").filter_map { |b| klass.from_xml(b.to_xml) }
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def facade_bibitem_class
|
|
206
|
+
Relaton::Bib::Bibitem
|
|
207
|
+
rescue NameError
|
|
208
|
+
Relaton::Bib::Item
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# 1.x Parse#authoritative_identifier semantics: the
|
|
212
|
+
# primary/language cascade, the excluded types (URN, DOI, ISBN...)
|
|
213
|
+
# dropped, and scoped ids (biblio-tag duplicates, IEEE trademark)
|
|
214
|
+
# demoted within their type group. isodoc hands the live bibitem
|
|
215
|
+
# node with the document's default namespace still attached, so the
|
|
216
|
+
# element queries must be namespace-agnostic.
|
|
217
|
+
EXCLUDED_ID_TYPES = %w(METANORMA METANORMA-ORDINAL AUTHOR-DATE TITLE
|
|
218
|
+
URN ISO-REFERENCE ISSN ISBN DOI).freeze
|
|
219
|
+
|
|
220
|
+
# Flavors extend the authoritative-identifier exclusions by
|
|
221
|
+
# overriding this reader on their facade subclass
|
|
222
|
+
def excluded_id_types
|
|
223
|
+
EXCLUDED_ID_TYPES
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def facade_docids(doc)
|
|
227
|
+
node = doc
|
|
228
|
+
unless node.respond_to?(:xpath)
|
|
229
|
+
require "moxml"
|
|
230
|
+
node = Moxml.parse(doc.to_s).root
|
|
231
|
+
end
|
|
232
|
+
return [] unless node
|
|
233
|
+
|
|
234
|
+
ids = node.xpath("./*[local-name() = 'docidentifier']")
|
|
235
|
+
ids = facade_scope_filter(ids)
|
|
236
|
+
out = nil
|
|
237
|
+
[
|
|
238
|
+
->(x) { x["language"] == @lang && x["primary"] },
|
|
239
|
+
->(x) { x["primary"] },
|
|
240
|
+
->(x) { x["language"] == @lang },
|
|
241
|
+
->(_x) { true },
|
|
242
|
+
].each do |p|
|
|
243
|
+
out = ids.select do |x|
|
|
244
|
+
p.call(x) && !excluded_id_types.include?(facade_id_type(x))
|
|
245
|
+
end
|
|
246
|
+
out.empty? or break
|
|
247
|
+
end
|
|
248
|
+
# esc-wrapped: isodoc's l10n skips the tags, leaving a standard
|
|
249
|
+
# identifier's punctuation verbatim (the 1.x Parse behaviour)
|
|
250
|
+
out.map { |x| "<esc>#{x.text.strip}</esc>" }
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def facade_id_type(id)
|
|
254
|
+
t = id["type"] or return nil
|
|
255
|
+
m = /\A(ISBN|ISSN)\..*/i.match(t) or return t.upcase
|
|
256
|
+
m[1].upcase
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def facade_scope_filter(ids)
|
|
260
|
+
ids.detect { |i| i["scope"] } or return ids
|
|
261
|
+
ids.group_by { |i| i["type"] }.flat_map do |type, group|
|
|
262
|
+
grouped = group.group_by { |i| i["scope"] }
|
|
263
|
+
if type == "IEEE" then grouped["trademark"] || grouped[nil] || []
|
|
264
|
+
else grouped[nil] || []
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# 1.x accepted string- and symbol-keyed options alike (the old
|
|
270
|
+
# engine deep-symbolized via metanorma-utils); isodoc's bibrenderer
|
|
271
|
+
# and every flavor still pass string keys.
|
|
272
|
+
def deep_symbolize(value)
|
|
273
|
+
case value
|
|
274
|
+
# isodoc i18n hashes carry nil keys (an unlabeled punct slot)
|
|
275
|
+
when Hash then value.to_h { |k, v| [k&.to_sym, deep_symbolize(v)] }
|
|
276
|
+
when Array then value.map { |v| deep_symbolize(v) }
|
|
277
|
+
else value
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# 1.x render/citation accepted a bibitem XML string; the Iso690
|
|
282
|
+
# engine consumes model instances.
|
|
283
|
+
def to_model(model)
|
|
284
|
+
return model unless model.is_a?(String)
|
|
285
|
+
|
|
286
|
+
klass = facade_bibitem_class
|
|
287
|
+
klass.from_xml(model)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def warn_general_config
|
|
291
|
+
return if General.config_warned
|
|
292
|
+
|
|
293
|
+
General.config_warned = true
|
|
294
|
+
warn "relaton-render: Relaton::Render::General is a compatibility " \
|
|
295
|
+
"facade over the v2 engine; the liquid config option is " \
|
|
296
|
+
"retired and ignored. Author a CitationStyle instance " \
|
|
297
|
+
"(relaton-models citation module) for custom rendering."
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
class << self
|
|
301
|
+
attr_accessor :config_warned
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|