Skip to content

Commit

Permalink
WIP v3
Browse files Browse the repository at this point in the history
  • Loading branch information
jcoyne committed Jun 8, 2023
1 parent 0b84d48 commit 8a08b1a
Show file tree
Hide file tree
Showing 33 changed files with 799 additions and 515 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ end
if ENV['FARADAY_VERSION']
gem 'faraday', ENV['FARADAY_VERSION']
end

gem 'debug'
102 changes: 61 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ 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](http://iiif.io/api/presentation/2.0/).
There is (or will be) a class for all types in [IIIF Presentation API Spec v3.0](http://iiif.io/api/presentation/3.0/).



Expand All @@ -19,7 +19,7 @@ There is (or will be) a class for all types in [IIIF Presentation API Spec](http
require 'iiif/presentation'

seed = {
'@id' => 'http://example.com/manifest',
'id' => 'http://example.com/manifest',
'label' => 'My Manifest'
}
# Any options you add are added to the object
Expand All @@ -33,32 +33,34 @@ 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'
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")
service = IIIF::Presentation::Resource.new('@context' => 'http://iiif.io/api/image/3/context.json', 'profile' => 'http://iiif.io/api/image/2/level2.json', 'id' => "http://images.exampl.com/loris2/my-image")

image = IIIF::Presentation::ImageResource.new()
i['@id'] = "http://images.exampl.com/loris2/my-image/full/#{canvas.width},#{canvas.height}/0/default.jpg"
image = IIIF::Presentation::ImageResource.new
i.id = "http://images.exampl.com/loris2/my-image/full/#{canvas.width},#{canvas.height}/0/default.jpg"
i.format = "image/jpeg"
i.width = canvas.width
i.height = canvas.height
i.service = service

images = IIIF::Presentation::Resource.new('@type' => 'oa:Annotation', 'motivation' => 'sc:painting', '@id' => "#{canvas['@id']}/images", 'resource' => i)
annotation = IIIF::Presentation::Annotation.new('type' => 'Annotation', 'motivation' => 'painting', 'id' => "#{canvas.id}/images", 'resource' => i)

canvas.images << images
annotation_page = IIIF::Presentation::AnnotationPage.new()
annotation_page.items << annotation
canvas.items << annoation_page

# Add other content resources
oc = IIIF::Presentation::Resource.new('@id' => 'http://example.com/content')
canvas.other_content << oc

manifest.sequences.first.canvases << canvas
manifest.canvases << canvas

puts manifest.to_json(pretty: true)
```
Expand Down Expand Up @@ -92,8 +94,8 @@ 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",
> "@context": "http://iiif.io/api/presentation/3/context.json",
> "type": "Manifest",
> "viewingHint": "paged"
> }

Expand All @@ -102,44 +104,61 @@ puts manifest.to_json(pretty: true, force: true) # force: true skips validations
## 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
should be, based on `type`, and fall back to `Hash` when
it can't e.g.:

```ruby
seed = '{
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": "http://example.com/manifest",
"@type": "sc:Manifest",
"@context": "http://iiif.io/api/presentation/3/context.json",
"id": "http://example.com/manifest",
"type": "Manifest",
"label": "My Manifest",
"service": {
"@context": "http://iiif.io/api/image/2/context.json",
"@id":"http://www.example.org/images/book1-page1",
"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",
"id": "http://www.example.org/library/catalog/book1.marc",
"format": "application/marc"
},
"sequences": [
"items": [
{
"@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": "Canvas",
"width": 10,
"height": 20,
"label": "My Canvas",
"items": [
{
"@id": "http://example.com/canvas",
"@type": "sc:Canvas",
"width": 10,
"height": 20,
"label": "My Canvas",
"otherContent": [
"id": "https://example.org/iiif/book1/page/p1/1",
"type": "AnnotationPage",
"items": [
{
"@id": "http://example.com/content",
"@type":"sc:AnnotationList",
"motivation": "sc:painting"
"id": "https://example.org/iiif/book1/annotation/p0001-image",
"type": "Annotation",
"motivation": "painting",
"body": {
"id": "https://example.org/iiif/book1/page1/full/max/0/default.jpg",
"type": "Image",
"format": "image/jpeg",
"service": [
{
"id": "https://example.org/iiif/book1/page1",
"type": "ImageService3",
"profile": "level2",
"service": [
{
"@id": "https://example.org/iiif/auth/login",
"@type": "AuthCookieService1"
}
]
}
],
"height": 2000,
"width": 1500
},
"target": "https://example.org/iiif/book1/canvas/p1"
}
]
}
Expand All @@ -148,6 +167,7 @@ seed = '{
]
}'


obj = IIIF::Service.parse(seed) # can also be a file path or a Hash
puts obj.class
puts obj.see_also.class
Expand All @@ -163,15 +183,15 @@ try to set something to a type it should never be:

```ruby
manifest = IIIF::Presentation::Manifest.new
manifest.sequences = 'quux'
manifest.items = 'quux'

> [...] sequences must be an Array. (IIIF::Presentation::IllegalValueError)
> [...] items must be an Array. (IIIF::Presentation::IllegalValueError)
```

and also if any required properties are missing when calling `to_json`

```ruby
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
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)
Expand All @@ -180,13 +200,13 @@ puts canvas.to_json(pretty: true)
but you can skip this validation by adding `force: true`:

```ruby
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
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"
> "@context": "http://iiif.io/api/presentation/3/context.json",
> "id": "http://example.com/canvas",
> "type": "Canvas"
> }
```
This all needs a bit of tidying up, finishing, and refactoring, so expect it to
Expand Down
4 changes: 2 additions & 2 deletions lib/iiif/presentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
manifest
resource
image_resource
sequence
service
range
start
}.each do |f|
require File.join(File.dirname(__FILE__), 'presentation', f)
end
Expand All @@ -20,7 +20,7 @@

module IIIF
module Presentation
CONTEXT ||= 'http://iiif.io/api/presentation/2/context.json'
CONTEXT ||= 'http://iiif.io/api/presentation/3/context.json'

class MissingRequiredKeyError < StandardError; end
class IllegalValueError < StandardError; end
Expand Down
2 changes: 1 addition & 1 deletion lib/iiif/presentation/abstract_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class AbstractResource < Service
# Every subclass should override the following five methods where
# appropriate, see Subclasses for how.
def required_keys
%w{ @type }
%w{ type }
end

def any_type_keys # these are allowed on all classes
Expand Down
4 changes: 2 additions & 2 deletions lib/iiif/presentation/annotation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def abstract_resource_only_keys
end

def initialize(hsh={})
hsh['@type'] = TYPE unless hsh.has_key? '@type'
hsh['motivation'] = 'sc:painting' unless hsh.has_key? 'motivation'
hsh['type'] = TYPE unless hsh.has_key? 'type'
hsh['motivation'] = 'painting' unless hsh.has_key? 'motivation'
super(hsh)
end

Expand Down
6 changes: 3 additions & 3 deletions lib/iiif/presentation/annotation_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ module IIIF
module Presentation
class AnnotationList < AbstractResource

TYPE = 'sc:AnnotationList'
TYPE = 'AnnotationList'

def required_keys
super + %w{ @id }
super + %w{ id }
end

def array_only_keys;
super + %w{ resources };
end

def initialize(hsh={})
hsh['@type'] = TYPE unless hsh.has_key? '@type'
hsh['type'] = TYPE unless hsh.has_key? 'type'
super(hsh)
end

Expand Down
6 changes: 3 additions & 3 deletions lib/iiif/presentation/canvas.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ class Canvas < AbstractResource

# TODO (?) a simple 'Image Canvas' constructor.

TYPE = 'sc:Canvas'
TYPE = 'Canvas'

def required_keys
super + %w{ @id width height label }
super + %w{ id width height label }
end

def any_type_keys
Expand All @@ -30,7 +30,7 @@ def legal_viewing_hint_values
end

def initialize(hsh={})
hsh['@type'] = TYPE unless hsh.has_key? '@type'
hsh['type'] = TYPE unless hsh.has_key? 'type'
super(hsh)
end

Expand Down
8 changes: 4 additions & 4 deletions lib/iiif/presentation/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ module IIIF
module Presentation
class Collection < AbstractResource

TYPE = 'sc:Collection'
TYPE = 'Collection'

def required_keys
super + %w{ @id label }
super + %w{ id label }
end

def array_only_keys
super + %w{ collections manifests }
end

def initialize(hsh={})
hsh['@type'] = TYPE unless hsh.has_key? '@type'
hsh['type'] = TYPE unless hsh.has_key? 'type'
super(hsh)
end

def validate
# each member of collections and manifests must be a Hash
# each member of collections and manifests MUST have @id, @type, and label
# each member of collections and manifests MUST have id, type, and label
end

end
Expand Down
14 changes: 7 additions & 7 deletions lib/iiif/presentation/image_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def int_only_keys
end

def initialize(hsh={})
hsh['@type'] = TYPE unless hsh.has_key? '@type'
hsh['type'] = TYPE unless hsh.has_key? 'type'
super(hsh)
end

Expand All @@ -22,7 +22,7 @@ class << self
IMAGE_API_CONTEXT = 'http://iiif.io/api/image/2/context.json'
DEFAULT_FORMAT = 'image/jpeg'
# Create a new ImageResource that includes a IIIF Image API Service
# See http://iiif.io/api/presentation/2.0/#image-resources
# See http://iiif.io/api/presentation/3.0/#image-resources
#
# Params
# * :service_id (required) - The base URI for the image on the image
Expand All @@ -48,12 +48,12 @@ class << self
# The result is something like this:
#
# {
# "@id":"http://www.example.org/iiif/book1/res/page1.jpg",
# "@type":"dctypes:Image",
# "id":"http://www.example.org/iiif/book1/res/page1.jpg",
# "type":"dctypes:Image",
# "format":"image/jpeg",
# "service": {
# "@context": "http://iiif.io/api/image/2/context.json",
# "@id":"http://www.example.org/images/book1-page1",
# "id":"http://www.example.org/images/book1-page1",
# "profile":"http://iiif.io/api/image/2/profiles/level2.json",
# },
# "height":2000,
Expand All @@ -76,7 +76,7 @@ def create_image_api_image_resource(params={})
remote_info = get_info(service_id) if !have_whp || copy_info

resource = self.new
resource['@id'] = resource_id
resource['id'] = resource_id
resource.format = format
resource.width = width.nil? ? remote_info['width'] : width
resource.height = height.nil? ? remote_info['height'] : height
Expand All @@ -85,7 +85,7 @@ def create_image_api_image_resource(params={})
resource.service.merge!(remote_info)
else
resource.service['@context'] = IMAGE_API_CONTEXT
resource.service['@id'] = service_id
resource.service['id'] = service_id
if profile.nil?
if remote_info['profile'].kind_of?(Array)
resource.service['profile'] = remote_info['profile'][0]
Expand Down
6 changes: 3 additions & 3 deletions lib/iiif/presentation/layer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ module IIIF
module Presentation
class Layer < AbstractResource

TYPE = 'sc:Layer'
TYPE = 'Layer'

def required_keys
super + %w{ @id label }
super + %w{ id label }
end

def array_only_keys
Expand All @@ -19,7 +19,7 @@ def string_only_keys
end

def initialize(hsh={})
hsh['@type'] = TYPE unless hsh.has_key? '@type'
hsh['type'] = TYPE unless hsh.has_key? 'type'
super(hsh)
end

Expand Down
Loading

0 comments on commit 8a08b1a

Please sign in to comment.