Skip to content

Commit

Permalink
Resolves #346 by ensuring that Ephemera Projects have IIIF Manifests …
Browse files Browse the repository at this point in the history
…exposing Boxes as Manifests; Ensures that Ephemera Projects have slugs generated for them; Ensures that these slugs are published using RabbitMQ when EphemeraBoxes and EphemeraFolders are updated
  • Loading branch information
jrgriffiniii committed Oct 16, 2017
1 parent bc6a63d commit 9f5ca17
Show file tree
Hide file tree
Showing 30 changed files with 471 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ def initialize(change_set_persister:, change_set:, post_save_resource: nil)

def run
messenger.record_updated(post_save_resource)
# For cases where the resource is a FileSet, propagate for the parent resource
messenger.record_updated(post_save_resource.decorate.parent) if post_save_resource.is_a? FileSet
end

delegate :messenger, to: :change_set_persister
Expand Down
3 changes: 2 additions & 1 deletion app/change_sets/ephemera_project_change_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ class EphemeraProjectChangeSet < Valkyrie::ChangeSet
validates :title, presence: true
property :title, multiple: false
property :member_ids, multiple: true, required: false, type: Types::Strict::Array.member(Valkyrie::Types::ID)
property :slug, multiple: false, required: false

def primary_terms
[:title]
[:title, :slug]
end
end
11 changes: 10 additions & 1 deletion app/controllers/ephemera_projects_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class EphemeraProjectsController < ApplicationController
class EphemeraProjectsController < BaseResourceController
include Valhalla::ResourceController
include TokenAuth
self.change_set_class = DynamicChangeSet
Expand All @@ -14,6 +14,15 @@ def index
render 'index'
end

def manifest
@resource = find_resource(params[:id])
respond_to do |f|
f.json do
render json: ManifestBuilder.new(@resource).build
end
end
end

private

def load_ephemera_projects
Expand Down
4 changes: 4 additions & 0 deletions app/decorators/collection_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ def manageable_files?
false
end

def members
@members ||= query_service.find_inverse_references_by(resource: model, property: :member_of_collection_ids).to_a
end

# Nested collections are not currently supported
def parents
[]
Expand Down
10 changes: 9 additions & 1 deletion app/decorators/ephemera_box_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@ def title
end

def members
@members ||= find_members(resource: model)
@members ||= query_service.find_members(resource: model).to_a
end

def folders
@folders ||= members.select { |r| r.is_a?(EphemeraFolder) }.map(&:decorate).to_a
end

def ephemera_projects
@ephemera_projects ||= query_service.find_parents(resource: model).map(&:decorate).to_a
end

def collection_slugs
@collection_slugs ||= ephemera_projects.map(&:slug)
end

def ephemera_project
@ephemera_box ||= query_service.find_parents(resource: model).to_a.first.try(:decorate) || NullProject.new
end
Expand Down
4 changes: 4 additions & 0 deletions app/decorators/ephemera_field_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
class EphemeraFieldDecorator < Valkyrie::ResourceDecorator
self.display_attributes = [:rendered_name, :vocabulary]

def parents
@parents ||= query_service.find_parents(resource: model).to_a
end

def projects
@projects ||= parents.select { |r| r.is_a?(EphemeraProject) }.map(&:decorate).to_a
end
Expand Down
15 changes: 11 additions & 4 deletions app/decorators/ephemera_folder_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ class EphemeraFolderDecorator < Valkyrie::ResourceDecorator
]
self.iiif_manifest_attributes = display_attributes + [:title] - \
imported_attributes(Schema::Common.attributes) - \
Schema::IIIF.attributes - \
[:visibility, :internal_resource, :rights_statement, :rendered_rights_statement, :thumbnail_id, :provenance]
Schema::IIIF.attributes - [:visibility, :internal_resource, :rights_statement, :rendered_rights_statement, :thumbnail_id]

def members
@members ||= query_service.find_members(resource: model).to_a
end

def collections
@collections ||= query_service.find_references_by(resource: self, property: :member_of_collection_ids).to_a.map(&:decorate)
Expand Down Expand Up @@ -63,11 +66,15 @@ def rendered_rights_statement
end

