Module: Labimotion::TemplateDoi::ClassMethods

Defined in:
lib/labimotion/models/concerns/template_doi.rb

Overview

ActiveSupport::Concern extends this into the including class, so every method below becomes a class method on the host's Doi.

Instance Method Summary collapse

Instance Method Details

#build_labimotion_suffix(record, version = nil) ⇒ Object

Deterministic DOI suffix for a LabIMotion template klass. The same suffix is used at reserve and at release time, so both share one DOI:

element -> labimotion/element/<name>/<identifier>/<doi_version>
segment -> labimotion/segment/<element>/<identifier>/<doi_version>
dataset -> labimotion/dataset/<ols_term_id>/<identifier>/<doi_version>

<identifier> is the first UUID group (8 hex chars) of the template's identifier/uuid, falling back to the record id. <doi_version> is the DOI version (see .labimotion_version_segment). Callers may pass an explicit template version; otherwise the record's current version is used.

Raises:

  • (ArgumentError)


37
38
39
40
41
42
43
44
45
46
# File 'lib/labimotion/models/concerns/template_doi.rb', line 37

def build_labimotion_suffix(record, version = nil)
  parts = labimotion_suffix_parts(record)
  raise ArgumentError, "unsupported template: #{record.class}" if parts.nil?

  segment = labimotion_version_segment(record, version)
  return nil if segment.blank?

  segments = parts.map { |part| sanitize_doi_part(part) }.reject(&:empty?)
  "#{segments.join('/')}/#{segment}"
end

#labimotion_doi_metadata(record, version = nil) ⇒ Object

Metadata stamped on a reserved template DOI so the version it represents survives on the record (used to gate reserving the next version).



81
82
83
# File 'lib/labimotion/models/concerns/template_doi.rb', line 81

def (record, version = nil)
  { 'labimotion' => { 'doi_version' => labimotion_version_segment(record, version) } }
end

#labimotion_doi_publication(doi) ⇒ Object

The publication metadata a released template DOI was published with. Nil while the DOI is still reserved, and for DOIs released before snapshots were kept — callers fall back to the template's current publication in both cases.



89
90
91
# File 'lib/labimotion/models/concerns/template_doi.rb', line 89

def labimotion_doi_publication(doi)
  doi..to_h.dig('labimotion', 'publication')
end

#labimotion_doi_version(doi) ⇒ Object

The DOI version (next-major integer) recorded on a reserved template DOI.



75
76
77
# File 'lib/labimotion/models/concerns/template_doi.rb', line 75

def labimotion_doi_version(doi)
  doi..to_h.dig('labimotion', 'doi_version')
end

#labimotion_dois(record) ⇒ Object

The DOIs reserved for a LabIMotion template, in reservation order.



65
66
67
# File 'lib/labimotion/models/concerns/template_doi.rb', line 65

def labimotion_dois(record)
  where(doiable_id: record.id, doiable_type: record.class.name).order(:id).to_a
end

#labimotion_latest_doi(record) ⇒ Object

The current (most recently reserved) DOI for a template, or nil.



70
71
72
# File 'lib/labimotion/models/concerns/template_doi.rb', line 70

def labimotion_latest_doi(record)
  labimotion_dois(record).last
end

#labimotion_metadata_with_publication(doi, publication) ⇒ Object

The DOI's metadata with the publication snapshotted into it, keeping the DOI version already stamped there. Taken at release: the template's publication keeps being edited for the next version, and this version must stop tracking those edits.



97
98
99
100
101
102
103
# File 'lib/labimotion/models/concerns/template_doi.rb', line 97

def (doi, publication)
   = doi..to_h
  # Deep-duped: the snapshot must not alias the template's publication,
  # which goes on being edited for the next version.
  labimotion = (['labimotion'] || {}).merge('publication' => publication.deep_dup)
  .merge('labimotion' => labimotion)
end

#labimotion_suffix_parts(record) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/labimotion/models/concerns/template_doi.rb', line 105

def labimotion_suffix_parts(record)
  # identifier/uuid may both be blank for a draft, which would collapse the
  # scope (colliding across templates). Fall back to the stable record id so
  # every template gets a unique, deterministic suffix. Resolved lazily so an
  # unsupported record (no #id) still falls through to the ArgumentError in the
  # caller. The identifier is shortened to its first UUID group (8 hex chars).
  # A segment's <element> is the name of the ElementKlass it belongs to.
  identifier = lambda do
    raw = record.try(:identifier).presence || record.try(:uuid).presence || record.id
    raw.to_s.split('-').first
  end
  case record
  when ::Labimotion::ElementKlass
    [LABIMOTION_DOI_NAMESPACE, 'element', record.name, identifier.call]
  when ::Labimotion::SegmentKlass
    [LABIMOTION_DOI_NAMESPACE, 'segment', record.element_klass&.name, identifier.call]
  when ::Labimotion::DatasetKlass
    [LABIMOTION_DOI_NAMESPACE, 'dataset', record.ols_term_id, identifier.call]
  end
end

#labimotion_version_segment(record, version = nil) ⇒ Object

The DOI version segment for a template version. A DOI is published per major release: the .0 release opens that version (X.0 -> "X") and later minor revisions roll into the NEXT DOI (X.y, y>=1 -> "X+1"). Sub-1.0 and unversioned/blank templates map to the first DOI (v1). 0.x -> "1" 1.0 -> "1" 1.1..2.0 -> "2" 2.1..3.0 -> "3"



53
54
55
56
57
58
59
60
61
62
# File 'lib/labimotion/models/concerns/template_doi.rb', line 53

def labimotion_version_segment(record, version = nil)
  raw = (version.presence || record.try(:version)).to_s.strip
  parts = raw.split('.')
  major = parts[0].to_i
  minor = parts[1].to_i
  return '1' if major.zero?
  return major.to_s if minor.zero?

  (major + 1).to_s
end

#sanitize_doi_part(value) ⇒ Object

Keeps a suffix segment DOI-safe: trims, collapses whitespace to hyphens and drops anything outside [A-Za-z0-9._-] (colons stripped, as elsewhere here).



128
129
130
# File 'lib/labimotion/models/concerns/template_doi.rb', line 128

def sanitize_doi_part(value)
  value.to_s.strip.gsub(/\s+/, '-').gsub(/[^A-Za-z0-9._-]/, '')
end