relaton-bib 2.0.0.pre.alpha.1 → 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 (63) 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 -0
  6. data/README.adoc +14 -2
  7. data/grammars/basicdoc.rng +75 -45
  8. data/grammars/biblio-standoc.rng +37 -56
  9. data/grammars/biblio.rng +68 -36
  10. data/grammars/versions.json +25 -25
  11. data/lib/relaton/bib/converter/asciibib/to_asciibib.rb +663 -0
  12. data/lib/relaton/bib/converter/asciibib.rb +13 -0
  13. data/lib/relaton/bib/converter/bibtex/from_bibtex.rb +245 -0
  14. data/lib/relaton/bib/converter/bibtex/to_bibtex.rb +341 -0
  15. data/lib/relaton/bib/converter/bibtex.rb +23 -0
  16. data/lib/relaton/bib/converter/bibxml/from_rfcxml.rb +383 -0
  17. data/lib/relaton/bib/converter/bibxml/from_rfcxml_referencegroup.rb +71 -0
  18. data/lib/relaton/bib/{renderer/rfc.rb → converter/bibxml/to_rfcxml.rb} +100 -92
  19. data/lib/relaton/bib/converter/bibxml/to_rfcxml_referencegroup.rb +52 -0
  20. data/lib/relaton/bib/converter/bibxml.rb +51 -0
  21. data/lib/relaton/bib/hash_parser_v1.rb +121 -78
  22. data/lib/relaton/bib/item_data.rb +83 -22
  23. data/lib/relaton/bib/model/abstract.rb +16 -0
  24. data/lib/relaton/bib/model/date.rb +6 -4
  25. data/lib/relaton/bib/model/depiction.rb +2 -0
  26. data/lib/relaton/bib/model/ext.rb +0 -3
  27. data/lib/relaton/bib/model/formattedref.rb +43 -0
  28. data/lib/relaton/bib/model/ics.rb +9 -0
  29. data/lib/relaton/bib/model/item.rb +12 -4
  30. data/lib/relaton/bib/model/item_base.rb +17 -9
  31. data/lib/relaton/bib/model/keyword.rb +3 -3
  32. data/lib/relaton/bib/model/logo.rb +2 -0
  33. data/lib/relaton/bib/model/organization_type.rb +1 -1
  34. data/lib/relaton/bib/model/relation.rb +2 -2
  35. data/lib/relaton/bib/model/series.rb +5 -3
  36. data/lib/relaton/bib/model/subdivision.rb +2 -0
  37. data/lib/relaton/bib/model/title.rb +38 -0
  38. data/lib/relaton/bib/model/type/string_date.rb +48 -0
  39. data/lib/relaton/bib/model/validity.rb +3 -3
  40. data/lib/relaton/bib/namespace_helper.rb +21 -0
  41. data/lib/relaton/bib/version.rb +1 -1
  42. data/lib/relaton/bib.rb +5 -82
  43. data/relaton-bib.gemspec +5 -2
  44. metadata +63 -29
  45. data/lib/relaton/bib/model/editorial_group.rb +0 -14
  46. data/lib/relaton/bib/model/technical_committee.rb +0 -14
  47. data/lib/relaton/bib/model/workgroup.rb +0 -20
  48. data/lib/relaton/bib/parser/rfc_address.rb +0 -60
  49. data/lib/relaton/bib/parser/rfc_contacts.rb +0 -35
  50. data/lib/relaton/bib/parser/rfc_docidentifier.rb +0 -70
  51. data/lib/relaton/bib/parser/rfc_organization.rb +0 -31
  52. data/lib/relaton/bib/parser/rfc_person.rb +0 -42
  53. data/lib/relaton/bib/parser/rfc_reference.rb +0 -143
  54. data/lib/relaton/bib/parser/rfc_referencegroup.rb +0 -45
  55. data/lib/relaton/bib/parser/rfc_shared.rb +0 -23
  56. data/lib/relaton/bib/renderer/bibtex_builder.rb +0 -348
  57. data/lib/relaton/bibtex_parser.rb +0 -243
  58. data/lib/relaton/bibxml_parser.rb +0 -419
  59. data/lib/relaton/deep_dup.rb +0 -30
  60. data/lib/relaton/hit.rb +0 -52
  61. data/lib/relaton/hit_collection.rb +0 -105
  62. data/lib/relaton/workers_pool.rb +0 -43
  63. data/lib/relaton/xml_parser.rb +0 -716
