Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add contexts to the flexible metadata model. #6915

Open
wants to merge 1 commit into
base: flexible_double_combo
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions app/controllers/concerns/hyrax/works_controller_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def new
# TODO: move these lines to the work form builder in Hyrax
curation_concern.depositor = current_user.user_key
curation_concern.admin_set_id = params[:admin_set_id] || admin_set_id_for_new
curation_concern.contexts = Hyrax.query_service.find_by(id: curation_concern.admin_set_id)&.contexts
build_form
end

Expand Down Expand Up @@ -178,7 +179,8 @@ def admin_set_id_for_new
Hyrax::AdminSetCreateService.find_or_create_default_admin_set.id.to_s
end

def build_form
def build_form(contexts: [])
curation_concern.contexts = contexts unless contexts.blank?
@form = work_form_service.build(curation_concern, current_ability, self)
end

Expand All @@ -190,7 +192,7 @@ def actor
# @return [#errors]
# rubocop:disable Metrics/MethodLength
def create_valkyrie_work
form = build_form
form = build_form(contexts: params[hash_key_for_curation_concern]['contexts'])
action = create_valkyrie_work_action.new(form: form,
transactions: transactions,
user: current_user,
Expand Down
39 changes: 39 additions & 0 deletions app/forms/concerns/hyrax/flexible_form_behavior.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true
module Hyrax
module FlexibleFormBehavior
extend ActiveSupport::Concern

included do
include Hyrax::BasedNearFieldBehavior
property :contexts
end

# OVERRIDE disposable 0.6.3 to make schema dynamic
def schema
if Hyrax.config.flexible?
Hyrax::Forms::ResourceForm::Definition::Each.new(singleton_class.schema_definitions)
else
super
end
end

private

# OVERRIDE valkyrie 3.0.1 to make schema dynamic
def field(field_name)
if Hyrax.config.flexible?
singleton_class.schema_definitions.fetch(field_name.to_s)
else
super
end
end

def _form_field_definitions
if Hyrax.config.flexible?
singleton_class.schema_definitions
else
self.class.definitions
end
end
end
end
2 changes: 1 addition & 1 deletion app/forms/hyrax/forms/administrative_set_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class AdministrativeSetForm < Hyrax::Forms::ResourceForm

property :title, required: true, primary: true
property :description, primary: true

property :contexts, primary: true if Hyrax.config.flexible?
property :creator

validates :title, presence: true
Expand Down
35 changes: 3 additions & 32 deletions app/forms/hyrax/forms/resource_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ResourceForm < Hyrax::ChangeSet # rubocop:disable Metrics/ClassLength
# These do not get auto loaded when using a flexible schema and should instead
# be added to the individual Form classes for a work type or smart enough
# to be selective as to when they trigger
include BasedNearFieldBehavior if Hyrax.config.flexible?
include FlexibleFormBehavior if Hyrax.config.flexible?

##
# @api private
Expand Down Expand Up @@ -56,8 +56,7 @@ def initialize(deprecated_resource = nil, resource: nil) # rubocop:disable Metri
if Hyrax.config.flexible?
singleton_class.schema_definitions = self.class.definitions
r = resource || deprecated_resource

Hyrax::Schema.default_schema_loader.form_definitions_for(schema: r.class.to_s, version: Hyrax::FlexibleSchema.current_schema_id).map do |field_name, options|
Hyrax::Schema.default_schema_loader.form_definitions_for(schema: r.class.to_s, version: Hyrax::FlexibleSchema.current_schema_id, contexts: r.contexts).map do |field_name, options|
singleton_class.property field_name.to_sym, options.merge(display: options.fetch(:display, true), default: [])
singleton_class.validates field_name.to_sym, presence: true if options.fetch(:required, false)
end
Expand Down Expand Up @@ -169,7 +168,7 @@ def primary_terms
.select { |_, definition| definition[:primary] }
.keys.map(&:to_sym)

terms = [:schema_version] + terms if Hyrax.config.flexible?
terms = [:schema_version, :contexts] + terms if Hyrax.config.flexible?
terms
end

Expand All @@ -186,34 +185,6 @@ def secondary_terms
def display_additional_fields?
secondary_terms.any?
end

# OVERRIDE disposable 0.6.3 to make schema dynamic
def schema
if Hyrax.config.flexible?
Definition::Each.new(singleton_class.schema_definitions)
else
super
end
end

private

# OVERRIDE valkyrie 3.0.1 to make schema dynamic
def field(field_name)
if Hyrax.config.flexible?
singleton_class.schema_definitions.fetch(field_name.to_s)
else
super
end
end

def _form_field_definitions
if Hyrax.config.flexible?
singleton_class.schema_definitions
else
self.class.definitions
end
end
end
end
end
2 changes: 1 addition & 1 deletion app/helpers/hyrax/attributes_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def view_options_for(presenter)
model_name = presenter.model.model_name.name

if Hyrax.config.flexible?
Hyrax::Schema.default_schema_loader.view_definitions_for(schema: model_name, version: presenter.solr_document.schema_version)
Hyrax::Schema.default_schema_loader.view_definitions_for(schema: model_name, version: presenter.solr_document.schema_version, contexts: presenter.solr_document.contexts)
else
schema = model_name.constantize.schema || (model_name + 'Resource').safe_constantize.schema
Hyrax::Schema.default_schema_loader.view_definitions_for(schema:)
Expand Down
9 changes: 8 additions & 1 deletion app/models/concerns/hyrax/flexibility.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Flexibility
extend ActiveSupport::Concern
included do
attribute :schema_version, Valkyrie::Types::String
attribute :contexts, Valkyrie::Types::Set.of(Valkyrie::Types::String)
end

class_methods do
Expand Down Expand Up @@ -70,13 +71,19 @@ def load(attributes, safe = false)
attributes[:schema_version] ||= Hyrax::FlexibleSchema.order('id DESC').pick(:id)
struct = allocate
schema_version = attributes[:schema_version]
struct.singleton_class.attributes(Hyrax::Schema(self, schema_version:).attributes)
contexts = attributes[:contexts] || []
struct.singleton_class.attributes(Hyrax::Schema(self, schema_version:, contexts:).attributes)
clean_attributes = safe ? struct.singleton_class.schema.call_safe(attributes) { |output = attributes| return yield output } : struct.singleton_class.schema.call_unsafe(attributes)
struct.__send__(:initialize, clean_attributes)
struct
end
end

def contexts=(value)
val = Array.wrap(value).map { |v| v.split }.flatten
@attributes[:contexts] = val
end

# Override set_value from valkyrie 3.1.1 to enable dynamic schema loading
def set_value(key, value)
@attributes[key.to_sym] = self.singleton_class.schema.key(key.to_sym).type.call(value)
Expand Down
6 changes: 5 additions & 1 deletion app/models/concerns/hyrax/solr_document_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def valkyrie?
def hydra_model(classifier: nil)
model_name = first('has_model_ssim')
valkyrie_model = "#{model_name}Resource".safe_constantize if Hyrax.config.valkyrie_transition

valkyrie_model ||
model_name&.safe_constantize ||
model_classifier(classifier).classifier(self).best_model
Expand Down Expand Up @@ -153,6 +153,10 @@ def schema_version
self['schema_version_ssi']
end

def contexts
self['contexts_ssim']
end

private

def model_classifier(classifier)
Expand Down
1 change: 1 addition & 0 deletions app/models/hyrax/file_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module Hyrax
class FileSet < Hyrax::Resource
include Hyrax::Schema(:core_metadata) unless Hyrax.config.flexible?
include Hyrax::Schema(:file_set_metadata) unless Hyrax.config.flexible?
# TODO why isn't this automatic?
include Hyrax::Schema('Hyrax::FileSet') if Hyrax.config.flexible?

def self.model_name(name_class: Hyrax::Name)
Expand Down
18 changes: 15 additions & 3 deletions app/models/hyrax/flexible_schema.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# frozen_string_literal: true
class Hyrax::FlexibleSchema < ApplicationRecord
serialize :profile, coder: YAML

serialize :contexts, coder: YAML

validate :validate_profile_classes

before_save :update_contexts

def self.current_version
order("created_at asc").last.profile
end
Expand All @@ -30,7 +33,11 @@ def self.default_properties
rescue StandardError => e
[]
end


def update_contexts
self.contexts = profile['contexts']
end

def title
"#{profile['profile']['responsibility_statement']} - version #{id}"
end
Expand All @@ -43,6 +50,10 @@ def schema_version
profile['m3_version']
end

def context_select
contexts.map { |k, v| [v&.[]('display_label'), k] }
end

def metadata_profile_type
profile['profile']['type']
end
Expand All @@ -61,7 +72,7 @@ def validate_profile_classes
required_classes = [
Hyrax.config.collection_model,
Hyrax.config.file_set_model,
Hyrax.config.admin_set_model
Hyrax.config.admin_set_model
]

if profile['classes'].blank?
Expand Down Expand Up @@ -97,6 +108,7 @@ def values_map(values)
values['predicate'] = values['property_uri']
values['index_keys'] = values['indexing']
values['multiple'] = values['multi_value']
values['context'] = values['available_on']['context']
values
end

Expand Down
16 changes: 12 additions & 4 deletions app/services/hyrax/m3_schema_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ module Hyrax
#
# @see config/metadata_profiles/m3_profile.yaml for an example configuration
class M3SchemaLoader < Hyrax::SchemaLoader
def view_definitions_for(schema:, version: 1)
definitions(schema, version).each_with_object({}) do |definition, hash|
def view_definitions_for(schema:, version: 1, contexts: nil)
definitions(schema, version, contexts).each_with_object({}) do |definition, hash|
next if definition.view_options.empty?

hash[definition.name] = definition.view_options
Expand All @@ -21,11 +21,19 @@ def view_definitions_for(schema:, version: 1)
##
# @param [#to_s] schema_name
# @return [Enumerable<AttributeDefinition]
def definitions(schema_name, version)
def definitions(schema_name, version, contexts = nil)
schema = Hyrax::FlexibleSchema.find_by(id: version) || Hyrax::FlexibleSchema.create_default_schema
schema.attributes_for(schema_name).map do |name, config|
# We might be able to consolidate these conditions, but they have been kept seperate to make it easier to reason about
# If there is a context filter on the metadata field and no context is set, skip it
next if contexts.blank? && config['context'].present?

# If there is a context filter on the metadata field and we have set a context, but the context does not match, skip it
next if contexts.present? && config['context'].present? && !contexts.intersect?(config['context'])

# Wew, we are in the clear to use this field
AttributeDefinition.new(name, config)
end
end.compact
rescue ActiveRecord::StatementInvalid
Rails.logger.error "Skipping definition load for migrations to run"
[]
Expand Down
14 changes: 7 additions & 7 deletions app/services/hyrax/schema_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class UndefinedSchemaError < ArgumentError; end
#
# @return [Hash<Symbol, Dry::Types::Type>] a map from attribute names to
# types
def attributes_for(schema:, version: 1)
definitions(schema, version).each_with_object({}) do |definition, hash|
def attributes_for(schema:, version: 1, contexts: nil)
definitions(schema, version, contexts).each_with_object({}) do |definition, hash|
hash[definition.name] = definition.type.meta(definition.config)
end
end
Expand All @@ -25,8 +25,8 @@ def attributes_for(schema:, version: 1)
# @param [Symbol] schema
#
# @return [Hash{Symbol => Hash{Symbol => Object}}]
def form_definitions_for(schema:, version: 1)
definitions(schema, version).each_with_object({}) do |definition, hash|
def form_definitions_for(schema:, version: 1, contexts: nil)
definitions(schema, version, contexts).each_with_object({}) do |definition, hash|
next if definition.form_options.empty?

hash[definition.name] = definition.form_options
Expand All @@ -37,8 +37,8 @@ def form_definitions_for(schema:, version: 1)
# @param [Symbol] schema
#
# @return [{Symbol => Symbol}] a map from index keys to attribute names
def index_rules_for(schema:, version: 1)
definitions(schema, version).each_with_object({}) do |definition, hash|
def index_rules_for(schema:, version: 1, contexts: nil)
definitions(schema, version, contexts).each_with_object({}) do |definition, hash|
definition.index_keys.each do |key|
hash[key] = definition.name
end
Expand Down Expand Up @@ -140,7 +140,7 @@ class UndefinedSchemaError < ArgumentError; end

private

def definitions(_schema_name, _version)
def definitions(_schema_name, _version, _contexts)
raise NotImplementedError, 'Implement #definitions in a child class'
end
end
Expand Down
4 changes: 2 additions & 2 deletions app/services/hyrax/simple_schema_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Hyrax
#
# @see config/metadata/basic_metadata.yaml for an example configuration
class SimpleSchemaLoader < Hyrax::SchemaLoader
def view_definitions_for(schema:, _version: 1)
def view_definitions_for(schema:, version: 1, contexts: nil)
schema.each_with_object({}) do |property, metadata|
view_options = property.meta['view']
metadata[property.name.to_s] = view_options unless view_options.nil?
Expand All @@ -26,7 +26,7 @@ def permissive_schema_for_valkrie_adapter
##
# @param [#to_s] schema_name
# @return [Enumerable<AttributeDefinition]
def definitions(schema_name, _version)
def definitions(schema_name, _version, _contexts = nil)
schema_config(schema_name)['attributes'].map do |name, config|
AttributeDefinition.new(name, config)
end
Expand Down
3 changes: 3 additions & 0 deletions app/views/hyrax/admin/admin_sets/_form_metadata.html.erb
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
<%= f.input :title %>
<%= f.input :description, as: :text %>
<% if Hyrax.config.flexible? && Hyrax.config.admin_set_class.new.respond_to?(:contexts) %>
<%= f.input :contexts, collection: Hyrax::FlexibleSchema.last.context_select, input_html: { multiple: true } %>
<% end %>
1 change: 1 addition & 0 deletions app/views/records/edit_fields/_contexts.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= f.input key, as: :hidden %>
2 changes: 1 addition & 1 deletion app/views/shared/_schema_version.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<% schema_version = SolrDocument.find(f.object.id).schema_version if action_name == 'edit' %>
<% schema_version = Hyrax::FlexibleSchema.current_schema_id if action_name == 'new' %>
<% updating = (schema_version.to_f < @latest_schema_version) ? 'updating' : '' %>
<% updating = (schema_version.to_f < @latest_schema_version.to_f) ? 'updating' : '' %>

<div class='pull-right'>
<% if schema_version.present? %>
Expand Down
Loading