Class: Pubid::Iso::Identifier

Inherits:
Pubid::Identifier show all
Defined in:
lib/pubid/iso/identifier.rb

Constant Summary collapse

ISO_TYPE_MAP =

Polymorphic type map for lutaml::Model key_value serialization Maps polymorphic_name → class name for deserialization Validated by spec to stay in sync with identifier_types

{
  "pubid:iso:international-standard" => "Pubid::Iso::Identifiers::InternationalStandard",
  "pubid:iso:international-standardized-profile" => "Pubid::Iso::Identifiers::InternationalStandardizedProfile",
  "pubid:iso:international-workshop-agreement" => "Pubid::Iso::Identifiers::InternationalWorkshopAgreement",
  "pubid:iso:technical-report" => "Pubid::Iso::Identifiers::TechnicalReport",
  "pubid:iso:technical-specification" => "Pubid::Iso::Identifiers::TechnicalSpecification",
  "pubid:iso:pas" => "Pubid::Iso::Identifiers::Pas",
  "pubid:iso:guide" => "Pubid::Iso::Identifiers::Guide",
  "pubid:iso:recommendation" => "Pubid::Iso::Identifiers::Recommendation",
  "pubid:iso:amendment" => "Pubid::Iso::Identifiers::Amendment",
  "pubid:iso:corrigendum" => "Pubid::Iso::Identifiers::Corrigendum",
  "pubid:iso:supplement" => "Pubid::Iso::Identifiers::Supplement",
  "pubid:iso:addendum" => "Pubid::Iso::Identifiers::Addendum",
  "pubid:iso:extract" => "Pubid::Iso::Identifiers::Extract",
  "pubid:iso:directives" => "Pubid::Iso::Identifiers::Directives",
  "pubid:iso:directives-supplement" => "Pubid::Iso::Identifiers::DirectivesSupplement",
  "pubid:iso:data" => "Pubid::Iso::Identifiers::Data",
  "pubid:iso:tc-document" => "Pubid::Iso::Identifiers::TcDocument",
  "pubid:iso:technology-trends-assessments" => "Pubid::Iso::Identifiers::TechnologyTrendsAssessments",
  "pubid:iso:bundled-identifier" => "Pubid::Iso::BundledIdentifier",
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Pubid::Identifier

#base_identifier, #eql?, #exclude, #hash, #initialize, #mr_number, #mr_number_with_part, #mr_part, #mr_publisher, #mr_type, #mr_year, #new_edition_of?, polymorphic_name, #render, #resolve_urn_generator, #root, #to_mr_string, #to_s, #to_supplement_s, #to_urn, #urn_supplement_type, #urn_type_code, #year

Constructor Details

This class inherits a constructor from Pubid::Identifier

Class Method Details

.build_type_mapObject

Build type map from Pubid::Iso.identifier_types for validation



89
90
91
92
93
# File 'lib/pubid/iso/identifier.rb', line 89

def self.build_type_map
  Pubid::Iso.identifier_types.to_h do |klass|
    [klass.polymorphic_name, klass.name]
  end
end

.default_publisherObject

The publisher implied when none is serialized. ISO for most types; publisher-less types (IWA) override this to nil.



16
17
18
# File 'lib/pubid/iso/identifier.rb', line 16

def self.default_publisher
  ::Pubid::Iso::Components::Publisher.new
end

.from_hash(data, options = {}) ⇒ Object

lutaml’s polymorphic key_value mapping reads ‘_type` only to validate; it does not re-instantiate the concrete subclass on deserialization. So `Identifier.from_hash(corrigendum_hash)` would return a bare Identifier and drop `base_identifier`. Route by `_type` to the right subclass and let its (inherited) from_hash do the real work, mirroring JIS.



285
286
287
288
289
290
291
292
293
# File 'lib/pubid/iso/identifier.rb', line 285

def self.from_hash(data, options = {})
  type = data["_type"] || data[:_type]
  klass_name = ISO_TYPE_MAP[type]
  if klass_name
    klass = Object.const_get(klass_name)
    return klass.from_hash(data, options) unless klass == self
  end
  super
end

.parse(string, format: :auto) ⇒ Object



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/pubid/iso/identifier.rb', line 261

def self.parse(string, format: :auto)
  format = Pubid::FormatDetector.detect(string) if format == :auto

  case format
  when :urn
    Pubid::Iso::UrnParser.parse(string)
  when :mr_string
    Pubid::Parsers::MrString.parse(string)
  else
    parsed = Pubid::Iso::Parser.new.parse(string)
    if parsed.nil? || parsed.empty?
      raise Pubid::Iso::Parser::ParseError,
            "Invalid identifier format"
    end

    Pubid::Iso::Builder.new.build(parsed)
  end
end

.published_typed_stageObject

The class’s published typed_stage (canonical surface form), or nil for types with no stages (e.g. TC documents).



28
29
30
31
32
33
34
35
36
37
# File 'lib/pubid/iso/identifier.rb', line 28

def self.published_typed_stage
  return nil unless const_defined?(:TYPED_STAGES)

  ts = self::TYPED_STAGES.find { |t| t.stage_code.to_s == "published" }
  return nil unless ts

  ts = ts.dup
  ts.original_abbr = ts.canonical_abbreviation
  ts
end

Instance Method Details

#all_parts_from_kv(model, value) ⇒ Object



136
137
138
# File 'lib/pubid/iso/identifier.rb', line 136

def all_parts_from_kv(model, value)
  model.all_parts = value
end

#all_parts_to_kv(model, doc) ⇒ Object



128
129
130
131
132
133
134
# File 'lib/pubid/iso/identifier.rb', line 128

def all_parts_to_kv(model, doc)
  return unless model.all_parts

  doc.add_child(
    Lutaml::KeyValue::DataModel::Element.new("all_parts", true),
  )
end

#build_code(value) ⇒ Object



200
201
202
# File 'lib/pubid/iso/identifier.rb', line 200

def build_code(value)
  ::Pubid::Iso::Components::Code.new(value: value.to_s)
end

#copublishers_from_kv(model, value) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/pubid/iso/identifier.rb', line 243

def copublishers_from_kv(model, value)
  list = Array(value).map(&:to_s)
  return unless list.any?

  # Mirror Builder#parse: copublishers live both on the primary
  # publisher (as strings) and as the top-level `copublishers`
  # collection of Publisher objects. `==` compares the latter, so
  # populate both or a deserialized id never equals a parsed one.
  publisher_for(model).copublisher = list
  model.copublishers = list.map do |cp|
    ::Pubid::Iso::Components::Publisher.new(publisher: cp)
  end
end

#copublishers_to_kv(model, doc) ⇒ Object



234
235
236
237
238
239
240
241
# File 'lib/pubid/iso/identifier.rb', line 234

def copublishers_to_kv(model, doc)
  cp = model.publisher&.copublisher
  return unless cp&.any?

  doc.add_child(
    Lutaml::KeyValue::DataModel::Element.new("copublishers", cp.map(&:to_s)),
  )
end

#date_for(model) ⇒ Object



218
219
220
# File 'lib/pubid/iso/identifier.rb', line 218

def date_for(model)
  model.date ||= ::Pubid::Components::Date.new
end

#day_from_kv(model, value) ⇒ Object



210
# File 'lib/pubid/iso/identifier.rb', line 210

def day_from_kv(model, value) = date_for(model).day = value.to_s

#day_to_kv(model, doc) ⇒ Object



209
# File 'lib/pubid/iso/identifier.rb', line 209

def day_to_kv(model, doc) = emit_date_part(doc, "day", model.date&.day)

#emit_code(doc, key, code) ⇒ Object



193
194
195
196
197
198
# File 'lib/pubid/iso/identifier.rb', line 193

def emit_code(doc, key, code)
  v = code.is_a?(::Pubid::Components::Code) ? code.value : code
  return if v.nil? || v.to_s.empty?

  doc.add_child(Lutaml::KeyValue::DataModel::Element.new(key, v.to_s))
end

#emit_date_part(doc, key, val) ⇒ Object



212
213
214
215
216
# File 'lib/pubid/iso/identifier.rb', line 212

def emit_date_part(doc, key, val)
  return if val.nil? || val.to_s.empty?

  doc.add_child(Lutaml::KeyValue::DataModel::Element.new(key, val.to_s))
end

#month_from_kv(model, value) ⇒ Object



208
# File 'lib/pubid/iso/identifier.rb', line 208

def month_from_kv(model, value) = date_for(model).month = value.to_s

#month_to_kv(model, doc) ⇒ Object



207
# File 'lib/pubid/iso/identifier.rb', line 207

def month_to_kv(model, doc) = emit_date_part(doc, "month", model.date&.month)

#number_from_kv(model, value) ⇒ Object



174
# File 'lib/pubid/iso/identifier.rb', line 174

def number_from_kv(model, value) = model.number = build_code(value)

#number_to_kv(model, doc) ⇒ Object

— Code components <-> plain string —



173
# File 'lib/pubid/iso/identifier.rb', line 173

def number_to_kv(model, doc) = emit_code(doc, "number", model.number)

#part_from_kv(model, value) ⇒ Object



176
# File 'lib/pubid/iso/identifier.rb', line 176

def part_from_kv(model, value) = model.part = build_code(value)

#part_to_kv(model, doc) ⇒ Object



175
# File 'lib/pubid/iso/identifier.rb', line 175

def part_to_kv(model, doc) = emit_code(doc, "part", model.part)

#publisher_for(model) ⇒ Object



257
258
259
# File 'lib/pubid/iso/identifier.rb', line 257

def publisher_for(model)
  model.publisher ||= ::Pubid::Iso::Components::Publisher.new
end

#publisher_from_kv(model, value) ⇒ Object



230
231
232
# File 'lib/pubid/iso/identifier.rb', line 230

def publisher_from_kv(model, value)
  publisher_for(model).publisher = value.to_s
end

#publisher_to_kv(model, doc) ⇒ Object

— publisher: primary only when non-default; copublishers as a list —



223
224
225
226
227
228
# File 'lib/pubid/iso/identifier.rb', line 223

def publisher_to_kv(model, doc)
  pub = model.publisher&.publisher
  return if pub.nil? || pub == model.class.default_publisher&.publisher

  doc.add_child(Lutaml::KeyValue::DataModel::Element.new("publisher", pub))
end

#stageObject



46
47
48
# File 'lib/pubid/iso/identifier.rb', line 46

def stage
  typed_stage&.to_stage
end

#stage_from_kv(model, value) ⇒ Object

Resolve the typed-stage code back to the full TypedStage within this identifier’s class, then derive type/stage from it.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/pubid/iso/identifier.rb', line 155

def stage_from_kv(model, value)
  return if value.nil? || value.to_s.empty?

  ts = (model.class.const_defined?(:TYPED_STAGES) &&
        model.class::TYPED_STAGES.find { |t| t.code.to_s == value.to_s }) ||
       Pubid::Iso::Scheme.locate_typed_stage_by_code(value)
  return unless ts

  # The renderer prefers `original_abbr` (the parsed surface form); without
  # it the supplement renderer falls back to `short_abbr` (e.g. "AMD").
  # Resolving from a code has no surface form, so render the canonical
  # abbreviation (`abbr.first`, e.g. "Amd").
  ts = ts.dup
  ts.original_abbr = ts.canonical_abbreviation
  model.typed_stage = ts
end

#stage_iteration_from_kv(model, value) ⇒ Object



189
190
191
# File 'lib/pubid/iso/identifier.rb', line 189

def stage_iteration_from_kv(model, value)
  model.stage_iteration = ::Pubid::Components::Iteration.new(number: value.to_s)
end

#stage_iteration_to_kv(model, doc) ⇒ Object



180
181
182
183
184
185
186
187
# File 'lib/pubid/iso/identifier.rb', line 180

def stage_iteration_to_kv(model, doc)
  iter = model.stage_iteration
  v = iter.is_a?(::Pubid::Components::Iteration) ? iter.number : iter
  return if v.nil? || v.to_s.empty?

  doc.add_child(Lutaml::KeyValue::DataModel::Element.new("stage_iteration",
                                                          v.to_s))
end

#stage_to_kv(model, doc) ⇒ Object

Serialize typed_stage as just its unique code (e.g. “is”, “dis”, “committee_draft_amd”). type/stage are recomputed from it on load.



142
143
144
145
146
147
148
149
150
151
# File 'lib/pubid/iso/identifier.rb', line 142

def stage_to_kv(model, doc)
  ts = model.typed_stage
  return unless ts&.code
  # Omit the published default (recomputed from the class on load).
  return if ts.stage_code.to_s == "published"

  doc.add_child(
    Lutaml::KeyValue::DataModel::Element.new("stage", ts.code.to_s),
  )
end

#subpart_from_kv(model, value) ⇒ Object



178
# File 'lib/pubid/iso/identifier.rb', line 178

def subpart_from_kv(model, value) = model.subpart = build_code(value)

#subpart_to_kv(model, doc) ⇒ Object



177
# File 'lib/pubid/iso/identifier.rb', line 177

def subpart_to_kv(model, doc) = emit_code(doc, "subpart", model.subpart)

#typeObject

type and stage are derived from typed_stage, never stored — so the doctype (fixed by the class / _type) can’t be lost when “stage” is omitted for the published default.



42
43
44
# File 'lib/pubid/iso/identifier.rb', line 42

def type
  typed_stage&.to_type
end

#with_harmonized_stage(harmonized_code) ⇒ Object

Return a copy with the lifecycle stage set from an ISO harmonized stage code (e.g. “90.92”). typed_stage is the single source of truth, so this is all that is needed to surface the stage in #to_s and #to_urn. Returns an unchanged copy if the code is not recognised.



54
55
56
57
# File 'lib/pubid/iso/identifier.rb', line 54

def with_harmonized_stage(harmonized_code)
  ts = Pubid::Iso.locate_stage_by_harmonized_code(harmonized_code)
  ts ? dup.tap { |id| id.typed_stage = ts } : dup
end

#year_from_kv(model, value) ⇒ Object



206
# File 'lib/pubid/iso/identifier.rb', line 206

def year_from_kv(model, value) = date_for(model).year = value.to_s

#year_to_kv(model, doc) ⇒ Object

— date serialized flat as year/month/day —



205
# File 'lib/pubid/iso/identifier.rb', line 205

def year_to_kv(model, doc) = emit_date_part(doc, "year", model.date&.year)