relaton 3.0.0.pre.alpha.4 → 3.0.0.pre.alpha.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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/lib/relaton/3gpp/bibliography.rb +19 -6
  3. data/lib/relaton/3gpp/processor.rb +6 -0
  4. data/lib/relaton/adobe/processor.rb +9 -0
  5. data/lib/relaton/bib/converter/csl.rb +110 -0
  6. data/lib/relaton/bib/converter/ris.rb +104 -0
  7. data/lib/relaton/bib/converter/titles.rb +24 -0
  8. data/lib/relaton/bib/item_data.rb +23 -0
  9. data/lib/relaton/bib/model/item.rb +6 -6
  10. data/lib/relaton/bib/sanitizer.rb +71 -30
  11. data/lib/relaton/bib.rb +10 -0
  12. data/lib/relaton/bipm/bibliography.rb +24 -21
  13. data/lib/relaton/bipm/processor.rb +1 -0
  14. data/lib/relaton/bipm/rawdata_bipm_metrologia/affiliations.rb +6 -6
  15. data/lib/relaton/bipm/si_brochure_parser.rb +2 -2
  16. data/lib/relaton/bsi/bibliography.rb +26 -23
  17. data/lib/relaton/bsi/processor.rb +5 -0
  18. data/lib/relaton/calconnect/bibliography.rb +6 -6
  19. data/lib/relaton/calconnect/hit_collection.rb +4 -1
  20. data/lib/relaton/ccsds/bibliography.rb +16 -10
  21. data/lib/relaton/ccsds/hit_collection.rb +1 -1
  22. data/lib/relaton/ccsds/processor.rb +13 -0
  23. data/lib/relaton/cen/bibliography.rb +27 -24
  24. data/lib/relaton/cen/hit_collection.rb +1 -1
  25. data/lib/relaton/cen/processor.rb +5 -0
  26. data/lib/relaton/cen/scraper.rb +9 -9
  27. data/lib/relaton/cie/bibliography.rb +11 -9
  28. data/lib/relaton/cie/data_fetcher.rb +18 -18
  29. data/lib/relaton/cie/processor.rb +1 -0
  30. data/lib/relaton/cie/scrapper.rb +15 -8
  31. data/lib/relaton/cie.rb +1 -1
  32. data/lib/relaton/cloud.rb +127 -0
  33. data/lib/relaton/core/hit_collection.rb +6 -10
  34. data/lib/relaton/core/processor.rb +121 -0
  35. data/lib/relaton/core/request_error.rb +21 -3
  36. data/lib/relaton/db/cache.rb +474 -148
  37. data/lib/relaton/db/cache_entry.rb +42 -0
  38. data/lib/relaton/db/registry.rb +152 -0
  39. data/lib/relaton/db.rb +275 -102
  40. data/lib/relaton/doi/crossref.rb +23 -6
  41. data/lib/relaton/doi/processor.rb +1 -0
  42. data/lib/relaton/easc/processor.rb +1 -0
  43. data/lib/relaton/ecma/bibliography.rb +18 -13
  44. data/lib/relaton/ecma/data_fetcher.rb +1 -1
  45. data/lib/relaton/ecma/data_parser.rb +1 -1
  46. data/lib/relaton/ecma/edition_parser.rb +2 -2
  47. data/lib/relaton/ecma/memento_parser.rb +4 -4
  48. data/lib/relaton/ecma/standard_parser.rb +6 -6
  49. data/lib/relaton/etsi/bibliography.rb +8 -7
  50. data/lib/relaton/etsi/processor.rb +1 -0
  51. data/lib/relaton/gb/bibliography.rb +20 -12
  52. data/lib/relaton/gb/gb_scraper.rb +5 -5
  53. data/lib/relaton/gb/scraper.rb +16 -16
  54. data/lib/relaton/gb/sec_scraper.rb +8 -8
  55. data/lib/relaton/gb/t_scraper.rb +5 -5
  56. data/lib/relaton/gost/processor.rb +1 -0
  57. data/lib/relaton/iala/bibliography.rb +15 -12
  58. data/lib/relaton/iala/processor.rb +1 -0
  59. data/lib/relaton/iana/bibliography.rb +12 -12
  60. data/lib/relaton/iana/data_fetcher.rb +3 -3
  61. data/lib/relaton/iana/parser.rb +12 -5
  62. data/lib/relaton/iana/processor.rb +9 -0
  63. data/lib/relaton/iec/bibliography.rb +18 -7
  64. data/lib/relaton/iec/data_parser.rb +24 -9
  65. data/lib/relaton/iec/processor.rb +11 -0
  66. data/lib/relaton/iec.rb +1 -1
  67. data/lib/relaton/ieee/bibliography.rb +19 -13
  68. data/lib/relaton/ieee/data_fetcher.rb +1 -1
  69. data/lib/relaton/ieee/processor.rb +24 -0
  70. data/lib/relaton/ieee/rawbib_id_parser.rb +2 -2
  71. data/lib/relaton/ietf/bibliography.rb +11 -9
  72. data/lib/relaton/ietf/data_fetcher.rb +1 -1
  73. data/lib/relaton/ietf/processor.rb +1 -0
  74. data/lib/relaton/ietf/rfc/entry.rb +15 -19
  75. data/lib/relaton/ietf/scraper.rb +5 -4
  76. data/lib/relaton/iho/processor.rb +1 -0
  77. data/lib/relaton/index/file_io.rb +2 -2
  78. data/lib/relaton/index/pool.rb +4 -3
  79. data/lib/relaton/index/shard_source.rb +1 -1
  80. data/lib/relaton/index/type.rb +2 -5
  81. data/lib/relaton/isbn/open_library.rb +11 -7
  82. data/lib/relaton/isbn/processor.rb +10 -0
  83. data/lib/relaton/iso/bibliography.rb +11 -9
  84. data/lib/relaton/iso/data_parser.rb +2 -2
  85. data/lib/relaton/iso/processor.rb +5 -0
  86. data/lib/relaton/iso/scraper.rb +20 -20
  87. data/lib/relaton/itu/bibliography.rb +107 -16
  88. data/lib/relaton/itu/data_crawler_r.rb +2 -2
  89. data/lib/relaton/itu/hit_collection.rb +64 -52
  90. data/lib/relaton/itu/processor.rb +1 -0
  91. data/lib/relaton/itu/scraper.rb +13 -3
  92. data/lib/relaton/itu.rb +0 -2
  93. data/lib/relaton/jis/bibliography.rb +22 -11
  94. data/lib/relaton/jis/data_fetcher.rb +4 -4
  95. data/lib/relaton/jis/processor.rb +1 -0
  96. data/lib/relaton/jis/scraper.rb +8 -8
  97. data/lib/relaton/nist/bibliography.rb +25 -21
  98. data/lib/relaton/oasis/bibliography.rb +16 -13
  99. data/lib/relaton/oasis/browser_agent.rb +2 -2
  100. data/lib/relaton/oasis/data_parser.rb +5 -5
  101. data/lib/relaton/oasis/data_parser_utils.rb +2 -2
  102. data/lib/relaton/oasis/data_part_parser.rb +7 -7
  103. data/lib/relaton/ogc/bibliography.rb +15 -14
  104. data/lib/relaton/ogc/hit_collection.rb +9 -7
  105. data/lib/relaton/ogc/processor.rb +13 -0
  106. data/lib/relaton/oiml/processor.rb +1 -0
  107. data/lib/relaton/omg/bibliography.rb +9 -9
  108. data/lib/relaton/omg/scraper.rb +14 -13
  109. data/lib/relaton/omg.rb +1 -1
  110. data/lib/relaton/plateau/bibliography.rb +9 -7
  111. data/lib/relaton/plateau/hit_collection.rb +2 -1
  112. data/lib/relaton/plateau/processor.rb +1 -0
  113. data/lib/relaton/un/bibliography.rb +21 -10
  114. data/lib/relaton/un/processor.rb +1 -0
  115. data/lib/relaton/version.rb +1 -1
  116. data/lib/relaton/w3c/processor.rb +1 -0
  117. data/lib/relaton/xsf/bibliography.rb +11 -6
  118. data/lib/relaton.rb +13 -0
  119. metadata +60 -14
  120. data/lib/relaton/itu/pubid.rb +0 -199
