hyrax 2.8.0 → 2.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +8 -8
  3. data/.gitignore +1 -0
  4. data/.regen +1 -1
  5. data/README.md +1 -1
  6. data/app/assets/javascripts/hyrax.js +1 -0
  7. data/app/assets/javascripts/hyrax/autocomplete.es6 +29 -0
  8. data/app/assets/javascripts/hyrax/editor.es6 +9 -10
  9. data/app/assets/javascripts/hyrax/skip_to_content.js +15 -0
  10. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +18 -7
  11. data/app/controllers/hyrax/file_sets_controller.rb +6 -1
  12. data/app/controllers/hyrax/users_controller.rb +1 -1
  13. data/app/helpers/hyrax/hyrax_helper_behavior.rb +1 -0
  14. data/app/helpers/hyrax/work_form_helper.rb +48 -0
  15. data/app/jobs/embargo_expiry_job.rb +15 -0
  16. data/app/jobs/iiif_manifest_cache_prewarm_job.rb +16 -0
  17. data/app/jobs/lease_expiry_job.rb +15 -0
  18. data/app/models/concerns/hyrax/ability.rb +1 -1
  19. data/app/models/concerns/hyrax/solr_document/characterization.rb +1 -1
  20. data/app/models/concerns/hyrax/solr_document/metadata.rb +1 -0
  21. data/app/models/concerns/hyrax/solr_document/ordered_members.rb +46 -0
  22. data/app/models/concerns/hyrax/solr_document_behavior.rb +10 -0
  23. data/app/presenters/hyrax/displays_image.rb +25 -21
  24. data/app/presenters/hyrax/iiif_manifest_presenter.rb +232 -0
  25. data/app/presenters/hyrax/member_presenter_factory.rb +1 -7
  26. data/app/services/hyrax/caching_iiif_manifest_builder.rb +53 -0
  27. data/app/services/hyrax/collection_types/permissions_service.rb +3 -3
  28. data/app/services/hyrax/collections/permissions_service.rb +1 -1
  29. data/app/services/hyrax/contextual_path.rb +1 -1
  30. data/app/services/hyrax/identifier/builder.rb +45 -0
  31. data/app/services/hyrax/identifier/dispatcher.rb +61 -0
  32. data/app/services/hyrax/identifier/registrar.rb +41 -0
  33. data/app/services/hyrax/manifest_builder_service.rb +88 -0
  34. data/app/services/hyrax/versioning_service.rb +9 -0
  35. data/app/views/hyrax/base/_form.html.erb +1 -1
  36. data/app/views/hyrax/base/_form_child_work_relationships.html.erb +1 -1
  37. data/app/views/hyrax/base/_form_progress.html.erb +4 -0
  38. data/app/views/hyrax/base/_form_visibility_error.html.erb +2 -0
  39. data/app/views/hyrax/base/_guts4form.html.erb +7 -1
  40. data/app/views/hyrax/base/_show_actions.html.erb +1 -1
  41. data/app/views/hyrax/batch_uploads/_form.html.erb +2 -2
  42. data/app/views/hyrax/dashboard/_sidebar.html.erb +1 -1
  43. data/config/features.rb +4 -0
  44. data/hyrax.gemspec +3 -2
  45. data/lib/generators/hyrax/templates/catalog_controller.rb +4 -0
  46. data/lib/generators/hyrax/templates/config/initializers/hyrax.rb +5 -0
  47. data/lib/generators/hyrax/templates/config/locales/hyrax.es.yml +1 -1
  48. data/lib/hyrax.rb +1 -0
  49. data/lib/hyrax/configuration.rb +23 -4
  50. data/lib/hyrax/engine.rb +1 -0
  51. data/lib/hyrax/specs/shared_specs.rb +1 -0
  52. data/lib/hyrax/specs/shared_specs/identifiers.rb +27 -0
  53. data/lib/hyrax/version.rb +1 -1
  54. data/template.rb +1 -1
  55. metadata +47 -8
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Identifier
4
+ ##
5
+ # Builds an identifier string.
6
+ #
7
+ # Implementations must accept a `prefix:` to `#initialize`, and a `hint:` to
8
+ # `#build`. Either or both may be used at the preference of the specific
9
+ # implementer or ignored entirely when `#build` is called.
10
+ #
11
+ # @example
12
+ # builder = Hyrax::Identifier::Builder.new(prefix: 'moomin')
13
+ # builder.build(hint: '1') # => "moomin/1"
14
+ class Builder
15
+ ##
16
+ # @!attribute prefix [rw]
17
+ # @return [String] the prefix to use when building identifiers
18
+ attr_accessor :prefix
19
+
20
+ ##
21
+ # @param prefix [String] the prefix to use when building identifiers
22
+ def initialize(prefix: 'pfx')
23
+ @prefix = prefix
24
+ end
25
+
26
+ ##
27
+ # @note this default builder requires a `hint` which it appends to the
28
+ # prefix to generate the identifier string.
29
+ #
30
+ # @param hint [#to_s] a string-able object which may be used by the builder
31
+ # to generate an identifier. Hints may be required by some builders, while
32
+ # others may ignore them to generate an identifier by other means.
33
+ #
34
+ # @return [String]
35
+ # @raise [ArgumentError] if an identifer can't be built from the provided
36
+ # hint.
37
+ def build(hint: nil)
38
+ raise(ArgumentError, "No hint provided to #{self.class}#build") if
39
+ hint.nil?
40
+
41
+ "#{prefix}/#{hint}"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Identifier
4
+ class Dispatcher
5
+ ##
6
+ # @!attribute [rw] registrar
7
+ # @return [Hyrax::Identifier::Registrar]
8
+ attr_accessor :registrar
9
+
10
+ ##
11
+ # @param registrar [Hyrax::Identifier::Registrar]
12
+ def initialize(registrar:)
13
+ @registrar = registrar
14
+ end
15
+
16
+ class << self
17
+ ##
18
+ # @param type [Symbol]
19
+ # @param registrar_opts [Hash]
20
+ # @option registrar_opts [Hyrax::Identifier::Builder] :builder
21
+ #
22
+ # @return [Hyrax::Identifier::Dispatcher] a dispatcher with an registrar for the
23
+ # given type
24
+ # @see IdentifierRegistrar.for
25
+ def for(type, **registrar_opts)
26
+ new(registrar: Hyrax::Identifier::Registrar.for(type, **registrar_opts))
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Assigns an identifier to the object.
32
+ #
33
+ # This involves two steps:
34
+ # - Registering the identifier with the registrar service via `registrar`.
35
+ # - Storing the new identifier on the object, in the provided `attribute`.
36
+ #
37
+ # @note the attribute for identifier storage must be multi-valued, and will
38
+ # be overwritten during assignment.
39
+ #
40
+ # @param attribute [Symbol] the attribute in which to store the identifier.
41
+ # This attribute will be overwritten during assignment.
42
+ # @param object [ActiveFedora::Base, Hyrax::Resource] the object to assign an identifier.
43
+ #
44
+ # @return [ActiveFedora::Base, Hyrax::Resource] object
45
+ def assign_for(object:, attribute: :identifier)
46
+ record = registrar.register!(object: object)
47
+ object.public_send("#{attribute}=".to_sym, [record.identifier])
48
+ object
49
+ end
50
+
51
+ ##
52
+ # Assigns an identifier and saves the object.
53
+ #
54
+ # @see #assign_for
55
+ def assign_for!(object:, attribute: :identifier)
56
+ assign_for(object: object, attribute: attribute).save!
57
+ object
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Identifier
4
+ class Registrar
5
+ class << self
6
+ ##
7
+ # @param type [Symbol]
8
+ # @param opts [Hash]
9
+ # @option opts [Hyrax::Identifier::Builder] :builder
10
+ #
11
+ # @return [Hyrax::Identifier::Registrar] a registrar for the given type
12
+ def for(type, **opts)
13
+ return Hyrax.config.identifier_registrars[type].new(**opts) if Hyrax.config.identifier_registrars.include?(type)
14
+ raise ArgumentError, "Hyrax::Identifier::Registrar not found to handle #{type}"
15
+ end
16
+ end
17
+
18
+ ##
19
+ # @!attribute builder [rw]
20
+ # @return [Hyrax::Identifier::Builder]
21
+ attr_accessor :builder
22
+
23
+ ##
24
+ # @param builder [Hyrax::Identifier::Builder]
25
+ def initialize(builder:)
26
+ @builder = builder
27
+ end
28
+
29
+ ##
30
+ # @abstract
31
+ #
32
+ # @param object [#id]
33
+ #
34
+ # @return [#identifier]
35
+ # @raise [NotImplementedError] when the method is abstract
36
+ def register!(*)
37
+ raise NotImplementedError
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ ##
5
+ # A class responsible for converting a Hyrax::Work like thing into a IIIF
6
+ # manifest.
7
+ #
8
+ # @see !{.as_json}
9
+ class ManifestBuilderService
10
+ ##
11
+ # @api public
12
+ #
13
+ # @param presenter [Hyrax::WorkShowPresenter] the work presenter from which
14
+ # we'll build a manifest.
15
+ # @param iiif_manifest_factory [Class] a class that initializes with presenter
16
+ # object and returns an object that responds to `#to_h`
17
+ #
18
+ # @note While the :presenter may be a Hyrax::WorkShowPresenter it is likely
19
+ # defined by Hyrax::WorksControllerBehavior.show_presenter
20
+ #
21
+ # @return [Hash] a Ruby hash representation of a IIIF manifest document
22
+ #
23
+ # @see Hyrax::WorksControllerBehavior
24
+ def self.manifest_for(presenter:, iiif_manifest_factory: ::IIIFManifest::ManifestFactory)
25
+ new(iiif_manifest_factory: iiif_manifest_factory)
26
+ .manifest_for(presenter: presenter)
27
+ end
28
+
29
+ ##
30
+ # @!attribute [r] manifest_factory
31
+ # @return [#to_h]
32
+ attr_reader :manifest_factory
33
+
34
+ ##
35
+ # @api public
36
+ #
37
+ # @param iiif_manifest_factory [Class] a class that initializes with presenter
38
+ # object and returns an object that responds to `#to_h`
39
+ def initialize(iiif_manifest_factory: ::IIIFManifest::ManifestFactory)
40
+ @manifest_factory = iiif_manifest_factory
41
+ end
42
+
43
+ ##
44
+ # @api public
45
+ #
46
+ # @param presenter [Hyrax::WorkShowPresenter]
47
+ #
48
+ # @return [Hash] a Ruby hash representation of a IIIF manifest document
49
+ def manifest_for(presenter:)
50
+ sanitized_manifest(presenter: presenter)
51
+ end
52
+
53
+ private
54
+
55
+ ##
56
+ # @api private
57
+ # @param presenter [Hyrax::WorkShowPresenter]
58
+ def sanitized_manifest(presenter:)
59
+ # ::IIIFManifest::ManifestBuilder#to_h returns a
60
+ # IIIFManifest::ManifestBuilder::IIIFManifest, not a Hash.
61
+ # to get a Hash, we have to call its #to_json, then parse.
62
+ #
63
+ # wild times. maybe there's a better way to do this with the
64
+ # ManifestFactory interface?
65
+ manifest = manifest_factory.new(presenter).to_h
66
+ hash = JSON.parse(manifest.to_json)
67
+
68
+ hash['label'] = sanitize_value(hash['label']) if hash.key?('label')
69
+ hash['description'] = Array(hash['description'])&.collect { |elem| sanitize_value(elem) } if hash.key?('description')
70
+
71
+ hash['sequences']&.each do |sequence|
72
+ sequence['canvases']&.each do |canvas|
73
+ canvas['label'] = sanitize_value(canvas['label'])
74
+ end
75
+ end
76
+
77
+ hash
78
+ end
79
+
80
+ ##
81
+ # @api private
82
+ # @param [#to_s] text
83
+ # @return [String] a sanitized verison of `text`
84
+ def sanitize_value(text)
85
+ Loofah.fragment(text.to_s).scrub!(:prune).to_s
86
+ end
87
+ end
88
+ end
@@ -22,5 +22,14 @@ module Hyrax
22
22
  return if version.nil?
