relaton-bib 1.14.4 → 1.14.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d516a0a4756006072259b99674e50cefab3405f8824761c50faeca40bac5c71
4
- data.tar.gz: f349fc209e2f2f684e71ad1044b3c23d37dcf905f4852841509f0319bfcd42be
3
+ metadata.gz: 4a4d2a5d90a56a9b93f34acc0d037d101130c36b34a4d436c28c6dc8c53496eb
4
+ data.tar.gz: 2b99e61f20b08a6ca48a2b42c16abef7cfe0e9f4340e413d1e37a4ff3e098969
5
5
  SHA512:
6
- metadata.gz: 99f2a47867468501d678c7b3807eb3be541dfd522ebe5f373b48499b82a3c4095c4bfe572967cd1f871e87b98e58d66c8207dc339a06a91355f25f560b06c4ad
7
- data.tar.gz: a69cad9aaee6bd53fb2e17dc55102e2c37738142d6f7a6978d5db3d4524b3b29a276df85ca9feb95567f173dfae6d574c46c2a5da97c771774c8de5de084dd24
6
+ metadata.gz: 2d1f0ec61452cdc6bdedde4bd1025a2aa9d3baeeeaa65f3e3b212aa56cd2753afee04dccc13870a1bea5e88b388a97e086adea56353272204a49251b46145384
7
+ data.tar.gz: 880ddde78e8fc665caf7b7a104efecd476c5787d34f839f73a7f1bc70425b6872dcd68bdaa7305fc751eb7cfe6e5c3395fa9df69771bec0634ceccdfdf66f890
data/.rubocop.yml CHANGED
@@ -7,6 +7,6 @@ require: rubocop-rails
7
7
  inherit_from:
8
8
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
9
9
  AllCops:
10
- TargetRubyVersion: 2.6
10
+ TargetRubyVersion: 2.7
11
11
  Rails:
12
12
  Enabled: false
@@ -62,9 +62,10 @@ module RelatonBib
62
62
  when "chapter" then item.chapter = reference_from
63
63
  when "page"
64
64
  value = reference_from
65
- value += "-#{reference_to}" if reference_to
65
+ value += "--#{reference_to}" if reference_to
66
66
  item.pages = value
67
67
  when "volume" then item.volume = reference_from
68
+ when "issue" then item.number = reference_from
68
69
  end
69
70
  end
70
71
  end
@@ -151,7 +151,7 @@ module RelatonBib
151
151
  # @param series [Array<RelatonBib::Series>]
152
152
  # @param medium [RelatonBib::Medium, nil]
153
153
  # @param place [Array<String, RelatonBib::Place>]
154
- # @param extent [Array<Relaton::Locality, RelatonBib::LocalityStack>]
154
+ # @param extent [Array<RelatonBib::Locality, RelatonBib::LocalityStack>]
155
155
  # @param accesslocation [Array<String>]
156
156
  # @param classification [Array<RelatonBib::Classification>]
157
157
  # @param validity [RelatonBib:Validity, nil]
@@ -378,13 +378,7 @@ module RelatonBib
378
378
  # @return [String, Nokogiri::XML::Builder::NodeBuilder] XML
379
379
  #
380
380
  def to_bibxml(builder = nil, include_keywords: false)
381
- if builder
382
- render_bibxml builder, include_keywords
383
- else
384
- Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
385
- render_bibxml xml, include_keywords
386
- end.doc.root.to_xml
387
- end
381
+ Renderer::BibXML.new(self).render builder: builder, include_keywords: include_keywords
388
382
  end
389
383
 
390
384
  # @return [Hash]
@@ -448,7 +442,7 @@ module RelatonBib
448
442
  # @return [String]
449
443
  #
450
444
  def to_bibtex(bibtex = nil)
451
- bibtext_item(bibtex).to_s
445
+ Renderer::BibtexBuilder.build(self, bibtex).to_s
452
446
  end
453
447
 
454
448
  #
@@ -459,7 +453,9 @@ module RelatonBib
459
453
  # @return [Hash] citeproc
460
454
  #
461
455
  def to_citeproc(bibtex = nil)
462
- bibtext_item(bibtex).to_citeproc.map { |cp| cp.transform_keys(&:to_s) }
456
+ Renderer::BibtexBuilder.build(self, bibtex).to_citeproc.map do |cp|
457
+ cp.transform_keys(&:to_s)
458
+ end
463
459
  end
464
460
 
465
461
  # @param lang [String, nil] language code Iso639
@@ -497,6 +493,8 @@ module RelatonBib
497
493
  ttl = me.title.select do |t|
498
494
  t.type != "main" && t.title.language&.include?(l)
499
495
  end
496
+ next if ttl.empty?
497
+
500
498
  tm_en = ttl.map { |t| t.title.content }.join " – "
501
499
  me.title.detect do |t|
502
500
  t.type == "main" && t.title.language&.include?(l)
@@ -590,162 +588,6 @@ module RelatonBib
590
588
 
591
589
  private
592
590
 
