pubid 2.0.0.pre.alpha.4 → 2.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.
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iala
5
+ module Identifiers
6
+ # IALA Resolutions and other Council publications (P prefix,
7
+ # planned). Today resolutions carry no code; P is reserved for
8
+ # future numbered resolutions.
9
+ class Resolution < Base
10
+ def self.type
11
+ { key: :resolution, title: "Resolution", short: "P" }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iala
5
+ module Identifiers
6
+ # IALA Standards (S series).
7
+ # Examples: S1010, S1020 Ed 2.0, S1070 Ed 2.0.
8
+ class Standard < Base
9
+ def self.type
10
+ { key: :standard, title: "Standard", short: "S" }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iala
5
+ module Identifiers
6
+ autoload :Base, "#{__dir__}/identifiers/base"
7
+ autoload :Standard, "#{__dir__}/identifiers/standard"
8
+ autoload :Recommendation, "#{__dir__}/identifiers/recommendation"
9
+ autoload :Guideline, "#{__dir__}/identifiers/guideline"
10
+ autoload :Manual, "#{__dir__}/identifiers/manual"
11
+ autoload :ModelCourse, "#{__dir__}/identifiers/model_course"
12
+ autoload :Report, "#{__dir__}/identifiers/report"
13
+ autoload :Resolution, "#{__dir__}/identifiers/resolution"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parslet"
4
+
5
+ module Pubid
6
+ module Iala
7
+ # Parslet grammar for IALA identifiers.
8
+ #
9
+ # Accepts every shape that appears on the IALA website or cover page:
10
+ #
11
+ # S1070
12
+ # S1070 Ed 2.0
13
+ # IALA S1070 Ed 2.0
14
+ # S1070 Ed 2.0 (F)
15
+ # R1016:ed2.0(F)
16
+ # C0103-1
17
+ # C0103-1 Ed 3.0
18
+ class Parser < Parslet::Parser
19
+ include ::Pubid::Parser::CommonParseRules
20
+
21
+ root :identifier
22
+
23
+ rule(:space) { str(" ") }
24
+ rule(:space?) { space.maybe }
25
+ rule(:dash) { str("-") }
26
+ rule(:colon) { str(":") }
27
+ rule(:dot) { str(".") }
28
+ rule(:lparen) { str("(") }
29
+ rule(:rparen) { str(")") }
30
+
31
+ # Type letter — one of IALA's seven document classes. Captured to
32
+ # route to the right Identifiers::* subclass.
33
+ rule(:type_letter) do
34
+ (str("S") | str("R") | str("G") | str("M") |
35
+ str("C") | str("X") | str("P")).as(:type_letter)
36
+ end
37
+
38
+ # 4-digit document number, e.g. "1070", "0126".
39
+ rule(:doc_number) do
40
+ match("[0-9]").repeat(4, 4).as(:doc_number)
41
+ end
42
+
43
+ # Numeric sub-part suffix(es): "-1", "-9-10", "-11". The catalogue
44
+ # uses ranges like R0124-9-10 & 11 — that compound is captured
45
+ # verbatim and resolved by the caller, not here.
46
+ rule(:subpart) do
47
+ (dash >> digits.as(:subpart_number)).repeat(1).as(:subpart)
48
+ end
49
+
50
+ rule(:code) do
51
+ type_letter >> doc_number >> subpart.maybe
52
+ end
53
+
54
+ # Edition forms observed on covers and listing pages:
55
+ # " Ed 2.0" — cover-page human form
56
+ # ":ed2.0" — listing-page compact form
57
+ rule(:edition_human) do
58
+ (space >> str("Ed") >> space >> edition_value).as(:edition)
59
+ end
60
+
61
+ rule(:edition_compact) do
62
+ (colon >> str("ed") >> edition_value).as(:edition)
63
+ end
64
+
65
+ rule(:edition_value) do
66
+ (digits >> (dot >> digits).repeat).as(:edition_value)
67
+ end
68
+
69
+ rule(:edition) do
70
+ edition_human | edition_compact
71
+ end
72
+
73
+ # Language is a single uppercase letter inside parens: (E), (F), …
74
+ rule(:language) do
75
+ (space? >> lparen >>
76
+ (str("E") | str("F") | str("S") | str("C") | str("A") | str("R")).as(:language) >>
77
+ rparen).as(:language_group)
78
+ end
79
+
80
+ # Optional "IALA " prefix.
81
+ rule(:publisher) do
82
+ (str("IALA") >> space).maybe
83
+ end
84
+
85
+ rule(:identifier) do
86
+ publisher >> code >> edition.maybe >> language.maybe
87
+ end
88
+
89
+ # Parse a string and return the raw parslet tree.
90
+ # @param string [String]
91
+ # @return [Hash]
92
+ def self.parse(string)
93
+ raise ArgumentError, "IALA identifier string exceeds maximum length" if string.length > ::Pubid::MAX_INPUT_LENGTH
94
+
95
+ new.parse(string.strip)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iala
5
+ # Human-readable renderer for IALA identifiers.
6
+ #
7
+ # Produces strings like:
8
+ # "IALA S1070"
9
+ # "IALA S1070 Ed 2.0"
10
+ # "IALA R1016 Ed 2.0 (F)"
11
+ class Renderer < ::Pubid::Renderers::Base
12
+ def render(context: nil, **_opts)
13
+ id = @id
14
+
15
+ rendered = "#{id.publisher} #{id.type_letter}#{id.number}"
16
+ rendered << " Ed #{id.edition}" if id.edition
17
+ rendered << " (#{id.language})" if id.language
18
+ rendered
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iala
5
+ # Generates URN strings for IALA identifiers using the Maritime Resource
6
+ # Name (MRN) namespace that IALA prints on its cover pages.
7
+ #
8
+ # Format: urn:mrn:iala:pub:<type-lower><number>[:ed<edition>][:<lang-lower>]
9
+ # Example: urn:mrn:iala:pub:s1070:ed2.0
10
+ # urn:mrn:iala:pub:r1016:ed2.0:f
11
+ class UrnGenerator
12
+ attr_reader :identifier
13
+
14
+ def initialize(identifier)
15
+ @identifier = identifier
16
+ end
17
+
18
+ def generate
19
+ parts = ["urn:mrn:iala:pub", code_segment]
20
+ parts << "ed#{identifier.edition}" if identifier.edition
21
+ parts << identifier.language.downcase if identifier.language
22
+ parts.join(":")
23
+ end
24
+
25
+ private
26
+
27
+ def code_segment
28
+ "#{identifier.type_letter.downcase}#{identifier.number}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iala
5
+ # Parses IALA MRN URNs back into identifiers.
6
+ #
7
+ # UrnGenerator emits:
8
+ # urn:mrn:iala:pub:<type-lower><number>[:ed<edition>][:<lang-lower>]
9
+ #
10
+ # Examples:
11
+ # urn:mrn:iala:pub:s1070:ed2.0 → IALA S1070 Ed 2.0
12
+ # urn:mrn:iala:pub:r1016:ed2.0:f → IALA R1016 Ed 2.0 (F)
13
+ # urn:mrn:iala:pub:c0103-1 → IALA C0103-1
14
+ class UrnParser < Pubid::UrnParser::Base
15
+ PREFIX = "urn:mrn:iala:pub:".freeze
16
+
17
+ def parse_urn(urn)
18
+ body = strip_namespace(urn)
19
+ parts = split_parts(body)
20
+ code = parts.fetch(0)
21
+
22
+ edition = nil
23
+ language = nil
24
+ parts.drop(1).each do |seg|
25
+ if seg.start_with?("ed")
26
+ edition = seg.sub(/\Aed/, "")
27
+ elsif seg.length == 1 && seg.match?(/\A[a-z]\z/i)
28
+ language = seg.upcase
29
+ end
30
+ end
31
+
32
+ text = "IALA #{code.upcase}"
33
+ text += " Ed #{edition}" if edition
34
+ text += " (#{language})" if language
35
+ flavor_parse(text)
36
+ end
37
+
38
+ private
39
+
40
+ def strip_namespace(urn)
41
+ unless urn.downcase.start_with?(PREFIX)
42
+ raise Pubid::UrnParser::Errors::ParseError,
43
+ "Invalid IALA URN: #{urn.inspect}"
44
+ end
45
+
46
+ urn[PREFIX.length..]
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/pubid/iala.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Iala
5
+ autoload :Builder, "#{__dir__}/iala/builder"
6
+ autoload :Identifier, "#{__dir__}/iala/identifier"
7
+ autoload :Identifiers, "#{__dir__}/iala/identifiers"
8
+ autoload :Parser, "#{__dir__}/iala/parser"
9
+ autoload :Renderer, "#{__dir__}/iala/renderer"
10
+ autoload :UrnGenerator, "#{__dir__}/iala/urn_generator"
11
+ autoload :UrnParser, "#{__dir__}/iala/urn_parser"
12
+
13
+ # Parse an IALA identifier string into an identifier object.
14
+ # @param identifier [String] The IALA identifier string to parse
15
+ # @return [Pubid::Iala::Identifier]
16
+ def self.parse(identifier)
17
+ Identifier.parse(identifier)
18
+ end
19
+
20
+ # Per-flavor format registry: inherits global formats, overrides :human
21
+ Identifiers::Base.format_registry = FormatRegistry.new(parent: ::Pubid::Identifier.format_registry)
22
+ Identifiers::Base.format_registry.register(:human, renderer: Iala::Renderer)
23
+
24
+ # Auto-discover all identifier types from the Identifiers namespace.
25
+ # @return [Array<Class>] identifier classes (Pubid::Identifier subclasses)
26
+ def self.identifier_types
27
+ @identifier_types ||= Identifiers.constants
28
+ .filter_map { |c| begin; Identifiers.const_get(c); rescue NameError; nil; end }
29
+ .select { |c| c.is_a?(Class) && c < Pubid::Identifier }
30
+ .reject { |c| c == Identifiers::Base }
31
+ end
32
+
33
+ # Build typed stage index from identifier types
34
+ # @return [Array<Pubid::Components::TypedStage>]
35
+ def self.all_typed_stages
36
+ @all_typed_stages ||= identifier_types.flat_map do |klass|
37
+ klass.const_defined?(:TYPED_STAGES) ? klass.const_get(:TYPED_STAGES) : []
38
+ end
39
+ end
40
+
41
+ # Lookup: type code -> identifier class
42
+ # @param code [String, Symbol]
43
+ # @return [Class, nil]
44
+ def self.locate_type(code)
45
+ identifier_types.find { |t| t.type[:key].to_s == code.to_s }
46
+ end
47
+
48
+ # Lookup: abbreviation -> typed stage
49
+ # @param abbr [String, Symbol]
50
+ # @return [Pubid::Components::TypedStage, nil]
51
+ def self.locate_stage(abbr)
52
+ abbr_str = abbr.to_s.upcase
53
+ all_typed_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } }
54
+ end
55
+
56
+ # Look up an identifier class by its IALA type letter (S, R, G, M, C, X, P).
57
+ # @param letter [String]
58
+ # @return [Class<Identifiers::Base>]
59
+ def self.identifier_klass_for_type_letter(letter)
60
+ @by_letter ||= identifier_types.to_h { |klass| [klass.type[:short], klass] }
61
+ @by_letter.fetch(letter.to_s.upcase)
62
+ end
63
+ end
64
+ end
65
+
66
+ Pubid::Registry.register(:iala, Pubid::Iala)
@@ -120,11 +120,96 @@ module Pubid
120
120
  nil
