Skip to content
Merged
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
7 changes: 7 additions & 0 deletions app/forms/concerns/hyrax/redirects_field_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ module Hyrax
module RedirectsFieldBehavior
def self.included(descendant)
return unless Hyrax.config.redirects_enabled?
# Load `redirects` (the persisted property the form partial reads
# via `f.object.redirects`) from `config/metadata/redirects.yaml`.
# In non-flexible mode this is the only thing that puts the
# property on the form. In flexible mode the m3 loader adds an
# equivalent definition on initialize; redefining via `property`
# is idempotent so this is a safe no-op there.
descendant.include Hyrax::FormFields(:redirects)
descendant.property :redirects_attributes,
virtual: true,
populator: :redirects_attributes_populator,
Expand Down
2 changes: 1 addition & 1 deletion config/metadata/redirects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ attributes:
type: hash
multiple: true
form:
primary: false
display: false
predicate: http://samvera.org/ns/hyku/redirects
mappings:
simple_dc_pmh: ~
11 changes: 8 additions & 3 deletions documentation/forms/field_behaviors.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@ Reform's `FormBuilderMethods#deserialize!` rewrites the submitted `<name>_attrib

A Field Behavior is a module that:

1. **Registers a virtual `<name>_attributes` property** in `self.included`, with a populator and prepopulator.
1. **Registers a virtual `<name>_attributes` property** in `self.included`, with a populator and prepopulator. If the persisted `<name>` property is not already on the form via some other include path, also load it from the corresponding YAML schema in `self.included` so the form partial can read `f.object.<name>` directly.

```ruby
def self.included(descendant)
descendant.include Hyrax::FormFields(:<name>)
descendant.property :<name>_attributes,
virtual: true,
populator: :<name>_attributes_populator,
prepopulator: :<name>_attributes_prepopulator
end
```

Whether to load the persisted property depends on where the schema is included on application forms. `BasedNearFieldBehavior` skips this step because adopter forms include `Hyrax::FormFields(:basic_metadata)` directly, which already registers `based_near`. `RedirectsFieldBehavior` includes it because the redirects schema is engine-level and not part of any per-form include — without the include here, non-flexible forms would not have the property registered, and the partial's `f.object.redirects` call would crash. A flexible install's m3 loader redefines the property on each instance harmlessly when this include is present.

2. **Composes via `super` in `deserialize!`**, then deletes its own renamed key.

```ruby
Expand Down Expand Up @@ -94,6 +97,7 @@ If your behavior is tied to a feature flag (Flipflop, env config, etc.), gate **
```ruby
def self.included(descendant)
return unless Hyrax.config.my_feature_enabled?
descendant.include Hyrax::FormFields(:<name>)
descendant.property ...
end

