Class: Bulkrax::ValkyrieObjectFactory

Inherits:
ObjectFactoryInterface show all
Includes:
FileFactory
Defined in:
app/factories/bulkrax/valkyrie_object_factory.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Classes: FileFactoryInnerWorkings

Instance Attribute Summary

Attributes inherited from ObjectFactoryInterface

#attributes, #importer_run_id, #klass, #object, #related_parents_parsed_mapping, #replace_files, #source_identifier_value, #update_files, #user, #work_identifier, #work_identifier_search_field

Class Method Interface collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ObjectFactoryInterface

add_user_to_collection_permissions, #add_user_to_collection_permissions, #base_permitted_attributes, clean!, default_admin_set_id, default_admin_set_or_nil, export_properties, #find, #find_or_create, find_or_nil, #initialize, #run, #search_by_identifier, #transformation_removes_blank_hash_values

Methods included from Loggable

#log_created, #log_deleted_fs, #log_updated

Methods included from DynamicRecordLookup

#find_record

Constructor Details

This class inherits a constructor from Bulkrax::ObjectFactoryInterface

Class Method Details

.add_child_to_parent_work(parent:, child:) ⇒ Object

When adding a child to a parent work, we save the parent. Locking appears inconsistent, so we are finding the parent and saving it with each child, but waiting until the end to reindex. To do this we are bypassing the save! method defined below



108
109
110
111
112
113
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 108

def self.add_child_to_parent_work(parent:, child:)
  parent = self.find(parent.id)
  return true if parent.member_ids.include?(child.id)
  parent.member_ids << child.id
  Hyrax.persister.save(resource: parent)
end

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

The resource added to a collection can be either a work or another collection.



117
118
119
120
121
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 117

def self.add_resource_to_collection(collection:, resource:, user:)
  resource = self.find(resource.id)
  resource.member_of_collection_ids << collection.id
  save!(resource: resource, user: user)
end

.field_multi_value?(field:, model:) ⇒ Boolean

Returns:

  • (Boolean)


123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 123

def self.field_multi_value?(field:, model:)
  return false unless field_supported?(field: field, model: model)

  if model.respond_to?(:schema)
    schema = model.new.singleton_class.schema || model.schema
    dry_type = schema.key(field.to_sym)
    return true if dry_type.respond_to?(:primitive) && dry_type.primitive == Array

    false
  else
    Bulkrax::ObjectFactory.field_multi_value?(field: field, model: model)
  end
end

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

Returns:

  • (Boolean)


137
138
139
140
141
142
143
144
145
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 137

def self.field_supported?(field:, model:)
  if model.respond_to?(:schema)
    schema_properties(model).include?(field)
  else
    # We *might* have a Fedora object, so we need to consider that approach as
    # well.
    Bulkrax::ObjectFactory.field_supported?(field: field, model: model)
  end
end

.file_sets_for(resource:) ⇒ Object



147
148
149
150
151
152
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 147

def self.file_sets_for(resource:)
  return [] if resource.blank?
  return [resource] if resource.is_a?(Bulkrax.file_model_class)

  Hyrax.query_service.custom_queries.find_child_file_sets(resource: resource)
end

.filename_for(fileset:) ⇒ String

#input [Fileset or FileMetadata]

Returns:

  • (String)

    the file name for the given fileset



246
247
248
249
250
251
252
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 246

def self.filename_for(fileset:)
  file = original_file(fileset: fileset)
  return nil unless file
  file.original_filename
rescue NoMethodError
  nil
end

.find(id) ⇒ Object



154
155
156
157
158
159
160
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 154

def self.find(id)
  Hyrax.query_service.find_by(id: id)
  # Because Hyrax is not a hard dependency, we need to transform the Hyrax exception into a
  # common exception so that callers can handle a generalize exception.
rescue Hyrax::ObjectNotFoundError, Valkyrie::Persistence::ObjectNotFoundError => e
  raise ObjectFactoryInterface::ObjectNotFoundError, e.message
end

.find_or_create_default_admin_setObject



162
163
164
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 162

def self.find_or_create_default_admin_set
  Hyrax::AdminSetCreateService.find_or_create_default_admin_set
end

.model_name(resource:) ⇒ String

If we always want the valkyrized resource name, even for unmigrated objects, we can simply use resource.model_name.name. At this point, we are differentiating to help identify items which have been migrated to Valkyrie vs those which have not.

Returns:

  • (String)

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



219
220
221
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 219

def self.model_name(resource:)
  resource.class.to_s
end

.ordered_file_sets_for(object) ⇒ Object



288
289
290
291
292
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 288

def self.ordered_file_sets_for(object)
  return [] if object.blank?

  Hyrax.custom_queries.find_child_file_sets(resource: object)
end

.original_file(fileset:) ⇒ FileMetadata

Returns the original file.

Returns:

  • (FileMetadata)

    the original file



238
239
240
241
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 238

def self.original_file(fileset:)
  return fileset if fileset.is_a?(Hyrax::FileMetadata)
  fileset.try(:original_file)
end

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

Raises:

  • (NotImplementedError)


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

def self.publish(event:, **kwargs)
  # It's a bit unclear what this should be if we can't rely on Hyrax.
  raise NotImplementedError, "#{self}.#{__method__}" unless defined?(Hyrax)
  Hyrax.publisher.publish(event, **kwargs)
end

.query(q, **kwargs) ⇒ Object

