relaton-bib 1.20.8 → 2.0.0

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rubocop.yml +1 -1
  4. data/CLAUDE.md +96 -0
  5. data/Gemfile +1 -4
  6. data/README.adoc +195 -154
  7. data/bin/console +1 -1
  8. data/grammars/basicdoc.rng +1572 -671
  9. data/grammars/biblio-standoc.rng +107 -46
  10. data/grammars/biblio.rng +1012 -377
  11. data/grammars/versions.json +26 -26
  12. data/lib/relaton/bib/converter/asciibib/to_asciibib.rb +663 -0
  13. data/lib/relaton/bib/converter/asciibib.rb +13 -0
  14. data/lib/relaton/bib/converter/bibtex/from_bibtex.rb +245 -0
  15. data/lib/relaton/bib/converter/bibtex/to_bibtex.rb +341 -0
  16. data/lib/relaton/bib/converter/bibtex.rb +23 -0
  17. data/lib/relaton/bib/converter/bibxml/from_rfcxml.rb +383 -0
  18. data/lib/relaton/bib/converter/bibxml/from_rfcxml_referencegroup.rb +71 -0
  19. data/lib/relaton/bib/converter/bibxml/to_rfcxml.rb +305 -0
  20. data/lib/relaton/bib/converter/bibxml/to_rfcxml_referencegroup.rb +52 -0
  21. data/lib/relaton/bib/converter/bibxml.rb +51 -0
  22. data/lib/relaton/bib/hash_parser_v1.rb +754 -0
  23. data/lib/relaton/bib/item_data.rb +229 -0
  24. data/lib/relaton/bib/model/abstract.rb +16 -0
  25. data/lib/relaton/bib/model/address.rb +22 -0
  26. data/lib/relaton/bib/model/affiliation.rb +16 -0
  27. data/lib/relaton/bib/model/bibdata.rb +24 -0
  28. data/lib/relaton/bib/model/bibitem.rb +23 -0
  29. data/lib/relaton/bib/model/contact.rb +18 -0
  30. data/lib/relaton/bib/model/contribution_info.rb +16 -0
  31. data/lib/relaton/bib/model/contributor.rb +71 -0
  32. data/lib/relaton/bib/model/copyright.rb +27 -0
  33. data/lib/relaton/bib/model/date.rb +31 -0
  34. data/lib/relaton/bib/model/depiction.rb +16 -0
  35. data/lib/relaton/bib/model/docidentifier.rb +33 -0
  36. data/lib/relaton/bib/model/doctype.rb +14 -0
  37. data/lib/relaton/bib/model/edition.rb +14 -0
  38. data/lib/relaton/bib/model/ext.rb +26 -0
  39. data/lib/relaton/bib/model/extent.rb +16 -0
  40. data/lib/relaton/bib/model/formattedref.rb +43 -0
  41. data/lib/relaton/bib/model/full_name_type.rb +64 -0
  42. data/lib/relaton/bib/model/fullname.rb +11 -0
  43. data/lib/relaton/bib/model/ics.rb +23 -0
  44. data/lib/relaton/bib/model/image.rb +28 -0
  45. data/lib/relaton/bib/model/item.rb +137 -0
  46. data/lib/relaton/bib/model/item_base.rb +27 -0
  47. data/lib/relaton/bib/model/keyword.rb +30 -0
  48. data/lib/relaton/bib/model/locality.rb +18 -0
  49. data/lib/relaton/bib/model/locality_stack.rb +14 -0
  50. data/lib/relaton/bib/model/localized_string.rb +57 -0
  51. data/lib/relaton/bib/model/localized_string_attrs.rb +24 -0
  52. data/lib/relaton/bib/model/logo.rb +14 -0
  53. data/lib/relaton/bib/model/medium.rb +22 -0
  54. data/lib/relaton/bib/model/note.rb +16 -0
  55. data/lib/relaton/bib/model/organization.rb +13 -0
  56. data/lib/relaton/bib/model/organization_type.rb +42 -0
  57. data/lib/relaton/bib/model/person.rb +36 -0
  58. data/lib/relaton/bib/model/phone.rb +14 -0
  59. data/lib/relaton/bib/model/place.rb +33 -0
  60. data/lib/relaton/bib/model/price.rb +14 -0
  61. data/lib/relaton/bib/model/relation.rb +43 -0
  62. data/lib/relaton/bib/model/series.rb +34 -0
  63. data/lib/relaton/bib/model/size.rb +23 -0
  64. data/lib/relaton/bib/model/source_locality_stack.rb +14 -0
  65. data/lib/relaton/bib/model/status.rb +27 -0
  66. data/lib/relaton/bib/model/structured_identifier.rb +48 -0
  67. data/lib/relaton/bib/model/subdivision.rb +16 -0
  68. data/lib/relaton/bib/model/title.rb +56 -0
  69. data/lib/relaton/bib/model/type/string_date.rb +48 -0
  70. data/lib/relaton/bib/model/uri.rb +18 -0
  71. data/lib/relaton/bib/model/validity.rb +16 -0
  72. data/lib/relaton/bib/model/version.rb +14 -0
  73. data/lib/relaton/bib/namespace_helper.rb +21 -0
  74. data/lib/relaton/bib/util.rb +18 -0
  75. data/lib/relaton/bib/version.rb +5 -0
  76. data/lib/relaton/bib.rb +45 -0
  77. data/relaton-bib.gemspec +11 -8
  78. metadata +125 -67
  79. data/lib/relaton_bib/bib_item_locality.rb +0 -175
  80. data/lib/relaton_bib/biblio_note.rb +0 -72
  81. data/lib/relaton_bib/biblio_version.rb +0 -46
  82. data/lib/relaton_bib/bibliographic_date.rb +0 -119
  83. data/lib/relaton_bib/bibliographic_item.rb +0 -668
  84. data/lib/relaton_bib/bibliographic_size.rb +0 -103
  85. data/lib/relaton_bib/bibtex_parser.rb +0 -246
  86. data/lib/relaton_bib/bibxml_parser.rb +0 -422
  87. data/lib/relaton_bib/classification.rb +0 -40
  88. data/lib/relaton_bib/config.rb +0 -16
  89. data/lib/relaton_bib/contribution_info.rb +0 -117
  90. data/lib/relaton_bib/contributor.rb +0 -277
  91. data/lib/relaton_bib/copyright_association.rb +0 -79
  92. data/lib/relaton_bib/deep_dup.rb +0 -30
  93. data/lib/relaton_bib/document_identifier.rb +0 -118
  94. data/lib/relaton_bib/document_relation.rb +0 -95
  95. data/lib/relaton_bib/document_relation_collection.rb +0 -57
  96. data/lib/relaton_bib/document_status.rb +0 -92
  97. data/lib/relaton_bib/document_type.rb +0 -52
  98. data/lib/relaton_bib/edition.rb +0 -55
  99. data/lib/relaton_bib/editorial_group.rb +0 -41
  100. data/lib/relaton_bib/extent.rb +0 -39
  101. data/lib/relaton_bib/forename.rb +0 -65
  102. data/lib/relaton_bib/formatted_ref.rb +0 -17
  103. data/lib/relaton_bib/formatted_string.rb +0 -133
  104. data/lib/relaton_bib/full_name.rb +0 -108
  105. data/lib/relaton_bib/hash_converter.rb +0 -568
  106. data/lib/relaton_bib/hit.rb +0 -52
  107. data/lib/relaton_bib/hit_collection.rb +0 -105
  108. data/lib/relaton_bib/ics.rb +0 -42
  109. data/lib/relaton_bib/image.rb +0 -95
  110. data/lib/relaton_bib/localized_string.rb +0 -149
  111. data/lib/relaton_bib/medium.rb +0 -76
  112. data/lib/relaton_bib/organization.rb +0 -165
  113. data/lib/relaton_bib/person.rb +0 -129
  114. data/lib/relaton_bib/place.rb +0 -203
  115. data/lib/relaton_bib/renderer/bibtex_builder.rb +0 -312
  116. data/lib/relaton_bib/renderer/bibxml.rb +0 -296
  117. data/lib/relaton_bib/series.rb +0 -119
  118. data/lib/relaton_bib/structured_identifier.rb +0 -173
  119. data/lib/relaton_bib/technical_committee.rb +0 -34
  120. data/lib/relaton_bib/typed_title_string.rb +0 -191
  121. data/lib/relaton_bib/typed_uri.rb +0 -57
  122. data/lib/relaton_bib/util.rb +0 -16
  123. data/lib/relaton_bib/validity.rb +0 -52
  124. data/lib/relaton_bib/version.rb +0 -3
  125. data/lib/relaton_bib/workers_pool.rb +0 -43
  126. data/lib/relaton_bib/workgroup.rb +0 -58
  127. data/lib/relaton_bib/xml_parser.rb +0 -718
  128. data/lib/relaton_bib.rb +0 -111
