Class: Bulkrax::ObjectFactoryInterface Abstract

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Callbacks
Includes:
DynamicRecordLookup, Loggable
Defined in:
app/factories/bulkrax/object_factory_interface.rb

Overview

This class is abstract.

The purpose of the object factory is to provide an interface for interacting with the underlying data repository’s storage. Each application that mounts Bulkrax should configure the appropriate object factory (via ‘Bulkrax.object_factory=`).

The class methods are for issueing query/commands to the underlying repository.

The instance methods are for mapping a Entry to a corresponding data repository object (e.g. a Fedora Commons record or a Postgresql record via ActiveFedora::Base and/or Valkyrie).

rubocop:disable Metrics/ClassLength

Direct Known Subclasses

ObjectFactory, ValkyrieObjectFactory

Defined Under Namespace

Classes: ObjectNotFoundError, RecordInvalid

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Loggable

#log_created, #log_deleted_fs, #log_updated

Methods included from DynamicRecordLookup

#find_record

Constructor Details

#initialize(attributes:, source_identifier_value:, work_identifier:, work_identifier_search_field:, related_parents_parsed_mapping: nil, replace_files: false, user: nil, klass: nil, importer_run_id: nil, update_files: false) ⇒ ObjectFactoryInterface

rubocop:disable Metrics/ParameterLists



290
291
292
293
294
295
296
297
298
299
300
301
# File 'app/factories/bulkrax/object_factory_interface.rb', line 290

def initialize(attributes:, source_identifier_value:, work_identifier:, work_identifier_search_field:, related_parents_parsed_mapping: nil, replace_files: false, user: nil, klass: nil, importer_run_id: nil, update_files: false)
  @attributes = ActiveSupport::HashWithIndifferentAccess.new(attributes)
  @replace_files = replace_files
  @update_files = update_files
  @user = user || User.batch_user
  @work_identifier = work_identifier
  @work_identifier_search_field = work_identifier_search_field
  @related_parents_parsed_mapping = related_parents_parsed_mapping
  @source_identifier_value = source_identifier_value
  @klass = klass || Bulkrax.default_work_type.constantize
  @importer_run_id = importer_run_id
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def attributes
  @attributes
end

#importer_run_idObject (readonly)

Returns the value of attribute importer_run_id.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def importer_run_id
  @importer_run_id
end

#klassObject (readonly)

Returns the value of attribute klass.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def klass
  @klass
end

#objectObject (readonly)

Returns the value of attribute object.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def object
  @object
end

Returns the value of attribute related_parents_parsed_mapping.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def related_parents_parsed_mapping
  @related_parents_parsed_mapping
end

#replace_filesObject (readonly)

Returns the value of attribute replace_files.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def replace_files
  @replace_files
end

#source_identifier_valueObject (readonly)

Returns the value of attribute source_identifier_value.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def source_identifier_value
  @source_identifier_value
end

#update_filesObject Also known as: update_files?

Returns the value of attribute update_files.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def update_files
  @update_files
end

#userObject (readonly)

Returns the value of attribute user.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def user
  @user
end

#work_identifierObject (readonly)

Returns the value of attribute work_identifier.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def work_identifier
  @work_identifier
end

#work_identifier_search_fieldObject (readonly)

Returns the value of attribute work_identifier_search_field.



275
276
277
# File 'app/factories/bulkrax/object_factory_interface.rb', line 275

def work_identifier_search_field
  @work_identifier_search_field
end

Class Method Details

.add_child_to_parent_work(parent:, child:) ⇒ Object

Note:

This does not save either object. We need to do that in another loop. Why? Because we might be adding many items to the parent.

Raises:

  • (NotImplementedError)


40
41
42
# File 'app/factories/bulkrax/object_factory_interface.rb', line 40

def self.add_child_to_parent_work(parent:, child:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.add_resource_to_collection(collection:, resource:, user:) ⇒ Object

Raises:

  • (NotImplementedError)


44
45
46
# File 'app/factories/bulkrax/object_factory_interface.rb', line 44

def self.add_resource_to_collection(collection:, resource:, user:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.add_user_to_collection_permissions(collection:, user:) ⇒ Object

Add the user to the collection; assuming the given collection is a Collection. This is also only something we use in Hyrax.

Parameters:

  • collection (#id)
  • user (User)

See Also:

  • Bulkrax.collection_model_class


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'app/factories/bulkrax/object_factory_interface.rb', line 55

def self.add_user_to_collection_permissions(collection:, user:)
  return unless collection.is_a?(Bulkrax.collection_model_class)
  return unless defined?(Hyrax)

  permission_template = Hyrax::PermissionTemplate.find_or_create_by!(source_id: collection.id)

  # NOTE: Should we extract the specific logic here?  Also, does it make
  # sense to apply permissions to the permission template (and then update)
  # instead of applying permissions directly to the collection?
  Hyrax::PermissionTemplateAccess.find_or_create_by!(
    permission_template_id: permission_template.id,
    agent_id: user.user_key,
    agent_type: 'user',
    access: 'manage'
  )

  # NOTE: This is a bit surprising that we'd add admin as a group.
  Hyrax::PermissionTemplateAccess.find_or_create_by!(
    permission_template_id: permission_template.id,
    agent_id: 'admin',
    agent_type: 'group',
    access: 'manage'
  )

  if permission_template.respond_to?(:reset_access_controls_for)
    # Hyrax 4+
    # must pass interpret_visibility: true to avoid clobbering provided visibility
    permission_template.reset_access_controls_for(collection: collection, interpret_visibility: true)
  elsif collection.respond_to?(:reset_access_controls!)
    # Hyrax 3 or earlier
    collection.reset_access_controls!
  else
    raise "Unable to reset access controls for #{collection.class} ID=#{collection.id}"
  end
end

.clean! { ... } ⇒ Object

Yields:

  • when Rails application is running in test environment.



93
94
95
96
# File 'app/factories/bulkrax/object_factory_interface.rb', line 93

def self.clean!
  return true unless Rails.env.test?
  yield
end

.default_admin_set_idString

Returns:

  • (String)


100
101
102
103
104
105
106
107
108
# File 'app/factories/bulkrax/object_factory_interface.rb', line 100

def self.default_admin_set_id
  if defined?(Hyrax::AdminSetCreateService::DEFAULT_ID)
    return Hyrax::AdminSetCreateService::DEFAULT_ID
  elsif defined?(AdminSet::DEFAULT_ID)
    return AdminSet::DEFAULT_ID
  else
    return 'admin_set/default'
  end
end

.default_admin_set_or_nilObject, NilClass

Returns:

  • (Object)

    when we have an existing admin set.

  • (NilClass)

    when we the default admin set does not exist.

See Also:



115
116
117
# File 'app/factories/bulkrax/object_factory_interface.rb', line 115

def self.default_admin_set_or_nil
  find_or_nil(default_admin_set_id)
end

.export_propertiesArray<String>

Returns:

  • (Array<String>)

Raises:

  • (NotImplementedError)


121
122
123
# File 'app/factories/bulkrax/object_factory_interface.rb', line 121

def self.export_properties
  raise NotImplementedError, "#{self}.#{__method__}"
end

.field_multi_value?(field:, model:) ⇒ TrueClass, FalseClass

Parameters:

  • field (String)
  • model (Class)

Returns:

  • (TrueClass)

    when the given :field is a multi-value property on the given :model.

  • (FalseClass)

    when given :field is not a scalar (not multi-value) property on the given :model.

Raises:

  • (NotImplementedError)


146
147
148
# File 'app/factories/bulkrax/object_factory_interface.rb', line 146

def self.field_multi_value?(field:, model:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.field_supported?(field:, model:) ⇒ FalseClass

Returns when the given :field is not a valid property on the given :model.

Returns:

  • (FalseClass)

    when the given :field is not a valid property on the given :model.

Raises:

  • (NotImplementedError)


134
135
136
# File 'app/factories/bulkrax/object_factory_interface.rb', line 134

def self.field_supported?(field:, model:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.file_sets_for(resource:) ⇒ Array<Object>

Returns interrogate the given :object and return an array of object’s file sets. When the object is a file set, return that file set as an Array of one element.

Parameters:

  • resource (Object)

Returns:

  • (Array<Object>)

    interrogate the given :object and return an array of object’s file sets. When the object is a file set, return that file set as an Array of one element.

Raises:

  • (NotImplementedError)


160
161
162
# File 'app/factories/bulkrax/object_factory_interface.rb', line 160

def self.file_sets_for(resource:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.filename_for(fileset:) ⇒ String

#input [Fileset or FileMetadata]

Returns:

  • (String)

    the file name for the given fileset

Raises:

  • (NotImplementedError)


234
235
236
# File 'app/factories/bulkrax/object_factory_interface.rb', line 234

def self.filename_for(fileset:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.find(id) ⇒ Object

Raises:

  • (NotImplementedError)

See Also:

  • ActiveFedora::Base.find


166
167
168
# File 'app/factories/bulkrax/object_factory_interface.rb', line 166

def self.find(id)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.find_or_create_default_admin_setObject

Raises:

  • (NotImplementedError)


150
151
152
# File 'app/factories/bulkrax/object_factory_interface.rb', line 150

def self.find_or_create_default_admin_set
  raise NotImplementedError, "#{self}.#{__method__}"
end

.find_or_nil(id) ⇒ Object



170
171
172
173
174
175
176
# File 'app/factories/bulkrax/object_factory_interface.rb', line 170

def self.find_or_nil(id)
  find(id)
rescue NotImplementedError => e
  raise e
rescue
  nil
end

.model_name(resource:) ⇒ String

Returns the name of the model class for the given resource/object.

Returns:

  • (String)

    the name of the model class for the given resource/object.

Raises:

  • (NotImplementedError)


214
215
216
# File 'app/factories/bulkrax/object_factory_interface.rb', line 214

def self.model_name(resource:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.original_file(fileset:) ⇒ File or FileMetadata

Returns the original file.

Returns:

  • (File or FileMetadata)

    the original file

Raises:

  • (NotImplementedError)


227
228
229
# File 'app/factories/bulkrax/object_factory_interface.rb', line 227

def self.original_file(fileset:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.publish(event:, **kwargs) ⇒ Object

Raises:

  • (NotImplementedError)


178
179
180
# File 'app/factories/bulkrax/object_factory_interface.rb', line 178

def self.publish(event:, **kwargs)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.query(q, **kwargs) ⇒ Object

Raises:

  • (NotImplementedError)


182
183
184
# File 'app/factories/bulkrax/object_factory_interface.rb', line 182

def self.query(q, **kwargs)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.save!(resource:, user:) ⇒ Object

Raises:

  • (NotImplementedError)


186
187
188
# File 'app/factories/bulkrax/object_factory_interface.rb', line 186

def self.save!(resource:, user:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.search_by_property(value:, klass:, field: nil, search_field: nil, name_field: nil, verify_property: false) ⇒ Object

rubocop:disable Metrics/ParameterLists

Raises:

  • (NotImplementedError)


191
192
193
# File 'app/factories/bulkrax/object_factory_interface.rb', line 191

def self.search_by_property(value:, klass:, field: nil, search_field: nil, name_field: nil, verify_property: false)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.solr_name(field_name) ⇒ Object

Raises:

  • (NotImplementedError)


195
196
197
# File 'app/factories/bulkrax/object_factory_interface.rb', line 195

def self.solr_name(field_name)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.thumbnail_for(resource:) ⇒ File or FileMetadata

Returns the thumbnail file for the given resource.

Returns:

  • (File or FileMetadata)

    the thumbnail file for the given resource

Raises:

  • (NotImplementedError)


220
221
222
# File 'app/factories/bulkrax/object_factory_interface.rb', line 220

def self.thumbnail_for(resource:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

.update_index(resources: []) ⇒ Object

Parameters:

  • resources (Array<Object>) (defaults to: [])

Raises:

  • (NotImplementedError)


201
202
203
# File 'app/factories/bulkrax/object_factory_interface.rb', line 201

def self.update_index(resources: [])
  raise NotImplementedError, "#{self}.#{__method__}"
end

.update_index_for_file_sets_of(resource:) ⇒ Object

Parameters:

  • resource (Object)

    something that might have file_sets members.

Raises:

  • (NotImplementedError)


207
208
209
# File 'app/factories/bulkrax/object_factory_interface.rb', line 207

def self.update_index_for_file_sets_of(resource:)
  raise NotImplementedError, "#{self}.#{__method__}"
end

Instance Method Details

#add_user_to_collection_permissions(*args) ⇒ Object



429
430
431
432
# File 'app/factories/bulkrax/object_factory_interface.rb', line 429

def add_user_to_collection_permissions(*args)
  arguments = args.first
  self.class.add_user_to_collection_permissions(**arguments)
end

#base_permitted_attributesArray<Symbol>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

These are the attributes that we assume all “work type” classes (e.g. the given :klass) will have in addition to their specific attributes.

Returns:

  • (Array<Symbol>)

See Also:

  • #permitted_attributes


246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'app/factories/bulkrax/object_factory_interface.rb', line 246

class_attribute :base_permitted_attributes,
default: i[
  admin_set_id
  edit_groups
  edit_users
  id
  read_groups
  visibility
  visibility_during_embargo
  embargo_release_date
  visibility_after_embargo
  visibility_during_lease
  lease_expiration_date
  visibility_after_lease
  work_members_attributes
]

#createObject

An ActiveFedora bug when there are many habtm <-> has_many associations means they won’t all get saved. github.com/projecthydra/active_fedora/issues/874 9+ years later, still open!



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'app/factories/bulkrax/object_factory_interface.rb', line 319

def create
  attrs = transform_attributes
  @object = klass.new
  conditionally_set_reindex_extent
  run_callbacks :save do
    run_callbacks :create do
      if klass == Bulkrax.collection_model_class
        create_collection(attrs)
      elsif klass == Bulkrax.file_model_class
        create_file_set(attrs)
      else
        create_work(attrs)
      end
    end
  end

  
  log_created(object)
end

#delete(_user) ⇒ Object

Raises:

  • (NotImplementedError)


339
340
341
# File 'app/factories/bulkrax/object_factory_interface.rb', line 339

def delete(_user)
  raise NotImplementedError, "#{self.class}##{__method__}"
end

#findObject, NilClass

Returns:

  • (Object)

    when we’ve found the object by the entry’s :id or by it’s source_identifier

  • (NilClass)

    when we cannot find the object.



349
350
351
# File 'app/factories/bulkrax/object_factory_interface.rb', line 349

def find
  find_by_id || search_by_identifier || nil
end

#find_by_idObject, FalseClass

This method is abstract.

Returns:

  • (Object)

    when we’ve found the object by the entry’s :id or by it’s source_identifier

  • (FalseClass)

    when we cannot find the object.

Raises:

  • (NotImplementedError)


359
360
361
# File 'app/factories/bulkrax/object_factory_interface.rb', line 359

def find_by_id
  raise NotImplementedError, "#{self.class}##{__method__}"
end

#find_or_createObject

Returns either the one found in persistence or the one created via the run method.

Returns:

  • (Object)

    either the one found in persistence or the one created via the run method.

See Also:



367
368
369
370
371
# File 'app/factories/bulkrax/object_factory_interface.rb', line 367

def find_or_create
  # Do we need to call save!   This was how we previously did this but it
  # seems odd that we'd not find it.  Also, why not simply call create.
  find || self.class.save!(object: run, user: @user)
end

#run {|object| ... } ⇒ Object

Yields:



373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'app/factories/bulkrax/object_factory_interface.rb', line 373

def run
  arg_hash = { id: attributes[:id], name: 'UPDATE', klass: klass }

  @object = find
  if object
    conditionally_set_reindex_extent
    ActiveSupport::Notifications.instrument('import.importer', arg_hash) { update }
  else
    ActiveSupport::Notifications.instrument('import.importer', arg_hash.merge(name: 'CREATE')) { create }
  end
  yield(object) if block_given?
  object
end

#run!Object



387
388
389
390
391
392
393
# File 'app/factories/bulkrax/object_factory_interface.rb', line 387

def run!
  self.run
  # Create the error exception if the object is not validly saved for some
  # reason
  raise ObjectFactoryInterface::RecordInvalid, object if !object.persisted? || object.changed?
  object
end

#search_by_identifierFalseClass, Object

Returns:

  • (FalseClass)

    when :source_identifier_value is blank or is not found via search_by_property query.

  • (Object)

    when we have a source_identifier_value value and we can find it in the data store.



400
401
402
403
404
405
406
407
408
409
# File 'app/factories/bulkrax/object_factory_interface.rb', line 400

def search_by_identifier
  return false if source_identifier_value.blank?

  self.class.search_by_property(
    klass: klass,
    search_field: work_identifier_search_field,
    value: source_identifier_value,
    name_field: work_identifier
  )
end

#transformation_removes_blank_hash_valuesBoolean

Examples:

Bulkrax::ObjectFactory.transformation_removes_blank_hash_values = true

Returns:

  • (Boolean)

See Also:



272
# File 'app/factories/bulkrax/object_factory_interface.rb', line 272

class_attribute :transformation_removes_blank_hash_values, default: false

#updateObject



411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'app/factories/bulkrax/object_factory_interface.rb', line 411

def update
  raise "Object doesn't exist" unless object
  conditionally_destroy_existing_files

  attrs = transform_attributes(update: true)
  run_callbacks :save do
    if klass == Bulkrax.collection_model_class
      update_collection(attrs)
    elsif klass == Bulkrax.file_model_class
      update_file_set(attrs)
    else
      update_work(attrs)
    end
  end
  
  log_updated(object)
end