593
- #
594
- # Create BibTeX item for this document
595
- #
596
- # @param [BibTeX::Bibliography, nil] bibtex <description>
597
- #
598
- # @return [BibTeX::Bibliography] BibTeX bibliography
599
- #
600
- def bibtext_item(bibtex) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
601
- item = BibTeX::Entry.new
602
- item.type = bibtex_type
603
- item.key = id
604
- title.to_bibtex item
605
- item.edition = edition.content if edition
606
- bibtex_author item
607
- bibtex_contributor item
608
- item.address = place.first.name if place.any?
609
- bibtex_note item
610
- bibtex_relation item
611
- bibtex_extent item
612
- bibtex_date item
613
- bibtex_series item
614
- bibtex_classification item
615
- item.keywords = keyword.map(&:content).join(", ") if keyword.any?
616
- bibtex_docidentifier item
617
- item.timestamp = fetched.to_s if fetched
618
- bibtex_link item
619
- bibtex ||= BibTeX::Bibliography.new
620
- bibtex << item
621
- bibtex
622
- end
623
-
624
- # @return [String]
625
- def bibtex_type
626
- case type
627
- when "standard", nil then "misc"
628
- else type
629
- end
630
- end
631
-
632
- # @param [BibTeX::Entry]
633
- def bibtex_author(item) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
634
- authors = contributor.select do |c|
635
- c.entity.is_a?(Person) && c.role.map(&:type).include?("author")
636
- end.map &:entity
637
-
638
- return unless authors.any?
639
-
640
- item.author = authors.map do |a|
641
- if a.name.surname
642
- "#{a.name.surname}, #{a.name.forename.map(&:to_s).join(' ')}"
643
- else
644
- a.name.completename.to_s
645
- end
646
- end.join " and "
647
- end
648
-
649
- # @param [BibTeX::Entry]
650
- def bibtex_contributor(item) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
651
- contributor.each do |c|
652
- rls = c.role.map(&:type)
653
- if rls.include?("publisher") then item.publisher = c.entity.name
654
- elsif rls.include?("distributor")
655
- case type
656
- when "techreport" then item.institution = c.entity.name
657
- when "inproceedings", "conference", "manual", "proceedings"
658
- item.organization = c.entity.name
659
- when "mastersthesis", "phdthesis" then item.school = c.entity.name
660
- end
661
- end
662
- end
663
- end
664
-
665
- # @param [BibTeX::Entry]
666
- def bibtex_note(item) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/AbcSize
667
- biblionote.each do |n|
668
- case n.type
669
- when "annote" then item.annote = n.content
670
- when "howpublished" then item.howpublished = n.content
671
- when "comment" then item.comment = n.content
672
- when "tableOfContents" then item.content = n.content
673
- when nil then item.note = n.content
674
- end
675
- end
676
- end
677
-
678
- # @param [BibTeX::Entry]
679
- def bibtex_relation(item)
680
- rel = relation.detect { |r| r.type == "partOf" }
681
- if rel
682
- title_main = rel.bibitem.title.detect { |t| t.type == "main" }
683
- item.booktitle = title_main.title.content
684
- end
685
- end
686
-
687
- # @param [BibTeX::Entry]
688
- def bibtex_extent(item)
689
- extent.each { |e| e.to_bibtex(item) }
690
- end
691
-
692
- # @param [BibTeX::Entry]
693
- def bibtex_date(item)
694
- date.each do |d|
695
- case d.type
696
- when "published"
697
- item.year = d.on :year
698
- item.month = d.on :month
699
- when "accessed" then item.urldate = d.on.to_s
700
- end
701
- end
702
- end
703
-
704
- # @param [BibTeX::Entry]
705
- def bibtex_series(item)
706
- series.each do |s|
707
- case s.type
708
- when "journal"
709
- item.journal = s.title.title
710
- item.number = s.number if s.number
711
- when nil then item.series = s.title.title
712
- end
713
- end
714
- end
715
-
716
- # @param [BibTeX::Entry]
717
- def bibtex_classification(item)
718
- classification.each do |c|
719
- case c.type
720
- when "type" then item["type"] = c.value
721
- # when "keyword" then item.keywords = c.value
722
- when "mendeley" then item["mendeley-tags"] = c.value
723
- end
724
- end
725
- end
726
-
727
- # @param [BibTeX::Entry]
728
- def bibtex_docidentifier(item)
729
- docidentifier.each do |i|
730
- case i.type
731
- when "isbn" then item.isbn = i.id
732
- when "lccn" then item.lccn = i.id
733
- when "issn" then item.issn = i.id
734
- end
735
- end
736
- end
737
-
738
- # @param [BibTeX::Entry]
739
- def bibtex_link(item)
740
- link.each do |l|
741
- case l.type
742
- when "doi" then item.doi = l.content
743
- when "file" then item.file2 = l.content
744
- when "src" then item.url = l.content
745
- end
746
- end
747
- end
748
-
749
591
  # @param opts [Hash]
750
592
  # @option opts [Nokogiri::XML::Builder] :builder XML builder
751
593
  # @option opts [Boolean] bibdata
@@ -811,268 +653,5 @@ module RelatonBib
811
653
  xml["schema-version"] = schema unless opts[:embedded]
812
654
  xml
813
655
  end