Raises:

  • (NotImplementedError)


178
179
180
181
182
183
184
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 178

def self.query(q, **kwargs)
  # Someone could choose ActiveFedora::SolrService.  But I think we're
  # assuming Valkyrie is specifcally working for Hyrax.  Someone could make
  # another object factory.
  raise NotImplementedError, "#{self}.#{__method__}" unless defined?(Hyrax)
  Hyrax::SolrService.query(q, **kwargs)
end

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



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 186

def self.save!(resource:, user:)
  if defined?(Hyrax)
    result = Hyrax.persister.save(resource: resource)
    raise Valkyrie::Persistence::ObjectNotFoundError unless result
    Hyrax.index_adapter.save(resource: result)
    if result.collection?
      self.publish(event: 'collection.metadata.updated', collection: result, user: user)
    else
      self.publish(event: 'object.metadata.updated', object: result, user: user)
    end
  else
    resource.save!
  end
  resource
end

.schema_properties(klass) ⇒ Array<String>

Retrieve properties from M3 model

Parameters:

  • klass

    the model

Returns:

  • (Array<String>)


278
279
280
281
282
283
284
285
286
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 278

def self.schema_properties(klass)
  @schema_properties_map ||= {}

  klass_key = klass.name
  schema = klass.new.singleton_class.schema || klass.schema
  @schema_properties_map[klass_key] = schema.map { |k| k.name.to_s } unless @schema_properties_map.key?(klass_key)

  @schema_properties_map[klass_key]
end

.search_by_property(value:, field: nil, name_field: nil, search_field:) ⇒ NilClass, Valkyrie::Resource

rubocop:disable Metrics/ParameterLists

Parameters:

  • value (String)
  • klass (Class, #where)
  • field (String, Symbol) (defaults to: nil)

    A convenience parameter where we pass the same value to search_field and name_field.

  • name_field (String) (defaults to: nil)

    the ActiveFedora::Base property name (e.g. “title”)

Returns:

  • (NilClass)

    when no object is found.

  • (Valkyrie::Resource)

    when a match is found, an instance of given :klass



265
266
267
268
269
270
271
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 265

def self.search_by_property(value:, field: nil, name_field: nil, search_field:, **)
  name_field ||= field
  raise "Expected named_field or field got nil" if name_field.blank?
  return if value.blank?
  # Return nil or a single object.
  Hyrax.query_service.custom_queries.find_by_property_value(property: name_field, value: value, search_field: search_field)
end

.solr_name(field_name) ⇒ Object

Raises:

  • (NotImplementedError)


166
167
168
169
170
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 166

def self.solr_name(field_name)
  # It's a bit unclear what this should be if we can't rely on Hyrax.
  raise NotImplementedError, "#{self}.#{__method__}" unless defined?(Hyrax)
  Hyrax.config.index_field_mapper.solr_name(field_name)
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



225
226
227
228
229
230
231
232
233
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 225

def self.thumbnail_for(resource:)
  # recursive call to parent if resource is a fileset - we want the work's thumbnail
  return thumbnail_for(resource: resource&.parent) if resource.is_a?(Bulkrax.file_model_class)

  return nil unless resource.respond_to?(:thumbnail_id) && resource.thumbnail_id.present?
  Bulkrax.object_factory.find(resource.thumbnail_id.to_s)
rescue Bulkrax::ObjectFactoryInterface::ObjectNotFoundError
  nil
end

.transactionsObject

Note:

Within Bulkrax::ValkyrieObjectFactory there are several calls to transactions; so you’ll need your container to register those transactions.

When you want a different set of transactions you can change the container.



96
97
98
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 96

def self.transactions
  @transactions || Hyrax::Transactions::Container
end

.update_index(resources:) ⇒ Object



202
203
204
205
206
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 202

def self.update_index(resources:)
  Array(resources).each do |resource|
    Hyrax.index_adapter.save(resource: resource)
  end
end

.update_index_for_file_sets_of(resource:) ⇒ Object



208
209
210
211
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 208

def self.update_index_for_file_sets_of(resource:)
  file_sets = Hyrax.query_service.custom_queries.find_child_file_sets(resource: resource)
  update_index(resources: file_sets)
end

Instance Method Details

#createObject

Customized create method for Valkyrie so that @object gets set



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 42

def create
  attrs = transform_attributes
  @object = klass.new
  conditionally_set_reindex_extent
  run_callbacks :save do
    run_callbacks :create do
      @object = 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



294
295
296
297
298
299
300
301
302
303
304
305
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 294

def delete(user)
  obj = find
  raise ObjectFactoryInterface::ObjectNotFoundError, "Object not found to delete" unless obj
  # delete the file sets when we delete a work
  # This has to be done before the work is deleted or we can't find them
  # via the custom query
  destroy_existing_files(object: obj)

  Hyrax.persister.delete(resource: obj)
  Hyrax.index_adapter.delete(resource: obj)
  Hyrax.publisher.publish('object.deleted', object: obj, user: user)
end

#run!Object



307
308
309
310
311
312
313
314
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 307

def run!
  run
  # reload the object
  object = find
  return object if object&.persisted?

  raise(ObjectFactoryInterface::RecordInvalid, object)
end

#updateObject

Customized update method for Valkyrie so that @object gets set



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'app/factories/bulkrax/valkyrie_object_factory.rb', line 63

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

  attrs = transform_attributes(update: true)
  run_callbacks :save do
    @object = 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