@@ -1,27 +1,27 @@
1
1
  module Relaton
2
2
  module Bib
3
- module Renderer
4
- module Rfc
5
- def self.transform(item, include_keywords: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
6
- if item.docnumber&.match(/^BCP/) ||
7
- (item.docidentifier.detect(&:primary) || item.docidentifier[0])&.content&.include?("BCP")
8
- Refrencegroup.new(item, include_keywords: include_keywords).transform
9
- else
10
- Reference.new(item, include_keywords: include_keywords).transform
11
- end
12
- end
13
-
14
- module Shared
3
+ module Converter
4
+ module BibXml
5
+ class ToRfcxml
15
6
  def initialize(item, include_keywords: true)
16
7
  @item = item
17
8
  @include_keywords = include_keywords
18
9
  end
19
10
 
20
- def create_anchor
21
- docid = @item.docidentifier.detect(&:primary) || @item.docidentifier[0]
22
- return unless docid
11
+ def transform
12
+ model = ::Rfcxml::V3::Reference.new
13
+ model.anchor = @item.docnumber || derive_anchor
14
+ model.target = create_target
15
+ model.front = create_front
16
+ model.format = create_format
17
+ model
18
+ end
23
19
 
24
- docid.content.to_s.sub(/^(RFC|BCP|FYI|STD) /, '\1').sub(/^\w+\./, "")
20
+ private
21
+
22
+ def derive_anchor
23
+ di = @item.docidentifier.detect(&:primary) || @item.docidentifier[0]
24
+ di&.content&.to_s&.gsub(" ", ".")
25
25
  end
26
26
 
27
27
  def create_target
@@ -31,46 +31,10 @@ module Relaton
31
31
 
32
32
  target.content.to_s
33
33
  end
34
- end
35
-
36
- class Refrencegroup
37
- include Shared
38
-
39
- def transform
40
- reference = @item.relation&.each_with_object([]) do |rel, refs|
41
- next unless rel.type == "includes"
42
-
43
- refs << Reference.new(rel.bibitem, include_keywords: @include_keywords).transform
44
- end
45
- Rfcxml::V3::Referencegroup.new(
46
- anchor: create_anchor,
47
- target: create_target,
48
- reference: reference,
49
- )
50
- end
51
- end
52
-
53
- class Reference
54
- include Shared
55
-
56
- ORGNAMES = {
57
- "IEEE" => "Institute of Electrical and Electronics Engineers",
58
- "W3C" => "World Wide Web Consortium",
59
- "3GPP" => "3rd Generation Partnership Project",
60
- }.freeze
61
-
62
- def transform
63
- model = ::Rfcxml::V3::Reference.new
64
- model.anchor = create_anchor
65
- model.target = create_target
66
- model.front = create_front
67
- model.format = create_format
68
- model
69
- end
70
34
 
71
35
  def create_front # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
72
36
  front = ::Rfcxml::V3::Front.new
73
- front.title = Rfcxml::V3::Title.new(content: @item.title[0].content)
37
+ front.title = Rfcxml::V3::Title.new(content: @item.title[0].content) if @item.title.any?
74
38
  front.series_info = create_seriesinfo
75
39
  front.author = create_authors
76
40
  front.date = create_date
@@ -81,13 +45,14 @@ module Relaton
81
45
  end
82
46
 
83
47
  def create_seriesinfo
84
- docidentifier_to_seresinfo + series_to_seriesinfo
48
+ docidentifier_to_seriesinfo + series_to_seriesinfo
85
49
  end
86
50
 
87
- def docidentifier_to_seresinfo
51
+ def docidentifier_to_seriesinfo
88
52
  @item.docidentifier.each_with_object([]) do |di, si|
89
53
  if di.type == "DOI" && di.scope != "trademark"
90
- si << Rfcxml::V3::SeriesInfo.new(name: di.type, value: di.content)
54
+ si << Rfcxml::V3::SeriesInfo.new(name: di.type,
55
+ value: di.content)
91
56
  end
92
57
  end
93
58
  end
@@ -99,12 +64,13 @@ module Relaton
99
64
  s.title.find { |t| t.content != "DOI" }.content
100
65
  end.each_with_object([]) do |s, si|
101
66
  title = s.title.find { |t| t.content != "DOI" }
102
- si << Rfcxml::V3::SeriesInfo.new(name: title.content, value: s.number)
67
+ si << Rfcxml::V3::SeriesInfo.new(name: title.content,
68
+ value: s.number)
103
69
  end
104
70
  end
105
71
 
106
72
  def create_authors # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
107
- @item.contributor.map do |contrib|
73
+ @item.contributor.reject { |c| committee_contributor?(c) }.map do |contrib|
108
74
  role = "editor" if contrib.role.detect { |r| r.type == "editor" }
109
75
  Rfcxml::V3::Author.new(
110
76
  role: role,
@@ -123,9 +89,7 @@ module Relaton
123
89
  if person.name.completename
124
90
  person.name.completename.content
125
91
  elsif person.name.forename.any?
126
- parts = person.name.forename.map do |n|
127
- n.content || n.initial
128
- end
92
+ parts = person.name.forename.map { |n| n.content || n.initial }
129
93
  parts << person.name.surname.content if person.name.surname
130
94
  parts.join(" ")
131
95
  end
@@ -137,7 +101,9 @@ module Relaton
137
101
  if person.name.formatted_initials
138
102
  person.name.formatted_initials.content
139
103
  elsif person.name.forename.any?
140
- person.name.forename.map { |f| "#{f.initial || f.content[0]}." }.join " "
104
+ person.name.forename.map do |f|
105
+ "#{f.initial || f.content[0]}."
106
+ end.join " "
141
107
  end
142
108
  end
143
109
 
@@ -147,21 +113,33 @@ module Relaton
147
113
  person.name.surname&.content
148
114
  end
149
115
 
150
- def create_organization(contrib, _role = []) # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/AbcSize
151
- org = contrib.organization || contrib.person && contrib.person.affiliation[0]&.organization
116
+ def create_organization(contrib) # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/AbcSize
117
+ org = contrib_org(contrib)
152
118
  return unless org
153
119
 
154
120
  abbrev = org.abbreviation&.content
121
+ orgname = resolve_orgname(org, abbrev)
122
+ Rfcxml::V3::Organization.new(
123
+ content: orgname, abbrev: abbrev,
124
+ )
125
+ end
126
+
127
+ def contrib_org(contrib)
128
+ contrib.organization ||
129
+ (contrib.person && contrib.person.affiliation[0]&.organization)
130
+ end
131
+
132
+ def resolve_orgname(org, abbrev)
155
133
  orgname = org.name&.first&.content
156
- orgname = if ORGNAMES.key?(abbrev) then abbrev
157
- else ORGNAMES.key(orgname) || orgname || abbrev
158
- end
159
- Rfcxml::V3::Organization.new(content: orgname, abbrev: abbrev)
134
+ if ORGNAMES.key?(abbrev) then abbrev
135
+ else ORGNAMES.key(orgname) || orgname || abbrev
136
+ end
160
137
  end
161
138
 
162
139
  def create_address(contrib) # rubocop:disable Metrics/AbcSize
163
140
  entity = contrib.person || contrib.organization
164
- return unless entity.address.any? || entity.phone.any? || entity.email.any? || entity.uri.any?
141
+ return unless entity.address.any? || entity.phone.any? ||
142
+ entity.email.any? || entity.uri.any?
165
143
 
166
144
  Rfcxml::V3::Address.new(
167
145
  postal: address_postal(entity),
@@ -190,26 +168,34 @@ module Relaton
190
168
  end
191
169
 
192
170
  def address_cities(entity)
193
- entity.address.each_with_object([]) do |address, cities|
194
- cities << Rfcxml::V3::City.new(content: address.city) if address.city
171
+ entity.address.each_with_object([]) do |addr, cities|
172
+ next unless addr.city
173
+
174
+ cities << Rfcxml::V3::City.new(content: addr.city)
195
175
  end
196
176
  end
197
177
 
198
178
  def address_postcodes(entity)
199
- entity.address.each_with_object([]) do |address, codes|
200
- codes << Rfcxml::V3::Code.new(content: address.postcode) if address.postcode
179
+ entity.address.each_with_object([]) do |addr, codes|
180
+ next unless addr.postcode
181
+
182
+ codes << Rfcxml::V3::Code.new(content: addr.postcode)
201
183
  end
202
184
  end
203
185
 
204
186
  def address_countries(entity)
205
- entity.address.each_with_object([]) do |address, countries|
206
- countries << Rfcxml::V3::Country.new(content: address.country) if address.country
187
+ entity.address.each_with_object([]) do |addr, countries|
188
+ next unless addr.country
189
+
190
+ countries << Rfcxml::V3::Country.new(content: addr.country)
207
191
  end
208
192
  end
209
193
 
210
194
  def address_states(entity)
211
- entity.address.each_with_object([]) do |address, states|
212
- states << Rfcxml::V3::Region.new(content: address.state) if address.state
195
+ entity.address.each_with_object([]) do |addr, states|
196
+ next unless addr.state
197
+
198
+ states << Rfcxml::V3::Region.new(content: addr.state)
213
199
  end
214
200
  end
215
201
 
@@ -222,8 +208,12 @@ module Relaton
222
208
  end
223
209
 
224
210
  def address_postal_lines(entity)
225
- entity.address.each_with_object([]) do |address, plines|
226
- plines << Rfcxml::V3::PostalLine.new(content: address.formatted_address) if address.formatted_address
211
+ entity.address.each_with_object([]) do |addr, plines|
212
+ next unless addr.formatted_address
213
+
214
+ plines << Rfcxml::V3::PostalLine.new(
215
+ content: addr.formatted_address,
216
+ )
227
217
  end
228
218
  end
229
219
 
@@ -259,35 +249,53 @@ module Relaton
259
249
  Rfcxml::V3::Date.new(**args)
260
250
  end
261
251
 
252
+ def committee_contributor?(contrib)
253
+ contrib.role.any? do |r|
254
+ r.type == "author" &&
255
+ r.description.any? { |d| d.content == "committee" }
256
+ end
257
+ end
258
+
262
259
  def create_workgroup
263
- @item.ext&.editorialgroup&.technical_committee&.each do |tc|
264
- Rfcxml::V3::Workgroup.new content: tc.content
260
+ @item.contributor.each_with_object([]) do |contrib, wgs|
261
+ next unless committee_contributor?(contrib)
262
+
263
+ contrib.organization&.subdivision&.each do |sd|
264
+ sd.name.each do |n|
265
+ wgs << Rfcxml::V3::Workgroup.new(content: n.content)
266
+ end
267
+ end
265
268
  end
266
269
  end
267
270
 
268
271
  def create_keyword
269
272
  @item.keyword.inject([]) do |a, k|
270
- a + k.taxon.map { |t| Rfcxml::V3::Keyword.new content: t.content }
273
+ if k.vocab
274
+ a + [Rfcxml::V3::Keyword.new(content: k.vocab.content)]
275
+ else
276
+ a + k.taxon.map { |t| Rfcxml::V3::Keyword.new(content: t.content) }
277
+ end
271
278
  end
272
279
  end
273
280
 
274
281
  def create_abstract
275
282
  return unless @item.abstract.any?
276
283
 
277
- Rfcxml::V3::Abstract.new t: [create_text]
284
+ content = @item.abstract[0].content
285
+ Rfcxml::V3::Abstract.new(
286
+ t: [Rfcxml::V3::Text.new(content: content)],
287
+ )
278
288
  end
279
289
 
280
- def create_text
281
- Rfcxml::V3::Text.new content: @item.abstract[0].content
282
- end
290
+ FORMAT_TYPES = %w[TXT HTML PDF XML DOC].freeze
283
291
 
284
- def create_format
285
- @item.source.reduce([]) do |a, l|
286
- next a unless l.type.casecmp("TXT").zero? || l.type.casecmp("HTML").zero? ||
287
- l.type.casecmp("PDF").zero? || l.type.casecmp("XML").zero? ||
288
- l.type.casecmp("DOC").zero?
292
+ def create_format # rubocop:disable Metrics/AbcSize
293
+ @item.source.each_with_object([]) do |l, a|
294
+ next unless FORMAT_TYPES.any? { |ft| l.type.casecmp(ft).zero? }
289
295
 
290
- a << Rfcxml::V3::Format.new(type: l.type, target: l.content)
296
+ a << Rfcxml::V3::Format.new(
297
+ type: l.type, target: l.content,
298
+ )
291
299
  end
292
300
  end
293
301
  end
@@ -0,0 +1,52 @@
1
+ module Relaton
2
+ module Bib
3
+ module Converter
4
+ module BibXml
5
+ class ToRfcxmlReferencegroup
6
+ def initialize(item, include_keywords: true)
7
+ @item = item
8
+ @include_keywords = include_keywords
9
+ end
10
+
11
+ def transform
12
+ Rfcxml::V3::Referencegroup.new(
13
+ anchor: create_anchor,
14
+ target: create_target,
15
+ reference: build_references,
16
+ )
17
+ end
18
+
19
+ private
20
+
21
+ def build_references
22
+ @item.relation&.each_with_object([]) do |rel, refs|
23
+ next unless rel.type == "includes"
24
+
25
+ converter = ToRfcxml.new(
26
+ rel.bibitem, include_keywords: @include_keywords
27
+ )
28
+ refs << converter.transform
29
+ end
30
+ end
31
+
32
+ def create_anchor
33
+ docid = @item.docidentifier.detect(&:primary) ||
34
+ @item.docidentifier[0]
35
+ return unless docid
36
+
37
+ id = docid.content.to_s
38
+ id.sub(/^(RFC|BCP|FYI|STD) /, '\1').sub(/^\w+\./, "")
39
+ end
40
+
41
+ def create_target
42
+ target = @item.source.detect { |l| l.type.casecmp("src").zero? } ||
43
+ @item.source.detect { |l| l.type.casecmp("doi").zero? }
44
+ return unless target
45
+
46
+ target.content.to_s
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,51 @@
1
+ require_relative "bibxml/to_rfcxml"
2
+ require_relative "bibxml/to_rfcxml_referencegroup"
3
+ require_relative "bibxml/from_rfcxml"
4
+ require_relative "bibxml/from_rfcxml_referencegroup"
5
+
6
+ module Relaton
7
+ module Bib
8
+ module Converter
9
+ module BibXml
10
+ ORGNAMES = {
11
+ "IEEE" => "Institute of Electrical and Electronics Engineers",
12
+ "W3C" => "World Wide Web Consortium",
13
+ "3GPP" => "3rd Generation Partnership Project",
14
+ }.freeze
15
+
16
+ RFCPREFIXES = %w[RFC BCP FYI STD].freeze
17
+
18
+ # Forward: ItemData -> Rfcxml model
19
+ def self.from_item(item, include_keywords: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
20
+ if bcp?(item)
21
+ ToRfcxmlReferencegroup.new(
22
+ item, include_keywords: include_keywords
23
+ ).transform
24
+ else
25
+ ToRfcxml.new(
26
+ item, include_keywords: include_keywords
27
+ ).transform
28
+ end
29
+ end
30
+
31
+ def self.bcp?(item) # rubocop:disable Metrics/CyclomaticComplexity
32
+ item.docnumber&.match(/^BCP/) ||
33
+ (item.docidentifier.detect(&:primary) ||
34
+ item.docidentifier[0])&.content&.to_s&.include?("BCP")
35
+ end
36
+ private_class_method :bcp?
37
+
38
+ # Reverse: XML string -> ItemData
39
+ def self.to_item(xml)
40
+ if xml.include?("<referencegroup") || xml.include?("<Referencegroup")
41
+ referencegroup = Rfcxml::V3::Referencegroup.from_xml(xml)
42
+ FromRfcxmlReferencegroup.new(referencegroup).transform
43
+ else
44
+ reference = Rfcxml::V3::Reference.from_xml(xml)
45
+ FromRfcxml.new(reference).transform
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end