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



264
265
266
267
268
269
270
271
272
273
274
275
# File 'app/factories/bulkrax/object_factory_interface.rb', line 264

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.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def attributes
  @attributes
end

#importer_run_idObject (readonly)

Returns the value of attribute importer_run_id.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def importer_run_id
  @importer_run_id
end

#klassObject (readonly)

Returns the value of attribute klass.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def klass
  @klass
end

#objectObject (readonly)

Returns the value of attribute object.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def object
  @object
end

Returns the value of attribute related_parents_parsed_mapping.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def related_parents_parsed_mapping
  @related_parents_parsed_mapping
end

#replace_filesObject (readonly)

Returns the value of attribute replace_files.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def replace_files
  @replace_files
end

#source_identifier_valueObject (readonly)

Returns the value of attribute source_identifier_value.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def source_identifier_value
  @source_identifier_value
end

#update_filesObject Also known as: update_files?

Returns the value of attribute update_files.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def update_files
  @update_files
end

#userObject (readonly)

Returns the value of attribute user.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def user
  @user
end

#work_identifierObject (readonly)

Returns the value of attribute work_identifier.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

def work_identifier
  @work_identifier
end

#work_identifier_search_fieldObject (readonly)

Returns the value of attribute work_identifier_search_field.



249
250
251
# File 'app/factories/bulkrax/object_factory_interface.rb', line 249

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

.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

.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

.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



403
404
405
406
# File 'app/factories/bulkrax/object_factory_interface.rb', line 403

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


220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'app/factories/bulkrax/object_factory_interface.rb', line 220

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!



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'app/factories/bulkrax/object_factory_interface.rb', line 293

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)


313
314
315
# File 'app/factories/bulkrax/object_factory_interface.rb', line 313

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

#findObject, FalseClass

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.



323
324
325
# File 'app/factories/bulkrax/object_factory_interface.rb', line 323

def find
  find_by_id || search_by_identifier || false
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)


333
334
335
# File 'app/factories/bulkrax/object_factory_interface.rb', line 333

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:



341
342
343
344
345
# File 'app/factories/bulkrax/object_factory_interface.rb', line 341

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:



347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'app/factories/bulkrax/object_factory_interface.rb', line 347

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



361
362
363
364
365
366
367
# File 'app/factories/bulkrax/object_factory_interface.rb', line 361

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.



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

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:



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

class_attribute :transformation_removes_blank_hash_values, default: false

#updateObject



385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'app/factories/bulkrax/object_factory_interface.rb', line 385

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