814
-
815
- #
816
- # Render BibXML (RFC, BCP)
817
- #
818
- # @param [Nokogiri::XML::Builder] builder
819
- # @param [Boolean] include_bibdata
820
- #
821
- def render_bibxml(builder, include_keywords) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
822
- target = link.detect { |l| l.type.casecmp("src").zero? } ||
823
- link.detect { |l| l.type.casecmp("doi").zero? }
824
- bxml = if docnumber&.match(/^BCP/) || (docidentifier.detect(&:primary)&.id ||
825
- docidentifier[0].id).include?("BCP")
826
- render_bibxml_refgroup(builder, include_keywords)
827
- else
828
- render_bibxml_ref(builder, include_keywords)
829
- end
830
- bxml[:target] = target.content.to_s if target
831
- end
832
-
833
- #
834
- # Render BibXML (BCP)
835
- #
836
- # @param [Nokogiri::XML::Builder] builder
837
- # @param [Boolean] include_keywords
838
- #
839
- def render_bibxml_refgroup(builder, include_keywords)
840
- builder.referencegroup(**ref_attrs) do |b|
841
- relation.each do |r|
842
- r.bibitem.to_bibxml(b, include_keywords: include_keywords) if r.type == "includes"
843
- end
844
- end
845
- end
846
-
847
- #
848
- # Render BibXML (RFC)
849
- #
850
- # @param [Nokogiri::XML::Builder] builder
851
- # @param [Boolean] include_keywords
852
- #
853
- def render_bibxml_ref(builder, include_keywords) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
854
- builder.reference(**ref_attrs) do |xml|
855
- if title.any? || contributor.any? || date.any? || abstract.any? ||
856
- editorialgroup&.technical_committee&.any? ||
857
- (include_keywords && keyword.any?)
858
- xml.front do
859
- xml.title title[0].title.content if title.any?
860
- render_authors xml
861
- render_date xml
862
- render_workgroup xml
863
- render_keyword xml if include_keywords
864
- render_abstract xml
865
- end
866
- end
867
- render_seriesinfo xml
868
- render_format xml
869
- end
870
- end
871
-
872
- def render_format(builder)
873
- link.select { |l| l.type == "TXT" }.each do |l|
874
- builder.format type: l.type, target: l.content
875
- end
876
- end
877
-
878
- #
879
- # Create reference attributes
880
- #
881
- # @return [Hash<Symbol=>String>] attributes
882
- #
883
- def ref_attrs # rubocop:disable Metrics/AbcSize
884
- discopes = %w[anchor docName number]
885
- attrs = docidentifier.each_with_object({}) do |di, h|
886
- next unless discopes.include?(di.scope)
887
-
888
- h[di.scope.to_sym] = di.id
889
- end
890
- return attrs if attrs.any?
891
-
892
- docidentifier.first&.tap do |di|
893
- anchor = di.type == "IANA" ? di.id.split[1..-1].join(" ").upcase : di.id
894
- return { anchor: anchor.gsub(" ", ".") }
895
- end
896
- end
897
-
898
- #
899
- # Render keyword
900
- #
901
- # @param [Nokogiri::XML::Builder] builder xml builder
902
- #
903
- def render_keyword(builder)
904
- keyword.each { |kw| builder.keyword kw.content }
905
- end
906
-
907
- #
908
- # Render workgroup
909
- #
910
- # @param [Nokogiri::XML::Builder] builder xml builder
911
- #
912
- def render_workgroup(builder)
913
- editorialgroup&.technical_committee&.each do |tc|
914
- builder.workgroup tc.workgroup.name
915
- end
916
- end
917
-
918
- #
919
- # Render abstract
920
- #
921
- # @param [Nokogiri::XML::Builder] builder xml builder
922
- #
923
- def render_abstract(builder)
924
- return unless abstract.any?
925
-
926
- builder.abstract { |xml| xml << abstract[0].content.gsub(/(<\/?)p(>)/, '\1t\2') }
927
- end
928
-
929
- #
930
- # Render date
931
- #
932
- # @param [Nokogiri::XML::Builder] builder xml builder
933
- #
934
- def render_date(builder) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
935
- dt = date.detect { |d| d.type == "published" }
936
- return unless dt
937
-
938
- elm = builder.date
939
- y = dt.on(:year) || dt.from(:year) || dt.to(:year)
940
- elm[:year] = y if y
941
- m = dt.on(:month) || dt.from(:month) || dt.to(:month)
942
- elm[:month] = Date::MONTHNAMES[m] if m
943
- # rfcs = %w[RFC BCP FYI STD]
944
- # unless rfcs.include?(doctype) && docidentifier.detect { |di| rfcs.include? di.type }
945
- d = dt.on(:day) || dt.from(:day) || dt.to(:day)
946
- elm[:day] = d if d
947
- # end
948
- end
949
-
950
- #
951
- # Render seriesinfo
952
- #
953
- # @param [Nokogiri::XML::Builder] builder xml builder
954
- #
955
- def render_seriesinfo(builder) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
956
- docidentifier.each do |di|
957
- if BibXMLParser::SERIESINFONAMES.include? di.type
958
- builder.seriesInfo(name: di.type, value: di.id)
959
- end
960
- end
961
- # di_types = docidentifier.map(&:type)
962
- series.select do |s|
963
- s.title && # !di_types.include?(s.title.title.to_s) &&
964
- !BibXMLParser::SERIESINFONAMES.include?(s.title.title.to_s)
965
- end.uniq { |s| s.title.title.to_s }.each do |s|
966
- si = builder.seriesInfo(name: s.title.title.to_s)
967
- si[:value] = s.number if s.number
968
- end
969
- end
970
-
971
- #
972
- # Render authors
973
- #
974
- # @param [Nokogiri::XML::Builder] builder xml builder
975
- #
976
- def render_authors(builder) # rubocop:disable Metrics/AbcSize
977
- contributor.each do |c|
978
- builder.author do |xml|
979
- xml.parent[:role] = "editor" if c.role.detect { |r| r.type == "editor" }
980
- if c.entity.is_a?(Person) then render_person xml, c.entity
981
- else render_organization xml, c.entity, c.role
982
- end
983
- render_address xml, c
984
- end
985
- end
986
- end
987
-
988
- #
989
- # Render address
990
- #
991
- # @param [Nokogiri::XML::Builder] builder xml builder
992
- # @param [RelatonBib::ContributionInfo] contrib contributor
993
- #
994
- def render_address(builder, contrib) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
995
- # addr = contrib.entity.contact.reject do |cn|
996
- # cn.is_a?(Address) && cn.postcode.nil?
997
- # end
998
- if contrib.entity.contact.any?
999
- builder.address do |xml|
1000
- address = contrib.entity.contact.detect { |cn| cn.is_a? Address }
1001
- if address
1002
- xml.postal do
1003
- xml.city address.city if address.city
1004
- xml.code address.postcode if address.postcode
1005
- xml.country address.country if address.country
1006
- xml.region address.state if address.state
1007
- xml.street address.street[0] if address.street.any?
1008
- end
1009
- end
1010
- render_contact xml, contrib.entity.contact
1011
- end
1012
- end
1013
- end
1014
-
1015
- #
1016
- # Render contact
1017
- #
1018
- # @param [Nokogiri::XML::Builder] builder xml builder
1019
- # @param [Array<RelatonBib::Address, RelatonBib::Contact>] addr contact
1020
- #
1021
- def render_contact(builder, addr)
1022
- %w[phone email uri].each do |type|
1023
- cont = addr.detect { |cn| cn.is_a?(Contact) && cn.type == type }
1024
- builder.send type, cont.value if cont
1025
- end
1026
- end
1027
-
1028
- #
1029
- # Render person
1030
- #
1031
- # @param [Nokogiri::XML::Builder] builder xml builder
1032
- # @param [RelatonBib::Person] person person
1033
- #
1034
- def render_person(builder, person) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1035
- render_organization builder, person.affiliation.first&.organization
1036
- if person.name.completename
1037
- builder.parent[:fullname] = person.name.completename.content
1038
- elsif person.name.forename.any?
1039
- builder.parent[:fullname] = person.name.forename.map do |n|
1040
- n.content || n.initial
1041
- end.join " "
1042
- end
1043
- if person.name.initials
1044
- builder.parent[:initials] = person.name.initials.content
1045
- elsif person.name.forename.any?
1046
- builder.parent[:initials] = person.name.forename.map do |f|
1047
- "#{f.initial || f.content[0]}."
1048
- end.join
1049
- end
1050
- if person.name.surname
1051
- if !person.name.completename && person.name.forename.any? && person.name.surname
1052
- builder.parent[:fullname] += " #{person.name.surname}"
1053
- end
1054
- builder.parent[:surname] = person.name.surname.content
1055
- end
1056
- end
1057
-
1058
- #
1059
- # Render organization
1060
- #
1061
- # @param [Nokogiri::XML::Builder] builder xml builder
1062
- # @param [RelatonBib::Organization] org organization
1063
- #
1064
- def render_organization(builder, org, _role = []) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
1065
- abbrev = org&.abbreviation&.content
1066
- orgname = org&.name&.first&.content
1067
- orgname = if BibXMLParser::ORGNAMES.key?(abbrev) then abbrev
1068
- else BibXMLParser::ORGNAMES.key(orgname) || orgname || abbrev
1069
- end
1070
- # if role.detect { |r| r.description.detect { |d| d.content == "BibXML author" } }
1071
- # builder.parent[:fullname] = orgname
1072
- # else
1073
- org = builder.organization orgname
1074
- org[:abbrev] = abbrev if abbrev
1075
- # end
1076
- end
1077
656
  end