121
121
  end
122
122
 
123
+ # The underlying standard, with amendment / corrigendum / Expert-commentary
124
+ # / Flex-version wrappers peeled recursively. Wrapper subclasses override
125
+ # this; a plain identifier is its own base document.
126
+ def base_document
127
+ self
128
+ end
129
+
123
130
  # @return [String, nil] publication year from the date component
124
131
  def year
125
132
  date&.year&.to_s
126
133
  end
127
134
 
135
+ # Canonicalize the serialized hash so it never carries a defaulted attribute
136
+ # still at its default (or empty) value. This makes to_hash a pure function
137
+ # of the identifier's values, independent of how the object was built.
138
+ #
139
+ # Motivation: lutaml materializes each attribute's default as an explicit
140
+ # assignment during deserialization (flipping using_default? to false), so a
141
+ # naive from_hash(x).to_hash re-emits defaults that parse(x).to_hash omits —
142
+ # breaking the exact-equality round-trip relaton-index relies on
143
+ # (from_hash(raw).to_hash == raw). pubid never consults lutaml's unset
144
+ # tracking and defines no render_default: true, so a defaulted attribute at
145
+ # its default/empty value carries no meaning and does not belong in the
146
+ # canonical hash. Dropping it here (rather than repairing from_hash) fixes
147
+ # the round-trip through every construction path — parse, from_hash, manual.
148
+ def to_hash(*args)
149
+ hash = super
150
+ canonicalize_hash(self, hash) if hash.is_a?(::Hash)
151
+ hash
152
+ end
153
+
154
+ # Recursively drop attributes holding only their default (or empty) value
155
+ # from +hash+, the serialization of +model+. Recurses into nested component
156
+ # / identifier values because lutaml serializes those via its own transform
157
+ # — bypassing their public to_hash — so a nested defaulted attribute (e.g.
158
+ # Components::Supplement#has_revision) would otherwise leak into the parent
159
+ # hash and break the idempotent round-trip.
160
+ def canonicalize_hash(model, hash)
161
+ register = model.lutaml_register
162
+ model.class.attributes(register).each do |name, attr|
163
+ canonicalize_attr(model, hash, name, attr) unless name == :_type
164
+ end
165
+ end
166
+
167
+ # Canonicalize a single attribute against its serialized value in +hash+:
168
+ # drop it when it holds only its default/empty value, else recurse into it.
169
+ def canonicalize_attr(model, hash, name, attr)
170
+ key = hash.key?(name.to_s) ? name.to_s : name
171
+ return unless hash.key?(key)
172
+
173
+ value = model.public_send(name)
174
+ if default_valued?(value, attr, model)
175
+ hash.delete(key)
176
+ else
177
+ canonicalize_nested(value, hash[key])
178
+ end
179
+ end
180
+
181
+ # True when +value+ is +attr+'s default (or empty), so it can be dropped.
182
+ def default_valued?(value, attr, model)
183
+ register = model.lutaml_register
184
+ attr.default_set?(register, model) &&
185
+ (Lutaml::Model::Utils.empty?(value) ||
186
+ value == attr.default(register, model))
187
+ end
188
+
189
+ # Recurse into a nested component / identifier (or a collection of them) so
190
+ # its own defaulted attributes are canonicalized against its sub-hash.
191
+ def canonicalize_nested(value, sub)
192
+ if value.is_a?(Lutaml::Model::Serialize)
193
+ canonicalize_hash(value, sub) if sub.is_a?(::Hash)
194
+ elsif value.is_a?(::Array)
195
+ canonicalize_collection(value, sub)
196
+ end
197
+ end
198
+
199
+ # Canonicalize each element of a collection against its serialized sub-hash.
200
+ # Only zip when object and serialized sizes match, so a (never-observed)
201
+ # length mismatch can't pair an element with the wrong sub-hash — worst case
202
+ # it leaves that collection untouched.
203
+ def canonicalize_collection(models, subs)
204
+ return unless subs.is_a?(::Array) && models.size == subs.size
205
+
206
+ models.each_with_index do |model, i|
207
+ canonicalize_nested(model, subs[i])
208
+ end
209
+ end
210
+ private :canonicalize_hash, :canonicalize_attr, :default_valued?,
211
+ :canonicalize_nested, :canonicalize_collection
212
+
128
213
  def initialize(attrs = {}, options = {})
