ezid-client 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fbe64c4c33da9f4e797d596ea878a03b79757e23
4
- data.tar.gz: 899e1b115a2d2be8c7958c6143c97e3d308df923
3
+ metadata.gz: f0b9d3dc7c1df7191bb249e09e448ef6a5f55957
4
+ data.tar.gz: 18456cea4f31aa57fe04e7aa8f2c60c9d05eb1a6
5
5
  SHA512:
6
- metadata.gz: 5c720cbfa1e104e7b371b7374e9714a0a4b4a1a7e264d58ef49e85f74ce41c4b4e25a5c987965ffc0b6b6179adbbeffec5dc4b4124471ed243264d16ff2f85ca
7
- data.tar.gz: b23da5c4d41d643e541683f799c830f979d68a212229ebdd3930bb9f6a65733a2ceecbcfaf28dd5c83d49bfe2b7eba086408dd0d5c17ac4cc41867556cbef7ef
6
+ metadata.gz: a97573030689afb7beef02c8cc3d38a3b0efd830537ff780f51a20211a3cb1dd19cf78a06d17aa87bcf59664d3062bdbfc40f039f3ce6b4c85874887e898b944
7
+ data.tar.gz: 49ead39cafe8695052078fa86c92f17f21679240cc83d78f010bf9a95c88126024fb380b3ec1b37584723a67938e663559900357988db15a5248526846f65c12
@@ -4,7 +4,7 @@ rvm:
4
4
  - 2.0.0
5
5
  cache:
6
6
  - bundler
7
- env: SPEC_OPTS="--tag ~type:feature"
7
+ env: SPEC_OPTS="--tag ~type:integration"
8
8
  notifications:
9
9
  email:
10
10
  - lib-drs@duke.edu
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in ezid-client.gemspec
4
3
  gemspec
