lib/ddr/models/permanent_id.rb
require 'ezid-client'
module Ddr::Models
class PermanentId
class Error < Ddr::Models::Error; end
class AssignmentFailed < Error; end
class RepoObjectNotPersisted < Error; end
class AlreadyAssigned < AssignmentFailed; end
class IdentifierNotAssigned < Error; end
class IdentifierNotFound < Error; end
PERMANENT_URL_BASE = "https://idn.duke.edu/".freeze
DEFAULT_STATUS = Ezid::Status::RESERVED
DEFAULT_EXPORT = "no".freeze
DEFAULT_PROFILE = "dc".freeze
DEFAULT_TARGET = "https://repository.duke.edu/id/%s"
FCREPO3_PID = "fcrepo3.pid".freeze
DELETED = "deleted".freeze
DEACCESSIONED = "deaccessioned".freeze
class_attribute :identifier_class, :identifier_repo_id_field
self.identifier_class = Ezid::Identifier
self.identifier_class.defaults = {
profile: DEFAULT_PROFILE,
export: DEFAULT_EXPORT,
status: DEFAULT_STATUS,
}
self.identifier_repo_id_field = FCREPO3_PID
# ActiveSupport::Notifications event handler
def self.call(*args)
event = ActiveSupport::Notifications::Event.new(*args)
repo_id, identifier_id, reason = event.payload.values_at(:pid, :permanent_id, :reason)
case event.name
when Base::UPDATE
if auto_update? && event.payload[:attributes_changed].include?("workflow_state")
update!(repo_id)
end
when Base::DEACCESSION
if auto_update? && identifier_id
deaccession!(repo_id, identifier_id, reason)
end
when Base::DELETE
if auto_update? && identifier_id
delete!(repo_id, identifier_id, reason)
end
end
end
def self.auto_update?
Ddr::Models.auto_update_permanent_id
end
def self.deaccession!(repo_object_or_id, identifier_or_id, reason = nil)
new(repo_object_or_id, identifier_or_id).deaccession!(reason)
end
def self.delete!(repo_object_or_id, identifier_or_id, reason = nil)
new(repo_object_or_id, identifier_or_id).delete!(reason)
end
def self.update!(repo_object_or_id)
perm_id = new(repo_object_or_id)
perm_id.update! if perm_id.assigned?
end
def self.assign!(repo_object_or_id, identifier_or_id = nil)
new(repo_object_or_id, identifier_or_id).assign!
end
def self.assigned(repo_object_or_id)
perm_id = new(repo_object_or_id)
perm_id.assigned? ? perm_id : nil
end
def initialize(repo_object_or_id, identifier_or_id = nil)
case repo_object_or_id
when ActiveFedora::Base
raise RepoObjectNotPersisted, "Repository object must be persisted." if repo_object_or_id.new_record?
@repo_object = repo_object_or_id
when String, nil
@repo_id = repo_object_or_id
else
raise TypeError, "#{repo_object_or_id.class} is not expected as the first argument."
end
case identifier_or_id
when identifier_class
@identifier = identifier_or_id
when String, nil
@identifier_id = identifier_or_id
else
raise TypeError, "#{identifier_or_id.class} is not expected as the second argument."
end
end
def repo_object
@repo_object ||= ActiveFedora::Base.find(repo_id)
end
def repo_id
@repo_id ||= @repo_object && @repo_object.id
end
def assign!(id = nil)
if assigned?
raise AlreadyAssigned,
"Repository object \"#{repo_object.id}\" has already been assigned permanent id \"#{repo_object.permanent_id}\"."
end
@identifier = case id
when identifier_class
id
when String
find_identifier(id)
when nil
mint_identifier
end
repo_object.reload
repo_object.permanent_id = identifier.id
repo_object.permanent_url = PERMANENT_URL_BASE + identifier.id
repo_object.save!
set_metadata!
end
def assigned?
repo_object.permanent_id
end
def update!
if !assigned?
raise IdentifierNotAssigned,
"Cannot update identifier for repository object \"#{repo_object.id}\"; not assigned."
end
set_status!
end
def deaccession!(reason = nil)
delete_or_make_unavailable(reason || DEACCESSIONED)
end
def delete!(reason = nil)
delete_or_make_unavailable(reason || DELETED)
end
def identifier
if @identifier.nil?
if identifier_id
@identifier = find_identifier(identifier_id)
elsif assigned?
@identifier = find_identifier(repo_object.permanent_id)
end
end
@identifier
end
def identifier_id
@identifier_id ||= @identifier && @identifier.id
end
def set_permanent_url
repo_object.permanent_url = PERMANENT_URL_BASE + identifier.id
end
def set_metadata!
set_metadata
save
self
end
def set_metadata
set_target
set_status
set_identifier_repo_id
end
def set_target
self.target = DEFAULT_TARGET % id
end
def set_identifier_repo_id
self.identifier_repo_id = repo_object.id
end
def identifier_repo_id=(val)
if identifier_repo_id
raise Error, "Identifier repository id already set to \"#{identifier_repo_id}\"; cannot change."
end
self[identifier_repo_id_field] = val
end
def identifier_repo_id
self[identifier_repo_id_field]
end
def set_status!
save if set_status
end
def set_status
if repo_object.published? && !public?
public!
elsif repo_object.unpublished? && public?
unavailable!("not published")
end
end
protected
def method_missing(name, *args, &block)
identifier.send(name, *args, &block)
end
private
def find_identifier(ark)
identifier_class.find(ark)
rescue Ezid::IdentifierNotFoundError => e
raise IdentifierNotFound, e.message
end
def mint_identifier(*args)
identifier_class.mint(*args)
end
def delete_or_make_unavailable(reason)
if repo_id && identifier_repo_id && ( identifier_repo_id != repo_id )
raise Error, "Identifier \"#{identifier_id}\" is assigned to a different repository object \"#{repo_id}\"."
end
if reserved?
delete
else
unavailable!(reason) && save
end
end
end
end