relaton-bib 1.9.2 → 1.9.6
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/README.adoc +51 -1
- data/lib/relaton_bib/bibliographic_item.rb +165 -1
- data/lib/relaton_bib/bibxml_parser.rb +345 -0
- data/lib/relaton_bib/contributor.rb +1 -1
- data/lib/relaton_bib/editorial_group.rb +1 -1
- data/lib/relaton_bib/localized_string.rb +1 -1
- data/lib/relaton_bib/organization.rb +1 -1
- data/lib/relaton_bib/version.rb +1 -1
- data/lib/relaton_bib/xml_parser.rb +1 -1
- data/lib/relaton_bib.rb +2 -0
- data/relaton-bib.gemspec +3 -0
- metadata +45 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04d616ee7971147c734b6e0520531c4e9b529e76b53c22a98dca1c36b6f49285
|
|
4
|
+
data.tar.gz: 2762c1d26b7a07888c3605939ae18de1b6f2ef44da7f1513dc779e3207ff176f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: beffce777ed3ef33181363edaf57a8e1efced984bcc19ffae88eaa8087a92e48d044b042686dc4b5e6e545f41fe7f67c1eb573abaddc4e056adc439643f0b12a
|
|
7
|
+
data.tar.gz: 44d2809601f70d17db2f5601a29614720fa13a0cfa3fbb1665406e0c4396390862688fc41cbd26de421e19efb5643a61e184b73bf2cb47be567c7aa4788ad4a4
|
data/README.adoc
CHANGED
|
@@ -366,6 +366,18 @@ RelatonBib::BibliographicItem.from_hash hash
|
|
|
366
366
|
...
|
|
367
367
|
----
|
|
368
368
|
|
|
369
|
+
=== Create bibliographic item from BibXML
|
|
370
|
+
|
|
371
|
+
[source,ruby]
|
|
372
|
+
----
|
|
373
|
+
bibxml = File.read "spec/examples/rfc.xml"
|
|
374
|
+
=> <reference anchor=...
|
|
375
|
+
|
|
376
|
+
RelatonBib::BibXMLParser.parse bibxml
|
|
377
|
+
=> #<RelatonBib::BibliographicItem:0x00007f9d0c75b268
|
|
378
|
+
...
|
|
379
|
+
----
|
|
380
|
+
|
|
369
381
|
=== Export bibliographic item to Hash
|
|
370
382
|
|
|
371
383
|
[source,ruby]
|
|
@@ -416,6 +428,45 @@ title.format:: text/plain
|
|
|
416
428
|
...
|
|
417
429
|
----
|
|
418
430
|
|
|
431
|
+
=== Export bibliographic item to BibXML (RFC)
|
|
432
|
+
|
|
433
|
+
[source,ruby]
|
|
434
|
+
----
|
|
435
|
+
item.to_bibxml
|
|
436
|
+
<reference anchor="ISOTC211" target="https://www.iso.org/standard/53798.html">
|
|
437
|
+
<front>
|
|
438
|
+
<title>Geographic information</title>
|
|
439
|
+
<seriesInfo name="DOI" value="10.17487/rfc1149"/>
|
|
440
|
+
<seriesInfo name="Internet-Draft" value="draft-ietf-somewg-someprotocol-07"/>
|
|
441
|
+
<seriesInfo name="RFC" value="4"/>
|
|
442
|
+
<author>
|
|
443
|
+
<organization abbrev="ISO">International Organization for Standardization</organization>
|
|
444
|
+
</author>
|
|
445
|
+
<author fullname="A. Bierman">
|
|
446
|
+
<organization abbrev="IETF">IETF</organization>
|
|
447
|
+
<address>
|
|
448
|
+
<postal>123456</postal>
|
|
449
|
+
<phone>223322</phone>
|
|
450
|
+
</address>
|
|
451
|
+
</author>
|
|
452
|
+
<author role="editor">
|
|
453
|
+
<organization abbrev="IETF">IETF</organization>
|
|
454
|
+
</author>
|
|
455
|
+
<author initials="A." surname="Bierman">
|
|
456
|
+
<organization abbrev="IETF">IETF</organization>
|
|
457
|
+
<address>
|
|
458
|
+
<postal>123456</postal>
|
|
459
|
+
<phone>223322</phone>
|
|
460
|
+
</address>
|
|
461
|
+
</author>
|
|
462
|
+
<author>
|
|
463
|
+
<organization>Institution</organization>
|
|
464
|
+
</author>
|
|
465
|
+
<date year="2014" month="April" day="1"/>
|
|
466
|
+
</front>
|
|
467
|
+
</reference>
|
|
468
|
+
----
|
|
469
|
+
|
|
419
470
|
== Development
|
|
420
471
|
|
|
421
472
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
@@ -429,4 +480,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/metano
|
|
|
429
480
|
== License
|
|
430
481
|
|
|
431
482
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
432
|
-
|
|
@@ -325,6 +325,23 @@ module RelatonBib
|
|
|
325
325
|
end
|
|
326
326
|
end
|
|
327
327
|
|
|
328
|
+
#
|
|
329
|
+
# Render BibXML (RFC)
|
|
330
|
+
#
|
|
331
|
+
# @param [Nokogiri::XML::Builder, nil] builder
|
|
332
|
+
#
|
|
333
|
+
# @return [String] <description>
|
|
334
|
+
#
|
|
335
|
+
def to_bibxml(builder = nil)
|
|
336
|
+
if builder
|
|
337
|
+
render_bibxml builder
|
|
338
|
+
else
|
|
339
|
+
Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
|
340
|
+
render_bibxml xml
|
|
341
|
+
end
|
|
342
|
+
end.doc.root.to_xml
|
|
343
|
+
end
|
|
344
|
+
|
|
328
345
|
# @return [Hash]
|
|
329
346
|
def to_hash # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
330
347
|
hash = {}
|
|
@@ -740,8 +757,155 @@ module RelatonBib
|
|
|
740
757
|
xml[:type] = type if type
|
|
741
758
|
xml
|
|
742
759
|
end
|
|
760
|
+
# rubocop:enable Style/NestedParenthesizedCalls, Metrics/BlockLength
|
|
761
|
+
|
|
762
|
+
#
|
|
763
|
+
# Render BibXML (RFC)
|
|
764
|
+
#
|
|
765
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
766
|
+
#
|
|
767
|
+
def render_bibxml(builder)
|
|
768
|
+
target = link.detect { |l| l.type == "src" } || link.detect { |l| l.type == "doi" }
|
|
769
|
+
bxml = builder.reference(anchor: anchor) do |xml|
|
|
770
|
+
xml.front do
|
|
771
|
+
xml.title title[0].title.content if title.any?
|
|
772
|
+
render_seriesinfo xml
|
|
773
|
+
render_authors xml
|
|
774
|
+
render_date xml
|
|
775
|
+
render_workgroup xml
|
|
776
|
+
render_keyword xml
|
|
777
|
+
render_abstract xml
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
bxml[:target] = target.content.to_s if target
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
def anchor
|
|
784
|
+
did = docidentifier.detect { |di| di.type == "rfc-anchor" }
|
|
785
|
+
return did.id if did
|
|
786
|
+
|
|
787
|
+
type = docidentifier[0].type
|
|
788
|
+
"#{type}.#{docnumber}"
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def render_keyword(builder)
|
|
792
|
+
keyword.each do |kw|
|
|
793
|
+
builder.keyword kw.content
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
def render_workgroup(builder)
|
|
798
|
+
editorialgroup&.technical_committee&.each do |tc|
|
|
799
|
+
builder.workgroup tc.workgroup.name
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
804
|
+
def render_abstract(builder)
|
|
805
|
+
return unless abstract.any?
|
|
806
|
+
|
|
807
|
+
builder.abstract { |xml| xml << abstract[0].content.gsub(/(<\/?)p(>)/, '\1t\2') }
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
811
|
+
def render_date(builder)
|
|
812
|
+
dt = date.detect { |d| d.type == "published" }
|
|
813
|
+
return unless dt
|
|
814
|
+
|
|
815
|
+
elm = builder.date
|
|
816
|
+
y = dt.on(:year) || dt.from(:year) || dt.to(:year)
|
|
817
|
+
elm[:year] = y if y
|
|
818
|
+
m = dt.on(:month) || dt.from(:month) || dt.to(:month)
|
|
819
|
+
elm[:month] = Date::MONTHNAMES[m] if m
|
|
820
|
+
d = dt.on(:day) || dt.from(:day) || dt.to(:day)
|
|
821
|
+
elm[:day] = d if d
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
825
|
+
def render_seriesinfo(builder)
|
|
826
|
+
docidentifier.each do |di|
|
|
827
|
+
if BibXMLParser::SERIESINFONAMES.include? di.type
|
|
828
|
+
builder.seriesInfo(name: di.type, value: di.id)
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
snames = ["ANSI", "BCP", "RFC", "STD"]
|
|
832
|
+
series.each do |s|
|
|
833
|
+
next unless s.title && snames.include?(s.title.title.to_s)
|
|
834
|
+
|
|
835
|
+
si = builder.seriesInfo(name: s.title.title.to_s)
|
|
836
|
+
si[:value] = s.number if s.number
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
841
|
+
def render_authors(builder)
|
|
842
|
+
contributor.each do |c|
|
|
843
|
+
builder.author do |xml|
|
|
844
|
+
xml.parent[:role] = "editor" if c.role.detect { |r| r.type == "editor" }
|
|
845
|
+
if c.entity.is_a?(Person) then render_person xml, c.entity
|
|
846
|
+
else render_organization xml, c.entity
|
|
847
|
+
end
|
|
848
|
+
render_address xml, c
|
|
849
|
+
end
|
|
850
|
+
end
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
854
|
+
# @param [RelatonBib::ContributionInfo] contrib
|
|
855
|
+
def render_address(builder, contrib)
|
|
856
|
+
# addr = contrib.entity.contact.reject do |cn|
|
|
857
|
+
# cn.is_a?(Address) && cn.postcode.nil?
|
|
858
|
+
# end
|
|
859
|
+
if contrib.entity.contact.any?
|
|
860
|
+
builder.address do |xml|
|
|
861
|
+
address = contrib.entity.contact.detect { |cn| cn.is_a? Address }
|
|
862
|
+
if address
|
|
863
|
+
xml.postal do
|
|
864
|
+
xml.city address.city if address.city
|
|
865
|
+
xml.code address.postcode if address.postcode
|
|
866
|
+
xml.country address.country if address.country
|
|
867
|
+
xml.region address.state if address.state
|
|
868
|
+
xml.street address.street[0] if address.street.any?
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
render_contact xml, contrib.entity.contact
|
|
872
|
+
end
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
877
|
+
# @param [Array<RelatonBib::Address, RelatonBib::Contact>] addr
|
|
878
|
+
def render_contact(builder, addr)
|
|
879
|
+
%w[phone email uri].each do |type|
|
|
880
|
+
cont = addr.detect { |cn| cn.is_a?(Contact) && cn.type == type }
|
|
881
|
+
builder.send type, cont.value if cont
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
886
|
+
# @param [RelatonBib::Person] person
|
|
887
|
+
def render_person(builder, person)
|
|
888
|
+
render_organization builder, person.affiliation.first&.organization
|
|
889
|
+
if person.name.completename
|
|
890
|
+
builder.parent[:fullname] = person.name.completename
|
|
891
|
+
end
|
|
892
|
+
if person.name.initial.any?
|
|
893
|
+
builder.parent[:initials] = person.name.initial.map(&:content).join
|
|
894
|
+
end
|
|
895
|
+
if person.name.surname
|
|
896
|
+
builder.parent[:surname] = person.name.surname
|
|
897
|
+
end
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
# @param [Nokogiri::XML::Builder] builder
|
|
901
|
+
# @param [RelatonBib::Organization] org
|
|
902
|
+
def render_organization(builder, org)
|
|
903
|
+
return unless org
|
|
904
|
+
|
|
905
|
+
o = builder.organization org.name.first&.content
|
|
906
|
+
o[:abbrev] = org.abbreviation.content if org.abbreviation
|
|
907
|
+
end
|
|
743
908
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
744
909
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
745
|
-
# rubocop:enable Style/NestedParenthesizedCalls, Metrics/BlockLength
|
|
746
910
|
end
|
|
747
911
|
end
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
module RelatonBib
|
|
2
|
+
module BibXMLParser
|
|
3
|
+
SERIESINFONAMES = ["DOI", "Internet-Draft"].freeze
|
|
4
|
+
FLAVOR = nil
|
|
5
|
+
|
|
6
|
+
def parse(bibxml, url: nil, is_relation: false, ver: nil)
|
|
7
|
+
doc = Nokogiri::XML bibxml
|
|
8
|
+
fetch_rfc doc.at("/referencegroup", "/reference"), url: url, is_relation: is_relation, ver: ver
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @param reference [Nokogiri::XML::Element, nil]
|
|
12
|
+
# @param is_relation [Boolean] don't add fetched date for relation if true
|
|
13
|
+
# @param url [String, nil]
|
|
14
|
+
# @param ver [String, nil] Internet Draft version
|
|
15
|
+
# @return [RelatonBib::BibliographicItem]
|
|
16
|
+
def fetch_rfc(reference, is_relation: false, url: nil, ver: nil) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
17
|
+
return unless reference
|
|
18
|
+
|
|
19
|
+
hash = {
|
|
20
|
+
is_relation: is_relation,
|
|
21
|
+
docnumber: docnumber(reference),
|
|
22
|
+
type: "standard",
|
|
23
|
+
docid: docids(reference, ver),
|
|
24
|
+
status: status(reference),
|
|
25
|
+
language: [language(reference)],
|
|
26
|
+
script: ["Latn"],
|
|
27
|
+
link: link(reference, url, ver),
|
|
28
|
+
title: titles(reference),
|
|
29
|
+
formattedref: formattedref(reference),
|
|
30
|
+
abstract: abstracts(reference),
|
|
31
|
+
contributor: contributors(reference),
|
|
32
|
+
relation: relations(reference),
|
|
33
|
+
date: dates(reference),
|
|
34
|
+
editorialgroup: editorialgroup(reference),
|
|
35
|
+
series: series(reference),
|
|
36
|
+
keyword: reference.xpath("front/keyword").map(&:text),
|
|
37
|
+
doctype: doctype(reference[:anchor]),
|
|
38
|
+
}
|
|
39
|
+
# hash[:fetched] = Date.today.to_s unless is_relation
|
|
40
|
+
bib_item(**hash)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def docnumber(reference)
|
|
44
|
+
reference[:anchor]&.sub(/^\w+\./, "")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @param attrs [Hash]
|
|
48
|
+
# @return [RelatonBib::IetfBibliographicItem]
|
|
49
|
+
def bib_item(**attrs)
|
|
50
|
+
# attrs[:place] = ["Fremont, CA"]
|
|
51
|
+
BibliographicItem.new(**attrs)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param reference [Nokogiri::XML::Element]
|
|
55
|
+
# @return [String]
|
|
56
|
+
def language(reference)
|
|
57
|
+
reference[:lang] || "en"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
#
|
|
61
|
+
# Extract document identifiers from reference
|
|
62
|
+
#
|
|
63
|
+
# @param reference [Nokogiri::XML::Element]
|
|
64
|
+
# @param ver [String, nil] Internet Draft version
|
|
65
|
+
#
|
|
66
|
+
# @return [Array<RelatonBib::DocumentIdentifier>]
|
|
67
|
+
#
|
|
68
|
+
def docids(reference, ver) # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/AbcSize
|
|
69
|
+
ret = []
|
|
70
|
+
sfid = reference.at("./seriesInfo[@name='#{self::FLAVOR}']", "./front/seriesInfo[@name='#{self::FLAVOR}']")
|
|
71
|
+
if sfid
|
|
72
|
+
ret << DocumentIdentifier.new(type: sfid[:name], id: sfid[:value])
|
|
73
|
+
elsif self::FLAVOR && (id = (reference[:anchor] || reference[:docName] || reference[:number]))
|
|
74
|
+
ret << DocumentIdentifier.new( type: self::FLAVOR, id: id.sub(/^(RFC)/, "\\1 "))
|
|
75
|
+
end
|
|
76
|
+
if (id = reference[:anchor])
|
|
77
|
+
ret << DocumentIdentifier.new(type: "rfc-anchor", id: id)
|
|
78
|
+
end
|
|
79
|
+
ret + reference.xpath("./seriesInfo", "./front/seriesInfo").map do |si|
|
|
80
|
+
next unless SERIESINFONAMES.include? si[:name]
|
|
81
|
+
|
|
82
|
+
id = si[:value]
|
|
83
|
+
id.sub!(/(?<=-)\d{2}$/, ver) if ver && si[:name] == "Internet-Draft"
|
|
84
|
+
DocumentIdentifier.new(id: id, type: si[:name])
|
|
85
|
+
end.compact
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# extract status
|
|
90
|
+
# @param reference [Nokogiri::XML::Element]
|
|
91
|
+
#
|
|
92
|
+
# @return [RelatonBib::DocumentStatus]
|
|
93
|
+
#
|
|
94
|
+
def status(reference)
|
|
95
|
+
st = reference.at("./seriesinfo[@status]")
|
|
96
|
+
DocumentStatus.new(stage: st[:status]) if st
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @param reference [Nokogiri::XML::Element]
|
|
100
|
+
# @param url [String]
|
|
101
|
+
# @param ver [String, nil] Internet Draft version
|
|
102
|
+
# @return [Array<Hash>]
|
|
103
|
+
def link(reference, url, ver)
|
|
104
|
+
l = []
|
|
105
|
+
l << { type: "xml", content: url } if url
|
|
106
|
+
l << { type: "src", content: reference[:target] } if reference[:target]
|
|
107
|
+
if /^I-D/.match? reference[:anchor]
|
|
108
|
+
reference.xpath("format").each do |f|
|
|
109
|
+
c = ver ? f[:target].sub(/(?<=-)\d{2}(?=\.)/, ver) : f[:target]
|
|
110
|
+
l << { type: f[:type], content: c }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
l
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @param reference [Nokogiri::XML::Element]
|
|
117
|
+
# @return [Array<Hash>]
|
|
118
|
+
def titles(reference)
|
|
119
|
+
reference.xpath("./front/title").map do |title|
|
|
120
|
+
{ content: title.text, language: language(reference), script: "Latn" }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @param reference [Nokogiri::XML::Element]
|
|
125
|
+
# @return [RelatonBib::FormattedRef, nil]
|
|
126
|
+
def formattedref(reference)
|
|
127
|
+
return if reference.at "./front/title"
|
|
128
|
+
|
|
129
|
+
cont = (reference[:anchor] || reference[:docName] || reference[:number])
|
|
130
|
+
if cont
|
|
131
|
+
FormattedRef.new(
|
|
132
|
+
content: cont, language: language(reference), script: "Latn",
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @param reference [Nokogiri::XML::Element]
|
|
138
|
+
# @return [Array<RelatonBib::FormattedString>]
|
|
139
|
+
def abstracts(ref)
|
|
140
|
+
ref.xpath("./front/abstract").map do |a|
|
|
141
|
+
FormattedString.new(
|
|
142
|
+
content: a.children.to_s.gsub(/(<\/?)t(>)/, '\1p\2'),
|
|
143
|
+
language: language(ref), script: "Latn", format: "text/html"
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# @param reference [Nokogiri::XML::Element]
|
|
149
|
+
# @return [Array<Hash>]
|
|
150
|
+
def contributors(reference)
|
|
151
|
+
reference.xpath("./front/author").map do |contrib|
|
|
152
|
+
if contrib[:fullname] || contrib[:surname] then person(contrib, reference)
|
|
153
|
+
else organization(contrib)
|
|
154
|
+
end
|
|
155
|
+
end.compact
|
|
156
|
+
# persons(reference) + organizations(reference)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# @param author [Nokogiri::XML::Element]
|
|
160
|
+
# @param reference [Nokogiri::XML::Element]
|
|
161
|
+
# @return [Array<Hash{Symbol=>RelatonBib::Person,Symbol=>Array<String>}>]
|
|
162
|
+
def person(author, reference)
|
|
163
|
+
# reference.xpath("./front/author[@surname]|./front/author[@fullname]")
|
|
164
|
+
# .map do |author|
|
|
165
|
+
entity = Person.new(
|
|
166
|
+
name: full_name(author, reference),
|
|
167
|
+
affiliation: affiliation(author),
|
|
168
|
+
contact: contacts(author.at("./address")),
|
|
169
|
+
)
|
|
170
|
+
{ entity: entity, role: [contributor_role(author)] }
|
|
171
|
+
# end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @param contrib [Nokogiri::XML::Element]
|
|
175
|
+
# @return [Array<Hash{Symbol=>RelatonBib::Organization,
|
|
176
|
+
# Symbol=>Array<String>}>]
|
|
177
|
+
def organization(contrib)
|
|
178
|
+
# publisher = { entity: new_org, role: [type: "publisher"] }
|
|
179
|
+
# orgs = reference.xpath("./seriesinfo").reduce([]) do |mem, si|
|
|
180
|
+
# next mem unless si[:stream]
|
|
181
|
+
|
|
182
|
+
# mem << { entity: new_org(si[:stream], nil), role: [type: "author"] }
|
|
183
|
+
# end
|
|
184
|
+
# orgs + reference.xpath(
|
|
185
|
+
# "front/author[not(@surname)][not(@fullname)]/organization",
|
|
186
|
+
# ).map do |org|
|
|
187
|
+
org = contrib.at("./organization")
|
|
188
|
+
{ entity: new_org(org.text, org[:abbrev]), role: [contributor_role(contrib)] }
|
|
189
|
+
# end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# @param author [Nokogiri::XML::Element]
|
|
193
|
+
# @param reference [Nokogiri::XML::Element]
|
|
194
|
+
# @return [RelatonBib::FullName]
|
|
195
|
+
def full_name(author, reference)
|
|
196
|
+
lang = language reference
|
|
197
|
+
FullName.new(
|
|
198
|
+
completename: localized_string(author[:fullname], lang),
|
|
199
|
+
initial: [localized_string(author[:initials], lang)].compact,
|
|
200
|
+
surname: localized_string(author[:surname], lang),
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @param author [Nokogiri::XML::Element]
|
|
205
|
+
# @return [Array<RelatonBib::Affiliation>]
|
|
206
|
+
def affiliation(author)
|
|
207
|
+
o = author.at("./organization")
|
|
208
|
+
return [] if o.nil? || o.text.empty?
|
|
209
|
+
|
|
210
|
+
org = new_org o.text, o[:abbrev]
|
|
211
|
+
[Affiliation.new(organization: org)]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @param name [String]
|
|
215
|
+
# @param abbr [String]
|
|
216
|
+
# @return [RelatonBib::Organization]
|
|
217
|
+
def new_org(name, abbr)
|
|
218
|
+
# (name = "Internet Engineering Task Force", abbr = "IETF")
|
|
219
|
+
Organization.new name: name, abbreviation: abbr
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# @param content [String, nil]
|
|
223
|
+
# @param lang [String, nil]
|
|
224
|
+
# @return [RelatonBib::LocalizedString, nil]
|
|
225
|
+
def localized_string(content, lang)
|
|
226
|
+
LocalizedString.new(content, lang) if content
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# @param postal [Nokogiri::XML::Element]
|
|
230
|
+
# @return [Array<RelatonBib::Address, RelatonBib::Phone>]
|
|
231
|
+
def contacts(addr)
|
|
232
|
+
conts = []
|
|
233
|
+
return conts unless addr
|
|
234
|
+
|
|
235
|
+
postal = addr.at("./postal")
|
|
236
|
+
conts << address(postal) if postal
|
|
237
|
+
add_contact(conts, "phone", addr.at("./phone"))
|
|
238
|
+
add_contact(conts, "email", addr.at("./email"))
|
|
239
|
+
add_contact(conts, "uri", addr.at("./uri"))
|
|
240
|
+
conts
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# @param postal [Nokogiri::XML::Element]
|
|
244
|
+
# @rerurn [RelatonBib::Address]
|
|
245
|
+
def address(postal) # rubocop:disable Metrics/CyclomaticComplexity
|
|
246
|
+
street = [
|
|
247
|
+
(postal.at("./postalLine") || postal.at("./street"))&.text
|
|
248
|
+
].compact
|
|
249
|
+
Address.new(
|
|
250
|
+
street: street,
|
|
251
|
+
city: postal.at("./city")&.text,
|
|
252
|
+
postcode: postal.at("./code")&.text,
|
|
253
|
+
country: postal.at("./country")&.text,
|
|
254
|
+
state: postal.at("./region")&.text,
|
|
255
|
+
)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# @param conts [Array<RelatonBib::Address, RelatonBib::Contact>]
|
|
259
|
+
# @param type [String] allowed "phone", "email" or "uri"
|
|
260
|
+
# @param value [String]
|
|
261
|
+
def add_contact(conts, type, value)
|
|
262
|
+
conts << Contact.new(type: type, value: value.text) if value
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# @param author [Nokogiri::XML::Document]
|
|
266
|
+
# @return [Hash]
|
|
267
|
+
def contributor_role(author)
|
|
268
|
+
{ type: author[:role] || "author" }
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# @param reference [Nokogiri::XML::Element]
|
|
272
|
+
# @return [Hash]
|
|
273
|
+
def relations(reference)
|
|
274
|
+
reference.xpath("reference").map do |ref|
|
|
275
|
+
{ type: "includes", bibitem: fetch_rfc(ref, is_relation: true) }
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
#
|
|
280
|
+
# Extract date from reference.
|
|
281
|
+
#
|
|
282
|
+
# @param reference [Nokogiri::XML::Element]
|
|
283
|
+
# @return [Array<RelatonBib::BibliographicDate>] published data.
|
|
284
|
+
#
|
|
285
|
+
def dates(reference)
|
|
286
|
+
return unless (date = reference.at "./front/date")
|
|
287
|
+
|
|
288
|
+
d = [date[:year], month(date[:month]), (date[:day] || 1)].compact.join "-"
|
|
289
|
+
date = Time.parse(d).strftime "%Y-%m-%d"
|
|
290
|
+
[BibliographicDate.new(type: "published", on: date)]
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# @param reference [Nokogiri::XML::Element]
|
|
294
|
+
# @return [RelatonBib::EditorialGroup, nil]
|
|
295
|
+
def editorialgroup(reference)
|
|
296
|
+
tc = reference.xpath("./front/workgroup").map do |ed|
|
|
297
|
+
wg = WorkGroup.new name: ed.text
|
|
298
|
+
committee wg
|
|
299
|
+
end
|
|
300
|
+
EditorialGroup.new tc if tc.any?
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# @param [RelatonBib::WorkGroup]
|
|
304
|
+
# @return [RelatonBib::TechnicalCommittee]
|
|
305
|
+
def committee(wgr)
|
|
306
|
+
TechnicalCommittee.new wgr
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def month(mon)
|
|
310
|
+
return 1 if !mon || mon.empty?
|
|
311
|
+
return mon if /^\d+$/.match? mon
|
|
312
|
+
|
|
313
|
+
Date::MONTHNAMES.index(mon)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
#
|
|
317
|
+
# Extract series form reference
|
|
318
|
+
# @param reference [Nokogiri::XML::Element]
|
|
319
|
+
#
|
|
320
|
+
# @return [Array<RelatonBib::Series>]
|
|
321
|
+
#
|
|
322
|
+
def series(reference)
|
|
323
|
+
reference.xpath("./seriesInfo", "./front/seriesInfo").map do |si|
|
|
324
|
+
next if si[:name] == "DOI" || si[:stream] || si[:status]
|
|
325
|
+
|
|
326
|
+
t = TypedTitleString.new(
|
|
327
|
+
content: si[:name], language: language(reference), script: "Latn",
|
|
328
|
+
)
|
|
329
|
+
Series.new(title: t, number: si[:value], type: "main")
|
|
330
|
+
end.compact
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# @param anchor [String]
|
|
334
|
+
# @return [String]
|
|
335
|
+
def doctype(anchor)
|
|
336
|
+
case anchor
|
|
337
|
+
when /I-D/ then "internet-draft"
|
|
338
|
+
when /IEEE/ then "ieee"
|
|
339
|
+
else "rfc"
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
extend BibXMLParser
|
|
344
|
+
end
|
|
345
|
+
end
|
|
@@ -138,7 +138,7 @@ module RelatonBib
|
|
|
138
138
|
desc = description.select { |d| d.language&.include? opts[:lang] }
|
|
139
139
|
desc = description unless desc.any?
|
|
140
140
|
desc.each { |d| builder.description { d.to_xml builder } }
|
|
141
|
-
organization.to_xml
|
|
141
|
+
organization.to_xml(**opts)
|
|
142
142
|
end
|
|
143
143
|
end
|
|
144
144
|
|
|
@@ -7,7 +7,7 @@ module RelatonBib
|
|
|
7
7
|
# @return [Array<RelatonBib::TechnicalCommittee>]
|
|
8
8
|
attr_accessor :technical_committee
|
|
9
9
|
|
|
10
|
-
# @param technical_committee [Array<RelatonBib::
|
|
10
|
+
# @param technical_committee [Array<RelatonBib::TechnicalCommittee>]
|
|
11
11
|
def initialize(technical_committee)
|
|
12
12
|
@technical_committee = technical_committee
|
|
13
13
|
end
|
|
@@ -56,7 +56,7 @@ module RelatonBib
|
|
|
56
56
|
else
|
|
57
57
|
builder.parent["language"] = language.join(",") if language&.any?
|
|
58
58
|
builder.parent["script"] = script.join(",") if script&.any?
|
|
59
|
-
builder.
|
|
59
|
+
builder.parent << content # .encode(xml: :text)
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
data/lib/relaton_bib/version.rb
CHANGED
|
@@ -320,7 +320,7 @@ module RelatonBib
|
|
|
320
320
|
# @return [Array<RelatonBib::FormattedString>]
|
|
321
321
|
def fetch_abstract(item)
|
|
322
322
|
item.xpath("./abstract").map do |a|
|
|
323
|
-
FormattedString.new(content: a.
|
|
323
|
+
FormattedString.new(content: a.children.to_s, language: a[:language],
|
|
324
324
|
script: a[:script], format: a[:format])
|
|
325
325
|
end
|
|
326
326
|
end
|
data/lib/relaton_bib.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require "forwardable"
|
|
2
|
+
require "yaml"
|
|
2
3
|
require "relaton_bib/version"
|
|
3
4
|
require "relaton_bib/deep_dup"
|
|
4
5
|
require "relaton_bib/localized_string"
|
|
5
6
|
require "relaton_bib/bibliographic_item"
|
|
6
7
|
require "relaton_bib/hit_collection"
|
|
7
8
|
require "relaton_bib/hit"
|
|
9
|
+
require "relaton_bib/bibxml_parser"
|
|
8
10
|
|
|
9
11
|
module RelatonBib
|
|
10
12
|
class Error < StandardError; end
|
data/relaton-bib.gemspec
CHANGED
|
@@ -27,6 +27,9 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_development_dependency "equivalent-xml", "~> 0.6"
|
|
28
28
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
29
29
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
30
|
+
spec.add_development_dependency "rubocop"
|
|
31
|
+
spec.add_development_dependency "rubocop-performance"
|
|
32
|
+
spec.add_development_dependency "rubocop-rails"
|
|
30
33
|
spec.add_development_dependency "ruby-jing"
|
|
31
34
|
spec.add_development_dependency "simplecov"
|
|
32
35
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: relaton-bib
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.9.
|
|
4
|
+
version: 1.9.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-10-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: byebug
|
|
@@ -66,6 +66,48 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '3.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rubocop
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rubocop-performance
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rubocop-rails
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
69
111
|
- !ruby/object:Gem::Dependency
|
|
70
112
|
name: ruby-jing
|
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -182,6 +224,7 @@ files:
|
|
|
182
224
|
- lib/relaton_bib/bibliographic_date.rb
|
|
183
225
|
- lib/relaton_bib/bibliographic_item.rb
|
|
184
226
|
- lib/relaton_bib/bibtex_parser.rb
|
|
227
|
+
- lib/relaton_bib/bibxml_parser.rb
|
|
185
228
|
- lib/relaton_bib/classification.rb
|
|
186
229
|
- lib/relaton_bib/contribution_info.rb
|
|
187
230
|
- lib/relaton_bib/contributor.rb
|