bulkrax 5.2.1 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a811bef32bb83948c7ea8fe9cbc3aead7e9fa3e09f88fe6d3ec45e4cdef7461
4
- data.tar.gz: 253314c9d1a35505b50ad70e6fb0f9dec0f5331a55cdee6555cb137fff697e08
3
+ metadata.gz: 1bdc3299c331e345417117694e5da25031db99e181701d6b52db630b5c297470
4
+ data.tar.gz: 105119be729e2ea30a5fceb5b2f47557d5edb933b05663b7905e49665ceb3dfa
5
5
  SHA512:
6
- metadata.gz: 3c8281a0c12778d8db9a6e3e8dc39a9c591e2d81e9d668b9b52da400b65e48012f40b51b86aa75182fc26b7bf8d20e761af7a00b6c1c798df34dba35da7890fc
7
- data.tar.gz: d71d16a09cfa1d9b0c3954bf2be9f2778bacdaa98e0a99d301c0b0762d8be04b0213f99463e05e6aab33321b64a9284551b32a1435e97569097e32c23e74f95d
6
+ metadata.gz: 027d2ea6005df040daebf48bf431e471b1386141e2e13ae5de149f25125f8d2306576accb8300869ab16fb500f5f2b4d3f88ea28e299648bf588bf5f4d9c8264
7
+ data.tar.gz: a3924a66c3ca3d8e3a243573cbb44ca41f11f687ca02f17bbfad6403883a516b917248cf0f807088e351cc206a6b18d99d9434643ccebebd06dbb126319c9445
data/README.md CHANGED
@@ -17,6 +17,7 @@ And then execute:
17
17
  ```bash
18
18
  $ bundle install
19
19
  $ rails generate bulkrax:install
20
+ $ rails db:migrate
20
21
  ```
21
22
 
22
23
  If using Sidekiq, set up queues for `import` and `export`.
@@ -32,6 +33,7 @@ gem 'bulkrax'
32
33
  And then execute:
33
34
  ```bash
34
35
  $ bundle install
36
+ $ rails db:migrate
35
37
  ```
36
38
 
37
39
  Mount the engine in your routes file
@@ -61,7 +63,7 @@ If using Sidekiq, set up queues for `import` and `export`.
61
63
  *= require 'bulkrax/application'
62
64
  ```
63
65
 
64
- You'll want to add an intializer to configure the importer to your needs:
66
+ You'll want to add an initializer to configure the importer to your needs:
65
67
 
66
68
  ```ruby
67
69
  # config/initializers/bulkrax.rb
@@ -112,13 +114,13 @@ An Import needs to know what Work Type to create. The importer looks for:
112
114
 
113
115
  If it does not find either of these, or the data they contain is not a valid Work Type in the repository, the `default_work_type` will be used.
114
116
 
115
- The install generator sets `default_work_type` to the first Work Type returned by `Hyrax.config.curation_concerns` but this can be overriden by setting `default_work_type` in `config/initializer/bulkrax.rb` as shown above.
117
+ The install generator sets `default_work_type` to the first Work Type returned by `Hyrax.config.curation_concerns` (stringified), but this can be overwritten by setting `default_work_type` in `config/initializer/bulkrax.rb` as shown above.
116
118
 
117
119
  ## Configuring Field Mapping
118
120
 
119
121
  It's unlikely that the incoming import data has fields that exactly match those in your repository. Field mappings allow you to tell bulkrax how to map field in the incoming data to a field in your application.
120
122
 
121
- By default, a mapping for the OAI parser has been added to map standard oai_dc fields to Hyrax basic_metadata. The other parsers have no default mapping, and will map any incoming fields to Hyrax properties with the same name. Configurations can be added in `config/intializers/bulkrax.rb`
123
+ By default, a mapping for the OAI parser has been added to map standard oai_dc fields to Hyrax basic_metadata. The other parsers have no default mapping, and will map any incoming fields to Hyrax properties with the same name. Configurations can be added in `config/initializers/bulkrax.rb`
122
124
 
