Skip to content

Commit

Permalink
Merge branch 'main' into hyrax-4-valkyrie-support
Browse files Browse the repository at this point in the history
* main:
  🎁 Config option for coercing a factory class name (#901)
  ♻️ Extract Bulkrax::FactoryClassFinder (#900)
  πŸ“š Adding documentation for configuration (#896)
  • Loading branch information
jeremyf committed Jan 26, 2024
2 parents 8311fe0 + acb0138 commit 2d33420
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 14 deletions.
62 changes: 48 additions & 14 deletions app/services/bulkrax/factory_class_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,62 @@

module Bulkrax
class FactoryClassFinder
##
# The v6.0.0 default coercer. Responsible for converting a factory class name to a constant.
module DefaultCoercer
##
# @param name [String]
# @return [Class] when the name is a coercible constant.
# @raise [NameError] when the name is not coercible to a constant.
def self.call(name)
name.constantize
end
end

##
# A name coercer that favors classes that end with "Resource" but will attempt to fallback to
# those that don't.
module ValkyrieMigrationCoercer
SUFFIX = "Resource"

##
# @param name [String]
# @param suffix [String] the suffix we use for a naming convention.
#
# @return [Class] when the name is a coercible constant.
# @raise [NameError] when the name is not coercible to a constant.
def self.call(name, suffix: SUFFIX)
if name.end_with?(suffix)
name.constantize
else
begin
"#{name}#{suffix}".constantize
rescue NameError
name.constantize
end
end
end
end

##
# @param entry [Bulkrax::Entry]
# @return [Class]
def self.find(entry:)
new(entry: entry).find
def self.find(entry:, coercer: Bulkrax.factory_class_name_coercer || DefaultCoercer)
new(entry: entry, coercer: coercer).find
end

def initialize(entry:)
def initialize(entry:, coercer:)
@entry = entry
@coercer = coercer
end
attr_reader :entry
attr_reader :entry, :coercer

##
# @return [Class] when we are able to derive the class based on the {#name}.
# @return [Nil] when we encounter errors with constantizing the {#name}.
# @see #name
def find
# TODO: We have a string, now we want to consider how we coerce. Let's say we have Work and
# WorkResource in our upstream application. Work extends ActiveFedora::Base and is legacy.
# And WorkResource extends Valkyrie::Resource and is where we want to be moving. We may want
# to coerce the "Work" name into "WorkResource"
name.constantize
coercer.call(name)
rescue NameError
nil
rescue
Expand All @@ -41,13 +75,13 @@ def name
# details.
Array.wrap(entry.parsed_metadata['work_type']).first
else
# The string might be frozen, so lets duplicate
entry.default_work_type.dup
entry.default_work_type
end

# Let's coerce this into the right shape.
fc.tr!(' ', '_')
fc.downcase! if fc.match?(/[-_]/)
# Let's coerce this into the right shape; we're not mutating the string because it might well
# be frozen.
fc = fc.tr(' ', '_')
fc = fc.downcase if fc.match?(/[-_]/)
fc.camelcase
rescue
entry.default_work_type
Expand Down
21 changes: 21 additions & 0 deletions lib/bulkrax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,25 @@ class Configuration
# @param adapter [Class<Bulkrax::PersistenceLayer::AbstractAdapter>]
attr_writer :persistence_adapter

##
# @param coercer [#call]
# @see Bulkrax::FactoryClassFinder
attr_writer :factory_class_name_coercer

##
# A function responsible for converting the name of a factory class to the corresponding
# constant.
#
# @return [#call, Bulkrax::FactoryClassFinder::DefaultCoercer] an object responding to call,
# with one positional parameter (e.g. arity == 1)
#
# @example
# Bulkrax.factory_class_name_coercer.call("Work")
# => Work
def factory_class_name_coercer
@factory_class_name_coercer || Bulkrax::FactoryClassFinder::DefaultCoercer
end

##
# Configure the persistence adapter used for persisting imported data.
#
Expand Down Expand Up @@ -99,6 +118,8 @@ def config
:default_work_type=,
:export_path,
:export_path=,
:factory_class_name_coercer,
:factory_class_name_coercer=,
:field_mappings,
:field_mappings=,
:file_model_class,
Expand Down
10 changes: 10 additions & 0 deletions spec/lib/bulkrax_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,14 @@
it { is_expected.to respond_to(:solr_name) }
it { is_expected.to respond_to(:clean!) }
end

context '.factory_class_name_coercer' do
subject { described_class.factory_class_name_coercer }

it { is_expected.to respond_to(:call) }

it "has a method arity of 1" do
expect(subject.method(:call).arity).to eq 1
end
end
end
60 changes: 60 additions & 0 deletions spec/services/bulkrax/factory_class_finder_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Bulkrax::FactoryClassFinder do
let(:legacy_work_class) { Class.new }
let(:legacy_work_resource_class) { Class.new }
let(:valkyrie_only_resource_class) { Class.new }
let(:active_fedora_only_class) { Class.new }

before do
Object.const_set(:LegacyWork, legacy_work_class)
Object.const_set(:LegacyWorkResource, legacy_work_resource_class)
Object.const_set(:ValkyrieOnlyResource, valkyrie_only_resource_class)
Object.const_set(:ActiveFedoraOnly, active_fedora_only_class)
end

after do
Object.send(:remove_const, :LegacyWork)
Object.send(:remove_const, :LegacyWorkResource)
Object.send(:remove_const, :ValkyrieOnlyResource)
Object.send(:remove_const, :ActiveFedoraOnly)
end

describe "DefaultCoercer" do
it "simply constantizes (unsafely) the given string" do
factory_class_name = "Work"
expect(described_class::DefaultCoercer.call(factory_class_name)).to eq(Work)
end
end

describe "ValkyrieMigrationCoercer" do
it 'favors mapping names to those ending in Resource' do
expect(described_class::ValkyrieMigrationCoercer.call("LegacyWork")).to eq(legacy_work_resource_class)
expect(described_class::ValkyrieMigrationCoercer.call("LegacyWorkResource")).to eq(legacy_work_resource_class)
expect(described_class::ValkyrieMigrationCoercer.call("ValkyrieOnlyResource")).to eq(valkyrie_only_resource_class)
expect(described_class::ValkyrieMigrationCoercer.call("ValkyrieOnly")).to eq(valkyrie_only_resource_class)
expect(described_class::ValkyrieMigrationCoercer.call("ActiveFedoraOnly")).to eq(active_fedora_only_class)
expect { described_class::ValkyrieMigrationCoercer.call("ActiveFedoraOnlyResource") }.to raise_error(NameError)
end
end

describe '.find' do
let(:entry) { double(Bulkrax::Entry, parsed_metadata: { "model" => model_name }, default_work_type: "Work") }
subject(:finder) { described_class.find(entry: entry, coercer: coercer) }

[
[Bulkrax::FactoryClassFinder::DefaultCoercer, "Legacy Work", "LegacyWork"],
[Bulkrax::FactoryClassFinder::ValkyrieMigrationCoercer, "Legacy Work", "LegacyWorkResource"],
[Bulkrax::FactoryClassFinder::DefaultCoercer, "Legacy Work Resource", "LegacyWorkResource"]
].each do |given_coercer, given_model_name, expected_class_name|
context "with an entry with model: #{given_model_name} and coercer: #{given_coercer}" do
let(:model_name) { given_model_name }
let(:coercer) { given_coercer }

it { is_expected.to eq expected_class_name.constantize }
end
end
end
end

0 comments on commit 2d33420

Please sign in to comment.