1078
657
  end
@@ -2,6 +2,7 @@ require "bibtex"
2
2
  require "iso639"
3
3
 
4
4
  module RelatonBib
5
+ # @todo: move this class to the RelatonBib::Bibtex module
5
6
  class BibtexParser
6
7
  class << self
7
8
  # @param bibtex [String]
@@ -1,6 +1,6 @@
1
1
  module RelatonBib
2
2
  class Forename < LocalizedString
3
- # @return [RelatonBib::Forename] forename
3
+ # @return [String, nil]
4
4
  attr_accessor :initial
5
5
 
6
6
  #
@@ -122,7 +122,7 @@ module RelatonBib
122
122
  str.gsub(/(?<=<)\w+:(?=\w+>)/, "").gsub(/(?<=<\/)\w+:(?=\w+>)/, "")
123
123
  .gsub(/<i>/, "<em>").gsub(/<\/i>/, "</em>")
124
124
  .gsub(/<b>/, "<strong>").gsub(/<\/b>/, "</strong>")
125
- .gsub(/<(?!\/?(em|strong|stem|sup|sub|tt|br\s?\/|p))[^\s!]\/?.*?>/, " ")
125
+ .gsub(/<(?!\/?(em|strong|stem|sup|sub|tt|br\s?\/|p))[^\s!]\/?.*?>/, "")
126
126
  .gsub(/\s+([.,:;!?<])/, "\\1").strip
127
127
  end
128
128
  end
@@ -282,7 +282,7 @@ module RelatonBib
282
282
  return [] unless person[:affiliation]
283
283
 
284
284
  RelatonBib.array(person[:affiliation]).map do |a|
285
- a[:description] = RelatonBib.array(a[:description])&.map do |d|
285
+ a[:description] = RelatonBib.array(a[:description]).map do |d|
286
286
  cnt = if d.is_a?(Hash)
287
287
  { content: d[:content], language: d[:language],
288
288
  script: d[:script], format: d[:format] }
@@ -323,7 +323,9 @@ module RelatonBib
323
323
  return unless ret[:copyright]
324
324
 
325
325
  ret[:copyright] = RelatonBib.array(ret[:copyright]).map do |c|
326
- c[:owner] = RelatonBib.array(c[:owner])
326
+ c[:owner] = RelatonBib.array(c[:owner]).map do |o|
327
+ org_hash_to_bib(o)
328
+ end
327
329
  c
328
330
  end
329
331
  end
@@ -1,3 +1,5 @@
1
+ require "weakref"
2
+
1
3
  module RelatonBib
2
4
  class Hit
3
5
  # @return [RelatonBib::HitCollection]
@@ -10,7 +12,7 @@ module RelatonBib
10
12
  # @param hit_collection [RelatonBib::HitCollection]
11
13
  def initialize(hit, hit_collection = nil)
12
14
  @hit = hit
13
- @hit_collection = hit_collection
15
+ @hit_collection = WeakRef.new hit_collection if hit_collection
14
16
  end
15
17
 
16
18
  # @return [String]
@@ -11,7 +11,7 @@ module RelatonBib
11
11
  # @param form [String, nil] form of the medium
12
12
  # @param carrier [String, nil] carrier of the medium
13
13
  # @param size [String, nil] size of the medium
14
- # @param scale [String, nil]
14
+ # @param scale [String, nil]
15
15
  #
16
16
  def initialize(**args)
17
17
  @content = args[:content]