@@ -24,21 +24,21 @@ module Relaton
24
24
  RECEDITIONS_URL = "#{DOMAIN}/mws/api/recommendations/getRecEditions?idrec=%<idrec>s&lang=en".freeze
25
25
  HANDLE_URL = "http://handle.itu.int/11.1002/1000/%<idrec>s-en".freeze
26
26
 
27
+ # `ref` is a Pubid::Itu identifier; its class and sector pick the route.
27
28
  def search
28
- case ref.to_ref
29
- # `ref` is a parsed Pubid, so only the abbreviated `OB.N` form survives
30
- # here — the local grammar reduces "Operational Bulletin 1096" to the
31
- # code "Operational" and drops the year, which no route can resolve.
32
- when %r{^ITU-R\sRR}, %r{\bOB\.} then request_publication
33
- when /^ITU-T/ then request_recommendation
34
- # A Report reference renders "Report ITU-R …" (the discriminator leads,
35
- # as ITU cites it), so it no longer starts with the sector — but it is
36
- # still served from the index like any other ITU-R document.
37
- when /^ITU-R\s/, /^Report\sITU-R\s/ then request_document
29
+ if publication?(ref) then request_publication
30
+ # an annex to a bulletin has no landing page, and it is not a
31
+ # recommendation either
32
+ elsif publication?(ref.root) then @array = []
33
+ else
34
+ case sector
35
+ when "T" then request_recommendation
36
+ when "R" then request_document
37
+ end
38
38
  end
39
39
  rescue Mechanize::ResponseCodeError, SocketError, Timeout::Error, Errno::ECONNRESET,
40
40
  EOFError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
41
- raise Relaton::RequestError, "Could not access #{ref.to_ref}: #{e.message}"
41
+ raise Relaton::RequestError, "Could not access #{ref}: #{e.message}"
42
42
  end