def ephemera_box
@ephemera_box ||= query_service.find_parents(resource: model).to_a.first.try(:decorate)
@ephemera_box ||= query_service.find_parents(resource: model).map(&:decorate).to_a.first
end

def ephemera_project
@ephemera_project ||= ephemera_box.ephemera_project
@ephemera_project ||= ephemera_box.try(:ephemera_project)
end

def collection_slugs
@collection_slugs ||= Array.wrap(ephemera_project.try(:slug))
end

def manageable_files?
Expand Down
54 changes: 54 additions & 0 deletions app/decorators/ephemera_project_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
class EphemeraProjectDecorator < Valkyrie::ResourceDecorator
self.display_attributes = [:title]

def members
@members ||= query_service.find_members(resource: model).to_a
end

def boxes
@boxes ||= members.select { |r| r.is_a?(EphemeraBox) }.map(&:decorate).to_a
end
Expand All @@ -25,4 +29,54 @@ def manageable_structure?
def title
super.first
end

# Access (or generate) the slug ID for the decorated resource
# @return [String] the slug value
def slug
value = super
generated_slug.value unless value.present?
end

def iiif_manifest_attributes
local_attributes(self.class.iiif_manifest_attributes).merge iiif_manifest_exhibit
end

# A local identifier (slug) generated for EphemeraProjects
class Slug
attr_reader :value

# Initialize using a prefix, seed, and optional delimiter
# @param prefix [String] the prefix for the slug ID
# @param seed [String] the value used to generate the suffix for the slug
# @param delimiter [String] the delimiter used between the prefix and suffix
def initialize(prefix:, seed:, delimiter: '-')
@prefix = prefix
@seed = seed
@delimiter = delimiter
@value = generate
end
alias to_s value

private

# Generate the slug ID value
# @return [String] the string value used as the slug
def generate
@prefix + @delimiter + @seed.slice(0, 4)
end
end

private

# Generate the slug value from the Valkyrie Resource ID
# @return [Slug] the slug for the resource
def generated_slug
@slug ||= Slug.new(prefix: 'lae', seed: model.id.to_s)
end

# Generate the Hash for the IIIF Manifest metadata exposing the slug as an "Exhibit" property
# @return [Hash] the exhibit metadata hash
def iiif_manifest_exhibit
{ exhibit: slug }
end
end
8 changes: 8 additions & 0 deletions app/decorators/file_set_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ def manageable_files?
false
end

def parents
query_service.find_parents(resource: model).to_a.map(&:decorate)
end

def parent
parents.first
end

def collections
[]
end

def collection_slugs
@collection_slugs ||= parent.try(:collection_slugs)
end
end
6 changes: 6 additions & 0 deletions app/decorators/index_collection_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true
class IndexCollectionDecorator < Valkyrie::ResourceDecorator
def iiif_manifest_attributes
[]
end
end
4 changes: 4 additions & 0 deletions app/decorators/scanned_map_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ class ScannedMapDecorator < Valkyrie::ResourceDecorator
self.iiif_manifest_attributes = display_attributes + [:title] - \
Schema::IIIF.attributes - [:visibility, :internal_resource, :rights_statement, :rendered_rights_statement, :thumbnail_id]

def members
@members ||= query_service.find_members(resource: model).to_a
end

def scanned_map_members
@scanned_maps ||= members.select { |r| r.is_a?(ScannedMap) }.map(&:decorate).to_a
end
Expand Down
8 changes: 8 additions & 0 deletions app/decorators/scanned_resource_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ class ScannedResourceDecorator < Valkyrie::ResourceDecorator
Schema::IIIF.attributes - [:visibility, :internal_resource, :rights_statement, :rendered_rights_statement, :thumbnail_id]
delegate(*Schema::Common.attributes, to: :primary_imported_metadata, prefix: :imported)

def members
@members ||= query_service.find_members(resource: model).to_a
end

