-
Notifications
You must be signed in to change notification settings - Fork 109
/
identifier.rb
144 lines (115 loc) · 4.02 KB
/
identifier.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# frozen_string_literal: true
# == Schema Information
#
# Table name: identifiers
#
# id :integer not null, primary key
# attrs :text
# identifiable_type :string
# value :string not null
# created_at :datetime
# updated_at :datetime
# identifiable_id :integer
# identifier_scheme_id :integer not null
#
# Indexes
#
# index_identifiers_on_identifiable_type_and_identifiable_id (identifiable_type,identifiable_id)
#
# Object that represents an identifier for an object
class Identifier < ApplicationRecord
# ================
# = Associations =
# ================
belongs_to :identifiable, polymorphic: true
belongs_to :identifier_scheme, optional: true
# ===============
# = Validations =
# ===============
validates :value, presence: { message: PRESENCE_MESSAGE }
validates :identifiable, presence: { message: PRESENCE_MESSAGE }
validate :value_uniqueness_with_scheme, if: :schemed?
validate :value_uniqueness_without_scheme, unless: :schemed?
# ===============
# = Scopes =
# ===============
def self.by_scheme_name(scheme, identifiable_type)
scheme_id = if scheme.instance_of?(IdentifierScheme)
scheme.id
else
IdentifierScheme.by_name(scheme).first&.id
end
where(identifier_scheme_id: scheme_id,
identifiable_type: identifiable_type)
end
# =========================
# = Custom Accessor Logic =
# =========================
# Ensure that the value of attrs is a hash
# TODO: evaluate this vs the Serialize approach in condition.rb
def attrs=(hash)
super(hash.is_a?(Hash) ? hash.to_json.to_s : '{}')
end
# Appends the identifier scheme's prefix to the identifier if necessary
# For example:
# value '0000-0000-0000-0001'
# becomes 'https://orcid.org/0000-0000-0000-0001'
# rubocop:disable Metrics/AbcSize
def value=(val)
if identifier_scheme.present? &&
identifier_scheme.identifier_prefix.present? &&
!val.to_s.strip.blank? &&
!val.to_s.starts_with?(identifier_scheme.identifier_prefix)
base = identifier_scheme.identifier_prefix
base += '/' unless base.ends_with?('/')
super("#{base}#{val.to_s.strip}")
else
super(val)
end
end
# rubocop:enable Metrics/AbcSize
# ===========================
# = Public instance methods =
# ===========================
# Determines the format of the identifier based on the scheme or value
def identifier_format
scheme = identifier_scheme&.name
return scheme if %w[orcid ror fundref].include?(scheme)
return 'ark' if value.include?('ark:')
doi_regex = %r{(doi:)?[0-9]{2}\.[0-9]+/.}
return 'doi' if value =~ doi_regex
return 'url' if value.starts_with?('http')
'other'
end
# Returns the value sans the identifier scheme's prefix.
# For example:
# value 'https://orcid.org/0000-0000-0000-0001'
# becomes '0000-0000-0000-0001'
def value_without_scheme_prefix
return value unless identifier_scheme.present? &&
identifier_scheme.identifier_prefix.present?
base = identifier_scheme.identifier_prefix
value.gsub(base, '').sub(%r{^/}, '')
end
private
# ==============
# = VALIDATION =
# ==============
# Simple check used by :validate methods above
def schemed?
identifier_scheme.present?
end
# Verify the uniqueness of :value across :identifiable
def value_uniqueness_without_scheme
# if scheme is nil, then just unique for identifiable
return unless Identifier.where(identifiable: identifiable, value: value).any?
errors.add(:value, _('must be unique'))
end
# Ensure that the identifiable only has one identifier for the scheme
def value_uniqueness_with_scheme
if new_record? && Identifier.where(identifier_scheme: identifier_scheme,
identifiable: identifiable).any?
errors.add(:identifier_scheme, _('already assigned a value'))
end
end
end