Expand Down Expand Up @@ -196,6 +200,7 @@ module Hyrax
module RedirectsFieldBehavior
def self.included(descendant)
return unless Hyrax.config.redirects_enabled?
descendant.include Hyrax::FormFields(:redirects)
descendant.property :redirects_attributes,
virtual: true,
populator: :redirects_attributes_populator,
Expand Down Expand Up @@ -235,9 +240,9 @@ module Hyrax
end
```

- **Persisted shape:** array of plain hashes (`'path'`, `'canonical'`, `'sequence'`). Declared with `type: hash, multiple: true` in the YAML schema.
- **Persisted shape:** array of plain hashes (`'path'`, `'canonical'`, `'sequence'`). Declared with `type: hash, multiple: true` in `config/metadata/redirects.yaml` and loaded onto the form via `Hyrax::FormFields(:redirects)` in `self.included`.
- **View-side shape:** array of `Hyrax::Redirect` presenters, exposing `.path` / `.canonical` / `.sequence`.
- **Diff from BasedNear:** entries carry multiple sub-fields, so the persisted shape is a hash rather than a string. The populator normalizes paths up front (canonical form lives in storage). The behavior is feature-gated — every callback consults `Hyrax.config.redirects_active?`.
- **Diff from BasedNear:** entries carry multiple sub-fields, so the persisted shape is a hash rather than a string. The populator normalizes paths up front (canonical form lives in storage). The behavior is feature-gated — every callback consults `Hyrax.config.redirects_active?`. The behavior also loads the persisted `redirects` property from YAML in `self.included`, since `Hyrax::Schema(:redirects)` puts the attribute on the model but the form needs the property registered separately for the partial's `f.object.redirects` call to work.

## Wiring on `ResourceForm`

Expand Down
4 changes: 2 additions & 2 deletions documentation/redirects.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ attributes:
type: hash
multiple: true
form:
primary: false
display: false
predicate: http://samvera.org/ns/hyku/redirects
mappings:
simple_dc_pmh: ~
Expand Down Expand Up @@ -281,7 +281,7 @@ Alternatively, gate the inclusion of the calling code itself on `Hyrax.config.re
- `documentation/flexible_metadata.md` — m3 profile fundamentals and how the redirects feature interacts with flexible metadata.
- `documentation/forms/field_behaviors.md` — the Field Behavior pattern used by `Hyrax::RedirectsFieldBehavior` to wire the form's nested-attribute property.
- `Hyrax::Redirect` (`app/models/hyrax/redirect.rb`) — thin Ruby presenter for a single redirect entry; used on the form's render path.
- `Hyrax::RedirectsFieldBehavior` (`app/forms/concerns/hyrax/redirects_field_behavior.rb`) — form-side populator/prepopulator and the `deserialize!` strip for the `redirects` property.
- `Hyrax::RedirectsFieldBehavior` (`app/forms/concerns/hyrax/redirects_field_behavior.rb`) — form-side wiring for the `redirects` and `redirects_attributes` properties: loads the persisted property from `config/metadata/redirects.yaml` via `Hyrax::FormFields(:redirects)`, and owns the populator/prepopulator and the `deserialize!` strip for the nested-attributes payload.
- `Hyrax::Indexers::RedirectsIndexer` (`app/indexers/hyrax/indexers/redirects_indexer.rb`) — the indexer mixin.
- `Hyrax::RedirectsController` (`app/controllers/hyrax/redirects_controller.rb`) — the redirect resolver.
- `Hyrax::FlexibleSchemaValidators::RedirectsValidator` (`app/services/hyrax/flexible_schema_validators/redirects_validator.rb`) — the m3 profile validator.
Expand Down
7 changes: 7 additions & 0 deletions spec/forms/concerns/hyrax/redirects_field_behavior_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def self.property(*); end
end

it 'registers the redirects_attributes virtual property when enabled' do
expect(property_target).to receive(:property).with(:redirects, hash_including(default: []))
expect(property_target).to receive(:property).with(
:redirects_attributes,
virtual: true,
Expand All @@ -49,6 +50,12 @@ def self.property(*); end
)
property_target.include(described_class)
end

it 'registers the redirects property loaded from redirects.yaml' do
expect(property_target).to receive(:property).with(:redirects, hash_including(default: []))
expect(property_target).to receive(:property).with(:redirects_attributes, anything)
property_target.include(described_class)
end
end

describe '#deserialize!' do
Expand Down
37 changes: 37 additions & 0 deletions spec/forms/hyrax/forms/resource_form_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,43 @@ def self.name
end
end

describe '#redirects' do
# When `redirects_enabled?` is true at class load,
# `RedirectsFieldBehavior.included` must put both `redirects` and
# `redirects_attributes` on the form. The view partial
# `_form_redirects.html.erb` calls `f.object.redirects` directly,
# so a missing `redirects` property crashes the Aliases tab.
#
# The engine test suite boots with `redirects_enabled?` off, so the
# app's models and forms don't carry the redirects properties. Build
# synthetic ones here with the gate stubbed open and the schema
# included from `config/metadata/redirects.yaml`, mirroring an
# installation that has the feature on.
subject(:form) { form_class.new(resource_class.new) }

before do
allow(Hyrax.config).to receive(:redirects_enabled?).and_return(true)
stub_const('RedirectsTestResource', Class.new(Hyrax::Resource) { include Hyrax::Schema(:redirects) })
end

let(:resource_class) { RedirectsTestResource }

let(:form_class) do
resource = resource_class
Class.new(Hyrax::Forms::ResourceForm(resource)) do
include Hyrax::RedirectsFieldBehavior
end
end

it 'exposes the redirects property so the form partial can render' do
expect(form).to respond_to(:redirects)
end

it 'exposes the redirects_attributes virtual property for nested form params' do
expect(form).to respond_to(:redirects_attributes)
end
end

describe '#embargo_release_date' do
context 'without an embargo' do
it 'is nil' do
Expand Down
Loading