23
23
  VersionCommitter.create(version_id: version.uri, committer_login: user_key)
24
24
  end
25
+
26
+ # @param [ActiveFedora::File | Hyrax::FileMetadata] content
27
+ def self.versioned_file_id(file)
28
+ versions = file.versions.all
29
+
30
+ return ActiveFedora::Base.uri_to_id(versions.last.uri) if versions.present?
31
+
32
+ file.id
33
+ end
25
34
  end
26
35
  end
@@ -19,7 +19,7 @@
19
19
  <p class="switch-upload-type">To create a separate work for each of the files, go to <%= link_to "Batch upload", hyrax.new_batch_upload_path %></p>
20
20
  <% end %>
21
21
  <% end %>
22
- <%= render 'hyrax/base/guts4form', f: f %>
22
+ <%= render 'hyrax/base/guts4form', f: f, tabs: form_tabs_for(form: f.object) %>
23
23
  <% end %>
24
24
 
25
25
  <script type="text/javascript">
@@ -24,7 +24,7 @@ HTML Properties:
24
24
  </div>
25
25
  <div class="message has-warning hidden"></div>
26
26
  <div style="margin-top: 10px">
27
- <%= link_to t('.attach_new_work'), polymorphic_path([main_app, :new, :hyrax, :parent, curation_concern.model_name.singular], parent_id: curation_concern.id), target: "_blank", class: 'btn btn-primary' %>
27
+ <%= link_to t('.attach_new_work'), polymorphic_path([main_app, :new, :hyrax, :parent, curation_concern.model_name.singular.to_sym], parent_id: curation_concern.id), target: "_blank", class: 'btn btn-primary' %>
28
28
  </div>
