relaton-iho 2.0.0.pre.alpha.3 → 2.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5857d0036fa3059a90923736f457dc8b38583be92860ea91ae73e3f401057ae9
4
- data.tar.gz: 1cf8ff7208ecfefa13ae943cb2fbdd7d8d6332dbbecf2276ea2ca11186d09c9b
3
+ metadata.gz: 925a166a381b8adb5378310ef223267789270224fe165464a13f925077509b4d
4
+ data.tar.gz: 370e60d822569a76887517f7a78406573974ba1696cc6b789960c34108e2b61a
5
5
  SHA512:
6
- metadata.gz: 863b486a65712fa0341f123163ff3fdf36705ca6466a6c075de916b10d85d3c93f1dcb26ce8f53a2e046d00359f01b69521c1ae7093db6145beb6eb88884b77b
7
- data.tar.gz: 9062b35aaae23d8739f01d32d0a513e0047116b17dee1be2a2608bc43c3bfbbf9270e0f258cd4d3abc9298d483dc8972b225a81fd3a01f2670e8949bbe54fbcf
6
+ metadata.gz: 17f0a775934419440d44bb7eebfa9dea5e9057c0d642ddd508756a5f03b6e5c19ba8afbc84d295455d93b75d667f5abb7429d6bf3e43c8f1364b78020e2077e2
7
+ data.tar.gz: 67634f4b8e47c58e46e03ab5c6a27856c8cfa4ea0b9187ae93e957ff3612578c119dc586740eea0fd23140fa2ab333d62ec4794c1c11c6ce6357710a197a175e
data/CLAUDE.md CHANGED
@@ -64,3 +64,7 @@ In `item.rb`, `require_relative "relation"` is placed **after** the `Item` class
64
64
  ### IHO-Specific Document Types
65
65
 
66
66
  policy-and-procedures, best-practices, supporting-document, report, legal, directives, proposal, standard
67
+
68
+ ## Testing
69
+
70
+ - **Index fixture:** `spec/fixtures/index-v1.zip` is pre-loaded into `Relaton::Index` pool in `before(:suite)` (configured in `spec/support/webmock.rb`). Run `rake spec:update_index` to refresh from relaton-data-iho.
data/README.adoc CHANGED
@@ -137,6 +137,8 @@ RelatonIho uses the relaton-logger gem for logging. By default, it logs to STDOU
137
137
 
138
138
  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.
139
139
 
140
+ To update the index test fixture (used by tests), run `rake spec:update_index`. This downloads the latest `index-v1.zip` from the https://github.com/relaton/relaton-data-iho[relaton-data-iho] repository.
141
+
140
142
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to https://rubygems.org[rubygems.org].
141
143
 
142
144
  == Contributing
data/Rakefile CHANGED
@@ -4,3 +4,27 @@ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
+
8
+ namespace :spec do
9
+ desc "Download latest IHO index fixture from relaton-data-iho"
10
+ task :update_index do
11
+ require "net/http"
12
+ require "uri"
13
+ require_relative "lib/relaton/iho"
14
+
15
+ filename = "#{Relaton::Iho::INDEXFILE}.zip"
16
+ url = "https://raw.githubusercontent.com/relaton/relaton-data-iho/data-v2/\#{filename}"
17
+ dest = File.join(__dir__, "spec", "fixtures", filename)
18
+
19
+ puts "Downloading \#{url} ..."
20
+ uri = URI.parse(url)
21
+ response = Net::HTTP.get_response(uri)
22
+
23
+ if response.is_a?(Net::HTTPSuccess)
24
+ File.binwrite(dest, response.body)
25
+ puts "Updated \#{dest} (\#{response.body.bytesize} bytes)"
26
+ else
27
+ abort "Failed to download: HTTP \#{response.code}"
28
+ end
29
+ end
30
+ end
@@ -2,7 +2,7 @@ require "net/http"
2
2
 
3
3
  module Relaton
4
4
  module Iho
5
- module Bibliography
5
+ module Bibliography
6
6
  ENDPOINT = "https://raw.githubusercontent.com/relaton/relaton-data-iho/refs/heads/data-v2/".freeze
7
7
 
8
8
  class << self
@@ -14,12 +14,11 @@ module Relaton
14
14
  # @return [RelatonIho::IhoBibliographicItem, nil] the IHO standard or nil if not found