129
214
  attrs = attrs.dup
130
215
  attrs[:_type] ||= self.class.polymorphic_name
@@ -233,17 +318,48 @@ module Pubid
233
318
  Pubid::UrnGenerator::Base
234
319
  end
235
320
 
321
+ # Excluded attributes are nilled; every other value is passed through
322
+ # #exclude_from_nested so the exclusion also propagates into nested
323
+ # identifiers — wrapper types (adopted standards, consolidated amendments,
324
+ # expert-commentary wrappers) delegate their date to an inner identifier
325
+ # rather than storing it in their own attribute.
236
326
  def exclude(*args)
327
+ # :amendment / :supplement are structural, not attributes — they reduce
328
+ # a supplemented identifier to the standard it wraps (#drop_supplements).
329
+ supplement_keys = args & %i[amendment supplement]
330
+ unless supplement_keys.empty?
331
+ return drop_supplements.exclude(*(args - supplement_keys))
332
+ end
333
+
237
334
  excluded_args = args.dup
238
335
  # Map :year to :date since identifiers store years inside date
239
336
  excluded_args << :date if excluded_args.delete(:year)
240
337
 
241
338
  attrs = self.class.attributes.each_with_object({}) do |(name, _), h|
242
- h[name] = excluded_args.include?(name) ? nil : public_send(name)
339
+ value = excluded_args.include?(name) ? nil : public_send(name)
340
+ h[name] = exclude_from_nested(value, args)
243
341
  end