43
43
 
44
44
  def agent
@@ -70,6 +70,21 @@ module Relaton
70
70
 
71
71
  private
72
72
 
73
+ # Radio Regulations and Operational Bulletins live on www.itu.int/pub,
74
+ # not in the dataset.
75
+ def publication?(id)
76
+ id.is_a?(::Pubid::Itu::Identifiers::RadioRegulations) ||
77
+ id.is_a?(::Pubid::Itu::Identifiers::SpecialPublication)
78
+ end
79
+
80
+ # An annex or an appendix carries its sector on the recommendation it
81
+ # belongs to.
82
+ #
83
+ # @return [String, nil] "T" or "R"
84
+ def sector
85
+ (ref.sector || ref.root.sector)&.to_s
86
+ end
87
+
73
88
  # ITU-T Recommendations and supplements. The index is preferred (offline,
74
89
  # every edition, no scraping); the live path covers what the dataset does
75
90
  # not have — Implementers' Guides, the `(V##)`/`Annex` forms the data
@@ -86,9 +101,8 @@ module Relaton
86
101
 
87
102
  # @return [Array<Relaton::Itu::Hit>] newest edition first
88
103
  def index_hits
89
- pubid = index_pubid_ref
90
- return [] unless pubid
91
-
104
+ # Every edition is a candidate; Bibliography selects the year.
105
+ pubid = ref.exclude(:year, :month, :day)
92
106
  Util.info "Fetching from Relaton repository ...", key: ref.to_s
93
107
  index.search(pubid) { |i| index_match?(pubid, i[:id]) }
94
108
  .map { |r| index_hit r }.sort_by { |h| edition_key h.hit[:code] }.reverse
@@ -112,7 +126,10 @@ module Relaton
112
126
  def year_satisfied?(hits)
113
127
  return false if hits.empty?
114
128
 
115
- ref.year.nil? || hits.any? { |h| edition_key(h.hit[:code]).first == ref.year.to_i }
129
+ year = Bibliography.edition_year(ref)
130
+ return true if year.nil?
131
+
132
+ hits.any? { |h| edition_key(h.hit[:code]).first == year.to_i }
116
133
  end
117
134
 
118
135
  # The edition date of a rendered docidentifier, as `[year, month]`. Uses
@@ -135,14 +152,13 @@ module Relaton
135
152
 
136
153
  def request_document # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
137
154
  Util.info "Fetching from Relaton repository ...", key: ref.to_s
138
- # Pass the reference's parsed pubid (not a String) so Relaton::Index can
155
+ # Pass the reference as a pubid (not a String) so Relaton::Index can
139
156
  # narrow candidates by document number before the block; `index_match?`
140
157
  # then matches every edition when the reference omits the part and the
141
158
  # exact edition when it names one. Rows are Pubid::Itu objects (not
142
159
  # Comparable), so rank by the numeric edition in `code.parts` (`["3"]` → 3)
143
160
  # to return the latest — index order isn't by edition.
144
- pubid = pubid_ref
145
- row = index.search(pubid) { |i| index_match?(pubid, i[:id]) }
161
+ row = index.search(ref) { |i| index_match?(ref, i[:id]) }
146
162
  .max_by { |i| i[:id].code&.parts&.last.to_i }
147
163
  return unless row
148
164
 
@@ -155,30 +171,6 @@ module Relaton
155
171
  @array = [hit]
156
172
  end
157
173
 
158
- # Parse the reference into a Pubid::Itu identifier for the index lookup. A
159
- # ref pubid can't parse raises (a `Pubid`/`Parslet` error) and propagates to
160
- # the caller (relaton-cli / API callers rescue it), mirroring the ETSI
161
- # flavor — the consumer no longer degrades to a raw-string substring search.
162
- #
163
- # @return [::Pubid::Itu::Identifier]
164
- def pubid_ref
165
- ::Pubid::Itu.parse ref.to_ref
166
- end
167
-
168
- # The reference as a pubid for an ITU-T index lookup, stripped of its
169
- # edition date (every edition is a candidate; Bibliography selects the
170
- # year). Unlike `#pubid_ref` this **rescues** an unparseable reference: the
171
- # ITU-T path has a live fallback to degrade to, and a malformed reference
172
- # (e.g. `ITU-T G.Suppl.47`) should warn and report "Not found", not raise.
173
- #
174
- # @return [::Pubid::Itu::Identifier, nil]
175
- def index_pubid_ref
176
- undated = ref.dup.tap { |r| r.year = r.month = r.day = nil }
177
- ::Pubid::Itu.parse undated.to_ref
178
- rescue StandardError
179
- nil
180
- end
181
-
182
174
  # Does an index row's id match the reference? When the reference omits the
183
175
  # part/edition (a bare `ITU-R P.838`), every edition of that exact document
184
176
  # matches — "search all parts"; when it names a part (`ITU-R P.838-2`), only
@@ -227,9 +219,15 @@ module Relaton
227
219
  nil