15
15
  #
16
16
  def search(text, _year = nil, _opts = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
17
- Util.info "Fetching from Relaton repository ...", key: text
18
- ref = text.sub(/^IHO\s/, "").sub(/^([[:alpha:]]+)(\d+)/, '\1-\2')
19
- index = Relaton::Index.find_or_create :iho, url: "#{ENDPOINT}#{INDEXFILE}.zip"
20
- row = index.search(ref).min_by { |r| r[:id] }
17
+ pubid = text.is_a?(String) ? ::Pubid::Iho::Identifier.parse(text) : text
18
+ Util.info "Fetching from Relaton repository ...", key: pubid.to_s
19
+ row = index.search { |r| pubid_match?(r[:id], pubid) }.min_by { |r| row_version(r[:id]) }
21
20
  unless row
22
- Util.info "Not found.", key: text
21
+ Util.info "Not found.", key: pubid.to_s
23
22
  return
24
23
  end
25
24
 
@@ -30,7 +29,8 @@ module Relaton
30
29
  end
31
30
 
32
31
  item = Relaton::Iho::Item.from_yaml resp.body
33
- Util.info "Found: `#{item.docidentifier.first.content}`", key: text
32
+ enrich_with_pubid(item, pubid)
33
+ Util.info "Found: `#{item.docidentifier.first.content}`", key: pubid.to_s
34
34
  item.tap { |i| i.fetched = Date.today.to_s }
35
35
  rescue SocketError, Errno::EINVAL, Errno::ECONNRESET, EOFError,
36
36
  Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
@@ -39,6 +39,31 @@ module Relaton
39
39
  raise Relaton::RequestError, "Could not access #{uri}: #{e.message}"
40
40
  end
41
41
 
42
+ # Populate ext.structuredidentifier from the parsed Pubid when the
43
+ # fetched record doesn't already provide one. Maps `pubid.number`
44
+ # (with type prefix, e.g. `S-100`) -> docnumber, `pubid.part` -> part,
45
+ # `pubid.appendix` -> appendixid, `pubid.annex` -> annexid,
46
+ # `pubid.supplement` -> supplementid.
47
+ def enrich_with_pubid(item, pubid)
48
+ return if item.ext&.structuredidentifier&.any?
49
+
50
+ sid = StructuredIdentifier.new(
51
+ docnumber: pubid_docnumber(pubid),
52
+ part: pubid.part,
53
+ appendixid: (pubid.appendix if pubid.respond_to?(:appendix)),
54
+ annexid: (pubid.annex if pubid.respond_to?(:annex)),
55
+ supplementid: (pubid.supplement if pubid.respond_to?(:supplement)),
56
+ )
57
+ item.ext ||= Ext.new
58
+ item.ext.structuredidentifier = [sid]
59
+ end
60
+
61
+ # "S-100" rather than "100" — keeps the type prefix that distinguishes
62
+ # the IHO series (S/P/M/B/C). Matches spec/fixtures/iho_part.xml.
63
+ def pubid_docnumber(pubid)
64
+ pubid.to_s.sub(/^IHO\s/, "").split(/\s+/, 2).first
65
+ end
66
+
42
67
  # @param ref [String] the IHO standard Code to look up (e..g "IHO B-11")
43
68
  # @param year [String] the year the standard was published (optional)
44
69
  #
@@ -51,6 +76,49 @@ module Relaton
51
76
  def get(ref, year = nil, opts = {})
52
77
  search(ref, year, opts)
53
78
  end
79
+
80
+ private
81
+
82
+ def index
83
+ Relaton::Index.find_or_create(
84
+ :iho,
85
+ url: "#{ENDPOINT}#{INDEXFILE}.zip",
86
+ file: "#{INDEXFILE}.yaml",
87
+ id_keys: %i[publisher number type version part appendix annex supplement],
88
+ pubid_class: ::Pubid::Iho::Identifier,
89
+ )
90
+ end
91
+
92
+ def pubid_match?(row_id, query)
93
+ row_attrs = row_attributes(row_id)
94
+ return false unless row_attrs
95
+
96
+ query_attrs = query.to_h
97
+ # Subdivision keys (part/appendix/annex/supplement) use strict
98
+ # equality — a nil query must match a nil row (the umbrella),
99
+ # not an arbitrary subdivision under the same (number, version).
100
+ # Only :version stays nil-tolerant: an unqualified `IHO B-11`
101
+ # query is expected to find the latest edition.
102
+ row_attrs[:publisher] == query_attrs[:publisher] &&
103
+ row_attrs[:type] == query_attrs[:type] &&
104
+ row_attrs[:number] == query_attrs[:number] &&
105
+ (query_attrs[:version].nil? || row_attrs[:version].to_s == query_attrs[:version].to_s) &&
106
+ row_attrs[:part].to_s == query_attrs[:part].to_s &&
107
+ row_attrs[:appendix].to_s == query_attrs[:appendix].to_s &&
108
+ row_attrs[:annex].to_s == query_attrs[:annex].to_s &&
109
+ row_attrs[:supplement].to_s == query_attrs[:supplement].to_s
110
+ end
111
+
112
+ def row_attributes(row_id)
113
+ return row_id.to_h if row_id.is_a?(::Pubid::Core::Identifier::Base)
114
+ return row_id if row_id.is_a?(Hash)
115
+
116
+ nil
117
+ end
118
+
119
+ def row_version(row_id)
120
+ row_attributes(row_id)&.dig(:version).to_s
121
+ end
54
122
  end
55
123
  end
56
124
  end
@@ -0,0 +1,35 @@
1
+ module Relaton
2
+ module Iho
3
+ class Docidentifier < Bib::Docidentifier
4
+ attr_reader :pubid
5
+
6
+ def initialize(attrs = {}, options = {})
7
+ pubid = attrs.is_a?(Hash) ? attrs.delete(:pubid) : nil
8
+ attrs[:content] ||= pubid.to_s if pubid
9
+ super
10
+ @pubid = pubid if pubid
11
+ end
12
+
13
+ def content=(value)
14
+ super
15
+ @pubid = ::Pubid::Iho::Identifier.parse(value) if value
16
+ end
17
+
18
+ def to_h
19
+ @pubid&.to_h || super
20
+ end
21
+
22
+ def remove_part!
23
+ @pubid&.part = nil
24
+ end
25
+
26
+ def remove_date!
27
+ @pubid&.year = nil
28
+ end
29
+
30
+ def to_all_parts!
31
+ @pubid&.all_parts = true
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,11 +1,14 @@
1
1
  require_relative "doctype"
2
2
  require_relative "comment_period"
3
+ require_relative "structured_identifier"
3
4
 
4
5
  module Relaton
5
6
  module Iho
6
7
  class Ext < Bib::Ext
7
8
  attribute :doctype, Doctype, default: -> { Doctype.new(content: "standard") }
8
9
  attribute :commentperiod, CommentPeriod
10
+ attribute :structuredidentifier, StructuredIdentifier,
11
+ collection: true, initialize_empty: true
9
12
 
10
13
  xml do
11
14
  map_element "commentperiod", to: :commentperiod
@@ -19,9 +19,20 @@ module Relaton
19
19
  ret[:ext][:flavor] ||= flavor(ret)
20
20
  ics_hash_to_bib ret
21
21
  commentperiod_hash_to_bib ret
22
+ structuredidentifier_hash_to_bib ret
22
23
  ret[:ext] = Ext.new(**ret[:ext])
23
24
  end
24
25
 
26
+ def structuredidentifier_hash_to_bib(ret)
27
+ sid = ret.dig(:ext, :structuredidentifier) || ret[:structuredidentifier]
28
+ return unless sid
29
+
30
+ ret[:ext]&.delete(:structuredidentifier)
31
+ ret.delete(:structuredidentifier)
32
+ ret[:ext][:structuredidentifier] =
33
+ array(sid).map { |s| StructuredIdentifier.new(**s) }
34
+ end
35
+
25
36
  def commentperiod_hash_to_bib(ret)
26
37
  cp = ret.dig(:ext, :commentperiod) || ret[:commentperiod]
27
38
  return unless cp
@@ -124,6 +135,10 @@ module Relaton
124
135
  def create_relation(rel)
125
136
  Relation.new(**rel)
126
137
  end
138
+
139
+ def create_docid(**args)
140
+ Docidentifier.new(**args)
141
+ end
127
142
  end
128
143
  end
129
144
  end
@@ -1,5 +1,6 @@
1
1
  require_relative "item_data"
2
2
  require_relative "ext"
3
+ require_relative "docidentifier"
3
4
 
4
5
  module Relaton
5
6
  module Iho
@@ -7,6 +8,8 @@ module Relaton
7
8
  model ItemData
8
9
 
9
10
  attribute :ext, Ext
11
+ attribute :docidentifier, Docidentifier, collection: true,
12
+ initialize_empty: true
10
13
  end
11
14
  end
12
15
  end
@@ -0,0 +1,20 @@
1
+ module Relaton
2
+ module Iho
3
+ class StructuredIdentifier < Lutaml::Model::Serializable
4
+ attribute :docnumber, :string
5
+ attribute :part, :string
6
+ attribute :annexid, :string
7
+ attribute :appendixid, :string
8
+ attribute :supplementid, :string
9
+
10
+ xml do
11
+ root "structuredidentifier"
12
+ map_element "docnumber", to: :docnumber
13
+ map_element "part", to: :part
14
+ map_element "annexid", to: :annexid
15
+ map_element "appendixid", to: :appendixid
16
+ map_element "supplementid", to: :supplementid
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  module Relaton
2
2
  module Iho
3
- VERSION = "2.0.0-alpha.3".freeze
3
+ VERSION = "2.0.3".freeze
4
4
  end
5
5
  end
data/lib/relaton/iho.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require "relaton/bib"
2
2
  require "relaton/index"
3
+ require "pubid/iho"
3
4
  require_relative "iho/version"
4
5
  require_relative "iho/util"
6
+ require_relative "iho/docidentifier"
5
7
  require_relative "iho/item"
6
8
  require_relative "iho/bibitem"
7
9
  require_relative "iho/bibdata"
@@ -9,7 +11,7 @@ require_relative "iho/bibliography"
9
11
 
10
12
  module Relaton
11
13
  module Iho
12
- INDEXFILE = "index-v1".freeze
14
+ INDEXFILE = "index-v2".freeze
13
15
 
14
16
  class Error < StandardError; end
15
17
 
data/relaton_iho.gemspec CHANGED
@@ -22,7 +22,8 @@ Gem::Specification.new do |s|
22
22
  s.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
23
23
 
24
24
  s.add_dependency "base64"
25
- s.add_dependency "relaton-bib", "~> 2.0.0-alpha.7"
25
+ s.add_dependency "pubid-iho", "~> 1.15.15"
26
+ s.add_dependency "relaton-bib", "~> 2.0.0"
26
27
  s.add_dependency "relaton-core", "~> 0.0.13"
27
28
  s.add_dependency "relaton-index", "~> 0.2.0"
28
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relaton-iho
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre.alpha.3
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -23,20 +23,34 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: pubid-iho
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.15.15
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.15.15
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: relaton-bib
28
42
  requirement: !ruby/object:Gem::Requirement
29
43
  requirements:
30
44
  - - "~>"
31
45
  - !ruby/object:Gem::Version
32
- version: 2.0.0.pre.alpha.7
46
+ version: 2.0.0
33
47
  type: :runtime
34
48
  prerelease: false
35
49
  version_requirements: !ruby/object:Gem::Requirement
36
50
  requirements:
37
51
  - - "~>"
38
52
  - !ruby/object:Gem::Version
39
- version: 2.0.0.pre.alpha.7
53
+ version: 2.0.0
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: relaton-core
42
56
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +107,7 @@ files:
93
107
  - lib/relaton/iho/bibitem.rb
94
108
  - lib/relaton/iho/bibliography.rb
95
109
  - lib/relaton/iho/comment_period.rb
110
+ - lib/relaton/iho/docidentifier.rb
96
111
  - lib/relaton/iho/doctype.rb
97
112
  - lib/relaton/iho/ext.rb
98
113
  - lib/relaton/iho/hash_parser_v1.rb
@@ -101,6 +116,7 @@ files:
101
116
  - lib/relaton/iho/item_data.rb
102
117
  - lib/relaton/iho/processor.rb
103
118
  - lib/relaton/iho/relation.rb
119
+ - lib/relaton/iho/structured_identifier.rb
104
120
  - lib/relaton/iho/util.rb
105
121
  - lib/relaton/iho/version.rb
106
122
  - relaton_iho.gemspec