datacite-mapping 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +42 -0
  3. data/.rubocop.yml +28 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +2 -0
  6. data/.yardopts +2 -0
  7. data/CHANGES.md +3 -0
  8. data/Gemfile +3 -0
  9. data/LICENSE.md +22 -0
  10. data/README.md +168 -0
  11. data/Rakefile +49 -0
  12. data/datacite-mapping.gemspec +37 -0
  13. data/examples/reading.rb +75 -0
  14. data/examples/writing.rb +49 -0
  15. data/lib/datacite/mapping.rb +36 -0
  16. data/lib/datacite/mapping/alternate_identifier.rb +45 -0
  17. data/lib/datacite/mapping/contributor.rb +125 -0
  18. data/lib/datacite/mapping/creator.rb +48 -0
  19. data/lib/datacite/mapping/date.rb +153 -0
  20. data/lib/datacite/mapping/description.rb +121 -0
  21. data/lib/datacite/mapping/geo_location.rb +49 -0
  22. data/lib/datacite/mapping/geo_location_box.rb +137 -0
  23. data/lib/datacite/mapping/geo_location_point.rb +102 -0
  24. data/lib/datacite/mapping/identifier.rb +45 -0
  25. data/lib/datacite/mapping/module_info.rb +12 -0
  26. data/lib/datacite/mapping/name_identifier.rb +48 -0
  27. data/lib/datacite/mapping/related_identifier.rb +209 -0
  28. data/lib/datacite/mapping/resource.rb +201 -0
  29. data/lib/datacite/mapping/resource_type.rb +83 -0
  30. data/lib/datacite/mapping/rights.rb +36 -0
  31. data/lib/datacite/mapping/subject.rb +55 -0
  32. data/lib/datacite/mapping/title.rb +69 -0
  33. data/spec/.rubocop.yml +7 -0
  34. data/spec/data/resource.xml +61 -0
  35. data/spec/rspec_custom_matchers.rb +69 -0
  36. data/spec/spec_helper.rb +31 -0
  37. data/spec/unit/datacite/mapping/alternate_identifier_spec.rb +60 -0
  38. data/spec/unit/datacite/mapping/contributor_spec.rb +129 -0
  39. data/spec/unit/datacite/mapping/creator_spec.rb +125 -0
  40. data/spec/unit/datacite/mapping/date_spec.rb +246 -0
  41. data/spec/unit/datacite/mapping/description_spec.rb +89 -0
  42. data/spec/unit/datacite/mapping/geo_location_box_spec.rb +241 -0
  43. data/spec/unit/datacite/mapping/geo_location_point_spec.rb +148 -0
  44. data/spec/unit/datacite/mapping/geo_location_spec.rb +116 -0
  45. data/spec/unit/datacite/mapping/identifier_spec.rb +75 -0
  46. data/spec/unit/datacite/mapping/name_identifier_spec.rb +89 -0
  47. data/spec/unit/datacite/mapping/related_identifier_spec.rb +157 -0
  48. data/spec/unit/datacite/mapping/resource_spec.rb +727 -0
  49. data/spec/unit/datacite/mapping/resource_type_spec.rb +69 -0
  50. data/spec/unit/datacite/mapping/rights_spec.rb +78 -0
  51. data/spec/unit/datacite/mapping/subject_spec.rb +108 -0
  52. data/spec/unit/datacite/mapping/title_spec.rb +113 -0
  53. metadata +262 -0
