datacite-mapping 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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