pubid 2.0.0.pre.alpha.40 → 2.0.0.pre.alpha.42

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: c1e4c5a6889f61e535b4d05517aff5798eb5307c79bf7e318ab21822ebfe73ae
4
- data.tar.gz: f129c70c4c9c646318c58e12d7a5e1097299c5698a083a0710e2866a2e7ad692
3
+ metadata.gz: 727766fd99f726fa80503d1b6c0a069a889b9837d100508cb94f11989b28c1c4
4
+ data.tar.gz: d748fad2cb8845c858a03b78aa303644f6c6f24eaeb83a3fa0803f56cb6a0039
5
5
  SHA512:
6
- metadata.gz: d2e7e5cea5f0749a2aa5715521a2d6c192b43395bcb9f72470207629f6e7d1f0b9a7dbb089a1cc8e23c41a6ffc61a1e1424ce2546618249ed3eb074d611b3e4f
7
- data.tar.gz: c4ee9f3166fa9caffb6f3b85d4684c11b6f08ac615ecf302af94831a9e7538a29264c2a862ffefa161fbeb4dbabc11b82405730c2435d5d666ab42c257fe2a61
6
+ metadata.gz: 52c2d82f4dd10de849784edc147c35a9747f1c3f77b23f3e971d976bad8f1f930e58f9b99904ed8542c7bed2be7eef94c564ded781283c0f008c9426f42e59ca
7
+ data.tar.gz: 0c614594edcdd773af2d5d4240362fa32ee088625bfa3a8cc819bc40d6c697904c53b0a54ccb5ca47c4f35e1f0f02a711fb55a731d5a05395d455a9b6e0be874
@@ -90,4 +90,4 @@ The general fix (`metanorma__pubid__exclude-recursion-depth` hand-off) promotes
90
90
 
91
91
  Verified: `spec/pubid/supplement_date_attributes_spec.rb` exercises the mechanism directly; `spec/pubid/bsi/parse_gaps_spec.rb`, `match_primitives_spec.rb` and `adoption_base_spec.rb`, and `spec/pubid/cen_cenelec/relaton_blockers_spec.rb`'s 10 pinned `:supplement_year` cases, all pass unchanged. Because the new logic only fires when `supplement_date_attributes` is non-empty, every other flavor (including ISO/IEC, which deliberately keep a supplement's year in `date` with no protection — see `lib/pubid/cen_cenelec/CLAUDE.md`) is untouched.
92
92
 
93
- **Open question, not resolved here: `#dated_version_of?`/`#draft_of?` on a bare BSI supplement.** Both build on `matches?(other, ignore: [:date, :year, …])`, which — like every other bare `:date`/`:year` exclusion — now protects a supplement's own date unless `:supplement_year` is also passed. Two standalone `BS 7273-4:2015+A1:2021`/`...+A1:2022` objects were `dated_version_of?` each other before this branch (BSI's old `year`-attribute dodge never protected against `exclude(:year)`) and are not after. **Whether that's the right answer is genuinely unclear and deliberately left open pending feedback** — one reading is that this just makes BSI consistent with CEN/CENELEC, which already behaved this way before this branch (its pre-existing per-flavor `#exclude` override had the same protection); the other is that these two generic relational predicates should be widened to accept a `:supplement_year`-aware `ignore` set so they can still recognize two differently-dated editions of the *same supplement* as related. Neither predicate was touched in this branch — this is recorded, not decided. Locked (as current behavior, not as a verdict) by `spec/pubid/supplement_date_attributes_spec.rb`.
93
+ **Open question, not resolved here: `#dated_version_of?`/`#draft_of?` on a bare BSI supplement.** Both compare `exclude(:date, :year[, …])`, which — like every other bare `:date`/`:year` exclusion — now protects a supplement's own date unless `:supplement_year` is also passed. Two standalone `BS 7273-4:2015+A1:2021`/`...+A1:2022` objects were `dated_version_of?` each other before this branch (BSI's old `year`-attribute dodge never protected against `exclude(:year)`) and are not after. **Whether that's the right answer is genuinely unclear and deliberately left open pending feedback** — one reading is that this just makes BSI consistent with CEN/CENELEC, which already behaved this way before this branch (its pre-existing per-flavor `#exclude` override had the same protection); the other is that these two generic relational predicates should be widened to accept a `:supplement_year`-aware exclusion set so they can still recognize two differently-dated editions of the *same supplement* as related. (The predicates switched from `matches?(other, ignore: …)` to the equivalent `exclude(…)` comparison so NIST's asymmetric `#matches?` override no longer breaks them; BSI behavior is unchanged.) Locked (as current behavior, not as a verdict) by `spec/pubid/supplement_date_attributes_spec.rb`.
@@ -1177,10 +1177,46 @@ module Pubid
1177
1177
 