@@ -0,0 +1,312 @@
1
+ # Monkey patch to fix the issue with month quotes in BibTeX
2
+ module BibTeX
3
+ class Value
4
+ def to_s(options = {}) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
5
+ if options.key?(:filter)
6
+ opts = options.reject { |k,| k == :filter || (k == :quotes && (!atomic? || symbol?)) }
7
+ return convert(options[:filter]).to_s(opts)
8
+ end
9
+
10
+ return value.to_s unless options.key?(:quotes) && atomic?
11
+
12
+ q = Array(options[:quotes])
13
+ [q[0], value, q[-1]].compact.join
14
+ end
15
+ end
16
+ end
17
+
18
+ module RelatonBib
19
+ # BibTeX builder.
20
+ module Renderer
21
+ class BibtexBuilder
22
+ ATTRS = %i[
23
+ type id title author editor booktitle series number edition contributor
24
+ date address note relation extent classification keyword docidentifier
25
+ timestamp link
26
+ ].freeze
27
+
28
+ #
29
+ # Build BibTeX bibliography.
30
+ #
31
+ # @param bib [RelatonBib::BibliographicItem]
32
+ # @param bibtex [BibTeX::Bibliography, nil] BibTeX bibliography
33
+ #
34
+ # @return [BibTeX::Bibliography] BibTeX bibliography
35
+ #
36
+ def self.build(bib, bibtex = nil)
37
+ new(bib).build bibtex
38
+ end
39
+
40
+ #
41
+ # Initialize BibTeX builder.
42
+ #
43
+ # @param bib [RelatonBib::BibliographicItem]
44
+ def initialize(bib)
45
+ @bib = bib
46
+ end
47
+
48
+ #
49
+ # Build BibTeX bibliography.
50
+ #
51
+ # @param bibtex [BibTeX::Bibliography, nil] BibTeX bibliography
52
+ #
53
+ # @return [BibTeX::Bibliography] BibTeX bibliography
54
+ #
55
+ def build(bibtex = nil)
56
+ @item = BibTeX::Entry.new
57
+ ATTRS.each { |a| send("add_#{a}") }
58
+ bibtex ||= BibTeX::Bibliography.new
59
+ bibtex << @item
60
+ bibtex
61
+ end
62
+
63
+ private
64
+
65
+ #
66
+ # Add type to BibTeX item
67
+ #
68
+ def add_type
69
+ @item.type = bibtex_type
70
+ end
71
+
72
+ # @return [String] BibTeX type
73
+ def bibtex_type
74
+ case @bib.type
75
+ when "standard", nil then "misc"
76
+ else @bib.type
77
+ end
78
+ end
79
+
80
+ #
81
+ # Add ID to BibTeX item
82
+ #
83
+ def add_id
84
+ @item.key = @bib.id
85
+ end
86
+
87
+ #
88
+ # Add title to BibTeX item
89
+ #
90
+ def add_title
91
+ @bib.title.to_bibtex @item
92
+ end
93
+
94
+ #
95
+ # Add booktitle to BibTeX item
96
+ #
97
+ def add_booktitle
98
+ included_in = @bib.relation.detect { |r| r.type == "includedIn" }
99
+ return unless included_in
100
+
101
+ @item.booktitle = included_in.bibitem.title.first.title
102
+ end
103
+
104
+ #
105
+ # Add author to BibTeX item
106
+ #
107
+ def add_author
108
+ add_author_editor "author"
109
+ end
110
+
111
+ #
112
+ # Add editor to BibTeX item
113
+ #
114
+ def add_editor
115
+ add_author_editor "editor"
116
+ end
117
+
118
+ #
119
+ # Add author or editor to BibTeX item
120
+ #
121
+ # @param [String] type "author" or "editor"
122
+ #
123
+ def add_author_editor(type)
124
+ contribs = @bib.contributor.select do |c|
125
+ c.entity.is_a?(Person) && c.role.any? { |e| e.type == type }
126
+ end.map &:entity
127
+
128
+ return unless contribs.any?
129
+
130
+ @item.send "#{type}=", concat_names(contribs)
131
+ end
132
+
133
+ #
134
+ # Concatenate names of contributors
135
+ #
136
+ # @param [Array<RelatonBib::Person>] contribs contributors
137
+ #
138
+ # @return [String] concatenated names
139
+ #
140
+ def concat_names(contribs)
141
+ contribs.map do |p|
142
+ if p.name.surname
143
+ "#{p.name.surname}, #{p.name.forename.map(&:to_s).join(' ')}"
144
+ else
145
+ p.name.completename.to_s
146
+ end
147
+ end.join(" and ")
148
+ end
149
+
150
+ #
151
+ # Add series to BibTeX item
152
+ #
153
+ def add_series
154
+ @bib.series.each do |s|
155
+ case s.type
156
+ when "journal"
157
+ @item.journal = s.title.title
158
+ @item.number = s.number if s.number
159
+ when nil then @item.series = s.title.title
160
+ end
161
+ end
162
+ end
163
+
164
+ #
165
+ # Add number to BibTeX item
166
+ #
167
+ def add_number
168
+ return unless %w[techreport manual].include? @bib.type
169
+
170
+ did = @bib.docidentifier.detect { |i| i.primary == true }
171
+ did ||= @bib.docidentifier.first
172
+ @item.number = did.id if did
173
+ end
174
+
175
+ #
176
+ # Add edition to BibTeX item
177
+ #
178
+ def add_edition
179
+ @item.edition = @bib.edition.content if @bib.edition
180
+ end
181
+
182
+ #
183
+ # Add contributor to BibTeX item
184
+ #
185
+ def add_contributor # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
186
+ @bib.contributor.each do |c|
187
+ rls = c.role.map(&:type)
188
+ if rls.include?("publisher") then @item.publisher = c.entity.name
189
+ elsif rls.include?("distributor")
190
+ case @bib.type
191
+ when "techreport" then @item.institution = c.entity.name
192
+ when "inproceedings", "conference", "manual", "proceedings"
193
+ @item.organization = c.entity.name
194
+ when "mastersthesis", "phdthesis" then @item.school = c.entity.name
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ #
201
+ # Add date to BibTeX item
202
+ #
203
+ def add_date
204
+ @bib.date.each do |d|
205
+ case d.type
206
+ when "published"
207
+ @item.year = d.on :year
208
+ month = d.on :month
209
+ @item.month = month if month
210
+ when "accessed" then @item.urldate = d.on.to_s
211
+ end
212
+ end
213
+ end
214
+
215
+ #
216
+ # Add address to BibTeX item
217
+ #
218
+ def add_address # rubocop:disable Metrics/AbcSize
219
+ return unless @bib.place.any?
220
+
221
+ reg = @bib.place[0].region[0].name if @bib.place[0].region.any?
222
+ addr = [@bib.place[0].name, @bib.place[0].city, reg]
223
+ @item.address = addr.compact.join(", ")
224
+ end
225
+
226
+ #
227
+ # Add note to BibTeX item
228
+ #
229
+ def add_note # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
230
+ @bib.biblionote.each do |n|
231
+ case n.type
232
+ when "annote" then @item.annote = n.content
233
+ when "howpublished" then @item.howpublished = n.content
234
+ when "comment" then @item.comment = n.content
235
+ when "tableOfContents" then @item.content = n.content
236
+ when nil then @item.note = n.content
237
+ end
238
+ end
239
+ end
240
+
241
+ #
242
+ # Add relation to BibTeX item
243
+ #
244
+ def add_relation
245
+ rel = @bib.relation.detect { |r| r.type == "partOf" }
246
+ if rel
247
+ title_main = rel.bibitem.title.detect { |t| t.type == "main" }
248
+ @item.booktitle = title_main.title.content
249
+ end
250
+ end
251
+
252
+ #
253
+ # Add extent to BibTeX item
254
+ #
255
+ def add_extent
256
+ @bib.extent.each { |e| e.to_bibtex(@item) }
257
+ end
258
+
259
+ #
260
+ # Add classification to BibTeX item
261
+ #
262
+ def add_classification
263
+ @bib.classification.each do |c|
264
+ case c.type
265
+ when "type" then @item["type"] = c.value
266
+ when "mendeley" then @item["mendeley-tags"] = c.value
267
+ end
268
+ end
269
+ end
270
+
271
+ #
272
+ # Add keywords to BibTeX item
273
+ #
274
+ def add_keyword
275
+ @item.keywords = @bib.keyword.map(&:content).join(", ") if @bib.keyword.any?
276
+ end
277
+
278
+ #
279
+ # Add docidentifier to BibTeX item
280
+ #
281
+ def add_docidentifier
282
+ @bib.docidentifier.each do |i|
283
+ case i.type
284
+ when "isbn" then @item.isbn = i.id
285
+ when "lccn" then @item.lccn = i.id
286
+ when "issn" then @item.issn = i.id
287
+ end
288
+ end
289
+ end
290
+
291
+ #
292
+ # Add identifier to BibTeX item
293
+ #
294
+ def add_timestamp
295
+ @item.timestamp = @bib.fetched.to_s if @bib.fetched
296
+ end
297
+
298
+ #
299
+ # Add link to BibTeX item
300
+ #
301
+ def add_link
302
+ @bib.link.each do |l|
303
+ case l.type&.downcase
304
+ when "doi" then @item.doi = l.content
305
+ when "file" then @item.file2 = l.content
306
+ when "src" then @item.url = l.content
307
+ end
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,290 @@
1
+ module RelatonBib
2
+ module Renderer
3
+ class BibXML
4
+ def initialize(bib)
5
+ @bib = bib
6
+ end
7
+
8
+ #
9
+ # Render BibXML (RFC)
10
+ #
11
+ # @param [Nokogiri::XML::Builder, nil] builder
12
+ # @param [Boolean] include_keywords (false)
13
+ #
14
+ # @return [String, Nokogiri::XML::Builder::NodeBuilder] XML
15
+ #
16
+ def render(builder: nil, include_keywords: true)
17
+ if builder
18
+ render_bibxml builder, include_keywords
19
+ else
20
+ Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
21
+ render_bibxml xml, include_keywords
22
+ end.doc.root.to_xml
23
+ end
24
+ end
25
+
26
+ #
27
+ # Render BibXML (RFC, BCP)
28
+ #
29
+ # @param [Nokogiri::XML::Builder] builder
30
+ # @param [Boolean] include_bibdata
31
+ #
32
+ def render_bibxml(builder, include_keywords) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
33
+ target = @bib.link.detect { |l| l.type.casecmp("src").zero? } ||
34
+ @bib.link.detect { |l| l.type.casecmp("doi").zero? }
35
+ bxml = if @bib.docnumber&.match(/^BCP/) || (@bib.docidentifier.detect(&:primary)&.id ||
36
+ @bib.docidentifier[0].id).include?("BCP")
37
+ render_bibxml_refgroup(builder, include_keywords)
38
+ else
39
+ render_bibxml_ref(builder, include_keywords)
40
+ end
41
+ bxml[:target] = target.content.to_s if target
42
+ end
43
+
44
+ #
45
+ # Render BibXML (BCP)
46
+ #
47
+ # @param [Nokogiri::XML::Builder] builder
48
+ # @param [Boolean] include_keywords
49
+ #
50
+ def render_bibxml_refgroup(builder, include_keywords)
51
+ builder.referencegroup(**ref_attrs) do |b|
52
+ @bib.relation.each do |r|
53
+ r.bibitem.to_bibxml(b, include_keywords: include_keywords) if r.type == "includes"
54
+ end
55
+ end
56
+ end
57
+
58
+ #
59
+ # Render BibXML (RFC)
60
+ #
61
+ # @param [Nokogiri::XML::Builder] builder
62
+ # @param [Boolean] include_keywords
63
+ #
64
+ def render_bibxml_ref(builder, include_keywords) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
65
+ builder.reference(**ref_attrs) do |xml|
66
+ if @bib.title.any? || @bib.contributor.any? || @bib.date.any? || @bib.abstract.any? ||
67
+ @bib.editorialgroup&.technical_committee&.any? ||
68
+ (include_keywords && @bib.keyword.any?)
69
+ xml.front do
70
+ xml.title @bib.title[0].title.content if @bib.title.any?
71
+ render_authors xml
72
+ render_date xml
73
+ render_workgroup xml
74
+ render_keyword xml if include_keywords
75
+ render_abstract xml
76
+ end
77
+ end
78
+ render_seriesinfo xml
79
+ render_format xml
80
+ end
81
+ end
82
+
83
+ #
84
+ # Create reference attributes
85
+ #
86
+ # @return [Hash<Symbol=>String>] attributes
87
+ #
88
+ def ref_attrs # rubocop:disable Metrics/AbcSize
89
+ discopes = %w[anchor docName number]
90
+ attrs = @bib.docidentifier.each_with_object({}) do |di, h|
91
+ next unless discopes.include?(di.scope)
92
+
93
+ h[di.scope.to_sym] = di.id
94
+ end
95
+ return attrs if attrs.any?
96
+
97
+ @bib.docidentifier.first&.tap do |di|
98
+ anchor = di.type == "IANA" ? di.id.split[1..].join(" ").upcase : di.id
99
+ return { anchor: anchor.gsub(" ", ".") }
100
+ end
101
+ end
102
+
103
+ #
104
+ # Render authors
105
+ #
106
+ # @param [Nokogiri::XML::Builder] builder xml builder
107
+ #
108
+ def render_authors(builder) # rubocop:disable Metrics/AbcSize
109
+ @bib.contributor.each do |c|
110
+ builder.author do |xml|
111
+ xml.parent[:role] = "editor" if c.role.detect { |r| r.type == "editor" }
112
+ if c.entity.is_a?(Person) then render_person xml, c.entity
113
+ else render_organization xml, c.entity, c.role
114
+ end
115
+ render_address xml, c
116
+ end
117
+ end
118
+ end
119
+
120
+ #
121
+ # Render address
122
+ #
123
+ # @param [Nokogiri::XML::Builder] builder xml builder
124
+ # @param [RelatonBib::ContributionInfo] contrib contributor
125
+ #
126
+ def render_address(builder, contrib) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
127
+ # addr = contrib.entity.contact.reject do |cn|
128
+ # cn.is_a?(Address) && cn.postcode.nil?
129
+ # end
130
+ if contrib.entity.contact.any?
131
+ builder.address do |xml|
132
+ address = contrib.entity.contact.detect { |cn| cn.is_a? Address }
133
+ if address
134
+ xml.postal do
135
+ xml.city address.city if address.city
136
+ xml.code address.postcode if address.postcode
137
+ xml.country address.country if address.country
138
+ xml.region address.state if address.state
139
+ xml.street address.street[0] if address.street.any?
140
+ end
141
+ end
142
+ render_contact xml, contrib.entity.contact
143
+ end
144
+ end
145
+ end
146
+
147
+ #
148
+ # Render contact
149
+ #
150
+ # @param [Nokogiri::XML::Builder] builder xml builder
151
+ # @param [Array<RelatonBib::Address, RelatonBib::Contact>] addr contact
152
+ #
153
+ def render_contact(builder, addr)
154
+ %w[phone email uri].each do |type|
155
+ cont = addr.detect { |cn| cn.is_a?(Contact) && cn.type == type }
156
+ builder.send type, cont.value if cont
157
+ end
158
+ end
159
+
160
+ #
161
+ # Render person
162
+ #
163
+ # @param [Nokogiri::XML::Builder] builder xml builder
164
+ # @param [RelatonBib::Person] person person
165
+ #
166
+ def render_person(builder, person) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
167
+ render_organization builder, person.affiliation.first&.organization
168
+ if person.name.completename
169
+ builder.parent[:fullname] = person.name.completename.content
170
+ elsif person.name.forename.any?
171
+ builder.parent[:fullname] = person.name.forename.map do |n|
172
+ n.content || n.initial
173
+ end.join " "
174
+ end
175
+ if person.name.initials
176
+ builder.parent[:initials] = person.name.initials.content
177
+ elsif person.name.forename.any?
178
+ builder.parent[:initials] = person.name.forename.map do |f|
179
+ "#{f.initial || f.content[0]}."
180
+ end.join
181
+ end
182
+ if person.name.surname
183
+ if !person.name.completename && person.name.forename.any? && person.name.surname
184
+ builder.parent[:fullname] += " #{person.name.surname}"
185
+ end
186
+ builder.parent[:surname] = person.name.surname.content
187
+ end
188
+ end
189
+
190
+ #
191
+ # Render organization
192
+ #
193
+ # @param [Nokogiri::XML::Builder] builder xml builder
194
+ # @param [RelatonBib::Organization] org organization
195
+ #
196
+ def render_organization(builder, org, _role = []) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
197
+ abbrev = org&.abbreviation&.content
198
+ orgname = org&.name&.first&.content
199
+ orgname = if BibXMLParser::ORGNAMES.key?(abbrev) then abbrev
200
+ else BibXMLParser::ORGNAMES.key(orgname) || orgname || abbrev
201
+ end
202
+ # if role.detect { |r| r.description.detect { |d| d.content == "BibXML author" } }
203
+ # builder.parent[:fullname] = orgname
204
+ # else
205
+ org = builder.organization orgname
206
+ org[:abbrev] = abbrev if abbrev
207
+ # end
208
+ end
209
+
210
+ #
211
+ # Render date
212
+ #
213
+ # @param [Nokogiri::XML::Builder] builder xml builder
214
+ #
215
+ def render_date(builder) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
216
+ dt = @bib.date.detect { |d| d.type == "published" }
217
+ return unless dt
218
+
219
+ elm = builder.date
220
+ y = dt.on(:year) || dt.from(:year) || dt.to(:year)
221
+ elm[:year] = y if y
222
+ m = dt.on(:month) || dt.from(:month) || dt.to(:month)
223
+ elm[:month] = Date::MONTHNAMES[m] if m
224
+ # rfcs = %w[RFC BCP FYI STD]
225
+ # unless rfcs.include?(doctype) && docidentifier.detect { |di| rfcs.include? di.type }
226
+ d = dt.on(:day) || dt.from(:day) || dt.to(:day)
227
+ elm[:day] = d if d
228
+ # end
229
+ end
230
+
231
+ #
232
+ # Render workgroup
233
+ #
234
+ # @param [Nokogiri::XML::Builder] builder xml builder
235
+ #
236
+ def render_workgroup(builder)
237
+ @bib.editorialgroup&.technical_committee&.each do |tc|
238
+ builder.workgroup tc.workgroup.name
239
+ end
240
+ end
241
+
242
+ #
243
+ # Render keyword
244
+ #
245
+ # @param [Nokogiri::XML::Builder] builder xml builder
246
+ #
247
+ def render_keyword(builder)
248
+ @bib.keyword.each { |kw| builder.keyword kw.content }
249
+ end
250
+
251
+ #
252
+ # Render abstract
253
+ #
254
+ # @param [Nokogiri::XML::Builder] builder xml builder
255
+ #
256
+ def render_abstract(builder)
257
+ return unless @bib.abstract.any?
258
+
259
+ builder.abstract { |xml| xml << @bib.abstract[0].content.gsub(/(<\/?)p(>)/, '\1t\2') }
260
+ end
261
+
262
+ #
263
+ # Render seriesinfo
264
+ #
265
+ # @param [Nokogiri::XML::Builder] builder xml builder
266
+ #
267
+ def render_seriesinfo(builder) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
268
+ @bib.docidentifier.each do |di|
269
+ if BibXMLParser::SERIESINFONAMES.include?(di.type) && di.scope != "trademark"
270
+ builder.seriesInfo(name: di.type, value: di.id)
271
+ end
272
+ end
273
+ # di_types = docidentifier.map(&:type)
274
+ @bib.series.select do |s|
275
+ s.title && # !di_types.include?(s.title.title.to_s) &&
276
+ !BibXMLParser::SERIESINFONAMES.include?(s.title.title.to_s)
277
+ end.uniq { |s| s.title.title.to_s }.each do |s|
278
+ si = builder.seriesInfo(name: s.title.title.to_s)
279
+ si[:value] = s.number if s.number
280
+ end
281
+ end
282
+
283
+ def render_format(builder)
284
+ @bib.link.select { |l| l.type == "TXT" }.each do |l|
285
+ builder.format type: l.type, target: l.content
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
@@ -1,3 +1,3 @@
1
1
  module RelatonBib