@@ -0,0 +1,383 @@
1
+ module Relaton
2
+ module Bib
3
+ module Converter
4
+ module BibXml
5
+ class FromRfcxml # rubocop:disable Metrics/ClassLength
6
+ include NamespaceHelper
7
+
8
+ def initialize(reference)
9
+ @reference = reference
10
+ end
11
+
12
+ def transform # rubocop:disable Metrics/MethodLength
13
+ namespace::ItemData.new(
14
+ docnumber: @reference.anchor,
15
+ type: "standard",
16
+ docidentifier: docidentifiers,
17
+ status: status,
18
+ language: ["en"],
19
+ script: ["Latn"],
20
+ source: source,
21
+ title: title,
22
+ formattedref: formattedref,
23
+ abstract: abstract,
24
+ contributor: contributor + workgroup_contributors,
25
+ date: date,
26
+ series: series,
27
+ keyword: keyword,
28
+ ext: ext,
29
+ )
30
+ end
31
+
32
+ private
33
+
34
+ # --- Source ---
35
+
36
+ def source # rubocop:disable Metrics/AbcSize
37
+ s = []
38
+ if @reference.target
39
+ s << Uri.new(type: "src",
40
+ content: @reference.target)
41
+ end
42
+ (@reference.format || []).each do |fr|
43
+ s << Uri.new(type: fr.type, content: fr.target)
44
+ end
45
+ s
46
+ end
47
+
48
+ # --- Docidentifiers ---
49
+
50
+ def docidentifiers
51
+ draft_id = versioned_internet_draft_id
52
+ ids = [create_docid(@reference.anchor, primary: draft_id.nil?)]
53
+ ids << draft_id if draft_id
54
+ ids + docid_from_series_info
55
+ end
56
+
57
+ def create_docid(id, primary: false) # rubocop:disable Metrics/MethodLength
58
+ args = {}
59
+ pref, num = id_to_pref_num(id)
60
+ if RFCPREFIXES.include?(pref)
61
+ args[:content] = "#{pref} #{num.sub(/^-?0+/, '')}"
62
+ args[:type] = pubid_type(id)
63
+ elsif %w[I-D draft].include?(pref)
64
+ args[:content] = "draft-#{num}"
65
+ args[:type] = "Internet-Draft"
66
+ else
67
+ args[:content] = pref ? "#{pref} #{num}" : id
68
+ args[:type] = pubid_type(id)
69
+ end
70
+ args[:primary] = true if primary
71
+ Docidentifier.new(**args)
72
+ end
73
+
74
+ def pubid_type(id)
75
+ id_to_pref_num(id)&.first
76
+ end
77
+
78
+ PREF_NUM_RE = /^(?<pref>I-D|draft|3GPP|W3C|[A-Z]{2,})[._-]?(?<num>.+)/
79
+
80
+ def id_to_pref_num(id)
81
+ tn = PREF_NUM_RE.match id
82
+ tn && tn.to_a[1..2]
83
+ end
84
+
85
+ def versioned_internet_draft_id
86
+ si = internet_draft_series_info(@reference.front) ||
87
+ internet_draft_series_info(@reference)
88
+ return unless si
89
+
90
+ Docidentifier.new(
91
+ type: "Internet-Draft", content: si.value, primary: true,
92
+ )
93
+ end
94
+
95
+ def internet_draft_series_info(parent)
96
+ (parent.series_info || []).find { |si| si.name == "Internet-Draft" }
97
+ end
98
+
99
+ def docid_from_series_info # rubocop:disable Metrics/CyclomaticComplexity
100
+ front_si = @reference.front.series_info || []
101
+ ref_si = @reference.series_info || []
102
+ (front_si + ref_si).reduce([]) do |acc, s|
103
+ next acc unless s.name.casecmp("doi").zero?
104
+
105
+ acc << Docidentifier.new(type: "DOI", content: s.value)
106
+ end
107
+ end
108
+
109
+ # --- Status ---
110
+
111
+ def status # rubocop:disable Metrics/CyclomaticComplexity
112
+ si = @reference.front.series_info&.find(&:status) ||
113
+ @reference.series_info&.find(&:status)
114
+ return unless si
115
+
116
+ stage = Status::Stage.new(content: si.status)
117
+ Status.new(stage: stage)
118
+ end
119
+
120
+ # --- Title / Formattedref ---
121
+
122
+ def title
123
+ return [] unless @reference.front.title
124
+
125
+ [Title.new(content: @reference.front.title.content, language: "en",
126
+ script: "Latn")]
127
+ end
128
+
129
+ def formattedref
130
+ return if @reference.front.title
131
+
132
+ Formattedref.new(content: @reference.anchor)
133
+ end
134
+
135
+ # --- Abstract ---
136
+
137
+ def abstract
138
+ return [] unless @reference.front.abstract&.t
139
+
140
+ @reference.front.abstract.t.map do |t|
141
+ Abstract.new(content: t.content, language: "en", script: "Latn")
142
+ end
143
+ end
144
+
145
+ # --- Contributors ---
146
+
147
+ def contributor
148
+ (@reference.front.author || []).reduce([]) do |acc, author|
149
+ p = person(author)
150
+ o = organization(author)
151
+ next acc unless p || o
152
+
153
+ args = { role: [contributor_role(author)] }
154
+ if p
155
+ args[:person] = p
156
+ else
157
+ args[:organization] = o
158
+ end
159
+ acc << Contributor.new(**args)
160
+ end
161
+ end
162
+
163
+ def contributor_role(author)
164
+ type = author.role || "author"
165
+ Contributor::Role.new(type: type)
166
+ end
167
+
168
+ # --- Person ---
169
+
170
+ def person(author)
171
+ return unless author.fullname || author.surname
172
+
173
+ Person.new(
174
+ name: person_name(author),
175
+ affiliation: person_affiliation(author),
176
+ address: person_address(author),
177
+ phone: contact_phone(author),
178
+ email: contact_email(author),
179
+ uri: contact_uri(author),
180
+ )
181
+ end
182
+
183
+ def person_name(author)
184
+ FullName.new(
185
+ completename: LocalizedString.new(content: author.fullname,
186
+ language: "en"),
187
+ formatted_initials: person_initials(author),
188
+ forename: person_forename(author),
189
+ surname: LocalizedString.new(content: author.surname,
190
+ language: "en"),
191
+ )
192
+ end
193
+
194
+ def person_initials(author)
195
+ return unless author.initials
196
+
197
+ LocalizedString.new(content: author.initials, language: "en")
198
+ end
199
+
200
+ def person_forename(author)
201
+ return [] unless author.initials
202
+
203
+ author.initials.split(/\.-?\s?|\s/).map do |i|
204
+ FullNameType::Forename.new(initial: i, language: "en")
205
+ end
206
+ end
207
+
208
+ def person_affiliation(author)
209
+ org = organization(author)
210
+ return [] unless org
211
+
212
+ [Affiliation.new(organization: org)]
213
+ end
214
+
215
+ # --- Organization ---
216
+
217
+ def organization(author) # rubocop:disable Metrics/AbcSize
218
+ org = author.organization
219
+ return if org.nil? || org.content.empty?
220
+
221
+ name = ORGNAMES[org.content] || org.content
222
+ orgname = TypedLocalizedString.new(content: name, language: "en")
223
+ abbrev = LocalizedString.new(content: org.abbrev, language: "en") if org.abbrev
224
+ Organization.new(
225
+ name: [orgname], abbreviation: abbrev,
226
+ address: org_address(author), phone: contact_phone(author),
227
+ email: contact_email(author), uri: contact_uri(author)
228
+ )
229
+ end
230
+
231
+ # --- Address ---
232
+
233
+ def person_address(author)
234
+ transform_address(author.address)
235
+ end
236
+
237
+ def org_address(author)
238
+ transform_address(author.address)
239
+ end
240
+
241
+ def transform_address(address) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength
242
+ return [] unless address&.postal
243
+
244
+ postal = address.postal
245
+ transform_address_args(
246
+ street: postal.street || [],
247
+ city: postal.city || [],
248
+ region: postal.region || [],
249
+ country: postal.country || [],
250
+ code: postal.code || [],
251
+ postal_line: postal.postal_line || [],
252
+ )
253
+ end
254
+
255
+ def transform_address_args(**args)
256
+ i = 0
257
+ addrs = []
258
+ while args.values.any? { |v| v[i] }
259
+ addrs << create_address(**args.transform_values { |v| v[i] })
260
+ i += 1
261
+ end
262
+ addrs
263
+ end
264
+
265
+ def create_address(**args) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/AbcSize,Metrics/PerceivedComplexity
266
+ street = []
267
+ street << args[:street]&.content if args[:street]
268
+ Address.new(
269
+ street: street,
270
+ city: args[:city]&.content,
271
+ state: args[:region]&.content,
272
+ country: args[:country]&.content,
273
+ postcode: args[:code]&.content,
274
+ formatted_address: args[:postal_line]&.content,
275
+ )
276
+ end
277
+
278
+ # --- Contacts ---
279
+
280
+ def contact_phone(author)
281
+ return [] unless author.address&.phone
282
+
283
+ [Phone.new(content: author.address.phone.content)]
284
+ end
285
+
286
+ def contact_email(author)
287
+ return [] unless author.address&.email
288
+
289
+ author.address.email.map(&:content)
290
+ end
291
+
292
+ def contact_uri(author)
293
+ return [] unless author.address&.uri
294
+
295
+ [Uri.new(content: author.address.uri.content)]
296
+ end
297
+
298
+ # --- Date ---
299
+
300
+ def date # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
301
+ dt = @reference.front&.date
302
+ return [] unless dt && (dt.year || dt.month || dt.day)
303
+
304
+ dparts = [dt.year, month_to_num(dt.month),
305
+ dt.day].compact.reject(&:empty?)
306
+ at = dt.content.empty? ? dparts.join("-") : dt.content
307
+ [Date.new(type: "published", at: at)]
308
+ end
309
+
310
+ def month_to_num(month)
311
+ return unless month
312
+
313
+ ::Date::MONTHNAMES.index(month.capitalize).to_s
314
+ end
315
+
316
+ # --- Series ---
317
+
318
+ def series # rubocop:disable Metrics/CyclomaticComplexity,Metrics/AbcSize
319
+ ref_si = @reference.series_info || []
320
+ front_si = @reference.front.series_info || []
321
+ (ref_si + front_si).map do |si|
322
+ next if si.name == "DOI" || si.stream || si.status
323
+
324
+ t = Title.new(content: si.name, language: "en", script: "Latn")
325
+ Series.new(title: [t], number: si.value, type: "main")
326
+ end.compact
327
+ end
328
+
329
+ # --- Keyword ---
330
+
331
+ def keyword
332
+ (@reference.front.keyword || []).map do |kw|
333
+ vocab = LocalizedString.new(content: kw.content, language: "en",
334
+ script: "Latn")
335
+ Keyword.new(vocab: vocab)
336
+ end
337
+ end
338
+
339
+ # --- Workgroup Contributors ---
340
+
341
+ def workgroup_contributors
342
+ return [] unless @reference.front.workgroup
343
+
344
+ @reference.front.workgroup.map do |wg|
345
+ Contributor.new(
346
+ role: [Contributor::Role.new(
347
+ type: "author",
348
+ description: [LocalizedMarkedUpString.new(content: "committee")],
349
+ )],
350
+ organization: Organization.new(
351
+ subdivision: [Subdivision.new(
352
+ type: "workgroup",
353
+ name: [TypedLocalizedString.new(content: wg.content)],
354
+ )],
355
+ ),
356
+ )
357
+ end
358
+ end
359
+
360
+ # --- Ext ---
361
+
362
+ def ext
363
+ dt = doctype
364
+ return unless dt
365
+
366
+ namespace::Ext.new doctype: dt, flavor: falvor
367
+ end
368
+
369
+ def doctype
370
+ type = case @reference.anchor
371
+ when /I-D/ then "internet-draft"
372
+ when /IEEE/ then "ieee"
373
+ else "rfc"
374
+ end
375
+ namespace::Doctype.new content: type
376
+ end
377
+
378
+ def falvor = "ietf"
379
+ end
380
+ end
381
+ end
382
+ end
383
+ end
@@ -0,0 +1,71 @@
1
+ module Relaton
2
+ module Bib
3
+ module Converter
4
+ module BibXml
5
+ class FromRfcxmlReferencegroup
6
+ include NamespaceHelper
7
+
8
+ def initialize(reference)
9
+ @reference = reference
10
+ end
11
+
12
+ def transform
13
+ namespace::ItemData.new(
14
+ docnumber: @reference.anchor.sub(/^\w+\./, ""),
15
+ type: "standard",
16
+ docidentifier: docidentifiers,
17
+ source: source,
18
+ relation: relation,
19
+ )
20
+ end
21
+
22
+ private
23
+
24
+ def docidentifiers
25
+ [create_docid(@reference.anchor, primary: true)]
26
+ end
27
+
28
+ def create_docid(id, primary: false) # rubocop:disable Metrics/MethodLength
29
+ pref, num = id_to_pref_num(id)
30
+ if RFCPREFIXES.include?(pref)
31
+ pid = "#{pref} #{num.sub(/^-?0+/, '')}"
32
+ type = pubid_type(id)
33
+ elsif %w[I-D draft].include?(pref)
34
+ pid = "draft-#{num}"
35
+ type = "Internet-Draft"
36
+ else
37
+ pid = pref ? "#{pref} #{num}" : id
38
+ type = pubid_type(id)
39
+ end
40
+ Docidentifier.new(type: type, content: pid, primary: primary)
41
+ end
42
+
43
+ def pubid_type(id)
44
+ id_to_pref_num(id)&.first
45
+ end
46
+
47
+ def id_to_pref_num(id)
48
+ tn = FromRfcxml::PREF_NUM_RE.match id
49
+ tn && tn.to_a[1..2]
50
+ end
51
+
52
+ def source
53
+ s = []
54
+ if @reference.target
55
+ s << Uri.new(type: "src",
56
+ content: @reference.target)
57
+ end
58
+ s
59
+ end
60
+
61
+ def relation
62
+ (@reference.reference || []).map do |ref|
63
+ item = namespace::Converter::BibXml::FromRfcxml.new(ref).transform
64
+ Relation.new(type: "includes", bibitem: item)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end