1178
1178
  # Self and +other+ refer to the same document but differ only in the
1179
1179
  # trailing date / year — the "dated vs undated" reference pattern.
1180
+ # Compares #exclude, not #matches?: NIST overrides #matches? as an
1181
+ # asymmetric wildcard query (and takes no ignore: keyword).
1180
1182
  def dated_version_of?(other)
1181
1183
  return false unless other.is_a?(::Pubid::Identifier)
1182
1184
 
1183
- matches?(other, ignore: [:date, :year]) && self != other
1185
+ exclude(:date, :year) == other.exclude(:date, :year) && self != other
1186
+ end
1187
+
1188
+ # The attributes that carry a revision marker. Flavors spell it
1189
+ # differently: OGC and IEEE hold a string (+revision+), NIST folds the
1190
+ # lettered revision and its date into an +update+ component.
1191
+ REVISION_ATTRIBUTES = %i[revision update update_component].freeze
1192
+
1193
+ # The revision marker this identifier carries, or nil. See
1194
+ # REVISION_ATTRIBUTES.
1195
+ # @return [String, Components::Update, nil]
1196
+ def revision_marker
1197
+ REVISION_ATTRIBUTES.each do |name|
1198
+ next unless self.class.attributes.key?(name)
1199
+
1200
+ value = public_send(name)
1201
+ return value unless value.nil? || value == ""
1202
+ end
1203
+ nil
1204
+ end
1205
+
1206
+ # Self is a revision of +other+: the same document, where self carries a
1207
+ # revision marker ("OGC 06-121r9", "NBS LC 800 rJun1992") that +other+
1208
+ # lacks or spells differently. A document whose revision is modelled as
1209
+ # an edition is covered by #edition_of? instead.
1210
+ def revision_of?(other)
1211
+ return false unless other.is_a?(::Pubid::Identifier)
1212
+ return false if self == other
1213
+ return false if revision_marker.nil?
1214
+
1215
+ # #exclude, not #matches?: NIST overrides #matches? as an asymmetric
1216
+ # wildcard query (and takes no ignore: keyword), which would compare a
1217
+ # query against a candidate rather than two concrete documents.
1218
+ exclude(*REVISION_ATTRIBUTES) == other.exclude(*REVISION_ATTRIBUTES) &&
1219
+ revision_marker != other.revision_marker
1184
1220
  end
1185
1221
 
1186
1222
  # The document a wrapper stands in front of: a supplement's base, or
@@ -1289,7 +1325,8 @@ module Pubid
1289
1325
 
1290
1326
  root == other.root &&
1291
1327
  self != other &&
1292
- matches?(other, ignore: [:date, :year, :stage, :typed_stage])
1328
+ exclude(:date, :year, :stage, :typed_stage) ==
1329
+ other.exclude(:date, :year, :stage, :typed_stage)
1293
1330
  end
1294
1331
 
1295
1332
  # Self and +other+ are editions of the same document: the edition-year
@@ -1315,7 +1352,7 @@ module Pubid
1315
1352
  # Any relational predicate holds.
1316
1353
  def related_to?(other)
1317
1354
  %i[supplement_of? has_supplement? dated_version_of? edition_of?
1318
- sibling_of? includes? draft_of?].any? do |m|
1355
+ revision_of? sibling_of? includes? draft_of?].any? do |m|
1319
1356
  public_send(m, other)
1320
1357
  end
1321
1358
  end
@@ -54,3 +54,21 @@ Read this before you change `lib/pubid/nist/` or `spec/pubid/nist/`. The root
54
54
  `update`/`update_component` and cleared to `nil` before the object is
55
55
  returned. Neither carries live information `edition`/`update` doesn't
56
56
  already cover. Locked by `spec/pubid/all_parts_edition_keys_audit_spec.rb`.