2
- VERSION = "1.14.4".freeze
2
+ VERSION = "1.14.6".freeze
3
3
  end
data/lib/relaton_bib.rb CHANGED
@@ -11,6 +11,8 @@ require "relaton_bib/bibliographic_item"
11
11
  require "relaton_bib/hit_collection"
12
12
  require "relaton_bib/hit"
13
13
  require "relaton_bib/bibxml_parser"
14
+ require "relaton_bib/renderer/bibtex_builder"
15
+ require "relaton_bib/renderer/bibxml"
14
16
 
15
17
  module RelatonBib
16
18
  class Error < StandardError; end
@@ -66,15 +68,17 @@ module RelatonBib
66
68
  # Parse yaml content
67
69
  #
68
70
  # @param [String] yaml content
71
+ # @param [Array] classes classes to be allowed
72
+ # @param [Boolean] symbolize_names symbolize names if true (default: false)
69
73
  #
70
74
  # @return [Hash] data
71
75
  #
72
- def parse_yaml(yaml, classes = [])
76
+ def parse_yaml(yaml, classes = [], symbolize_names: false)
73
77
  # Newer versions of Psych uses the `permitted_classes:` parameter
74
78
  if YAML.method(:safe_load).parameters.map(&:last).include? :permitted_classes
75
- YAML.safe_load(yaml, permitted_classes: classes)
79
+ YAML.safe_load(yaml, permitted_classes: classes, symbolize_names: symbolize_names)
76
80
  else