244
342
  self.class.new(attrs)
245
343
  end
246
344
 
345
+ # The standard this identifier supplements, dropping its own supplement
346
+ # layer (one level). Overridden by ConsolidatedIdentifier / Amendment /
347
+ # Corrigendum; a non-supplement identifier supplements nothing, so it is
348
+ # returned unchanged.
349
+ def drop_supplements
350
+ self
351
+ end
352
+
353
+ # Fuzz-level equality: two identifiers match when they are equal after
354
+ # excluding the given aspects. `ignore` accepts the same symbols as #exclude
355
+ # (e.g. :date, :edition, :amendment). This is the primitive relaton uses to
356
+ # match a reference against catalogue hits at varying strictness.
357
+ def matches?(other, ignore: [])
358
+ return false unless other.is_a?(::Pubid::Identifier)
359
+
360
+ exclude(*ignore) == other.exclude(*ignore)
361
+ end
362
+
247
363
  def new_edition_of?(other)
248
364
  unless publisher == other.publisher
249
365
  raise ArgumentError,
@@ -284,6 +400,21 @@ module Pubid
284
400
 
285
401
  private
286
402
 
403
+ # Propagate an #exclude into a nested attribute value: recurse when it is
404
+ # (or contains) another identifier, otherwise return it unchanged.
405
+ # Components and scalars are copied as-is. Passes the original args so
406
+ # nested identifiers re-apply the same :year->:date mapping themselves.
407
+ def exclude_from_nested(value, args)
408
+ case value
409
+ when ::Pubid::Identifier
410
+ value.exclude(*args)
411
+ when Array
412
+ value.map { |item| exclude_from_nested(item, args) }
413
+ else
414
+ value
415
+ end
416
+ end
417
+
287
418
  def build_rendering_context(_renderer, format:, with_edition: false,
288
419
  lang: :en, lang_single: false,
289
420
  stage_format_long: nil, with_date: nil,
@@ -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]
@@ -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 | trailing_supplement_identifier | base_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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pubid
4
- VERSION = "2.0.0.pre.alpha.4"
4
+ VERSION = "2.0.0.pre.alpha.6"
5
5
  end