57
+
58
+ - **The URN parser must rebuild the human face the generator started from,
59
+ field for field.** `UrnGenerator` dot-joins identity fields after the
60
+ number (`urn:nist:sp:304.e2017.A.supp`), and `UrnParser#parse_payload`
61
+ splits them back: the edition/volume/revision glue onto the number, a
62
+ single capital-letter token is a **part** glued to the code
63
+ (`304A`), an edition's additional text keeps its dot (`13e2.June1908` —
64
+ the token after `eN`), `insert`/`errata`/`add` markers glue verbatim,
65
+ the bare `.supp` is supplementability (non-identity) and a valued
66
+ `.supp-<v>` is `sup<v>`. The rebuild is only correct if the flavor's own
67
+ grammar accepts it, so every branch is measured against the corpus —
68
+ before this, the five rows with a part letter, an edition additional
69
+ text, or an addendum marker failed to re-parse and the reference's own
70
+ `parse_urn` raised on its own output. The ts mirror must reproduce
71
+ Ruby's `String#split` behaviour and drop **trailing** empty fields
72
+ (`"800-38.A.supp.add."` splits with a trailing `""` in JS but not in
73
+ Ruby; keeping it appends a stray `.`). Locked by
74
+ `spec/pubid/nist/urn_parser_spec.rb`.
@@ -40,10 +40,10 @@ module Pubid
40
40
  parts = split_parts(body)
41
41
 
42
42
  type_token, payload = parts
43
- code, revision = parse_payload(payload)
43
+ code, suffix = parse_payload(payload)
44
44
 
45
45
  text = "#{publisher_for(type_token)} #{type_label(type_token)} #{code}"
46
- text += revision if revision
46
+ text += suffix
47
47
  flavor_parse(text)
48
48
  end
49
49
 
@@ -58,14 +58,42 @@ module Pubid
58
58
  NBS_SERIES.include?(type_token.downcase) ? "NBS" : "NIST"
59
59
  end
60
60
 
61
+ # The generator dot-joins the identity's fields after the number
62
+ # ("urn:nist:circ:25.e1924.supp", "urn:nist:circ:539.v10.supp",
63
+ # "urn:nist:circ:24.supp-1"). Reassemble the human spelling the
64
+ # flavor parser reads: the edition and volume glue onto the number,
65
+ # a revision glues after them, the bare ".supp" marks
66
+ # supplementability (non-identity) and a valued ".supp-<v>" is the
67
+ # supplement's "sup<v>" face.
61
68
  def parse_payload(payload)
62
- # Strip the trailing ".supp" supplement marker.
63
- stripped = payload.sub(/\.supp\z/, "")
64
- # Split revision suffix from code (e.g., "800-53.r5" → ["800-53", "r5"]).
65
- match = stripped.match(/^(.*)\.(r\d+)$/i)
66
- return [stripped, nil] unless match
69
+ tokens = payload.split(".")
70
+ code = tokens.shift.to_s
71
+ suffix = +""
72
+ part = nil
73
+ edition = nil
74
+ volume = nil
75
+ revision = nil
76
+ supplement = nil
67
77
 
68
- [match[1], match[2]]
78
+ tokens.each do |token|
79
+ case token
80
+ when /\Ar\d+\z/i then revision = token
81
+ when /\Ae[\w]+\z/i then edition = token
82
+ when /\Av\d+\z/i then volume = token
83
+ when /\Asupp-(.+)\z/im then supplement = Regexp.last_match(1)
84
+ when "supp" then next # supplementability marker, not identity
85
+ when "insert", "errata" then suffix << token # glued in the human
86
+ when /\A[A-Z]\z/ then part = token # part letter, glued to the code
87
+ when /\A[A-Z][a-z]+\d{4}\z/ # edition's additional text ("13e2.June1908")
88
+ edition = "#{edition}.#{token}" if edition
89
+ else suffix << ".#{token}" # unknown marker: keep verbatim
90
+ end
91
+ end
92
+
93
+ code << part.to_s
94
+ suffix << edition.to_s << volume.to_s << revision.to_s
95
+ suffix << "sup#{supplement}" if supplement
96
+ [code, suffix]
69
97
  end
70
98
 
71
99
  def type_label(token)
data/lib/pubid/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pubid
4
- VERSION = "2.0.0.pre.alpha.40"
4
+ VERSION = "2.0.0.pre.alpha.42"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pubid
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre.alpha.40
4
+ version: 2.0.0.pre.alpha.42
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.