From b43a8db22a2a7070d521f25f65c1c667cf2b7065 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 23 Mar 2018 11:48:31 -0400 Subject: [PATCH 01/20] WIP Tests running --- lib/iiif_manifest.rb | 1 + lib/iiif_manifest/iiif_collection.rb | 10 +- lib/iiif_manifest/v3.rb | 10 + lib/iiif_manifest/v3/manifest_builder.rb | 32 ++ .../v3/manifest_builder/canvas_builder.rb | 17 + .../v3/manifest_builder/iiif_service.rb | 168 +++++++++ .../record_property_builder.rb | 18 + lib/iiif_manifest/v3/manifest_factory.rb | 36 ++ .../v3/manifest_service_locator.rb | 82 +++++ .../iiif_manifest/v3/manifest_factory_spec.rb | 333 ++++++++++++++++++ 10 files changed, 703 insertions(+), 4 deletions(-) create mode 100644 lib/iiif_manifest/v3.rb create mode 100644 lib/iiif_manifest/v3/manifest_builder.rb create mode 100644 lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb create mode 100644 lib/iiif_manifest/v3/manifest_builder/iiif_service.rb create mode 100644 lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb create mode 100644 lib/iiif_manifest/v3/manifest_factory.rb create mode 100644 lib/iiif_manifest/v3/manifest_service_locator.rb create mode 100644 spec/lib/iiif_manifest/v3/manifest_factory_spec.rb diff --git a/lib/iiif_manifest.rb b/lib/iiif_manifest.rb index bd0599a..2ecd88e 100644 --- a/lib/iiif_manifest.rb +++ b/lib/iiif_manifest.rb @@ -11,4 +11,5 @@ module IIIFManifest autoload :DisplayImage autoload :IIIFCollection autoload :IIIFEndpoint + autoload :V3 end diff --git a/lib/iiif_manifest/iiif_collection.rb b/lib/iiif_manifest/iiif_collection.rb index 457caa0..2684e72 100644 --- a/lib/iiif_manifest/iiif_collection.rb +++ b/lib/iiif_manifest/iiif_collection.rb @@ -1,6 +1,8 @@ -class IIIFCollection < SimpleDelegator - def viewing_hint - return super if __getobj__.respond_to?(:viewing_hint) - 'multi-part' +module IIIFManifest + class IIIFCollection < SimpleDelegator + def viewing_hint + return super if __getobj__.respond_to?(:viewing_hint) + 'multi-part' + end end end diff --git a/lib/iiif_manifest/v3.rb b/lib/iiif_manifest/v3.rb new file mode 100644 index 0000000..0513d75 --- /dev/null +++ b/lib/iiif_manifest/v3.rb @@ -0,0 +1,10 @@ +require 'active_support' +require 'active_support/core_ext/module' +require 'active_support/core_ext/object' + +module IIIFManifest::V3 + extend ActiveSupport::Autoload + autoload :ManifestBuilder + autoload :ManifestFactory + autoload :ManifestServiceLocator +end diff --git a/lib/iiif_manifest/v3/manifest_builder.rb b/lib/iiif_manifest/v3/manifest_builder.rb new file mode 100644 index 0000000..7f883d2 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder.rb @@ -0,0 +1,32 @@ +require_relative 'manifest_builder/iiif_service' +require_relative 'manifest_builder/canvas_builder' +require_relative 'manifest_builder/record_property_builder' + +module IIIFManifest::V3 + class ManifestBuilder + attr_reader :work, + :builders, + :top_record_factory + def initialize(work, builders:, top_record_factory:) + @work = work + @builders = builders + @top_record_factory = top_record_factory + end + + def apply(collection) + collection['manifests'] ||= [] + collection['manifests'] << to_h + collection + end + + def to_h + @to_h ||= builders.new(work).apply(top_record) + end + + private + + def top_record + top_record_factory.new + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb new file mode 100644 index 0000000..83b2bfe --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -0,0 +1,17 @@ +module IIIFManifest::V3 + class ManifestBuilder + class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder + def apply(items) + return items if canvas.images.blank? + items += [canvas] + end + + private + + def apply_record_properties + canvas['id'] = path + canvas.label = record.to_s + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb new file mode 100644 index 0000000..f2b1ecd --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -0,0 +1,168 @@ +module IIIFManifest::V3 + class ManifestBuilder + class IIIFService < IIIFManifest::ManifestBuilder::IIIFService + end + + class IIIFManifest < IIIFService + def label + inner_hash['label'] + end + + def label=(label) + inner_hash['label'] = label + end + + def summary=(summary) + return unless summary.present? + inner_hash['summary'] = summary + end + + def viewing_direction=(viewing_direction) + return unless viewing_direction.present? + inner_hash['viewingDirection'] = viewing_direction + end + + def viewingDirection + inner_hash['viewingDirection'] + end + + def items + inner_hash['items'] || [] + end + + def items=(items) + inner_hash['items'] = items + end + + def metadata=(metadata) + inner_hash['metadata'] = metadata + end + + def service + inner_hash['service'] || [] + end + + def service=(service) + inner_hash['service'] = service + end + + def see_also=(see_also) + inner_hash['seeAlso'] = see_also + end + + def rights=(rights) + inner_hash['rights'] = rights + end + + def initial_attributes + { + '@context' => [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3/context.json" + ], + 'type' => 'Manifest' + } + end + + class Collection < IIIFManifest + def initial_attributes + { + '@context' => [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3/context.json" + ], + 'type' => 'Collection' + } + end + + def viewing_direction=(viewing_direction) + raise NotImplementedError + end + + def viewingDirection + raise NotImplementedError + end + end + + class Canvas < IIIFService + def label=(label) + inner_hash['label'] = label + end + + def items + inner_hash['items'] || [] + end + + def items=(items) + inner_hash['items'] = items + end + + def initial_attributes + { + 'type' => 'Canvas' + } + end + end + + class Range < IIIFService + def initial_attributes + { + 'type' => 'Range' + } + end + end + + class Annotation < IIIFService + def body=(body) + inner_hash['body'] = body + end + + def body + inner_hash['body'] + end + + def initial_attributes + { + 'type' => 'Annotation', + 'motivation' => 'painting' + } + end + end + + class SearchService < IIIFService + def service=(service) + inner_hash['service'] = service + end + + def search_service=(search_service) + inner_hash['id'] = search_service + end + + def initial_attributes + { + '@context' => 'http://iiif.io/api/search/1/context.json', + 'profile' => 'http://iiif.io/api/search/1/search', + 'label' => 'Search within this manifest' + } + end + end + + class AutocompleteService < IIIFService + def autocomplete_service + inner_hash['id'] + end + + def autocomplete_service=(autocomplete_service) + inner_hash['id'] = autocomplete_service + end + + def initial_attributes + { + 'profile' => 'http://iiif.io/api/search/1/autocomplete', + 'label' => 'Get suggested words in this manifest' + } + end + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb new file mode 100644 index 0000000..6a30594 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -0,0 +1,18 @@ +module IIIFManifest::V3 + class ManifestBuilder + class RecordPropertyBuilder < ::IIIFManifest::ManifestBuilder::RecordPropertyBuilder + # rubocop:disable Metrics/AbcSize + def apply(manifest) + manifest['id'] = record.manifest_url.to_s + manifest.label = record.to_s + manifest.summary = record.description + manifest.viewing_hint = viewing_hint if viewing_hint.present? + manifest.viewing_direction = viewing_direction if viewing_direction.present? + manifest.metadata = record.manifest_metadata if valid_metadata? + manifest.service = services if search_service.present? + manifest + end + # rubocop:enable Metrics/AbcSize + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_factory.rb b/lib/iiif_manifest/v3/manifest_factory.rb new file mode 100644 index 0000000..0a93394 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_factory.rb @@ -0,0 +1,36 @@ +module IIIFManifest::V3 + class ManifestFactory + class << self + def new(work, manifest_service_locator: ManifestServiceLocator) + super(manifest_service_locator).new(work) + end + end + + delegate :collection_manifest_builder, :manifest_builder, :sammelband_manifest_builder, + to: :manifest_service_locator + attr_reader :manifest_service_locator + + def initialize(manifest_service_locator) + @manifest_service_locator = manifest_service_locator + end + + def new(work) + if !work.work_presenters.empty? + if sammelband?(work) || !work.file_set_presenters.empty? + sammelband_manifest_builder.new(work) + elsif work.file_set_presenters.empty? + work = IIIFManifest::IIIFCollection.new(work) + collection_manifest_builder.new(work) + end + else + manifest_builder.new(work) + end + end + + private + + def sammelband?(work) + work.respond_to?(:sammelband?) && work.sammelband? + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb new file mode 100644 index 0000000..998206c --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -0,0 +1,82 @@ +module IIIFManifest::V3 + class ManifestServiceLocator < IIIFManifest::ManifestServiceLocator + class << self + # Builders which receive a work as an argument to .new and return objects + # that respond to #apply. + def manifest_builders + composite_builder_factory.new( + record_property_builder, + structure_builder, + composite_builder: composite_builder + ) + end + + def sammelband_manifest_builders + composite_builder_factory.new( + record_property_builder, + composite_builder: composite_builder + ) + end + + def collection_manifest_builders + composite_builder_factory.new( + record_property_builder, + child_manifest_builder_factory, + composite_builder: composite_builder + ) + end + + def iiif_collection_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Collection + end + + def record_property_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::RecordPropertyBuilder, + iiif_search_service_factory: iiif_search_service_factory, + iiif_autocomplete_service_factory: iiif_autocomplete_service_factory + ) + end + + def sequence_builder + raise NotImplementedError + end + + def sammelband_sequence_builder + raise NotImplementedError + end + + def sequence_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Sequence + end + + def iiif_service_factory + IIIFManifest::V3::ManifestBuilder::IIIFService + end + + def iiif_annotation_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation + end + + def iiif_manifest_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest + end + + def iiif_canvas_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Canvas + end + + def iiif_range_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Range + end + + def iiif_search_service_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::SearchService + end + + def iiif_autocomplete_service_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::AutocompleteService + end + end + end +end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb new file mode 100644 index 0000000..ea1e379 --- /dev/null +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -0,0 +1,333 @@ +require 'spec_helper' + +RSpec.describe IIIFManifest::V3::ManifestFactory do + subject { described_class.new(book_presenter) } + + let(:presenter_class) { Book } + let(:book_presenter) { presenter_class.new('book-77') } + + before do + class Book + def initialize(id) + @id = id + end + + def description + 'a brief description' + end + + def file_set_presenters + [] + end + + def work_presenters + [] + end + + def manifest_url + "http://test.host/books/#{@id}/manifest" + end + + def ranges + @ranges ||= + [ + ManifestRange.new(label: 'Table of Contents', ranges: [ + ManifestRange.new(label: 'Chapter 1', file_set_presenters: []) + ]) + ] + end + end + + class ManifestRange + attr_reader :label, :ranges, :file_set_presenters + def initialize(label:, ranges: [], file_set_presenters: []) + @label = label + @ranges = ranges + @file_set_presenters = file_set_presenters + end + end + + class DisplayImagePresenter + def id + 'test-22' + end + + def display_image + IIIFManifest::DisplayImage.new(id, width: 100, height: 100, format: 'image/jpeg') + end + end + end + + after do + Object.send(:remove_const, :DisplayImagePresenter) + Object.send(:remove_const, :Book) + end + + describe '#to_h' do + let(:result) { subject.to_h } + let(:json_result) { JSON.parse(subject.to_h.to_json) } + + it 'has a label' do + expect(result.label).to eq book_presenter.to_s + end + it 'has an ID' do + expect(result['@id']).to eq 'http://test.host/books/book-77/manifest' + end + + context 'when there are no files' do + it 'returns no sequences' do + expect(result['sequences']).to eq nil + end + end + + context 'when there is a fileset' do + let(:file_presenter) { DisplayImagePresenter.new } + + it 'returns a sequence' do + allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + + result + + expect(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to have_received(:new) + .exactly(1).times.with(file_presenter, anything, anything) + end + it 'builds a structure if it can' do + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + allow(book_presenter.ranges[0].ranges[0]).to receive(:file_set_presenters).and_return([file_presenter]) + + expect(result['structures'].length).to eq 2 + structure = result['structures'].first + expect(structure['label']).to eq 'Table of Contents' + expect(structure['viewingHint']).to eq 'top' + expect(structure['canvases']).to be_blank + expect(structure['ranges'].length).to eq 1 + expect(structure['ranges'][0]).not_to eq structure['@id'] + + sub_range = result['structures'].last + expect(sub_range['ranges']).to be_blank + expect(sub_range['canvases'].length).to eq 1 + end + end + + context 'when there is a no sequence_rendering method' do + let(:file_presenter) { DisplayImagePresenter.new } + + it 'does not have a rendering on the sequence' do + allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + expect(result['sequences'][0]['rendering']).to eq [] + end + end + + context 'when there is a sequence_rendering method' do + let(:file_presenter) { DisplayImagePresenter.new } + + before do + class Book + def initialize(id) + @id = id + end + + def description + 'a brief description' + end + + def file_set_presenters + [] + end + + def work_presenters + [] + end + + def manifest_url + "http://test.host/books/#{@id}/manifest" + end + + def sequence_rendering + [{ '@id' => 'http://test.host/file_set/id/download', + 'format' => 'application/pdf', + 'label' => 'Download' }] + end + end + end + + it 'has a rendering on the sequence' do + allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + + expect(result['sequences'][0]['rendering']).to eq [{ + '@id' => 'http://test.host/file_set/id/download', 'format' => 'application/pdf', 'label' => 'Download' + }] + end + end + + context 'when there is no manifest_metadata method' do + let(:file_presenter) { DisplayImagePresenter.new } + + it 'does not have a metadata element' do + allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + expect(result['metadata']).to eq nil + end + end + + context 'when there is a manifest_metadata method' do + let(:metadata) { [{ 'label' => 'Title', 'value' => 'Title of the Item' }] } + + it 'has metadata' do + allow(book_presenter).to receive(:manifest_metadata).and_return(metadata) + expect(result['metadata'][0]['label']).to eq 'Title' + expect(result['metadata'][0]['value']).to eq 'Title of the Item' + end + end + + context 'when there is a manifest_metadata method with invalid data' do + let(:metadata) { 'invalid data' } + + it 'has no metadata' do + allow(book_presenter).to receive(:manifest_metadata).and_return(metadata) + expect(result['metadata']).to eq nil + end + end + + context 'when there is no search_service method' do + let(:file_presenter) { DisplayImagePresenter.new } + + it 'does not have a service element' do + allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + expect(result['service']).to eq nil + end + end + + context 'when there is a search_service method' do + let(:search_service) { 'http://test.host/books/book-77/search' } + + it 'has a service element with the correct profile, @id and without an embedded service element' do + allow(book_presenter).to receive(:search_service).and_return(search_service) + expect(result['service'][0]['profile']).to eq 'http://iiif.io/api/search/0/search' + expect(result['service'][0]['@id']).to eq 'http://test.host/books/book-77/search' + expect(result['service'][0]['service']).to eq nil + end + end + + context 'when there is a search_service method that returns nil' do + let(:search_service) { '' } + + it 'has no service' do + allow(book_presenter).to receive(:search_service).and_return(search_service) + expect(result['service']).to eq nil + end + end + + context 'when there is an autocomplete_service method' do + let(:search_service) { 'http://test.host/books/book-77/search' } + let(:autocomplete_service) { 'http://test.host/books/book-77/autocomplete' } + + it 'has a service element within the first service containing @id and profile for the autocomplete service' do + allow(book_presenter).to receive(:search_service).and_return(search_service) + allow(book_presenter).to receive(:autocomplete_service).and_return(autocomplete_service) + expect(result['service'][0]['service']['@id']).to eq 'http://test.host/books/book-77/autocomplete' + expect(result['service'][0]['service']['profile']).to eq 'http://iiif.io/api/search/0/autocomplete' + end + end + + context 'when there is no autocomplete_service method' do + let(:search_service) { 'http://test.host/books/book-77/search' } + + it 'has a service element within the first service' do + allow(book_presenter).to receive(:search_service).and_return(search_service) + expect(result['service'][0]['service']).to eq nil + end + end + + context 'when there is an autocomplete_service method but no search service' do + let(:autocomplete_service) { 'http://test.host/books/book-77/autocomplete' } + + it 'has a service element within the first service' do + allow(book_presenter).to receive(:autocomplete_service).and_return(autocomplete_service) + expect(result['service']).to eq nil + end + end + + context 'when there are child works' do + let(:child_work_presenter) { presenter_class.new('test2') } + + before do + allow(book_presenter).to receive(:work_presenters).and_return([child_work_presenter]) + end + it 'returns a IIIF Collection' do + expect(result['@type']).to eq 'sc:Collection' + end + it "doesn't build sequences" do + expect(result['sequences']).to eq nil + end + it 'has a multi-part viewing hint' do + expect(json_result['viewingHint']).to eq 'multi-part' + end + it 'builds child manifests' do + expect(result['manifests'].length).to eq 1 + first_child = result['manifests'].first + expect(first_child['@id']).to eq 'http://test.host/books/test2/manifest' + expect(first_child['@type']).to eq 'sc:Manifest' + expect(first_child['label']).to eq child_work_presenter.to_s + end + end + + context 'when there are child works AND files' do + let(:child_work_presenter) { presenter_class.new('test-99') } + let(:file_presenter) { DisplayImagePresenter.new } + let(:file_presenter2) { DisplayImagePresenter.new } + + before do + allow(book_presenter).to receive(:work_presenters).and_return([child_work_presenter]) + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + allow(child_work_presenter).to receive(:file_set_presenters).and_return([file_presenter2]) + end + it 'returns a IIIF Manifest' do + expect(result['@type']).to eq 'sc:Manifest' + end + it "doesn't build manifests" do + expect(result['manifests']).to eq nil + end + it 'builds sequences from all the child file sets' do + expect(result['sequences'].first['canvases'].length).to eq 2 + end + end + + context 'when there are child works AMD when the work identifies itself as a sammelband' do + let(:child_work_presenter) { presenter_class.new('test-99') } + let(:file_presenter) { DisplayImagePresenter.new } + + before do + allow(book_presenter).to receive(:sammelband?).and_return(true) + allow(book_presenter).to receive(:work_presenters).and_return([child_work_presenter]) + allow(child_work_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + end + it 'returns a IIIF Manifest' do + expect(result['@type']).to eq 'sc:Manifest' + end + it "doesn't build manifests" do + expect(result['manifests']).to eq nil + end + it 'builds sequences from all the child file sets' do + expect(result['sequences'].first['canvases'].length).to eq 1 + end + end + + context 'when there is no viewing_direction method' do + it 'does not have a viewingDirection element' do + expect(result['viewingDirection']).to eq nil + end + end + + context 'when there is a viewing_direction method' do + it 'has a viewingDirection' do + allow(book_presenter).to receive(:viewing_direction).and_return('right-to-left') + expect(result.viewingDirection).to eq 'right-to-left' + end + end + end +end From 5803b5da1568a1677c7729eda80e3ffb7e95cb40 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 23 Mar 2018 22:50:43 -0400 Subject: [PATCH 02/20] viewingHint -> behavior, @id -> id, @type -> type --- .../v3/manifest_builder/iiif_service.rb | 7 ++++- .../record_property_builder.rb | 2 +- .../iiif_manifest/v3/manifest_factory_spec.rb | 28 +++++++++---------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index f2b1ecd..0891d2d 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -2,7 +2,7 @@ module IIIFManifest::V3 class ManifestBuilder class IIIFService < IIIFManifest::ManifestBuilder::IIIFService end - + class IIIFManifest < IIIFService def label inner_hash['label'] @@ -17,6 +17,11 @@ def summary=(summary) inner_hash['summary'] = summary end + def behavior=(behavior) + return unless behavior.present? + inner_hash['behavior'] = behavior + end + def viewing_direction=(viewing_direction) return unless viewing_direction.present? inner_hash['viewingDirection'] = viewing_direction diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index 6a30594..37141ca 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -6,7 +6,7 @@ def apply(manifest) manifest['id'] = record.manifest_url.to_s manifest.label = record.to_s manifest.summary = record.description - manifest.viewing_hint = viewing_hint if viewing_hint.present? + manifest.behavior = viewing_hint if viewing_hint.present? manifest.viewing_direction = viewing_direction if viewing_direction.present? manifest.metadata = record.manifest_metadata if valid_metadata? manifest.service = services if search_service.present? diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index ea1e379..f5bdeb1 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -71,7 +71,7 @@ def display_image expect(result.label).to eq book_presenter.to_s end it 'has an ID' do - expect(result['@id']).to eq 'http://test.host/books/book-77/manifest' + expect(result['id']).to eq 'http://test.host/books/book-77/manifest' end context 'when there are no files' do @@ -102,7 +102,7 @@ def display_image expect(structure['viewingHint']).to eq 'top' expect(structure['canvases']).to be_blank expect(structure['ranges'].length).to eq 1 - expect(structure['ranges'][0]).not_to eq structure['@id'] + expect(structure['ranges'][0]).not_to eq structure['id'] sub_range = result['structures'].last expect(sub_range['ranges']).to be_blank @@ -205,10 +205,10 @@ def sequence_rendering context 'when there is a search_service method' do let(:search_service) { 'http://test.host/books/book-77/search' } - it 'has a service element with the correct profile, @id and without an embedded service element' do + it 'has a service element with the correct profile, id and without an embedded service element' do allow(book_presenter).to receive(:search_service).and_return(search_service) - expect(result['service'][0]['profile']).to eq 'http://iiif.io/api/search/0/search' - expect(result['service'][0]['@id']).to eq 'http://test.host/books/book-77/search' + expect(result['service'][0]['profile']).to eq 'http://iiif.io/api/search/1/search' + expect(result['service'][0]['id']).to eq 'http://test.host/books/book-77/search' expect(result['service'][0]['service']).to eq nil end end @@ -226,11 +226,11 @@ def sequence_rendering let(:search_service) { 'http://test.host/books/book-77/search' } let(:autocomplete_service) { 'http://test.host/books/book-77/autocomplete' } - it 'has a service element within the first service containing @id and profile for the autocomplete service' do + it 'has a service element within the first service containing id and profile for the autocomplete service' do allow(book_presenter).to receive(:search_service).and_return(search_service) allow(book_presenter).to receive(:autocomplete_service).and_return(autocomplete_service) - expect(result['service'][0]['service']['@id']).to eq 'http://test.host/books/book-77/autocomplete' - expect(result['service'][0]['service']['profile']).to eq 'http://iiif.io/api/search/0/autocomplete' + expect(result['service'][0]['service']['id']).to eq 'http://test.host/books/book-77/autocomplete' + expect(result['service'][0]['service']['profile']).to eq 'http://iiif.io/api/search/1/autocomplete' end end @@ -259,19 +259,19 @@ def sequence_rendering allow(book_presenter).to receive(:work_presenters).and_return([child_work_presenter]) end it 'returns a IIIF Collection' do - expect(result['@type']).to eq 'sc:Collection' + expect(result['type']).to eq 'Collection' end it "doesn't build sequences" do expect(result['sequences']).to eq nil end it 'has a multi-part viewing hint' do - expect(json_result['viewingHint']).to eq 'multi-part' + expect(json_result['behavior']).to eq 'multi-part' end it 'builds child manifests' do expect(result['manifests'].length).to eq 1 first_child = result['manifests'].first - expect(first_child['@id']).to eq 'http://test.host/books/test2/manifest' - expect(first_child['@type']).to eq 'sc:Manifest' + expect(first_child['id']).to eq 'http://test.host/books/test2/manifest' + expect(first_child['type']).to eq 'Manifest' expect(first_child['label']).to eq child_work_presenter.to_s end end @@ -287,7 +287,7 @@ def sequence_rendering allow(child_work_presenter).to receive(:file_set_presenters).and_return([file_presenter2]) end it 'returns a IIIF Manifest' do - expect(result['@type']).to eq 'sc:Manifest' + expect(result['type']).to eq 'Manifest' end it "doesn't build manifests" do expect(result['manifests']).to eq nil @@ -307,7 +307,7 @@ def sequence_rendering allow(child_work_presenter).to receive(:file_set_presenters).and_return([file_presenter]) end it 'returns a IIIF Manifest' do - expect(result['@type']).to eq 'sc:Manifest' + expect(result['type']).to eq 'Manifest' end it "doesn't build manifests" do expect(result['manifests']).to eq nil From ee7d594ca3487703c5d86e40750dd512d13fbe6c Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Sat, 24 Mar 2018 08:41:20 -0400 Subject: [PATCH 03/20] Move rendering from sequence to manifest --- lib/iiif_manifest/v3/manifest_builder/iiif_service.rb | 4 ++++ .../v3/manifest_builder/record_property_builder.rb | 9 +++++++++ spec/lib/iiif_manifest/v3/manifest_factory_spec.rb | 10 +++++----- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 0891d2d..62fe483 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -55,6 +55,10 @@ def see_also=(see_also) inner_hash['seeAlso'] = see_also end + def rendering=(rendering) + inner_hash['rendering'] = rendering + end + def rights=(rights) inner_hash['rights'] = rights end diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index 37141ca..d500d5b 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -10,9 +10,18 @@ def apply(manifest) manifest.viewing_direction = viewing_direction if viewing_direction.present? manifest.metadata = record.manifest_metadata if valid_metadata? manifest.service = services if search_service.present? + manifest.rendering = populate_rendering manifest end # rubocop:enable Metrics/AbcSize + + def populate_rendering + if record.respond_to?(:sequence_rendering) + record.sequence_rendering.each(&:to_h) + else + [] + end + end end end end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index f5bdeb1..2d9b8fd 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -75,8 +75,8 @@ def display_image end context 'when there are no files' do - it 'returns no sequences' do - expect(result['sequences']).to eq nil + it 'returns no items' do + expect(result['items']).to eq nil end end @@ -110,13 +110,13 @@ def display_image end end - context 'when there is a no sequence_rendering method' do + context 'when there is no sequence_rendering method' do let(:file_presenter) { DisplayImagePresenter.new } it 'does not have a rendering on the sequence' do allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) - expect(result['sequences'][0]['rendering']).to eq [] + expect(result['rendering']).to eq [] end end @@ -157,7 +157,7 @@ def sequence_rendering allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) - expect(result['sequences'][0]['rendering']).to eq [{ + expect(result['rendering']).to eq [{ '@id' => 'http://test.host/file_set/id/download', 'format' => 'application/pdf', 'label' => 'Download' }] end From 4f7f92dca90e592b352d7a8027cb47efe88ad4cf Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Sun, 25 Mar 2018 20:57:37 -0400 Subject: [PATCH 04/20] WIP Builds Canvases --- lib/iiif_manifest/v3/manifest_builder.rb | 2 ++ .../v3/manifest_builder/canvas_builder.rb | 2 +- .../v3/manifest_builder/image_builder.rb | 12 +++++++ .../record_property_builder.rb | 14 ++++++++ .../v3/manifest_builder/resource_builder.rb | 15 ++++++++ .../v3/manifest_service_locator.rb | 36 +++++++++++++++++-- .../iiif_manifest/v3/manifest_factory_spec.rb | 18 ++++++---- 7 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 lib/iiif_manifest/v3/manifest_builder/image_builder.rb create mode 100644 lib/iiif_manifest/v3/manifest_builder/resource_builder.rb diff --git a/lib/iiif_manifest/v3/manifest_builder.rb b/lib/iiif_manifest/v3/manifest_builder.rb index 7f883d2..f76cef9 100644 --- a/lib/iiif_manifest/v3/manifest_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder.rb @@ -1,6 +1,8 @@ require_relative 'manifest_builder/iiif_service' require_relative 'manifest_builder/canvas_builder' require_relative 'manifest_builder/record_property_builder' +require_relative 'manifest_builder/image_builder' +require_relative 'manifest_builder/resource_builder' module IIIFManifest::V3 class ManifestBuilder diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index 83b2bfe..886c38a 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -2,7 +2,7 @@ module IIIFManifest::V3 class ManifestBuilder class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder def apply(items) - return items if canvas.images.blank? + return items if canvas.items.blank? items += [canvas] end diff --git a/lib/iiif_manifest/v3/manifest_builder/image_builder.rb b/lib/iiif_manifest/v3/manifest_builder/image_builder.rb new file mode 100644 index 0000000..d62cc03 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/image_builder.rb @@ -0,0 +1,12 @@ +module IIIFManifest::V3 + class ManifestBuilder + class ImageBuilder < ::IIIFManifest::ManifestBuilder::ImageBuilder + def apply(canvas) + annotation['target'] = canvas['id'] + canvas['width'] = annotation.body['width'] + canvas['height'] = annotation.body['height'] + canvas.items += [annotation] + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index d500d5b..63e166a 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -1,6 +1,12 @@ module IIIFManifest::V3 class ManifestBuilder class RecordPropertyBuilder < ::IIIFManifest::ManifestBuilder::RecordPropertyBuilder + attr_reader :canvas_builder_factory + def initialize(record, iiif_search_service_factory:, iiif_autocomplete_service_factory:, canvas_builder_factory:) + super(record, iiif_search_service_factory: iiif_search_service_factory, iiif_autocomplete_service_factory: iiif_autocomplete_service_factory) + @canvas_builder_factory = canvas_builder_factory + end + # rubocop:disable Metrics/AbcSize def apply(manifest) manifest['id'] = record.manifest_url.to_s @@ -11,6 +17,8 @@ def apply(manifest) manifest.metadata = record.manifest_metadata if valid_metadata? manifest.service = services if search_service.present? manifest.rendering = populate_rendering + # Build the items array + canvas_builder.apply(manifest.items) manifest end # rubocop:enable Metrics/AbcSize @@ -22,6 +30,12 @@ def populate_rendering [] end end + + private + + def canvas_builder + canvas_builder_factory.from(record) + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb b/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb new file mode 100644 index 0000000..1377307 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb @@ -0,0 +1,15 @@ +module IIIFManifest::V3 + class ManifestBuilder + class ResourceBuilder < ::IIIFManifest::ManifestBuilder::ResourceBuilder + def apply(annotation) + resource['id'] = display_image.url + resource['type'] = 'dctypes:Image' + resource['height'] = display_image.height + resource['width'] = display_image.width + resource['format'] = display_image.format + image_service_builder.apply(resource) if iiif_endpoint + annotation.body = resource + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index 998206c..5bb0685 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -34,7 +34,39 @@ def record_property_builder IIIFManifest::ManifestServiceLocator::InjectedFactory.new( ManifestBuilder::RecordPropertyBuilder, iiif_search_service_factory: iiif_search_service_factory, - iiif_autocomplete_service_factory: iiif_autocomplete_service_factory + iiif_autocomplete_service_factory: iiif_autocomplete_service_factory, + canvas_builder_factory: canvas_builder_factory + ) + end + + def canvas_builder_factory + IIIFManifest::ManifestBuilder::CanvasBuilderFactory.new( + composite_builder: composite_builder, + canvas_builder_factory: canvas_builder + ) + end + + def canvas_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::CanvasBuilder, + iiif_canvas_factory: iiif_canvas_factory, + image_builder: image_builder + ) + end + + def image_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::ImageBuilder, + iiif_annotation_factory: iiif_annotation_factory, + resource_builder_factory: resource_builder_factory + ) + end + + def resource_builder_factory + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::ResourceBuilder, + iiif_resource_factory: iiif_resource_factory, + image_service_builder_factory: image_service_builder_factory ) end @@ -47,7 +79,7 @@ def sammelband_sequence_builder end def sequence_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::Sequence + raise NotImplementedError end def iiif_service_factory diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index 2d9b8fd..0e515f8 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -100,13 +100,13 @@ def display_image structure = result['structures'].first expect(structure['label']).to eq 'Table of Contents' expect(structure['viewingHint']).to eq 'top' - expect(structure['canvases']).to be_blank + expect(structure['items']).to be_blank expect(structure['ranges'].length).to eq 1 expect(structure['ranges'][0]).not_to eq structure['id'] sub_range = result['structures'].last expect(sub_range['ranges']).to be_blank - expect(sub_range['canvases'].length).to eq 1 + expect(sub_range['items'].length).to eq 1 end end @@ -292,8 +292,11 @@ def sequence_rendering it "doesn't build manifests" do expect(result['manifests']).to eq nil end - it 'builds sequences from all the child file sets' do - expect(result['sequences'].first['canvases'].length).to eq 2 + it 'builds items array from all the child file sets' do + expect(result['items'].length).to eq 2 + end + it 'builds structures from all the child file sets' do + expect(result['structures'].first['items'].length).to eq 2 end end @@ -312,8 +315,11 @@ def sequence_rendering it "doesn't build manifests" do expect(result['manifests']).to eq nil end - it 'builds sequences from all the child file sets' do - expect(result['sequences'].first['canvases'].length).to eq 1 + it 'builds items array from all the child file sets' do + expect(result['items'].length).to eq 1 + end + it 'builds structures from all the child file sets' do + expect(result['structures'].first['items'].length).to eq 1 end end From 343af50952e6202341f62136b45f9bddd3b1ea25 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Sun, 25 Mar 2018 21:53:31 -0400 Subject: [PATCH 05/20] WIP ranges and sub-ranges --- lib/iiif_manifest/v3/manifest_builder.rb | 1 + .../v3/manifest_builder/structure_builder.rb | 41 +++++++++++++++++++ .../v3/manifest_service_locator.rb | 8 ++++ .../iiif_manifest/v3/manifest_factory_spec.rb | 13 +++--- 4 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 lib/iiif_manifest/v3/manifest_builder/structure_builder.rb diff --git a/lib/iiif_manifest/v3/manifest_builder.rb b/lib/iiif_manifest/v3/manifest_builder.rb index f76cef9..6f88632 100644 --- a/lib/iiif_manifest/v3/manifest_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder.rb @@ -3,6 +3,7 @@ require_relative 'manifest_builder/record_property_builder' require_relative 'manifest_builder/image_builder' require_relative 'manifest_builder/resource_builder' +require_relative 'manifest_builder/structure_builder' module IIIFManifest::V3 class ManifestBuilder diff --git a/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb b/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb new file mode 100644 index 0000000..8cbef23 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb @@ -0,0 +1,41 @@ +module IIIFManifest::V3 + class ManifestBuilder + class StructureBuilder < ::IIIFManifest::ManifestBuilder::StructureBuilder + def range_builder(top_range) + RangeBuilder.new( + top_range, + record, true, + canvas_builder_factory: canvas_builder_factory, + iiif_range_factory: iiif_range_factory + ) + end + end + class RangeBuilder < ::IIIFManifest::ManifestBuilder::RangeBuilder + def apply(manifest) + manifest << range + sub_ranges.map do |sub_range| + sub_range.apply(range['items']) + end + manifest + end + + def build_range + range['id'] = path + range['label'] = record.label + range['behavior'] = 'top' if top? + range['items'] = canvas_builders.map(&:path) + end + + def sub_ranges + @sub_ranges ||= record.ranges.map do |sub_range| + RangeBuilder.new( + sub_range, + parent, + canvas_builder_factory: canvas_builder_factory, + iiif_range_factory: iiif_range_factory + ) + end + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index 5bb0685..f0d6a6d 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -39,6 +39,14 @@ def record_property_builder ) end + def structure_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::StructureBuilder, + canvas_builder_factory: canvas_builder, + iiif_range_factory: iiif_range_factory + ) + end + def canvas_builder_factory IIIFManifest::ManifestBuilder::CanvasBuilderFactory.new( composite_builder: composite_builder, diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index 0e515f8..7d2b40c 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -96,16 +96,13 @@ def display_image allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) allow(book_presenter.ranges[0].ranges[0]).to receive(:file_set_presenters).and_return([file_presenter]) - expect(result['structures'].length).to eq 2 + expect(result['structures'].length).to eq 1 structure = result['structures'].first expect(structure['label']).to eq 'Table of Contents' - expect(structure['viewingHint']).to eq 'top' - expect(structure['items']).to be_blank - expect(structure['ranges'].length).to eq 1 - expect(structure['ranges'][0]).not_to eq structure['id'] - - sub_range = result['structures'].last - expect(sub_range['ranges']).to be_blank + expect(structure['behavior']).to eq 'top' + expect(structure['items'].length).to eq 1 + expect(structure['items'][0]['type']).to eq 'Range' + sub_range = structure['items'][0] expect(sub_range['items'].length).to eq 1 end end From 948fd03191856c9fbfad0082430e8720b958f586 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Sun, 25 Mar 2018 22:00:53 -0400 Subject: [PATCH 06/20] WIP Rewrite id attribute of rendering hash --- .../v3/manifest_builder/record_property_builder.rb | 6 +++++- spec/lib/iiif_manifest/v3/manifest_factory_spec.rb | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index 63e166a..645417e 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -25,7 +25,11 @@ def apply(manifest) def populate_rendering if record.respond_to?(:sequence_rendering) - record.sequence_rendering.each(&:to_h) + record.sequence_rendering.collect do |rendering| + sequence_rendering = rendering.to_h.except('@id') + sequence_rendering['id'] = rendering['@id'] + sequence_rendering + end else [] end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index 7d2b40c..cef8968 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -155,7 +155,7 @@ def sequence_rendering allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) expect(result['rendering']).to eq [{ - '@id' => 'http://test.host/file_set/id/download', 'format' => 'application/pdf', 'label' => 'Download' + 'id' => 'http://test.host/file_set/id/download', 'format' => 'application/pdf', 'label' => 'Download' }] end end From 7a00f939ff32ff7284b4c608b9c2621e28852ace Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Sun, 25 Mar 2018 22:24:51 -0400 Subject: [PATCH 07/20] WIP actually put canvases in manifest items --- .../v3/manifest_builder/canvas_builder.rb | 6 +++--- lib/iiif_manifest/v3/manifest_builder/iiif_service.rb | 11 +++++++++++ .../v3/manifest_builder/record_property_builder.rb | 2 +- .../v3/manifest_builder/resource_builder.rb | 4 ++-- lib/iiif_manifest/v3/manifest_service_locator.rb | 4 ++++ spec/lib/iiif_manifest/v3/manifest_factory_spec.rb | 3 ++- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index 886c38a..c49cd2f 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -1,9 +1,9 @@ module IIIFManifest::V3 class ManifestBuilder class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder - def apply(items) - return items if canvas.items.blank? - items += [canvas] + def apply(record) + return record if canvas.items.blank? + record.items += [canvas] end private diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 62fe483..7ae4b52 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -121,6 +121,17 @@ def initial_attributes end end + class Resource < IIIFService + def service=(service) + inner_hash['service'] = service + end + + def initial_attributes + { + } + end + end + class Annotation < IIIFService def body=(body) inner_hash['body'] = body diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index 645417e..e324ee3 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -18,7 +18,7 @@ def apply(manifest) manifest.service = services if search_service.present? manifest.rendering = populate_rendering # Build the items array - canvas_builder.apply(manifest.items) + canvas_builder.apply(manifest) manifest end # rubocop:enable Metrics/AbcSize diff --git a/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb b/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb index 1377307..72b19e8 100644 --- a/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb @@ -3,10 +3,10 @@ class ManifestBuilder class ResourceBuilder < ::IIIFManifest::ManifestBuilder::ResourceBuilder def apply(annotation) resource['id'] = display_image.url - resource['type'] = 'dctypes:Image' + resource['type'] = 'Image' resource['height'] = display_image.height resource['width'] = display_image.width - resource['format'] = display_image.format + resource['format'] = display_image.format if display_image.format image_service_builder.apply(resource) if iiif_endpoint annotation.body = resource end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index f0d6a6d..72dac8e 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -94,6 +94,10 @@ def iiif_service_factory IIIFManifest::V3::ManifestBuilder::IIIFService end + def iiif_resource_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Resource + end + def iiif_annotation_factory IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index cef8968..b8b2406 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -91,6 +91,7 @@ def display_image expect(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to have_received(:new) .exactly(1).times.with(file_presenter, anything, anything) + expect(result['items'].length).to eq 1 end it 'builds a structure if it can' do allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) @@ -297,7 +298,7 @@ def sequence_rendering end end - context 'when there are child works AMD when the work identifies itself as a sammelband' do + context 'when there are child works AND when the work identifies itself as a sammelband' do let(:child_work_presenter) { presenter_class.new('test-99') } let(:file_presenter) { DisplayImagePresenter.new } From 62c37a8472d4fe949977a3c5b3c46d26f6a6b1e8 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Mon, 26 Mar 2018 02:39:56 -0400 Subject: [PATCH 08/20] WIP use deep canvas builder always --- .../v3/manifest_builder/canvas_builder.rb | 6 +++--- lib/iiif_manifest/v3/manifest_builder/iiif_service.rb | 4 ++-- .../v3/manifest_builder/record_property_builder.rb | 2 +- lib/iiif_manifest/v3/manifest_factory.rb | 2 +- lib/iiif_manifest/v3/manifest_service_locator.rb | 10 ++-------- spec/lib/iiif_manifest/v3/manifest_factory_spec.rb | 4 +++- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index c49cd2f..f51bcf3 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -1,9 +1,9 @@ module IIIFManifest::V3 class ManifestBuilder class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder - def apply(record) - return record if canvas.items.blank? - record.items += [canvas] + def apply(items) + return items if canvas.items.blank? + items << canvas end private diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 7ae4b52..2fa7160 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -32,7 +32,7 @@ def viewingDirection end def items - inner_hash['items'] || [] + inner_hash['items'] ||= [] end def items=(items) @@ -99,7 +99,7 @@ def label=(label) end def items - inner_hash['items'] || [] + inner_hash['items'] ||= [] end def items=(items) diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index e324ee3..645417e 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -18,7 +18,7 @@ def apply(manifest) manifest.service = services if search_service.present? manifest.rendering = populate_rendering # Build the items array - canvas_builder.apply(manifest) + canvas_builder.apply(manifest.items) manifest end # rubocop:enable Metrics/AbcSize diff --git a/lib/iiif_manifest/v3/manifest_factory.rb b/lib/iiif_manifest/v3/manifest_factory.rb index 0a93394..44f6d04 100644 --- a/lib/iiif_manifest/v3/manifest_factory.rb +++ b/lib/iiif_manifest/v3/manifest_factory.rb @@ -17,7 +17,7 @@ def initialize(manifest_service_locator) def new(work) if !work.work_presenters.empty? if sammelband?(work) || !work.file_set_presenters.empty? - sammelband_manifest_builder.new(work) + manifest_builder.new(work) elsif work.file_set_presenters.empty? work = IIIFManifest::IIIFCollection.new(work) collection_manifest_builder.new(work) diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index 72dac8e..83ede54 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -11,13 +11,6 @@ def manifest_builders ) end - def sammelband_manifest_builders - composite_builder_factory.new( - record_property_builder, - composite_builder: composite_builder - ) - end - def collection_manifest_builders composite_builder_factory.new( record_property_builder, @@ -35,7 +28,8 @@ def record_property_builder ManifestBuilder::RecordPropertyBuilder, iiif_search_service_factory: iiif_search_service_factory, iiif_autocomplete_service_factory: iiif_autocomplete_service_factory, - canvas_builder_factory: canvas_builder_factory + canvas_builder_factory: deep_canvas_builder_factory + # canvas_builder_factory: canvas_builder_factory ) end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index b8b2406..3022883 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -76,7 +76,7 @@ def display_image context 'when there are no files' do it 'returns no items' do - expect(result['items']).to eq nil + expect(result['items']).to eq [] end end @@ -295,6 +295,8 @@ def sequence_rendering end it 'builds structures from all the child file sets' do expect(result['structures'].first['items'].length).to eq 2 + expect(result['structures'].first['items'].first['type']).to eq 'Canvas' + expect(result['structures'].first['items'].second['type']).to eq 'Range' end end From dbfd4508e1a705ff2e34d1a4c75272d722c53e11 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Mon, 2 Apr 2018 17:06:31 -0400 Subject: [PATCH 09/20] WIP All tests passing --- .../v3/manifest_builder/structure_builder.rb | 2 +- .../iiif_manifest/v3/manifest_factory_spec.rb | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb b/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb index 8cbef23..7119c2f 100644 --- a/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb @@ -23,7 +23,7 @@ def build_range range['id'] = path range['label'] = record.label range['behavior'] = 'top' if top? - range['items'] = canvas_builders.map(&:path) + range['items'] = canvas_builders.collect {|cb| { 'id' => cb.path, 'type' => 'Canvas' } } end def sub_ranges diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index 3022883..3ecac7c 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -48,8 +48,12 @@ def initialize(label:, ranges: [], file_set_presenters: []) end class DisplayImagePresenter + def initialize(id: 'test-22') + @id = id + end + def id - 'test-22' + @id end def display_image @@ -105,6 +109,8 @@ def display_image expect(structure['items'][0]['type']).to eq 'Range' sub_range = structure['items'][0] expect(sub_range['items'].length).to eq 1 + expect(sub_range['items'][0]['type']).to eq 'Canvas' + expect(sub_range['items'][0]['id']).to eq 'http://test.host/books/book-77/manifest/canvas/test-22' end end @@ -276,13 +282,15 @@ def sequence_rendering context 'when there are child works AND files' do let(:child_work_presenter) { presenter_class.new('test-99') } - let(:file_presenter) { DisplayImagePresenter.new } - let(:file_presenter2) { DisplayImagePresenter.new } + let(:file_presenter) { DisplayImagePresenter.new(id: 'test-22') } + let(:file_presenter2) { DisplayImagePresenter.new(id: 'test-33') } before do allow(book_presenter).to receive(:work_presenters).and_return([child_work_presenter]) allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) allow(child_work_presenter).to receive(:file_set_presenters).and_return([file_presenter2]) + allow(child_work_presenter).to receive(:ranges).and_return([ManifestRange.new(label: 'Child Work', file_set_presenters: [file_presenter2])]) + allow(book_presenter.ranges[0]).to receive(:ranges).and_return([ManifestRange.new(label: 'Chapter 1', file_set_presenters: [file_presenter])] + child_work_presenter.ranges) end it 'returns a IIIF Manifest' do expect(result['type']).to eq 'Manifest' @@ -295,8 +303,10 @@ def sequence_rendering end it 'builds structures from all the child file sets' do expect(result['structures'].first['items'].length).to eq 2 - expect(result['structures'].first['items'].first['type']).to eq 'Canvas' - expect(result['structures'].first['items'].second['type']).to eq 'Range' + expect(result['structures'].first['items'][0]['items'].first['type']).to eq 'Canvas' + expect(result['structures'].first['items'][0]['items'].first['id']).to eq 'http://test.host/books/book-77/manifest/canvas/test-22' + expect(result['structures'].first['items'][1]['items'].first['type']).to eq 'Canvas' + expect(result['structures'].first['items'][1]['items'].first['id']).to eq 'http://test.host/books/book-77/manifest/canvas/test-33' end end @@ -318,9 +328,6 @@ def sequence_rendering it 'builds items array from all the child file sets' do expect(result['items'].length).to eq 1 end - it 'builds structures from all the child file sets' do - expect(result['structures'].first['items'].length).to eq 1 - end end context 'when there is no viewing_direction method' do From aef65aff3a3f72ccd788bf7bc66ae6c1c0bb2b54 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Tue, 3 Apr 2018 09:53:39 -0400 Subject: [PATCH 10/20] Appease rubocop --- .rubocop.yml | 1 + .rubocop_todo.yml | 14 + iiif_manifest.gemspec | 2 +- lib/iiif_manifest/v3.rb | 12 +- lib/iiif_manifest/v3/manifest_builder.rb | 44 +-- .../v3/manifest_builder/canvas_builder.rb | 24 +- .../v3/manifest_builder/iiif_service.rb | 288 +++++++++--------- .../v3/manifest_builder/image_builder.rb | 18 +- .../record_property_builder.rb | 77 ++--- .../v3/manifest_builder/resource_builder.rb | 24 +- .../v3/manifest_builder/structure_builder.rb | 66 ++-- lib/iiif_manifest/v3/manifest_factory.rb | 50 +-- .../v3/manifest_service_locator.rb | 234 +++++++------- .../iiif_manifest/v3/manifest_factory_spec.rb | 19 +- spec/spec_helper.rb | 2 +- 15 files changed, 459 insertions(+), 416 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 11c062b..747ae6f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -14,6 +14,7 @@ Metrics/BlockLength: Naming/MethodName: Exclude: - 'lib/iiif_manifest/manifest_builder/iiif_service.rb' + - 'lib/iiif_manifest/v3/manifest_builder/iiif_service.rb' RSpec/MultipleExpectations: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 3452718..1631598 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -11,6 +11,8 @@ Metrics/AbcSize: Exclude: - 'lib/iiif_manifest/manifest_builder/resource_builder.rb' - 'lib/iiif_manifest/manifest_factory.rb' + - 'lib/iiif_manifest/v3/manifest_builder/resource_builder.rb' + - 'lib/iiif_manifest/v3/manifest_factory.rb' # Offense count: 1 # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. @@ -41,6 +43,7 @@ RSpec/VerifiedDoubles: RSpec/ExampleLength: Exclude: - 'spec/lib/iiif_manifest/manifest_factory_spec.rb' + - 'spec/lib/iiif_manifest/v3/manifest_factory_spec.rb' # Offense count: 20 Style/Documentation: @@ -66,7 +69,18 @@ Style/Documentation: - 'lib/iiif_manifest/manifest_service_locator.rb' - 'lib/iiif_manifest.rb' - 'lib/iiif_manifest/manifest_builder/iiif_service.rb' + - 'lib/iiif_manifest/v3.rb' + - 'lib/iiif_manifest/v3/manifest_factory.rb' + - 'lib/iiif_manifest/v3/manifest_builder/structure_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/iiif_service.rb' + - 'lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/image_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/resource_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder.rb' + - 'lib/iiif_manifest/v3/manifest_service_locator.rb' - 'spec/lib/iiif_manifest/manifest_factory_spec.rb' + - 'spec/lib/iiif_manifest/v3/manifest_factory_spec.rb' # Offense count: 1 Style/MethodMissing: diff --git a/iiif_manifest.gemspec b/iiif_manifest.gemspec index 77a3896..67943bb 100644 --- a/iiif_manifest.gemspec +++ b/iiif_manifest.gemspec @@ -1,4 +1,4 @@ -lib = File.expand_path('../lib', __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'iiif_manifest/version' diff --git a/lib/iiif_manifest/v3.rb b/lib/iiif_manifest/v3.rb index 0513d75..c0dfebf 100644 --- a/lib/iiif_manifest/v3.rb +++ b/lib/iiif_manifest/v3.rb @@ -2,9 +2,11 @@ require 'active_support/core_ext/module' require 'active_support/core_ext/object' -module IIIFManifest::V3 - extend ActiveSupport::Autoload - autoload :ManifestBuilder - autoload :ManifestFactory - autoload :ManifestServiceLocator +module IIIFManifest + module V3 + extend ActiveSupport::Autoload + autoload :ManifestBuilder + autoload :ManifestFactory + autoload :ManifestServiceLocator + end end diff --git a/lib/iiif_manifest/v3/manifest_builder.rb b/lib/iiif_manifest/v3/manifest_builder.rb index 6f88632..f31bead 100644 --- a/lib/iiif_manifest/v3/manifest_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder.rb @@ -5,31 +5,33 @@ require_relative 'manifest_builder/resource_builder' require_relative 'manifest_builder/structure_builder' -module IIIFManifest::V3 - class ManifestBuilder - attr_reader :work, - :builders, - :top_record_factory - def initialize(work, builders:, top_record_factory:) - @work = work - @builders = builders - @top_record_factory = top_record_factory - end +module IIIFManifest + module V3 + class ManifestBuilder + attr_reader :work, + :builders, + :top_record_factory + def initialize(work, builders:, top_record_factory:) + @work = work + @builders = builders + @top_record_factory = top_record_factory + end - def apply(collection) - collection['manifests'] ||= [] - collection['manifests'] << to_h - collection - end + def apply(collection) + collection['manifests'] ||= [] + collection['manifests'] << to_h + collection + end - def to_h - @to_h ||= builders.new(work).apply(top_record) - end + def to_h + @to_h ||= builders.new(work).apply(top_record) + end - private + private - def top_record - top_record_factory.new + def top_record + top_record_factory.new + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index f51bcf3..9d34314 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -1,16 +1,18 @@ -module IIIFManifest::V3 - class ManifestBuilder - class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder - def apply(items) - return items if canvas.items.blank? - items << canvas - end +module IIIFManifest + module V3 + class ManifestBuilder + class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder + def apply(items) + return items if canvas.items.blank? + items << canvas + end - private + private - def apply_record_properties - canvas['id'] = path - canvas.label = record.to_s + def apply_record_properties + canvas['id'] = path + canvas.label = record.to_s + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 2fa7160..5c2f815 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -1,101 +1,35 @@ -module IIIFManifest::V3 - class ManifestBuilder - class IIIFService < IIIFManifest::ManifestBuilder::IIIFService - end - - class IIIFManifest < IIIFService - def label - inner_hash['label'] +module IIIFManifest + module V3 + class ManifestBuilder + class IIIFService < IIIFManifest::ManifestBuilder::IIIFService end - def label=(label) - inner_hash['label'] = label - end - - def summary=(summary) - return unless summary.present? - inner_hash['summary'] = summary - end - - def behavior=(behavior) - return unless behavior.present? - inner_hash['behavior'] = behavior - end - - def viewing_direction=(viewing_direction) - return unless viewing_direction.present? - inner_hash['viewingDirection'] = viewing_direction - end - - def viewingDirection - inner_hash['viewingDirection'] - end - - def items - inner_hash['items'] ||= [] - end - - def items=(items) - inner_hash['items'] = items - end - - def metadata=(metadata) - inner_hash['metadata'] = metadata - end - - def service - inner_hash['service'] || [] - end - - def service=(service) - inner_hash['service'] = service - end - - def see_also=(see_also) - inner_hash['seeAlso'] = see_also - end - - def rendering=(rendering) - inner_hash['rendering'] = rendering - end + class IIIFManifest < IIIFService + def label + inner_hash['label'] + end - def rights=(rights) - inner_hash['rights'] = rights - end + def label=(label) + inner_hash['label'] = label + end - def initial_attributes - { - '@context' => [ - "http://www.w3.org/ns/anno.jsonld", - "http://iiif.io/api/presentation/3/context.json" - ], - 'type' => 'Manifest' - } - end + def summary=(summary) + return unless summary.present? + inner_hash['summary'] = summary + end - class Collection < IIIFManifest - def initial_attributes - { - '@context' => [ - "http://www.w3.org/ns/anno.jsonld", - "http://iiif.io/api/presentation/3/context.json" - ], - 'type' => 'Collection' - } + def behavior=(behavior) + return unless behavior.present? + inner_hash['behavior'] = behavior end def viewing_direction=(viewing_direction) - raise NotImplementedError + return unless viewing_direction.present? + inner_hash['viewingDirection'] = viewing_direction end def viewingDirection - raise NotImplementedError - end - end - - class Canvas < IIIFService - def label=(label) - inner_hash['label'] = label + inner_hash['viewingDirection'] end def items @@ -106,81 +40,149 @@ def items=(items) inner_hash['items'] = items end - def initial_attributes - { - 'type' => 'Canvas' - } + def metadata=(metadata) + inner_hash['metadata'] = metadata end - end - class Range < IIIFService - def initial_attributes - { - 'type' => 'Range' - } + def service + inner_hash['service'] || [] end - end - class Resource < IIIFService def service=(service) inner_hash['service'] = service end - def initial_attributes - { - } + def see_also=(see_also) + inner_hash['seeAlso'] = see_also end - end - class Annotation < IIIFService - def body=(body) - inner_hash['body'] = body + def rendering=(rendering) + inner_hash['rendering'] = rendering end - def body - inner_hash['body'] + def rights=(rights) + inner_hash['rights'] = rights end def initial_attributes { - 'type' => 'Annotation', - 'motivation' => 'painting' - } - end - end - - class SearchService < IIIFService - def service=(service) - inner_hash['service'] = service - end - - def search_service=(search_service) - inner_hash['id'] = search_service - end - - def initial_attributes - { - '@context' => 'http://iiif.io/api/search/1/context.json', - 'profile' => 'http://iiif.io/api/search/1/search', - 'label' => 'Search within this manifest' + '@context' => [ + 'http://www.w3.org/ns/anno.jsonld', + 'http://iiif.io/api/presentation/3/context.json' + ], + 'type' => 'Manifest' } end - end - class AutocompleteService < IIIFService - def autocomplete_service - inner_hash['id'] - end - - def autocomplete_service=(autocomplete_service) - inner_hash['id'] = autocomplete_service - end - - def initial_attributes - { - 'profile' => 'http://iiif.io/api/search/1/autocomplete', - 'label' => 'Get suggested words in this manifest' - } + class Collection < IIIFManifest + def initial_attributes + { + '@context' => [ + 'http://www.w3.org/ns/anno.jsonld', + 'http://iiif.io/api/presentation/3/context.json' + ], + 'type' => 'Collection' + } + end + + def viewing_direction=(_viewing_direction) + raise NotImplementedError + end + + def viewingDirection + raise NotImplementedError + end + end + + class Canvas < IIIFService + def label=(label) + inner_hash['label'] = label + end + + def items + inner_hash['items'] ||= [] + end + + def items=(items) + inner_hash['items'] = items + end + + def initial_attributes + { + 'type' => 'Canvas' + } + end + end + + class Range < IIIFService + def initial_attributes + { + 'type' => 'Range' + } + end + end + + class Resource < IIIFService + def service=(service) + inner_hash['service'] = service + end + + def initial_attributes + { + } + end + end + + class Annotation < IIIFService + def body=(body) + inner_hash['body'] = body + end + + def body + inner_hash['body'] + end + + def initial_attributes + { + 'type' => 'Annotation', + 'motivation' => 'painting' + } + end + end + + class SearchService < IIIFService + def service=(service) + inner_hash['service'] = service + end + + def search_service=(search_service) + inner_hash['id'] = search_service + end + + def initial_attributes + { + '@context' => 'http://iiif.io/api/search/1/context.json', + 'profile' => 'http://iiif.io/api/search/1/search', + 'label' => 'Search within this manifest' + } + end + end + + class AutocompleteService < IIIFService + def autocomplete_service + inner_hash['id'] + end + + def autocomplete_service=(autocomplete_service) + inner_hash['id'] = autocomplete_service + end + + def initial_attributes + { + 'profile' => 'http://iiif.io/api/search/1/autocomplete', + 'label' => 'Get suggested words in this manifest' + } + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/image_builder.rb b/lib/iiif_manifest/v3/manifest_builder/image_builder.rb index d62cc03..58406cf 100644 --- a/lib/iiif_manifest/v3/manifest_builder/image_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/image_builder.rb @@ -1,11 +1,13 @@ -module IIIFManifest::V3 - class ManifestBuilder - class ImageBuilder < ::IIIFManifest::ManifestBuilder::ImageBuilder - def apply(canvas) - annotation['target'] = canvas['id'] - canvas['width'] = annotation.body['width'] - canvas['height'] = annotation.body['height'] - canvas.items += [annotation] +module IIIFManifest + module V3 + class ManifestBuilder + class ImageBuilder < ::IIIFManifest::ManifestBuilder::ImageBuilder + def apply(canvas) + annotation['target'] = canvas['id'] + canvas['width'] = annotation.body['width'] + canvas['height'] = annotation.body['height'] + canvas.items += [annotation] + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index 645417e..37c3a80 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -1,44 +1,51 @@ -module IIIFManifest::V3 - class ManifestBuilder - class RecordPropertyBuilder < ::IIIFManifest::ManifestBuilder::RecordPropertyBuilder - attr_reader :canvas_builder_factory - def initialize(record, iiif_search_service_factory:, iiif_autocomplete_service_factory:, canvas_builder_factory:) - super(record, iiif_search_service_factory: iiif_search_service_factory, iiif_autocomplete_service_factory: iiif_autocomplete_service_factory) - @canvas_builder_factory = canvas_builder_factory - end +module IIIFManifest + module V3 + class ManifestBuilder + class RecordPropertyBuilder < ::IIIFManifest::ManifestBuilder::RecordPropertyBuilder + attr_reader :canvas_builder_factory + def initialize(record, + iiif_search_service_factory:, + iiif_autocomplete_service_factory:, + canvas_builder_factory:) + super(record, + iiif_search_service_factory: iiif_search_service_factory, + iiif_autocomplete_service_factory: iiif_autocomplete_service_factory) + @canvas_builder_factory = canvas_builder_factory + end - # rubocop:disable Metrics/AbcSize - def apply(manifest) - manifest['id'] = record.manifest_url.to_s - manifest.label = record.to_s - manifest.summary = record.description - manifest.behavior = viewing_hint if viewing_hint.present? - manifest.viewing_direction = viewing_direction if viewing_direction.present? - manifest.metadata = record.manifest_metadata if valid_metadata? - manifest.service = services if search_service.present? - manifest.rendering = populate_rendering - # Build the items array - canvas_builder.apply(manifest.items) - manifest - end - # rubocop:enable Metrics/AbcSize + # rubocop:disable Metrics/AbcSize + def apply(manifest) + manifest['id'] = record.manifest_url.to_s + manifest.label = record.to_s + manifest.summary = record.description + manifest.behavior = viewing_hint if viewing_hint.present? + manifest.viewing_direction = viewing_direction if viewing_direction.present? + manifest.metadata = record.manifest_metadata if valid_metadata? + manifest.service = services if search_service.present? + manifest.rendering = populate_rendering + # Build the items array + canvas_builder.apply(manifest.items) + manifest + end + # rubocop:enable Metrics/AbcSize - def populate_rendering - if record.respond_to?(:sequence_rendering) - record.sequence_rendering.collect do |rendering| - sequence_rendering = rendering.to_h.except('@id') - sequence_rendering['id'] = rendering['@id'] - sequence_rendering + def populate_rendering + if record.respond_to?(:sequence_rendering) + record.sequence_rendering.collect do |rendering| + sequence_rendering = rendering.to_h.except('@id') + sequence_rendering['id'] = rendering['@id'] + sequence_rendering + end + else + [] end - else - [] end - end - private + private - def canvas_builder - canvas_builder_factory.from(record) + def canvas_builder + canvas_builder_factory.from(record) + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb b/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb index 72b19e8..ba9db3c 100644 --- a/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb @@ -1,14 +1,16 @@ -module IIIFManifest::V3 - class ManifestBuilder - class ResourceBuilder < ::IIIFManifest::ManifestBuilder::ResourceBuilder - def apply(annotation) - resource['id'] = display_image.url - resource['type'] = 'Image' - resource['height'] = display_image.height - resource['width'] = display_image.width - resource['format'] = display_image.format if display_image.format - image_service_builder.apply(resource) if iiif_endpoint - annotation.body = resource +module IIIFManifest + module V3 + class ManifestBuilder + class ResourceBuilder < ::IIIFManifest::ManifestBuilder::ResourceBuilder + def apply(annotation) + resource['id'] = display_image.url + resource['type'] = 'Image' + resource['height'] = display_image.height + resource['width'] = display_image.width + resource['format'] = display_image.format if display_image.format + image_service_builder.apply(resource) if iiif_endpoint + annotation.body = resource + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb b/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb index 7119c2f..ff8145c 100644 --- a/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/structure_builder.rb @@ -1,41 +1,43 @@ -module IIIFManifest::V3 - class ManifestBuilder - class StructureBuilder < ::IIIFManifest::ManifestBuilder::StructureBuilder - def range_builder(top_range) - RangeBuilder.new( - top_range, - record, true, - canvas_builder_factory: canvas_builder_factory, - iiif_range_factory: iiif_range_factory - ) - end - end - class RangeBuilder < ::IIIFManifest::ManifestBuilder::RangeBuilder - def apply(manifest) - manifest << range - sub_ranges.map do |sub_range| - sub_range.apply(range['items']) - end - manifest - end - - def build_range - range['id'] = path - range['label'] = record.label - range['behavior'] = 'top' if top? - range['items'] = canvas_builders.collect {|cb| { 'id' => cb.path, 'type' => 'Canvas' } } - end - - def sub_ranges - @sub_ranges ||= record.ranges.map do |sub_range| +module IIIFManifest + module V3 + class ManifestBuilder + class StructureBuilder < ::IIIFManifest::ManifestBuilder::StructureBuilder + def range_builder(top_range) RangeBuilder.new( - sub_range, - parent, + top_range, + record, true, canvas_builder_factory: canvas_builder_factory, iiif_range_factory: iiif_range_factory ) end end + class RangeBuilder < ::IIIFManifest::ManifestBuilder::RangeBuilder + def apply(manifest) + manifest << range + sub_ranges.map do |sub_range| + sub_range.apply(range['items']) + end + manifest + end + + def build_range + range['id'] = path + range['label'] = record.label + range['behavior'] = 'top' if top? + range['items'] = canvas_builders.collect { |cb| { 'id' => cb.path, 'type' => 'Canvas' } } + end + + def sub_ranges + @sub_ranges ||= record.ranges.map do |sub_range| + RangeBuilder.new( + sub_range, + parent, + canvas_builder_factory: canvas_builder_factory, + iiif_range_factory: iiif_range_factory + ) + end + end + end end end end diff --git a/lib/iiif_manifest/v3/manifest_factory.rb b/lib/iiif_manifest/v3/manifest_factory.rb index 44f6d04..cdcc8f6 100644 --- a/lib/iiif_manifest/v3/manifest_factory.rb +++ b/lib/iiif_manifest/v3/manifest_factory.rb @@ -1,36 +1,38 @@ -module IIIFManifest::V3 - class ManifestFactory - class << self - def new(work, manifest_service_locator: ManifestServiceLocator) - super(manifest_service_locator).new(work) +module IIIFManifest + module V3 + class ManifestFactory + class << self + def new(work, manifest_service_locator: ManifestServiceLocator) + super(manifest_service_locator).new(work) + end end - end - delegate :collection_manifest_builder, :manifest_builder, :sammelband_manifest_builder, - to: :manifest_service_locator - attr_reader :manifest_service_locator + delegate :collection_manifest_builder, :manifest_builder, :sammelband_manifest_builder, + to: :manifest_service_locator + attr_reader :manifest_service_locator - def initialize(manifest_service_locator) - @manifest_service_locator = manifest_service_locator - end + def initialize(manifest_service_locator) + @manifest_service_locator = manifest_service_locator + end - def new(work) - if !work.work_presenters.empty? - if sammelband?(work) || !work.file_set_presenters.empty? + def new(work) + if !work.work_presenters.empty? + if sammelband?(work) || !work.file_set_presenters.empty? + manifest_builder.new(work) + elsif work.file_set_presenters.empty? + work = IIIFManifest::IIIFCollection.new(work) + collection_manifest_builder.new(work) + end + else manifest_builder.new(work) - elsif work.file_set_presenters.empty? - work = IIIFManifest::IIIFCollection.new(work) - collection_manifest_builder.new(work) end - else - manifest_builder.new(work) end - end - private + private - def sammelband?(work) - work.respond_to?(:sammelband?) && work.sammelband? + def sammelband?(work) + work.respond_to?(:sammelband?) && work.sammelband? + end end end end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index 83ede54..aa6c3b9 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -1,119 +1,121 @@ -module IIIFManifest::V3 - class ManifestServiceLocator < IIIFManifest::ManifestServiceLocator - class << self - # Builders which receive a work as an argument to .new and return objects - # that respond to #apply. - def manifest_builders - composite_builder_factory.new( - record_property_builder, - structure_builder, - composite_builder: composite_builder - ) - end - - def collection_manifest_builders - composite_builder_factory.new( - record_property_builder, - child_manifest_builder_factory, - composite_builder: composite_builder - ) - end - - def iiif_collection_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::Collection - end - - def record_property_builder - IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - ManifestBuilder::RecordPropertyBuilder, - iiif_search_service_factory: iiif_search_service_factory, - iiif_autocomplete_service_factory: iiif_autocomplete_service_factory, - canvas_builder_factory: deep_canvas_builder_factory - # canvas_builder_factory: canvas_builder_factory - ) - end - - def structure_builder - IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - ManifestBuilder::StructureBuilder, - canvas_builder_factory: canvas_builder, - iiif_range_factory: iiif_range_factory - ) - end - - def canvas_builder_factory - IIIFManifest::ManifestBuilder::CanvasBuilderFactory.new( - composite_builder: composite_builder, - canvas_builder_factory: canvas_builder - ) - end - - def canvas_builder - IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - ManifestBuilder::CanvasBuilder, - iiif_canvas_factory: iiif_canvas_factory, - image_builder: image_builder - ) - end - - def image_builder - IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - ManifestBuilder::ImageBuilder, - iiif_annotation_factory: iiif_annotation_factory, - resource_builder_factory: resource_builder_factory - ) - end - - def resource_builder_factory - IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - ManifestBuilder::ResourceBuilder, - iiif_resource_factory: iiif_resource_factory, - image_service_builder_factory: image_service_builder_factory - ) - end - - def sequence_builder - raise NotImplementedError - end - - def sammelband_sequence_builder - raise NotImplementedError - end - - def sequence_factory - raise NotImplementedError - end - - def iiif_service_factory - IIIFManifest::V3::ManifestBuilder::IIIFService - end - - def iiif_resource_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::Resource - end - - def iiif_annotation_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation - end - - def iiif_manifest_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest - end - - def iiif_canvas_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::Canvas - end - - def iiif_range_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::Range - end - - def iiif_search_service_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::SearchService - end - - def iiif_autocomplete_service_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::AutocompleteService +module IIIFManifest + module V3 + class ManifestServiceLocator < IIIFManifest::ManifestServiceLocator + class << self + # Builders which receive a work as an argument to .new and return objects + # that respond to #apply. + def manifest_builders + composite_builder_factory.new( + record_property_builder, + structure_builder, + composite_builder: composite_builder + ) + end + + def collection_manifest_builders + composite_builder_factory.new( + record_property_builder, + child_manifest_builder_factory, + composite_builder: composite_builder + ) + end + + def iiif_collection_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Collection + end + + def record_property_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::RecordPropertyBuilder, + iiif_search_service_factory: iiif_search_service_factory, + iiif_autocomplete_service_factory: iiif_autocomplete_service_factory, + canvas_builder_factory: deep_canvas_builder_factory + # canvas_builder_factory: canvas_builder_factory + ) + end + + def structure_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::StructureBuilder, + canvas_builder_factory: canvas_builder, + iiif_range_factory: iiif_range_factory + ) + end + + def canvas_builder_factory + IIIFManifest::ManifestBuilder::CanvasBuilderFactory.new( + composite_builder: composite_builder, + canvas_builder_factory: canvas_builder + ) + end + + def canvas_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::CanvasBuilder, + iiif_canvas_factory: iiif_canvas_factory, + image_builder: image_builder + ) + end + + def image_builder + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::ImageBuilder, + iiif_annotation_factory: iiif_annotation_factory, + resource_builder_factory: resource_builder_factory + ) + end + + def resource_builder_factory + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::ResourceBuilder, + iiif_resource_factory: iiif_resource_factory, + image_service_builder_factory: image_service_builder_factory + ) + end + + def sequence_builder + raise NotImplementedError + end + + def sammelband_sequence_builder + raise NotImplementedError + end + + def sequence_factory + raise NotImplementedError + end + + def iiif_service_factory + IIIFManifest::V3::ManifestBuilder::IIIFService + end + + def iiif_resource_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Resource + end + + def iiif_annotation_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation + end + + def iiif_manifest_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest + end + + def iiif_canvas_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Canvas + end + + def iiif_range_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Range + end + + def iiif_search_service_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::SearchService + end + + def iiif_autocomplete_service_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::AutocompleteService + end end end end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index 3ecac7c..cd0f7fd 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -8,6 +8,8 @@ before do class Book + attr_reader :id + def initialize(id) @id = id end @@ -25,7 +27,7 @@ def work_presenters end def manifest_url - "http://test.host/books/#{@id}/manifest" + "http://test.host/books/#{id}/manifest" end def ranges @@ -48,14 +50,11 @@ def initialize(label:, ranges: [], file_set_presenters: []) end class DisplayImagePresenter + attr_reader :id def initialize(id: 'test-22') @id = id end - def id - @id - end - def display_image IIIFManifest::DisplayImage.new(id, width: 100, height: 100, format: 'image/jpeg') end @@ -129,6 +128,8 @@ def display_image before do class Book + attr_reader :id + def initialize(id) @id = id end @@ -146,7 +147,7 @@ def work_presenters end def manifest_url - "http://test.host/books/#{@id}/manifest" + "http://test.host/books/#{id}/manifest" end def sequence_rendering @@ -284,13 +285,15 @@ def sequence_rendering let(:child_work_presenter) { presenter_class.new('test-99') } let(:file_presenter) { DisplayImagePresenter.new(id: 'test-22') } let(:file_presenter2) { DisplayImagePresenter.new(id: 'test-33') } + let(:chapter_1_range) { ManifestRange.new(label: 'Chapter 1', file_set_presenters: [file_presenter]) } + let(:child_work_range) { ManifestRange.new(label: 'Child Work', file_set_presenters: [file_presenter2]) } before do allow(book_presenter).to receive(:work_presenters).and_return([child_work_presenter]) allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) allow(child_work_presenter).to receive(:file_set_presenters).and_return([file_presenter2]) - allow(child_work_presenter).to receive(:ranges).and_return([ManifestRange.new(label: 'Child Work', file_set_presenters: [file_presenter2])]) - allow(book_presenter.ranges[0]).to receive(:ranges).and_return([ManifestRange.new(label: 'Chapter 1', file_set_presenters: [file_presenter])] + child_work_presenter.ranges) + allow(child_work_presenter).to receive(:ranges).and_return([child_work_range]) + allow(book_presenter.ranges[0]).to receive(:ranges).and_return([chapter_1_range] + child_work_presenter.ranges) end it 'returns a IIIF Manifest' do expect(result['type']).to eq 'Manifest' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 275fdf3..ef4dcff 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,3 @@ -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) require 'iiif_manifest' require 'pry' From 1c8f6c2ac69e1ab77070ecacbc19e7a450e56ecc Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Tue, 3 Apr 2018 12:38:56 -0400 Subject: [PATCH 11/20] Add AnnotationPage --- .../v3/manifest_builder/canvas_builder.rb | 18 +++++++++++++++++ .../v3/manifest_builder/iiif_service.rb | 20 +++++++++++++++++++ .../v3/manifest_builder/image_builder.rb | 3 ++- .../v3/manifest_service_locator.rb | 7 ++++++- .../iiif_manifest/v3/manifest_factory_spec.rb | 17 +++++++++++++++- 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index 9d34314..4c0ee86 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -2,6 +2,18 @@ module IIIFManifest module V3 class ManifestBuilder class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder + attr_reader :iiif_annotation_page_factory + + def initialize(record, parent, iiif_canvas_factory:, image_builder:, iiif_annotation_page_factory:) + @record = record + @parent = parent + @iiif_canvas_factory = iiif_canvas_factory + @image_builder = image_builder + @iiif_annotation_page_factory = iiif_annotation_page_factory + apply_record_properties + attach_image if display_image + end + def apply(items) return items if canvas.items.blank? items << canvas @@ -12,6 +24,12 @@ def apply(items) def apply_record_properties canvas['id'] = path canvas.label = record.to_s + annotation_page['id'] = "#{path}/annotation_page/#{annotation_page.index}" + canvas.items = [annotation_page] + end + + def annotation_page + @annotation_page ||= iiif_annotation_page_factory.new end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 5c2f815..3e20be4 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -133,6 +133,26 @@ def initial_attributes end end + class AnnotationPage < IIIFService + def items + inner_hash['items'] ||= [] + end + + def items=(items) + inner_hash['items'] = items + end + + def index + @index ||= SecureRandom.uuid + end + + def initial_attributes + { + 'type' => 'AnnotationPage', + } + end + end + class Annotation < IIIFService def body=(body) inner_hash['body'] = body diff --git a/lib/iiif_manifest/v3/manifest_builder/image_builder.rb b/lib/iiif_manifest/v3/manifest_builder/image_builder.rb index 58406cf..e981898 100644 --- a/lib/iiif_manifest/v3/manifest_builder/image_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/image_builder.rb @@ -6,7 +6,8 @@ def apply(canvas) annotation['target'] = canvas['id'] canvas['width'] = annotation.body['width'] canvas['height'] = annotation.body['height'] - canvas.items += [annotation] + # Assume first item in canvas is an annotation page + canvas.items.first.items += [annotation] end end end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index aa6c3b9..6d40906 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -53,7 +53,8 @@ def canvas_builder IIIFManifest::ManifestServiceLocator::InjectedFactory.new( ManifestBuilder::CanvasBuilder, iiif_canvas_factory: iiif_canvas_factory, - image_builder: image_builder + image_builder: image_builder, + iiif_annotation_page_factory: iiif_annotation_page_factory ) end @@ -97,6 +98,10 @@ def iiif_annotation_factory IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation end + def iiif_annotation_page_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::AnnotationPage + end + def iiif_manifest_factory IIIFManifest::V3::ManifestBuilder::IIIFManifest end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index cd0f7fd..c85a508 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -95,6 +95,21 @@ def display_image expect(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to have_received(:new) .exactly(1).times.with(file_presenter, anything, anything) expect(result['items'].length).to eq 1 + expect(result['items'].first['type']).to eq 'Canvas' + expect(result['items'].first['id']).to eq 'http://test.host/books/book-77/manifest/canvas/test-22' + expect(result['items'].first['height']).to eq 100 + expect(result['items'].first['width']).to eq 100 + expect(result['items'].first['items'].first['type']).to eq 'AnnotationPage' + expect(result['items'].first['items'].first['id']).not_to be_empty + expect(result['items'].first['items'].first['items'].length).to eq 1 + expect(result['items'].first['items'].first['items'].first['type']).to eq 'Annotation' + expect(result['items'].first['items'].first['items'].first['motivation']).to eq 'painting' + expect(result['items'].first['items'].first['items'].first['target']).to eq result['items'].first['id'] + expect(result['items'].first['items'].first['items'].first['body']['type']).to eq 'Image' + expect(result['items'].first['items'].first['items'].first['body']['id']).not_to be_empty + expect(result['items'].first['items'].first['items'].first['body']['height']).to eq 100 + expect(result['items'].first['items'].first['items'].first['body']['width']).to eq 100 + expect(result['items'].first['items'].first['items'].first['body']['format']).to eq 'image/jpeg' end it 'builds a structure if it can' do allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) @@ -158,7 +173,7 @@ def sequence_rendering end end - it 'has a rendering on the sequence' do + it 'has a rendering on the canvas' do allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) From 71dcc52cda3a3169640c9ee5ee1f2531db434e80 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Tue, 3 Apr 2018 13:15:24 -0400 Subject: [PATCH 12/20] Test with real labels --- .../iiif_manifest/v3/manifest_factory_spec.rb | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index c85a508..c710ca6 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -8,10 +8,11 @@ before do class Book - attr_reader :id + attr_reader :id, :label - def initialize(id) + def initialize(id, label: 'A good book') @id = id + @label = label end def description @@ -30,6 +31,10 @@ def manifest_url "http://test.host/books/#{id}/manifest" end + def to_s + label + end + def ranges @ranges ||= [ @@ -50,9 +55,14 @@ def initialize(label:, ranges: [], file_set_presenters: []) end class DisplayImagePresenter - attr_reader :id - def initialize(id: 'test-22') + attr_reader :id, :label + def initialize(id: 'test-22', label: 'Page 1') @id = id + @label = label + end + + def to_s + label end def display_image @@ -71,7 +81,7 @@ def display_image let(:json_result) { JSON.parse(subject.to_h.to_json) } it 'has a label' do - expect(result.label).to eq book_presenter.to_s + expect(result.label).to eq 'A good book' end it 'has an ID' do expect(result['id']).to eq 'http://test.host/books/book-77/manifest' @@ -273,7 +283,7 @@ def sequence_rendering end context 'when there are child works' do - let(:child_work_presenter) { presenter_class.new('test2') } + let(:child_work_presenter) { presenter_class.new('test2', label: 'Inner book') } before do allow(book_presenter).to receive(:work_presenters).and_return([child_work_presenter]) @@ -292,14 +302,14 @@ def sequence_rendering first_child = result['manifests'].first expect(first_child['id']).to eq 'http://test.host/books/test2/manifest' expect(first_child['type']).to eq 'Manifest' - expect(first_child['label']).to eq child_work_presenter.to_s + expect(first_child['label']).to eq 'Inner book' end end context 'when there are child works AND files' do let(:child_work_presenter) { presenter_class.new('test-99') } let(:file_presenter) { DisplayImagePresenter.new(id: 'test-22') } - let(:file_presenter2) { DisplayImagePresenter.new(id: 'test-33') } + let(:file_presenter2) { DisplayImagePresenter.new(id: 'test-33', label: 'Page 2') } let(:chapter_1_range) { ManifestRange.new(label: 'Chapter 1', file_set_presenters: [file_presenter]) } let(:child_work_range) { ManifestRange.new(label: 'Child Work', file_set_presenters: [file_presenter2]) } From 490f427f7f599c2bbb45b831d41077e8ccb8140b Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Tue, 3 Apr 2018 15:49:22 -0400 Subject: [PATCH 13/20] Refactor and prepare for choice builder --- .rubocop_todo.yml | 4 +- lib/iiif_manifest/v3/display_content.rb | 17 +++++++ lib/iiif_manifest/v3/manifest_builder.rb | 4 +- .../v3/manifest_builder/body_builder.rb | 40 +++++++++++++++++ .../v3/manifest_builder/canvas_builder.rb | 41 +++++++++++++++-- .../v3/manifest_builder/content_builder.rb | 38 ++++++++++++++++ .../v3/manifest_builder/iiif_service.rb | 2 +- .../v3/manifest_builder/resource_builder.rb | 17 ------- .../v3/manifest_service_locator.rb | 35 +++++++++++---- .../v3/manifest_builder/body_builder_spec.rb | 44 +++++++++++++++++++ 10 files changed, 207 insertions(+), 35 deletions(-) create mode 100644 lib/iiif_manifest/v3/display_content.rb create mode 100644 lib/iiif_manifest/v3/manifest_builder/body_builder.rb create mode 100644 lib/iiif_manifest/v3/manifest_builder/content_builder.rb delete mode 100644 lib/iiif_manifest/v3/manifest_builder/resource_builder.rb create mode 100644 spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1631598..08caf26 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -11,7 +11,7 @@ Metrics/AbcSize: Exclude: - 'lib/iiif_manifest/manifest_builder/resource_builder.rb' - 'lib/iiif_manifest/manifest_factory.rb' - - 'lib/iiif_manifest/v3/manifest_builder/resource_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/body_builder.rb' - 'lib/iiif_manifest/v3/manifest_factory.rb' # Offense count: 1 @@ -75,7 +75,7 @@ Style/Documentation: - 'lib/iiif_manifest/v3/manifest_builder/iiif_service.rb' - 'lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder/image_builder.rb' - - 'lib/iiif_manifest/v3/manifest_builder/resource_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/body_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder.rb' - 'lib/iiif_manifest/v3/manifest_service_locator.rb' diff --git a/lib/iiif_manifest/v3/display_content.rb b/lib/iiif_manifest/v3/display_content.rb new file mode 100644 index 0000000..ae7dc69 --- /dev/null +++ b/lib/iiif_manifest/v3/display_content.rb @@ -0,0 +1,17 @@ +module IIIFManifest + module V3 + class DisplayContent + attr_reader :url, :width, :height, :duration, :iiif_endpoint, :format + def initialize(url, type:, width:, height:, duration:, label:, format: nil, iiif_endpoint: nil) + @url = url + @type = type + @width = width + @height = height + @duration = duration + @label = label + @format = format + @iiif_endpoint = iiif_endpoint + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder.rb b/lib/iiif_manifest/v3/manifest_builder.rb index f31bead..43d6279 100644 --- a/lib/iiif_manifest/v3/manifest_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder.rb @@ -1,8 +1,8 @@ require_relative 'manifest_builder/iiif_service' require_relative 'manifest_builder/canvas_builder' require_relative 'manifest_builder/record_property_builder' -require_relative 'manifest_builder/image_builder' -require_relative 'manifest_builder/resource_builder' +require_relative 'manifest_builder/content_builder' +require_relative 'manifest_builder/body_builder' require_relative 'manifest_builder/structure_builder' module IIIFManifest diff --git a/lib/iiif_manifest/v3/manifest_builder/body_builder.rb b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb new file mode 100644 index 0000000..505c007 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb @@ -0,0 +1,40 @@ +module IIIFManifest + module V3 + class ManifestBuilder + class BodyBuilder + attr_reader :display_content, :iiif_body_factory, :image_service_builder_factory + def initialize(display_content, iiif_body_factory:, image_service_builder_factory:) + @display_content = display_content + @iiif_body_factory = iiif_body_factory + @image_service_builder_factory = image_service_builder_factory + end + + def apply(annotation) + body['id'] = display_content.url + body['type'] = display_content.type if display_content.try(:type) + body['type'] ||= 'Image' # For backwards-compatibility + body['height'] = display_content.height if display_content.try(:height) + body['width'] = display_content.width if display_content.try(:width) + body['duration'] = display_content.duration if display_content.try(:duration) + body['format'] = display_content.format if display_content.format + image_service_builder.apply(body) if iiif_endpoint + annotation.body = body + end + + private + + def body + @body ||= iiif_body_factory.new + end + + def iiif_endpoint + display_content.try(:iiif_endpoint) + end + + def image_service_builder + image_service_builder_factory.new(iiif_endpoint) + end + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index 4c0ee86..2105554 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -1,17 +1,29 @@ module IIIFManifest module V3 class ManifestBuilder - class CanvasBuilder < ::IIIFManifest::ManifestBuilder::CanvasBuilder - attr_reader :iiif_annotation_page_factory + class CanvasBuilder + attr_reader :record, :parent, :iiif_canvas_factory, :content_builder, :choice_builder, :iiif_annotation_page_factory - def initialize(record, parent, iiif_canvas_factory:, image_builder:, iiif_annotation_page_factory:) + def initialize(record, parent, iiif_canvas_factory:, content_builder:, choice_builder:, iiif_annotation_page_factory:) @record = record @parent = parent @iiif_canvas_factory = iiif_canvas_factory - @image_builder = image_builder + @content_builder = content_builder + @choice_builder = choice_builder @iiif_annotation_page_factory = iiif_annotation_page_factory apply_record_properties + # Presentation 2.x approach attach_image if display_image + # Presentation 3.0 approach + attach_content if display_content + end + + def canvas + @canvas ||= iiif_canvas_factory.new + end + + def path + "#{parent.manifest_url}/canvas/#{record.id}" end def apply(items) @@ -21,6 +33,14 @@ def apply(items) private + def display_image + record.display_image if record.respond_to?(:display_image) + end + + def display_content + record.display_content if record.respond_to?(:display_content) + end + def apply_record_properties canvas['id'] = path canvas.label = record.to_s @@ -31,6 +51,19 @@ def apply_record_properties def annotation_page @annotation_page ||= iiif_annotation_page_factory.new end + + def attach_image + content_builder.new(display_image).apply(canvas) + end + + def attach_content + if display_content.size == 1 + content_builder.new(display_content.first).apply(canvas) + else + byebug + choice_builder.new(display_content).apply(canvas) + end + end end end end diff --git a/lib/iiif_manifest/v3/manifest_builder/content_builder.rb b/lib/iiif_manifest/v3/manifest_builder/content_builder.rb new file mode 100644 index 0000000..761dc36 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/content_builder.rb @@ -0,0 +1,38 @@ +module IIIFManifest + module V3 + class ManifestBuilder + class ContentBuilder + attr_reader :display_content, :iiif_annotation_factory, :body_builder_factory + def initialize(display_content, iiif_annotation_factory:, body_builder_factory:) + @display_content = display_content + @iiif_annotation_factory = iiif_annotation_factory + @body_builder_factory = body_builder_factory + build_resource + end + + def apply(canvas) + annotation['target'] = canvas['id'] + canvas['width'] = annotation.body['width'] + canvas['height'] = annotation.body['height'] + canvas['duration'] = annotation.body['duration'] + # Assume first item in canvas is an annotation page + canvas.items.first.items += [annotation] + end + + private + + def build_resource + body_builder.apply(annotation) + end + + def body_builder + body_builder_factory.new(display_content) + end + + def annotation + @annotation ||= iiif_annotation_factory.new + end + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 3e20be4..64b8821 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -122,7 +122,7 @@ def initial_attributes end end - class Resource < IIIFService + class Body < IIIFService def service=(service) inner_hash['service'] = service end diff --git a/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb b/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb deleted file mode 100644 index ba9db3c..0000000 --- a/lib/iiif_manifest/v3/manifest_builder/resource_builder.rb +++ /dev/null @@ -1,17 +0,0 @@ -module IIIFManifest - module V3 - class ManifestBuilder - class ResourceBuilder < ::IIIFManifest::ManifestBuilder::ResourceBuilder - def apply(annotation) - resource['id'] = display_image.url - resource['type'] = 'Image' - resource['height'] = display_image.height - resource['width'] = display_image.width - resource['format'] = display_image.format if display_image.format - image_service_builder.apply(resource) if iiif_endpoint - annotation.body = resource - end - end - end - end -end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index 6d40906..67722ae 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -53,23 +53,40 @@ def canvas_builder IIIFManifest::ManifestServiceLocator::InjectedFactory.new( ManifestBuilder::CanvasBuilder, iiif_canvas_factory: iiif_canvas_factory, - image_builder: image_builder, + content_builder: content_builder, + choice_builder: nil, iiif_annotation_page_factory: iiif_annotation_page_factory ) end - def image_builder + def content_builder IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - ManifestBuilder::ImageBuilder, + ManifestBuilder::ContentBuilder, iiif_annotation_factory: iiif_annotation_factory, - resource_builder_factory: resource_builder_factory + body_builder_factory: body_builder_factory ) end - def resource_builder_factory + def choice_builder IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - ManifestBuilder::ResourceBuilder, - iiif_resource_factory: iiif_resource_factory, + ManifestBuilder::ChoiceBuilder, + iiif_annotation_factory: iiif_annotation_factory, + body_builder_factory: body_builder_factory + ) + end + + # def image_builder + # IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + # ManifestBuilder::ImageBuilder, + # iiif_annotation_factory: iiif_annotation_factory, + # body_builder_factory: body_builder_factory + # ) + # end + + def body_builder_factory + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::BodyBuilder, + iiif_body_factory: iiif_body_factory, image_service_builder_factory: image_service_builder_factory ) end @@ -90,8 +107,8 @@ def iiif_service_factory IIIFManifest::V3::ManifestBuilder::IIIFService end - def iiif_resource_factory - IIIFManifest::V3::ManifestBuilder::IIIFManifest::Resource + def iiif_body_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body end def iiif_annotation_factory diff --git a/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb new file mode 100644 index 0000000..4283feb --- /dev/null +++ b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +RSpec.describe IIIFManifest::V3::ManifestBuilder::BodyBuilder do + let(:builder) do + described_class.new( + display_image, + iiif_body_factory: IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body, + image_service_builder_factory: image_service_builder_factory + ) + end + let(:url) { 'http://example.com/img1' } + let(:display_image) { IIIFManifest::DisplayImage.new(url, width: 640, height: 480) } + let(:annotation) { IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation.new } + let(:image_service_builder_factory) { IIIFManifest::V3::ManifestServiceLocator.image_service_builder_factory } + + describe '#apply' do + subject { builder.apply(annotation) } + + context 'without iiif_endpoint' do + it 'sets a resource on the annotation' do + subject + expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body + expect(annotation.body['id']).to eq url + expect(annotation.body['type']).to eq 'Image' + expect(annotation.body).not_to have_key 'service' + end + end + + context 'with iiif_endpoint' do + let(:iiif_endpoint) { IIIFManifest::IIIFEndpoint.new('http://example.com/') } + let(:display_image) do + IIIFManifest::DisplayImage.new(url, width: 640, height: 480, iiif_endpoint: iiif_endpoint) + end + + it 'sets a resource on the annotation' do + subject + expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body + expect(annotation.body['id']).to eq url + expect(annotation.body['type']).to eq 'Image' + expect(annotation.body['service']).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFService + end + end + end +end From 7edc97157ed4542e0ed5935a867e1d2669a882f8 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Tue, 3 Apr 2018 16:36:03 -0400 Subject: [PATCH 14/20] WIP add support for choice --- lib/iiif_manifest/v3.rb | 1 + lib/iiif_manifest/v3/display_content.rb | 2 +- lib/iiif_manifest/v3/manifest_builder.rb | 1 + .../v3/manifest_builder/canvas_builder.rb | 1 - .../v3/manifest_builder/choice_builder.rb | 47 +++++++++++++++++++ .../v3/manifest_builder/iiif_service.rb | 17 +++++++ .../v3/manifest_service_locator.rb | 9 +++- .../iiif_manifest/v3/manifest_factory_spec.rb | 4 ++ 8 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 lib/iiif_manifest/v3/manifest_builder/choice_builder.rb diff --git a/lib/iiif_manifest/v3.rb b/lib/iiif_manifest/v3.rb index c0dfebf..bda3093 100644 --- a/lib/iiif_manifest/v3.rb +++ b/lib/iiif_manifest/v3.rb @@ -8,5 +8,6 @@ module V3 autoload :ManifestBuilder autoload :ManifestFactory autoload :ManifestServiceLocator + autoload :DisplayContent end end diff --git a/lib/iiif_manifest/v3/display_content.rb b/lib/iiif_manifest/v3/display_content.rb index ae7dc69..75940f1 100644 --- a/lib/iiif_manifest/v3/display_content.rb +++ b/lib/iiif_manifest/v3/display_content.rb @@ -2,7 +2,7 @@ module IIIFManifest module V3 class DisplayContent attr_reader :url, :width, :height, :duration, :iiif_endpoint, :format - def initialize(url, type:, width:, height:, duration:, label:, format: nil, iiif_endpoint: nil) + def initialize(url, type:, width: nil, height: nil, duration: nil, label: nil, format: nil, iiif_endpoint: nil) @url = url @type = type @width = width diff --git a/lib/iiif_manifest/v3/manifest_builder.rb b/lib/iiif_manifest/v3/manifest_builder.rb index 43d6279..3e75b45 100644 --- a/lib/iiif_manifest/v3/manifest_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder.rb @@ -1,6 +1,7 @@ require_relative 'manifest_builder/iiif_service' require_relative 'manifest_builder/canvas_builder' require_relative 'manifest_builder/record_property_builder' +require_relative 'manifest_builder/choice_builder' require_relative 'manifest_builder/content_builder' require_relative 'manifest_builder/body_builder' require_relative 'manifest_builder/structure_builder' diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index 2105554..9a0dd0f 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -60,7 +60,6 @@ def attach_content if display_content.size == 1 content_builder.new(display_content.first).apply(canvas) else - byebug choice_builder.new(display_content).apply(canvas) end end diff --git a/lib/iiif_manifest/v3/manifest_builder/choice_builder.rb b/lib/iiif_manifest/v3/manifest_builder/choice_builder.rb new file mode 100644 index 0000000..ce7a476 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/choice_builder.rb @@ -0,0 +1,47 @@ +module IIIFManifest + module V3 + class ManifestBuilder + class ChoiceBuilder + attr_reader :display_content, :iiif_annotation_factory, :body_builder_factory, :iiif_choice_factory + def initialize(display_content, iiif_annotation_factory:, body_builder_factory:, iiif_choice_factory:) + @display_content = display_content + @iiif_annotation_factory = iiif_annotation_factory + @body_builder_factory = body_builder_factory + @iiif_choice_factory = iiif_choice_factory + build_choice + end + + def apply(canvas) + annotation['target'] = canvas['id'] + canvas['width'] = choice.items.first['width'] + canvas['height'] = choice.items.first['height'] + canvas['duration'] = choice.items.first['duration'] + annotation.body = choice + # Assume first item in canvas is an annotation page + canvas.items.first.items += [annotation] + end + + private + + def build_choice + display_content.each do |content| + content_body = body_builder(content).apply(iiif_annotation_factory.new) + choice.items += [content_body] + end + end + + def body_builder(content) + body_builder_factory.new(content) + end + + def annotation + @annotation ||= iiif_annotation_factory.new + end + + def choice + @choice ||= iiif_choice_factory.new + end + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 64b8821..4c4e447 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -133,6 +133,23 @@ def initial_attributes end end + class Choice < IIIFService + def items + inner_hash['items'] ||= [] + end + + def items=(items) + inner_hash['items'] = items + end + + def initial_attributes + { + 'type' => 'Choice', + 'choiceHint' => 'user' + } + end + end + class AnnotationPage < IIIFService def items inner_hash['items'] ||= [] diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index 67722ae..f5b72d2 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -54,7 +54,7 @@ def canvas_builder ManifestBuilder::CanvasBuilder, iiif_canvas_factory: iiif_canvas_factory, content_builder: content_builder, - choice_builder: nil, + choice_builder: choice_builder, iiif_annotation_page_factory: iiif_annotation_page_factory ) end @@ -71,7 +71,8 @@ def choice_builder IIIFManifest::ManifestServiceLocator::InjectedFactory.new( ManifestBuilder::ChoiceBuilder, iiif_annotation_factory: iiif_annotation_factory, - body_builder_factory: body_builder_factory + body_builder_factory: body_builder_factory, + iiif_choice_factory: iiif_choice_factory ) end @@ -111,6 +112,10 @@ def iiif_body_factory IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body end + def iiif_choice_factory + IIIFManifest::V3::ManifestBuilder::IIIFManifest::Choice + end + def iiif_annotation_factory IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index c710ca6..d0ef831 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -67,6 +67,10 @@ def to_s def display_image IIIFManifest::DisplayImage.new(id, width: 100, height: 100, format: 'image/jpeg') + # TODO: write tests for #display_content both array of size 1 and array with size greater than 1 + # [IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'High', width: 100, height: 100, duration: 100, format: 'video/mp4'), + # IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'Medium', width: 100, height: 100, duration: 100, format: 'video/mp4'), + # IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'Low', width: 100, height: 100, duration: 100, format: 'video/mp4')] end end end From 060445aa1182803af132bfc9ab68c07aaab42214 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Tue, 3 Apr 2018 16:54:45 -0400 Subject: [PATCH 15/20] Appease rubocop --- .rubocop_todo.yml | 13 ++++++++++++- .../v3/manifest_builder/body_builder.rb | 12 ++++++++---- .../v3/manifest_builder/canvas_builder.rb | 10 ++++++++-- .../v3/manifest_builder/iiif_service.rb | 2 +- .../v3/manifest_builder/image_builder.rb | 15 --------------- lib/iiif_manifest/v3/manifest_service_locator.rb | 8 -------- .../lib/iiif_manifest/v3/manifest_factory_spec.rb | 3 +++ 7 files changed, 32 insertions(+), 31 deletions(-) delete mode 100644 lib/iiif_manifest/v3/manifest_builder/image_builder.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 08caf26..e93264b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -13,6 +13,8 @@ Metrics/AbcSize: - 'lib/iiif_manifest/manifest_factory.rb' - 'lib/iiif_manifest/v3/manifest_builder/body_builder.rb' - 'lib/iiif_manifest/v3/manifest_factory.rb' + - 'lib/iiif_manifest/v3/manifest_builder/content_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/choice_builder.rb' # Offense count: 1 # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. @@ -33,6 +35,7 @@ RSpec/NamedSubject: - 'spec/lib/iiif_manifest/display_image_spec.rb' - 'spec/lib/iiif_manifest/iiif_endpoint_spec.rb' - 'spec/lib/iiif_manifest/manifest_builder/resource_builder_spec.rb' + - 'spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb' # Offense count: 1 # Configuration parameters: IgnoreSymbolicNames. @@ -70,11 +73,13 @@ Style/Documentation: - 'lib/iiif_manifest.rb' - 'lib/iiif_manifest/manifest_builder/iiif_service.rb' - 'lib/iiif_manifest/v3.rb' + - 'lib/iiif_manifest/v3/display_content.rb' - 'lib/iiif_manifest/v3/manifest_factory.rb' - 'lib/iiif_manifest/v3/manifest_builder/structure_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder/iiif_service.rb' - 'lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb' - - 'lib/iiif_manifest/v3/manifest_builder/image_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/choice_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/content_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder/body_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder.rb' @@ -90,6 +95,7 @@ Style/MethodMissing: Metrics/ClassLength: Exclude: - 'lib/iiif_manifest/manifest_service_locator.rb' + - 'lib/iiif_manifest/v3/manifest_service_locator.rb' # Offense count: 1 # Cop supports --auto-correct. @@ -97,3 +103,8 @@ Metrics/ClassLength: # SupportedStyles: percent, brackets Style/SymbolArray: EnforcedStyle: brackets + +Metrics/ParameterLists: + Exclude: + - 'lib/iiif_manifest/v3/display_content.rb' + - 'lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb' diff --git a/lib/iiif_manifest/v3/manifest_builder/body_builder.rb b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb index 505c007..e759bbc 100644 --- a/lib/iiif_manifest/v3/manifest_builder/body_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb @@ -10,6 +10,14 @@ def initialize(display_content, iiif_body_factory:, image_service_builder_factor end def apply(annotation) + build_body + image_service_builder.apply(body) if iiif_endpoint + annotation.body = body + end + + private + + def build_body body['id'] = display_content.url body['type'] = display_content.type if display_content.try(:type) body['type'] ||= 'Image' # For backwards-compatibility @@ -17,12 +25,8 @@ def apply(annotation) body['width'] = display_content.width if display_content.try(:width) body['duration'] = display_content.duration if display_content.try(:duration) body['format'] = display_content.format if display_content.format - image_service_builder.apply(body) if iiif_endpoint - annotation.body = body end - private - def body @body ||= iiif_body_factory.new end diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index 9a0dd0f..b731dfc 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -2,9 +2,15 @@ module IIIFManifest module V3 class ManifestBuilder class CanvasBuilder - attr_reader :record, :parent, :iiif_canvas_factory, :content_builder, :choice_builder, :iiif_annotation_page_factory + attr_reader :record, :parent, :iiif_canvas_factory, :content_builder, + :choice_builder, :iiif_annotation_page_factory - def initialize(record, parent, iiif_canvas_factory:, content_builder:, choice_builder:, iiif_annotation_page_factory:) + def initialize(record, + parent, + iiif_canvas_factory:, + content_builder:, + choice_builder:, + iiif_annotation_page_factory:) @record = record @parent = parent @iiif_canvas_factory = iiif_canvas_factory diff --git a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb index 4c4e447..353bb5d 100644 --- a/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb +++ b/lib/iiif_manifest/v3/manifest_builder/iiif_service.rb @@ -165,7 +165,7 @@ def index def initial_attributes { - 'type' => 'AnnotationPage', + 'type' => 'AnnotationPage' } end end diff --git a/lib/iiif_manifest/v3/manifest_builder/image_builder.rb b/lib/iiif_manifest/v3/manifest_builder/image_builder.rb deleted file mode 100644 index e981898..0000000 --- a/lib/iiif_manifest/v3/manifest_builder/image_builder.rb +++ /dev/null @@ -1,15 +0,0 @@ -module IIIFManifest - module V3 - class ManifestBuilder - class ImageBuilder < ::IIIFManifest::ManifestBuilder::ImageBuilder - def apply(canvas) - annotation['target'] = canvas['id'] - canvas['width'] = annotation.body['width'] - canvas['height'] = annotation.body['height'] - # Assume first item in canvas is an annotation page - canvas.items.first.items += [annotation] - end - end - end - end -end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index f5b72d2..2272e16 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -76,14 +76,6 @@ def choice_builder ) end - # def image_builder - # IIIFManifest::ManifestServiceLocator::InjectedFactory.new( - # ManifestBuilder::ImageBuilder, - # iiif_annotation_factory: iiif_annotation_factory, - # body_builder_factory: body_builder_factory - # ) - # end - def body_builder_factory IIIFManifest::ManifestServiceLocator::InjectedFactory.new( ManifestBuilder::BodyBuilder, diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index d0ef831..d792513 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -67,10 +67,13 @@ def to_s def display_image IIIFManifest::DisplayImage.new(id, width: 100, height: 100, format: 'image/jpeg') + # rubocop:disable Metrics/LineLength # TODO: write tests for #display_content both array of size 1 and array with size greater than 1 + # TODO: write tests for audio, video, and image cases of DisplayContent # [IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'High', width: 100, height: 100, duration: 100, format: 'video/mp4'), # IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'Medium', width: 100, height: 100, duration: 100, format: 'video/mp4'), # IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'Low', width: 100, height: 100, duration: 100, format: 'video/mp4')] + # rubocop:enable Metrics/LineLength end end end From cd6cdff339f6994201ed856f5e53b89d75bf0a35 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 6 Apr 2018 17:05:25 -0400 Subject: [PATCH 16/20] Test body and choice --- lib/iiif_manifest/v3/display_content.rb | 2 +- .../v3/manifest_builder/body_builder.rb | 1 + .../v3/manifest_builder/canvas_builder.rb | 2 +- .../v3/manifest_builder/body_builder_spec.rb | 74 ++++++++++++++-- .../iiif_manifest/v3/manifest_factory_spec.rb | 87 ++++++++++++++++++- 5 files changed, 156 insertions(+), 10 deletions(-) diff --git a/lib/iiif_manifest/v3/display_content.rb b/lib/iiif_manifest/v3/display_content.rb index 75940f1..addd192 100644 --- a/lib/iiif_manifest/v3/display_content.rb +++ b/lib/iiif_manifest/v3/display_content.rb @@ -1,7 +1,7 @@ module IIIFManifest module V3 class DisplayContent - attr_reader :url, :width, :height, :duration, :iiif_endpoint, :format + attr_reader :url, :width, :height, :duration, :iiif_endpoint, :format, :type, :label def initialize(url, type:, width: nil, height: nil, duration: nil, label: nil, format: nil, iiif_endpoint: nil) @url = url @type = type diff --git a/lib/iiif_manifest/v3/manifest_builder/body_builder.rb b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb index e759bbc..0e2b4b2 100644 --- a/lib/iiif_manifest/v3/manifest_builder/body_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb @@ -25,6 +25,7 @@ def build_body body['width'] = display_content.width if display_content.try(:width) body['duration'] = display_content.duration if display_content.try(:duration) body['format'] = display_content.format if display_content.format + body['label'] = display_content.label if display_content.try(:label) end def body diff --git a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb index b731dfc..349d5ea 100644 --- a/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb @@ -44,7 +44,7 @@ def display_image end def display_content - record.display_content if record.respond_to?(:display_content) + Array.wrap(record.display_content) if record.respond_to?(:display_content) end def apply_record_properties diff --git a/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb index 4283feb..592c6b2 100644 --- a/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb @@ -3,13 +3,13 @@ RSpec.describe IIIFManifest::V3::ManifestBuilder::BodyBuilder do let(:builder) do described_class.new( - display_image, + display_content, iiif_body_factory: IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body, image_service_builder_factory: image_service_builder_factory ) end let(:url) { 'http://example.com/img1' } - let(:display_image) { IIIFManifest::DisplayImage.new(url, width: 640, height: 480) } + let(:display_content) { IIIFManifest::DisplayImage.new(url, width: 640, height: 480) } let(:annotation) { IIIFManifest::V3::ManifestBuilder::IIIFManifest::Annotation.new } let(:image_service_builder_factory) { IIIFManifest::V3::ManifestServiceLocator.image_service_builder_factory } @@ -17,7 +17,7 @@ subject { builder.apply(annotation) } context 'without iiif_endpoint' do - it 'sets a resource on the annotation' do + it 'sets a body on the annotation' do subject expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body expect(annotation.body['id']).to eq url @@ -28,11 +28,11 @@ context 'with iiif_endpoint' do let(:iiif_endpoint) { IIIFManifest::IIIFEndpoint.new('http://example.com/') } - let(:display_image) do + let(:display_content) do IIIFManifest::DisplayImage.new(url, width: 640, height: 480, iiif_endpoint: iiif_endpoint) end - it 'sets a resource on the annotation' do + it 'sets a body on the annotation' do subject expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body expect(annotation.body['id']).to eq url @@ -40,5 +40,69 @@ expect(annotation.body['service']).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFService end end + + context 'with display image' do + it 'sets a body on the annotation' do + subject + expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body + expect(annotation.body['id']).to eq url + expect(annotation.body['type']).to eq 'Image' + expect(annotation.body['width']).to eq 640 + expect(annotation.body['height']).to eq 480 + expect(annotation.body['format']).to be_nil + expect(annotation.body['duration']).to be_nil + expect(annotation.body['label']).to be_nil + end + end + + context 'with display content' do + context 'with image content' do + let(:display_content) { IIIFManifest::V3::DisplayContent.new(url, width: 640, height: 480, type: 'Image', format: 'image/jpeg', label: 'full') } + + it 'sets a body on the annotation' do + subject + expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body + expect(annotation.body['id']).to eq url + expect(annotation.body['type']).to eq 'Image' + expect(annotation.body['width']).to eq 640 + expect(annotation.body['height']).to eq 480 + expect(annotation.body['format']).to eq 'image/jpeg' + expect(annotation.body['duration']).to be_nil + expect(annotation.body['label']).to eq 'full' + end + end + + context 'with audio content' do + let(:display_content) { IIIFManifest::V3::DisplayContent.new(url, duration: 1000, type: 'Audio', label: 'Track 1', format: 'audio/aac') } + + it 'sets a body on the annotation' do + subject + expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body + expect(annotation.body['id']).to eq url + expect(annotation.body['type']).to eq 'Audio' + expect(annotation.body['duration']).to eq 1000 + expect(annotation.body['format']).to eq 'audio/aac' + expect(annotation.body['label']).to eq 'Track 1' + expect(annotation.body['width']).to be_nil + expect(annotation.body['height']).to be_nil + end + end + + context 'with video content' do + let(:display_content) { IIIFManifest::V3::DisplayContent.new(url, width: 640, height: 480, duration: 1000, type: 'Video', label: 'Reel 1', format: 'video/mp4') } + + it 'sets a body on the annotation' do + subject + expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body + expect(annotation.body['id']).to eq url + expect(annotation.body['type']).to eq 'Video' + expect(annotation.body['width']).to eq 640 + expect(annotation.body['height']).to eq 480 + expect(annotation.body['duration']).to eq 1000 + expect(annotation.body['label']).to eq 'Reel 1' + expect(annotation.body['format']).to eq 'video/mp4' + end + end + end end end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index d792513..8add693 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -68,8 +68,6 @@ def to_s def display_image IIIFManifest::DisplayImage.new(id, width: 100, height: 100, format: 'image/jpeg') # rubocop:disable Metrics/LineLength - # TODO: write tests for #display_content both array of size 1 and array with size greater than 1 - # TODO: write tests for audio, video, and image cases of DisplayContent # [IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'High', width: 100, height: 100, duration: 100, format: 'video/mp4'), # IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'Medium', width: 100, height: 100, duration: 100, format: 'video/mp4'), # IIIFManifest::V3::DisplayContent.new(id, type: 'Video', label: 'Low', width: 100, height: 100, duration: 100, format: 'video/mp4')] @@ -103,7 +101,7 @@ def display_image context 'when there is a fileset' do let(:file_presenter) { DisplayImagePresenter.new } - it 'returns a sequence' do + it 'returns items' do allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) @@ -377,5 +375,88 @@ def sequence_rendering expect(result.viewingDirection).to eq 'right-to-left' end end + + context 'when there is display_content' do + before do + class DisplayContentPresenter + attr_reader :id, :label, :content + def initialize(id: 'test-22', label: 'Section 1', content:) + @id = id + @label = label + @content = content + end + + def to_s + label + end + + def display_content + content + end + end + end + + after do + Object.send(:remove_const, :DisplayContentPresenter) + end + + let(:file_presenter) { DisplayContentPresenter.new(content: content) } + let(:content_annotation_body) { result['items'].first['items'].first['items'].first["body"] } + + before do + allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) + allow(file_presenter).to receive(:respond_to?).with(:display_image).and_return(false) + allow(file_presenter).to receive(:respond_to?).with(:display_content).and_call_original + expect(file_presenter).not_to receive(:display_image) + end + + context 'with a DisplayImage' do + let(:content) { IIIFManifest::DisplayImage.new(SecureRandom.uuid, width: 100, height: 100, format: 'image/jpeg') } + + it 'returns items' do + expect(content_annotation_body['type']).to eq 'Image' + expect(content_annotation_body['id']).not_to be_empty + expect(content_annotation_body['height']).to eq 100 + expect(content_annotation_body['width']).to eq 100 + expect(content_annotation_body['format']).to eq 'image/jpeg' + end + end + + context 'with a single file' do + let(:content) { IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'High', width: 100, height: 100, duration: 100, format: 'video/mp4') } + + it 'returns items' do + expect(content_annotation_body['type']).to eq 'Video' + expect(content_annotation_body['id']).not_to be_empty + expect(content_annotation_body['height']).to eq 100 + expect(content_annotation_body['width']).to eq 100 + expect(content_annotation_body['format']).to eq 'video/mp4' + expect(content_annotation_body['duration']).to eq 100 + expect(content_annotation_body['label']).to eq 'High' + end + end + + context 'with multiple files' do + let(:content) { [IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'High', width: 100, height: 100, duration: 100, format: 'video/mp4'), + IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'Medium', width: 100, height: 100, duration: 100, format: 'video/mp4'), + IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'Low', width: 100, height: 100, duration: 100, format: 'video/mp4')] } + + it 'returns items' do + expect(content_annotation_body['type']).to eq 'Choice' + expect(content_annotation_body['choiceHint']).to eq 'user' + expect(content_annotation_body.items.size).to eq 3 + content_annotation_body.items.each do |choice| + expect(choice['type']).to eq 'Video' + expect(choice['id']).not_to be_empty + expect(choice['width']).to eq 100 + expect(choice['height']).to eq 100 + expect(choice['format']).to eq 'video/mp4' + expect(choice['duration']).to eq 100 + expect(choice['label']).not_to be_empty + end + end + end + end end end From 6c75b263d3d45da2978fc789886ccddeccc27a3e Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 6 Apr 2018 17:27:39 -0400 Subject: [PATCH 17/20] Appease rubocop --- .rubocop_todo.yml | 6 +++ .../v3/manifest_builder/body_builder.rb | 7 ++- .../v3/manifest_builder/body_builder_spec.rb | 24 +++++++-- .../iiif_manifest/v3/manifest_factory_spec.rb | 51 ++++++++++++++----- 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e93264b..798eaf7 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -47,6 +47,7 @@ RSpec/ExampleLength: Exclude: - 'spec/lib/iiif_manifest/manifest_factory_spec.rb' - 'spec/lib/iiif_manifest/v3/manifest_factory_spec.rb' + - 'spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb' # Offense count: 20 Style/Documentation: @@ -108,3 +109,8 @@ Metrics/ParameterLists: Exclude: - 'lib/iiif_manifest/v3/display_content.rb' - 'lib/iiif_manifest/v3/manifest_builder/canvas_builder.rb' + +RSpec/NestedGroups: + Exclude: + - 'spec/lib/iiif_manifest/v3/manifest_factory_spec.rb' + - 'spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb' diff --git a/lib/iiif_manifest/v3/manifest_builder/body_builder.rb b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb index 0e2b4b2..73454b4 100644 --- a/lib/iiif_manifest/v3/manifest_builder/body_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/body_builder.rb @@ -19,8 +19,7 @@ def apply(annotation) def build_body body['id'] = display_content.url - body['type'] = display_content.type if display_content.try(:type) - body['type'] ||= 'Image' # For backwards-compatibility + body['type'] = body_type body['height'] = display_content.height if display_content.try(:height) body['width'] = display_content.width if display_content.try(:width) body['duration'] = display_content.duration if display_content.try(:duration) @@ -32,6 +31,10 @@ def body @body ||= iiif_body_factory.new end + def body_type + display_content.try(:type) || 'Image' + end + def iiif_endpoint display_content.try(:iiif_endpoint) end diff --git a/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb index 592c6b2..fce76f9 100644 --- a/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb @@ -57,7 +57,13 @@ context 'with display content' do context 'with image content' do - let(:display_content) { IIIFManifest::V3::DisplayContent.new(url, width: 640, height: 480, type: 'Image', format: 'image/jpeg', label: 'full') } + let(:display_content) do + IIIFManifest::V3::DisplayContent.new(url, width: 640, + height: 480, + type: 'Image', + format: 'image/jpeg', + label: 'full') + end it 'sets a body on the annotation' do subject @@ -73,7 +79,12 @@ end context 'with audio content' do - let(:display_content) { IIIFManifest::V3::DisplayContent.new(url, duration: 1000, type: 'Audio', label: 'Track 1', format: 'audio/aac') } + let(:display_content) do + IIIFManifest::V3::DisplayContent.new(url, duration: 1000, + type: 'Audio', + format: 'audio/aac', + label: 'Track 1') + end it 'sets a body on the annotation' do subject @@ -89,7 +100,14 @@ end context 'with video content' do - let(:display_content) { IIIFManifest::V3::DisplayContent.new(url, width: 640, height: 480, duration: 1000, type: 'Video', label: 'Reel 1', format: 'video/mp4') } + let(:display_content) do + IIIFManifest::V3::DisplayContent.new(url, width: 640, + height: 480, + duration: 1000, + type: 'Video', + format: 'video/mp4', + label: 'Reel 1') + end it 'sets a body on the annotation' do subject diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index 8add693..5723812 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -394,6 +394,9 @@ def display_content content end end + + allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original + allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) end after do @@ -401,18 +404,14 @@ def display_content end let(:file_presenter) { DisplayContentPresenter.new(content: content) } - let(:content_annotation_body) { result['items'].first['items'].first['items'].first["body"] } - - before do - allow(IIIFManifest::V3::ManifestBuilder::CanvasBuilder).to receive(:new).and_call_original - allow(book_presenter).to receive(:file_set_presenters).and_return([file_presenter]) - allow(file_presenter).to receive(:respond_to?).with(:display_image).and_return(false) - allow(file_presenter).to receive(:respond_to?).with(:display_content).and_call_original - expect(file_presenter).not_to receive(:display_image) - end + let(:content_annotation_body) { result['items'].first['items'].first['items'].first['body'] } context 'with a DisplayImage' do - let(:content) { IIIFManifest::DisplayImage.new(SecureRandom.uuid, width: 100, height: 100, format: 'image/jpeg') } + let(:content) do + IIIFManifest::DisplayImage.new(SecureRandom.uuid, width: 100, + height: 100, + format: 'image/jpeg') + end it 'returns items' do expect(content_annotation_body['type']).to eq 'Image' @@ -424,7 +423,14 @@ def display_content end context 'with a single file' do - let(:content) { IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'High', width: 100, height: 100, duration: 100, format: 'video/mp4') } + let(:content) do + IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, width: 100, + height: 100, + duration: 100, + type: 'Video', + format: 'video/mp4', + label: 'High') + end it 'returns items' do expect(content_annotation_body['type']).to eq 'Video' @@ -438,9 +444,26 @@ def display_content end context 'with multiple files' do - let(:content) { [IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'High', width: 100, height: 100, duration: 100, format: 'video/mp4'), - IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'Medium', width: 100, height: 100, duration: 100, format: 'video/mp4'), - IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', label: 'Low', width: 100, height: 100, duration: 100, format: 'video/mp4')] } + let(:content) do + [IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', + label: 'High', + width: 100, + height: 100, + duration: 100, + format: 'video/mp4'), + IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', + label: 'Medium', + width: 100, + height: 100, + duration: 100, + format: 'video/mp4'), + IIIFManifest::V3::DisplayContent.new(SecureRandom.uuid, type: 'Video', + label: 'Low', + width: 100, + height: 100, + duration: 100, + format: 'video/mp4')] + end it 'returns items' do expect(content_annotation_body['type']).to eq 'Choice' From 88c48e2f2ee4eb77321ce830b4c5304947e174b2 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Thu, 12 Apr 2018 12:15:51 -0400 Subject: [PATCH 18/20] Transform metadata into prezi 3 style --- .../record_property_builder.rb | 54 ++++++++++++++++++- .../iiif_manifest/v3/manifest_factory_spec.rb | 34 ++++++++---- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index 37c3a80..53eb8c3 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -14,13 +14,18 @@ def initialize(record, end # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/MethodLength def apply(manifest) manifest['id'] = record.manifest_url.to_s manifest.label = record.to_s manifest.summary = record.description manifest.behavior = viewing_hint if viewing_hint.present? manifest.viewing_direction = viewing_direction if viewing_direction.present? - manifest.metadata = record.manifest_metadata if valid_metadata? + if valid_v3_metadata? + manifest.metadata = record.manifest_metadata + elsif valid_metadata? + manifest.metadata = transform_metadata(record.manifest_metadata) + end manifest.service = services if search_service.present? manifest.rendering = populate_rendering # Build the items array @@ -28,6 +33,7 @@ def apply(manifest) manifest end # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/MethodLength def populate_rendering if record.respond_to?(:sequence_rendering) @@ -46,6 +52,52 @@ def populate_rendering def canvas_builder canvas_builder_factory.from(record) end + + # Validate manifest_metadata against the IIIF spec format for metadata + # + # @return [Boolean] + def valid_v3_metadata? + return false unless record.respond_to?(:manifest_metadata) + metadata = record.manifest_metadata + valid_v3_metadata_structure?(metadata) && valid_metadata_content?(metadata) + end + + # Manifest metadata must be an array containing hashes + # + # @param metadata [Array] a list of metadata with label and value as required keys for each entry + # @return [Boolean] + def valid_v3_metadata_structure?(metadata) + metadata.is_a?(Array) && metadata.all? do |v| + v.is_a?(Hash) && v.all? do |k2, v2| + k2.is_a?(String) && v2.is_a?(Hash) && v2.all? do |k3, v3| + k3.is_a?(String) && v3.is_a?(Array) + end + end + end + end + + def transform_metadata(metadata) + metadata.collect { |field| transform_field(field) } + end + + def transform_field(field) + metadata_field = {} + metadata_field['label'] = transform_obj(field['label']) + metadata_field['value'] = transform_obj(field['value']) + metadata_field + end + + def transform_obj(obj) + obj.is_a?(Hash) ? transform_hash_value(obj) : transform_obj_value(obj) + end + + def transform_obj_value(obj) + { '@none' => Array(obj) } + end + + def transform_hash_value(h) + { h['@language'] => Array(h['@value']) } + end end end end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index 5723812..ca0fd29 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -209,21 +209,33 @@ def sequence_rendering end context 'when there is a manifest_metadata method' do - let(:metadata) { [{ 'label' => 'Title', 'value' => 'Title of the Item' }] } + context 'that returns invalid data' do + let(:metadata) { 'invalid data' } - it 'has metadata' do - allow(book_presenter).to receive(:manifest_metadata).and_return(metadata) - expect(result['metadata'][0]['label']).to eq 'Title' - expect(result['metadata'][0]['value']).to eq 'Title of the Item' + it 'has no metadata' do + allow(book_presenter).to receive(:manifest_metadata).and_return(metadata) + expect(result['metadata']).to eq nil + end end - end - context 'when there is a manifest_metadata method with invalid data' do - let(:metadata) { 'invalid data' } + context 'that returns presentation 2 style metadata' do + let(:metadata) { [{ 'label' => 'Title', 'value' => 'Title of the Item' }] } - it 'has no metadata' do - allow(book_presenter).to receive(:manifest_metadata).and_return(metadata) - expect(result['metadata']).to eq nil + it 'has metadata' do + allow(book_presenter).to receive(:manifest_metadata).and_return(metadata) + expect(result['metadata'][0]['label']).to eq('@none' => ['Title']) + expect(result['metadata'][0]['value']).to eq('@none' => ['Title of the Item']) + end + end + + context 'that returns presentation 3 style metadata' do + let(:metadata) { [{ 'label' => { '@en' => ['Title'] }, 'value' => { '@en' => ['Title of the Item'] } }] } + + it 'has metadata' do + allow(book_presenter).to receive(:manifest_metadata).and_return(metadata) + expect(result['metadata'][0]['label']).to eq('@en' => ['Title']) + expect(result['metadata'][0]['value']).to eq('@en' => ['Title of the Item']) + end end end From 0b72969eef76f3be08b43bd52aedd45fe8a5488c Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Thu, 12 Apr 2018 13:46:32 -0400 Subject: [PATCH 19/20] Fix more rubocop issues --- .../v3/manifest_builder/record_property_builder.rb | 4 ++-- spec/lib/iiif_manifest/v3/manifest_factory_spec.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb index 53eb8c3..b8db378 100644 --- a/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb @@ -95,8 +95,8 @@ def transform_obj_value(obj) { '@none' => Array(obj) } end - def transform_hash_value(h) - { h['@language'] => Array(h['@value']) } + def transform_hash_value(hash) + { hash['@language'] => Array(hash['@value']) } end end end diff --git a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb index ca0fd29..8d7a893 100644 --- a/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_factory_spec.rb @@ -209,7 +209,7 @@ def sequence_rendering end context 'when there is a manifest_metadata method' do - context 'that returns invalid data' do + context 'with invalid data' do let(:metadata) { 'invalid data' } it 'has no metadata' do @@ -218,7 +218,7 @@ def sequence_rendering end end - context 'that returns presentation 2 style metadata' do + context 'with presentation 2 style metadata' do let(:metadata) { [{ 'label' => 'Title', 'value' => 'Title of the Item' }] } it 'has metadata' do @@ -228,7 +228,7 @@ def sequence_rendering end end - context 'that returns presentation 3 style metadata' do + context 'with presentation 3 style metadata' do let(:metadata) { [{ 'label' => { '@en' => ['Title'] }, 'value' => { '@en' => ['Title of the Item'] } }] } it 'has metadata' do From 139b553dd5563890dc2ba038b48445556fea6559 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Thu, 12 Apr 2018 14:21:03 -0400 Subject: [PATCH 20/20] Set image service the prezi3 way --- .rubocop_todo.yml | 1 + lib/iiif_manifest/v3/manifest_builder.rb | 1 + .../manifest_builder/image_service_builder.rb | 35 +++++++++++++++++++ .../v3/manifest_service_locator.rb | 7 ++++ .../v3/manifest_builder/body_builder_spec.rb | 7 +++- 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 798eaf7..c18e6d0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -83,6 +83,7 @@ Style/Documentation: - 'lib/iiif_manifest/v3/manifest_builder/content_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder/body_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder/record_property_builder.rb' + - 'lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb' - 'lib/iiif_manifest/v3/manifest_builder.rb' - 'lib/iiif_manifest/v3/manifest_service_locator.rb' - 'spec/lib/iiif_manifest/manifest_factory_spec.rb' diff --git a/lib/iiif_manifest/v3/manifest_builder.rb b/lib/iiif_manifest/v3/manifest_builder.rb index 3e75b45..c122199 100644 --- a/lib/iiif_manifest/v3/manifest_builder.rb +++ b/lib/iiif_manifest/v3/manifest_builder.rb @@ -5,6 +5,7 @@ require_relative 'manifest_builder/content_builder' require_relative 'manifest_builder/body_builder' require_relative 'manifest_builder/structure_builder' +require_relative 'manifest_builder/image_service_builder' module IIIFManifest module V3 diff --git a/lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb b/lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb new file mode 100644 index 0000000..0364e83 --- /dev/null +++ b/lib/iiif_manifest/v3/manifest_builder/image_service_builder.rb @@ -0,0 +1,35 @@ +module IIIFManifest + module V3 + class ManifestBuilder + class ImageServiceBuilder + attr_reader :iiif_endpoint, :iiif_service_factory + def initialize(iiif_endpoint, iiif_service_factory:) + @iiif_endpoint = iiif_endpoint + @iiif_service_factory = iiif_service_factory + end + + def apply(resource) + service['id'] = iiif_endpoint.url + service['profile'] = iiif_endpoint.profile + service['type'] = determine_type(iiif_endpoint.context) + resource.service = [service] + end + + private + + def determine_type(context) + case context + when 'http://iiif.io/api/image/1/context.json' + 'ImageService1' + when 'http://iiif.io/api/image/2/context.json' + 'ImageService2' + end + end + + def service + @service ||= iiif_service_factory.new + end + end + end + end +end diff --git a/lib/iiif_manifest/v3/manifest_service_locator.rb b/lib/iiif_manifest/v3/manifest_service_locator.rb index 2272e16..75f7478 100644 --- a/lib/iiif_manifest/v3/manifest_service_locator.rb +++ b/lib/iiif_manifest/v3/manifest_service_locator.rb @@ -84,6 +84,13 @@ def body_builder_factory ) end + def image_service_builder_factory + IIIFManifest::ManifestServiceLocator::InjectedFactory.new( + ManifestBuilder::ImageServiceBuilder, + iiif_service_factory: iiif_service_factory + ) + end + def sequence_builder raise NotImplementedError end diff --git a/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb index fce76f9..8f83ac0 100644 --- a/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb +++ b/spec/lib/iiif_manifest/v3/manifest_builder/body_builder_spec.rb @@ -37,7 +37,12 @@ expect(annotation.body).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFManifest::Body expect(annotation.body['id']).to eq url expect(annotation.body['type']).to eq 'Image' - expect(annotation.body['service']).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFService + expect(annotation.body['service']).to be_kind_of Array + service = annotation.body['service'].first + expect(service).to be_kind_of IIIFManifest::V3::ManifestBuilder::IIIFService + expect(service['id']).to eq iiif_endpoint.url + expect(service['profile']).to eq iiif_endpoint.profile + expect(service['type']).to eq 'ImageService2' end end