Project

osullivan

0.03
No release in over 3 years
Low commit activity in last 3 years
There's a lot of open issues
API for working with IIIF Presentation manifests.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0
>= 0
~> 2.9.3

Runtime

>= 3.2.18
~> 0.9.0
>= 0
 Project Readme

O'Sullivan: A Ruby API for working with IIIF Presentation manifests

Coverage Status Gem Version

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.