228
220
  end
229
221
 
230
- # @return [String] the `rec=` value for the rec.aspx lookup
222
+ # The `rec=` value for the rec.aspx lookup: the document without its
223
+ # sector, edition date or version — "H.264", "A Suppl. 2",
224
+ # "G.989 Suppl. 1". An amendment or an annex is looked up by the
225
+ # recommendation it belongs to.
226
+ #
227
+ # @return [String]
231
228
  def rec_query
232
- ref.suppl ? "#{ref.code} Suppl. #{ref.suppl}" : ref.code
229
+ Bibliography.document(ref).exclude(:year, :month, :day, :version)
230
+ .to_s.delete_prefix("ITU-T ")
233
231
  end
234
232
 
235
233
  # @param idrec [String]
@@ -244,10 +242,14 @@ module Relaton
244
242
  # @param edition [Hash] a getRecEditions entry
245
243
  # @return [Relaton::Itu::Hit]
246
244
  def recommendation_hit(edition)
245
+ # The crawler's rec_name canonicalisation ("G 231" -> "G.231"): the
246
+ # code is parsed with Pubid::Itu, which rejects the space spelling.
247
+ require_relative "data_parser_t"
248
+ name = DataParserT.normalize_rec_name edition["rec_name"]
247
249
  # No `:ref` key: it drives Hit#gi_imp, which would route an Implementers'
248
250
  # Guide reference onto the getImplGuides endpoints.
