relaton-render 1.3.0 → 1.4.0.pre.alpha.1
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/Gemfile +15 -2
- data/lib/relaton/render/iso690/element.rb +43 -0
- data/lib/relaton/render/iso690/elements/component_part.rb +50 -0
- data/lib/relaton/render/iso690/elements/creator.rb +129 -0
- data/lib/relaton/render/iso690/elements/edition.rb +47 -0
- data/lib/relaton/render/iso690/elements/identifier.rb +32 -0
- data/lib/relaton/render/iso690/elements/location.rb +27 -0
- data/lib/relaton/render/iso690/elements/medium.rb +27 -0
- data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
- data/lib/relaton/render/iso690/elements/production.rb +36 -0
- data/lib/relaton/render/iso690/elements/pub_date.rb +51 -0
- data/lib/relaton/render/iso690/elements/series.rb +40 -0
- data/lib/relaton/render/iso690/elements/title.rb +30 -0
- data/lib/relaton/render/iso690/elements.rb +40 -0
- data/lib/relaton/render/iso690/fields.rb +53 -0
- data/lib/relaton/render/iso690/i18n/en.yml +13 -0
- data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
- data/lib/relaton/render/iso690/i18n.rb +53 -0
- data/lib/relaton/render/iso690/identifier_kinds.rb +34 -0
- data/lib/relaton/render/iso690/index_renderer.rb +57 -0
- data/lib/relaton/render/iso690/kinds.rb +43 -0
- data/lib/relaton/render/iso690/renderer.rb +52 -0
- data/lib/relaton/render/iso690/style/locale.rb +44 -0
- data/lib/relaton/render/iso690/style/name_form.rb +21 -0
- data/lib/relaton/render/iso690/style/scheme.rb +23 -0
- data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
- data/lib/relaton/render/iso690/style/template_map.rb +28 -0
- data/lib/relaton/render/iso690/style/type_template.rb +21 -0
- data/lib/relaton/render/iso690/style.rb +76 -0
- data/lib/relaton/render/iso690/styles/author-date.yml +28 -0
- data/lib/relaton/render/iso690/template.rb +72 -0
- data/lib/relaton/render/iso690.rb +22 -0
- data/lib/relaton/render/version.rb +1 -1
- data/lib/relaton-render.rb +3 -7
- data/relaton-render.gemspec +2 -19
- metadata +35 -249
- data/lib/relaton/render/fields/date.rb +0 -48
- data/lib/relaton/render/fields/fields.rb +0 -266
- data/lib/relaton/render/general/citations.rb +0 -178
- data/lib/relaton/render/general/config.yml +0 -129
- data/lib/relaton/render/general/render.rb +0 -316
- data/lib/relaton/render/general/render_classes.rb +0 -132
- data/lib/relaton/render/general/uri.rb +0 -57
- data/lib/relaton/render/i18n/i18n.rb +0 -40
- data/lib/relaton/render/parse/parse.rb +0 -152
- data/lib/relaton/render/parse/parse_contributors.rb +0 -184
- data/lib/relaton/render/parse/parse_extract.rb +0 -200
- data/lib/relaton/render/parse/parse_id.rb +0 -128
- data/lib/relaton/render/template/liquid.rb +0 -48
- data/lib/relaton/render/template/subclasses.rb +0 -123
- data/lib/relaton/render/template/template.rb +0 -228
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Relaton
|
|
6
|
+
module Render
|
|
7
|
+
module Iso690
|
|
8
|
+
# Language-specific labels and typographic punctuation, declared per
|
|
9
|
+
# language in i18n/<lang>.yml. Adding a language adds a YAML
|
|
10
|
+
# declaration — no code. A style instance's localized strings overlay
|
|
11
|
+
# the language pack; language-varying intra-element punctuation
|
|
12
|
+
# (e.g. French " : " production separator) is declared under
|
|
13
|
+
# `punct:`.
|
|
14
|
+
class I18n < Lutaml::Model::Serializable
|
|
15
|
+
attribute :lang, :string, default: "en"
|
|
16
|
+
attribute :labels, :hash, default: {}
|
|
17
|
+
attribute :punct, :hash, default: {}
|
|
18
|
+
|
|
19
|
+
key_value do
|
|
20
|
+
map "lang", to: :lang
|
|
21
|
+
map "labels", to: :labels
|
|
22
|
+
map "punct", to: :punct
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def label(key)
|
|
26
|
+
labels.fetch(key.to_s, key.to_s)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def punct_fetch(key, default)
|
|
30
|
+
punct.fetch(key, default)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Style-instance localized strings win over the language pack.
|
|
34
|
+
def overlay!(locale)
|
|
35
|
+
labels.merge!(locale.label_map)
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
def load(lang = "en")
|
|
41
|
+
code = lang.to_s.empty? ? "en" : lang
|
|
42
|
+
path = File.join(__dir__, "i18n", "#{code}.yml")
|
|
43
|
+
unless File.file?(path)
|
|
44
|
+
raise ArgumentError, "no i18n declarations for language #{code}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
from_yaml(File.read(path))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
# Standard and persistent identifiers (ISO 690 clause 7.11): each
|
|
7
|
+
# identifier kind carries its own rendering method. Kinds are keyed by
|
|
8
|
+
# the docidentifier's type attribute — never guessed from content.
|
|
9
|
+
# Flavors register additional kinds (e.g. Pubid::Iala's MRN).
|
|
10
|
+
module IdentifierKinds
|
|
11
|
+
KINDS = {
|
|
12
|
+
"DOI" => ->(content) { "https://doi.org/#{content}" },
|
|
13
|
+
"ISBN" => ->(content) { "ISBN #{content}" },
|
|
14
|
+
"ISSN" => ->(content) { "ISSN #{content}" },
|
|
15
|
+
"URN" => ->(content) { content },
|
|
16
|
+
"MRN" => ->(content) { content },
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def register(type, kind)
|
|
21
|
+
KINDS[type] = kind
|
|
22
|
+
kind
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render(docidentifier)
|
|
26
|
+
content = docidentifier.content.to_s
|
|
27
|
+
kind = KINDS[docidentifier.type]
|
|
28
|
+
kind ? kind.call(content) : content
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
# A bibliographic index: the ordered reference list, laid out per the
|
|
7
|
+
# BibliographicStyle instance. Sorting follows the style's declared
|
|
8
|
+
# sortKey rules (creator, then date); numbering is layout data.
|
|
9
|
+
class IndexRenderer
|
|
10
|
+
def initialize(style: "author-date", lang: "en", script: "Latn")
|
|
11
|
+
@style = Style.load(style)
|
|
12
|
+
@i18n = I18n.load(lang).overlay!(@style.scheme.locale)
|
|
13
|
+
@renderer = Renderer.new(style: style, lang: lang, script: script)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The reference strings, sorted per the style's sortKey rules.
|
|
17
|
+
def references(models)
|
|
18
|
+
sort_rules = @style.sort_keys
|
|
19
|
+
fields = models.to_h { |m| [m, field_table(m)] }
|
|
20
|
+
models.sort do |a, b|
|
|
21
|
+
fa = fields[a]
|
|
22
|
+
fb = fields[b]
|
|
23
|
+
sort_rules.each do |rule|
|
|
24
|
+
cmp = key_of(fa, rule) <=> key_of(fb, rule)
|
|
25
|
+
next if cmp.zero?
|
|
26
|
+
|
|
27
|
+
break(rule.descending? ? -cmp : cmp)
|
|
28
|
+
end || 0
|
|
29
|
+
end.map { |model| @renderer.render(model) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The laid-out index: numbered entries per the layout rules
|
|
33
|
+
# (arabic by default; "none" renders bare references).
|
|
34
|
+
def render(models, numbering: "arabic")
|
|
35
|
+
refs = references(models)
|
|
36
|
+
return refs if numbering == "none"
|
|
37
|
+
|
|
38
|
+
refs.each_with_index.map { |ref, i| "#{i + 1}. #{ref}" }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def field_table(model)
|
|
44
|
+
Fields.new(model, style: @style, i18n: @i18n).to_h
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def key_of(fields, rule)
|
|
48
|
+
field = fields[rule.attribute.to_s.delete("_").downcase]
|
|
49
|
+
return "" if field.nil?
|
|
50
|
+
|
|
51
|
+
text = field.respond_to?(:text) ? field.text : field.to_s
|
|
52
|
+
text.downcase
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
# The vocabulary mapping from a bibliographic item's wire type value
|
|
7
|
+
# to its ISO 690 clause 8 resource kind (TemplateType). This table is
|
|
8
|
+
# the engine's charter — style instances select perType templates on
|
|
9
|
+
# kinds and never enumerate type synonyms. Unmapped values fall to
|
|
10
|
+
# the report kind (clause 8.11), the runner's fallback.
|
|
11
|
+
module Kinds
|
|
12
|
+
TO_KIND = {
|
|
13
|
+
"book" => "monograph",
|
|
14
|
+
"booklet" => "monograph",
|
|
15
|
+
"manual" => "monograph",
|
|
16
|
+
"proceedings" => "monograph",
|
|
17
|
+
"thesis" => "monograph",
|
|
18
|
+
"standard" => "report",
|
|
19
|
+
"techreport" => "report",
|
|
20
|
+
"report" => "report",
|
|
21
|
+
"patent" => "patent",
|
|
22
|
+
"journal" => "continuing",
|
|
23
|
+
"serial" => "continuing",
|
|
24
|
+
"article-journal" => "continuing",
|
|
25
|
+
"article-magazine" => "continuing",
|
|
26
|
+
"inbook" => "component_part",
|
|
27
|
+
"incollection" => "component_part",
|
|
28
|
+
"inproceedings" => "component_part",
|
|
29
|
+
"website" => "online",
|
|
30
|
+
"webresource" => "online",
|
|
31
|
+
"webpage" => "online",
|
|
32
|
+
"online" => "online",
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
DEFAULT_KIND = "report"
|
|
36
|
+
|
|
37
|
+
def self.kind_for(type)
|
|
38
|
+
TO_KIND.fetch(type.to_s, DEFAULT_KIND)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
# Public API: relaton model in, citation string out. The style
|
|
7
|
+
# instance supplies every rendering decision; this class only wires
|
|
8
|
+
# the template evaluator to the field resolver. No XML, no
|
|
9
|
+
# intermediate hashes.
|
|
10
|
+
class Renderer
|
|
11
|
+
class Unrenderable < StandardError; end
|
|
12
|
+
|
|
13
|
+
DEFAULT_STYLE = "author-date"
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
def render(model, style: DEFAULT_STYLE, lang: "en", script: "Latn")
|
|
17
|
+
new(style: style, lang: lang, script: script).render(model)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def citation(model, disambiguator: nil, style: DEFAULT_STYLE,
|
|
21
|
+
lang: "en", script: "Latn")
|
|
22
|
+
new(style: style, lang: lang, script: script)
|
|
23
|
+
.citation(model, disambiguator: disambiguator)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(style: DEFAULT_STYLE, lang: "en", script: "Latn")
|
|
28
|
+
@style = Style.load(style)
|
|
29
|
+
@i18n = I18n.load(lang).overlay!(@style.scheme.locale)
|
|
30
|
+
@script = script
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render(model)
|
|
34
|
+
out = Template.new(@style.template_for(Kinds.kind_for(model.type))).evaluate(
|
|
35
|
+
Fields.new(model, style: @style, i18n: @i18n).to_h,
|
|
36
|
+
)
|
|
37
|
+
raise Unrenderable, "no renderable elements" if out.strip.empty?
|
|
38
|
+
|
|
39
|
+
out
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# In-text form: SURNAME, year + disambiguator (name-and-date)
|
|
43
|
+
def citation(model, disambiguator: nil)
|
|
44
|
+
Template.new(@style.templates.citation).evaluate(
|
|
45
|
+
Fields.new(model, style: @style, i18n: @i18n,
|
|
46
|
+
disambiguator: disambiguator).to_h,
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
class Style
|
|
7
|
+
# LocalizedStrings: per-style overrides of the language pack's
|
|
8
|
+
# connective strings, keyed as in the CitationStyle model.
|
|
9
|
+
class Locale < Lutaml::Model::Serializable
|
|
10
|
+
attribute :conj, :string
|
|
11
|
+
attribute :others, :string
|
|
12
|
+
attribute :no_date, :string
|
|
13
|
+
attribute :no_author, :string
|
|
14
|
+
attribute :in_str, :string
|
|
15
|
+
attribute :at, :string
|
|
16
|
+
attribute :available_at, :string
|
|
17
|
+
|
|
18
|
+
key_value do
|
|
19
|
+
map "and", to: :conj
|
|
20
|
+
map "others", to: :others
|
|
21
|
+
map "noDate", to: :no_date
|
|
22
|
+
map "noAuthor", to: :no_author
|
|
23
|
+
map "in", to: :in_str
|
|
24
|
+
map "at", to: :at
|
|
25
|
+
map "availableAt", to: :available_at
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
LABEL_FOR = {
|
|
29
|
+
"conj" => "and", "others" => "others", "no_date" => "no_date",
|
|
30
|
+
"no_author" => "no_author", "in_str" => "in", "at" => "at",
|
|
31
|
+
"available_at" => "available_from",
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
def label_map
|
|
35
|
+
LABEL_FOR.filter_map do |attr, label|
|
|
36
|
+
value = public_send(attr)
|
|
37
|
+
[label, value] unless value.nil?
|
|
38
|
+
end.to_h
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
class Style
|
|
7
|
+
# How personal names are formed, as declared by the style's
|
|
8
|
+
# citation scheme (relaton-models NameFormRules).
|
|
9
|
+
class NameForm < Lutaml::Model::Serializable
|
|
10
|
+
attribute :initials, :boolean, default: -> { false }
|
|
11
|
+
attribute :given_name_first, :boolean, default: -> { false }
|
|
12
|
+
|
|
13
|
+
key_value do
|
|
14
|
+
map "initials", to: :initials
|
|
15
|
+
map "givenNameFirst", to: :given_name_first
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
class Style
|
|
7
|
+
# CitationScheme: the citation system, its name form and its
|
|
8
|
+
# localized strings (relaton-models CitationScheme).
|
|
9
|
+
class Scheme < Lutaml::Model::Serializable
|
|
10
|
+
attribute :system, :string, default: "name-date"
|
|
11
|
+
attribute :name_form, NameForm, default: -> { NameForm.new }
|
|
12
|
+
attribute :locale, Locale, default: -> { Locale.new }
|
|
13
|
+
|
|
14
|
+
key_value do
|
|
15
|
+
map "system", to: :system
|
|
16
|
+
map "nameForm", to: :name_form
|
|
17
|
+
map "locale", to: :locale
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
class Style
|
|
7
|
+
# SortRule: one ordering key of a bibliographic index sort
|
|
8
|
+
# (relaton-models SortRule).
|
|
9
|
+
class SortRule < Lutaml::Model::Serializable
|
|
10
|
+
attribute :attribute, :string
|
|
11
|
+
attribute :order, :string
|
|
12
|
+
|
|
13
|
+
key_value do
|
|
14
|
+
map "attribute", to: :attribute
|
|
15
|
+
map "order", to: :order
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def descending?
|
|
19
|
+
order == "descending"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
class Style
|
|
7
|
+
# TemplateMap: citation and reference templates as data, plus the
|
|
8
|
+
# title emphasis markers and the first-creator name form. Slots
|
|
9
|
+
# name ISO 690 clause 7 data elements or name-form fields.
|
|
10
|
+
class TemplateMap < Lutaml::Model::Serializable
|
|
11
|
+
attribute :title_open, :string, default: "_"
|
|
12
|
+
attribute :title_close, :string, default: "_"
|
|
13
|
+
attribute :citation, :string, default: "{{surname}}, {{date}}"
|
|
14
|
+
attribute :reference, :string
|
|
15
|
+
attribute :name, :string, default: "{{surname}}, {{givenNames}}"
|
|
16
|
+
|
|
17
|
+
key_value do
|
|
18
|
+
map "titleOpen", to: :title_open
|
|
19
|
+
map "titleClose", to: :title_close
|
|
20
|
+
map "citation", to: :citation
|
|
21
|
+
map "reference", to: :reference
|
|
22
|
+
map "name", to: :name
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
class Style
|
|
7
|
+
# TypeTemplate: the template variant for one bibliographic item
|
|
8
|
+
# type, selected by data lookup (relaton-models TypeTemplate).
|
|
9
|
+
class TypeTemplate < Lutaml::Model::Serializable
|
|
10
|
+
attribute :type, :string
|
|
11
|
+
attribute :template, :string
|
|
12
|
+
|
|
13
|
+
key_value do
|
|
14
|
+
map "type", to: :type
|
|
15
|
+
map "template", to: :template
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
# A citation style as an instance of the relaton-models CitationStyle
|
|
7
|
+
# model: the citation scheme (name form, localized strings), the
|
|
8
|
+
# citation and reference templates, and per-type template variants.
|
|
9
|
+
# All rendering knowledge is data; the engine holds no styles, no
|
|
10
|
+
# vocabularies, no localized strings.
|
|
11
|
+
class Style < Lutaml::Model::Serializable
|
|
12
|
+
autoload :NameForm, "relaton/render/iso690/style/name_form"
|
|
13
|
+
autoload :Locale, "relaton/render/iso690/style/locale"
|
|
14
|
+
autoload :Scheme, "relaton/render/iso690/style/scheme"
|
|
15
|
+
autoload :TemplateMap, "relaton/render/iso690/style/template_map"
|
|
16
|
+
autoload :SortRule, "relaton/render/iso690/style/sort_rule"
|
|
17
|
+
autoload :TypeTemplate, "relaton/render/iso690/style/type_template"
|
|
18
|
+
|
|
19
|
+
attribute :name, :string
|
|
20
|
+
attribute :scheme, Scheme, default: -> { Scheme.new }
|
|
21
|
+
attribute :templates, TemplateMap, default: -> { TemplateMap.new }
|
|
22
|
+
attribute :per_type, TypeTemplate, collection: true, default: []
|
|
23
|
+
attribute :sort_key, SortRule, collection: true, default: []
|
|
24
|
+
|
|
25
|
+
key_value do
|
|
26
|
+
map "name", to: :name
|
|
27
|
+
map "scheme", to: :scheme
|
|
28
|
+
map "templates", to: :templates
|
|
29
|
+
map "perType", to: :per_type
|
|
30
|
+
map "sortKey", to: :sort_key
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The index sort rules; creator then date when the style declares
|
|
34
|
+
# none (the name-and-date default).
|
|
35
|
+
def sort_keys
|
|
36
|
+
return sort_key unless sort_key.empty?
|
|
37
|
+
|
|
38
|
+
[SortRule.new(attribute: "creator"), SortRule.new(attribute: "date")]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Per-type template selection is a data lookup; unmatched types
|
|
42
|
+
# fall back to the general reference template.
|
|
43
|
+
def template_for(type)
|
|
44
|
+
per_type.find { |t| t.type == type.to_s }&.template ||
|
|
45
|
+
templates.reference
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# First-creator name form: the style's declared name template with
|
|
49
|
+
# the surname upcased, per the name-and-date convention.
|
|
50
|
+
def render_name(surname:, given:)
|
|
51
|
+
Template.new(templates.name).evaluate(
|
|
52
|
+
"surname" => Template::Field[!surname.empty?, surname.upcase],
|
|
53
|
+
"givennames" => Template::Field[!given.empty?, given],
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class << self
|
|
58
|
+
def load(name_or_path)
|
|
59
|
+
path = style_path(name_or_path) or
|
|
60
|
+
raise ArgumentError, "unknown style #{name_or_path}"
|
|
61
|
+
from_yaml(File.read(path))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def style_path(name)
|
|
67
|
+
file = File.join(__dir__, "styles", "#{name}.yml")
|
|
68
|
+
return file if File.file?(file)
|
|
69
|
+
|
|
70
|
+
name if name.is_a?(String) && File.file?(name)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ISO 690 name-and-date citation style, an instance of the relaton-models
|
|
2
|
+
# CitationStyle model. Templates place the ISO 690 clause 7 data elements
|
|
3
|
+
# ({{creator}}, {{title}}, ...) in each resource kind's clause 8 order; an
|
|
4
|
+
# absent element takes its trailing punctuation with it. A new style is a
|
|
5
|
+
# YAML instance — the engine holds no styles.
|
|
6
|
+
name: "ISO 690 (name and date)"
|
|
7
|
+
scheme:
|
|
8
|
+
system: name-date
|
|
9
|
+
nameForm:
|
|
10
|
+
initials: false
|
|
11
|
+
givenNameFirst: false
|
|
12
|
+
templates:
|
|
13
|
+
titleOpen: "_"
|
|
14
|
+
titleClose: "_"
|
|
15
|
+
citation: "{{surname}}, {{date}}{{disambiguator}}"
|
|
16
|
+
reference: "{{creator}}. {{title}}. {{medium}}. {{edition}}. {{series}}. {{production}}, {{date}}. {{identifier}}. {{location}}."
|
|
17
|
+
name: "{{surname}}, {{givenNames}}"
|
|
18
|
+
perType:
|
|
19
|
+
- type: monograph
|
|
20
|
+
template: "{{creator}}. {{title}}. {{edition}}. {{series}}. {{production}}, {{date}}. {{identifier}}. {{location}}."
|
|
21
|
+
- type: report
|
|
22
|
+
template: "{{creator}}. {{title}}. {{numeration}}. {{edition}}. {{series}}. {{production}}, {{date}}. {{identifier}}. {{location}}."
|
|
23
|
+
- type: patent
|
|
24
|
+
template: "{{creator}}. {{title}}. {{identifier}}. {{date}}."
|
|
25
|
+
- type: continuing
|
|
26
|
+
template: "{{creator}}. {{title}}. {{componentPart}}. {{medium}}. {{edition}}. {{production}}, {{date}}. {{numeration}}. {{identifier}}. {{location}}."
|
|
27
|
+
- type: online
|
|
28
|
+
template: "{{creator}}. {{title}}. {{date}}. {{identifier}}. {{location}}."
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
# A citation template as declared in a style instance: literal text
|
|
7
|
+
# interleaved with {{slot}} placeholders resolved against the ISO 690
|
|
8
|
+
# data element inventory. A leading literal attaches to the first
|
|
9
|
+
# slot, and a literal between two slots to the preceding one, so an
|
|
10
|
+
# absent field never orphans punctuation around it. The last rendered
|
|
11
|
+
# slot gives up its trailing literal in favour of the template
|
|
12
|
+
# terminator, appended once when any field rendered and the output
|
|
13
|
+
# does not already end with it.
|
|
14
|
+
class Template
|
|
15
|
+
SLOT = /\{\{\s*([\w-]+)\s*\}\}/.freeze
|
|
16
|
+
|
|
17
|
+
Field = Struct.new(:present?, :text)
|
|
18
|
+
|
|
19
|
+
def initialize(source)
|
|
20
|
+
@slots = []
|
|
21
|
+
@terminator = ""
|
|
22
|
+
scan(source.to_s)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def evaluate(fields)
|
|
26
|
+
rendered = @slots.filter_map do |(name, head, tail)|
|
|
27
|
+
field = fields[normalise(name)] or next
|
|
28
|
+
next unless field.present?
|
|
29
|
+
|
|
30
|
+
[head, field.text, tail]
|
|
31
|
+
end
|
|
32
|
+
return "" if rendered.empty?
|
|
33
|
+
|
|
34
|
+
terminate(rendered.each_with_index.map do |(head, text, tail), i|
|
|
35
|
+
i == rendered.size - 1 ? "#{head}#{text}" : "#{head}#{text}#{tail}"
|
|
36
|
+
end.join).rstrip
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def scan(source)
|
|
42
|
+
rest = source
|
|
43
|
+
while (slot = SLOT.match(rest))
|
|
44
|
+
head = @slots.empty? ? rest[0...slot.begin(0)] : ""
|
|
45
|
+
@slots << [slot[1], head, ""]
|
|
46
|
+
rest = rest[slot.end(0)..]
|
|
47
|
+
following = SLOT.match(rest)
|
|
48
|
+
boundary = following ? following.begin(0) : rest.length
|
|
49
|
+
literal = rest[0...boundary]
|
|
50
|
+
if following
|
|
51
|
+
@slots.last[2] = literal
|
|
52
|
+
else
|
|
53
|
+
@terminator = literal
|
|
54
|
+
end
|
|
55
|
+
rest = rest[boundary..]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def terminate(body)
|
|
60
|
+
return body if @terminator.strip.empty?
|
|
61
|
+
return body if body.rstrip.end_with?(@terminator.strip)
|
|
62
|
+
|
|
63
|
+
"#{body}#{@terminator}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def normalise(slot)
|
|
67
|
+
slot.to_s.delete("_").downcase
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
# Citation rendering per the ISO 690 architecture: a style instance
|
|
6
|
+
# places the inventory's data elements (clause 7) in each resource
|
|
7
|
+
# kind's order (clause 8); the engine is a template evaluator over a
|
|
8
|
+
# field resolver and holds no styles, no vocabularies, no i18n.
|
|
9
|
+
module Iso690
|
|
10
|
+
autoload :Element, "relaton/render/iso690/element"
|
|
11
|
+
autoload :Elements, "relaton/render/iso690/elements"
|
|
12
|
+
autoload :Fields, "relaton/render/iso690/fields"
|
|
13
|
+
autoload :I18n, "relaton/render/iso690/i18n"
|
|
14
|
+
autoload :IndexRenderer, "relaton/render/iso690/index_renderer"
|
|
15
|
+
autoload :IdentifierKinds, "relaton/render/iso690/identifier_kinds"
|
|
16
|
+
autoload :Kinds, "relaton/render/iso690/kinds"
|
|
17
|
+
autoload :Renderer, "relaton/render/iso690/renderer"
|
|
18
|
+
autoload :Style, "relaton/render/iso690/style"
|
|
19
|
+
autoload :Template, "relaton/render/iso690/template"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/relaton-render.rb
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "relaton/render/version"
|
|
2
|
-
require "relaton/render/
|
|
3
|
-
require "relaton/render/fields/fields"
|
|
4
|
-
require "relaton/render/parse/parse"
|
|
5
|
-
require "relaton/render/i18n/i18n"
|
|
6
|
-
require "isodoc/i18n"
|
|
7
|
-
require "base64" # Liquid
|
|
8
|
-
require "bigdecimal" # Liquid
|
|
4
|
+
require "relaton/render/iso690"
|
data/relaton-render.gemspec
CHANGED
|
@@ -19,25 +19,8 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.bindir = "exe"
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
21
21
|
spec.require_paths = ["lib"]
|
|
22
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 3.
|
|
22
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.3.0")
|
|
23
23
|
|
|
24
|
-
spec.
|
|
25
|
-
spec.add_development_dependency "canon"
|
|
26
|
-
spec.add_development_dependency "debug"
|
|
27
|
-
spec.add_development_dependency "isodoc"
|
|
28
|
-
spec.add_development_dependency "openssl"
|
|
29
|
-
spec.add_development_dependency "rake", ">= 12.3.3"
|
|
30
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
|
31
|
-
spec.add_development_dependency "simplecov"
|
|
32
|
-
|
|
33
|
-
spec.add_dependency "base64" # Liquid
|
|
34
|
-
spec.add_dependency "bigdecimal" # Liquid
|
|
35
|
-
spec.add_dependency "isodoc-i18n", "~> 1.5.0"
|
|
36
|
-
spec.add_dependency "liquid", "~> 5"
|
|
37
|
-
spec.add_dependency "metanorma-utils", "~> 2"
|
|
38
|
-
spec.add_dependency "nokogiri"
|
|
39
|
-
spec.add_dependency "relaton-bib", ">= 2.0.0.pre.alpha.6", "< 3"
|
|
40
|
-
spec.add_dependency "twitter_cldr"
|
|
41
|
-
spec.add_dependency "tzinfo-data" # we need this for windows only
|
|
24
|
+
spec.add_dependency "lutaml-model"
|
|
42
25
|
# spec.metadata["rubygems_mfa_required"] = "true"
|
|
43
26
|
end
|