29
29
  <table class="table table-striped">
30
30
  <caption><%= t('.caption') %></caption>
@@ -26,6 +26,10 @@
26
26
  <%= f.input :on_behalf_of, collection: current_user.can_make_deposits_for.map(&:user_key), prompt: "Yourself" %>
27
27
  </div>
28
28
  <% end %>
29
+
30
+ <% form_progress_sections_for(form: f.object).each do |section| %>
31
+ <%= render "form_progress_#{section}", f: f %>
32
+ <% end %>
29
33
  </div>
30
34
  <div class="panel-footer text-center">
31
35
  <% if ::Flipflop.show_deposit_agreement? %>
@@ -1,3 +1,5 @@
1
1
  <%= f.full_error(:visibility) %>
2
2
  <%= f.full_error(:embargo_release_date) %>
3
3
  <%= f.full_error(:visibility_after_embargo) %>
4
+ <%= f.full_error(:lease_expiration_date) %>
5
+ <%= f.full_error(:visibility_after_lease) %>
@@ -1,5 +1,11 @@
1
1
  <% # we will yield to content_for for each tab, e.g. :files_tab %>
2
- <% tabs ||= %w[metadata files relationships] # default tab order %>
2
+ <%# Not passing tabs local param to this partial is deprecated and the tabs param will be required in Hyrax 3.0 %>
3
+ <% unless defined?(tabs) %>
4
+ <% Deprecation.warn(self, "Passing the tabs local param to the _guts4form partial will be required in Hyrax 3.0. " \
5
+ "Consider removing overriding view partials and customizing the tab list " \
6
+ "by overriding the new form_tabs_for(form:) helper.") %>
7
+ <% tabs = form_tabs_for(form: f.object) # default tab order %>
8
+ <% end %>
3
9
  <div class="row">
