O'Sullivan: A Ruby API for working with IIIF Presentation manifests
Installation
From the source code do rake install
, or get the latest release from RubyGems.
Building New Objects
There is (or will be) a class for all types in IIIF Presentation API Spec.
require 'iiif/presentation'
seed = {
'@id' => 'http://example.com/manifest',
'label' => 'My Manifest'
}
# Any options you add are added to the object
manifest = IIIF::Presentation::Manifest.new(seed)
# sequences array is generated for you, but let's add a sequence object
sequence = IIIF::Presentation::Sequence.new()
sequence['@id'] = "http://example.com/manifest/seq/"
manifest.sequences << sequence
canvas = IIIF::Presentation::Canvas.new()
# All classes act like `ActiveSupport::OrderedHash`es, for the most part.
# Use `[]=` to set JSON-LD properties...
canvas['@id'] = 'http://example.com/canvas'
# ...but there are also accessors and mutators for the properties mentioned in
# the spec
canvas.width = 10
canvas.height = 20
canvas.label = 'My Canvas'
# Add images
service = IIIF::Presentation::Resource.new('@context' => 'http://iiif.io/api/image/2/context.json', 'profile' => 'http://iiif.io/api/image/2/level2.json', '@id' => "http://images.exampl.com/loris2/my-image")
image = IIIF::Presentation::ImageResource.new()
image['@id'] = "http://images.exampl.com/loris2/my-image/full/#{canvas.width},#{canvas.height}/0/default.jpg"
image.format = "image/jpeg"
image.width = canvas.width
image.height = canvas.height
image.service = service
images = IIIF::Presentation::Resource.new('@type' => 'oa:Annotation', 'motivation' => 'sc:painting', '@id' => "#{canvas['@id']}/images", 'resource' => image)
canvas.images << images
# Add other content resources
oc = IIIF::Presentation::Resource.new('@id' => 'http://example.com/content')
canvas.other_content << oc
manifest.sequences.first.canvases << canvas
puts manifest.to_json(pretty: true)
Methods are generated dynamically, which means #methods
is your friend:
manifest = IIIF::Presentation::Manifest.new()
puts manifest.methods(false)
> label=
> label
> description=
> description
> thumbnail=
> thumbnail
> attribution=
> attribution
> viewing_hint=
> viewingHint=
> viewing_hint
> viewingHint
[...]
Note that multi-word properties are implemented as snake_case (because this is Ruby), but is serialized as camelCase. There are camelCase aliases for these.
manifest = IIIF::Presentation::Manifest.new()
manifest.viewing_hint = 'paged'
puts manifest.to_json(pretty: true, force: true) # force: true skips validations
> {
> "@context": "http://iiif.io/api/presentation/2/context.json",
> "@type": "sc:Manifest",
> "viewingHint": "paged"
> }
Parsing Existing Objects
Use IIIF::Service#parse
. It will figure out what the object
should be, based on @type
, and fall back to Hash
when
it can't e.g.:
seed = '{
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": "http://example.com/manifest",
"@type": "sc:Manifest",
"label": "My Manifest",
"service": {
"@context": "http://iiif.io/api/image/2/context.json",
"@id":"http://www.example.org/images/book1-page1",
"profile":"http://iiif.io/api/image/2/profiles/level2.json"
},
"seeAlso": {
"@id": "http://www.example.org/library/catalog/book1.marc",
"format": "application/marc"
},
"sequences": [
{
"@id":"http://www.example.org/iiif/book1/sequence/normal",
"@type":"sc:Sequence",
"label":"Current Page Order",
"viewingDirection":"left-to-right",
"viewingHint":"paged",
"startCanvas": "http://www.example.org/iiif/book1/canvas/p2",
"canvases": [
{
"@id": "http://example.com/canvas",
"@type": "sc:Canvas",
"width": 10,
"height": 20,
"label": "My Canvas",
"otherContent": [
{
"@id": "http://example.com/content",
"@type":"sc:AnnotationList",
"motivation": "sc:painting"
}
]
}
]
}
]
}'
obj = IIIF::Service.parse(seed) # can also be a file path or a Hash
puts obj.class
puts obj.see_also.class
> IIIF::Presentation::Manifest
> Hash
Validation and Exceptions
This is work in progress. Right now exceptions are generally raised when you try to set something to a type it should never be:
manifest = IIIF::Presentation::Manifest.new
manifest.sequences = 'quux'
> [...] sequences must be an Array. (IIIF::Presentation::IllegalValueError)
and also if any required properties are missing when calling to_json
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
puts canvas.to_json(pretty: true)
> A(n) width is required for each IIIF::Presentation::Canvas (IIIF::Presentation::MissingRequiredKeyError)
but you can skip this validation by adding force: true
:
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
puts canvas.to_json(pretty: true, force: true)
> {
> "@context": "http://iiif.io/api/presentation/2/context.json",
> "@id": "http://example.com/canvas",
> "@type": "sc:Canvas"
> }
This all needs a bit of tidying up, finishing, and refactoring, so expect it to change.