4
+
5
+ gem 'coveralls', require: false
data/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  EZID API Version 2 bindings. See http://ezid.cdlib.org/doc/apidoc.html.
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/ezid-client.svg)](http://badge.fury.io/rb/ezid-client)
6
+ [![Build Status](https://travis-ci.org/duke-libraries/ezid-client.svg?branch=master)](https://travis-ci.org/duke-libraries/ezid-client)
7
+
5
8
  ## Installation
6
9
 
7
10
  Add this line to your application's Gemfile:
@@ -16,7 +19,9 @@ Or install it yourself as:
16
19
 
17
20
  $ gem install ezid-client
18
21
 
19
- ## Resource-oriented Usage (CRUD)
22
+ ## Basic Resource-oriented Usage (CRUD)
23
+
24
+ See the test suite for more examples.
20
25
 
21
26
  Create (Mint)
22
27
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.4.1
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "ezid-client"
7
7
  spec.version = File.read(File.expand_path("../VERSION", __FILE__)).chomp
8
- spec.authors = ["dchandekstark"]
8
+ spec.authors = ["David Chandek-Stark"]
9
9
  spec.email = ["dchandekstark@gmail.com"]
10
10
  spec.summary = "Ruby client for EZID API Version 2"
11
11
  spec.description = "Ruby client for EZID API Version 2 (http://ezid.cdlib.org/doc/apidoc.html)"
@@ -1,16 +1,25 @@
1
1
  module Ezid
2
+ #
3
+ # Represents an EZID identifier as a resource.
4
+ #
5
+ # @api public
6
+ #
2
7
  class Identifier
3
8
 
4
9
  attr_reader :id, :client
5
10
  attr_accessor :shoulder, :metadata
6
11
 
12
+ # Attributes to display on inspect
7
13
  INSPECT_ATTRS = %w( id status target created )
8
14
 
15
+ # EZID status terms
9
16
  PUBLIC = "public"
10
17
  RESERVED = "reserved"
11
18
  UNAVAILABLE = "unavailable"
12
19
 
13
20
  class << self
21
+ # Creates or mints an identifier (depending on arguments)
22
+ # @see #save
14
23
  # @return [Ezid::Identifier] the new identifier
15
24
  # @raise [Ezid::Error]
16
25
  def create(attrs = {})
@@ -18,6 +27,7 @@ module Ezid
18
27
  identifier.save
19
28
  end
20
29
 
30
+ # Retrieves an identifier
21
31
  # @return [Ezid::Identifier] the identifier
22
32
  # @raise [Ezid::Error] if the identifier does not exist in EZID
23
33
  def find(id)
@@ -31,7 +41,7 @@ module Ezid
31
41
  @id = args.delete(:id)
32
42
  @shoulder = args.delete(:shoulder)
33
43
  @metadata = Metadata.new(args.delete(:metadata))
34
- update_attributes(args)
44
+ update_metadata(args)
35
45
  @deleted = false
36
46
  end
37
47
 
@@ -65,8 +75,12 @@ module Ezid
65
75
  reload
66
76
  end
67
77
 
68
- def update_attributes(attrs={})
78
+ # Updates the metadata
79
+ # @param attrs [Hash] the metadata
80
+ # @return [Ezid::Identifier] the identifier
81
+ def update_metadata(attrs={})
69
82
  attrs.each { |k, v| send("#{k}=", v) }
83
+ self
70
84
  end
71
85
 
72
86
  # Is the identifier persisted?
@@ -82,13 +96,16 @@ module Ezid
82
96
  @deleted
83
97
  end
84
98
 
99
+ # Updates the metadata and saves the identifier
100
+ # @param data [Hash] a hash of metadata
85
101
  # @return [Ezid::Identifier] the identifier
86
102
  # @raise [Ezid::Error]
87
- def update(data)
88
- metadata.update(data)
103
+ def update(data={})
104
+ update_metadata(data)
89
105
  save
90
106
  end
91
107
 
108
+ # Reloads the metadata from EZID (local changes will be lost!)
92
109
  # @return [Ezid::Identifier] the identifier
93
110
  # @raise [Ezid::Error]
94
111
  def reload
@@ -96,13 +113,14 @@ module Ezid
96
113
  self
97
114
  end
98
115
 
99
- # Empties the (local) metadata
116
+ # Empties the (local) metadata (changes will be lost!)
100
117
  # @return [Ezid::Identifier] the identifier
101
118
  def reset
102
119
  clear_metadata
103
120
  self
104
121
  end
105
122
 
123
+ # Deletes the identifier from EZID
106
124
  # @return [Ezid::Identifier] the identifier
107
125
  # @raise [Ezid::Error]
108
126
  def delete
@@ -112,28 +130,36 @@ module Ezid
112
130
  reset
113
131
  end
114
132
 
115
- def method_missing(name, *args)
116
- return metadata.send(name, *args) if metadata.respond_to?(name)
117
- super
118
- end
119
-
133
+ # Is the identifier reserved?
134
+ # @return [Boolean]
120
135
  def reserved?
121
136
  status == RESERVED
122
137
  end
123
138
 
139
+ # Is the identifier public?
140
+ # @return [Boolean]
124
141
  def public?
125
142
  status == PUBLIC
126
143
  end
127
144
 
145
+ # Is the identifier unavailable?
146
+ # @return [Boolean]
128
147
  def unavailable?
129
148
  status == UNAVAILABLE
130
149
  end
131
150
 
151
+ protected
152
+
153
+ def method_missing(name, *args)
154
+ return metadata.send(name, *args) if metadata.respond_to?(name)
155
+ super
156
+ end
157
+
132
158
  private
133
159
 
134
160
  def refresh_metadata
135
161
  response = client.get_identifier_metadata(id)
136
- @metadata.replace(response.metadata)
162
+ @metadata = Metadata.new(response.metadata)
137
163
  end
138
164
 
139
165
  def clear_metadata
@@ -54,36 +54,13 @@ module Ezid
54
54
  to_anvl
55
55
  end
56
56
 
57
- # Adds metadata to the collection
58
- # @param data [String, Hash, Ezid::Metadata] the data to add
59
- # @return [Ezid::Metadata] the updated metadata
60
- def update(data)
61
- __getobj__.update(coerce(data))
62
- self
63
- end
64
-
65
- # Replaces the collection with new metadata
66
- # @param data [String, Hash, Ezid::Metadata] the metadata replacing the current metadata
67
- # @return [Ezid::Metadata] the replaced metadata
68
- def replace(data)
69
- __getobj__.replace(coerce(data))
70
- self
71
- end
72
-
73
57
  private
74
58
 
75
59
  # Coerce data into a Hash of elements
76
60
  def coerce(data)
77
- begin
78
- stringify_keys(data.to_h)
79
- rescue NoMethodError
80
- coerce_string(data)
81
- end
82
- end
83
-
84
- # Coerce hash keys to strings
85
- def stringify_keys(hsh)
86
- hsh.each_with_object({}) { |(k, v), memo| memo[k.to_s] = v }
61
+ data.to_h
62
+ rescue NoMethodError
63
+ coerce_string(data)
87
64
  end
88
65
 
89
66
  # Escape elements hash keys and values
@@ -1,43 +1,85 @@
1
1
  require "active_support"
2
2
 
3
3
  module Ezid
4
+ #
5
+ # EZID metadata elements
6
+ #
7
+ # @note Intended to be used only as included in Ezid::Metadata.
8
+ #
4
9
  module MetadataElements
5
10
  extend ActiveSupport::Concern
6
11
 
7
- DC_ELEMENTS = %w( creator title publisher date type )
8
- DATACITE_ELEMENTS = %w( creator title publisher publicationyear resourcetype )
9
- ERC_ELEMENTS = %w( who what when )
12
+ # Metadata profiles
13
+ PROFILES = {
14
+ "dc" => %w( creator title publisher date type ).freeze,
15
+ "datacite" => %w( creator title publisher publicationyear resourcetype ).freeze,
16
+ "erc" => %w( who what when ).freeze,
17
+ "crossref" => [].freeze
18
+ }
10
19
 
20
+ # Elements for metadata profiles (values may include multiple elements)
21
+ PROFILE_ELEMENTS = PROFILES.keys - ["dc"]
22
+
23
+ # EZID reserved metadata elements that have time values
24
+ # @see http://ezid.cdlib.org/doc/apidoc.html#internal-metadata
11
25
  RESERVED_TIME_ELEMENTS = %w( _created _updated )
26
+
27
+ # EZID reserved metadata elements that are read-only
28
+ # @see http://ezid.cdlib.org/doc/apidoc.html#internal-metadata
12
29
  RESERVED_READONLY_ELEMENTS = %w( _owner _ownergroup _shadows _shadowedby _datacenter _created _updated )
30
+
31
+ # EZID reserved metadata elements that may be set by clients
32
+ # @see http://ezid.cdlib.org/doc/apidoc.html#internal-metadata
13
33
  RESERVED_READWRITE_ELEMENTS = %w( _coowners _target _profile _status _export _crossref )
34
+
35
+ # All EZID reserved metadata elements
36
+ # @see http://ezid.cdlib.org/doc/apidoc.html#internal-metadata
14
37
  RESERVED_ELEMENTS = RESERVED_READONLY_ELEMENTS + RESERVED_READWRITE_ELEMENTS + RESERVED_TIME_ELEMENTS
15
38
 
16
39
  included do
17
- reserved_accessor *(RESERVED_READWRITE_ELEMENTS - ["_crossref"])
18
- reserved_reader *(RESERVED_READONLY_ELEMENTS - RESERVED_TIME_ELEMENTS)
19
- reserved_time_reader *RESERVED_TIME_ELEMENTS
40
+ # "_crossref" does not get a reserved accessor because of the "crossref" element.
41
+ reserved_element_accessor *(RESERVED_READWRITE_ELEMENTS - ["_crossref"])
42
+ reserved_element_reader *(RESERVED_READONLY_ELEMENTS - RESERVED_TIME_ELEMENTS)
43
+ reserved_element_time_reader *RESERVED_TIME_ELEMENTS
20
44
 
21
- profile_accessor :dc, *DC_ELEMENTS
22
- profile_accessor :datacite, *DATACITE_ELEMENTS
23
- profile_accessor :erc, *ERC_ELEMENTS
45
+ profile_element_accessors
24
46
 
25
- element_accessor "datacite", "crossref", "_crossref"
47
+ element_accessor *PROFILE_ELEMENTS
48
+
49
+ # "_crossref" does not get a reserved accessor because of the "crossref" element.
50
+ element_accessor "_crossref"
26
51
  end
27
52
 
28
53
  module ClassMethods
29
- def reserved_accessor(*elements)
30
- reserved_reader(*elements)
31
- reserved_writer(*elements)
54
+ # Creates an accessor for each reserved element
55
+ # @see .reserved_element_reader
56
+ # @see .reserved_element_writer
57
+ # @param elements [Array<String>] a list of elements
58
+ def reserved_element_accessor(*elements)
59
+ reserved_element_reader(*elements)
60
+ reserved_element_writer(*elements)
32
61
  end
33
62
 
34
- def reserved_reader(*elements)
63
+ # Creates a reader for each reserved element
64
+ # The leading underscore of the element is removed from the reader name
65
+ # -- e.g.
66
+ #
67
+ # def status
68
+ # reader("_status")
69
+ # end
70
+ #
71
+ # @param elements [Array<String>] a list of elements
72
+ def reserved_element_reader(*elements)
35
73
  elements.each do |element|
36
74
  define_method(element.sub("_", "")) { reader(element) }
37
75
  end
38
76
  end
39
77
 
40
- def reserved_time_reader(*elements)
78
+ # Creates a reader for each time-based reserved element
79
+ # The reader will return a Time instance for the value (or nil if not present)
80
+ # @see .reserved_element_reader
81
+ # @param elements [Array<String>] a list of elements
82
+ def reserved_element_time_reader(*elements)
41
83
  elements.each do |element|
42
84
  define_method(element.sub("_", "")) do
43
85
  time = reader(element).to_i
@@ -47,7 +89,16 @@ module Ezid
47
89
  end
48
90
  end
49
91
 
50
- def reserved_writer(*elements)
92
+ # Creates a writer for each reserved element
93
+ # The leading underscore of the element is removed from the reader name
94
+ # -- e.g.
95
+ #
96
+ # def status=(value)
97
+ # writer("_status", value)
98
+ # end
99
+ #
100
+ # @param elements [Array<String>] a list of elements
101
+ def reserved_element_writer(*elements)
51
102
  elements.each do |element|
52
103
  define_method("#{element.sub('_', '')}=") do |value|
53
104
  writer(element, value)
@@ -55,18 +106,23 @@ module Ezid
55
106
  end
56
107
  end
57
108
 
58
- def profile_accessor(profile, *elements)
59
- elements.each do |element|
60
- define_method("#{profile}_#{element}") do
61
- reader("#{profile}.#{element}")
62
- end
109
+ # Creates a accessors for all metadata profile elements
110
+ def profile_element_accessors
111
+ PROFILES.each do |profile, elements|
112
+ elements.each do |element|
113
+ define_method("#{profile}_#{element}") do
114
+ reader("#{profile}.#{element}")
115
+ end
63
116
 
64
- define_method("#{profile}_#{element}=") do |value|
65
- writer("#{profile}.#{element}", value)
66
- end
117
+ define_method("#{profile}_#{element}=") do |value|
118
+ writer("#{profile}.#{element}", value)
119
+ end
120
+ end
67
121
  end
68
122
  end
69
123
 
124
+ # Creates an accessor for each element
125
+ # @param elements [Array<String>] a list of elements
70
126
  def element_accessor(*elements)
71
127
  elements.each do |element|
72
128
  define_method(element) { reader(element) }
@@ -0,0 +1,66 @@
1
+ module Ezid
2
+ RSpec.describe Client, type: :integration do
3
+
4
+ shared_examples "an EZID client" do |client|
5
+ it "should mint and modify" do
6
+ minted = client.mint_identifier(ARK_SHOULDER, "_status: reserved")
7
+ expect(minted).to be_success
8
+ @id = minted.id
9
+ modified = client.modify_identifier(@id, "dc.title" => "Test")
10
+ expect(modified).to be_success
11
+ retrieved = client.get_identifier_metadata(@id)
12
+ expect(retrieved).to be_success
13
+ expect(retrieved.metadata).to match(/dc.title: Test/)
14
+ deleted = client.delete_identifier(@id)
15
+ expect(deleted).to be_success
16
+ expect { client.get_identifier_metadata(@id) }.to raise_error
17
+ end
18
+ end
19
+
20
+ describe "initialization with a block" do
21
+ it "should wrap the block in a session" do
22
+ expect_any_instance_of(described_class).to receive(:login).and_call_original
23
+ expect_any_instance_of(described_class).to receive(:logout).and_call_original
24
+ described_class.new do |client|
25
+ expect(client.session).to be_open
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "authentication" do
31
+ describe "#login" do
32
+ it "should open a session" do
33
+ expect(subject.session).to be_closed
34
+ subject.login
35
+ expect(subject.session).to be_open
36
+ end
37
+ end
38
+ describe "#logout" do
39
+ before { subject.login }
40
+ it "should close the session" do
41
+ expect(subject.session).to be_open
42
+ subject.logout
43
+ expect(subject.session).to be_closed
44
+ end
45
+ end
46
+ describe "without a session" do
47
+ it "should send the user name and password" do
48
+ expect_any_instance_of(Net::HTTP::Post).to receive(:basic_auth).with(subject.user, subject.password).and_call_original
49
+ subject.mint_identifier(ARK_SHOULDER)
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "identifier lifecycle management" do
55
+ describe "with a session" do
56
+ Client.new do |client|
57
+ it_behaves_like "an EZID client", client
58
+ end
59
+ end
60
+ describe "without a session" do
61
+ it_behaves_like "an EZID client", Client.new
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ module Ezid
2
+ RSpec.describe Identifier do
3
+
4
+ it "should handle CRUD operations", type: :integration do
5
+ # create (mint)
6
+ identifier = described_class.create(shoulder: ARK_SHOULDER)
7
+ expect(identifier.status).to eq("public")
8
+ # update
9
+ identifier.target = "http://example.com"
10
+ identifier.save
11
+ # retrieve
12
+ identifier = described_class.find(identifier.id)
13
+ expect(identifier.target).to eq("http://example.com")
14
+ # delete
15
+ identifier = described_class.create(shoulder: ARK_SHOULDER, status: "reserved")
16
+ identifier.delete
17
+ expect { described_class.find(identifier.id) }.to raise_error
18
+ end
19
+
20
+ end
21
+ end
@@ -15,6 +15,9 @@
15
15
  #
16
16
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
17
 
18
+ require 'coveralls'
19
+ Coveralls.wear!
20
+
18
21
  require "ezid-client"
19
22
 
20
23
  Ezid::Client.configure do |config|
@@ -1,48 +1,13 @@
1
1
  module Ezid
2
2
  RSpec.describe Client do
3
3
 
4
- describe "initialization" do
5
- describe "without a block" do
6
- it "should not login" do
7
- expect_any_instance_of(described_class).not_to receive(:login)
8
- described_class.new
9
- end
10
- end
11
- describe "with a block", type: :feature do
12
- it "should wrap the block in a session" do
13
- expect_any_instance_of(described_class).to receive(:login).and_call_original
14
- expect_any_instance_of(described_class).to receive(:logout).and_call_original
15
- described_class.new do |client|
16
- expect(client.session).to be_open
17
- end
18
- end
4
+ describe "initialization without a block" do
5
+ it "should not login" do
6
+ expect_any_instance_of(described_class).not_to receive(:login)
7
+ described_class.new
19
8
  end
20
9
  end
21
10
 
22
- describe "authentication", type: :feature do
23
- describe "#login" do
24
- it "should open a session" do
25
- expect(subject.session).to be_closed
26
- subject.login
27
- expect(subject.session).to be_open
28
- end
29
- end
30
- describe "#logout" do
31
- before { subject.login }
32
- it "should close the session" do
33
- expect(subject.session).to be_open
34
- subject.logout
35
- expect(subject.session).to be_closed
36
- end
37
- end
38
- describe "without a session" do
39
- it "should send the user name and password" do
40
- expect_any_instance_of(Net::HTTP::Post).to receive(:basic_auth).with(subject.user, subject.password).and_call_original
41
- subject.mint_identifier(ARK_SHOULDER)
42
- end
43
- end
44
- end
45
-
46
11
  describe "#create_identifier" do
47
12
  let(:id) { "ark:/99999/fk4fn19h88" }
48
13
  let(:http_response) { double(body: "success: ark:/99999/fk4fn19h88") }
@@ -121,29 +86,6 @@ EOS
121
86
  end
122
87
  end
123
88
 
124
- describe "#modify_identifier", type: :feature do
125
- before do
126
- @id = described_class.new.mint_identifier(ARK_SHOULDER).id
127
- end
128
- subject { described_class.new.modify_identifier(@id, "dc.title" => "Test") }
129
- it "should update the metadata" do
130
- expect(subject).to be_success
131
- response = described_class.new.get_identifier_metadata(@id)
132
- expect(response.metadata).to match(/dc.title: Test/)
133
- end
134
- end
135
-
136
- describe "#delete_identifier", type: :feature do
137
- before do
138
- @id = described_class.new.mint_identifier(ARK_SHOULDER, "_status" => "reserved").id
139
- end
140
- subject { described_class.new.delete_identifier(@id) }
141
- it "should delete the identifier" do
142
- expect(subject).to be_success
143
- expect { described_class.new.get_identifier_metadata(@id) }.to raise_error
144
- end
145
- end
146
-
147
89
  describe "server status" do
148
90
  let(:http_response) do
149
91
  double(body: <<-EOS
@@ -61,7 +61,7 @@ module Ezid
61
61
 
62
62
  describe "#update" do
63
63
  let(:id) { "ark:/99999/fk4fn19h88" }
64
- let(:metadata) { {"_status" => "unavailable"} }
64
+ let(:metadata) { {"status" => "unavailable"} }
65
65
  subject { described_class.new(id: id) }
66
66
  before do
67
67
  allow(subject).to receive(:persisted?) { true }
@@ -70,7 +70,7 @@ module Ezid
70
70
  end
71
71
  end
72
72
  it "should update the metadata" do
73
- expect(subject.metadata).to receive(:update).with(metadata)
73
+ expect(subject).to receive(:update_metadata).with(metadata)
74
74
  subject.update(metadata)
75
75
  end
76
76
  it "should save the identifier" do
@@ -86,8 +86,8 @@ module Ezid
86
86
  before do
87
87
  allow(subject.client).to receive(:get_identifier_metadata).with(id) { double(metadata: metadata) }
88
88
  end
89
- it "should update the metadata from EZID" do
90
- expect(subject.metadata).to receive(:replace).with(metadata)
89
+ it "should reinitialize the metadata from EZID" do
90
+ expect(Metadata).to receive(:new).with(metadata)
91
91
  subject.reload
92
92
  end
93
93
  end
@@ -183,21 +183,5 @@ module Ezid
183
183
  end
184
184
  end
185
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
186
  end
203
187
  end
@@ -0,0 +1,43 @@
1
+ require "delegate"
2
+
3
+ module Ezid
4
+ RSpec.describe MetadataElements do
5
+
6
+ before do
7
+ class TestMetadata < SimpleDelegator
8
+ def initialize
9
+ super(Hash.new)
10
+ end
11
+ include MetadataElements
12
+ end
13
+ end
14
+ after do
15
+ Ezid.remove_const(:TestMetadata)
16
+ end
17
+
18
+ describe "reserved reader" do
19
+
20
+ end
21
+
22
+ describe "reserved writer" do
23
+
24
+ end
25
+
26
+ describe "reserved time reader" do
27
+
28
+ end
29
+
30
+ describe "reserved accessor" do
31
+
32
+ end
33
+
34
+ describe "profile accessor" do
35
+
36
+ end
37
+
38
+ describe "element accessor" do
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,141 @@
1
+ module Ezid
2
+ RSpec.describe Metadata do
3
+
4
+ describe "reserved elements" do
5
+ describe "readers" do
6
+ Metadata::RESERVED_ELEMENTS.each do |element|
7
+ next if element == "_crossref"
8
+ it "should have a reader for '#{element}'" do
9
+ expect(subject).to receive(:reader).with(element)
10
+ subject.send(element.sub("_", ""))
11
+ end
12
+ end
13
+ describe "for time-based elements" do
14
+ Metadata::RESERVED_TIME_ELEMENTS.each do |element|
15
+ context "\"#{element}\"" do
16
+ before { subject[element] = "1416507086" }
17
+ it "should have a reader than returns a Time instance" do
18
+ expect(subject).to receive(:reader).with(element).and_call_original
19
+ expect(subject.send(element.sub("_", ""))).to eq(Time.parse("2014-11-20 13:11:26 -0500"))
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ describe "writers" do
26
+ Metadata::RESERVED_READWRITE_ELEMENTS.each do |element|
27
+ next if element == "_crossref"
28
+ it "should have a writer for '#{element}'" do
29
+ expect(subject).to receive(:writer).with(element, "value")
30
+ subject.send("#{element.sub('_', '')}=", "value")
31
+ end
32
+ end
33
+ end
34
+ end
35
+ describe "metadata profiles" do
36
+ Metadata::PROFILES.each do |profile, elements|
37
+ describe "the '#{profile}' metadata profile" do
38
+ describe "readers" do
39
+ elements.each do |element|
40
+ it "should have a reader for '#{profile}.#{element}'" do
41
+ expect(subject).to receive(:reader).with("#{profile}.#{element}")
42
+ subject.send("#{profile}_#{element}")
43
+ end
44
+ end
45
+ end
46
+ describe "writers" do
47
+ elements.each do |element|
48
+ it "should have a writer for '#{profile}.#{element}'" do
49
+ expect(subject).to receive(:writer).with("#{profile}.#{element}", "value")
50
+ subject.send("#{profile}_#{element}=", "value")
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "ANVL output" do
59
+ let(:elements) do
60
+ { "_target" => "http://example.com/path%20with%20spaces",
61
+ "_erc" => "who: Proust, Marcel\nwhat: Remembrance of Things Past",
62
+ "_status" => "public" }
63
+ end
64
+ subject { described_class.new(elements) }
65
+ it "should output the proper format and escape" do
66
+ expect(subject.to_anvl).to eq("\
67
+ _target: http://example.com/path%2520with%2520spaces
68
+ _erc: who: Proust, Marcel%0Awhat: Remembrance of Things Past
69
+ _status: public")
70
+ end
71
+ describe "encoding" do
72
+ before do
73
+ subject.each_key { |k| subject[k] = subject[k].force_encoding(Encoding::US_ASCII) }
74
+ end
75
+ it "should be encoded in UTF-8" do
76
+ expect(subject.to_anvl.encoding).to eq(Encoding::UTF_8)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "coercion" do
82
+ subject { described_class.new(data) }
83
+ context "of nil" do
84
+ let(:data) { nil }
85
+ it "should create an empty hash" do
86
+ expect(subject).to eq({})
87
+ end
88
+ end
89
+ context "of a string" do
90
+ let(:data) do <<-EOS
91
+ _updated: 1416507086
92
+ _target: http://example.com/path%2520with%2520spaces
93
+ _profile: erc
94
+ _erc: who: Proust, Marcel%0Awhat: Remembrance of Things Past
95
+ _ownergroup: apitest
96
+ _owner: apitest
97
+ _export: yes
98
+ _created: 1416507086
99
+ _status: public
100
+ EOS
101
+ end
102
+ it "should treat the string as an ANVL document, splitting into keys and values and unescaping" do
103
+ expect(subject).to eq({ "_updated" => "1416507086",
104
+ "_target" => "http://example.com/path%20with%20spaces",
105
+ "_profile" => "erc",
106
+ "_erc" => "who: Proust, Marcel\nwhat: Remembrance of Things Past",
107
+ "_ownergroup" => "apitest",
108
+ "_owner" => "apitest",
109
+ "_export" => "yes",
110
+ "_created" => "1416507086",
111
+ "_status" => "public" })
112
+ end
113
+ end
114
+ context "of a hash-like object" do
115
+ let(:hsh) do
116
+ { "_updated" => "1416507086",
117
+ "_target" => "http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87",
118
+ "_profile" => "erc",
119
+ "_ownergroup" => "apitest",
120
+ "_owner" => "apitest",
121
+ "_export" => "yes",
122
+ "_created" => "1416507086",
123
+ "_status" => "public" }
124
+ end
125
+ context "which is a normal Hash" do
126
+ let(:data) { hsh }
127
+ it "should set the metadata to the hash" do
128
+ expect(subject).to eq(hsh)
129
+ end
130
+ end
131
+ context "which is a Metadata instance" do
132
+ let(:data) { Metadata.new(hsh) }
133
+ it "should set the metadata to the hash" do
134
+ expect(subject).to eq(hsh)
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ end
141
+ end
metadata CHANGED
@@ -1,69 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ezid-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
- - dchandekstark
7
+ - David Chandek-Stark
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-04 00:00:00.000000000 Z
11
+ date: 2014-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.6'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.6'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '3.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
69
  description: Ruby client for EZID API Version 2 (http://ezid.cdlib.org/doc/apidoc.html)
@@ -73,9 +73,9 @@ executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
- - .gitignore
77
- - .rspec
78
- - .travis.yml
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
79
  - Gemfile
80
80
  - LICENSE.txt
81
81
  - README.md
@@ -93,10 +93,13 @@ files:
93
93
  - lib/ezid/response.rb
94
94
  - lib/ezid/session.rb
95
95
  - lib/ezid/status.rb
96
- - spec/lib/ezid/client_spec.rb
97
- - spec/lib/ezid/identifier_spec.rb
98
- - spec/lib/ezid/metadata_spec.rb
96
+ - spec/integration/client_spec.rb
97
+ - spec/integration/identifier_spec.rb
99
98
  - spec/spec_helper.rb
99
+ - spec/unit/client_spec.rb
100
+ - spec/unit/identifier_spec.rb
101
+ - spec/unit/metadata_elements_spec.rb
102
+ - spec/unit/metadata_spec.rb
100
103
  homepage: https://github.com/duke-libraries/ezid-client
101
104
  licenses:
102
105
  - BSD-3-Clause
@@ -107,12 +110,12 @@ require_paths:
107
110
  - lib
108
111
  required_ruby_version: !ruby/object:Gem::Requirement
109
112
  requirements:
110
- - - ~>
113
+ - - "~>"
111
114
  - !ruby/object:Gem::Version
112
115
  version: '2.0'
113
116
  required_rubygems_version: !ruby/object:Gem::Requirement
114
117
  requirements:
115
- - - '>='
118
+ - - ">="
116
119
  - !ruby/object:Gem::Version
117
120
  version: '0'
118
121
  requirements: []
@@ -122,7 +125,10 @@ signing_key:
122
125
  specification_version: 4
123
126
  summary: Ruby client for EZID API Version 2
124
127
  test_files:
125
- - spec/lib/ezid/client_spec.rb
126
- - spec/lib/ezid/identifier_spec.rb
127
- - spec/lib/ezid/metadata_spec.rb
128
+ - spec/integration/client_spec.rb
129
+ - spec/integration/identifier_spec.rb
128
130
  - spec/spec_helper.rb
131
+ - spec/unit/client_spec.rb
132
+ - spec/unit/identifier_spec.rb
133
+ - spec/unit/metadata_elements_spec.rb
134
+ - spec/unit/metadata_spec.rb
@@ -1,288 +0,0 @@
1
- module Ezid
2
- RSpec.describe Metadata do
3
-
4
- let(:elements) do
5
- { "_updated" => "1416507086",
6
- "_target" => "http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87",
7
- "_profile" => "erc",
8
- "_ownergroup" => "apitest",
9
- "_owner" => "apitest",
10
- "_export" => "yes",
11
- "_created" => "1416507086",
12
- "_status" => "public" }
13
- 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
21
- end
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")
27
- end
28
- end
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"
40
- end
41
-
42
- describe "#replace" do
43
- it "should coerce the data"
44
- it "should call `replace' on the delegated hash"
45
- end
46
-
47
- describe "ANVL output" do
48
- it "should output the proper format" do
49
- expect(subject.to_anvl).to eq("\
50
- _updated: 1416507086
51
- _target: http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87
52
- _profile: erc
53
- _ownergroup: apitest
54
- _owner: apitest
55
- _export: yes
56
- _created: 1416507086
57
- _status: public")
58
- end
59
- describe "encoding" do
60
- before do
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)
65
- end
66
- end
67
- describe "escaping" do
68
- before do
69
- subject["_target"] = "http://example.com/path%20with%20spaces"
70
- subject["dc.title"] = "A really long title\nneeds a line feed"
71
- subject["dc.creator"] = "David Chandek-Stark\r\nJim Coble"
72
- end
73
- it "should escape a line feed" do
74
- expect(subject.to_anvl).to match(/dc.title: A really long title%0Aneeds a line feed/)
75
- end
76
- it "should escape a carriage return" do
77
- expect(subject.to_anvl).to match(/dc.creator: David Chandek-Stark%0D%0AJim Coble/)
78
- end
79
- it "should escape a percent sign" do
80
- expect(subject.to_anvl).to match(/_target: http:\/\/example.com\/path%2520with%2520spaces/)
81
- end
82
- end
83
- end
84
-
85
- describe "coercion" do
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
93
- context "of a string" do
94
- let(:data) do <<-EOS
95
- _updated: 1416507086
96
- _target: http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87
97
- _profile: erc
98
- _ownergroup: apitest
99
- _owner: apitest
100
- _export: yes
101
- _created: 1416507086
102
- _status: public
103
- EOS
104
- end
105
- it "should coerce the data into a hash" do
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"})
114
- end
115
- end
116
- context "of a hash" do
117
- let(:data) do
118
- { _updated: "1416507086",
119
- _target: "http://ezid.cdlib.org/id/ark:/99999/fk4fn19h87",
120
- _profile: "erc",
121
- _ownergroup: "apitest",
122
- _owner: "apitest",
123
- _export: "yes",
124
- _created: "1416507086",
125
- _status: "public" }
126
- end
127
- it "should stringify the keys" do
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
283
- end
284
- end
285
- end
286
-
287
- end
288
- end