4
10
  <div class="col-xs-12 col-sm-8">
5
11
  <div class="panel panel-default tabs" role="main">
@@ -15,7 +15,7 @@
15
15
  <ul class="dropdown-menu">
16
16
  <% presenter.valid_child_concerns.each do |concern| %>
17
17
  <li>
18
- <%= link_to "Attach #{concern.human_readable_type}", polymorphic_path([main_app, :new, :hyrax, :parent, concern.model_name.singular], parent_id: presenter.id) %>
18
+ <%= link_to "Attach #{concern.human_readable_type}", polymorphic_path([main_app, :new, :hyrax, :parent, concern.model_name.singular.to_sym], parent_id: presenter.id) %>
19
19
  </li>
20
20
  <% end %>
21
21
  </ul>
@@ -7,10 +7,10 @@
7
7
  <% provide :files_tab do %>
8
8
  <p class="instructions"><%= t("hyrax.batch_uploads.files.instructions") %></p>
9
9
  <p class="switch-upload-type"><%= t("hyrax.batch_uploads.files.upload_type_instructions") %>
10
- <%= link_to t("hyrax.batch_uploads.files.button_label"), [main_app, :new, Hyrax.primary_work_type.model_name.singular_route_key] %>
10
+ <%= link_to t("hyrax.batch_uploads.files.button_label"), [main_app, :new, Hyrax.primary_work_type.model_name.singular_route_key.to_sym] %>
11
11
  </p>