def volumes
@volumes ||= members.select { |r| r.is_a?(ScannedResource) }.map(&:decorate).to_a
end
Expand Down Expand Up @@ -65,6 +69,10 @@ def parents
end
alias collections parents

def collection_slugs
@collection_slugs ||= collections.map(&:slug)
end

def display_imported_language
(imported_language || []).map do |language|
ControlledVocabulary.for(:language).find(language).label
Expand Down
4 changes: 4 additions & 0 deletions app/decorators/vector_work_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
class VectorWorkDecorator < Valkyrie::ResourceDecorator
self.display_attributes += Schema::Geo.attributes + [:rendered_coverage, :member_of_collections] - [:thumbnail_id, :coverage]

def members
@members ||= query_service.find_members(resource: model).to_a
end

# Use case for nesting vector works
# - time series: e.g., nyc transit system, released every 6 months
def vector_work_members
Expand Down
5 changes: 5 additions & 0 deletions app/models/ephemera_project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ class EphemeraProject < Valhalla::Resource
attribute :id, Valkyrie::Types::ID.optional
attribute :member_ids, Valkyrie::Types::Array
attribute :title, Valkyrie::Types::Set
attribute :slug, Valkyrie::Types::Set

def logical_structure
[]
end
end
10 changes: 10 additions & 0 deletions app/models/index_collection.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# frozen_string_literal: true
# Model for exposing IIIF Manifests for all Collection resources
class IndexCollection
# Decorates the object (as this is not a Valkyrie::Resource)
# @return [IndexCollectionDecorator] an instance of the decorator
def decorate
IndexCollectionDecorator.new(self)
end

def logical_structure
[]
end
end
45 changes: 23 additions & 22 deletions app/services/manifest_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ def build
# Presenter modeling the Resource subjects as root nodes
class RootNode
def self.for(resource)
if resource.is_a?(Collection)
case resource
when Collection
CollectionNode.new(resource)
elsif resource.is_a?(IndexCollection)
when IndexCollection
IndexCollectionNode.new(resource)
elsif resource.is_a?(ScannedMap)
when ScannedMap
ScannedMapNode.new(resource)
else
new(resource)
Expand All @@ -48,7 +49,12 @@ def to_s
end

def description
value = resource.respond_to?(:primary_imported_metadata) ? resource.primary_imported_metadata.description : resource.description
value = if resource.respond_to?(:primary_imported_metadata)
resource.primary_imported_metadata.try(:description)
else
resource.try(:description)
end

Array.wrap(value).first
end

Expand Down Expand Up @@ -106,7 +112,7 @@ def helper
# Retrieve the child members for the subject resource of the Manifest
# @return [Resource]
def members
@members ||= query_service.find_members(resource: resource).to_a
@members ||= decorate.members
end

##
Expand Down Expand Up @@ -136,32 +142,20 @@ def file_set_presenters
[]
end

def members
@members ||= query_service.find_inverse_references_by(resource: resource, property: :member_of_collection_ids).to_a
end

def viewing_hint
nil
end

def description
resource.description
end
end

class IndexCollectionNode < RootNode
def file_set_presenters
[]
def viewing_hint
'multi-part'
end
end

class IndexCollectionNode < CollectionNode
def members
@members ||= query_service.find_all_of_model(model: Collection).to_a
end

def viewing_hint
nil
end

def manifest_url
helper.index_manifest_url
end
Expand All @@ -173,6 +167,10 @@ def to_s
def description
"All collections which are a part of Plum."
end

def id
nil
end
end

class ScannedMapNode < RootNode
Expand Down Expand Up @@ -328,8 +326,11 @@ def default_url_options
end

def manifest_url(resource)
if resource.is_a?(Collection)
case resource
when Collection
"#{protocol}://#{host}/collections/#{resource.id}/manifest"
when FileSet
"#{protocol}://#{host}/concern/#{resource.decorate.parent.model_name.plural}/#{resource.decorate.parent.id}/manifest"
else
"#{protocol}://#{host}/concern/#{resource.model_name.plural}/#{resource.id}/manifest"
end
Expand Down
Loading

0 comments on commit 9f5ca17

Please sign in to comment.