relaton-render 0.1.0 → 3.0.0.pre.alpha.25

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +4 -1
  3. data/.github/workflows/release.yml +24 -0
  4. data/.gitignore +14 -0
  5. data/.rubocop.yml +2 -2
  6. data/Gemfile +17 -1
  7. data/README.adoc +61 -197
  8. data/docs/citation-style-pack.adoc +367 -0
  9. data/lib/relaton/render/general.rb +305 -0
  10. data/lib/relaton/render/iso690/element.rb +153 -0
  11. data/lib/relaton/render/iso690/elements/access.rb +44 -0
  12. data/lib/relaton/render/iso690/elements/authorizer.rb +30 -0
  13. data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
  14. data/lib/relaton/render/iso690/elements/component_part.rb +99 -0
  15. data/lib/relaton/render/iso690/elements/creator.rb +258 -0
  16. data/lib/relaton/render/iso690/elements/edition.rb +92 -0
  17. data/lib/relaton/render/iso690/elements/extent.rb +71 -0
  18. data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
  19. data/lib/relaton/render/iso690/elements/location.rb +73 -0
  20. data/lib/relaton/render/iso690/elements/medium.rb +32 -0
  21. data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
  22. data/lib/relaton/render/iso690/elements/production.rb +92 -0
  23. data/lib/relaton/render/iso690/elements/pub_date.rb +56 -0
  24. data/lib/relaton/render/iso690/elements/series.rb +66 -0
  25. data/lib/relaton/render/iso690/elements/size.rb +41 -0
  26. data/lib/relaton/render/iso690/elements/status.rb +30 -0
  27. data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
  28. data/lib/relaton/render/iso690/elements/title.rb +56 -0
  29. data/lib/relaton/render/iso690/elements/updated.rb +38 -0
  30. data/lib/relaton/render/iso690/elements.rb +67 -0
  31. data/lib/relaton/render/iso690/fields.rb +142 -0
  32. data/lib/relaton/render/iso690/i18n/en.yml +47 -0
  33. data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
  34. data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
  35. data/lib/relaton/render/iso690/i18n.rb +83 -0
  36. data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
  37. data/lib/relaton/render/iso690/index_renderer.rb +57 -0
  38. data/lib/relaton/render/iso690/kinds.rb +53 -0
  39. data/lib/relaton/render/iso690/renderer.rb +148 -0
  40. data/lib/relaton/render/iso690/style/locale.rb +53 -0
  41. data/lib/relaton/render/iso690/style/name_form.rb +87 -0
  42. data/lib/relaton/render/iso690/style/scheme.rb +33 -0
  43. data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
  44. data/lib/relaton/render/iso690/style/template_map.rb +40 -0
  45. data/lib/relaton/render/iso690/style/type_template.rb +37 -0
  46. data/lib/relaton/render/iso690/style.rb +119 -0
  47. data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
  48. data/lib/relaton/render/iso690/template.rb +248 -0
  49. data/lib/relaton/render/iso690.rb +26 -0
  50. data/lib/relaton/render/version.rb +5 -0
  51. data/lib/relaton-render.rb +12 -5
  52. data/relaton-render.gemspec +7 -11
  53. metadata +57 -86
  54. data/lib/isodoc/i18n.rb +0 -24
  55. data/lib/isodoc-yaml/i18n-ar.yaml +0 -2
  56. data/lib/isodoc-yaml/i18n-de.yaml +0 -2
  57. data/lib/isodoc-yaml/i18n-en.yaml +0 -22
  58. data/lib/isodoc-yaml/i18n-es.yaml +0 -2
  59. data/lib/isodoc-yaml/i18n-fr.yaml +0 -2
  60. data/lib/isodoc-yaml/i18n-ru.yaml +0 -23
  61. data/lib/isodoc-yaml/i18n-zh-Hans.yaml +0 -2
  62. data/lib/parse/parse.rb +0 -54
  63. data/lib/parse/parse_contributors.rb +0 -96
  64. data/lib/parse/parse_extract.rb +0 -155
  65. data/lib/relaton/config.yml +0 -67
  66. data/lib/relaton/render.rb +0 -123
  67. data/lib/relaton/render_classes.rb +0 -126
  68. data/lib/relaton/render_fields.rb +0 -151
  69. data/lib/relaton/version.rb +0 -5
  70. data/lib/template/template.rb +0 -152
  71. data/lib/utils/utils.rb +0 -27