12
12
  <% end %>
13
- <%= render 'hyrax/base/guts4form', f: f, tabs: %w[files metadata relationships] %>
13
+ <%= render 'hyrax/base/guts4form', f: f, tabs: form_tabs_for(form: f.object) %>
14
14
  <%= f.hidden_field :payload_concern, value: @form.payload_concern %>
15
15
  <% end %>
16
16
 
@@ -1,6 +1,6 @@
1
1
  <% menu = Hyrax::MenuPresenter.new(self) %>
2
2
  <div class="sidebar-toggle"><span class="fa fa-chevron-circle-right"></span></div>
3
- <nav>
3
+ <nav aria-label="sidebar-nav">
4
4
  <ul class="nav nav-pills nav-stacked">
5
5
  <li>
6
6
  <div class="profile">
data/config/features.rb CHANGED
@@ -42,4 +42,8 @@ Flipflop.configure do
42
42
  feature :hide_users_list,
43
43
  default: true,
44
44
  description: "Do not show users list unless user has authenticated."
45
+
46
+ feature :cache_work_iiif_manifest,
47
+ default: false,
48
+ description: "Use Rails.cache to cache the JSON document for IIIF manifests"
45
49
  end
data/hyrax.gemspec CHANGED
@@ -39,6 +39,7 @@ SUMMARY
39
39
  spec.add_dependency 'browse-everything', '>= 0.16'
40
40
  spec.add_dependency 'carrierwave', '~> 1.0'
41
41
  spec.add_dependency 'clipboard-rails', '~> 1.5'
42
+ spec.add_dependency 'draper', '~> 4.0'
42
43
  spec.add_dependency 'dry-equalizer', '~> 0.2'
43
44
  spec.add_dependency 'dry-struct', '>= 0.1', '< 2.0'
44
45
  spec.add_dependency 'dry-transaction', '~> 0.11'
@@ -49,7 +50,7 @@ SUMMARY
49
50
  spec.add_dependency 'font-awesome-rails', '~> 4.2'
50
51
  spec.add_dependency 'hydra-derivatives', '~> 3.3'
51
52
  spec.add_dependency 'hydra-editor', '>= 3.3', '< 6.0'
52
- spec.add_dependency 'hydra-head', '>= 10.6.1'
53
+ spec.add_dependency 'hydra-head', '>= 10.6.1', '< 12'
53
54
  spec.add_dependency 'hydra-works', '>= 0.16', '< 2.0'
54
55
  spec.add_dependency 'iiif_manifest', '>= 0.3', '< 0.6'
55
56
  spec.add_dependency 'jquery-datatables-rails', '~> 3.4'
@@ -67,7 +68,7 @@ SUMMARY
67
68
  spec.add_dependency 'posix-spawn'
68
69
  spec.add_dependency 'power_converter', '~> 0.1', '>= 0.1.2'
69
70
  spec.add_dependency 'pul_uv_rails', '~> 2.0'
70
- spec.add_dependency 'qa', '~> 2.0' # questioning_authority
71
+ spec.add_dependency 'qa', '>= 2.0', '< 6.0' # questioning_authority
71
72
  spec.add_dependency 'rails_autolink', '~> 1.1'
72
73
  spec.add_dependency 'rdf-rdfxml' # controlled vocabulary importer
73
74
  spec.add_dependency 'rdf-vocab', '< 3.1.5'
@@ -20,6 +20,10 @@ class CatalogController < ApplicationController
20
20
  config.view.gallery.partials = [:index_header, :index]
21
21
  config.view.slideshow.partials = [:index]
22
22
 