249
251
  Hit.new({
250
- code: "ITU-T #{edition['rec_name']}",
252
+ code: "ITU-T #{name}",
251
253
  title: edition["title"],
252
254
  url: format(HANDLE_URL, idrec: edition["idrec"]),
253
255
  type: "recommendation",
@@ -279,21 +281,31 @@ module Relaton
279
281
  end
280
282
 
281
283
  # The /pub identifier for a Radio Regulation or an Operational Bulletin
282
- # (`OB.1096` → `T-SP-OB.1096-2016`). nil — no hit — rather than a bogus URL
283
- # when the bulletin reference carries no number.
284
+ # (`OB.1096` → `T-SP-OB.1096-2016`). nil — no hit — for any other special
285
+ # publication, which has no derivable landing page.
284
286
  #
285
287
  # @return [String, nil]
286
288
  def publication_id
287
- return "R-REG-RR-#{ref.year}" if ref.code == "RR"
289
+ return "R-REG-RR-#{ref.year}" if radio_regulations?
288
290
 
289
- num = ref.code[/\d+/]
290
- "T-SP-OB.#{num}-#{ref.year}" if num
291
+ "T-SP-OB.#{ref.number}-#{ref.year}" if bulletin?
291
292
  end
292
293
 
293
- # @return [String] the docidentifier-friendly code (year only, no month)
294
+ # The docidentifier (year only, no month or day). A bulletin keeps its
295
+ # sector even when the reference is the sector-less `ITU OB No. 1096`.
296
+ #
297
+ # @return [String]
294
298
  def publication_code
295
- "#{ref.prefix}-#{ref.sector} #{ref.code} (#{ref.year})"
299
+ return "ITU-R RR (#{ref.year})" if radio_regulations?
300
+
301
+ "ITU-T OB.#{ref.number} (#{ref.year})"
296
302
  end
303
+
304
+ def radio_regulations?
305
+ ref.is_a? ::Pubid::Itu::Identifiers::RadioRegulations
306
+ end
307
+
308
+ def bulletin? = ref.series.to_s == "OB"
297
309
  end
298
310
  end
299
311
  end
@@ -6,6 +6,7 @@ module Relaton
6
6
  def initialize
7
7
  @short = :relaton_itu
8
8
  @prefix = "ITU"
9
+ @pubid_identifier = :Itu # Db cache key
9
10
  @defaultprefix = %r{^ITU\s}
10
11
  @idtype = "ITU"
11
12
  @datasets = %w[itu-r itu-t]
@@ -112,13 +112,23 @@ module Relaton
112
112
  type = "ISO"
113
113
  text.match(/[^(]+/).to_s.strip.squeeze(" ")
114
114
  else
115
- pubid = Pubid.parse(text)
116
- type = pubid.prefix
117
- pubid.to_s
115
+ type = "ITU"
116
+ canonical_id text
118
117
  end => id
119
118
  Docidentifier.new(type: type, content: id, primary: true)
120
119
  end
121
120
 
121
+ # The pubid spelling of an ITU code. The code is data from ITU, so a
122
+ # form pubid cannot identify is kept as ITU wrote it.
123
+ #
124
+ # @param text [String]
125
+ # @return [String]
126
+ def canonical_id(text)
127
+ ::Pubid::Itu.parse(text).to_s
128
+ rescue ::Pubid::Errors::ParseError
129
+ text
130
+ end
131
+
122
132
  # @return [Array<Relaton::Bib::Contributor>]
123
133
  def fetch_contributors
124
134
  contribs = fetch_publisher_contributors
data/lib/relaton/itu.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require "mechanize"
2
- require "parslet"
3
2
  require "pubid"
4
3
  require "digest/md5"
5
4
  require "relaton/index"
@@ -11,7 +10,6 @@ require_relative "itu/item_data"
11
10
  require_relative "itu/model/item"
12
11
  require_relative "itu/model/bibitem"
13
12
  require_relative "itu/model/bibdata"
14
- require_relative "itu/pubid"
15
13
  require_relative "itu/scraper"
16
14
  require_relative "itu/hit_collection"
17
15
  require_relative "itu/bibliography"
@@ -13,7 +13,8 @@ module Relaton
13
13
  # the reference's series and number (a supplement is filed under its base
14
14
  # number), so an edition and its amendments are returned together.
15
15
  #
16
- # @param [String] code JIS document code
16
+ # @param [String, Pubid::Jis::Identifier] ref JIS document reference; a
17
+ # pubid is never mutated
17
18
  # @param [String, nil] year JIS document year
18
19
  #
19
20
  # @return [Relaton::Jis::HitCollection] search result
@@ -21,16 +22,21 @@ module Relaton
21
22
  # @raise [Pubid::Errors::ParseError] when the reference is not a JIS
22
23
  # identifier, so a malformed reference is not reported as "not found"
23
24
  #
24
- def search(code, year = nil)
25
- pubid = ::Pubid::Jis::Identifier.parse code
26
- pubid.year ||= year.to_i if year
25
+ def search(ref, year = nil)
26
+ pubid = ref.is_a?(String) ? ::Pubid::Jis::Identifier.parse(ref) : ref
27
+ if year && pubid.year.nil?
28
+ # Copy first: a caller's pubid is also its Db cache key.
29
+ pubid = pubid.class.from_hash(pubid.to_hash)
30
+ pubid.year = year.to_i
31
+ end
27
32
  HitCollection.new pubid
28
33
  end
29
34
 
30
35
  #
31
36
  # Get JIS document by reference
32
37
  #
33
- # @param [String] ref JIS document reference
38
+ # @param [String, Pubid::Jis::Identifier] ref JIS document reference, or
39
+ # the parse that Relaton::Db routed with (relaton#205)
34
40
  # @param [String, nil] year JIS document year
35
41
  # @param [Hash] opts options
36
42
  # @option opts [Boolean] :all_parts return all parts of document
@@ -41,16 +47,21 @@ module Relaton
41
47
  # identifier
42
48
  #
43
49
  def get(ref, year = nil, opts = {}) # rubocop:disable Metrics/AbcSize
44
- code = ref.sub(/\s\((all parts|規格群)\)/, "")
45
- opts[:all_parts] ||= !$1.nil?
46
- Util.info "Fetching from webdesk.jsa.or.jp ...", key: ref
47
- hits = search(code, year)
50
+ if ref.is_a?(String)
51
+ query = ref.sub(/\s\((all parts|規格群)\)/, "")
52
+ opts[:all_parts] ||= !$1.nil?
53
+ else
54
+ opts[:all_parts] ||= ref.all_parts?
55
+ query = ref.all_parts? ? ref.identifiers.first : ref
56
+ end
57
+ Util.info "Fetching from webdesk.jsa.or.jp ...", key: ref.to_s
58
+ hits = search(query, year)
48
59
  result = opts[:all_parts] ? hits.find_all_parts : hits.find
49
60
  if result.is_a? Bib::ItemData
50
- Util.info "Found: `#{result.docidentifier[0].content}`", key: ref
61
+ Util.info "Found: `#{result.docidentifier[0].content}`", key: ref.to_s
51
62
  return result
52
63
  end
53
- hint result, ref, year
64
+ hint result, ref.to_s, year
54
65
  end
55
66
 
56
67
  #
@@ -114,12 +114,12 @@ module Relaton
114
114
  end
115
115
 
116
116
  def parse_offset(resp) # rubocop:disable Metrics/AbcSize
117
- if resp.at('//*[@id="btnPaging"]') # first page
117
+ if resp.at_xpath('//*[@id="btnPaging"]') # first page
118
118
  xpath = '//script[contains(.,"var count =")]'
119
- @count = resp.at(xpath).text.match(/var count = (\d+);/)[1]
120
- resp.at("//*[@id='offset']")[:value].to_i
119
+ @count = resp.at_xpath(xpath).text.match(/var count = (\d+);/)[1]
120
+ resp.at_xpath("//*[@id='offset']")[:value].to_i
121
121
  else
122
- script = resp.at("//script").text
122
+ script = resp.at_xpath("//script").text
123
123
  script.match(/\("offset"\)\.value = '(\d+)'/)[1].to_i
124
124
  end
125
125
  end
@@ -10,6 +10,7 @@ module Relaton
10
10
  def initialize # rubocop:disable Lint/MissingSuper
11
11
  @short = :relaton_jis
12
12
  @prefix = "JIS"
13
+ @pubid_identifier = :Jis # Db cache key
13
14
  @defaultprefix = %r{^(JIS|TR)\s}
14
15
  @idtype = "JIS"
15
16
  @datasets = %w[jis-webdesk]
@@ -22,7 +22,7 @@ module Relaton
22
22
  end
23
23
 
24
24
  def fetch # rubocop:disable Metrics/MethodLength
25
- @doc = @agent.get(@url).at "//div[@id='main']/section"
25
+ @doc = @agent.get(@url).at_xpath "//div[@id='main']/section"
26
26
  contributors = fetch_contributor
27
27
  eg_contributor = fetch_editorialgroup_contributor
28
28
  contributors << eg_contributor if eg_contributor
@@ -33,7 +33,7 @@ module Relaton
33
33
 
34
34
  def fetch_title
35
35
  result = { "ja" => "Jpan", "en" => "Latn" }.map.with_index do |(lang, script), i|
36
- content = @doc.at("./h2/text()[#{i + 2}]").text.strip
36
+ content = @doc.at_xpath("./h2/text()[#{i + 2}]").text.strip
37
37
  Bib::Title.new content: content, language: lang, script: script
38
38
  end
39
39
  @errors[:title] &&= result.empty?
@@ -84,12 +84,12 @@ module Relaton
84
84
  end
85
85
 
86
86
  def document_id
87
- @document_id ||= @doc.at("./h2/text()[1]")&.text&.strip
87
+ @document_id ||= @doc.at_xpath("./h2/text()[1]")&.text&.strip
88
88
  end
89
89
 
90
90
  def fetch_date
91
91
  result = DATETYPES.each_with_object([]) do |(key, type), a|
92
- node = @doc.at("./div/div/div/p/text()[contains(.,'#{key}')]")
92
+ node = @doc.at_xpath("./div/div/div/p/text()[contains(.,'#{key}')]")
93
93
  next unless node
94
94
 
95
95
  at = node.text.match(/\d{4}-\d{2}-\d{2}/).to_s
@@ -116,7 +116,7 @@ module Relaton
116
116
  def langs_scripts # rubocop:disable Metrics/MethodLength
117
117
  @langs_scripts ||= begin
118
118
  result = LANGS.each_with_object([]) do |(key, lang), a|
119
- l = @doc.at(
119
+ l = @doc.at_xpath(
120
120
  "./div/div/div[@class='blockContentFile']/div/div/p[1]" \
121
121
  "/span[contains(.,'#{key}')]/following-sibling::span",
122
122
  )
@@ -132,7 +132,7 @@ module Relaton
132
132
  def fetch_status
133
133
  xpath = "./div/div/div/p/text()[contains(.,'状態')]" \
134
134
  "/following-sibling::span"
135
- st = @doc.at(xpath)
135
+ st = @doc.at_xpath(xpath)
136
136
  status_val = STATUSES[st&.text&.strip]
137
137
  @errors[:status] &&= status_val.nil?
138
138
  return unless status_val
@@ -155,7 +155,7 @@ module Relaton
155
155
  end
156
156
 
157
157
  def fetch_ics
158
- td = @doc.at("./table/tr[th[.='ICS']]/td")
158
+ td = @doc.at_xpath("./table/tr[th[.='ICS']]/td")
159
159
  @errors[:ics] &&= td.nil?
160
160
  return [] unless td
161
161
 
@@ -194,7 +194,7 @@ module Relaton
194
194
  end
195
195
 
196
196
  def fetch_editorialgroup_contributor # rubocop:disable Metrics/MethodLength
197
- node = @doc.at("./table/tr[th[.='原案作成団体']]/td")
197
+ node = @doc.at_xpath("./table/tr[th[.='原案作成団体']]/td")
198
198
  @errors[:editorialgroup] &&= node.nil?
199
199
  return unless node
200
200
 
@@ -7,18 +7,18 @@ module Relaton
7
7
  #
8
8
  # Search NIST documents by reference
9
9
  #
10
- # @param text [String] reference
10
+ # @param ref [String] reference
11
11
  #
12
12
  # @return [Relaton::Nist::HitCollection] search result
13
13
  #
14
- def search(text, year = nil, opts = {})
15
- ref = text.sub(/^NISTIR/, "NIST IR").sub(/\/Add/, " Add")
14
+ def search(ref, year = nil, opts = {})
15
+ query = ref.sub(/^NISTIR/, "NIST IR").sub(/\/Add/, " Add")
16
16
  # pubid 2.x only recognizes the addendum marker with a trailing
17
17
  # period, and only splits an uppercase part letter — canonicalize
18
18
  # both ("800-38a Add" -> "800-38A Add.") so @reference parses to the
19
19
  # same pubid the index/CSRC carry.
20
- ref = ref.sub(/\bAdd\b\.?/i, "Add.").sub(/([0-9])([a-z])(?=\s+Add\.)/) { "#{$1}#{$2.upcase}" }
21
- HitCollection.search ref, year, opts
20
+ query = query.sub(/\bAdd\b\.?/i, "Add.").sub(/([0-9])([a-z])(?=\s+Add\.)/) { "#{$1}#{$2.upcase}" }
21
+ HitCollection.search query, year, opts
22
22
  rescue OpenURI::HTTPError, SocketError, OpenSSL::SSL::SSLError => e
23
23
  raise Relaton::RequestError, e.message
24
24
  end
@@ -26,34 +26,38 @@ module Relaton
26
26
  #
27
27
  # Get NIST document by reference
28
28
  #
29
- # @param code [String] the NIST standard Code to look up
29
+ # @param ref [String, Pubid::Nist::Identifier] the NIST standard Code
30
+ # to look up, or its parse from Relaton::Db (relaton#205)
30
31
  # @param year [String] the year the standard was published (optional)
31
32
  # @param opts [Hash] options
32
33
  # @option opts [Boolean] :all_parts restricted to all parts
33
34
  #
34
35
  # @return [Relaton::Nist::ItemData, nil] bibliographic item
35
36
  #
36
- def get(code, year = nil, opts = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
37
- return fetch_ref_err(code, year, []) if code.match?(/\sEP$/)
37
+ def get(ref, year = nil, opts = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
38
+ # The lookup below works on text, so a pubid from Relaton::Db is
39
+ # read back as its printed form; the pubid itself is not changed.
40
+ ref = ref.to_s unless ref.is_a?(String)
41
+ return fetch_ref_err(ref, year, []) if ref.match?(/\sEP$/)
38
42
 
39
- /^(?<code2>[^(]+)(?:\((?<date2>\w+\s(?:\d{2},\s)?\d{4})\))?\s?\(?(?:(?<=\()(?<stage>(?:I|F|\d)PD))?/ =~ code
40
- stage ||= /(?<=\.)PD-\w+(?=\.)/.match(code)&.to_s
43
+ /^(?<code2>[^(]+)(?:\((?<date2>\w+\s(?:\d{2},\s)?\d{4})\))?\s?\(?(?:(?<=\()(?<stage>(?:I|F|\d)PD))?/ =~ ref
44
+ stage ||= /(?<=\.)PD-\w+(?=\.)/.match(ref)&.to_s
41
45
  if code2
42
- code = code2.strip
46
+ ref = code2.strip
43
47
  opts[:date] = parse_date(date2, str: false) if date2
44
48
  opts[:stage] = stage if stage
45
49
  end
46
50
 
47
51
  if year.nil?
48
- /^(?<code1>[^:]+):(?<year1>[^:]+)$/ =~ code
52
+ /^(?<code1>[^:]+):(?<year1>[^:]+)$/ =~ ref
49
53
  unless code1.nil?
50
- code = code1
54
+ ref = code1
51
55
  year = year1
52
56
  end
53
57
  end
54
58
 
55
- code += "-1" if opts[:all_parts]
56
- nistbib_get(code, year, opts)
59
+ ref += "-1" if opts[:all_parts]
60
+ nistbib_get(ref, year, opts)
57
61
  end
58
62
 
59
63
  private
@@ -61,14 +65,14 @@ module Relaton
61
65
  #
62
66
  # Get NIST document by reference
63
67
  #
64
- # @param [String] code reference
68
+ # @param [String] ref reference
65
69
  # @param [String] year year
66
70
  # @param [Hash] opts options
67
71
  #
68
72
  # @return [Relaton::Nist::ItemData, nil] bibliographic item
69
73
  #
70
- def nistbib_get(code, year, opts)
71
- result = nistbib_search_filter(code, year, opts) || (return nil)
74
+ def nistbib_get(ref, year, opts)
75
+ result = nistbib_search_filter(ref, year, opts) || (return nil)
72
76
  ret = nistbib_results_filter(result, year, opts)
73
77
  if ret[:ret]
74
78
  Util.info "Found: `#{ret[:ret].docidentifier.first.content}`", key: result.reference
@@ -153,14 +157,14 @@ module Relaton
153
157
  #
154
158
  # Get search results and filter them by code and year
155
159
  #
156
- # @param code [String] reference
160
+ # @param ref [String] reference
157
161
  # @param year [String, nil] year
158
162
  # @param opts [Hash] options
159
163
  #
160
164
  # @return [Relaton::Nist::HitCollection] hits collection
161
165
  #
162
- def nistbib_search_filter(code, year, opts)
163
- result = search(code, year, opts)
166
+ def nistbib_search_filter(ref, year, opts)
167
+ result = search(ref, year, opts)
164
168
  result.search_filter
165
169
  end
166
170
 
@@ -16,16 +16,16 @@ module Relaton
16
16
  OPTIONAL = %i[version stage part label].freeze
17
17
 
18
18
  class << self
19
- def search(text, _year = nil, _opts = {}) # rubocop:disable Metrics/MethodLength
20
- Util.info "Fetching from Relaton repository ...", key: text
21
- row = find_index_entry(text)
19
+ def search(ref, _year = nil, _opts = {}) # rubocop:disable Metrics/MethodLength
20
+ Util.info "Fetching from Relaton repository ...", key: ref.to_s
21
+ row = find_index_entry(ref)
22
22
  unless row
23
- Util.info "Not found.", key: text
23
+ Util.info "Not found.", key: ref.to_s
24
24
  return
25
25
  end
26
26
 
27
27
  uri = URI("#{ENDPOINT}#{row[:file]}")
28
- parse_item(fetch_yaml(uri), text)
28
+ parse_item(fetch_yaml(uri), ref)
29
29
  rescue SocketError, Errno::EINVAL, Errno::ECONNRESET, EOFError,
30
30
  Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
31
31
  Net::ProtocolError, Net::ReadTimeout,
@@ -78,11 +78,11 @@ module Relaton
78
78
  # by substring, so `OASIS amqp` resolved to some `amqp-core` record;
79
79
  # v2 matches identifiers, so a partial name no longer resolves.
80
80
  #
81
- # @param text [String] the reference as the caller wrote it
81
+ # @param ref [String] the reference as the caller wrote it
82
82
  # @return [Hash, nil] the index row, or nil
83
83
  #
84
- def find_index_entry(text)
85
- pubid = parse_ref text
84
+ def find_index_entry(ref)
85
+ pubid = parse_ref ref
86
86
  return unless pubid
87
87
 
88
88
  rows = index.search(pubid)
@@ -111,12 +111,15 @@ module Relaton
111
111
  # Rescuing here would collapse "this identifier is malformed" into "no
112
112
  # such document", leaving a caller unable to tell them apart.
113
113
  #
114
- # @param text [String]
114
+ # @param ref [String, Pubid::Oasis::Identifier]
115
115
  # @return [Pubid::Oasis::Identifier]
116
116
  # @raise [Pubid::Errors::ParseError]
117
117
  #
118
- def parse_ref(text)
119
- slug = text.to_s.strip.sub(/\A#{Regexp.escape PREFIX}/i, "")
118
+ def parse_ref(ref)
119
+ # A parsed pubid comes from Relaton::Db (relaton#205); it is used as it is.
120
+ return ref unless ref.is_a?(String)
121
+
122
+ slug = ref.to_s.strip.sub(/\A#{Regexp.escape PREFIX}/i, "")
120
123
  ::Pubid::Oasis::Identifier.parse "#{PREFIX}#{slug}"
121
124
  end
122
125
 
@@ -190,9 +193,9 @@ module Relaton
190
193
  resp.body
191
194
  end
192
195
 
193
- def parse_item(yaml, text)
196
+ def parse_item(yaml, ref)
194
197
  item = Item.from_yaml yaml
195
- Util.info "Found: `#{item.docidentifier.first.content}`", key: text
198
+ Util.info "Found: `#{item.docidentifier.first.content}`", key: ref.to_s
196
199
  item.tap { |i| i.fetched = Date.today.to_s }
197
200
  end
198
201
  end
@@ -1,5 +1,5 @@
1
1
  require "ferrum"
2
- require "nokogiri"
2
+ require "moxml"
3
3
 
4
4
  module Relaton
5
5
  module Oasis
@@ -42,7 +42,7 @@ module Relaton
42
42
  def get(url)
43
43
  @browser.go_to(url)
44
44
  wait_for_challenge
45
- Nokogiri::HTML(@browser.body)
45
+ Moxml.new.parse_html(@browser.body)
46
46
  end
47
47
 
48
48
  # HTTP status code of the most recent navigation's main resource,
@@ -7,7 +7,7 @@ module Relaton
7
7
  #
8
8
  # Initialize parser.
9
9
  #
10
- # @param [Nokogiri::HTML::Element] node document node
10
+ # @param [Moxml::Element] node document node
11
11
  #
12
12
  def initialize(node, errors = {}, agent: nil)
13
13
  @node = node
@@ -16,14 +16,14 @@ module Relaton
16
16
  end
17
17
 
18
18
  def title
19
- @title ||= @node.at("./summary/div/h2").text
19
+ @title ||= @node.at_xpath("./summary/div/h2").text
20
20
  end
21
21
 
22
22
  def text
23
23
  xpath = "./div/div/div[contains(@class, " \
24
24
  "'standard__grid--cite-as')]" \
25
25
  "/p[em or i or a or span]"
26
- @text ||= @node.at(xpath)&.text&.strip
26
+ @text ||= @node.at_xpath(xpath)&.text&.strip
27
27
  end
28
28
 
29
29
  #
@@ -144,7 +144,7 @@ module Relaton
144
144
  xpath = "./div/div/div[contains(@class, " \
145
145
  "'standard__grid--cite-as')]" \
146
146
  "/p[strong or span/strong]/a"
147
- @link_node ||= @node.at(xpath)
147
+ @link_node ||= @node.at_xpath(xpath)
148
148
  end
149
149
 
150
150
  #
@@ -220,7 +220,7 @@ module Relaton
220
220
  parts = document_part_refs
221
221
  result = case parts.size
222
222
  when 0
223
- txt = @node.at("./summary/div/h2").text
223
+ txt = @node.at_xpath("./summary/div/h2").text
224
224
  parse_spec title_to_docid(txt)
225
225
  when 1 then parse_part parse_spec(parts[0])
226
226
  else parts_to_docid parts