pubid 2.0.0.pre.alpha.4 → 2.0.0.pre.alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/pubid/identifier.rb +78 -0
- data/lib/pubid/oiml/builder.rb +3 -1
- data/lib/pubid/oiml/parser.rb +14 -1
- data/lib/pubid/oiml/renderer.rb +10 -0
- data/lib/pubid/oiml/supplement_identifier.rb +4 -0
- data/lib/pubid/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70b24e82967e02c1a0a641c5adc3ad9a77e563f0b7e6f69a59aa4f02971db3d2
|
|
4
|
+
data.tar.gz: c206002602026012406fa8ebe4339ff6ca0f02db7aa0b00ba49796a034cb697c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f74a26e175164470260014a2d492eadfc2f396b0b56bc9f5848fc79168c1ec1d627380bb64c83126bd0eeb2841da40828e336cf263b785b4c2c1959a712b95c5
|
|
7
|
+
data.tar.gz: b450a0f2343231dff1174c01cec84fdacda74279134fed4c913a552fcd3c7a82e662e1fba5e52018462c295de1df9ba5c78af5a62a1ebde09974fbcd8270f7d6
|
data/lib/pubid/identifier.rb
CHANGED
|
@@ -125,6 +125,84 @@ module Pubid
|
|
|
125
125
|
date&.year&.to_s
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
+
# Canonicalize the serialized hash so it never carries a defaulted attribute
|
|
129
|
+
# still at its default (or empty) value. This makes to_hash a pure function
|
|
130
|
+
# of the identifier's values, independent of how the object was built.
|
|
131
|
+
#
|
|
132
|
+
# Motivation: lutaml materializes each attribute's default as an explicit
|
|
133
|
+
# assignment during deserialization (flipping using_default? to false), so a
|
|
134
|
+
# naive from_hash(x).to_hash re-emits defaults that parse(x).to_hash omits —
|
|
135
|
+
# breaking the exact-equality round-trip relaton-index relies on
|
|
136
|
+
# (from_hash(raw).to_hash == raw). pubid never consults lutaml's unset
|
|
137
|
+
# tracking and defines no render_default: true, so a defaulted attribute at
|
|
138
|
+
# its default/empty value carries no meaning and does not belong in the
|
|
139
|
+
# canonical hash. Dropping it here (rather than repairing from_hash) fixes
|
|
140
|
+
# the round-trip through every construction path — parse, from_hash, manual.
|
|
141
|
+
def to_hash(*args)
|
|
142
|
+
hash = super
|
|
143
|
+
canonicalize_hash(self, hash) if hash.is_a?(::Hash)
|
|
144
|
+
hash
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Recursively drop attributes holding only their default (or empty) value
|
|
148
|
+
# from +hash+, the serialization of +model+. Recurses into nested component
|
|
149
|
+
# / identifier values because lutaml serializes those via its own transform
|
|
150
|
+
# — bypassing their public to_hash — so a nested defaulted attribute (e.g.
|
|
151
|
+
# Components::Supplement#has_revision) would otherwise leak into the parent
|
|
152
|
+
# hash and break the idempotent round-trip.
|
|
153
|
+
def canonicalize_hash(model, hash)
|
|
154
|
+
register = model.lutaml_register
|
|
155
|
+
model.class.attributes(register).each do |name, attr|
|
|
156
|
+
canonicalize_attr(model, hash, name, attr) unless name == :_type
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Canonicalize a single attribute against its serialized value in +hash+:
|
|
161
|
+
# drop it when it holds only its default/empty value, else recurse into it.
|
|
162
|
+
def canonicalize_attr(model, hash, name, attr)
|
|
163
|
+
key = hash.key?(name.to_s) ? name.to_s : name
|
|
164
|
+
return unless hash.key?(key)
|
|
165
|
+
|
|
166
|
+
value = model.public_send(name)
|
|
167
|
+
if default_valued?(value, attr, model)
|
|
168
|
+
hash.delete(key)
|
|
169
|
+
else
|
|
170
|
+
canonicalize_nested(value, hash[key])
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# True when +value+ is +attr+'s default (or empty), so it can be dropped.
|
|
175
|
+
def default_valued?(value, attr, model)
|
|
176
|
+
register = model.lutaml_register
|
|
177
|
+
attr.default_set?(register, model) &&
|
|
178
|
+
(Lutaml::Model::Utils.empty?(value) ||
|
|
179
|
+
value == attr.default(register, model))
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Recurse into a nested component / identifier (or a collection of them) so
|
|
183
|
+
# its own defaulted attributes are canonicalized against its sub-hash.
|
|
184
|
+
def canonicalize_nested(value, sub)
|
|
185
|
+
if value.is_a?(Lutaml::Model::Serialize)
|
|
186
|
+
canonicalize_hash(value, sub) if sub.is_a?(::Hash)
|
|
187
|
+
elsif value.is_a?(::Array)
|
|
188
|
+
canonicalize_collection(value, sub)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Canonicalize each element of a collection against its serialized sub-hash.
|
|
193
|
+
# Only zip when object and serialized sizes match, so a (never-observed)
|
|
194
|
+
# length mismatch can't pair an element with the wrong sub-hash — worst case
|
|
195
|
+
# it leaves that collection untouched.
|
|
196
|
+
def canonicalize_collection(models, subs)
|
|
197
|
+
return unless subs.is_a?(::Array) && models.size == subs.size
|
|
198
|
+
|
|
199
|
+
models.each_with_index do |model, i|
|
|
200
|
+
canonicalize_nested(model, subs[i])
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
private :canonicalize_hash, :canonicalize_attr, :default_valued?,
|
|
204
|
+
:canonicalize_nested, :canonicalize_collection
|
|
205
|
+
|
|
128
206
|
def initialize(attrs = {}, options = {})
|
|
129
207
|
attrs = attrs.dup
|
|
130
208
|
attrs[:_type] ||= self.class.polymorphic_name
|
data/lib/pubid/oiml/builder.rb
CHANGED
|
@@ -78,13 +78,14 @@ module Pubid
|
|
|
78
78
|
|
|
79
79
|
def build_supplement(parsed_hash)
|
|
80
80
|
marker = parsed_hash[:trailing_marker].to_s if parsed_hash[:trailing_marker]
|
|
81
|
+
plus_marker = parsed_hash[:plus_marker].to_s if parsed_hash[:plus_marker]
|
|
81
82
|
|
|
82
83
|
# Determine supplement type. The trailing word ("Amendment"/"Errata")
|
|
83
84
|
# selects the class; the concrete class then carries the word via
|
|
84
85
|
# #supplement_type, so only the `trailing` flag needs storing.
|
|
85
86
|
supplement_class = if parsed_hash[:annex_letter] || parsed_hash[:annex_marker]
|
|
86
87
|
Identifiers::Annex
|
|
87
|
-
elsif marker == "Errata"
|
|
88
|
+
elsif marker == "Errata" || plus_marker == "Errata"
|
|
88
89
|
Identifiers::Errata
|
|
89
90
|
else
|
|
90
91
|
Identifiers::Amendment
|
|
@@ -92,6 +93,7 @@ module Pubid
|
|
|
92
93
|
|
|
93
94
|
supplement = supplement_class.new
|
|
94
95
|
supplement.trailing = true if marker
|
|
96
|
+
supplement.joined = true if plus_marker
|
|
95
97
|
|
|
96
98
|
# Recursively parse base identifier
|
|
97
99
|
if parsed_hash[:base_identifier]
|
data/lib/pubid/oiml/parser.rb
CHANGED
|
@@ -19,7 +19,8 @@ module Pubid
|
|
|
19
19
|
# Main identifier pattern - check supplements first
|
|
20
20
|
rule(:identifier) do
|
|
21
21
|
amendment_identifier | amendment_short | annex_letter_identifier |
|
|
22
|
-
annex_identifier |
|
|
22
|
+
annex_identifier | plus_supplement_identifier |
|
|
23
|
+
trailing_supplement_identifier | base_identifier
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
# Publisher - always "OIML"
|
|
@@ -172,6 +173,18 @@ module Pubid
|
|
|
172
173
|
language_portion.maybe.as(:language)
|
|
173
174
|
end
|
|
174
175
|
|
|
176
|
+
# Plus-joined supplement - "BASE:YEAR+Supplement:YEAR" form where both
|
|
177
|
+
# the base and the supplement carry their own year. Used for amendments
|
|
178
|
+
# and errata to dated bases (e.g. "OIML B 10:2011+Amendment:2012").
|
|
179
|
+
# Annexes already encode the year-on-base intent via their own model.
|
|
180
|
+
rule(:plus_supplement_identifier) do
|
|
181
|
+
base_without_language.as(:base_identifier) >>
|
|
182
|
+
str("+") >>
|
|
183
|
+
(str("Amendment") | str("Errata")).as(:plus_marker) >>
|
|
184
|
+
(colon >> year_digits.as(:year)).maybe >>
|
|
185
|
+
language_portion.maybe.as(:language)
|
|
186
|
+
end
|
|
187
|
+
|
|
175
188
|
# Annex identifier - "BASE Annexes Edition YYYY" / "BASE Annexes:YYYY" /
|
|
176
189
|
# "BASE:YYYY Annexes" (year on the base, no annex year).
|
|
177
190
|
rule(:annex_identifier) do
|
data/lib/pubid/oiml/renderer.rb
CHANGED
|
@@ -84,6 +84,16 @@ module Pubid
|
|
|
84
84
|
def render_supplement(id)
|
|
85
85
|
format = effective_format(id)
|
|
86
86
|
|
|
87
|
+
# Plus-joined: "BASE+Amendment:YEAR" / "BASE+Errata:YEAR" with both
|
|
88
|
+
# the base and the supplement carrying their own year.
|
|
89
|
+
if id.joined
|
|
90
|
+
base_str = strip_language(id.base_identifier.to_s)
|
|
91
|
+
result = "#{base_str}+#{id.supplement_type}"
|
|
92
|
+
result += ":#{id.year}" if id.year
|
|
93
|
+
result += " (#{id.language})" if id.language
|
|
94
|
+
return result
|
|
95
|
+
end
|
|
96
|
+
|
|
87
97
|
# Trailing-word shorthand: "BASE Amendment" / "BASE Errata" with the
|
|
88
98
|
# publication year kept on the base identifier. The word comes from the
|
|
89
99
|
# concrete supplement class.
|
|
@@ -13,6 +13,9 @@ module Pubid
|
|
|
13
13
|
# "Amendment (YYYY) to BASE" prose form. The word itself comes from the
|
|
14
14
|
# concrete class (#supplement_type), so only this flag is stored.
|
|
15
15
|
attribute :trailing, :boolean, default: false
|
|
16
|
+
# True for the plus-joined form ("OIML B 10:2011+Amendment:2012") where
|
|
17
|
+
# both the base and the supplement carry their own year, joined by "+".
|
|
18
|
+
attribute :joined, :boolean, default: false
|
|
16
19
|
attribute :parsed_format, :string, default: -> {
|
|
17
20
|
"short"
|
|
18
21
|
} # Track supplement's parsed format
|
|
@@ -25,6 +28,7 @@ module Pubid
|
|
|
25
28
|
with: { to: :base_identifier_to_kv, from: :base_identifier_from_kv }
|
|
26
29
|
map "year", to: :year
|
|
27
30
|
map "trailing", to: :trailing
|
|
31
|
+
map "joined", to: :joined
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def base_identifier_to_kv(model, doc)
|
data/lib/pubid/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pubid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.0.pre.alpha.
|
|
4
|
+
version: 2.0.0.pre.alpha.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|