77
- YAML.safe_load(yaml, classes)
81
+ YAML.safe_load(yaml, classes, symbolize_names: symbolize_names)
78
82
  end
79
83
  end
80
84
  end
data/relaton-bib.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.bindir = "exe"
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
- spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
24
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
25
25
 
26
26
  spec.add_development_dependency "byebug"
27
27
  spec.add_development_dependency "equivalent-xml", "~> 0.6"
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.14.4
4
+ version: 1.14.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: 2023-01-07 00:00:00.000000000 Z
11
+ date: 2023-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -266,6 +266,8 @@ files:
266
266
  - lib/relaton_bib/organization.rb
267
267
  - lib/relaton_bib/person.rb
268
268
  - lib/relaton_bib/place.rb
269
+ - lib/relaton_bib/renderer/bibtex_builder.rb
270
+ - lib/relaton_bib/renderer/bibxml.rb
269
271
  - lib/relaton_bib/series.rb
270
272
  - lib/relaton_bib/structured_identifier.rb
271
273
  - lib/relaton_bib/technical_committee.rb
@@ -289,7 +291,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
289
291
  requirements:
290
292
  - - ">="
291
293
  - !ruby/object:Gem::Version
292
- version: 2.6.0
294
+ version: 2.7.0
293
295
  required_rubygems_version: !ruby/object:Gem::Requirement
294
296
  requirements:
295
297
  - - ">="