@@ -0,0 +1,148 @@
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
+ DEFAULT_STYLE = "author-date"
12
+
13
+ class << self
14
+ def render(model, style: DEFAULT_STYLE, lang: "en", script: "Latn")
15
+ new(style: style, lang: lang, script: script).render(model)
16
+ end
17
+
18
+ def citation(model, disambiguator: nil, style: DEFAULT_STYLE,
19
+ lang: "en", script: "Latn")
20
+ new(style: style, lang: lang, script: script)
21
+ .citation(model, disambiguator: disambiguator)
22
+ end
23
+ end
24
+
25
+ def initialize(style: DEFAULT_STYLE, lang: "en", script: "Latn",
26
+ labels: {}, elements: {})
27
+ @elements = elements
28
+ @style = Style.load(style)
29
+ @i18n = I18n.load(lang)
30
+ @i18n.overlay_hash!(labels) unless labels.empty?
31
+ # The style instance is the citation-style authority: its
32
+ # declared strings win over the caller's generic i18n hash
33
+ @i18n.overlay!(@style.scheme.locale)
34
+ @script = script
35
+ end
36
+
37
+ def render(model, disambiguator: nil)
38
+ kind = @style.kind_for(model.type)
39
+ template = @style.template_for(kind, home: home_docid?(model))
40
+ out = Template.new(template).evaluate(
41
+ Fields.new(model, style: @style, i18n: @i18n,
42
+ disambiguator: disambiguator,
43
+ elements: @elements).to_h,
44
+ )
45
+ raise ::Relaton::Render::Unrenderable,
46
+ "no renderable elements" if out.strip.empty?
47
+
48
+ out
49
+ end
50
+
51
+ def home_docid?(model)
52
+ types = Array(@style.scheme.home_docid_type)
53
+ return false if types.empty?
54
+
55
+ Array(model.docidentifier).any? { |d| types.include?(d.type) }
56
+ end
57
+
58
+ # A creator list alone (flavors' document-history name forms)
59
+ def render_creators(model)
60
+ Elements.build(:creator, model, style: @style, i18n: @i18n,
61
+ short: true, elements: @elements).render
62
+ end
63
+
64
+ # The short cite: the reference with the first-biblio marker
65
+ def render_short(model, delim)
66
+ kind = @style.kind_for(model.type)
67
+ template = @style.short_template_for(kind,
68
+ home: home_docid?(model)) ||
69
+ @style.template_for(kind, home: home_docid?(model))
70
+ Template.new(template).evaluate_short(
71
+ Fields.new(model, style: @style, i18n: @i18n, short: true,
72
+ elements: @elements).to_h,
73
+ delim,
74
+ )
75
+ rescue ::Relaton::Render::Unrenderable
76
+ ""
77
+ end
78
+
79
+ # The in-text creator with its et-al truncation: the
80
+ # author-date tag's author part
81
+ def in_text_author(model)
82
+ Elements.build(:creator, model, style: @style, i18n: @i18n,
83
+ elements: @elements).in_text
84
+ end
85
+
86
+ # The author-date in-text capsule ("Aluffi <em>et al.</em>
87
+ # 2022a"): the in-text creator and the disambiguated date
88
+ def author_date_citation(model, disambiguator: nil)
89
+ f = Fields.new(model, style: @style, i18n: @i18n,
90
+ disambiguator: disambiguator,
91
+ elements: @elements).to_h
92
+ [in_text_author(model), f["disambiguateddate"].text]
93
+ .reject(&:empty?).join(" ")
94
+ end
95
+
96
+ # The bracketed author-date in-text capsule ("Aluffi (2022)"):
97
+ # the author and the PLAIN date (un-disambiguated, the 1.x
98
+ # citetemplate's {{ date }}) in the locale's parentheses
99
+ def bracketed_date_citation(model, **)
100
+ f = Fields.new(model, style: @style, i18n: @i18n,
101
+ elements: @elements).to_h
102
+ date = f["date"].text
103
+ return "" if date.empty?
104
+
105
+ open = @i18n.punct_fetch("open-paren", "(")
106
+ close = @i18n.punct_fetch("close-paren", ")")
107
+ [in_text_author(model), "#{open}#{date}#{close}"]
108
+ .reject(&:empty?).join(" ")
109
+ end
110
+
111
+ # The disambiguated date alone (the citation renderings' date)
112
+ def disambiguated_date(model, disambiguator: nil)
113
+ Fields.new(model, style: @style, i18n: @i18n,
114
+ disambiguator: disambiguator,
115
+ elements: @elements).to_h["disambiguateddate"].text
116
+ end
117
+
118
+ def short_from_reference?
119
+ @style.scheme.short_from_reference
120
+ end
121
+
122
+ # The title citation ("{{ title }}" in the 1.x citetemplate):
123
+ # the title element's plain form, no style markers
124
+ def title_citation(model)
125
+ Elements.build(:title, model, style: @style, i18n: @i18n,
126
+ elements: @elements).plain
127
+ end
128
+
129
+ # The in-text author key (the principal creator's surname, as
130
+ # the citation renders it): the batch disambiguation groups by
131
+ # it
132
+ def author_key(model)
133
+ Fields.new(model, style: @style, i18n: @i18n,
134
+ elements: @elements).to_h["surname"].text
135
+ end
136
+
137
+ # In-text form: SURNAME, year + disambiguator (name-and-date)
138
+ def citation(model, disambiguator: nil)
139
+ Template.new(@style.templates.citation).evaluate(
140
+ Fields.new(model, style: @style, i18n: @i18n,
141
+ disambiguator: disambiguator,
142
+ elements: @elements).to_h,
143
+ )
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,53 @@
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
+ # Arbitrary label overrides (edition words, join punctuation,
18
+ # size units): merged over the language pack verbatim.
19
+ attribute :labels, :hash, default: -> { {} }
20
+ # Typographic punctuation overrides (production order and
21
+ # separator), declared as in the language packs
22
+ attribute :punct, :hash, default: -> { {} }
23
+
24
+ key_value do
25
+ map "and", to: :conj
26
+ map "others", to: :others
27
+ map "noDate", to: :no_date
28
+ map "noAuthor", to: :no_author
29
+ map "in", to: :in_str
30
+ map "at", to: :at
31
+ map "availableAt", to: :available_at
32
+ map "labels", to: :labels
33
+ map "punct", to: :punct
34
+ end
35
+
36
+ LABEL_FOR = {
37
+ "conj" => "and", "others" => "others", "no_date" => "no_date",
38
+ "no_author" => "no_author", "in_str" => "in", "at" => "at",
39
+ "available_at" => "available_from",
40
+ }.freeze
41
+
42
+ def label_map
43
+ base = LABEL_FOR.filter_map do |attr, label|
44
+ value = public_send(attr)
45
+ [label, value] unless value.nil?
46
+ end.to_h
47
+ base.merge(labels || {})
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,87 @@
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
+ # Where an all-editor creator list's role marker sits relative
13
+ # to the name's terminating period: "glue" renders
14
+ # "NAME (eds.)." (the name-date default), "afterPeriod"
15
+ # renders "NAME. (eds.)" (ISO 690 clause-flavour).
16
+ attribute :role_placement, :string, default: "glue"
17
+ # A completename is rendered, not decomposed; styles that
18
+ # upcase inverted names may declare the completename treatment
19
+ attribute :completename_upcase, :boolean, default: -> { false }
20
+ # Every personal name takes the inverted form (surname first),
21
+ # not just the principal creator
22
+ attribute :inverted_all, :boolean, default: -> { false }
23
+ # "serial": the oxford comma appears at every gap of the
24
+ # creator list, including between two names
25
+ attribute :list_style, :string, default: ""
26
+ # Organization names take the inverted form's casing; small-cap
27
+ # styles keep the declared case
28
+ attribute :org_upcase, :boolean, default: -> { true }
29
+ # When no personal creator exists, the creator slot falls back
30
+ # to the publisher's abbreviation ("ISO: ISO 712, ...")
31
+ attribute :creator_fallback, :string
32
+ # A lone editor still carries the role marker ("(ed.)")
33
+ attribute :lone_editor_marked, :boolean, default: -> { false }
34
+ # Initials without their trailing periods ("Nixon RM")
35
+ attribute :initials_period, :boolean, default: -> { true }
36
+ # The inverted form's surname casing: the name-date convention
37
+ # upcases; small-cap styles keep the declared case
38
+ attribute :surname_upcase, :boolean, default: -> { true }
39
+ # Initials are a list; this separates them ("J. K." vs "J.K.")
40
+ attribute :initials_separator, :string, default: " "
41
+ # The first forename cites in full, the rest as initials
42
+ # ("Milena S."; document-history name forms)
43
+ attribute :subsequent_initials, :boolean, default: -> { false }
44
+ # The direct-order surname casing after the first creator:
45
+ # the name-date convention upcases
46
+ attribute :subsequent_surname_upcase, :boolean,
47
+ default: -> { true }
48
+ # At this many creators the creator list truncates to the
49
+ # first etalDisplay names with "et al." (0 disables)
50
+ attribute :etal_count, :integer, default: 0
51
+ attribute :etal_display, :integer, default: 0
52
+ # The in-text cite truncates separately from the creator
53
+ # list: at this many creators it shows the first
54
+ # inTextEtalDisplay names with "et al." (0 disables; IEEE
55
+ # cites 3+ as "Aluffi et al." while listing 6 in the
56
+ # bibliography)
57
+ attribute :in_text_etal_count, :integer, default: 0
58
+ attribute :in_text_etal_display, :integer, default: 1
59
+ # Editor lists carry the role marker ("(Eds.)")
60
+ attribute :editors_marked, :boolean, default: -> { true }
61
+
62
+ key_value do
63
+ map "initials", to: :initials
64
+ map "givenNameFirst", to: :given_name_first
65
+ map "rolePlacement", to: :role_placement
66
+ map "completenameUpcase", to: :completename_upcase
67
+ map "invertedAll", to: :inverted_all
68
+ map "listStyle", to: :list_style
69
+ map "orgUpcase", to: :org_upcase
70
+ map "creatorFallback", to: :creator_fallback
71
+ map "loneEditorMarked", to: :lone_editor_marked
72
+ map "initialsPeriod", to: :initials_period
73
+ map "surnameUpcase", to: :surname_upcase
74
+ map "initialsSeparator", to: :initials_separator
75
+ map "subsequentInitials", to: :subsequent_initials
76
+ map "subsequentSurnameUpcase", to: :subsequent_surname_upcase
77
+ map "etalCount", to: :etal_count
78
+ map "etalDisplay", to: :etal_display
79
+ map "inTextEtalCount", to: :in_text_etal_count
80
+ map "inTextEtalDisplay", to: :in_text_etal_display
81
+ map "editorsMarked", to: :editors_marked
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,33 @@
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
+ attribute :home_docid_type, :string, collection: true
14
+ attribute :default_kind, :string, default: ""
15
+
16
+ # The renderings' short cite takes the reference form with the
17
+ # first-biblio marker (the 1.x citetemplate short), not the
18
+ # citation template
19
+ attribute :short_from_reference, :boolean, default: -> { false }
20
+
21
+ key_value do
22
+ map "system", to: :system
23
+ map "nameForm", to: :name_form
24
+ map "locale", to: :locale
25
+ map "homeDocidType", to: :home_docid_type
26
+ map "defaultKind", to: :default_kind
27
+ map "shortFromReference", to: :short_from_reference
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ 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,40 @@
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
+ attribute :series, :string, default: ""
17
+ attribute :uri, :string, default: ""
18
+ attribute :creators, :string, default: ""
19
+ attribute :uri_short, :string, default: ""
20
+ # "first": cite only the first identifier (1.x
21
+ # authoritative_identifier|first styles)
22
+ attribute :identifier_mode, :string, default: "all"
23
+
24
+ key_value do
25
+ map "titleOpen", to: :title_open
26
+ map "titleClose", to: :title_close
27
+ map "citation", to: :citation
28
+ map "reference", to: :reference
29
+ map "name", to: :name
30
+ map "series", to: :series
31
+ map "uri", to: :uri
32
+ map "creators", to: :creators
33
+ map "uriShort", to: :uri_short
34
+ map "identifierMode", to: :identifier_mode
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
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
+ attribute :series, :string
13
+ attribute :title, :string
14
+ attribute :home, :boolean
15
+ attribute :fallbacks, :hash, default: -> { {} }
16
+ # The short-cite template for this kind; absent falls back to
17
+ # the reference template in short mode
18
+ attribute :short, :string
19
+ # Render the style's no_place label when the publisher has no
20
+ # place (the book-family templates of some flavors)
21
+ attribute :no_place, :boolean
22
+
23
+ key_value do
24
+ map "type", to: :type
25
+ map "template", to: :template
26
+ map "series", to: :series
27
+ map "title", to: :title
28
+ map "home", to: :home
29
+ map "fallbacks", to: :fallbacks
30
+ map "short", to: :short
31
+ map "noPlace", to: :no_place
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Relaton
6
+ module Render
7
+ module Iso690
8
+ # A citation style as an instance of the relaton-models CitationStyle
9
+ # model: the citation scheme (name form, localized strings), the
10
+ # citation and reference templates, and per-type template variants.
11
+ # All rendering knowledge is data; the engine holds no styles, no
12
+ # vocabularies, no localized strings.
13
+ class Style < Lutaml::Model::Serializable
14
+ autoload :NameForm, "relaton/render/iso690/style/name_form"
15
+ autoload :Locale, "relaton/render/iso690/style/locale"
16
+ autoload :Scheme, "relaton/render/iso690/style/scheme"
17
+ autoload :TemplateMap, "relaton/render/iso690/style/template_map"
18
+ autoload :SortRule, "relaton/render/iso690/style/sort_rule"
19
+ autoload :TypeTemplate, "relaton/render/iso690/style/type_template"
20
+
21
+ attribute :name, :string
22
+ attribute :scheme, Scheme, default: -> { Scheme.new }
23
+ attribute :templates, TemplateMap, default: -> { TemplateMap.new }
24
+ attribute :per_type, TypeTemplate, collection: true, default: []
25
+ attribute :sort_key, SortRule, collection: true, default: []
26
+
27
+ key_value do
28
+ map "name", to: :name
29
+ map "scheme", to: :scheme
30
+ map "templates", to: :templates
31
+ map "perType", to: :per_type
32
+ map "sortKey", to: :sort_key
33
+ end
34
+
35
+ # The index sort rules; creator then date when the style declares
36
+ # none (the name-and-date default).
37
+ def sort_keys
38
+ return sort_key unless sort_key.empty?
39
+
40
+ [SortRule.new(attribute: "creator"), SortRule.new(attribute: "date")]
41
+ end
42
+
43
+ # The ISO 690 kind of an item: the vocabulary's mapping, or
44
+ # the style's declared kind for types the vocabulary does not
45
+ # map (a flavor whose untyped items are not reports)
46
+ def kind_for(type)
47
+ return Kinds.kind_for(type) if Kinds.mapped?(type)
48
+
49
+ declared = scheme.default_kind.to_s
50
+ declared.empty? ? Kinds.kind_for(type) : declared
51
+ end
52
+
53
+ # Per-type template selection is a data lookup; unmatched types
54
+ # fall back to the general reference template. When a kind has
55
+ # home-flagged variants, the split is resolved by whether the item
56
+ # carries the scheme's home document identifier.
57
+ def template_for(type, home: nil)
58
+ candidates = per_type.select { |t| t.type == type.to_s }
59
+ if candidates.size > 1 && !home.nil?
60
+ candidates = candidates.select { |t| (t.home || false) == home }
61
+ end
62
+ candidates.first&.template || templates.reference
63
+ end
64
+
65
+ def type_template_for(type)
66
+ per_type.find { |t| t.type == type.to_s }
67
+ end
68
+
69
+ # The home split applies to the short cite as it does to the
70
+ # reference template (a home standard's short cite is not an
71
+ # external standard's)
72
+ def short_template_for(type, home: nil)
73
+ candidates = per_type.select { |t| t.type == type.to_s }
74
+ if candidates.size > 1 && !home.nil?
75
+ candidates = candidates.select { |t| (t.home || false) == home }
76
+ end
77
+ candidates.filter_map(&:short).first
78
+ end
79
+
80
+ # The absent-slot replacement text declared for a type, when any
81
+ def fallback_for(type, slot)
82
+ type_template_for(type)&.fallbacks&.[](slot.to_s)
83
+ end
84
+
85
+ def title_form_for(type)
86
+ per_type.lazy.select { |t| t.type == type.to_s }
87
+ .find { |t| t.title && !t.title.empty? }&.title
88
+ end
89
+
90
+ # First-creator name form: the style's declared name template with
91
+ # the surname upcased, per the name-and-date convention.
92
+ def render_name(surname:, given:)
93
+ shown = scheme.name_form.surname_upcase ? surname.upcase : surname
94
+ Template.new(templates.name).evaluate(
95
+ "surname" => Template::Field[!shown.empty?, shown],
96
+ "givennames" => Template::Field[!given.empty?, given],
97
+ )
98
+ end
99
+
100
+ class << self
101
+ def load(name_or_path)
102
+ path = style_path(name_or_path) or
103
+ raise ArgumentError, "unknown style #{name_or_path}"
104
+ from_yaml(File.read(path))
105
+ end
106
+
107
+ private
108
+
109
+ def style_path(name)
110
+ file = File.join(__dir__, "styles", "#{name}.yml")
111
+ return file if File.file?(file)
112
+
113
+ name if name.is_a?(String) && File.file?(name)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,31 @@
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
+ # ISO 690 marks an editor list's role ("(éd.)") also when the list
13
+ # holds a single editor; flavors opt out per their own style
14
+ loneEditorMarked: true
15
+ templates:
16
+ titleOpen: "_"
17
+ titleClose: "_"
18
+ citation: "{{surname}}, {{date}}{{disambiguator}}"
19
+ reference: "{{creator}}. {{title}}. {{medium}}. {{edition}}. {{series}}. {{production}}, {{date}}. {{identifier}}. {{location}}."
20
+ name: "{{surname}}, {{givenNames}}"
21
+ perType:
22
+ - type: monograph
23
+ template: "{{creator}}. {{title}}. {{edition}}. {{series}}. {{production}}, {{date}}. {{identifier}}. {{location}}."
24
+ - type: report
25
+ template: "{{creator}}. {{title}}. {{numeration}}. {{edition}}. {{series}}. {{production}}, {{date}}. {{identifier}}. {{location}}."
26
+ - type: patent
27
+ template: "{{creator}}. {{title}}. {{identifier}}. {{date}}."
28
+ - type: continuing
29
+ template: "{{creator}}. {{title}}. {{componentPart}}. {{medium}}. {{edition}}. {{production}}, {{date}}. {{numeration}}. {{identifier}}. {{location}}."
30
+ - type: online
31
+ template: "{{creator}}. {{title}}. {{date}}. {{identifier}}. {{location}}."