23
+ # Because too many times on Samvera tech people raise a problem regarding a failed query to SOLR.
24
+ # Often, it's because they inadvertantly exceeded the character limit of a GET request.
25
+ config.http_method = :post
26
+
23
27
  ## Default parameters to send to solr for all search-like requests. See also SolrHelper#solr_search_params
24
28
  config.default_solr_params = {
25
29
  qt: "search",
@@ -266,6 +266,11 @@ Hyrax.config do |config|
266
266
  # mount point.
267
267
  #
268
268
  # config.whitelisted_ingest_dirs = []
269
+
270
+ ## Remote identifiers configuration
271
+ # Add registrar implementations by uncommenting and adding to the hash below.
272
+ # See app/services/hyrax/identifier/registrar.rb for the registrar interface
273
+ # config.identifier_registrars = {}
269
274
  end
270
275
 
271
276
  Date::DATE_FORMATS[:standard] = "%m/%d/%Y"
@@ -51,7 +51,7 @@ es:
51
51
  suffix: "@example.org"
52
52
  footer:
53
53
  copyright_html: "<strong>Copyright &copy; 2018 Samvera</strong> bajo licencia de Apache, Version 2.0"
54
- service_html: Un servicio de <a href="http://samvera.org/" class="navbar-link" target="_blank">Samvera/a>.
54
+ service_html: Un servicio de <a href="http://samvera.org/" class="navbar-link" target="_blank">Samvera</a>.
55
55
  institution_name: institución
56
56
  institution_name_full: El nombre de la institución
57
57
  product_name: Hyrax
data/lib/hyrax.rb CHANGED
@@ -8,6 +8,7 @@ require 'font-awesome-rails'
8
8
  require 'tinymce-rails'
9
9
  require 'blacklight'
10
10
  require 'blacklight/gallery'
11
+ require 'iiif_manifest'
11
12
  require 'noid-rails'
12
13
  require 'hydra/head'
13
14
  require 'hydra-editor'
@@ -431,6 +431,16 @@ module Hyrax
431
431
  end
432
432
  attr_writer :iiif_metadata_fields
433
433
 
434
+ # Duration in which we should cache the generated IIIF manifest.
435
+ # Default is 30 days (in seconds).
436
+ #
437
+ # @return [Integer] number of seconds
438
+ # @see https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-fetch
439
+ def iiif_manifest_cache_duration
440
+ @iiif_manifest_cache_duration ||= 30.days.to_i
441
+ end
442
+ attr_writer :iiif_manifest_cache_duration
443
+
434
444
  # Should a button with "Share my work" show on the front page to users who are not logged in?
435
445
  attr_writer :display_share_button_when_not_logged_in
436
446
  def display_share_button_when_not_logged_in?
@@ -469,10 +479,14 @@ module Hyrax
469
479
  attr_writer :translate_uri_to_id
470
480
 
471
481
  def translate_uri_to_id
472
- @translate_uri_to_id ||= lambda do |uri|
473
- baseparts = 2 + [(::Noid::Rails.config.template.gsub(/\.[rsz]/, '').length.to_f / 2).ceil, 4].min
474
- uri.to_s.sub("#{ActiveFedora.fedora.host}#{ActiveFedora.fedora.base_path}", '').split('/', baseparts).last
475
- end
482
+ @translate_uri_to_id ||=
483
+ begin
484
+ baseparts = 2 + [(::Noid::Rails.config.template.gsub(/\.[rsz]/, '').length.to_f / 2).ceil, 4].min
485
+
486
+ lambda do |uri|
487
+ uri.to_s.split(ActiveFedora.fedora.base_path).last.split('/', baseparts).last
488
+ end
489
+ end
476
490
  end
477
491
 
478
492
  attr_writer :translate_id_to_uri
@@ -514,6 +528,11 @@ module Hyrax
514
528
  ->(id:, extent:) { Samvera::NestingIndexer.reindex_relationships(id: id, extent: extent) }
515
529
  end
516
530
 
531
+ attr_writer :identifier_registrars
532
+ def identifier_registrars
533
+ @identifier_registrars ||= {}
534
+ end
535
+
517
536
  private
518
537
 
519
538
  # @param [Symbol, #to_s] model_name - symbol representing the model