@@ -0,0 +1,83 @@
1
+ require 'xml/mapping_extensions'
2
+
3
+ module Datacite
4
+ module Mapping
5
+
6
+ # Controlled vocabulary of general resource types.
7
+ class ResourceTypeGeneral < TypesafeEnum::Base
8
+ # @!parse AUDIOVISUAL = Audiovisual
9
+ new :AUDIOVISUAL, 'Audiovisual'
10
+
11
+ # @!parse COLLECTION = Collection
12
+ new :COLLECTION, 'Collection'
13
+
14
+ # @!parse DATASET = Dataset
15
+ new :DATASET, 'Dataset'
16
+
17
+ # @!parse EVENT = Event
18
+ new :EVENT, 'Event'
19
+
20
+ # @!parse IMAGE = Image
21
+ new :IMAGE, 'Image'
22
+
23
+ # @!parse INTERACTIVE_RESOURCE = InteractiveResource
24
+ new :INTERACTIVE_RESOURCE, 'InteractiveResource'
25
+
26
+ # @!parse MODEL = Model
27
+ new :MODEL, 'Model'
28
+
29
+ # @!parse PHYSICAL_OBJECT = PhysicalObject
30
+ new :PHYSICAL_OBJECT, 'PhysicalObject'
31
+
32
+ # @!parse SERVICE = Service
33
+ new :SERVICE, 'Service'
34
+
35
+ # @!parse SOFTWARE = Software
36
+ new :SOFTWARE, 'Software'
37
+
38
+ # @!parse SOUND = Sound
39
+ new :SOUND, 'Sound'
40
+
41
+ # @!parse TEXT = Text
42
+ new :TEXT, 'Text'
43
+
44
+ # @!parse WORKFLOW = Workflow
45
+ new :WORKFLOW, 'Workflow'
46
+
47
+ # @!parse OTHER = Other
48
+ new :OTHER, 'Other'
49
+
50
+ end
51
+
52
+ # The type of the resource
53
+ class ResourceType
54
+ include XML::Mapping
55
+
56
+ root_element_name 'resourceType'
57
+
58
+ # @!attribute [rw] resource_type_general
59
+ # @return [ResourceTypeGeneral] the general resource type
60
+ typesafe_enum_node :resource_type_general, '@resourceTypeGeneral', class: ResourceTypeGeneral
61
+
62
+ # @!attribute [rw] value
63
+ # @return [String] additional free text description of the resource type. Optional.
64
+ text_node :value, 'text()', default_value: nil
65
+
66
+ alias_method :_resource_type_general=, :resource_type_general=
67
+ private :_resource_type_general=
68
+
69
+ # Initializes a new {ResourceType}
70
+ # @param resource_type_general [ResourceTypeGeneral] the general resource type
71
+ # @param value [String] additional free text description of the resource type.
72
+ def initialize(resource_type_general:, value: nil)
73
+ self.resource_type_general = resource_type_general
74
+ self.value = value
75
+ end
76
+
77
+ def resource_type_general=(val)
78
+ fail ArgumentError, 'General resource type cannot be nil' unless val
79
+ self._resource_type_general = val
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,36 @@
1
+ require 'xml/mapping_extensions'
2
+
3
+ module Datacite
4
+ module Mapping
5
+
6
+ # Rights information for the {Resource}
7
+ class Rights
8
+ include XML::Mapping
9
+
10
+ # @!attribute [rw] uri
11
+ # @return [URI, nil] a URI for the license. Optional.
12
+ uri_node :uri, '@rightsURI', default_value: nil
13
+
14
+ # @!attribute [rw] value
15
+ # @return [String] the rights statement. Cannot be empty or nil.
16
+ text_node :value, 'text()'
17
+
18
+ # Initializes a new {Rights} object
19
+ #
20
+ # @param uri [URI, nil] a URI for the license. Optional.
21
+ # @param value [String] the rights statement. Cannot be empty or nil.
22
+ def initialize(uri: nil, value:)
23
+ self.uri = uri
24
+ self.value = value
25
+ end
26
+
27
+ alias_method :_value=, :value=
28
+ private :_value=
29
+
30
+ def value=(v)
31
+ fail ArgumentError, 'Value cannot be empty or nil' unless v && !v.empty?
32
+ self._value = v.strip
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ require 'xml/mapping'
2
+
3
+ module Datacite
4
+ module Mapping
5
+
6
+ # Subject, keyword, classification code, or key phrase describing the {Resource}.
7
+ class Subject
8
+ include XML::Mapping
9
+
10
+ # @!attribute [rw] scheme
11
+ # @return [String, nil] the subject scheme or classification code or authority if one is used. Optional.
12
+ text_node :scheme, '@subjectScheme', default_value: nil
13
+
14
+ # @!attribute [rw] scheme_uri
15
+ # @return [URI, nil] the URI of the subject scheme or classification code or authority if one is used. Optional.
16
+ uri_node :scheme_uri, '@schemeURI', default_value: nil
17
+
18
+ # @!attribute [rw] language
19
+ # @return [String] an IETF BCP 47, ISO 639-1 language code identifying the language.
20
+ # It's unclear from the spec whether language is required; to play it safe, if it's missing, we default to 'en'.
21
+ text_node :language, '@xml:lang', default_value: nil
22
+
23
+ # @!attribute [rw] value
24
+ # @return [String] the subject itself.
25
+ text_node :value, 'text()'
26
+
27
+ alias_method :_language, :language
28
+ private :_language
29
+
30
+ alias_method :_language=, :language=
31
+ private :_language=
32
+
33
+ # Initializes a new {Subject}
34
+ # @param scheme [String, nil] the subject scheme or classification code or authority if one is used. Optional.
35
+ # @param scheme_uri [URI, nil] the URI of the subject scheme or classification code or authority if one is used. Optional.
36
+ # @param language [String] an IETF BCP 47, ISO 639-1 language code identifying the language.
37
+ # It's unclear from the spec whether language is required; to play it safe, if it's missing, we default to 'en'.
38
+ # @param value [String] the subject itself.
39
+ def initialize(scheme: nil, scheme_uri: nil, language: 'en', value:)
40
+ self.scheme = scheme
41
+ self.scheme_uri = scheme_uri
42
+ self.language = language
43
+ self.value = value
44
+ end
45
+
46
+ def language
47
+ _language || 'en'
48
+ end
49
+
50
+ def language=(value)
51
+ self._language = value.strip if value
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,69 @@
1
+ require 'xml/mapping_extensions'
2
+
3
+ module Datacite
4
+ module Mapping
5
+
6
+ # Controlled vocabulary of title types (for titles other than the main/default title).
7
+ class TitleType < TypesafeEnum::Base
8
+ # @!parse ALTERNATIVE_TITLE = AlternativeTitle
9
+ new :ALTERNATIVE_TITLE, 'AlternativeTitle'
10
+
11
+ # @!parse SUBTITLE = Subtitle
12
+ new :SUBTITLE, 'Subtitle'
13
+
14
+ # @!parse TRANSLATED_TITLE = TranslatedTitle
15
+ new :TRANSLATED_TITLE, 'TranslatedTitle'
16
+
17
+ end
18
+
19
+ # A name or title by which a {Resource} is known.
20
+ class Title
21
+ include XML::Mapping
22
+
23
+ # @!attribute [rw] language
24
+ # @return [String] an IETF BCP 47, ISO 639-1 language code identifying the language.
25
+ # It's unclear from the spec whether language is required; to play it safe, if it's missing, we default to 'en'.
26
+ text_node :language, '@xml:lang', default_value: nil
27
+
28
+ # @!attribute [rw] type
29
+ # @return [TitleType, nil] the title type. Optional.
30
+ typesafe_enum_node :type, '@titleType', class: TitleType, default_value: nil
31
+
32
+ # @!attribute [rw] value
33
+ # @return [String] the title itself.
34
+ text_node :value, 'text()'
35
+
36
+ alias_method :_language, :language
37
+ private :_language
38
+
39
+ alias_method :_language=, :language=
40
+ private :_language=
41
+
42
+ # Initializes a new {Title}.
43
+ # @param language [String] an IETF BCP 47, ISO 639-1 language code identifying the language.
44
+ # It's unclear from the spec whether language is required; to play it safe, if it's missing, we default to 'en'.
45
+ # @param value [String] the title itself.
46
+ # @param type [TitleType, nil] the title type. Optional.
47
+ def initialize(language: 'en', value:, type: nil)
48
+ self.language = language
49
+ self.type = type
50
+ self.value = value
51
+ end
52
+
53
+ def language
54
+ _language || 'en'
55
+ end
56
+
57
+ def language=(value)
58
+ self._language = value.strip if value
59
+ end
60
+
61
+ alias_method :_value=, :value=
62
+
63
+ def value=(v)
64
+ fail ArgumentError, 'Value cannot be empty or nil' unless v && !v.empty?
65
+ self._value = v.strip
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,7 @@
1
+ inherit_from: ../.rubocop.yml
2
+
3
+ Metrics/MethodLength:
4
+ Enabled: false
5
+
6
+ Metrics/ModuleLength:
7
+ Enabled: false
@@ -0,0 +1,61 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <resource xsi:schemaLocation="http://datacite.org/schema/kernel-3 http://schema.datacite.org/meta/kernel-3/metadata.xsd" xmlns="http://datacite.org/schema/kernel-3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3
+ <identifier identifierType="DOI">10.5072/example-full</identifier>
4
+ <creators>
5
+ <creator>
6
+ <creatorName>Miller, Elizabeth</creatorName>
7
+ <nameIdentifier schemeURI="http://orcid.org/" nameIdentifierScheme="ORCID">0000-0001-5000-0007</nameIdentifier>
8
+ <affiliation>DataCite</affiliation>
9
+ </creator>
10
+ </creators>
11
+ <titles>
12
+ <title xml:lang="en-us">Full DataCite XML Example</title>
13
+ <title xml:lang="en-us" titleType="Subtitle">Demonstration of DataCite Properties.</title>
14
+ </titles>
15
+ <publisher>DataCite</publisher>
16
+ <publicationYear>2014</publicationYear>
17
+ <subjects>
18
+ <subject xml:lang="en-us" schemeURI="http://dewey.info/" subjectScheme="dewey">000 computer science</subject>
19
+ </subjects>
20
+ <contributors>
21
+ <contributor contributorType="ProjectLeader">
22
+ <contributorName>Starr, Joan</contributorName>
23
+ <nameIdentifier schemeURI="http://orcid.org/" nameIdentifierScheme="ORCID">0000-0002-7285-027X</nameIdentifier>
24
+ <affiliation>California Digital Library</affiliation>
25
+ </contributor>
26
+ </contributors>
27
+ <dates>
28
+ <date dateType="Updated">2014-10-17</date>
29
+ </dates>
30
+ <language>en-us</language>
31
+ <resourceType resourceTypeGeneral="Software">XML</resourceType>
32
+ <alternateIdentifiers>
33
+ <alternateIdentifier alternateIdentifierType="URL">http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml</alternateIdentifier>
34
+ </alternateIdentifiers>
35
+ <relatedIdentifiers>
36
+ <relatedIdentifier relatedIdentifierType="URL" relationType="HasMetadata" relatedMetadataScheme="citeproc+json" schemeURI="https://github.com/citation-style-language/schema/raw/master/csl-data.json">http://data.datacite.org/application/citeproc+json/10.5072/example-full</relatedIdentifier>
37
+ <relatedIdentifier relatedIdentifierType="arXiv" relationType="IsReviewedBy">arXiv:0706.0001</relatedIdentifier>
38
+ </relatedIdentifiers>
39
+ <sizes>
40
+ <size>3KB</size>
41
+ </sizes>
42
+ <formats>
43
+ <format>application/xml</format>
44
+ </formats>
45
+ <version>3.1</version>
46
+ <rightsList>
47
+ <rights rightsURI="http://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0 Universal</rights>
48
+ </rightsList>
49
+ <descriptions>
50
+ <description xml:lang="en-us" descriptionType="Abstract">
51
+ XML example of all DataCite Metadata Schema v3.1 properties.
52
+ </description>
53
+ </descriptions>
54
+ <geoLocations>
55
+ <geoLocation>
56
+ <geoLocationPoint>31.233 -67.302</geoLocationPoint>
57
+ <geoLocationBox>41.09 -71.032 42.893 -68.211</geoLocationBox>
58
+ <geoLocationPlace>Atlantic Ocean</geoLocationPlace>
59
+ </geoLocation>
60
+ </geoLocations>
61
+ </resource>
@@ -0,0 +1,69 @@
1
+ require 'rspec/expectations'
2
+ require 'equivalent-xml'
3
+
4
+ RSpec::Matchers.define :be_xml do |expected|
5
+
6
+ def to_nokogiri(xml)
7
+ return nil unless xml
8
+ case xml
9
+ when Nokogiri::XML::Element
10
+ xml
11
+ when Nokogiri::XML::Document
12
+ xml.root
13
+ when String
14
+ to_nokogiri(Nokogiri::XML(xml))
15
+ when REXML::Element
16
+ to_nokogiri(xml.to_s)
17
+ else
18
+ fail "be_xml() expected XML, got #{xml.class}"
19
+ end
20
+ end
21
+
22
+ def to_pretty(nokogiri)
23
+ return nil unless nokogiri
24
+ out = StringIO.new
25
+ save_options = Nokogiri::XML::Node::SaveOptions::FORMAT | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
26
+ nokogiri.write_xml_to(out, encoding: 'UTF-8', indent: 2, save_with: save_options)
27
+ out.string
28
+ end
29
+
30
+ match do |actual|
31
+ expected_xml = to_nokogiri(expected) || fail("expected value #{expected} does not appear to be XML")
32
+ actual_xml = to_nokogiri(actual)
33
+
34
+ EquivalentXml.equivalent?(expected_xml, actual_xml, element_order: false, normalize_whitespace: true)
35
+ end
36
+
37
+ failure_message do |actual|
38
+ expected_string = to_pretty(to_nokogiri(expected))
39
+ actual_string = to_pretty(to_nokogiri(actual)) || actual
40
+ "expected XML:\n#{expected_string}\n\nbut was:\n#{actual_string}"
41
+ end
42
+
43
+ failure_message_when_negated do |actual|
44
+ actual_xml = to_element(actual) || actual
45
+ "expected not to get XML:\n\t#{actual_xml}"
46
+ end
47
+ end
48
+
49
+ RSpec::Matchers.define :be_time do |expected|
50
+
51
+ def to_string(time)
52
+ time.is_a?(Time) ? time.utc.round(2).iso8601(2) : time.to_s
53
+ end
54
+
55
+ match do |actual|
56
+ if expected
57
+ fail "Expected value #{expected} is not a Time" unless expected.is_a?(Time)
58
+ actual.is_a?(Time) && (to_string(expected) == to_string(actual))
59
+ else
60
+ return actual.nil?
61
+ end
62
+ end
63
+
64
+ failure_message do |actual|
65
+ expected_str = to_string(expected)
66
+ actual_str = to_string(actual)
67
+ "expected time:\n#{expected_str}\n\nbut was:\n#{actual_str}"
68
+ end
69
+ end
@@ -0,0 +1,31 @@
1
+ # ------------------------------------------------------------
2
+ # SimpleCov setup
3
+
4
+ if ENV['COVERAGE']
5
+ require 'simplecov'
6
+ require 'simplecov-console'
7
+
8
+ SimpleCov.minimum_coverage 100
9
+ SimpleCov.start do
10
+ add_filter '/spec/'
11
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
12
+ SimpleCov::Formatter::HTMLFormatter,
13
+ SimpleCov::Formatter::Console,
14
+ ]
15
+ end
16
+ end
17
+
18
+ # ------------------------------------------------------------
19
+ # Rspec configuration
20
+
21
+ RSpec.configure do |config|
22
+ config.raise_errors_for_deprecations!
23
+ config.mock_with :rspec
24
+ end
25
+
26
+ require 'rspec_custom_matchers'
27
+
28
+ # ------------------------------------------------------------
29
+ # Stash::Wrapper
30
+
31
+ require 'datacite/mapping'
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module Datacite
4
+ module Mapping
5
+ describe AlternateIdentifier do
6
+
7
+ describe '#initialize' do
8
+ it 'sets the identifier type and value' do
9
+ id = AlternateIdentifier.new(type: 'URL', value: 'http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
10
+ expect(id.type).to eq('URL')
11
+ expect(id.value).to eq('http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
12
+ end
13
+ end
14
+
15
+ describe 'type' do
16
+ it 'sets the type' do
17
+ rt = AlternateIdentifier.allocate
18
+ rt.type = 'URL'
19
+ expect(rt.type).to eq('URL')
20
+ end
21
+ it 'requires a type' do
22
+ rt = AlternateIdentifier.new(type: 'URL', value: 'http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
23
+ expect { rt.type = nil }.to raise_error(ArgumentError)
24
+ expect(rt.type).to eq('URL')
25
+ end
26
+ end
27
+
28
+ describe 'value' do
29
+ it 'sets the value' do
30
+ rt = AlternateIdentifier.allocate
31
+ rt.value = 'http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml'
32
+ expect(rt.value).to eq('http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
33
+ end
34
+ it 'requires a value' do
35
+ rt = AlternateIdentifier.new(type: 'URL', value: 'http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
36
+ expect { rt.value = nil }.to raise_error(ArgumentError)
37
+ expect(rt.value).to eq('http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
38
+ end
39
+ end
40
+
41
+ describe '#load_from_xml' do
42
+ it 'parses XML' do
43
+ xml_text = '<alternateIdentifier alternateIdentifierType="URL">http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml</alternateIdentifier>'
44
+ xml = REXML::Document.new(xml_text).root
45
+ id = AlternateIdentifier.load_from_xml(xml)
46
+ expect(id.type).to eq('URL')
47
+ expect(id.value).to eq('http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
48
+ end
49
+ end
50
+
51
+ describe '#save_to_xml' do
52
+ it 'writes XML' do
53
+ id = AlternateIdentifier.new(type: 'URL', value: 'http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml')
54
+ expected_xml = '<alternateIdentifier alternateIdentifierType="URL">http://schema.datacite.org/schema/meta/kernel-3.1/example/datacite-example-full-v3.1.xml</alternateIdentifier>'
55
+ expect(id.save_to_xml).to be_xml(expected_xml)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end