ddr-models 2.6.0.rc1 → 2.6.0.rc2
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.
- checksums.yaml +4 -4
- data/config/initializers/subscriptions.rb +6 -0
- data/lib/ddr/models.rb +9 -0
- data/lib/ddr/models/base.rb +9 -6
- data/lib/ddr/models/permanent_id.rb +263 -0
- data/lib/ddr/models/version.rb +1 -1
- data/spec/models/permanent_id_spec.rb +440 -0
- data/spec/support/shared_examples_for_ddr_models.rb +30 -20
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a6aaa85c1cec8d9127e06e4db353d27bff2242f
|
4
|
+
data.tar.gz: d00f7e80d79c688e814ea6f3db6f07acf011cf09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a9dbb8d4bc4b4e4ecba17969d8a1dea7f10fc23da40e9002f8abfb86cce07005f461f100ebf3fd9dc70ef1485f40124b028b2df39361ba60d1cb091d481880f
|
7
|
+
data.tar.gz: 28eba8f8e3b45a2b19155e138a7886e97e6884b2296c4d344a9c11d1e1c2e1a01e07f9ab6bae1f2c1353b2073e6953a3fff270ff17505b842d52264a04a83af6
|
@@ -13,10 +13,16 @@ ActiveSupport::Notifications.subscribe(Ddr::Notifications::CREATION, Ddr::Events
|
|
13
13
|
|
14
14
|
# Update
|
15
15
|
ActiveSupport::Notifications.subscribe(Ddr::Notifications::UPDATE, Ddr::Events::UpdateEvent)
|
16
|
+
ActiveSupport::Notifications.subscribe("assign.permanent_id", Ddr::Events::UpdateEvent)
|
16
17
|
|
17
18
|
# Deletion
|
18
19
|
ActiveSupport::Notifications.subscribe(Ddr::Notifications::DELETION, Ddr::Events::DeletionEvent)
|
19
20
|
ActiveSupport::Notifications.subscribe(/destroy\.\w+/, Ddr::Events::DeletionEvent)
|
21
|
+
ActiveSupport::Notifications.subscribe(/destroy\.\w+/, Ddr::Models::PermanentId)
|
20
22
|
|
21
23
|
# Deaccession
|
22
24
|
ActiveSupport::Notifications.subscribe(/deaccession\.\w+/, Ddr::Events::DeaccessionEvent)
|
25
|
+
ActiveSupport::Notifications.subscribe(/deaccession\.\w+/, Ddr::Models::PermanentId)
|
26
|
+
|
27
|
+
# Workflow
|
28
|
+
ActiveSupport::Notifications.subscribe(/workflow/, Ddr::Models::PermanentId)
|
data/lib/ddr/models.rb
CHANGED
@@ -63,6 +63,7 @@ module Ddr
|
|
63
63
|
autoload :HasThumbnail
|
64
64
|
autoload :Indexing
|
65
65
|
autoload :NotFoundError, 'ddr/models/error'
|
66
|
+
autoload :PermanentId
|
66
67
|
autoload :SolrDocument
|
67
68
|
autoload :StructDiv
|
68
69
|
autoload :Structure
|
@@ -119,6 +120,14 @@ module Ddr
|
|
119
120
|
false
|
120
121
|
end
|
121
122
|
|
123
|
+
mattr_accessor :auto_assign_permanent_id do
|
124
|
+
false
|
125
|
+
end
|
126
|
+
|
127
|
+
mattr_accessor :auto_update_permanent_id do
|
128
|
+
false
|
129
|
+
end
|
130
|
+
|
122
131
|
# Yields an object with module configuration accessors
|
123
132
|
def self.configure
|
124
133
|
yield self
|
data/lib/ddr/models/base.rb
CHANGED
@@ -24,7 +24,7 @@ module Ddr
|
|
24
24
|
|
25
25
|
around_save :notify_save
|
26
26
|
around_save :notify_workflow_change, if: [:workflow_state_changed?, :persisted?]
|
27
|
-
after_create :
|
27
|
+
after_create :assign_permanent_id, if: :assign_permanent_id?
|
28
28
|
around_deaccession :notify_deaccession
|
29
29
|
around_destroy :notify_destroy
|
30
30
|
|
@@ -136,11 +136,6 @@ module Ddr
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
-
def notify_create
|
140
|
-
ActiveSupport::Notifications.instrument("create.#{self.class.to_s.underscore}",
|
141
|
-
pid: pid)
|
142
|
-
end
|
143
|
-
|
144
139
|
def notify_deaccession
|
145
140
|
ActiveSupport::Notifications.instrument("deaccession.#{self.class.to_s.underscore}",
|
146
141
|
pid: pid,
|
@@ -157,6 +152,14 @@ module Ddr
|
|
157
152
|
end
|
158
153
|
end
|
159
154
|
|
155
|
+
def assign_permanent_id?
|
156
|
+
permanent_id.nil? && Ddr::Models.auto_assign_permanent_id
|
157
|
+
end
|
158
|
+
|
159
|
+
def assign_permanent_id
|
160
|
+
PermanentId.assign!(self)
|
161
|
+
end
|
162
|
+
|
160
163
|
end
|
161
164
|
end
|
162
165
|
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'ezid-client'
|
2
|
+
|
3
|
+
module Ddr::Models
|
4
|
+
class PermanentId
|
5
|
+
|
6
|
+
class Error < Ddr::Models::Error; end
|
7
|
+
class AssignmentFailed < Error; end
|
8
|
+
class RepoObjectNotPersisted < Error; end
|
9
|
+
class AlreadyAssigned < AssignmentFailed; end
|
10
|
+
class IdentifierNotAssigned < Error; end
|
11
|
+
class IdentifierNotFound < Error; end
|
12
|
+
|
13
|
+
PERMANENT_URL_BASE = "https://idn.duke.edu/".freeze
|
14
|
+
DEFAULT_STATUS = Ezid::Status::RESERVED
|
15
|
+
DEFAULT_EXPORT = "no".freeze
|
16
|
+
DEFAULT_PROFILE = "dc".freeze
|
17
|
+
DEFAULT_TARGET = "https://repository.duke.edu/id/%s"
|
18
|
+
FCREPO3_PID = "fcrepo3.pid".freeze
|
19
|
+
DELETED = "deleted".freeze
|
20
|
+
DEACCESSIONED = "deaccessioned".freeze
|
21
|
+
|
22
|
+
class_attribute :identifier_class, :identifier_repo_id_field
|
23
|
+
|
24
|
+
self.identifier_class = Ezid::Identifier
|
25
|
+
self.identifier_class.defaults = {
|
26
|
+
profile: DEFAULT_PROFILE,
|
27
|
+
export: DEFAULT_EXPORT,
|
28
|
+
status: DEFAULT_STATUS,
|
29
|
+
}
|
30
|
+
|
31
|
+
self.identifier_repo_id_field = FCREPO3_PID
|
32
|
+
|
33
|
+
# ActiveSupport::Notifications event handler
|
34
|
+
def self.call(*args)
|
35
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
36
|
+
repo_id, identifier_id, reason = event.payload.values_at(:pid, :permanent_id, :reason)
|
37
|
+
case event.name
|
38
|
+
when /workflow/
|
39
|
+
update!(repo_id) if auto_update?
|
40
|
+
when /^deaccession/
|
41
|
+
if auto_update? && identifier_id
|
42
|
+
deaccession!(repo_id, identifier_id, reason)
|
43
|
+
end
|
44
|
+
when /^destroy/
|
45
|
+
if auto_update? && identifier_id
|
46
|
+
delete!(repo_id, identifier_id, reason)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.auto_update?
|
52
|
+
Ddr::Models.auto_update_permanent_id
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.deaccession!(repo_object_or_id, identifier_or_id, reason = nil)
|
56
|
+
new(repo_object_or_id, identifier_or_id).deaccession!(reason)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.delete!(repo_object_or_id, identifier_or_id, reason = nil)
|
60
|
+
new(repo_object_or_id, identifier_or_id).delete!(reason)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.update!(repo_object_or_id)
|
64
|
+
perm_id = new(repo_object_or_id)
|
65
|
+
perm_id.update! if perm_id.assigned?
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.assign!(repo_object_or_id, identifier_or_id = nil)
|
69
|
+
new(repo_object_or_id, identifier_or_id).assign!
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.assigned(repo_object_or_id)
|
73
|
+
perm_id = new(repo_object_or_id)
|
74
|
+
perm_id.assigned? ? perm_id : nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize(repo_object_or_id, identifier_or_id = nil)
|
78
|
+
case repo_object_or_id
|
79
|
+
when ActiveFedora::Base
|
80
|
+
raise RepoObjectNotPersisted, "Repository object must be persisted." if repo_object_or_id.new_record?
|
81
|
+
@repo_object = repo_object_or_id
|
82
|
+
when String, nil
|
83
|
+
@repo_id = repo_object_or_id
|
84
|
+
else
|
85
|
+
raise TypeError, "#{repo_object_or_id.class} is not expected as the first argument."
|
86
|
+
end
|
87
|
+
|
88
|
+
case identifier_or_id
|
89
|
+
when identifier_class
|
90
|
+
@identifier = identifier_or_id
|
91
|
+
when String, nil
|
92
|
+
@identifier_id = identifier_or_id
|
93
|
+
else
|
94
|
+
raise TypeError, "#{identifier_or_id.class} is not expected as the second argument."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def repo_object
|
99
|
+
@repo_object ||= ActiveFedora::Base.find(repo_id)
|
100
|
+
end
|
101
|
+
|
102
|
+
def repo_id
|
103
|
+
@repo_id ||= @repo_object && @repo_object.id
|
104
|
+
end
|
105
|
+
|
106
|
+
def assign!(id = nil)
|
107
|
+
ActiveSupport::Notifications.instrument("assign.permanent_id", pid: repo_object.id) do |payload|
|
108
|
+
assign(id)
|
109
|
+
software = [ "ddr-models #{Ddr::Models::VERSION}", Ezid::Client.version ].join("; ")
|
110
|
+
detail = <<-EOS
|
111
|
+
Permanent ID: #{repo_object.permanent_id}
|
112
|
+
Permanent URL: #{repo_object.permanent_url}
|
113
|
+
|
114
|
+
EZID Metadata:
|
115
|
+
#{identifier.metadata}
|
116
|
+
EOS
|
117
|
+
payload.merge!(summary: "Permanent ID assignment",
|
118
|
+
detail: detail,
|
119
|
+
software: software,
|
120
|
+
permanent_id: identifier.id)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def assigned?
|
125
|
+
repo_object.permanent_id
|
126
|
+
end
|
127
|
+
|
128
|
+
def update!
|
129
|
+
ActiveSupport::Notifications.instrument("update.permanent_id", pid: repo_object.id) do |payload|
|
130
|
+
update
|
131
|
+
payload.merge!(permanent_id: identifier.id)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def deaccession!(reason = nil)
|
136
|
+
delete_or_make_unavailable(reason || DEACCESSIONED)
|
137
|
+
end
|
138
|
+
|
139
|
+
def delete!(reason = nil)
|
140
|
+
delete_or_make_unavailable(reason || DELETED)
|
141
|
+
end
|
142
|
+
|
143
|
+
def identifier
|
144
|
+
if @identifier.nil?
|
145
|
+
if identifier_id
|
146
|
+
@identifier = find_identifier(identifier_id)
|
147
|
+
elsif assigned?
|
148
|
+
@identifier = find_identifier(repo_object.permanent_id)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
@identifier
|
152
|
+
end
|
153
|
+
|
154
|
+
def identifier_id
|
155
|
+
@identifier_id ||= @identifier && @identifier.id
|
156
|
+
end
|
157
|
+
|
158
|
+
def set_permanent_url
|
159
|
+
repo_object.permanent_url = PERMANENT_URL_BASE + identifier.id
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_metadata!
|
163
|
+
set_metadata
|
164
|
+
save
|
165
|
+
self
|
166
|
+
end
|
167
|
+
|
168
|
+
def set_metadata
|
169
|
+
set_target
|
170
|
+
set_status
|
171
|
+
set_identifier_repo_id
|
172
|
+
end
|
173
|
+
|
174
|
+
def set_target
|
175
|
+
self.target = DEFAULT_TARGET % id
|
176
|
+
end
|
177
|
+
|
178
|
+
def set_identifier_repo_id
|
179
|
+
self.identifier_repo_id = repo_object.id
|
180
|
+
end
|
181
|
+
|
182
|
+
def identifier_repo_id=(val)
|
183
|
+
if identifier_repo_id
|
184
|
+
raise Error, "Identifier repository id already set to \"#{identifier_repo_id}\"; cannot change."
|
185
|
+
end
|
186
|
+
self[identifier_repo_id_field] = val
|
187
|
+
end
|
188
|
+
|
189
|
+
def identifier_repo_id
|
190
|
+
self[identifier_repo_id_field]
|
191
|
+
end
|
192
|
+
|
193
|
+
def set_status!
|
194
|
+
save if set_status
|
195
|
+
end
|
196
|
+
|
197
|
+
def set_status
|
198
|
+
if repo_object.published? && !public?
|
199
|
+
public!
|
200
|
+
elsif repo_object.unpublished? && public?
|
201
|
+
unavailable!("not published")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
protected
|
206
|
+
|
207
|
+
def method_missing(name, *args, &block)
|
208
|
+
identifier.send(name, *args, &block)
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
def find_identifier(ark)
|
214
|
+
identifier_class.find(ark)
|
215
|
+
rescue Ezid::IdentifierNotFoundError => e
|
216
|
+
raise IdentifierNotFound, e.message
|
217
|
+
end
|
218
|
+
|
219
|
+
def mint_identifier(*args)
|
220
|
+
identifier_class.mint(*args)
|
221
|
+
end
|
222
|
+
|
223
|
+
def update
|
224
|
+
if !assigned?
|
225
|
+
raise IdentifierNotAssigned,
|
226
|
+
"Cannot update identifier for repository object \"#{repo_object.id}\"; not assigned."
|
227
|
+
end
|
228
|
+
set_status!
|
229
|
+
end
|
230
|
+
|
231
|
+
def assign(id = nil)
|
232
|
+
if assigned?
|
233
|
+
raise AlreadyAssigned,
|
234
|
+
"Repository object \"#{repo_object.id}\" has already been assigned permanent id \"#{repo_object.permanent_id}\"."
|
235
|
+
end
|
236
|
+
@identifier = case id
|
237
|
+
when identifier_class
|
238
|
+
id
|
239
|
+
when String
|
240
|
+
find_identifier(id)
|
241
|
+
when nil
|
242
|
+
mint_identifier
|
243
|
+
end
|
244
|
+
repo_object.reload
|
245
|
+
repo_object.permanent_id = identifier.id
|
246
|
+
repo_object.permanent_url = PERMANENT_URL_BASE + identifier.id
|
247
|
+
repo_object.save(validate: false)
|
248
|
+
set_metadata!
|
249
|
+
end
|
250
|
+
|
251
|
+
def delete_or_make_unavailable(reason)
|
252
|
+
if repo_id && identifier_repo_id && ( identifier_repo_id != repo_id )
|
253
|
+
raise Error, "Identifier \"#{identifier_id}\" is assigned to a different repository object \"#{repo_id}\"."
|
254
|
+
end
|
255
|
+
if reserved?
|
256
|
+
delete
|
257
|
+
else
|
258
|
+
unavailable!(reason) && save
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
data/lib/ddr/models/version.rb
CHANGED
@@ -0,0 +1,440 @@
|
|
1
|
+
module Ddr::Models
|
2
|
+
RSpec.describe PermanentId do
|
3
|
+
|
4
|
+
describe "auto assigment" do
|
5
|
+
let(:obj) { FactoryGirl.create(:item) }
|
6
|
+
describe "when enabled" do
|
7
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
8
|
+
before do
|
9
|
+
allow(id).to receive(:save) { nil }
|
10
|
+
allow(described_class.identifier_class).to receive(:mint) { id }
|
11
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
12
|
+
allow(Ddr::Models).to receive(:auto_assign_permanent_id) { true }
|
13
|
+
end
|
14
|
+
after do
|
15
|
+
obj.permanent_id = nil
|
16
|
+
obj.save!
|
17
|
+
end
|
18
|
+
it "assigns a permanent id to the object" do
|
19
|
+
obj.reload
|
20
|
+
expect(obj.permanent_id).to eq("foo")
|
21
|
+
expect(obj.permanent_url).to eq("https://idn.duke.edu/foo")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
describe "when disabled" do
|
25
|
+
before do
|
26
|
+
allow(Ddr::Models).to receive(:auto_assign_permanent_id) { false }
|
27
|
+
end
|
28
|
+
it "does not assign a permanent id to the object" do
|
29
|
+
expect(obj.reload.permanent_id).to be_nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "assignment" do
|
35
|
+
let(:obj) { FactoryGirl.create(:item) }
|
36
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
37
|
+
before do
|
38
|
+
allow(id).to receive(:save) { nil }
|
39
|
+
allow(described_class.identifier_class).to receive(:mint) { id }
|
40
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
41
|
+
end
|
42
|
+
after do
|
43
|
+
obj.permanent_id = nil
|
44
|
+
obj.save!
|
45
|
+
end
|
46
|
+
it "creates an update event" do
|
47
|
+
expect { described_class.assign!(obj) }.to change(Ddr::Events::UpdateEvent, :count).to(1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "auto updating" do
|
52
|
+
let(:obj) { Item.create(pid: "test:1") }
|
53
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
54
|
+
before do
|
55
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
56
|
+
end
|
57
|
+
describe "when enabled" do
|
58
|
+
before do
|
59
|
+
allow(Ddr::Models).to receive(:auto_update_permanent_id) { true }
|
60
|
+
end
|
61
|
+
describe "when the object workflow state changes" do
|
62
|
+
describe "and the object has a permanent id" do
|
63
|
+
before do
|
64
|
+
allow(id).to receive(:save) { nil }
|
65
|
+
obj.permanent_id = "foo"
|
66
|
+
end
|
67
|
+
after do
|
68
|
+
obj.permanent_id = nil
|
69
|
+
obj.save!
|
70
|
+
end
|
71
|
+
it "updates the permanent id" do
|
72
|
+
expect { obj.publish! }.to change(id, :status).to("public")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
describe "and the object does not have a permanent id" do
|
76
|
+
it "does not update the permanent id" do
|
77
|
+
expect { obj.publish! }.not_to change(id, :status)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
describe "when the object workflow state does not change" do
|
82
|
+
it "does not update the permanent id" do
|
83
|
+
expect { obj.save! }.not_to change(id, :status)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
describe "when disabled" do
|
88
|
+
before do
|
89
|
+
allow(Ddr::Models).to receive(:auto_update_permanent_id) { false }
|
90
|
+
end
|
91
|
+
describe "when the object workflow state changes" do
|
92
|
+
describe "and the object has a permanent id" do
|
93
|
+
before do
|
94
|
+
allow(id).to receive(:save) { nil }
|
95
|
+
obj.permanent_id = "foo"
|
96
|
+
end
|
97
|
+
after do
|
98
|
+
obj.permanent_id = nil
|
99
|
+
obj.save!
|
100
|
+
end
|
101
|
+
it "does not update the permanent id" do
|
102
|
+
expect { obj.publish! }.not_to change(id, :status)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
describe "and the object does not have a permanent id" do
|
106
|
+
it "does not update the permanent id" do
|
107
|
+
expect { obj.publish! }.not_to change(id, :status)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "auto deleting / marking unavailable" do
|
115
|
+
describe "when enabled" do
|
116
|
+
before do
|
117
|
+
allow(Ddr::Models).to receive(:auto_update_permanent_id) { true }
|
118
|
+
end
|
119
|
+
describe "when an object has a permanent id" do
|
120
|
+
let(:obj) { Item.create(pid: "test:1", permanent_id: "foo") }
|
121
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
122
|
+
before do
|
123
|
+
allow(id).to receive(:save) { nil }
|
124
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
125
|
+
end
|
126
|
+
describe "and it's deacessioned" do
|
127
|
+
specify {
|
128
|
+
expect(described_class).to receive(:deaccession!).with("test:1", "foo", nil) { nil }
|
129
|
+
obj.deaccession
|
130
|
+
}
|
131
|
+
end
|
132
|
+
describe "and it's deleted" do
|
133
|
+
specify {
|
134
|
+
expect(described_class).not_to receive(:delete!)
|
135
|
+
expect(described_class).not_to receive(:deaccession!)
|
136
|
+
obj.send(:delete)
|
137
|
+
}
|
138
|
+
end
|
139
|
+
describe "and it's destroyed" do
|
140
|
+
specify {
|
141
|
+
expect(described_class).to receive(:delete!).with("test:1", "foo", nil) { nil }
|
142
|
+
obj.destroy
|
143
|
+
}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
describe "when an object does not have a permanent id" do
|
147
|
+
let(:obj) { FactoryGirl.create(:item) }
|
148
|
+
describe "and it's deacessioned" do
|
149
|
+
specify {
|
150
|
+
expect(described_class).not_to receive(:deaccession!)
|
151
|
+
obj.deaccession
|
152
|
+
}
|
153
|
+
end
|
154
|
+
describe "and it's deleted" do
|
155
|
+
specify {
|
156
|
+
expect(described_class).not_to receive(:deaccession!)
|
157
|
+
expect(described_class).not_to receive(:delete!)
|
158
|
+
obj.send(:delete)
|
159
|
+
}
|
160
|
+
end
|
161
|
+
describe "and it's destroyed" do
|
162
|
+
specify {
|
163
|
+
expect(described_class).not_to receive(:delete!)
|
164
|
+
obj.destroy
|
165
|
+
}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
describe "when disabled" do
|
170
|
+
before do
|
171
|
+
allow(Ddr::Models).to receive(:auto_update_permanent_id) { false }
|
172
|
+
end
|
173
|
+
describe "when an object has a permanent id" do
|
174
|
+
let(:obj) { Item.create(pid: "test:1", permanent_id: "foo") }
|
175
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
176
|
+
before do
|
177
|
+
allow(id).to receive(:save) { nil }
|
178
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
179
|
+
end
|
180
|
+
describe "and it's deacessioned" do
|
181
|
+
specify {
|
182
|
+
expect(described_class).not_to receive(:deaccession!)
|
183
|
+
obj.deaccession
|
184
|
+
}
|
185
|
+
end
|
186
|
+
describe "and it's destroyed" do
|
187
|
+
specify {
|
188
|
+
expect(described_class).not_to receive(:delete!)
|
189
|
+
obj.destroy
|
190
|
+
}
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "constructor" do
|
197
|
+
describe "when the object is new" do
|
198
|
+
let(:obj) { FactoryGirl.build(:item) }
|
199
|
+
it "raises an error" do
|
200
|
+
expect { described_class.new(obj) }.to raise_error(PermanentId::RepoObjectNotPersisted)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
describe "when passed a repo object id" do
|
204
|
+
let(:obj) { FactoryGirl.create(:item) }
|
205
|
+
subject { described_class.new(obj.id) }
|
206
|
+
its(:repo_id) { is_expected.to eq(obj.id) }
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "instance methods" do
|
211
|
+
let(:obj) { FactoryGirl.create(:item) }
|
212
|
+
subject { described_class.new(obj) }
|
213
|
+
|
214
|
+
describe "#assign!" do
|
215
|
+
describe "when the object already has a permanent identifier" do
|
216
|
+
before { obj.permanent_id = "foo" }
|
217
|
+
it "raises an error" do
|
218
|
+
expect { subject.assign! }.to raise_error(PermanentId::AlreadyAssigned)
|
219
|
+
expect { subject.assign!("bar") }.to raise_error(PermanentId::AlreadyAssigned)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
describe "when the object does not have a permanent identifier" do
|
223
|
+
let(:obj) { FactoryGirl.create(:item) }
|
224
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
225
|
+
before do
|
226
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
227
|
+
allow(id).to receive(:save) { nil }
|
228
|
+
allow(obj).to receive(:save) { true }
|
229
|
+
end
|
230
|
+
describe "when passed an ARK" do
|
231
|
+
before do
|
232
|
+
subject.assign!("foo")
|
233
|
+
end
|
234
|
+
it "assigns the ARK" do
|
235
|
+
expect(obj.permanent_id).to eq("foo")
|
236
|
+
end
|
237
|
+
it "sets the target on the identifier" do
|
238
|
+
expect(id.target).to eq("https://repository.duke.edu/id/foo")
|
239
|
+
end
|
240
|
+
it "sets the status on the identifier" do
|
241
|
+
expect(id.status).to eq("reserved")
|
242
|
+
end
|
243
|
+
it "sets the repository id on the identifier" do
|
244
|
+
expect(id["fcrepo3.pid"]).to eq(obj.id)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
describe "when not passed an ARK" do
|
248
|
+
before do
|
249
|
+
subject.assign!("foo")
|
250
|
+
allow(described_class.identifier_class).to receive(:mint) { id }
|
251
|
+
end
|
252
|
+
it "mints and assigns an ARK" do
|
253
|
+
expect(obj.permanent_id).to eq("foo")
|
254
|
+
end
|
255
|
+
it "sets the target on the identifier" do
|
256
|
+
expect(id.target).to eq("https://repository.duke.edu/id/foo")
|
257
|
+
end
|
258
|
+
it "sets the status on the identifier" do
|
259
|
+
expect(id.status).to eq("reserved")
|
260
|
+
end
|
261
|
+
it "sets the repository id on the identifier" do
|
262
|
+
expect(id["fcrepo3.pid"]).to eq(obj.id)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "#update!" do
|
269
|
+
describe "when the object has not been assigned a permanent id" do
|
270
|
+
it "raises an error" do
|
271
|
+
expect { subject.update! }.to raise_error(PermanentId::IdentifierNotAssigned)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
describe "when the object has been assigned a permanent id" do
|
275
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
276
|
+
before do
|
277
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
278
|
+
obj.permanent_id = "foo"
|
279
|
+
end
|
280
|
+
it "sets the status on the permanent id" do
|
281
|
+
expect(subject).to receive(:set_status!) { nil }
|
282
|
+
subject.update!
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "#deaccession!" do
|
288
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
289
|
+
subject { described_class.new("test:1", "foo") }
|
290
|
+
before do
|
291
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
292
|
+
allow(id).to receive(:save) { nil }
|
293
|
+
end
|
294
|
+
specify {
|
295
|
+
expect(id).to receive(:delete)
|
296
|
+
subject.deaccession!
|
297
|
+
}
|
298
|
+
describe " when the identifier is not reserved" do
|
299
|
+
before { id.public! }
|
300
|
+
specify {
|
301
|
+
expect { subject.deaccession! }.to change(id, :status).to("unavailable | deaccessioned")
|
302
|
+
}
|
303
|
+
end
|
304
|
+
describe "when the identifier is associated with another repo id" do
|
305
|
+
before { subject.identifier_repo_id = "test:2" }
|
306
|
+
specify {
|
307
|
+
expect { subject.deaccession! }.to raise_error(PermanentId::Error)
|
308
|
+
}
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
describe "#delete!" do
|
313
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
314
|
+
subject { described_class.new("test:1", "foo") }
|
315
|
+
before do
|
316
|
+
allow(described_class.identifier_class).to receive(:find).with("foo") { id }
|
317
|
+
end
|
318
|
+
specify {
|
319
|
+
expect(id).to receive(:delete)
|
320
|
+
subject.delete!
|
321
|
+
}
|
322
|
+
describe " when the identifier is not reserved" do
|
323
|
+
before do
|
324
|
+
allow(id).to receive(:save) { nil }
|
325
|
+
id.public!
|
326
|
+
end
|
327
|
+
specify {
|
328
|
+
expect { subject.delete! }.to change(id, :status).to("unavailable | deleted")
|
329
|
+
}
|
330
|
+
end
|
331
|
+
describe "when the identifier is associated with another repo id" do
|
332
|
+
before { subject.identifier_repo_id = "test:2" }
|
333
|
+
specify {
|
334
|
+
expect { subject.delete! }.to raise_error(PermanentId::Error)
|
335
|
+
}
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe "identifier metadata" do
|
340
|
+
let(:obj) { Item.create(pid: "test:1") }
|
341
|
+
let!(:id) { described_class.identifier_class.new("foo") }
|
342
|
+
subject { described_class.new(obj) }
|
343
|
+
before do
|
344
|
+
allow(subject).to receive(:identifier) { id }
|
345
|
+
end
|
346
|
+
describe "#identifier_repo_id" do
|
347
|
+
before do
|
348
|
+
id["fcrepo3.pid"] = "test:1"
|
349
|
+
end
|
350
|
+
its(:identifier_repo_id) { is_expected.to eq("test:1") }
|
351
|
+
end
|
352
|
+
describe "#identifier_repo_id=" do
|
353
|
+
specify {
|
354
|
+
subject.identifier_repo_id = "test:1"
|
355
|
+
expect(id["fcrepo3.pid"]).to eq("test:1")
|
356
|
+
}
|
357
|
+
describe "when a value was previously assigned" do
|
358
|
+
before { subject.identifier_repo_id = "test:1" }
|
359
|
+
specify {
|
360
|
+
expect { subject.identifier_repo_id = "test:2" }.to raise_error(PermanentId::Error)
|
361
|
+
}
|
362
|
+
end
|
363
|
+
end
|
364
|
+
describe "#set_identifier_repo_id" do
|
365
|
+
specify {
|
366
|
+
subject.set_identifier_repo_id
|
367
|
+
expect(subject.identifier_repo_id).to eq("test:1")
|
368
|
+
}
|
369
|
+
end
|
370
|
+
describe "#set_target" do
|
371
|
+
specify {
|
372
|
+
subject.set_target
|
373
|
+
expect(subject.target).to eq("https://repository.duke.edu/id/foo")
|
374
|
+
}
|
375
|
+
end
|
376
|
+
describe "#set_status" do
|
377
|
+
describe "when object is published" do
|
378
|
+
before { obj.workflow_state = "published" }
|
379
|
+
describe "and identifier is public" do
|
380
|
+
before { subject.public! }
|
381
|
+
it "does not change" do
|
382
|
+
expect { subject.set_status }.not_to change(subject, :status)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
describe "and identifier is reserved" do
|
386
|
+
it "changes to public" do
|
387
|
+
expect { subject.set_status }.to change(subject, :status).to("public")
|
388
|
+
end
|
389
|
+
end
|
390
|
+
describe "and identifier is unavailable" do
|
391
|
+
before { subject.unavailable! }
|
392
|
+
it "changes to public" do
|
393
|
+
expect { subject.set_status }.to change(subject, :status).to("public")
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
describe "when object is unpublished" do
|
398
|
+
before { obj.workflow_state = "unpublished" }
|
399
|
+
describe "and identifier is public" do
|
400
|
+
before { subject.public! }
|
401
|
+
it "changes to unavailable" do
|
402
|
+
expect { subject.set_status }.to change(subject, :status).to("unavailable | not published")
|
403
|
+
end
|
404
|
+
end
|
405
|
+
describe "and identifier is reserved" do
|
406
|
+
it "does not change" do
|
407
|
+
expect { subject.set_status }.not_to change(subject, :status)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
describe "and identifier is unavailable" do
|
411
|
+
before { subject.unavailable! }
|
412
|
+
it "does not change" do
|
413
|
+
expect { subject.set_status }.not_to change(subject, :status)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
describe "when object has no workflow state" do
|
418
|
+
describe "and identifier is public" do
|
419
|
+
before { subject.public! }
|
420
|
+
it "does not change" do
|
421
|
+
expect { subject.set_status }.not_to change(subject, :status)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
describe "and identifier is reserved" do
|
425
|
+
it "does not change" do
|
426
|
+
expect { subject.set_status }.not_to change(subject, :status)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
describe "and identifier is unavailable" do
|
430
|
+
before { subject.unavailable! }
|
431
|
+
it "does not change" do
|
432
|
+
expect { subject.set_status }.not_to change(subject, :status)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
@@ -6,6 +6,36 @@ RSpec.shared_examples "a DDR model" do
|
|
6
6
|
it_behaves_like "an object that has a display title"
|
7
7
|
it_behaves_like "an object that has identifiers"
|
8
8
|
|
9
|
+
describe "permanent ID assignment" do
|
10
|
+
describe "when auto assignment is enabled" do
|
11
|
+
before do
|
12
|
+
allow(Ddr::Models).to receive(:auto_assign_permanent_id) { true }
|
13
|
+
end
|
14
|
+
describe "and a permanent ID is pre-assigned" do
|
15
|
+
before do
|
16
|
+
subject.permanent_id = "foo"
|
17
|
+
end
|
18
|
+
it "does not assign a permanent ID" do
|
19
|
+
expect { subject.save(validate: false) }.not_to change(subject, :permanent_id)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
describe "and no permanent ID has been pre-assigned" do
|
23
|
+
before do
|
24
|
+
expect(Ddr::Models::PermanentId).to receive(:assign!).with(subject) { nil }
|
25
|
+
subject.save(validate: false)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
describe "when auto assignment is disabled" do
|
30
|
+
before do
|
31
|
+
allow(Ddr::Models).to receive(:auto_assign_permanent_id) { false }
|
32
|
+
end
|
33
|
+
it "does not assign a permanent ID" do
|
34
|
+
expect { subject.save(validate: false) }.not_to change(subject, :permanent_id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
9
39
|
describe "notification on save" do
|
10
40
|
let(:events) { [] }
|
11
41
|
before {
|
@@ -30,26 +60,6 @@ RSpec.shared_examples "a DDR model" do
|
|
30
60
|
end
|
31
61
|
end
|
32
62
|
|
33
|
-
describe "notification on create" do
|
34
|
-
let(:events) { [] }
|
35
|
-
before {
|
36
|
-
@subscriber = ActiveSupport::Notifications.subscribe("create.#{described_class.to_s.underscore}") do |name, start, finish, id, payload|
|
37
|
-
events << payload
|
38
|
-
end
|
39
|
-
}
|
40
|
-
after {
|
41
|
-
ActiveSupport::Notifications.unsubscribe(@subscriber)
|
42
|
-
}
|
43
|
-
it "happens after create" do
|
44
|
-
subject.title = [ "My Title Changed" ]
|
45
|
-
subject.save
|
46
|
-
subject.title = [ "My Title Changed Again" ]
|
47
|
-
subject.save
|
48
|
-
expect(events.size).to eq(1)
|
49
|
-
expect(events.first[:pid]).to eq(subject.pid)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
63
|
describe "notification on workflow state change" do
|
54
64
|
let(:events) { [] }
|
55
65
|
before {
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ddr-models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.6.0.
|
4
|
+
version: 2.6.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Coble
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-01-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -552,6 +552,7 @@ files:
|
|
552
552
|
- lib/ddr/models/licenses/inherited_license.rb
|
553
553
|
- lib/ddr/models/licenses/license.rb
|
554
554
|
- lib/ddr/models/licenses/parent_license.rb
|
555
|
+
- lib/ddr/models/permanent_id.rb
|
555
556
|
- lib/ddr/models/solr_document.rb
|
556
557
|
- lib/ddr/models/struct_div.rb
|
557
558
|
- lib/ddr/models/structure.rb
|
@@ -697,6 +698,7 @@ files:
|
|
697
698
|
- spec/models/indexing_spec.rb
|
698
699
|
- spec/models/item_spec.rb
|
699
700
|
- spec/models/license_spec.rb
|
701
|
+
- spec/models/permanent_id_spec.rb
|
700
702
|
- spec/models/solr_document_spec.rb
|
701
703
|
- spec/models/struct_div_spec.rb
|
702
704
|
- spec/models/structure_spec.rb
|
@@ -874,6 +876,7 @@ test_files:
|
|
874
876
|
- spec/models/indexing_spec.rb
|
875
877
|
- spec/models/item_spec.rb
|
876
878
|
- spec/models/license_spec.rb
|
879
|
+
- spec/models/permanent_id_spec.rb
|
877
880
|
- spec/models/solr_document_spec.rb
|
878
881
|
- spec/models/struct_div_spec.rb
|
879
882
|
- spec/models/structure_spec.rb
|