ezid-client 1.2.0 → 1.3.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 +1 -0
- data/README.md +18 -15
- data/VERSION +1 -1
- data/lib/ezid/batch_download.rb +131 -0
- data/lib/ezid/client.rb +3 -1
- data/lib/ezid/identifier.rb +5 -10
- data/lib/ezid/metadata.rb +15 -37
- data/lib/ezid/reserved_metadata.rb +26 -0
- data/lib/ezid/status.rb +10 -0
- data/spec/unit/identifier_spec.rb +12 -12
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 141f157b6169fdedba53b58d11e0b9c1b26c7662
|
4
|
+
data.tar.gz: ef0923245729d7f20e079f3581d7647bc7cb5581
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16b22cbb1ba43ab241036710cfb044f66b365dd434a783a31a1370743e063517a530df12b2f72c24c0f3575dabdb8045e49fa673fc01306621d46fe2a8a88683
|
7
|
+
data.tar.gz: f329532cc4d162575b0c3c14a76d8b7ca253963486686e73565ec4f31debd1c498f5804034f2713badac901b0875d7a27eba322a488bd6ef48f073b01246ce70
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -113,21 +113,24 @@ I, [2014-12-04T15:12:48.853964 #86734] INFO -- : EZID DeleteIdentifier -- succe
|
|
113
113
|
|
114
114
|
## Batch Download
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
>>
|
124
|
-
=>
|
125
|
-
>>
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
=> "http://ezid.cdlib.org/download/
|
130
|
-
|
116
|
+
See http://ezid.cdlib.org/doc/apidoc.html#parameters. Repeated values should be given as an array value for the parameter key.
|
117
|
+
|
118
|
+
```
|
119
|
+
>> batch = Ezid::BatchDownload.new(:csv)
|
120
|
+
=> #<Ezid::BatchDownload format=:csv>
|
121
|
+
>> batch.column = ["_id", "_target"]
|
122
|
+
=> ["_id", "_target"]
|
123
|
+
>> batch.createdAfter = Date.today.to_time
|
124
|
+
=> 2016-02-24 00:00:00 -0500
|
125
|
+
>> batch
|
126
|
+
=> #<Ezid::BatchDownload column=["_id", "_target"] createdAfter=1456290000 format=:csv>
|
127
|
+
>> batch.download_url
|
128
|
+
I, [2016-02-24T18:03:40.828005 #1084] INFO -- : EZID BatchDownload -- success: http://ezid.cdlib.org/download/4a63401e17.csv.gz
|
129
|
+
=> "http://ezid.cdlib.org/download/4a63401e17.csv.gz"
|
130
|
+
>> batch.download_file
|
131
|
+
File successfully download to /current/working/directory/4a63401e17.csv.gz.
|
132
|
+
=> nil
|
133
|
+
```
|
131
134
|
|
132
135
|
## Metadata handling
|
133
136
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "hashie"
|
2
|
+
require "net/http"
|
3
|
+
require "uri"
|
4
|
+
require_relative "reserved_metadata"
|
5
|
+
|
6
|
+
module Ezid
|
7
|
+
class BatchDownloadError < Error; end
|
8
|
+
|
9
|
+
class BatchDownload < Hashie::Dash
|
10
|
+
include Hashie::Extensions::Coercion
|
11
|
+
|
12
|
+
ANVL = "anvl".freeze
|
13
|
+
CSV = "csv".freeze
|
14
|
+
XML = "xml".freeze
|
15
|
+
FORMATS = [ ANVL, CSV, XML ].freeze
|
16
|
+
|
17
|
+
YES = "yes".freeze
|
18
|
+
NO = "no".freeze
|
19
|
+
BOOLEANS = [ YES, NO ].freeze
|
20
|
+
|
21
|
+
TEST = "test".freeze
|
22
|
+
REAL = "real".freeze
|
23
|
+
PERMANENCE = [ TEST, REAL ].freeze
|
24
|
+
|
25
|
+
ARK = "ark".freeze
|
26
|
+
DOI = "doi".freeze
|
27
|
+
URN = "urn".freeze
|
28
|
+
TYPES = [ ARK, DOI, URN, ].freeze
|
29
|
+
|
30
|
+
# CSV Columns
|
31
|
+
ID = "_id".freeze
|
32
|
+
MAPPED_CREATOR = "_mappedCreator".freeze
|
33
|
+
MAPPED_TITLE = "_mappedTitle".freeze
|
34
|
+
MAPPED_PUBLISHER = "_mappedPublisher".freeze
|
35
|
+
MAPPED_DATE = "_mappedDate".freeze
|
36
|
+
MAPPED_TYPE = "_mappedType".freeze
|
37
|
+
|
38
|
+
MAX_DOWNLOAD_TRIES = 300
|
39
|
+
DOWNLOAD_RETRY_INTERVAL = 1
|
40
|
+
|
41
|
+
# Parameters
|
42
|
+
property :format, required: true # {anvl|csv|xml}
|
43
|
+
property :column # repeatable
|
44
|
+
property :notify # repeatable
|
45
|
+
property :convertTimestamps # {yes|no}
|
46
|
+
|
47
|
+
# Search constraints
|
48
|
+
property :createdAfter
|
49
|
+
property :createdBefore
|
50
|
+
property :crossref # {yes|no}
|
51
|
+
property :exported # {yes|no}
|
52
|
+
property :owner # repeatable
|
53
|
+
property :ownergroup # repeatable
|
54
|
+
property :permanence # {test|real}
|
55
|
+
property :profile # (repeatable)
|
56
|
+
property :status # {reserved|public|unavailable} (repeatable)
|
57
|
+
property :type # {ark|doi|urn} (repeatable)
|
58
|
+
property :updatedAfter
|
59
|
+
property :updatedBefore
|
60
|
+
|
61
|
+
coerce_value FalseClass, ->(v) { NO }
|
62
|
+
coerce_value TrueClass, ->(v) { YES }
|
63
|
+
coerce_value DateTime, ->(v) { v.to_time.utc.iso8601 }
|
64
|
+
coerce_value Time, Integer
|
65
|
+
|
66
|
+
def initialize(format, args={})
|
67
|
+
super(args.merge(format: format))
|
68
|
+
end
|
69
|
+
|
70
|
+
def params
|
71
|
+
to_h
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_response
|
75
|
+
@response ||= client.batch_download(params)
|
76
|
+
end
|
77
|
+
|
78
|
+
def reload
|
79
|
+
@response = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def download_url
|
83
|
+
get_response.download_url
|
84
|
+
end
|
85
|
+
|
86
|
+
def download_file(path: nil)
|
87
|
+
path ||= Dir.getwd
|
88
|
+
fullpath = File.directory?(path) ? File.join(path, download_filename) : path
|
89
|
+
tries = 0
|
90
|
+
begin
|
91
|
+
tries += 1
|
92
|
+
download = Net::HTTP.get_response(download_uri)
|
93
|
+
download.value
|
94
|
+
rescue Net::HTTPServerException => e
|
95
|
+
if download.is_a?(Net::HTTPNotFound)
|
96
|
+
if tries < MAX_DOWNLOAD_TRIES
|
97
|
+
print "Download file not yet available (attempt #{tries} of #{MAX_DOWNLOAD_TRIES})."
|
98
|
+
puts " Trying again in #{DOWNLOAD_RETRY_INTERVAL} second(s) ..."
|
99
|
+
sleep DOWNLOAD_RETRY_INTERVAL
|
100
|
+
retry
|
101
|
+
else
|
102
|
+
raise BatchDownloadError,
|
103
|
+
"Maximum download attempts (#{MAX_DOWNLOAD_TRIES}) reached unsuccessfully."
|
104
|
+
end
|
105
|
+
else
|
106
|
+
raise
|
107
|
+
end
|
108
|
+
else
|
109
|
+
File.open(fullpath, "wb") do |f|
|
110
|
+
f.write(download.body)
|
111
|
+
end
|
112
|
+
puts "File successfully download to #{fullpath}."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def download_uri
|
119
|
+
URI(download_url)
|
120
|
+
end
|
121
|
+
|
122
|
+
def download_filename
|
123
|
+
File.basename(download_uri.path)
|
124
|
+
end
|
125
|
+
|
126
|
+
def client
|
127
|
+
Client.new
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/lib/ezid/client.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require "net/http"
|
2
2
|
|
3
|
+
require_relative "error"
|
4
|
+
require_relative "status"
|
3
5
|
require_relative "configuration"
|
4
6
|
require_relative "session"
|
5
7
|
require_relative "metadata"
|
6
8
|
require_relative "identifier"
|
7
9
|
require_relative "proxy_identifier"
|
8
|
-
require_relative "
|
10
|
+
require_relative "batch_download"
|
9
11
|
|
10
12
|
Dir[File.expand_path("../responses/*.rb", __FILE__)].each { |m| require m }
|
11
13
|
Dir[File.expand_path("../requests/*.rb", __FILE__)].each { |m| require m }
|
data/lib/ezid/identifier.rb
CHANGED
@@ -14,11 +14,6 @@ module Ezid
|
|
14
14
|
# Attributes to display on inspect
|
15
15
|
INSPECT_ATTRS = %w( id status target created ).freeze
|
16
16
|
|
17
|
-
# EZID status terms
|
18
|
-
PUBLIC = "public".freeze
|
19
|
-
RESERVED = "reserved".freeze
|
20
|
-
UNAVAILABLE = "unavailable".freeze
|
21
|
-
|
22
17
|
class << self
|
23
18
|
attr_accessor :defaults
|
24
19
|
|
@@ -151,19 +146,19 @@ module Ezid
|
|
151
146
|
# Is the identifier reserved?
|
152
147
|
# @return [Boolean]
|
153
148
|
def reserved?
|
154
|
-
status == RESERVED
|
149
|
+
status == Status::RESERVED
|
155
150
|
end
|
156
151
|
|
157
152
|
# Is the identifier public?
|
158
153
|
# @return [Boolean]
|
159
154
|
def public?
|
160
|
-
status == PUBLIC
|
155
|
+
status == Status::PUBLIC
|
161
156
|
end
|
162
157
|
|
163
158
|
# Is the identifier unavailable?
|
164
159
|
# @return [Boolean]
|
165
160
|
def unavailable?
|
166
|
-
status
|
161
|
+
status.to_s.start_with? Status::UNAVAILABLE
|
167
162
|
end
|
168
163
|
|
169
164
|
# Is the identifier deletable?
|
@@ -182,7 +177,7 @@ module Ezid
|
|
182
177
|
if unavailable? and reason.nil?
|
183
178
|
return
|
184
179
|
end
|
185
|
-
value = UNAVAILABLE
|
180
|
+
value = Status::UNAVAILABLE
|
186
181
|
if reason
|
187
182
|
value += " | #{reason}"
|
188
183
|
end
|
@@ -192,7 +187,7 @@ module Ezid
|
|
192
187
|
# Mark the identifier as public
|
193
188
|
# @return [String] the new status
|
194
189
|
def public!
|
195
|
-
self.status = PUBLIC
|
190
|
+
self.status = Status::PUBLIC
|
196
191
|
end
|
197
192
|
|
198
193
|
protected
|
data/lib/ezid/metadata.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "hashie"
|
2
|
+
require_relative "reserved_metadata"
|
2
3
|
|
3
4
|
module Ezid
|
4
5
|
#
|
@@ -7,6 +8,7 @@ module Ezid
|
|
7
8
|
# @api private
|
8
9
|
#
|
9
10
|
class Metadata < Hashie::Mash
|
11
|
+
include ReservedMetadata
|
10
12
|
|
11
13
|
# EZID metadata field/value separator
|
12
14
|
ANVL_SEPARATOR = ": "
|
@@ -27,16 +29,11 @@ module Ezid
|
|
27
29
|
LINE_CONTINUATION_RE = /\r?\n\s+/
|
28
30
|
# A line ending
|
29
31
|
LINE_ENDING_RE = /\r?\n/
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# @note crossref is not included because it is a simple element
|
36
|
-
PROFILES = %w( dc datacite erc ).freeze
|
37
|
-
RESERVED_ALIASES = [ :coowners=, :export=, :profile=, :status=, :target=,
|
38
|
-
:coowners, :export, :profile, :status, :target,
|
39
|
-
:datacenter, :owner, :ownergroup, :shadowedby, :shadows ]
|
32
|
+
# @api private
|
33
|
+
RESERVED_ALIASES = %w(
|
34
|
+
coowners datacenter export owner ownergroup
|
35
|
+
profile shadowedby shadows status target
|
36
|
+
).freeze
|
40
37
|
|
41
38
|
def initialize(data={})
|
42
39
|
super coerce(data)
|
@@ -75,39 +72,20 @@ module Ezid
|
|
75
72
|
|
76
73
|
protected
|
77
74
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
# Overrides Hashie::Mash
|
76
|
+
def convert_key(key)
|
77
|
+
k = super
|
78
|
+
if RESERVED_ALIASES.include?(k)
|
79
|
+
"_#{k}"
|
80
|
+
elsif k =~ /\A(dc|datacite|erc)_/
|
81
|
+
k.sub(/_/, ".")
|
83
82
|
else
|
84
|
-
|
83
|
+
k
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
88
87
|
private
|
89
88
|
|
90
|
-
def reserved_alias?(name)
|
91
|
-
RESERVED_ALIASES.include?(name)
|
92
|
-
end
|
93
|
-
|
94
|
-
def reserved_alias(name, *args)
|
95
|
-
send("_#{name}", *args)
|
96
|
-
end
|
97
|
-
|
98
|
-
def profile_accessor?(name)
|
99
|
-
PROFILES.include? name.to_s.split("_").first
|
100
|
-
end
|
101
|
-
|
102
|
-
def profile_accessor(name, *args)
|
103
|
-
key = name.to_s.sub("_", ".")
|
104
|
-
if key.end_with?("=")
|
105
|
-
self[key[0..-2]] = args.first
|
106
|
-
else
|
107
|
-
self[key]
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
89
|
def to_time(value)
|
112
90
|
time = value.to_i
|
113
91
|
(time == 0) ? nil : Time.at(time).utc
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ezid
|
2
|
+
#
|
3
|
+
# EZID reserved metadata elements
|
4
|
+
#
|
5
|
+
# @see http://ezid.cdlib.org/doc/apidoc.html#internal-metadata
|
6
|
+
#
|
7
|
+
module ReservedMetadata
|
8
|
+
COOWNERS = "_coowners".freeze
|
9
|
+
CREATED = "_created".freeze
|
10
|
+
DATACENTER = "_datacenter".freeze
|
11
|
+
EXPORT = "_export".freeze
|
12
|
+
OWNER = "_owner".freeze
|
13
|
+
OWNERGROUP = "_ownergroup".freeze
|
14
|
+
PROFILE = "_profile".freeze
|
15
|
+
SHADOWEDBY = "_shadowedby".freeze
|
16
|
+
SHADOWS = "_shadows".freeze
|
17
|
+
STATUS = "_status".freeze
|
18
|
+
TARGET = "_target".freeze
|
19
|
+
UPDATED = "_updated".freeze
|
20
|
+
|
21
|
+
# Read-only elements
|
22
|
+
READONLY = [
|
23
|
+
CREATED, DATACENTER, OWNER, OWNERGROUP, SHADOWEDBY, SHADOWS, UPDATED
|
24
|
+
].freeze
|
25
|
+
end
|
26
|
+
end
|
data/lib/ezid/status.rb
ADDED
@@ -119,7 +119,7 @@ module Ezid
|
|
119
119
|
|
120
120
|
describe "#delete" do
|
121
121
|
context "when the identifier is reserved" do
|
122
|
-
subject { described_class.new(id: "id", status:
|
122
|
+
subject { described_class.new(id: "id", status: Status::RESERVED) }
|
123
123
|
context "and is persisted" do
|
124
124
|
before { allow(subject).to receive(:persisted?) { true } }
|
125
125
|
it "deletes the identifier" do
|
@@ -136,7 +136,7 @@ module Ezid
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
context "when identifier is not reserved" do
|
139
|
-
subject { described_class.new(id: "id", status:
|
139
|
+
subject { described_class.new(id: "id", status: Status::PUBLIC) }
|
140
140
|
it "raises an exception" do
|
141
141
|
expect { subject.delete }.to raise_error(Error)
|
142
142
|
end
|
@@ -193,7 +193,7 @@ module Ezid
|
|
193
193
|
it { is_expected.not_to be_unavailable }
|
194
194
|
end
|
195
195
|
context "when the identifier is reserved" do
|
196
|
-
before { subject.status =
|
196
|
+
before { subject.status = Status::RESERVED }
|
197
197
|
it { is_expected.not_to be_public }
|
198
198
|
it { is_expected.to be_reserved }
|
199
199
|
it { is_expected.not_to be_unavailable }
|
@@ -218,7 +218,7 @@ module Ezid
|
|
218
218
|
subject { described_class.new(id: "id", status: status) }
|
219
219
|
describe "#unavailable!" do
|
220
220
|
context "when the status is \"unavailable\"" do
|
221
|
-
let(:status) { "#{
|
221
|
+
let(:status) { "#{Status::UNAVAILABLE} | whatever" }
|
222
222
|
context "and no reason is given" do
|
223
223
|
it "logs a warning" do
|
224
224
|
pending "https://github.com/duke-libraries/ezid-client/issues/46"
|
@@ -238,12 +238,12 @@ module Ezid
|
|
238
238
|
subject.unavailable!("because")
|
239
239
|
end
|
240
240
|
it "should change the status" do
|
241
|
-
expect { subject.unavailable!("because") }.to change(subject, :status).from(status).to("#{
|
241
|
+
expect { subject.unavailable!("because") }.to change(subject, :status).from(status).to("#{Status::UNAVAILABLE} | because")
|
242
242
|
end
|
243
243
|
end
|
244
244
|
end
|
245
245
|
context "when the status is \"reserved\"" do
|
246
|
-
let(:status) {
|
246
|
+
let(:status) { Status::RESERVED }
|
247
247
|
context "and persisted" do
|
248
248
|
before { allow(subject).to receive(:persisted?) { true } }
|
249
249
|
it "raises an exception" do
|
@@ -253,28 +253,28 @@ module Ezid
|
|
253
253
|
context "and not persisted" do
|
254
254
|
before { allow(subject).to receive(:persisted?) { false } }
|
255
255
|
it "changes the status" do
|
256
|
-
expect { subject.unavailable! }.to change(subject, :status).from(
|
256
|
+
expect { subject.unavailable! }.to change(subject, :status).from(Status::RESERVED).to(Status::UNAVAILABLE)
|
257
257
|
end
|
258
258
|
end
|
259
259
|
end
|
260
260
|
context "when the status is \"public\"" do
|
261
|
-
let(:status) {
|
261
|
+
let(:status) { Status::PUBLIC }
|
262
262
|
context "and no reason is given" do
|
263
263
|
it "changes the status" do
|
264
|
-
expect { subject.unavailable! }.to change(subject, :status).from(
|
264
|
+
expect { subject.unavailable! }.to change(subject, :status).from(Status::PUBLIC).to(Status::UNAVAILABLE)
|
265
265
|
end
|
266
266
|
end
|
267
267
|
context "and a reason is given" do
|
268
268
|
it "changes the status and appends the reason" do
|
269
|
-
expect { subject.unavailable!("withdrawn") }.to change(subject, :status).from(
|
269
|
+
expect { subject.unavailable!("withdrawn") }.to change(subject, :status).from(Status::PUBLIC).to("#{Status::UNAVAILABLE} | withdrawn")
|
270
270
|
end
|
271
271
|
end
|
272
272
|
end
|
273
273
|
end
|
274
274
|
describe "#public!" do
|
275
|
-
subject { described_class.new(id: "id", status:
|
275
|
+
subject { described_class.new(id: "id", status: Status::UNAVAILABLE) }
|
276
276
|
it "changes the status" do
|
277
|
-
expect { subject.public! }.to change(subject, :status).from(
|
277
|
+
expect { subject.public! }.to change(subject, :status).from(Status::UNAVAILABLE).to(Status::PUBLIC)
|
278
278
|
end
|
279
279
|
end
|
280
280
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ezid-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Chandek-Stark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hashie
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- VERSION
|
84
84
|
- ezid-client.gemspec
|
85
85
|
- lib/ezid-client.rb
|
86
|
+
- lib/ezid/batch_download.rb
|
86
87
|
- lib/ezid/client.rb
|
87
88
|
- lib/ezid/configuration.rb
|
88
89
|
- lib/ezid/error.rb
|
@@ -101,6 +102,7 @@ files:
|
|
101
102
|
- lib/ezid/requests/modify_identifier_request.rb
|
102
103
|
- lib/ezid/requests/request.rb
|
103
104
|
- lib/ezid/requests/server_status_request.rb
|
105
|
+
- lib/ezid/reserved_metadata.rb
|
104
106
|
- lib/ezid/responses/batch_download_response.rb
|
105
107
|
- lib/ezid/responses/create_identifier_response.rb
|
106
108
|
- lib/ezid/responses/delete_identifier_response.rb
|
@@ -114,6 +116,7 @@ files:
|
|
114
116
|
- lib/ezid/responses/response.rb
|
115
117
|
- lib/ezid/responses/server_status_response.rb
|
116
118
|
- lib/ezid/session.rb
|
119
|
+
- lib/ezid/status.rb
|
117
120
|
- lib/ezid/test_helper.rb
|
118
121
|
- spec/integration/client_spec.rb
|
119
122
|
- spec/integration/identifier_spec.rb
|
@@ -143,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
146
|
version: '0'
|
144
147
|
requirements: []
|
145
148
|
rubyforge_project:
|
146
|
-
rubygems_version: 2.4.
|
149
|
+
rubygems_version: 2.4.3
|
147
150
|
signing_key:
|
148
151
|
specification_version: 4
|
149
152
|
summary: Ruby client for EZID API Version 2
|