data/lib/pubid.rb CHANGED
@@ -95,6 +95,7 @@ module Pubid
95
95
  autoload :Idf, "pubid/idf"
96
96
  autoload :Iec, "pubid/iec"
97
97
  autoload :Ieee, "pubid/ieee"
98
+ autoload :Iala, "pubid/iala"
98
99
  autoload :Iho, "pubid/iho"
99
100
  autoload :Iso, "pubid/iso"
100
101
  autoload :Itu, "pubid/itu"
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
4
+ version: 2.0.0.pre.alpha.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: 2026-07-03 00:00:00.000000000 Z
11
+ date: 2026-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -335,6 +335,22 @@ files:
335
335
  - lib/pubid/export/result.rb
336
336
  - lib/pubid/format_detector.rb
337
337
  - lib/pubid/format_registry.rb
338
+ - lib/pubid/iala.rb
339
+ - lib/pubid/iala/builder.rb
340
+ - lib/pubid/iala/identifier.rb
341
+ - lib/pubid/iala/identifiers.rb
342
+ - lib/pubid/iala/identifiers/base.rb
343
+ - lib/pubid/iala/identifiers/guideline.rb
344
+ - lib/pubid/iala/identifiers/manual.rb
345
+ - lib/pubid/iala/identifiers/model_course.rb
346
+ - lib/pubid/iala/identifiers/recommendation.rb
347
+ - lib/pubid/iala/identifiers/report.rb
348
+ - lib/pubid/iala/identifiers/resolution.rb
349
+ - lib/pubid/iala/identifiers/standard.rb
350
+ - lib/pubid/iala/parser.rb
351
+ - lib/pubid/iala/renderer.rb
352
+ - lib/pubid/iala/urn_generator.rb
353
+ - lib/pubid/iala/urn_parser.rb
338
354
  - lib/pubid/identifier.rb
339
355
  - lib/pubid/identifier_metadata.rb
340
356
  - lib/pubid/idf.rb