ezid-client 0.3.0 → 0.4.0
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/.travis.yml +10 -0
- data/README.md +38 -59
- data/Rakefile +0 -7
- data/VERSION +1 -1
- data/ezid-client.gemspec +4 -2
- data/lib/ezid/client.rb +70 -100
- data/lib/ezid/configuration.rb +27 -12
- data/lib/ezid/identifier.rb +170 -0
- data/lib/ezid/metadata.rb +52 -94
- data/lib/ezid/metadata_elements.rb +89 -0
- data/lib/ezid/request.rb +20 -34
- data/lib/ezid/response.rb +40 -26
- data/lib/ezid/session.rb +2 -8
- data/lib/ezid/status.rb +23 -0
- data/spec/lib/ezid/client_spec.rb +136 -45
- data/spec/lib/ezid/identifier_spec.rb +203 -0
- data/spec/lib/ezid/metadata_spec.rb +200 -41
- data/spec/spec_helper.rb +7 -10
- metadata +32 -43
- data/lib/ezid/api.rb +0 -67
- data/lib/ezid/logger.rb +0 -31
- data/lib/ezid/test_helper.rb +0 -22
@@ -0,0 +1,203 @@
|
|
1
|
+
module Ezid
|
2
|
+
RSpec.describe Identifier do
|
3
|
+
|
4
|
+
describe ".create" do
|
5
|
+
describe "when given an id" do
|
6
|
+
let(:id) { "ark:/99999/fk4zzzzzzz" }
|
7
|
+
subject { described_class.create(id: id) }
|
8
|
+
before do
|
9
|
+
allow_any_instance_of(Client).to receive(:create_identifier).with(id, {}) { double(id: id) }
|
10
|
+
allow_any_instance_of(Client).to receive(:get_identifier_metadata).with(id) { double(metadata: {}) }
|
11
|
+
end
|
12
|
+
it "should create an identifier" do
|
13
|
+
expect(subject).to be_a(described_class)
|
14
|
+
expect(subject.id).to eq(id)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
describe "when given a shoulder (and no id)" do
|
18
|
+
let(:id) { "ark:/99999/fk4fn19h88" }
|
19
|
+
subject { described_class.create(shoulder: ARK_SHOULDER) }
|
20
|
+
before do
|
21
|
+
allow_any_instance_of(Client).to receive(:mint_identifier).with(ARK_SHOULDER, {}) { double(id: id) }
|
22
|
+
allow_any_instance_of(Client).to receive(:get_identifier_metadata).with(id) { double(metadata: {}) }
|
23
|
+
end
|
24
|
+
it "should mint an identifier" do
|
25
|
+
expect(subject).to be_a(described_class)
|
26
|
+
expect(subject.id).to eq(id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
describe "when given neither an id nor a shoulder" do
|
30
|
+
it "should raise an exception" do
|
31
|
+
expect { described_class.create }.to raise_error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
describe "with metadata" do
|
35
|
+
it "should send the metadata"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ".find" do
|
40
|
+
describe "when the id exists" do
|
41
|
+
let(:id) { "ark:/99999/fk4fn19h88" }
|
42
|
+
subject { described_class.find(id) }
|
43
|
+
before do
|
44
|
+
allow_any_instance_of(Client).to receive(:get_identifier_metadata).with(id) { double(id: id, metadata: {}) }
|
45
|
+
end
|
46
|
+
it "should get the identifier" do
|
47
|
+
expect(subject).to be_a(Identifier)
|
48
|
+
expect(subject.id).to eq(id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
describe "when the id does not exist" do
|
52
|
+
let(:id) { "ark:/99999/fk4zzzzzzz" }
|
53
|
+
before do
|
54
|
+
allow_any_instance_of(Client).to receive(:get_identifier_metadata).with(id).and_raise(Error)
|
55
|
+
end
|
56
|
+
it "should raise an exception" do
|
57
|
+
expect { described_class.find(id) }.to raise_error
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#update" do
|
63
|
+
let(:id) { "ark:/99999/fk4fn19h88" }
|
64
|
+
let(:metadata) { {"_status" => "unavailable"} }
|
65
|
+
subject { described_class.new(id: id) }
|
66
|
+
before do
|
67
|
+
allow(subject).to receive(:persisted?) { true }
|
68
|
+
allow(subject.client).to receive(:modify_identifier).with(id, subject.metadata) do
|
69
|
+
double(id: id, metadata: {})
|
70
|
+
end
|
71
|
+
end
|
72
|
+
it "should update the metadata" do
|
73
|
+
expect(subject.metadata).to receive(:update).with(metadata)
|
74
|
+
subject.update(metadata)
|
75
|
+
end
|
76
|
+
it "should save the identifier" do
|
77
|
+
expect(subject).to receive(:save)
|
78
|
+
subject.update(metadata)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#reload" do
|
83
|
+
let(:id) { "ark:/99999/fk4fn19h88" }
|
84
|
+
let(:metadata) { "_created: 1416507086" }
|
85
|
+
subject { described_class.new(id: id) }
|
86
|
+
before do
|
87
|
+
allow(subject.client).to receive(:get_identifier_metadata).with(id) { double(metadata: metadata) }
|
88
|
+
end
|
89
|
+
it "should update the metadata from EZID" do
|
90
|
+
expect(subject.metadata).to receive(:replace).with(metadata)
|
91
|
+
subject.reload
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#reset" do
|
96
|
+
it "should clear the local metadata" do
|
97
|
+
expect(subject.metadata).to receive(:clear)
|
98
|
+
subject.reset
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#persisted?" do
|
103
|
+
it "should be false if id is nil" do
|
104
|
+
expect(subject).not_to be_persisted
|
105
|
+
end
|
106
|
+
context "when `created' is nil" do
|
107
|
+
before { allow(subject).to receive(:id) { "ark:/99999/fk4fn19h88" } }
|
108
|
+
it "should be false" do
|
109
|
+
expect(subject).not_to be_persisted
|
110
|
+
end
|
111
|
+
end
|
112
|
+
context "when id and `created' are present" do
|
113
|
+
before do
|
114
|
+
allow(subject).to receive(:id) { "ark:/99999/fk4fn19h88" }
|
115
|
+
allow(subject.metadata).to receive(:created) { Time.at(1416507086) }
|
116
|
+
end
|
117
|
+
it "should be true" do
|
118
|
+
expect(subject).to be_persisted
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "#delete" do
|
124
|
+
let(:id) { "ark:/99999/fk4zzzzzzz" }
|
125
|
+
subject { described_class.new(id: id, status: "reserved") }
|
126
|
+
before do
|
127
|
+
allow_any_instance_of(Client).to receive(:delete_identifier).with(id) { double(id: id) }
|
128
|
+
end
|
129
|
+
it "should delete the identifier" do
|
130
|
+
expect(subject.client).to receive(:delete_identifier).with(id)
|
131
|
+
subject.delete
|
132
|
+
expect(subject).to be_deleted
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#save" do
|
137
|
+
let(:id) { "ark:/99999/fk4zzzzzzz" }
|
138
|
+
before do
|
139
|
+
allow(subject.client).to receive(:get_identifier_metadata).with(id) { double(metadata: {}) }
|
140
|
+
end
|
141
|
+
context "when the identifier is persisted" do
|
142
|
+
before do
|
143
|
+
allow_any_instance_of(Client).to receive(:modify_identifier).with(id, {}) { double(id: id) }
|
144
|
+
allow(subject).to receive(:id) { id }
|
145
|
+
allow(subject).to receive(:persisted?) { true }
|
146
|
+
end
|
147
|
+
it "should modify the identifier" do
|
148
|
+
expect(subject.client).to receive(:modify_identifier).with(id, {})
|
149
|
+
subject.save
|
150
|
+
end
|
151
|
+
end
|
152
|
+
context "when the identifier is not persisted" do
|
153
|
+
before do
|
154
|
+
allow(subject).to receive(:persisted?) { false }
|
155
|
+
end
|
156
|
+
context "and `id' is present" do
|
157
|
+
before do
|
158
|
+
allow(subject).to receive(:id) { id }
|
159
|
+
allow_any_instance_of(Client).to receive(:create_identifier).with(id, {}) { double(id: id) }
|
160
|
+
end
|
161
|
+
it "should create the identifier" do
|
162
|
+
expect(subject.client).to receive(:create_identifier).with(id, {})
|
163
|
+
subject.save
|
164
|
+
end
|
165
|
+
end
|
166
|
+
context "and `id' is not present" do
|
167
|
+
context "and `shoulder' is present" do
|
168
|
+
before do
|
169
|
+
allow(subject).to receive(:shoulder) { ARK_SHOULDER }
|
170
|
+
allow_any_instance_of(Client).to receive(:mint_identifier).with(ARK_SHOULDER, {}) { double(id: id) }
|
171
|
+
end
|
172
|
+
it "should mint the identifier" do
|
173
|
+
expect(subject.client).to receive(:mint_identifier).with(ARK_SHOULDER, {})
|
174
|
+
subject.save
|
175
|
+
end
|
176
|
+
end
|
177
|
+
context "and `shoulder' is not present" do
|
178
|
+
it "should raise an exception" do
|
179
|
+
expect { subject.save }.to raise_error
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should handle CRUD operations", type: :feature do
|
187
|
+
# create (mint)
|
188
|
+
identifier = Ezid::Identifier.create(shoulder: ARK_SHOULDER)
|
189
|
+
expect(identifier.status).to eq("public")
|
190
|
+
# update
|
191
|
+
identifier.target = "http://example.com"
|
192
|
+
identifier.save
|
193
|
+
# retrieve
|
194
|
+
identifier = Ezid::Identifier.find(identifier.id)
|
195
|
+
expect(identifier.target).to eq("http://example.com")
|
196
|
+
# delete
|
197
|
+
identifier = Ezid::Identifier.create(shoulder: ARK_SHOULDER, status: "reserved")
|
198
|
+
identifier.delete
|
199
|
+
expect { Ezid::Identifier.find(identifier.id) }.to raise_error
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
@@ -11,31 +11,37 @@ module Ezid
|
|
11
11
|
"_created" => "1416507086",
|
12
12
|
"_status" => "public" }
|
13
13
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
it "should return the target URL" do
|
22
|
-
expect(subject.target).to eq("http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87")
|
23
|
-
end
|
14
|
+
subject { described_class.new(elements) }
|
15
|
+
|
16
|
+
it "should have a non-leading-underscore reader for each reserved element, except '_crossref'" do
|
17
|
+
Metadata::RESERVED_ELEMENTS.each do |element|
|
18
|
+
next if element == "_crossref"
|
19
|
+
expect(subject).to respond_to(element.sub("_", ""))
|
20
|
+
end
|
24
21
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
|
23
|
+
describe "element reader aliases for datetime elements" do
|
24
|
+
it "should return Time values" do
|
25
|
+
expect(subject.created).to eq Time.parse("2014-11-20 13:11:26 -0500")
|
26
|
+
expect(subject.updated).to eq Time.parse("2014-11-20 13:11:26 -0500")
|
28
27
|
end
|
29
28
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
|
30
|
+
it "should have a non-leading-underscore writer for each writable reserved element, except '_crossref'" do
|
31
|
+
Metadata::RESERVED_READWRITE_ELEMENTS.each do |element|
|
32
|
+
next if element == "_crossref"
|
33
|
+
expect(subject).to respond_to("#{element.sub('_', '')}=")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#update" do
|
38
|
+
it "should coerce the data"
|
39
|
+
it "should update the delegated hash"
|
34
40
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
|
42
|
+
describe "#replace" do
|
43
|
+
it "should coerce the data"
|
44
|
+
it "should call `replace' on the delegated hash"
|
39
45
|
end
|
40
46
|
|
41
47
|
describe "ANVL output" do
|
@@ -52,11 +58,11 @@ _status: public")
|
|
52
58
|
end
|
53
59
|
describe "encoding" do
|
54
60
|
before do
|
55
|
-
subject.each_key { |k| subject[k] = subject[k].force_encoding(
|
61
|
+
subject.each_key { |k| subject[k] = subject[k].force_encoding(Encoding::US_ASCII) }
|
62
|
+
end
|
63
|
+
it "should be encoded in UTF-8" do
|
64
|
+
expect(subject.to_anvl.encoding).to eq(Encoding::UTF_8)
|
56
65
|
end
|
57
|
-
end
|
58
|
-
it "should be encoded in UTF-8" do
|
59
|
-
expect(subject.to_anvl.encoding).to eq(Encoding::UTF_8)
|
60
66
|
end
|
61
67
|
describe "escaping" do
|
62
68
|
before do
|
@@ -78,6 +84,12 @@ _status: public")
|
|
78
84
|
|
79
85
|
describe "coercion" do
|
80
86
|
subject { described_class.new(data) }
|
87
|
+
context "of nil" do
|
88
|
+
let(:data) { nil }
|
89
|
+
it "should return an empty hash" do
|
90
|
+
expect(subject).to eq({})
|
91
|
+
end
|
92
|
+
end
|
81
93
|
context "of a string" do
|
82
94
|
let(:data) do <<-EOS
|
83
95
|
_updated: 1416507086
|
@@ -91,14 +103,14 @@ _status: public
|
|
91
103
|
EOS
|
92
104
|
end
|
93
105
|
it "should coerce the data into a hash" do
|
94
|
-
expect(subject
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
106
|
+
expect(subject).to eq({"_updated" => "1416507086",
|
107
|
+
"_target" => "http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87",
|
108
|
+
"_profile" => "erc",
|
109
|
+
"_ownergroup" => "apitest",
|
110
|
+
"_owner" => "apitest",
|
111
|
+
"_export" => "yes",
|
112
|
+
"_created" => "1416507086",
|
113
|
+
"_status" => "public"})
|
102
114
|
end
|
103
115
|
end
|
104
116
|
context "of a hash" do
|
@@ -113,14 +125,161 @@ EOS
|
|
113
125
|
_status: "public" }
|
114
126
|
end
|
115
127
|
it "should stringify the keys" do
|
116
|
-
expect(subject
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
128
|
+
expect(subject).to eq({"_updated" => "1416507086",
|
129
|
+
"_target" => "http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87",
|
130
|
+
"_profile" => "erc",
|
131
|
+
"_ownergroup" => "apitest",
|
132
|
+
"_owner" => "apitest",
|
133
|
+
"_export" => "yes",
|
134
|
+
"_created" => "1416507086",
|
135
|
+
"_status" => "public"})
|
136
|
+
end
|
137
|
+
end
|
138
|
+
context "of a Metadata instance" do
|
139
|
+
let(:hsh) do
|
140
|
+
{ "_updated" => "1416507086",
|
141
|
+
"_target" => "http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87",
|
142
|
+
"_profile" => "erc",
|
143
|
+
"_ownergroup" => "apitest",
|
144
|
+
"_owner" => "apitest",
|
145
|
+
"_export" => "yes",
|
146
|
+
"_created" => "1416507086",
|
147
|
+
"_status" => "public" }
|
148
|
+
end
|
149
|
+
let(:data) { Metadata.new(hsh) }
|
150
|
+
it "should have the same hash" do
|
151
|
+
expect(subject).to eq(hsh)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "profiles" do
|
157
|
+
describe "dc" do
|
158
|
+
describe "readers" do
|
159
|
+
before do
|
160
|
+
subject.update("dc.title" => "Testing Profiles",
|
161
|
+
"dc.creator" => "Kermit the Frog",
|
162
|
+
"dc.publisher" => "Duke University",
|
163
|
+
"dc.date" => "2004",
|
164
|
+
"dc.type" => "Text")
|
165
|
+
end
|
166
|
+
it "should have a reader for each element" do
|
167
|
+
expect(subject.dc_title).to eq("Testing Profiles")
|
168
|
+
expect(subject.dc_creator).to eq("Kermit the Frog")
|
169
|
+
expect(subject.dc_publisher).to eq("Duke University")
|
170
|
+
expect(subject.dc_date).to eq("2004")
|
171
|
+
expect(subject.dc_type).to eq("Text")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
describe "writers" do
|
175
|
+
before do
|
176
|
+
subject.dc_title = "Run of the Mill"
|
177
|
+
subject.dc_creator = "Jack Bean"
|
178
|
+
subject.dc_publisher = "Random Housing"
|
179
|
+
subject.dc_date = "1967"
|
180
|
+
subject.dc_type = "Physical Object"
|
181
|
+
end
|
182
|
+
it "should have a writer for each element" do
|
183
|
+
expect(subject["dc.title"]).to eq "Run of the Mill"
|
184
|
+
expect(subject["dc.creator"]).to eq "Jack Bean"
|
185
|
+
expect(subject["dc.publisher"]).to eq "Random Housing"
|
186
|
+
expect(subject["dc.date"]).to eq "1967"
|
187
|
+
expect(subject["dc.type"]).to eq "Physical Object"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
describe "erc" do
|
192
|
+
describe "readers" do
|
193
|
+
before do
|
194
|
+
subject.update("erc.what" => "Testing Profiles",
|
195
|
+
"erc.who" => "Kermit the Frog",
|
196
|
+
"erc.when" => "2004")
|
197
|
+
end
|
198
|
+
it "should have a reader for each element" do
|
199
|
+
expect(subject.erc_what).to eq("Testing Profiles")
|
200
|
+
expect(subject.erc_who).to eq("Kermit the Frog")
|
201
|
+
expect(subject.erc_when).to eq("2004")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
describe "writers" do
|
205
|
+
before do
|
206
|
+
subject.erc_what = "Run of the Mill"
|
207
|
+
subject.erc_who = "Jack Bean"
|
208
|
+
subject.erc_when = "1967"
|
209
|
+
end
|
210
|
+
it "should have a writer for each element" do
|
211
|
+
expect(subject["erc.what"]).to eq "Run of the Mill"
|
212
|
+
expect(subject["erc.who"]).to eq "Jack Bean"
|
213
|
+
expect(subject["erc.when"]).to eq "1967"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
describe "crossref" do
|
218
|
+
describe "xml document reader" do
|
219
|
+
before do
|
220
|
+
subject["crossref"] = "<xml/>"
|
221
|
+
end
|
222
|
+
it "should return the xml" do
|
223
|
+
expect(subject.crossref).to eq "<xml/>"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
describe "xml document writer" do
|
227
|
+
before do
|
228
|
+
subject.crossref = "<yml/>"
|
229
|
+
end
|
230
|
+
it "should set the 'crossref' metadata element" do
|
231
|
+
expect(subject["crossref"]).to eq "<yml/>"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
describe "datacite" do
|
236
|
+
describe "element readers" do
|
237
|
+
before do
|
238
|
+
subject.update("datacite.title" => "Testing Profiles",
|
239
|
+
"datacite.creator" => "Kermit the Frog",
|
240
|
+
"datacite.publisher" => "Duke University",
|
241
|
+
"datacite.publicationyear" => "2004",
|
242
|
+
"datacite.resourcetype" => "Text")
|
243
|
+
end
|
244
|
+
it "should have a reader for each element" do
|
245
|
+
expect(subject.datacite_title).to eq("Testing Profiles")
|
246
|
+
expect(subject.datacite_creator).to eq("Kermit the Frog")
|
247
|
+
expect(subject.datacite_publisher).to eq("Duke University")
|
248
|
+
expect(subject.datacite_publicationyear).to eq("2004")
|
249
|
+
expect(subject.datacite_resourcetype).to eq("Text")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
describe "element writers" do
|
253
|
+
before do
|
254
|
+
subject.datacite_title = "Run of the Mill"
|
255
|
+
subject.datacite_creator = "Jack Bean"
|
256
|
+
subject.datacite_publisher = "Random Housing"
|
257
|
+
subject.datacite_publicationyear = "1967"
|
258
|
+
subject.datacite_resourcetype = "Physical Object"
|
259
|
+
end
|
260
|
+
it "should have a writer for each element" do
|
261
|
+
expect(subject["datacite.title"]).to eq "Run of the Mill"
|
262
|
+
expect(subject["datacite.creator"]).to eq "Jack Bean"
|
263
|
+
expect(subject["datacite.publisher"]).to eq "Random Housing"
|
264
|
+
expect(subject["datacite.publicationyear"]).to eq "1967"
|
265
|
+
expect(subject["datacite.resourcetype"]).to eq "Physical Object"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
describe "xml document reader" do
|
269
|
+
before do
|
270
|
+
subject["datacite"] = "<xml/>"
|
271
|
+
end
|
272
|
+
it "should return the xml" do
|
273
|
+
expect(subject.datacite).to eq "<xml/>"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
describe "xml document writer" do
|
277
|
+
before do
|
278
|
+
subject.datacite = "<yml/>"
|
279
|
+
end
|
280
|
+
it "should set the 'datacite' metadata element" do
|
281
|
+
expect(subject["datacite"]).to eq "<yml/>"
|
282
|
+
end
|
124
283
|
end
|
125
284
|
end
|
126
285
|
end
|