ezid-client 0.4.0 → 0.4.1

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 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