123
125
  Configuring field mappings is documented in the [Bulkrax Configuration Guide](https://github.com/samvera-labs/bulkrax/wiki/Configuring-Bulkrax).
124
126
 
@@ -176,7 +178,7 @@ To edit an importer or exporter, select the edit icon (pencil) and complete the
176
178
  To delete an importer or exporter, select the delete (x) icon.
177
179
 
178
180
  ### Downloading an export
179
- Once your the exporter has run, a download icon will apear on the exporters menu page.
181
+ Once your the exporter has run, a download icon will appear on the exporters menu page.
180
182
 
181
183
  ## Contributing
182
184
  If you're working on a PR for this project, create a feature branch off of `main`.
@@ -0,0 +1,9 @@
1
+ <% unless defined?(::Hyku) %>
2
+ // enables the tabs in the importers/exporters pages.
3
+ $(document).ready(function() {
4
+ $('.nav-tabs a').click(function (e) {
5
+ e.preventDefault();
6
+ $(this).tab('show');
7
+ });
8
+ });
9
+ <% end %>
@@ -13,7 +13,8 @@ module Bulkrax
13
13
 
14
14
  # GET /exporters
15
15
  def index
16
- @exporters = Exporter.all
16
+ # NOTE: We're paginating this in the browser.
17
+ @exporters = Exporter.order(created_at: :desc).all
17
18
 
18
19
  add_exporter_breadcrumbs if defined?(::Hyrax)
19
20
  end
@@ -20,7 +20,8 @@ module Bulkrax
20
20
 
21
21
  # GET /importers
22
22
  def index
23
- @importers = Importer.all
23
+ # NOTE: We're paginating this in the browser.
24
+ @importers = Importer.order(created_at: :desc).all
24
25
  if api_request?
25
26
  json_response('index')
26
27
  elsif defined?(::Hyrax)
@@ -40,7 +40,7 @@ module Bulkrax
40
40
 
41
41
  include DynamicRecordLookup
42
42
 
43
- queue_as :import
43
+ queue_as Bulkrax.config.ingest_queue_name
44
44
 
45
45
  # @param parent_identifier [String] Work/Collection ID or Bulkrax::Entry source_identifiers
46
46
  # @param importer_run [Bulkrax::ImporterRun] current importer run (needed to properly update counters)
@@ -61,38 +61,82 @@ module Bulkrax
61
61
  number_of_successes = 0
62
62
  number_of_failures = 0
63
63
  errors = []
64
-
65
- ActiveRecord::Base.uncached do
66
- Bulkrax::PendingRelationship.where(parent_id: parent_identifier, importer_run_id: importer_run_id)
67
- .ordered.find_each do |rel|
68
- process(relationship: rel, importer_run_id: importer_run_id, parent_record: parent_record, ability: ability)
69
- number_of_successes += 1
70
- rescue => e
71
- number_of_failures += 1
72
- errors << e
64
+ @parent_record_members_added = false
65
+ @child_members_added = []
66
+
67
+ if parent_record
68
+ conditionally_acquire_lock_for(parent_record.id) do
69
+ ActiveRecord::Base.uncached do
70
+ Bulkrax::PendingRelationship.where(parent_id: parent_identifier, importer_run_id: importer_run_id)
71
+ .ordered.find_each do |rel|
72
+ process(relationship: rel, importer_run_id: importer_run_id, parent_record: parent_record, ability: ability)
73
+ number_of_successes += 1
74
+ rescue => e
75
+ number_of_failures += 1
76
+ errors << e
77
+ end
78
+ end
79
+
80
+ # save record if members were added
81
+ if @parent_record_members_added
82
+ parent_record.save!
83
+ # Ensure that the new relationship gets indexed onto the children
84
+ @child_members_added.each(&:update_index)
85
+ end
73
86
  end
87
+ else
88
+ # In moving the check of the parent record "up" we've exposed a hidden reporting foible.
89
+ # Namely we were reporting one error per child record when the parent record was itself
90
+ # unavailable.
91
+ #
92
+ # We have chosen not to duplicate that "number of errors" as it does not seem like the
93
+ # correct pattern for reporting a singular error (the previous pattern being one error per
94
+ # child who's parent is not yet created).
95
+ number_of_failures = 1
96
+ errors = ["Parent record not yet available for creating relationships with children records."]
74
97
  end
75
98
 
76
- # save record if members were added
77
- parent_record.save! if @parent_record_members_added
78
-
79
- # rubocop:disable Rails/SkipsModelValidations
80
99
  if errors.present?
100
+ # rubocop:disable Rails/SkipsModelValidations
81
101
  importer_run.increment!(:failed_relationships, number_of_failures)
102
+ # rubocop:enable Rails/SkipsModelValidations
103
+
82
104
  parent_entry&.set_status_info(errors.last, importer_run)
83
105
 
84
106
  # TODO: This can create an infinite job cycle, consider a time to live tracker.
85
107
  reschedule({ parent_identifier: parent_identifier, importer_run_id: importer_run_id })
86
108
  return false # stop current job from continuing to run after rescheduling
87
109
  else
110
+ # rubocop:disable Rails/SkipsModelValidations
88
111
  Bulkrax::ImporterRun.find(importer_run_id).increment!(:processed_relationships, number_of_successes)
112
+ # rubocop:enable Rails/SkipsModelValidations
89
113
  end
90
- # rubocop:enable Rails/SkipsModelValidations
91
114
  end
92
115
  # rubocop:enable Metrics/MethodLength
93
116
 
94
117
  private
95
118
 
119
+ ##
120
+ # We can use Hyrax's lock manager when we have one available.
121
+ if defined?(::Hyrax)
122
+ include Hyrax::Lockable
123
+
124
+ def conditionally_acquire_lock_for(*args, &block)
125
+ if Bulkrax.use_locking?
126
+ acquire_lock_for(*args, &block)
127
+ else
128
+ yield
129
+ end
130
+ end
131
+ else
132
+ # Otherwise, we're providing no meaningful lock manager at this time.
133
+ def acquire_lock_for(*)
134
+ yield
135
+ end
136
+
137
+ alias conditionally_acquire_lock_for acquire_lock_for
138
+ end
139
+
96
140
  def process(relationship:, importer_run_id:, parent_record:, ability:)
97
141
  raise "#{relationship} needs a child to create relationship" if relationship.child_id.nil?
98
142
  raise "#{relationship} needs a parent to create relationship" if relationship.parent_id.nil?
@@ -124,8 +168,7 @@ module Bulkrax
124
168
 
125
169
  parent_record.ordered_members << child_record
126
170
  @parent_record_members_added = true
127
- # TODO: Do we need to save the child record?
128
- child_record.save!
171
+ @child_members_added << child_record
129
172
  end
130
173
 
131
174
  def reschedule(parent_identifier:, importer_run_id:)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Bulkrax
4
4
  class DeleteJob < ApplicationJob
5
- queue_as :import
5
+ queue_as Bulkrax.config.ingest_queue_name
6
6
 
7
7
  # rubocop:disable Rails/SkipsModelValidations
8
8
  def perform(entry, importer_run)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Bulkrax
4
4
  class DownloadCloudFileJob < ApplicationJob
5
- queue_as :import
5
+ queue_as Bulkrax.config.ingest_queue_name
6
6
 
7
7
  # Retrieve cloud file and write to the imports directory
8
8
  # Note: if using the file system, the mounted directory in
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Bulkrax
4
4
  class ImportCollectionJob < ApplicationJob
5
- queue_as :import
5
+ queue_as Bulkrax.config.ingest_queue_name
6
6
 
7
7
  # rubocop:disable Rails/SkipsModelValidations
8
8
  def perform(*args)
@@ -6,7 +6,7 @@ module Bulkrax
6
6
  class ImportFileSetJob < ApplicationJob
7
7
  include DynamicRecordLookup
8
8
 
9
- queue_as :import
9
+ queue_as Bulkrax.config.ingest_queue_name
10
10
 
11
11
  attr_reader :importer_run_id
12
12
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Bulkrax
4
4
  class ImportWorkJob < ApplicationJob
5
- queue_as :import
5
+ queue_as Bulkrax.config.ingest_queue_name
6
6
 
7
7
  # rubocop:disable Rails/SkipsModelValidations
8
8
  #
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Bulkrax
4
4
  class ImporterJob < ApplicationJob
5
- queue_as :import
5
+ queue_as Bulkrax.config.ingest_queue_name
6
6
 
7
7
  def perform(importer_id, only_updates_since_last_import = false)
8
8
  importer = Importer.find(importer_id)
@@ -26,7 +26,8 @@ module Bulkrax
26
26
 
27
27
  # @result will evaluate to an empty string for nil content values
28
28
  @result = content.to_s.gsub(/\s/, ' ').strip # remove any line feeds and tabs
29
- process_split if @result.present?
29
+ # blank needs to be based to split, only skip nil
30
+ process_split unless @result.nil?
30
31
  @result = @result[0] if @result.is_a?(Array) && @result.size == 1
31
32
  process_parse
32
33
  return @result
@@ -36,8 +37,8 @@ module Bulkrax
36
37
  if self.split.is_a?(TrueClass)
37
38
  @result = @result.split(Bulkrax.multi_value_element_split_on)
38
39
  elsif self.split
39
- result = @result.split(Regexp.new(self.split))
40
- @result = result.map(&:strip)
40
+ @result = @result.split(Regexp.new(self.split))
41
+ @result = @result.map(&:strip).select(&:present?)
41
42
  end
42
43
  end
43
44
 
@@ -20,7 +20,7 @@ module Bulkrax
20
20
  raise StandardError, 'CSV path empty' if path.blank?
21
21
  options = {
22
22
  headers: true,
23
- header_converters: ->(h) { h.to_sym },
23
+ header_converters: ->(h) { h.to_s.strip.to_sym },
24
24
  encoding: 'utf-8'
25
25
  }.merge(csv_read_data_options)
26
26
 
@@ -243,20 +243,17 @@ module Bulkrax
243
243
  object_metadata(Array.wrap(data))
244
244
  end
245
245
 
246
- def build_value(key, value)
247
- return unless hyrax_record.respond_to?(key.to_s)
246
+ def build_value(property_name, mapping_config)
247
+ return unless hyrax_record.respond_to?(property_name.to_s)
248
248
 
249
- data = hyrax_record.send(key.to_s)
250
- if data.is_a?(ActiveTriples::Relation)
251
- if value['join']
252
- self.parsed_metadata[key_for_export(key)] = data.map { |d| prepare_export_data(d) }.join(Bulkrax.multi_value_element_join_on).to_s
253
- else
254
- data.each_with_index do |d, i|
255
- self.parsed_metadata["#{key_for_export(key)}_#{i + 1}"] = prepare_export_data(d)
256
- end
257
- end
249
+ data = hyrax_record.send(property_name.to_s)
250
+
251
+ if mapping_config['join'] || !data.is_a?(Enumerable)
252
+ self.parsed_metadata[key_for_export(property_name)] = prepare_export_data_with_join(data)
258
253
  else
259
- self.parsed_metadata[key_for_export(key)] = prepare_export_data(data)
254
+ data.each_with_index do |d, i|
255
+ self.parsed_metadata["#{key_for_export(property_name)}_#{i + 1}"] = prepare_export_data(d)
256
+ end
260
257
  end
261
258
  end
262
259
 
@@ -269,6 +266,14 @@ module Bulkrax
269
266
  "#{unnumbered_key}#{key.sub(clean_key, '')}"
270
267
  end
271
268
 
269
+ def prepare_export_data_with_join(data)
270
+ # Yes...it's possible we're asking to coerce a multi-value but only have a single value.
271
+ return data.to_s unless data.is_a?(Enumerable)
272
+ return "" if data.empty?
273
+
274
+ data.map { |d| prepare_export_data(d) }.join(Bulkrax.multi_value_element_join_on).to_s
275
+ end
276
+
272
277
  def prepare_export_data(datum)
273
278
  if datum.is_a?(ActiveTriples::Resource)
274
279
  datum.to_uri.to_s
@@ -24,13 +24,12 @@ module Bulkrax
24
24
  attr_writer :current_run
25
25
 
26
26
  def self.safe_uri_filename(uri)
27
- uri = URI.parse(uri) unless uri.is_a?(URI)
28
27
  r = Faraday.head(uri.to_s)
29
28
  return CGI.parse(r.headers['content-disposition'])["filename"][0].delete("\"")
30
29
  rescue
31
- filename = File.basename(uri.path)
30
+ filename = File.basename(uri.to_s)
32
31
  filename.delete!('/')
33
- filename.presence || file_set.id
32
+ filename.presence || SecureRandom.uuid
34
33
  end
35
34
 
36
35
  def status
@@ -33,9 +33,9 @@ module Bulkrax
33
33
  model_field_mappings.map(&:to_sym).each do |model_mapping|
34
34
  next unless r.key?(model_mapping)
35
35
 
36
- if r[model_mapping].casecmp('collection').zero?
36
+ if r[model_mapping].strip.casecmp('collection').zero?
37
37
  @collections << r
38
- elsif r[model_mapping].casecmp('fileset').zero?
38
+ elsif r[model_mapping].strip.casecmp('fileset').zero?
39
39
  @file_sets << r
40
40
  else
41
41
  @works << r
@@ -21,6 +21,30 @@ module Bulkrax
21
21
  "Bulkrax::ParserExportRecordSet::#{export_from.classify}".constantize.new(parser: parser)
22
22
  end
23
23
 
24
+ SOLR_QUERY_PAGE_SIZE = 512
25
+
26
+ ##
27
+ # A helper method for handling querying large batches of IDs. By default SOLR has a max of 1024
28
+ # `OR` clauses per query. This method helps chunk large sets of IDs into batches.
29
+ #
30
+ # @param array [Array<Object>]
31
+ # @param page_size [Integer]
32
+ # @yieldparam [Array<Object>] slice of the original arrays which are yielded. The results of
33
+ # the yield are merged into the return value.
34
+ #
35
+ # @return [Array<Object>]
36
+ #
37
+ # @see https://github.com/samvera-labs/bulkrax/issues/776
38
+ def self.in_batches(array, page_size: SOLR_QUERY_PAGE_SIZE)
39
+ array = Array.wrap(array)
40
+ return [] if array.empty?
41
+ results = []
42
+ array.each_slice(page_size) do |slice|
43
+ results += Array.wrap(yield(slice))
44
+ end
45
+ results
46
+ end
47
+
24
48
  # @abstract
25
49
  #
26
50
  # @note This has {#each} and {#count} but is not an Enumerable. But because it has these two
@@ -36,6 +60,7 @@ module Bulkrax
36
60
  delegate :limit_reached?, :work_entry_class, :collection_entry_class, :file_set_entry_class, :importerexporter, to: :parser
37
61
  private :limit_reached?, :work_entry_class, :collection_entry_class, :file_set_entry_class, :importerexporter
38
62
 
63
+ ##
39
64
  # @return [Integer]
40
65
  def count
41
66
  sum = works.count + collections.count + file_sets.count
@@ -44,6 +69,7 @@ module Bulkrax
44
69
  return sum
45
70
  end
46
71
 
72
+ ##
47
73
  # Yield first the works, then collections, then file sets. Once we've yielded as many times
48
74
  # as the parser's limit, we break the iteration and return.
49
75
  #
@@ -134,8 +160,6 @@ module Bulkrax
134
160
  end
135
161
  end
136
162
 
137
- SOLR_QUERY_PAGE_SIZE = 512
138
-
139
163
  # @note In most cases, when we don't have any candidate file sets, there is no need to query SOLR.
140
164
  #
141
165
  # @see Bulkrax::ParserExportRecordSet::Importer#file_sets
@@ -148,20 +172,14 @@ module Bulkrax
148
172
  # @see https://github.com/scientist-softserv/britishlibrary/issues/289
149
173
  # @see https://github.com/samvera/hyrax/blob/64c0bbf0dc0d3e1b49f040b50ea70d177cc9d8f6/app/indexers/hyrax/work_indexer.rb#L15-L18
150
174
  def file_sets
151
- @file_sets ||= if candidate_file_set_ids.empty?
152
- []
153
- else
154
- results = []
155
- candidate_file_set_ids.each_slice(SOLR_QUERY_PAGE_SIZE) do |ids|
156
- fsq = "has_model_ssim:#{Bulkrax.file_model_class} AND id:(\"" + ids.join('" OR "') + "\")"
157
- fsq += extra_filters if extra_filters.present?
158
- results += ActiveFedora::SolrService.query(
159
- fsq,
160
- { fl: "id", method: :post, rows: ids.size }
161
- )
162
- end
163
- results
164
- end
175
+ @file_sets ||= ParserExportRecordSet.in_batches(candidate_file_set_ids) do |batch_of_ids|
176
+ fsq = "has_model_ssim:#{Bulkrax.file_model_class} AND id:(\"" + batch_of_ids.join('" OR "') + "\")"
177
+ fsq += extra_filters if extra_filters.present?
178
+ ActiveFedora::SolrService.query(
179
+ fsq,
180
+ { fl: "id", method: :post, rows: batch_of_ids.size }
181
+ )
182
+ end
165
183
  end
166
184
 
167
185
  def solr_name(base_name)
@@ -227,32 +245,34 @@ module Bulkrax
227
245
  end
228
246
  end
229
247
 
230
- def works_query_kwargs
231
- query_kwargs.merge(
232
- fq: [
233
- %(#{solr_name(work_identifier)}:("#{complete_entry_identifiers.join('" OR "')}")),
234
- "has_model_ssim:(#{Bulkrax.curation_concerns.join(' OR ')})"
235
- ],
236
- fl: 'id'
237
- )
238
- end
239
-
240
- def works_query
241
- extra_filters.to_s
242
- end
243
-
244
- def collections_query_kwargs
245
- query_kwargs.merge(
246
- fq: [
247
- %(#{solr_name(work_identifier)}:("#{complete_entry_identifiers.join('" OR "')}")),
248
- "has_model_ssim:Collection"
249
- ],
250
- fl: 'id'
251
- )
248
+ def works
249
+ @works ||= ParserExportRecordSet.in_batches(complete_entry_identifiers) do |ids|
250
+ ActiveFedora::SolrService.query(
251
+ extra_filters.to_s,
252
+ **query_kwargs.merge(
253
+ fq: [
254
+ %(#{solr_name(work_identifier)}:("#{ids.join('" OR "')}")),
255
+ "has_model_ssim:(#{Bulkrax.curation_concerns.join(' OR ')})"
256
+ ],
257
+ fl: 'id'
258
+ )
259
+ )
260
+ end
252
261
  end
253
262
 
254
- def collections_query
255
- "has_model_ssim:Collection #{extra_filters}"
263
+ def collections
264
+ @collections ||= ParserExportRecordSet.in_batches(complete_entry_identifiers) do |ids|
265
+ ActiveFedora::SolrService.query(
266
+ "has_model_ssim:Collection #{extra_filters}",
267
+ **query_kwargs.merge(
268
+ fq: [
269
+ %(#{solr_name(work_identifier)}:("#{ids.join('" OR "')}")),
270
+ "has_model_ssim:Collection"
271
+ ],
272
+ fl: "id"
273
+ )
274
+ )
275
+ end
256
276
  end
257
277
 
258
278
  # This is an exception; we don't know how many candidate file sets there might be. So we will instead
@@ -260,21 +280,18 @@ module Bulkrax
260
280
  #
261
281
  # @see Bulkrax::ParserExportRecordSet::Base#file_sets
262
282
  def file_sets
263
- @file_sets ||= ActiveFedora::SolrService.query(file_sets_query, **file_sets_query_kwargs)
264
- end
265
-
266
- def file_sets_query_kwargs
267
- query_kwargs.merge(
268
- fq: [
269
- %(#{solr_name(work_identifier)}:("#{complete_entry_identifiers.join('" OR "')}")),
270
- "has_model_ssim:#{Bulkrax.file_model_class}"
271
- ],
272
- fl: 'id'
273
- )
274
- end
275
-
276
- def file_sets_query
277
- extra_filters
283
+ @file_sets ||= ParserExportRecordSet.in_batches(complete_entry_identifiers) do |ids|
284
+ ActiveFedora::SolrService.query(
285
+ extra_filters,
286
+ query_kwargs.merge(
287
+ fq: [
288
+ %(#{solr_name(work_identifier)}:("#{ids.join('" OR "')}")),
289
+ "has_model_ssim:#{Bulkrax.file_model_class}"
290
+ ],
291
+ fl: 'id'
292
+ )
293
+ )
294
+ end
278
295
  end
279
296
  end
280
297
  end
@@ -10,7 +10,9 @@
10
10
  <%= render 'form', importer: @importer, form: form %>
11
11
  <div class="panel-footer">
12
12
  <div class='pull-right'>
13
- <%= link_to 'Update Importer', '#bulkraxModal', class: "btn btn-primary", data: { toggle: 'modal' } %>
13
+ <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#bulkraxModal">
14
+ Update Importer
15
+ </button>
14
16
  <%= render 'edit_form_buttons', form: form %>
15
17
  <% cancel_path = form.object.persisted? ? importer_path(form.object) : importers_path %>
16
18
  | <%= link_to t('.cancel'), cancel_path, class: 'btn btn-default ' %>
@@ -19,4 +21,4 @@
19
21
  <% end %>
20
22
  </div>
21
23
  </div>
22
- </div>
24
+ </div>
@@ -9,8 +9,10 @@
9
9
  <%= render 'form', importer: @importer, form: form %>
10
10
  <div class="panel-footer">
11
11
  <div class='pull-right'>
12
- <%= form.button :submit, value: 'Create and Validate', class: 'btn btn-primary' %>
13
- |
12
+ <% if ENV['SHOW_CREATE_AND_VALIDATE'] == 'true' %>
13
+ <%= form.button :submit, value: 'Create and Validate', class: 'btn btn-primary' %>
14
+ |
15
+ <% end %>
14
16
  <%= form.button :submit, value: 'Create and Import', class: 'btn btn-primary' %>
15
17
  |
16
18
  <%= form.button :submit, value: 'Create', class: 'btn btn-primary' %>
@@ -1,10 +1,12 @@
1
1
  <% if current_ability.can_import_works? %>
2
- <%= menu.nav_link(bulkrax.importers_path) do %>
2
+ <%= menu.nav_link(bulkrax.importers_path,
3
+ title: t('bulkrax.admin.sidebar.importers')) do %>
3
4
  <span class="fa fa-cloud-upload" aria-hidden="true"></span> <span class="sidebar-action-text"><%= t('bulkrax.admin.sidebar.importers') %></span>
4
5
  <% end %>
5
6
  <% end %>
6
7
  <% if current_ability.can_export_works? %>
7
- <%= menu.nav_link(bulkrax.exporters_path) do %>
8
+ <%= menu.nav_link(bulkrax.exporters_path,
9
+ title: t('bulkrax.admin.sidebar.exporters')) do %>
8
10
  <span class="fa fa-cloud-download" aria-hidden="true"></span> <span class="sidebar-action-text"><%= t('bulkrax.admin.sidebar.exporters') %></span>
9
11
  <% end %>
10
12
  <% end %>
@@ -3,14 +3,16 @@
3
3
  <%= menu.nav_link(hyrax.my_collections_path,
4
4
  class: "nav-link",
5
5
  onclick: "dontChangeAccordion(event);",
6
- also_active_for: hyrax.dashboard_collections_path) do %>
6
+ also_active_for: hyrax.dashboard_collections_path,
7
+ title: t('hyrax.admin.sidebar.collections')) do %>
7
8
  <span class="fa fa-folder-open" aria-hidden="true"></span> <span class="sidebar-action-text"><%= t('hyrax.admin.sidebar.collections') %></span>
8
9
  <% end %>
9
10
 
10
11
  <%= menu.nav_link(hyrax.my_works_path,
11
12
  class: "nav-link",
12
13
  onclick: "dontChangeAccordion(event);",
13
- also_active_for: hyrax.dashboard_works_path) do %>
14
+ also_active_for: hyrax.dashboard_works_path,
15
+ title: t('hyrax.admin.sidebar.works')) do %>
14
16
  <span class="fa fa-file" aria-hidden="true"></span> <span class="sidebar-action-text"><%= t('hyrax.admin.sidebar.works') %></span>
15
17
  <% end %>
16
18
 
@@ -0,0 +1,14 @@
1
+ class AddIndicesToBulkrax < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_index :bulkrax_entries, :identifier
4
+ add_index :bulkrax_entries, :type
5
+ add_index :bulkrax_entries, [:importerexporter_id, :importerexporter_type], name: 'bulkrax_entries_importerexporter_idx'
6
+
7
+ add_index :bulkrax_pending_relationships, :parent_id
8
+ add_index :bulkrax_pending_relationships, :child_id
9
+
10
+ add_index :bulkrax_statuses, [:statusable_id, :statusable_type], name: 'bulkrax_statuses_statusable_idx'
11
+ add_index :bulkrax_statuses, [:runnable_id, :runnable_type], name: 'bulkrax_statuses_runnable_idx'
12
+ add_index :bulkrax_statuses, :error_class
13
+ end
14
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bulkrax
4
- VERSION = '5.2.1'
4
+ VERSION = '5.3.1'
5
5
  end
data/lib/bulkrax.rb CHANGED
@@ -1,16 +1,16 @@
1
- # frozen_string_literal: true
1
+ # frozen_string_literal: true
2
2
 
3
- require "bulkrax/version"
4
- require "bulkrax/engine"
5
- require 'active_support/all'
3
+ require "bulkrax/version"
4
+ require "bulkrax/engine"
5
+ require 'active_support/all'
6
6
 
7
- # rubocop:disable Metrics/ModuleLength
7
+ # rubocop:disable Metrics/ModuleLength
8
8
  module Bulkrax
9
- extend self # rubocop:disable Style/ModuleFunction
10
- extend Forwardable
9
+ extend self # rubocop:disable Style/ModuleFunction
10
+ extend Forwardable
11
11
 
12
- ##
13
- # @api public
12
+ ##
13
+ # @api public
14
14
  class Configuration
15
15
  attr_accessor :api_definition,
16
16
  :curation_concerns,
@@ -34,60 +34,81 @@ module Bulkrax
34
34
  :required_elements,
35
35
  :reserved_properties,
36
36
  :server_name
37
- end
38
37
 
39
- def config
40
- @config ||= Configuration.new
41
- yield @config if block_given?
42
- @config
38
+ attr_writer :ingest_queue_name
39
+ ##
40
+ # @return [String, Proc]
41
+ def ingest_queue_name
42
+ return @ingest_queue_name if @ingest_queue_name.present?
43
+ return Hyrax.config.ingest_queue_name if defined?(Hyrax)
44
+ :import
45
+ end
46
+
47
+ attr_writer :use_locking
48
+
49
+ def use_locking
50
+ return @use_locking if defined?(@use_locking)
51
+
52
+ ENV.key?("REDIS_HOST")
53
+ end
54
+ alias use_locking? use_locking
43
55
  end
44
- alias setup config
45
56
 
46
- def_delegators :@config,
47
- :api_definition,
48
- :api_definition=,
49
- :curation_concerns,
50
- :curation_concerns=,
51
- :default_field_mapping,
52
- :default_field_mapping=,
53
- :default_work_type,
54
- :default_work_type=,
55
- :export_path,
56
- :export_path=,
57
- :field_mappings,
58
- :field_mappings=,
59
- :file_model_class,
60
- :file_model_class=,
61
- :fill_in_blank_source_identifiers,
62
- :fill_in_blank_source_identifiers=,
63
- :generated_metadata_mapping,
64
- :generated_metadata_mapping=,
65
- :import_path,
66
- :import_path=,
67
- :multi_value_element_join_on,
68
- :multi_value_element_join_on=,
69
- :multi_value_element_split_on,
70
- :multi_value_element_split_on=,
71
- :object_factory,
72
- :object_factory=,
73
- :parsers,
74
- :parsers=,
75
- :qa_controlled_properties,
76
- :qa_controlled_properties=,
77
- :related_children_field_mapping,
78
- :related_children_field_mapping=,
79
- :related_parents_field_mapping,
80
- :related_parents_field_mapping=,
81
- :relationship_job_class,
82
- :relationship_job_class=,
83
- :removed_image_path,
84
- :removed_image_path=,
85
- :required_elements,
86
- :required_elements=,
87
- :reserved_properties,
88
- :reserved_properties=,
89
- :server_name,
90
- :server_name=
57
+ def config
58
+ @config ||= Configuration.new
59
+ yield @config if block_given?
60
+ @config
61
+ end
62
+ alias setup config
63
+
64
+ def_delegators :@config,
65
+ :api_definition,
66
+ :api_definition=,
67
+ :curation_concerns,
68
+ :curation_concerns=,
69
+ :default_field_mapping,
70
+ :default_field_mapping=,
71
+ :default_work_type,
72
+ :default_work_type=,
73
+ :export_path,
74
+ :export_path=,
75
+ :field_mappings,
76
+ :field_mappings=,
77
+ :file_model_class,
78
+ :file_model_class=,
79
+ :fill_in_blank_source_identifiers,
80
+ :fill_in_blank_source_identifiers=,
81
+ :generated_metadata_mapping,
82
+ :generated_metadata_mapping=,
83
+ :import_path,
84
+ :import_path=,
85
+ :multi_value_element_join_on,
86
+ :multi_value_element_join_on=,
87
+ :multi_value_element_split_on,
88
+ :multi_value_element_split_on=,
89
+ :object_factory,
90
+ :object_factory=,
91
+ :parsers,
92
+ :parsers=,
93
+ :qa_controlled_properties,
94
+ :qa_controlled_properties=,
95
+ :related_children_field_mapping,
96
+ :related_children_field_mapping=,
97
+ :related_parents_field_mapping,
98
+ :related_parents_field_mapping=,
99
+ :relationship_job_class,
100
+ :relationship_job_class=,
101
+ :removed_image_path,
102
+ :removed_image_path=,
103
+ :required_elements,
104
+ :required_elements=,
105
+ :reserved_properties,
106
+ :reserved_properties=,
107
+ :server_name,
108
+ :server_name=,
109
+ :use_locking,
110
+ :use_locking=,
111
+ :use_locking?
91
112
 
92
113
  config do |conf|
93
114
  conf.parsers = [
@@ -126,149 +147,149 @@ module Bulkrax
126
147
  # Based on Hyrax CoreMetadata && BasicMetadata
127
148
  # Override at application level to change
128
149
  conf.field_mappings = {
129
- "Bulkrax::OaiDcParser" => {
130
- "contributor" => { from: ["contributor"] },
131
- # no appropriate mapping for coverage (based_near needs id)
132
- # ""=>{:from=>["coverage"]},
133
- "creator" => { from: ["creator"] },
134
- "date_created" => { from: ["date"] },
135
- "description" => { from: ["description"] },
136
- # no appropriate mapping for format
137
- # ""=>{:from=>["format"]},
138
- "identifier" => { from: ["identifier"] },
139
- "language" => { from: ["language"], parsed: true },
140
- "publisher" => { from: ["publisher"] },
141
- "related_url" => { from: ["relation"] },
142
- "rights_statement" => { from: ["rights"] },
143
- "source" => { from: ["source"] },
144
- "subject" => { from: ["subject"], parsed: true },
145
- "title" => { from: ["title"] },
146
- "resource_type" => { from: ["type"], parsed: true },
147
- "remote_files" => { from: ["thumbnail_url"], parsed: true }
150
+ "Bulkrax::OaiDcParser" => {
151
+ "contributor" => { from: ["contributor"] },
152
+ # no appropriate mapping for coverage (based_near needs id)
153
+ # ""=>{:from=>["coverage"]},
154
+ "creator" => { from: ["creator"] },
155
+ "date_created" => { from: ["date"] },
156
+ "description" => { from: ["description"] },
157
+ # no appropriate mapping for format
158
+ # ""=>{:from=>["format"]},
159
+ "identifier" => { from: ["identifier"] },
160
+ "language" => { from: ["language"], parsed: true },
161
+ "publisher" => { from: ["publisher"] },
162
+ "related_url" => { from: ["relation"] },
163
+ "rights_statement" => { from: ["rights"] },
164
+ "source" => { from: ["source"] },
165
+ "subject" => { from: ["subject"], parsed: true },
166
+ "title" => { from: ["title"] },
167
+ "resource_type" => { from: ["type"], parsed: true },
168
+ "remote_files" => { from: ["thumbnail_url"], parsed: true }
148
169
  },
149
- "Bulkrax::OaiQualifiedDcParser" => {
150
- "abstract" => { from: ["abstract"] },
151
- "alternative_title" => { from: ["alternative"] },
170
+ "Bulkrax::OaiQualifiedDcParser" => {
171
+ "abstract" => { from: ["abstract"] },
172
+ "alternative_title" => { from: ["alternative"] },
152
173
  "bibliographic_citation" => { from: ["bibliographicCitation"] },
153
- "contributor" => { from: ["contributor"] },
154
- "creator" => { from: ["creator"] },
155
- "date_created" => { from: ["created"] },
156
- "description" => { from: ["description"] },
157
- "language" => { from: ["language"] },
158
- "license" => { from: ["license"] },
159
- "publisher" => { from: ["publisher"] },
160
- "related_url" => { from: ["relation"] },
161
- "rights_holder" => { from: ["rightsHolder"] },
162
- "rights_statement" => { from: ["rights"] },
163
- "source" => { from: ["source"] },
164
- "subject" => { from: ["subject"], parsed: true },
165
- "title" => { from: ["title"] },
166
- "resource_type" => { from: ["type"], parsed: true },
167
- "remote_files" => { from: ["thumbnail_url"], parsed: true }
168
- },
169
- # When empty, a default_field_mapping will be generated
170
- "Bulkrax::CsvParser" => {},
171
- 'Bulkrax::BagitParser' => {},
172
- 'Bulkrax::XmlParser' => {}
173
- }
174
+ "contributor" => { from: ["contributor"] },
175
+ "creator" => { from: ["creator"] },
176
+ "date_created" => { from: ["created"] },
177
+ "description" => { from: ["description"] },
178
+ "language" => { from: ["language"] },
179
+ "license" => { from: ["license"] },
180
+ "publisher" => { from: ["publisher"] },
181
+ "related_url" => { from: ["relation"] },
182
+ "rights_holder" => { from: ["rightsHolder"] },
183
+ "rights_statement" => { from: ["rights"] },
184
+ "source" => { from: ["source"] },
185
+ "subject" => { from: ["subject"], parsed: true },
186
+ "title" => { from: ["title"] },
187
+ "resource_type" => { from: ["type"], parsed: true },
188
+ "remote_files" => { from: ["thumbnail_url"], parsed: true }
189
+ },
190
+ # When empty, a default_field_mapping will be generated
191
+ "Bulkrax::CsvParser" => {},
192
+ 'Bulkrax::BagitParser' => {},
193
+ 'Bulkrax::XmlParser' => {}
194
+ }
174
195
 
175
- # Lambda to set the default field mapping
176
- conf.default_field_mapping = lambda do |field|
177
- return if field.blank?
178
- {
179
- field.to_s =>
180
- {
181
- from: [field.to_s],
182
- split: false,
183
- parsed: Bulkrax::ApplicationMatcher.method_defined?("parse_#{field}"),
184
- if: nil,
185
- excluded: false
186
- }
187
- }
188
- end
196
+ # Lambda to set the default field mapping
197
+ conf.default_field_mapping = lambda do |field|
198
+ return if field.blank?
199
+ {
200
+ field.to_s =>
201
+ {
202
+ from: [field.to_s],
203
+ split: false,
204
+ parsed: Bulkrax::ApplicationMatcher.method_defined?("parse_#{field}"),
205
+ if: nil,
206
+ excluded: false
207
+ }
208
+ }
209
+ end
189
210
 
190
- # Properties that should not be used in imports. They are reserved for use by Hyrax.
191
- conf.reserved_properties = %w[
192
- create_date
193
- modified_date
194
- date_modified
195
- date_uploaded
196
- depositor
197
- arkivo_checksum
198
- has_model
199
- head
200
- label
201
- import_url
202
- on_behalf_of
203
- proxy_depositor
204
- owner
205
- state
206
- tail
207
- original_url
208
- relative_path
209
- ]
211
+ # Properties that should not be used in imports. They are reserved for use by Hyrax.
212
+ conf.reserved_properties = %w[
213
+ create_date
214
+ modified_date
215
+ date_modified
216
+ date_uploaded
217
+ depositor
218
+ arkivo_checksum
219
+ has_model
220
+ head
221
+ label
222
+ import_url
223
+ on_behalf_of
224
+ proxy_depositor
225
+ owner
226
+ state
227
+ tail
228
+ original_url
229
+ relative_path
230
+ ]
210
231
 
211
- # List of Questioning Authority properties that are controlled via YAML files in
212
- # the config/authorities/ directory. For example, the :rights_statement property
213
- # is controlled by the active terms in config/authorities/rights_statements.yml
214
- conf.qa_controlled_properties = %w[rights_statement license]
215
- end
232
+ # List of Questioning Authority properties that are controlled via YAML files in
233
+ # the config/authorities/ directory. For example, the :rights_statement property
234
+ # is controlled by the active terms in config/authorities/rights_statements.yml
235
+ conf.qa_controlled_properties = %w[rights_statement license]
236
+ end
216
237
 
217
- def api_definition
218
- @api_definition ||= ActiveSupport::HashWithIndifferentAccess.new(
219
- YAML.safe_load(
220
- ERB.new(
221
- File.read(Rails.root.join('config', 'bulkrax_api.yml'))
222
- ).result
223
- )
224
- )
225
- end
238
+ def api_definition
239
+ @api_definition ||= ActiveSupport::HashWithIndifferentAccess.new(
240
+ YAML.safe_load(
241
+ ERB.new(
242
+ File.read(Rails.root.join('config', 'bulkrax_api.yml'))
243
+ ).result
244
+ )
245
+ )
246
+ end
226
247
 
227
- DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON = ' | '
228
- # Specify the delimiter for joining an attribute's multi-value array into a string.
229
- #
230
- # @note the specific delimiter should likely be present in the multi_value_element_split_on
231
- # expression.
232
- def multi_value_element_join_on
233
- @multi_value_element_join_on ||= DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON
234
- end
248
+ DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON = ' | '
249
+ # Specify the delimiter for joining an attribute's multi-value array into a string.
250
+ #
251
+ # @note the specific delimiter should likely be present in the multi_value_element_split_on
252
+ # expression.
253
+ def multi_value_element_join_on
254
+ @multi_value_element_join_on ||= DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON
255
+ end
235
256
 
236
- DEFAULT_MULTI_VALUE_ELEMENT_SPLIT_ON = /\s*[:;|]\s*/.freeze
237
- # @return [RegexClass] the regular express to use to "split" an attribute's values. If set to
238
- # `true` use the DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON.
239
- #
240
- # @note The "true" value is to preserve backwards compatibility.
241
- # @see DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON
257
+ DEFAULT_MULTI_VALUE_ELEMENT_SPLIT_ON = /\s*[:;|]\s*/.freeze
258
+ # @return [RegexClass] the regular express to use to "split" an attribute's values. If set to
259
+ # `true` use the DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON.
260
+ #
261
+ # @note The "true" value is to preserve backwards compatibility.
262
+ # @see DEFAULT_MULTI_VALUE_ELEMENT_JOIN_ON
242
263
  def multi_value_element_split_on
243
- if @multi_value_element_join_on.is_a?(TrueClass)
244
- DEFAULT_MULTI_VALUE_ELEMENT_SPLIT_ON
264
+ if @multi_value_element_join_on.is_a?(TrueClass)
265
+ DEFAULT_MULTI_VALUE_ELEMENT_SPLIT_ON
245
266
  else
246
- @multi_value_element_split_on ||= DEFAULT_MULTI_VALUE_ELEMENT_SPLIT_ON
267
+ @multi_value_element_split_on ||= DEFAULT_MULTI_VALUE_ELEMENT_SPLIT_ON
247
268
  end
248
269
  end
249
270
 
250
- # Responsible for stripping hidden characters from the given string.
251
- #
252
- # @param value [#to_s]
253
- # @return [String] with hidden characters removed
254
- #
255
- # @see https://github.com/samvera-labs/bulkrax/issues/688
256
- def normalize_string(value)
257
- # Removing [Byte Order Mark (BOM)](https://en.wikipedia.org/wiki/Byte_order_mark)
258
- value.to_s.delete("\xEF\xBB\xBF")
259
- end
271
+ # Responsible for stripping hidden characters from the given string.
272
+ #
273
+ # @param value [#to_s]
274
+ # @return [String] with hidden characters removed
275
+ #
276
+ # @see https://github.com/samvera-labs/bulkrax/issues/688
277
+ def normalize_string(value)
278
+ # Removing [Byte Order Mark (BOM)](https://en.wikipedia.org/wiki/Byte_order_mark)
279
+ value.to_s.delete("\xEF\xBB\xBF")
280
+ end
260
281
 
261
- def fallback_user_for_importer_exporter_processing
262
- return User.batch_user if defined?(Hyrax) && User.respond_to?(:batch_user)
282
+ def fallback_user_for_importer_exporter_processing
283
+ return User.batch_user if defined?(Hyrax) && User.respond_to?(:batch_user)
263
284
 
264
- raise "We have no fallback user available for Bulkrax.fallback_user_for_importer_exporter_processing"
265
- end
285
+ raise "We have no fallback user available for Bulkrax.fallback_user_for_importer_exporter_processing"
286
+ end
266
287
 
267
- # This class confirms to the Active::Support.serialize interface. It's job is to ensure that we
268
- # don't have keys with the tricksy Byte Order Mark character.
269
- #
270
- # @see https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize
271
- class NormalizedJson
288
+ # This class confirms to the Active::Support.serialize interface. It's job is to ensure that we
289
+ # don't have keys with the tricksy Byte Order Mark character.
290
+ #
291
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize
292
+ class NormalizedJson
272
293
  def self.normalize_keys(hash)
273
294
  return hash unless hash.respond_to?(:each_pair)
274
295
  returning_value = {}
@@ -280,18 +301,18 @@ module Bulkrax
280
301
 
281
302
  # When we write the serialized data to the database, we "dump" the value into that database
282
303
  # column.
283
- def self.dump(value)
284
- JSON.dump(normalize_keys(value))
285
- end
304
+ def self.dump(value)
305
+ JSON.dump(normalize_keys(value))
306
+ end
286
307
 
287
308
  # When we load the serialized data from the database, we pass the database's value into "load"
288
309
  # function.
289
310
  #
290
311
  # rubocop:disable Security/JSONLoad
291
- def self.load(string)
292
- normalize_keys(JSON.load(string))
293
- end
312
+ def self.load(string)
313
+ normalize_keys(JSON.load(string))
314
+ end
294
315
  # rubocop:enable Security/JSONLoad
295
- end
316
+ end
296
317
  end
297
- # rubocop:disable Metrics/ModuleLength
318
+ # rubocop:disable Metrics/ModuleLength
@@ -7,8 +7,8 @@ Bulkrax.setup do |config|
7
7
  # ]
8
8
 
9
9
  # WorkType to use as the default if none is specified in the import
10
- # Default is the first returned by Hyrax.config.curation_concerns
11
- # config.default_work_type = MyWork
10
+ # Default is the first returned by Hyrax.config.curation_concerns, stringified
11
+ # config.default_work_type = "MyWork"
12
12
 
13
13
  # Factory Class to use when generating and saving objects
14
14
  config.object_factory = Bulkrax::ObjectFactory
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bulkrax
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.1
4
+ version: 5.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Kaufman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-24 00:00:00.000000000 Z
11
+ date: 2024-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dry-monads
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.4.0
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: iso8601
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -238,6 +252,34 @@ dependencies:
238
252
  - - ">="
239
253
  - !ruby/object:Gem::Version
240
254
  version: '0'
255
+ - !ruby/object:Gem::Dependency
256
+ name: redis
257
+ requirement: !ruby/object:Gem::Requirement
258
+ requirements:
259
+ - - "~>"
260
+ - !ruby/object:Gem::Version
261
+ version: '4.2'
262
+ type: :development
263
+ prerelease: false
264
+ version_requirements: !ruby/object:Gem::Requirement
265
+ requirements:
266
+ - - "~>"
267
+ - !ruby/object:Gem::Version
268
+ version: '4.2'
269
+ - !ruby/object:Gem::Dependency
270
+ name: psych
271
+ requirement: !ruby/object:Gem::Requirement
272
+ requirements:
273
+ - - "~>"
274
+ - !ruby/object:Gem::Version
275
+ version: '3.3'
276
+ type: :development
277
+ prerelease: false
278
+ version_requirements: !ruby/object:Gem::Requirement
279
+ requirements:
280
+ - - "~>"
281
+ - !ruby/object:Gem::Version
282
+ version: '3.3'
241
283
  description: Bulkrax is a batteries included importer for Samvera applications. It
242
284
  currently includes support for OAI-PMH (DC and Qualified DC) and CSV out of the
243
285
  box. It is also designed to be extensible, allowing you to easily add new importers
@@ -258,6 +300,7 @@ files:
258
300
  - app/assets/javascripts/bulkrax/entries.js
259
301
  - app/assets/javascripts/bulkrax/exporters.js
260
302
  - app/assets/javascripts/bulkrax/importers.js.erb
303
+ - app/assets/javascripts/bulkrax/navtabs.js.erb
261
304
  - app/assets/stylesheets/bulkrax/accordion.scss
262
305
  - app/assets/stylesheets/bulkrax/application.css
263
306
  - app/assets/stylesheets/bulkrax/coderay.scss
@@ -395,6 +438,7 @@ files:
395
438
  - db/migrate/20220412233954_add_include_thumbnails_to_bulkrax_exporters.rb
396
439
  - db/migrate/20220413180915_add_generated_metadata_to_bulkrax_exporters.rb
397
440
  - db/migrate/20220609001128_rename_bulkrax_importer_run_to_importer_run.rb
441
+ - db/migrate/20230608153601_add_indices_to_bulkrax.rb
398
442
  - lib/bulkrax.rb
399
443
  - lib/bulkrax/engine.rb
400
444
  - lib/bulkrax/entry_spec_helper.rb
@@ -427,7 +471,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
427
471
  - !ruby/object:Gem::Version
428
472
  version: '0'
429
473
  requirements: []
430
- rubygems_version: 3.0.3
474
+ rubygems_version: 3.1.6
431
475
  signing_key:
432
476
  specification_version: 4
433
477
  summary: Import and export tool for Hyrax and Hyku