hyrax-doi 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.
- checksums.yaml +7 -0
- data/README.md +65 -0
- data/Rakefile +23 -0
- data/app/actors/hyrax/actors/doi_actor.rb +50 -0
- data/app/assets/config/hyrax_doi_manifest.js +2 -0
- data/app/assets/javascripts/hyrax/doi/application.js +15 -0
- data/app/assets/stylesheets/hyrax/doi/application.css +15 -0
- data/app/controllers/hyrax/doi/application_controller.rb +15 -0
- data/app/controllers/hyrax/doi/hyrax_doi_controller.rb +91 -0
- data/app/forms/concerns/hyrax/doi/datacite_doi_form_behavior.rb +18 -0
- data/app/forms/concerns/hyrax/doi/doi_form_behavior.rb +18 -0
- data/app/helpers/hyrax/doi/helper_behavior.rb +9 -0
- data/app/helpers/hyrax/doi/work_form_helper.rb +15 -0
- data/app/helpers/hyrax/doi/work_show_helper.rb +12 -0
- data/app/jobs/hyrax/doi/application_job.rb +7 -0
- data/app/jobs/hyrax/doi/register_doi_job.rb +18 -0
- data/app/models/concerns/hyrax/doi/datacite_doi_behavior.rb +21 -0
- data/app/models/concerns/hyrax/doi/doi_behavior.rb +38 -0
- data/app/models/concerns/hyrax/doi/solr_document/datacite_doi_behavior.rb +14 -0
- data/app/models/concerns/hyrax/doi/solr_document/doi_behavior.rb +14 -0
- data/app/presenters/concerns/hyrax/doi/datacite_doi_presenter_behavior.rb +20 -0
- data/app/presenters/concerns/hyrax/doi/doi_presenter_behavior.rb +12 -0
- data/app/services/bolognese/readers/hyrax_work_reader.rb +99 -0
- data/app/services/bolognese/writers/hyrax_work_writer.rb +50 -0
- data/app/services/hyrax/doi/datacite_client.rb +138 -0
- data/app/services/hyrax/doi/datacite_registrar.rb +121 -0
- data/app/views/hyrax/base/_attribute_rows.html.erb +18 -0
- data/app/views/hyrax/base/_form_doi.html.erb +73 -0
- data/config/locales/hyrax_doi.en.yml +7 -0
- data/config/routes.rb +5 -0
- data/lib/generators/hyrax/doi/add_to_work_type_generator.rb +97 -0
- data/lib/generators/hyrax/doi/install_generator.rb +74 -0
- data/lib/generators/hyrax/doi/templates/config/initializers/hyrax-doi.rb +15 -0
- data/lib/hyrax/doi.rb +9 -0
- data/lib/hyrax/doi/engine.rb +28 -0
- data/lib/hyrax/doi/errors.rb +8 -0
- data/lib/hyrax/doi/spec/shared_specs.rb +9 -0
- data/lib/hyrax/doi/spec/shared_specs/datacite_doi_behavior.rb +40 -0
- data/lib/hyrax/doi/spec/shared_specs/datacite_doi_form_behavior.rb +17 -0
- data/lib/hyrax/doi/spec/shared_specs/datacite_doi_presenter_behavior.rb +39 -0
- data/lib/hyrax/doi/spec/shared_specs/doi_behavior.rb +63 -0
- data/lib/hyrax/doi/spec/shared_specs/doi_form_behavior.rb +17 -0
- data/lib/hyrax/doi/spec/shared_specs/doi_presenter_behavior.rb +19 -0
- data/lib/hyrax/doi/spec/shared_specs/solr_document/datacite_doi_behavior.rb +20 -0
- data/lib/hyrax/doi/spec/shared_specs/solr_document/doi_behavior.rb +20 -0
- data/lib/hyrax/doi/version.rb +6 -0
- data/lib/tasks/hyrax/doi_tasks.rake +5 -0
- metadata +319 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d0263fd4d11d35e30f48410437354a6604f0726ddfeb13791062faf174c85d81
|
4
|
+
data.tar.gz: 7db8943b4fa0b9960572ad3dba14f4f6bb069af024338033b5260ba74aac4450
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 69950241eb6d5511544d937cd87cdb94351ec171ebc94e7ad6f4dc91a16cd95be5263dea80d366ff431c6e79ba47bc28fd0b89e435078d8d199fc5caa8f7d2eb
|
7
|
+
data.tar.gz: 789ac23699e1a9476c215350d1abef88dcf0fd5c596e2c7e76974d442fc4065d44ea9f0d8e26a895401635d93a5eb06f5bdc5b084189dfdd14702dff247b1266
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Hyrax::DOI
|
2
|
+
Code: [](https://circleci.com/gh/ubiquitypress/hyrax-doi)
|
3
|
+
[](https://codeclimate.com/github/ubiquitypress/hyrax-doi)
|
4
|
+
|
5
|
+
|
6
|
+
Docs: [](./CONTRIBUTING.md)
|
7
|
+
[](./LICENSE)
|
8
|
+
|
9
|
+
Jump in: [](http://slack.samvera.org/)
|
10
|
+
|
11
|
+
Hyrax-doi is a Hyrax plugin that provides tools for working with DOIs including model attributes, minting, and fetching descriptive metadata.
|
12
|
+
|
13
|
+
## Compatibilty
|
14
|
+
Hyrax-doi is compatible with Hyrax 2.9+ and tested with a [Hyrax 2.9.0 test application](https://github.com/ubiquitypress/hyrax_test_app) that mirrors the generated app used by Hyrax internally for testing.
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'hyrax-doi'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
```bash
|
25
|
+
$ bundle
|
26
|
+
```
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
```bash
|
30
|
+
$ gem install hyrax-doi
|
31
|
+
```
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
### Setup work type for hyrax-doi
|
36
|
+
Run the generator to add DOI support to a given work type:
|
37
|
+
```
|
38
|
+
rails g hyrax:doi:add_to_work_type MyWorkType
|
39
|
+
```
|
40
|
+
|
41
|
+
## Development
|
42
|
+
|
43
|
+
### Running Rake Tasks and Generators
|
44
|
+
When working on this engine rake tasks from Hyku can be run by prepending the `app` namespace (e.g. `rake app:db:migrate`). Generators provided by rails or other gems/engines can be run like normal from this engine's root (e.g. `rails g job UbiquityExporter`).
|
45
|
+
|
46
|
+
### Development Server
|
47
|
+
|
48
|
+
To run a development server locally outside of docker do the following with each line in its own shell from the root of the engine:
|
49
|
+
```
|
50
|
+
solr_wrapper -v --config .solr_wrapper.yml
|
51
|
+
fcrepo_wrapper -v --config .fcrepo_wrapper.yml
|
52
|
+
bundle exec rails server -b 0.0.0.0
|
53
|
+
```
|
54
|
+
|
55
|
+
### Testing
|
56
|
+
|
57
|
+
Tests are run automatically on CircleCI with rubocop and codeclimate. These tests must pass before pull requests can be merged.
|
58
|
+
|
59
|
+
To run the tests locally outside of docker do the following with each line in its own shell from the root of the engine:
|
60
|
+
```
|
61
|
+
solr_wrapper -v --config .solr_wrapper_test.yml
|
62
|
+
fcrepo_wrapper -v --config .fcrepo_wrapper_test.yml
|
63
|
+
bundle exec rspec
|
64
|
+
```
|
65
|
+
You shouldn't need to run anything from inside `spec/internal_test_hyrax` unless explicitly told to do so.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'rdoc/task'
|
9
|
+
|
10
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
11
|
+
rdoc.rdoc_dir = 'rdoc'
|
12
|
+
rdoc.title = 'Hyrax::DOI'
|
13
|
+
rdoc.options << '--line-numbers'
|
14
|
+
rdoc.rdoc_files.include('README.md')
|
15
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
16
|
+
end
|
17
|
+
|
18
|
+
APP_RAKEFILE = File.expand_path("spec/internal_test_hyrax/Rakefile", __dir__)
|
19
|
+
load 'rails/tasks/engine.rake'
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module Actors
|
4
|
+
##
|
5
|
+
# An actor that registers a DOI using the configured registar
|
6
|
+
# This actor should come after the model actor which saves the work
|
7
|
+
#
|
8
|
+
# @example use in middleware
|
9
|
+
# stack = ActionDispatch::MiddlewareStack.new.tap do |middleware|
|
10
|
+
# # middleware.use OtherMiddleware
|
11
|
+
# middleware.use Hyrax::Actors::DOIActor
|
12
|
+
# # middleware.use MoreMiddleware
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# env = Hyrax::Actors::Environment.new(object, ability, attributes)
|
16
|
+
# last_actor = Hyrax::Actors::Terminator.new
|
17
|
+
# stack.build(last_actor).create(env)
|
18
|
+
class DOIActor < AbstractActor
|
19
|
+
##
|
20
|
+
# @return [Boolean]
|
21
|
+
#
|
22
|
+
# @see Hyrax::Actors::AbstractActor
|
23
|
+
def create(env)
|
24
|
+
# Assume the model actor has already run and saved the work
|
25
|
+
create_or_update_doi(env.curation_concern) && next_actor.create(env)
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# @return [Boolean]
|
30
|
+
#
|
31
|
+
# @see Hyrax::Actors::AbstractActor
|
32
|
+
def update(env)
|
33
|
+
create_or_update_doi(env.curation_concern) && next_actor.update(env)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def create_or_update_doi(work)
|
39
|
+
return true unless doi_enabled_work_type?(work)
|
40
|
+
|
41
|
+
Hyrax::DOI::RegisterDOIJob.perform_later(work, registrar: work.doi_registrar.presence, registrar_opts: work.doi_registrar_opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check if work is DOI enabled
|
45
|
+
def doi_enabled_work_type?(work)
|
46
|
+
work.class.ancestors.include? Hyrax::DOI::DOIBehavior
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require rails-ujs
|
14
|
+
//= require activestorage
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
class ApplicationController < ActionController::Base
|
5
|
+
protect_from_forgery with: :exception
|
6
|
+
|
7
|
+
def self.search_state_class=(*)
|
8
|
+
# no-op to make Hyrax::Controller happy
|
9
|
+
end
|
10
|
+
|
11
|
+
# Include after search_state_class is defined since Hyrax::Controller calls it
|
12
|
+
include Hyrax::Controller
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
class HyraxDOIController < ApplicationController
|
5
|
+
before_action :check_authorization
|
6
|
+
|
7
|
+
def create_draft_doi
|
8
|
+
draft_doi = mint_draft_doi
|
9
|
+
|
10
|
+
respond_to do |format|
|
11
|
+
format.js { render js: autofill_field(doi_attribute_name, draft_doi), status: :created }
|
12
|
+
format.json { render_json_response(response_type: :created, options: { data: draft_doi }) }
|
13
|
+
end
|
14
|
+
rescue Hyrax::DOI::DataCiteClient::Error => e
|
15
|
+
respond_to do |format|
|
16
|
+
format.js { render plain: e.message, status: :internal_server_error }
|
17
|
+
format.json { render_json_response(response_type: :internal_error, message: e.full_message) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def autofill
|
22
|
+
doi = params['doi']
|
23
|
+
|
24
|
+
respond_to do |format|
|
25
|
+
format.js { render js: autofill_js(doi), status: :ok }
|
26
|
+
end
|
27
|
+
rescue Hyrax::DOI::NotFoundError => e
|
28
|
+
respond_to do |format|
|
29
|
+
format.js { render plain: e.message, status: :internal_server_error }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def check_authorization
|
36
|
+
raise Hydra::AccessDenied unless current_ability.can_create_any_work?
|
37
|
+
end
|
38
|
+
|
39
|
+
def mint_draft_doi
|
40
|
+
doi_registrar.mint_draft_doi
|
41
|
+
end
|
42
|
+
|
43
|
+
def doi_registrar
|
44
|
+
# TODO: generalize this
|
45
|
+
Hyrax::Identifier::Registrar.for(:datacite, {})
|
46
|
+
end
|
47
|
+
|
48
|
+
def field_selector(attribute_name)
|
49
|
+
".#{params[:curation_concern]}_#{attribute_name}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def doi_attribute_name
|
53
|
+
params[:attribute] || "doi"
|
54
|
+
end
|
55
|
+
|
56
|
+
def hyrax_work_from_doi(doi)
|
57
|
+
meta = Bolognese::Metadata.new(input: doi)
|
58
|
+
# Check that a record was actually loaded
|
59
|
+
raise Hyrax::DOI::NotFoundError, "DOI (#{doi}) could not be found." if meta.blank? || meta.doi.blank?
|
60
|
+
meta.hyrax_work
|
61
|
+
end
|
62
|
+
|
63
|
+
# TODO: Move this out to a partial that gets rendered?
|
64
|
+
def autofill_js(doi)
|
65
|
+
# TODO: Need to wipe old data or is this just supplemental?
|
66
|
+
js = hyrax_work_from_doi(doi).attributes.collect { |k, v| autofill_field(k, v) }.reject(&:blank?).join("\n")
|
67
|
+
js << "document.location = '#metadata';"
|
68
|
+
end
|
69
|
+
|
70
|
+
# TODO: Move this out to a partial that gets rendered?
|
71
|
+
def autofill_field(attribute_name, value)
|
72
|
+
js = []
|
73
|
+
# TODO: add error handling in the JS so an error doesn't leave the autofilling incomplete
|
74
|
+
Array(value).each_with_index do |v, index|
|
75
|
+
# Is this the right way to do this?
|
76
|
+
# Need to be smarter to see if all repeated fields are filled before trying to create a new one by clicking?
|
77
|
+
js << "document.querySelectorAll('#{field_selector(attribute_name)} button.add')[0].click();" unless index.zero?
|
78
|
+
js << "document.querySelectorAll('#{field_selector(attribute_name)} .form-control')[#{index}].value = '#{helpers.escape_javascript(v)}';"
|
79
|
+
end
|
80
|
+
js.reject(&:blank?).join("\n")
|
81
|
+
end
|
82
|
+
|
83
|
+
# Override of Hyrax method (See https://github.com/samvera/hyrax/pull/4495)
|
84
|
+
# render a json response for +response_type+
|
85
|
+
def render_json_response(response_type: :success, message: nil, options: {})
|
86
|
+
json_body = Hyrax::API.generate_response_body(response_type: response_type, message: message, options: options)
|
87
|
+
render json: json_body, status: Hyrax::API.default_responses[response_type][:code]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
module DataCiteDOIFormBehavior
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
self.terms += [:doi_status_when_public]
|
9
|
+
|
10
|
+
delegate :doi_status_when_public, to: :model
|
11
|
+
end
|
12
|
+
|
13
|
+
def secondary_terms
|
14
|
+
super - [:doi_status_when_public]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
module DOIFormBehavior
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
self.terms += [:doi]
|
9
|
+
|
10
|
+
delegate :doi, to: :model
|
11
|
+
end
|
12
|
+
|
13
|
+
def secondary_terms
|
14
|
+
super - [:doi]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
module WorkFormHelper
|
5
|
+
def form_tabs_for(form:)
|
6
|
+
if form.model_class.ancestors.include? Hyrax::DOI::DOIBehavior
|
7
|
+
# TODO: Add check for feature flipper?
|
8
|
+
super.prepend("doi")
|
9
|
+
else
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
module WorkShowHelper
|
5
|
+
def render_doi?(presenter)
|
6
|
+
return false unless presenter.class.ancestors.include? Hyrax::DOI::DOIPresenterBehavior
|
7
|
+
return presenter.doi_status_when_public.in? [nil, 'registered', 'findable'] if presenter.class.ancestors.include? Hyrax::DOI::DataCiteDOIPresenterBehavior
|
8
|
+
true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
class RegisterDOIJob < ApplicationJob
|
5
|
+
queue_as Hyrax.config.ingest_queue_name
|
6
|
+
|
7
|
+
##
|
8
|
+
# @param model [ActiveFedora::Base]
|
9
|
+
# @param registrar [String] Note this is a string and not a symbol because ActiveJob cannot serialize a symbol
|
10
|
+
# @param registrar_opts [Hash]
|
11
|
+
def perform(model, registrar: Hyrax.config.identifier_registrars.keys.first, registrar_opts: {})
|
12
|
+
Hyrax::Identifier::Dispatcher
|
13
|
+
.for(registrar.to_sym, **registrar_opts)
|
14
|
+
.assign_for!(object: model, attribute: :doi)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
module DataCiteDOIBehavior
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
property :doi_status_when_public, predicate: ::RDF::URI('http://samvera.org/ns/hyrax/doi#doi_status_when_public'), multiple: false do |index|
|
9
|
+
index.as :stored_sortable
|
10
|
+
end
|
11
|
+
|
12
|
+
validates :doi_status_when_public, inclusion: { in: Hyrax::DOI::DataCiteRegistrar::STATES }, allow_nil: true
|
13
|
+
end
|
14
|
+
|
15
|
+
# Override
|
16
|
+
def doi_registrar
|
17
|
+
'datacite'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module DOI
|
4
|
+
module DOIBehavior
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
DOI_REGEX = /\A10\.\d{4,}(\.\d+)*\/[-._;():\/A-Za-z\d]+\z/.freeze
|
8
|
+
|
9
|
+
included do
|
10
|
+
property :doi, predicate: ::RDF::Vocab::BIBO.doi, multiple: true do |index|
|
11
|
+
index.as :stored_sortable
|
12
|
+
end
|
13
|
+
|
14
|
+
validate :validate_doi
|
15
|
+
end
|
16
|
+
|
17
|
+
# Override this method
|
18
|
+
# Specify a registrar to use with this class
|
19
|
+
def doi_registrar
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# Override this method
|
24
|
+
# Specify options for the registrar to use with this class
|
25
|
+
def doi_registrar_opts
|
26
|
+
{}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def validate_doi
|
32
|
+
Array(doi).each do |doi|
|
33
|
+
errors.add(:doi, "DOI (#{doi}) is invalid.") unless doi.match? DOI_REGEX
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|