djatoka 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile.lock +9 -2
- data/README.rdoc +42 -3
- data/djatoka.gemspec +7 -5
- data/lib/djatoka/iiif_request.rb +163 -0
- data/lib/djatoka/metadata.rb +88 -3
- data/lib/djatoka/resolver.rb +10 -0
- data/lib/djatoka.rb +4 -0
- data/test/helper.rb +2 -1
- data/test/test_iiif_request.rb +173 -0
- data/test/test_metadata.rb +81 -6
- data/test/test_resolver.rb +20 -0
- metadata +53 -39
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 11aaf1e80818a81069ad5935756e293a80b87dbf
|
4
|
+
data.tar.gz: 850d1b033f7c80a5ac496e1fcfa3902ad1040bb8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac8a7b99068dd81346499e49302d00a5bced11ee481744f8a3698e8f4ff66f93adcffbe6e8a2fa741c1a4bc96e012e518088116f576f4232a4b808f39c996288
|
7
|
+
data.tar.gz: 3e0231731a8cbe8154d37895994a2e8b0d097eaae00a40866dd3bf15a5e64302bda280e16e7991e05a1de3e258e938c2d1b949a8fbff314589a21cb1e2444373
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
djatoka (0.2.
|
4
|
+
djatoka (0.2.2)
|
5
5
|
addressable
|
6
6
|
hashie
|
7
7
|
json
|
8
|
+
mime-types
|
8
9
|
trollop
|
9
10
|
|
10
11
|
GEM
|
@@ -13,10 +14,12 @@ GEM
|
|
13
14
|
activesupport (3.2.13)
|
14
15
|
i18n (= 0.6.1)
|
15
16
|
multi_json (~> 1.0)
|
16
|
-
addressable (2.3.
|
17
|
+
addressable (2.3.4)
|
17
18
|
bourne (1.4.0)
|
18
19
|
mocha (~> 0.13.2)
|
19
20
|
coderay (1.0.9)
|
21
|
+
equivalent-xml (0.3.0)
|
22
|
+
nokogiri (>= 1.4.3)
|
20
23
|
fakeweb (1.3.0)
|
21
24
|
ffi (1.6.0-java)
|
22
25
|
hashie (2.0.3)
|
@@ -25,9 +28,12 @@ GEM
|
|
25
28
|
json (1.7.7-java)
|
26
29
|
metaclass (0.0.1)
|
27
30
|
method_source (0.8.1)
|
31
|
+
mime-types (1.22)
|
28
32
|
mocha (0.13.3)
|
29
33
|
metaclass (~> 0.0.1)
|
30
34
|
multi_json (1.7.2)
|
35
|
+
nokogiri (1.5.9)
|
36
|
+
nokogiri (1.5.9-java)
|
31
37
|
pry (0.9.12)
|
32
38
|
coderay (~> 1.0.5)
|
33
39
|
method_source (~> 0.8)
|
@@ -59,6 +65,7 @@ PLATFORMS
|
|
59
65
|
|
60
66
|
DEPENDENCIES
|
61
67
|
djatoka!
|
68
|
+
equivalent-xml
|
62
69
|
fakeweb
|
63
70
|
mocha
|
64
71
|
pry
|
data/README.rdoc
CHANGED
@@ -90,6 +90,46 @@ Tested with the following Ruby versions:
|
|
90
90
|
- 1.8.7-p302
|
91
91
|
- 1.9.2-p0
|
92
92
|
|
93
|
+
== IIIF Support
|
94
|
+
This gem can translate parameters from {International Image Interoperability Framework (IIIF)}[http://www-sul.stanford.edu/iiif/image-api/] requests into Djatoka requests. It follows the same pattern as creating a Djatoka::Region
|
95
|
+
|
96
|
+
=== IIIF Example
|
97
|
+
# Create a resolver object with a URL.
|
98
|
+
resolver = Djatoka::Resolver.new('http://african.lanl.gov/adore-djatoka/resolver')
|
99
|
+
|
100
|
+
# A known good identifier
|
101
|
+
identifier = 'info:lanl-repo/ds/5aa182c2-c092-4596-af6e-e95d2e263de3'
|
102
|
+
|
103
|
+
# IIIF Image Request
|
104
|
+
# Create a IiifRequest with the resolver and id
|
105
|
+
iiif_request = Djatoka::IiifRequest.new(resolver, identifier)
|
106
|
+
|
107
|
+
# Set IIIF parameters and create a Djatoka::Region
|
108
|
+
# Note: All IIIF parameters are required before creating a Djatoka::Region
|
109
|
+
djatoka_region = iiif_request.region('full').size('full').rotation('0').quality('native').format('jpg').djatoka_region
|
110
|
+
|
111
|
+
# Use the Djatoka::Region as normal
|
112
|
+
djatoka_region.url
|
113
|
+
|
114
|
+
# IIIF Info Request (Metadata)
|
115
|
+
# First, create a Djatoka::Metadata object and perform the request to the server for metadata.
|
116
|
+
metadata = resolver.metadata(identifier).perform
|
117
|
+
|
118
|
+
# Create a IIIF Info json response string
|
119
|
+
# Set values to optional fields by passing in a block and calling setters on the yielded Mash
|
120
|
+
json = metadata.to_iiif_json do |info|
|
121
|
+
info.tile_width = 512
|
122
|
+
info.tile_height = 512
|
123
|
+
info.formats = ['jpg', 'png']
|
124
|
+
info.qualities = ['native', 'grey']
|
125
|
+
info.profile = 'http://library.stanford.edu/iiif/image-api/compliance.html#level1'
|
126
|
+
info.image_host = 'http://myserver.com/image'
|
127
|
+
end
|
128
|
+
|
129
|
+
# If you want the xml flavor of a IIIF Info response, use
|
130
|
+
xml = metadata.to_iiif_xml
|
131
|
+
# It can be called with the same type of block as #to_iiif_json
|
132
|
+
|
93
133
|
== Commandline
|
94
134
|
|
95
135
|
There is also a little commandline utility for outputting the OpenURL.
|
@@ -102,14 +142,13 @@ There is also a little commandline utility for outputting the OpenURL.
|
|
102
142
|
|
103
143
|
$ djatoka_url --help
|
104
144
|
|
105
|
-
|
106
145
|
== TODO
|
107
146
|
- Testing the view helpers.
|
108
147
|
- View more examples of images where the dwtLevels differ from the Djatoka levels.
|
109
148
|
|
110
|
-
==
|
149
|
+
== Authors
|
111
150
|
|
112
|
-
Jason Ronallo
|
151
|
+
Jason Ronallo, Willy Mene
|
113
152
|
|
114
153
|
== Copyright
|
115
154
|
|
data/djatoka.gemspec
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{djatoka}
|
8
|
-
s.version = "0.2.
|
9
|
-
s.authors = ["Jason Ronallo"]
|
10
|
-
s.email = %q{jronallo@gmail.com}
|
8
|
+
s.version = "0.2.3"
|
9
|
+
s.authors = ["Jason Ronallo", "Willy Mene"]
|
10
|
+
s.email = %q{jronallo@gmail.com wmene@stanford.edu}
|
11
11
|
s.homepage = %q{http://github.com/jronallo/djatoka}
|
12
12
|
s.summary = %q{Djatoka image server helpers for Ruby and Rails.}
|
13
13
|
s.description = %q{The djatoka library provides some simple methods for creation of the OpenURLs needed to communicate with the Djatoka image server.}
|
@@ -16,15 +16,17 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
|
-
|
19
|
+
|
20
20
|
s.add_dependency "addressable"
|
21
21
|
s.add_dependency "hashie"
|
22
22
|
s.add_dependency "json"
|
23
|
-
|
23
|
+
s.add_dependency "mime-types"
|
24
|
+
|
24
25
|
s.add_dependency "trollop"
|
25
26
|
|
26
27
|
s.add_development_dependency "mocha"
|
27
28
|
s.add_development_dependency "fakeweb"
|
28
29
|
s.add_development_dependency "shoulda"
|
30
|
+
s.add_development_dependency "equivalent-xml"
|
29
31
|
end
|
30
32
|
|
@@ -0,0 +1,163 @@
|
|
1
|
+
|
2
|
+
# For rotation param testing. Allows you to do the following with String:
|
3
|
+
# '123.456'.numeric? => true
|
4
|
+
class String
|
5
|
+
def numeric?
|
6
|
+
return true if self =~ /^\d+$/
|
7
|
+
true if Float(self) rescue false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Djatoka
|
12
|
+
|
13
|
+
class IiifException < Exception; end
|
14
|
+
|
15
|
+
class IiifInvalidParam < IiifException
|
16
|
+
|
17
|
+
def initialize (param_name, value=nil)
|
18
|
+
@param_name = param_name
|
19
|
+
@value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#{@param_name.capitalize} is invalid: " << @value.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# A class for translating IIIF parameters into a Djatoka::Region object.
|
29
|
+
#
|
30
|
+
# * See the {documentation for the IIIF API}[http://www-sul.stanford.edu/iiif/image-api/#size]
|
31
|
+
#
|
32
|
+
# It behaves like the Djatoka::Region object, in that you can chain methods together when setting params.
|
33
|
+
# Once you've set all the params, call #djatoka_region to translate the parameters. Validation of
|
34
|
+
# values occurs in this method, and a Djatoka::IiifInvalidParam exception is raised if any of the values
|
35
|
+
# are invalid.
|
36
|
+
#
|
37
|
+
# * Usage example
|
38
|
+
# resolver = Djatoka::Resolver.new('http://server.edu/adore-djatoka/resolver')
|
39
|
+
# id = 'someImageId1234'
|
40
|
+
#
|
41
|
+
# request = Djatoka::IiifRequest.new(resolver, id)
|
42
|
+
# djatoka_region = request.region('full').size('full').rotation('0').quality('native').format('jpg').djatoka_region
|
43
|
+
class IiifRequest
|
44
|
+
|
45
|
+
ALL_PARAMS = Set.new(['region', 'size', 'rotation', 'quality', 'format'])
|
46
|
+
|
47
|
+
attr_accessor :iiif_params
|
48
|
+
|
49
|
+
# You can set the params for the request in 2 ways:
|
50
|
+
# 1. Pass in params as a hash with the IIIF parameters {:id => 'some/id', :region => 'full'...}
|
51
|
+
# 2. Do not pass in params, and use the chain methods to set each value
|
52
|
+
def initialize(resolver, id, params = nil)
|
53
|
+
@id = id
|
54
|
+
@resolver = resolver
|
55
|
+
@iiif_params = Hashie::Mash.new
|
56
|
+
|
57
|
+
if(!params.nil? && params.is_a?(Hash))
|
58
|
+
params.keys.each do |k|
|
59
|
+
self.send("#{k}", params[k])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
# else, params is nil, the caller will set each value
|
63
|
+
end
|
64
|
+
|
65
|
+
def id(v)
|
66
|
+
@iiif_params[:id] = v
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def region(v)
|
71
|
+
@iiif_params[:region] = v
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def size(v)
|
76
|
+
@iiif_params[:size] = v
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def rotation(v)
|
81
|
+
@iiif_params[:rotation] = v
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def quality(v)
|
86
|
+
@iiif_params[:quality] = v
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def format(v)
|
91
|
+
@iiif_params[:format] = v
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def all_params_present?
|
96
|
+
names = Set.new(@iiif_params.keys)
|
97
|
+
names == ALL_PARAMS
|
98
|
+
end
|
99
|
+
|
100
|
+
def djatoka_region
|
101
|
+
unless(all_params_present?)
|
102
|
+
current = Set.new(@iiif_params.keys)
|
103
|
+
missing = (ALL_PARAMS - current).to_a
|
104
|
+
msg = "Invalid IIIF request. The following params are missing: " << missing.join(',')
|
105
|
+
raise IiifException.new(msg)
|
106
|
+
end
|
107
|
+
|
108
|
+
region = @resolver.region(@iiif_params[:id])
|
109
|
+
|
110
|
+
if(@iiif_params[:region] =~ /^(\d+),(\d+),(\d+),(\d+)$/)
|
111
|
+
region.region("#{$2},#{$1},#{$4},#{$3}")
|
112
|
+
elsif(!(@iiif_params[:region] =~ /^full$/i))
|
113
|
+
raise IiifInvalidParam.new "region", @iiif_params[:region]
|
114
|
+
end
|
115
|
+
|
116
|
+
s = @iiif_params[:size]
|
117
|
+
case s
|
118
|
+
when /^full$/i
|
119
|
+
s #noop
|
120
|
+
when /^(\d+),$/
|
121
|
+
region.scale( ["#{$1}", "0"] ) #w => w,0
|
122
|
+
when /^,(\d+)$/
|
123
|
+
region.scale( ["0", "#{$1}"] ) #h => 0,h
|
124
|
+
when /^pct:(\d+)$/i
|
125
|
+
dj_scale = $1.to_f / 100.0
|
126
|
+
region.scale(dj_scale.to_s)
|
127
|
+
when /^(\d+),(\d+)$/
|
128
|
+
region.scale("#{$1},#{$2}")
|
129
|
+
# TODO Best Fit: when /^!(\d+),(\d+)$/
|
130
|
+
else
|
131
|
+
raise IiifInvalidParam.new "size", s
|
132
|
+
end
|
133
|
+
|
134
|
+
unless(@iiif_params[:rotation].numeric?)
|
135
|
+
raise IiifInvalidParam.new "rotation", @iiif_params[:rotation]
|
136
|
+
end
|
137
|
+
region.rotate(@iiif_params[:rotation])
|
138
|
+
|
139
|
+
f = @iiif_params[:format]
|
140
|
+
if(f)
|
141
|
+
if(f =~ /\//)
|
142
|
+
type = MIME::Types[f].first
|
143
|
+
else
|
144
|
+
type = MIME::Types.type_for(@iiif_params[:format]).first
|
145
|
+
end
|
146
|
+
raise IiifInvalidParam.new("format", f) if(type.nil?)
|
147
|
+
else
|
148
|
+
#default to jpg or let djatoka determine default
|
149
|
+
type = MIME::Types.type_for('jpg').first
|
150
|
+
end
|
151
|
+
region.format(type.to_s)
|
152
|
+
|
153
|
+
unless(@iiif_params[:quality] =~ /^(native|color|grey|bitonal)$/i)
|
154
|
+
raise IiifInvalidParam.new 'quality', @iiif_params[:quality]
|
155
|
+
end
|
156
|
+
|
157
|
+
region
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
data/lib/djatoka/metadata.rb
CHANGED
@@ -56,7 +56,7 @@ class Djatoka::Metadata
|
|
56
56
|
def all_levels
|
57
57
|
perform if !response # if we haven't already performed the metadata query do it now
|
58
58
|
levels_hash = Hashie::Mash.new
|
59
|
-
levels_i = levels.to_i
|
59
|
+
levels_i = levels.to_i
|
60
60
|
(0..levels_i).each do |level_num|
|
61
61
|
level_height = height.to_i
|
62
62
|
level_width = width.to_i
|
@@ -71,9 +71,94 @@ class Djatoka::Metadata
|
|
71
71
|
end
|
72
72
|
levels_hash
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
def max_level
|
76
|
-
all_levels.keys.sort.last
|
76
|
+
all_levels.keys.sort.last
|
77
|
+
end
|
78
|
+
|
79
|
+
# Builds a String containing the JSON response to a IIIF Image Information Request
|
80
|
+
#
|
81
|
+
# * {Documentation about the Image Info Request}[http://www-sul.stanford.edu/iiif/image-api/#info]
|
82
|
+
#
|
83
|
+
# It will fill in the required fields of identifier, width, and height. It will also fill in
|
84
|
+
# the scale_factors as determined from Djatoka::Metadata#levels
|
85
|
+
# The method yields a Mash where you can set the value of the optional fields. Here's an example:
|
86
|
+
#
|
87
|
+
# metadata.to_iiif_xml do |info|
|
88
|
+
# info.tile_width = 512
|
89
|
+
# info.tile_height = 512
|
90
|
+
# info.formats = ['jpg', 'png']
|
91
|
+
# info.qualities = ['native', 'grey']
|
92
|
+
# info.profile = 'http://library.stanford.edu/iiif/image-api/compliance.html#level1'
|
93
|
+
# info.image_host = 'http://myserver.com/image'
|
94
|
+
# end
|
95
|
+
def to_iiif_json(&block)
|
96
|
+
info = Hashie::Mash.new
|
97
|
+
info.identifier = @rft_id
|
98
|
+
info.width = @width.to_i
|
99
|
+
info.height = @height.to_i
|
100
|
+
info.scale_factors = levels_as_i
|
101
|
+
# optional fields map directly to json from the Mash
|
102
|
+
yield(info)
|
103
|
+
|
104
|
+
# convert strings to ints for tile width and height
|
105
|
+
info.tile_width = info.tile_width.to_i if(info.tile_width?)
|
106
|
+
info.tile_height = info.tile_height.to_i if(info.tile_height?)
|
107
|
+
JSON.pretty_generate(info)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Builds a String containing the xml response to a IIIF Image Information Request
|
111
|
+
#
|
112
|
+
# * {Documentation about the Image Info Request}[http://www-sul.stanford.edu/iiif/image-api/#info]
|
113
|
+
#
|
114
|
+
# It will fill in the required fields of identifier, width, and height. It will also fill in
|
115
|
+
# the scale_factors as determined from Djatoka::Metadata#levels
|
116
|
+
# The method yields a Mash where you can set the values of the optional fields. Here's an example:
|
117
|
+
#
|
118
|
+
# metadata.to_iiif_json do |info|
|
119
|
+
# info.tile_width = 512
|
120
|
+
# info.tile_height = 512
|
121
|
+
# info.formats = ['jpg', 'png']
|
122
|
+
# info.qualities = ['native', 'grey']
|
123
|
+
# info.profile = 'http://library.stanford.edu/iiif/image-api/compliance.html#level1'
|
124
|
+
# info.image_host = 'http://myserver.com/image'
|
125
|
+
# end
|
126
|
+
def to_iiif_xml(&block)
|
127
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
128
|
+
info = Hashie::Mash.new
|
129
|
+
yield(info)
|
130
|
+
xml.info('xmlns' => 'http://library.stanford.edu/iiif/image-api/ns/') {
|
131
|
+
xml.identifier @rft_id
|
132
|
+
xml.width @width
|
133
|
+
xml.height @height
|
134
|
+
xml.scale_factors {
|
135
|
+
levels_as_i.each {|lvl| xml.scale_factor lvl}
|
136
|
+
}
|
137
|
+
|
138
|
+
#optional fields
|
139
|
+
xml.tile_width info.tile_width if(info.tile_width?)
|
140
|
+
xml.tile_height info.tile_height if(info.tile_height?)
|
141
|
+
if(info.formats?)
|
142
|
+
xml.formats {
|
143
|
+
info.formats.each {|mt| xml.format mt}
|
144
|
+
}
|
145
|
+
end
|
146
|
+
if(info.qualities?)
|
147
|
+
xml.qualities {
|
148
|
+
info.qualities.each {|q| xml.quality q}
|
149
|
+
}
|
150
|
+
end
|
151
|
+
xml.profile info.profile if(info.profile?)
|
152
|
+
xml.image_host info.image_host if(info.image_host?)
|
153
|
+
}
|
154
|
+
end
|
155
|
+
builder.to_xml
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
# Just the levels themselves, as a sorted array of integers
|
160
|
+
def levels_as_i
|
161
|
+
all_levels.keys.map{ |l| l.to_i}.sort
|
77
162
|
end
|
78
163
|
|
79
164
|
end
|
data/lib/djatoka/resolver.rb
CHANGED
@@ -63,6 +63,16 @@ class Djatoka::Resolver
|
|
63
63
|
Djatoka::Region.new(self, rft_id, params).uri
|
64
64
|
end
|
65
65
|
|
66
|
+
# Shortcut for creating a Djatoka::Region from a Hash of Iiif parameters.
|
67
|
+
def iiif_region(rft_id, params={})
|
68
|
+
Djatoka::IiifRequest.new(self, rft_id, params).djatoka_region
|
69
|
+
end
|
70
|
+
|
71
|
+
# Shortcut for creating an Addressable::URI from a Hash of Iiif parameters.
|
72
|
+
def iiif_uri(rft_id, params={})
|
73
|
+
Djatoka::IiifRequest.new(self, rft_id, params).djatoka_region.uri
|
74
|
+
end
|
75
|
+
|
66
76
|
def base_uri_params
|
67
77
|
params = {:host => host, :path => path, :scheme => scheme}
|
68
78
|
params[:port] = port if port
|
data/lib/djatoka.rb
CHANGED
@@ -52,17 +52,21 @@ end
|
|
52
52
|
require 'net/http'
|
53
53
|
require 'net/https'
|
54
54
|
require 'uri'
|
55
|
+
require 'set'
|
55
56
|
|
56
57
|
require 'djatoka/net'
|
57
58
|
require 'djatoka/resolver'
|
58
59
|
require 'djatoka/metadata'
|
59
60
|
require 'djatoka/common'
|
60
61
|
require 'djatoka/region'
|
62
|
+
require 'djatoka/iiif_request'
|
61
63
|
|
62
64
|
require 'addressable/uri'
|
63
65
|
require 'addressable/template'
|
64
66
|
require 'json'
|
67
|
+
require 'nokogiri'
|
65
68
|
require 'hashie'
|
69
|
+
require 'mime/types'
|
66
70
|
|
67
71
|
|
68
72
|
if defined? Rails
|
data/test/helper.rb
CHANGED
@@ -3,6 +3,7 @@ require 'test/unit'
|
|
3
3
|
require 'shoulda'
|
4
4
|
require 'fakeweb'
|
5
5
|
require 'pry'
|
6
|
+
require 'equivalent-xml'
|
6
7
|
|
7
8
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
9
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
@@ -39,7 +40,7 @@ end
|
|
39
40
|
|
40
41
|
FakeWeb.register_uri(:get, "http://african.lanl.gov/adore-djatoka/resolver?rft_id=ua023_015-006-bx0003-014-075&svc_id=info%3Alanl-repo%2Fsvc%2FgetMetadata&url_ver=Z39.88-2004",
|
41
42
|
:response => File.read('test/fixtures/ua023_015-006-bx0003-014-075-metadata.json'))
|
42
|
-
|
43
|
+
|
43
44
|
FakeWeb.register_uri(:get, "http://african.lanl.gov/adore-djatoka/resolver?rft_id=asdf&svc_id=info%3Alanl-repo%2Fsvc%2FgetMetadata&url_ver=Z39.88-2004",
|
44
45
|
:response => File.read('test/fixtures/empty-metadata.json'))
|
45
46
|
FakeWeb.register_uri(:get, "http://african.lanl.gov/adore-djatoka/resolver?url_ver=Z39.88-2004&svc_id=info%3Alanl-repo%2Fsvc%2Fping&rft_id=asdf",
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestDjatokaIiifRequest < Test::Unit::TestCase
|
4
|
+
with_a_resolver do
|
5
|
+
context 'creates Djatoka::Region objects' do
|
6
|
+
setup do
|
7
|
+
@req = Djatoka::IiifRequest.new(@resolver, @identifier)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'from a IIIF request with all defaults' do
|
11
|
+
setup do
|
12
|
+
@region = @req.region('full').size('full').rotation('0').quality('native').format('jpg').djatoka_region
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'set region to nil from full' do
|
16
|
+
assert_nil @region.query.region
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'set size to nil from full' do
|
20
|
+
assert_nil @region.query.scale
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'set rotation to 0' do
|
24
|
+
assert_equal '0', @region.query.rotate
|
25
|
+
end
|
26
|
+
|
27
|
+
should 'set format to image/jpg' do
|
28
|
+
assert_equal 'image/jpeg', @region.query.format
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'translates region parameters' do
|
34
|
+
setup do
|
35
|
+
@req.size('full').rotation('0').quality('native').format('jpg')
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'set x,y,w,h requests' do
|
39
|
+
reg = @req.region('10,20,50,100').djatoka_region
|
40
|
+
assert_equal '20,10,100,50', reg.query.region
|
41
|
+
end
|
42
|
+
|
43
|
+
should 'raise an exception if the region does not fit the x,y,w,h format, or is not "full"' do
|
44
|
+
assert_raise Djatoka::IiifInvalidParam do
|
45
|
+
@req.region('blah').djatoka_region
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'translates size parameters' do
|
51
|
+
setup do
|
52
|
+
@req.region('10,20,50,100').rotation('0').quality('native').format('jpg')
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'set "w," requests to the correct scale value' do
|
56
|
+
reg = @req.size('800,').djatoka_region
|
57
|
+
assert_equal '800,0', reg.query.scale
|
58
|
+
end
|
59
|
+
|
60
|
+
should 'set ",h" requests to the correct scale value' do
|
61
|
+
reg = @req.size(',900').djatoka_region
|
62
|
+
assert_equal '0,900', reg.query.scale
|
63
|
+
end
|
64
|
+
|
65
|
+
should 'set "pct:n" requests to the correct scale value' do
|
66
|
+
reg = @req.size('pct:75').djatoka_region
|
67
|
+
assert_equal '0.75', reg.query.scale
|
68
|
+
|
69
|
+
reg = @req.size('pct:125').djatoka_region
|
70
|
+
assert_equal '1.25', reg.query.scale
|
71
|
+
end
|
72
|
+
|
73
|
+
should 'set "w,h" requests to the correct scale value' do
|
74
|
+
reg = @req.size('1024,768').djatoka_region
|
75
|
+
assert_equal '1024,768', reg.query.scale
|
76
|
+
end
|
77
|
+
|
78
|
+
should 'raise an exception if the value cannot be parsed into a Float' do
|
79
|
+
assert_raise Djatoka::IiifInvalidParam do
|
80
|
+
@req.size('pct:0.75').djatoka_region
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'translates rotation parameters' do
|
87
|
+
setup do
|
88
|
+
@req.region('10,20,50,100').size('800,').quality('native').format('jpg')
|
89
|
+
end
|
90
|
+
|
91
|
+
should 'set values that are numeric' do
|
92
|
+
reg = @req.rotation('90').djatoka_region
|
93
|
+
assert_equal '90', reg.query.rotate
|
94
|
+
|
95
|
+
reg = @req.rotation('270').djatoka_region
|
96
|
+
assert_equal '270', reg.query.rotate
|
97
|
+
end
|
98
|
+
|
99
|
+
should 'raise an exception if the value is not numeric' do
|
100
|
+
assert_raise Djatoka::IiifInvalidParam do
|
101
|
+
@req.rotation('blah').djatoka_region
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'translates format parameters' do
|
107
|
+
setup do
|
108
|
+
@req.region('full').size('full').rotation('0').quality('native')
|
109
|
+
end
|
110
|
+
|
111
|
+
should 'set the format from a valid extension as from the end of a URL' do
|
112
|
+
reg = @req.format('image.png').djatoka_region
|
113
|
+
assert_equal 'image/png', reg.query.format
|
114
|
+
end
|
115
|
+
|
116
|
+
should 'set the format from a valid mime-type as from the "Accept:" HTTP header' do
|
117
|
+
reg = @req.format('image/tiff').djatoka_region
|
118
|
+
assert_equal 'image/tiff', reg.query.format
|
119
|
+
end
|
120
|
+
|
121
|
+
should 'raise an exception if the value is not a valid mime type extension' do
|
122
|
+
assert_raise Djatoka::IiifInvalidParam do
|
123
|
+
@req.format('nobody').djatoka_region
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
should 'raise an exception if the value is not a valid mime type value' do
|
128
|
+
assert_raise Djatoka::IiifInvalidParam do
|
129
|
+
@req.format('image/blahtype').djatoka_region
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'validates quaility parameters' do
|
135
|
+
setup do
|
136
|
+
@req.region('full').size('full').rotation('0').format('jpg')
|
137
|
+
end
|
138
|
+
|
139
|
+
should 'not raise when the quality is valid' do
|
140
|
+
assert_nothing_raised do
|
141
|
+
@req.quality('color').djatoka_region
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
should 'raise an exception when the quality is invalid' do
|
146
|
+
assert_raise Djatoka::IiifInvalidParam do
|
147
|
+
@req.quality('3d').djatoka_region
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context '#all_params_present?' do
|
153
|
+
should 'return true when all the valid params have been set' do
|
154
|
+
@req.region('full').size('full').rotation('0').quality('native').format('jpg')
|
155
|
+
assert @req.all_params_present?
|
156
|
+
end
|
157
|
+
|
158
|
+
should 'return false when params are missing' do
|
159
|
+
@req.region('full').size('full').quality('native').format('jpg')
|
160
|
+
assert_equal false, @req.all_params_present?
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context '#djatoka_region' do
|
165
|
+
should 'raise a IiifException if a required param is missing from the request' do
|
166
|
+
assert_raise Djatoka::IiifException do
|
167
|
+
@req.size('800,').djatoka_region
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end #context
|
172
|
+
end #with_a_resolver
|
173
|
+
end
|
data/test/test_metadata.rb
CHANGED
@@ -60,14 +60,14 @@ class TestDjatokaMetadata < Test::Unit::TestCase
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
context 'using net::https' do
|
64
|
-
should 'get metadata when using an https URI' do
|
63
|
+
context 'using net::https' do
|
64
|
+
should 'get metadata when using an https URI' do
|
65
65
|
Djatoka.use_curb=false
|
66
66
|
resolver = Djatoka::Resolver.new('https://scrc.lib.ncsu.edu/adore-djatoka/resolver')
|
67
67
|
metadata_obj = resolver.metadata('0004817')
|
68
68
|
metadata = metadata_obj.perform
|
69
69
|
assert_equal('10669', metadata.height)
|
70
|
-
end
|
70
|
+
end
|
71
71
|
end
|
72
72
|
|
73
73
|
context 'determining all the levels for a particular metadata response' do
|
@@ -92,7 +92,7 @@ class TestDjatokaMetadata < Test::Unit::TestCase
|
|
92
92
|
should 'know which is the max level' do
|
93
93
|
assert_equal "6", @metadata.max_level
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
should 'return appropriate height and width for all levels when levels and dwt_levels do not match' do
|
97
97
|
levels = {"0"=>{"height"=>58, "width"=>37},
|
98
98
|
"1"=>{"height"=>115, "width"=>74},
|
@@ -104,10 +104,85 @@ class TestDjatokaMetadata < Test::Unit::TestCase
|
|
104
104
|
assert_equal levels, returned_levels
|
105
105
|
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
end # levels
|
109
109
|
|
110
|
+
context 'IIIF Image Information Requests' do
|
111
|
+
setup do
|
112
|
+
@metadata_obj = @resolver.metadata(@identifier)
|
113
|
+
@metadata = @metadata_obj.perform
|
114
|
+
end
|
115
|
+
|
116
|
+
should 'create json responses' do
|
117
|
+
iiif_json = <<-EOF
|
118
|
+
{
|
119
|
+
"identifier": "info:lanl-repo/ds/5aa182c2-c092-4596-af6e-e95d2e263de3",
|
120
|
+
"width": 5120,
|
121
|
+
"height": 3372,
|
122
|
+
"scale_factors": [ 0,1,2,3,4,5,6 ],
|
123
|
+
"tile_width": 512,
|
124
|
+
"tile_height": 512,
|
125
|
+
"formats": [ "jpg", "png" ],
|
126
|
+
"qualities": [ "native", "grey" ],
|
127
|
+
"profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1",
|
128
|
+
"image_host": "http://myserver.com/image"
|
129
|
+
}
|
130
|
+
EOF
|
131
|
+
expected = JSON.parse(iiif_json)
|
132
|
+
|
133
|
+
str = @metadata.to_iiif_json do |info|
|
134
|
+
info.tile_width = '512'
|
135
|
+
info.tile_height = 512 # tile_* can be string or int
|
136
|
+
info.formats = ['jpg', 'png']
|
137
|
+
info.qualities = ['native', 'grey']
|
138
|
+
info.profile = 'http://library.stanford.edu/iiif/image-api/compliance.html#level1'
|
139
|
+
info.image_host = 'http://myserver.com/image'
|
140
|
+
end
|
141
|
+
assert_equal expected, JSON.parse(str)
|
142
|
+
end
|
143
|
+
|
144
|
+
should 'create xml responses' do
|
145
|
+
iiif_xml =<<-EOXML
|
146
|
+
<info xmlns="http://library.stanford.edu/iiif/image-api/ns/">
|
147
|
+
<identifier>info:lanl-repo/ds/5aa182c2-c092-4596-af6e-e95d2e263de3</identifier>
|
148
|
+
<width>5120</width>
|
149
|
+
<height>3372</height>
|
150
|
+
<scale_factors>
|
151
|
+
<scale_factor>0</scale_factor>
|
152
|
+
<scale_factor>1</scale_factor>
|
153
|
+
<scale_factor>2</scale_factor>
|
154
|
+
<scale_factor>3</scale_factor>
|
155
|
+
<scale_factor>4</scale_factor>
|
156
|
+
<scale_factor>5</scale_factor>
|
157
|
+
<scale_factor>6</scale_factor>
|
158
|
+
</scale_factors>
|
159
|
+
<tile_width>512</tile_width>
|
160
|
+
<tile_height>512</tile_height>
|
161
|
+
<formats>
|
162
|
+
<format>jpg</format>
|
163
|
+
<format>png</format>
|
164
|
+
</formats>
|
165
|
+
<qualities>
|
166
|
+
<quality>native</quality>
|
167
|
+
<quality>grey</quality>
|
168
|
+
</qualities>
|
169
|
+
<profile>http://library.stanford.edu/iiif/image-api/compliance.html#level1</profile>
|
170
|
+
<image_host>http://myserver.com/image</image_host>
|
171
|
+
</info>
|
172
|
+
EOXML
|
173
|
+
|
174
|
+
str = @metadata.to_iiif_xml do |info|
|
175
|
+
info.tile_width = '512'
|
176
|
+
info.tile_height = 512 # tile_* can be string or int
|
177
|
+
info.formats = ['jpg', 'png']
|
178
|
+
info.qualities = ['native', 'grey']
|
179
|
+
info.profile = 'http://library.stanford.edu/iiif/image-api/compliance.html#level1'
|
180
|
+
info.image_host = 'http://myserver.com/image'
|
181
|
+
end
|
182
|
+
assert EquivalentXml.equivalent?(str, iiif_xml)
|
183
|
+
end
|
184
|
+
end #IIIF Info Responses
|
185
|
+
|
110
186
|
end #with_a_resolver
|
111
187
|
|
112
188
|
end
|
113
|
-
|
data/test/test_resolver.rb
CHANGED
@@ -58,6 +58,26 @@ class TestDjatokaResolver < Test::Unit::TestCase
|
|
58
58
|
assert @resolver.region_uri(@identifier).is_a? Addressable::URI
|
59
59
|
end
|
60
60
|
|
61
|
+
context 'Iiif Requests' do
|
62
|
+
setup do
|
63
|
+
@iiif = {
|
64
|
+
:region => 'full',
|
65
|
+
:size => 'full',
|
66
|
+
:rotation => '0',
|
67
|
+
:quality => 'native',
|
68
|
+
:format => 'jpg'
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
should 'create a region from a hash of IIIF parameters' do
|
73
|
+
assert @resolver.iiif_region(@identifier, @iiif).is_a? Djatoka::Region
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'create a region uri from a hash of IIIF parameters' do
|
77
|
+
assert @resolver.iiif_uri(@identifier, @iiif).is_a? Addressable::URI
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
61
81
|
end #context a Djatoka::Resolver
|
62
82
|
|
63
83
|
context 'a resolver with a port number' do
|
metadata
CHANGED
@@ -1,131 +1,145 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: djatoka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.3
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jason Ronallo
|
8
|
+
- Willy Mene
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: addressable
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
17
|
requirements:
|
19
|
-
- -
|
18
|
+
- - '>='
|
20
19
|
- !ruby/object:Gem::Version
|
21
20
|
version: '0'
|
22
21
|
type: :runtime
|
23
22
|
prerelease: false
|
24
23
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
24
|
requirements:
|
27
|
-
- -
|
25
|
+
- - '>='
|
28
26
|
- !ruby/object:Gem::Version
|
29
27
|
version: '0'
|
30
28
|
- !ruby/object:Gem::Dependency
|
31
29
|
name: hashie
|
32
30
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
31
|
requirements:
|
35
|
-
- -
|
32
|
+
- - '>='
|
36
33
|
- !ruby/object:Gem::Version
|
37
34
|
version: '0'
|
38
35
|
type: :runtime
|
39
36
|
prerelease: false
|
40
37
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
38
|
requirements:
|
43
|
-
- -
|
39
|
+
- - '>='
|
44
40
|
- !ruby/object:Gem::Version
|
45
41
|
version: '0'
|
46
42
|
- !ruby/object:Gem::Dependency
|
47
43
|
name: json
|
48
44
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
45
|
requirements:
|
51
|
-
- -
|
46
|
+
- - '>='
|
52
47
|
- !ruby/object:Gem::Version
|
53
48
|
version: '0'
|
54
49
|
type: :runtime
|
55
50
|
prerelease: false
|
56
51
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
52
|
requirements:
|
59
|
-
- -
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: mime-types
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
60
68
|
- !ruby/object:Gem::Version
|
61
69
|
version: '0'
|
62
70
|
- !ruby/object:Gem::Dependency
|
63
71
|
name: trollop
|
64
72
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
73
|
requirements:
|
67
|
-
- -
|
74
|
+
- - '>='
|
68
75
|
- !ruby/object:Gem::Version
|
69
76
|
version: '0'
|
70
77
|
type: :runtime
|
71
78
|
prerelease: false
|
72
79
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
80
|
requirements:
|
75
|
-
- -
|
81
|
+
- - '>='
|
76
82
|
- !ruby/object:Gem::Version
|
77
83
|
version: '0'
|
78
84
|
- !ruby/object:Gem::Dependency
|
79
85
|
name: mocha
|
80
86
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
87
|
requirements:
|
83
|
-
- -
|
88
|
+
- - '>='
|
84
89
|
- !ruby/object:Gem::Version
|
85
90
|
version: '0'
|
86
91
|
type: :development
|
87
92
|
prerelease: false
|
88
93
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
94
|
requirements:
|
91
|
-
- -
|
95
|
+
- - '>='
|
92
96
|
- !ruby/object:Gem::Version
|
93
97
|
version: '0'
|
94
98
|
- !ruby/object:Gem::Dependency
|
95
99
|
name: fakeweb
|
96
100
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
101
|
requirements:
|
99
|
-
- -
|
102
|
+
- - '>='
|
100
103
|
- !ruby/object:Gem::Version
|
101
104
|
version: '0'
|
102
105
|
type: :development
|
103
106
|
prerelease: false
|
104
107
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
108
|
requirements:
|
107
|
-
- -
|
109
|
+
- - '>='
|
108
110
|
- !ruby/object:Gem::Version
|
109
111
|
version: '0'
|
110
112
|
- !ruby/object:Gem::Dependency
|
111
113
|
name: shoulda
|
112
114
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
115
|
requirements:
|
115
|
-
- -
|
116
|
+
- - '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: equivalent-xml
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - '>='
|
116
131
|
- !ruby/object:Gem::Version
|
117
132
|
version: '0'
|
118
133
|
type: :development
|
119
134
|
prerelease: false
|
120
135
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
136
|
requirements:
|
123
|
-
- -
|
137
|
+
- - '>='
|
124
138
|
- !ruby/object:Gem::Version
|
125
139
|
version: '0'
|
126
140
|
description: The djatoka library provides some simple methods for creation of the
|
127
141
|
OpenURLs needed to communicate with the Djatoka image server.
|
128
|
-
email: jronallo@gmail.com
|
142
|
+
email: jronallo@gmail.com wmene@stanford.edu
|
129
143
|
executables:
|
130
144
|
- djatoka_url
|
131
145
|
extensions: []
|
@@ -158,6 +172,7 @@ files:
|
|
158
172
|
- init.rb
|
159
173
|
- lib/djatoka.rb
|
160
174
|
- lib/djatoka/common.rb
|
175
|
+
- lib/djatoka/iiif_request.rb
|
161
176
|
- lib/djatoka/metadata.rb
|
162
177
|
- lib/djatoka/net.rb
|
163
178
|
- lib/djatoka/region.rb
|
@@ -173,6 +188,7 @@ files:
|
|
173
188
|
- test/helper.rb
|
174
189
|
- test/test_common.rb
|
175
190
|
- test/test_djatoka.rb
|
191
|
+
- test/test_iiif_request.rb
|
176
192
|
- test/test_metadata.rb
|
177
193
|
- test/test_region.rb
|
178
194
|
- test/test_resolver.rb
|
@@ -180,27 +196,25 @@ files:
|
|
180
196
|
- watchr.rb
|
181
197
|
homepage: http://github.com/jronallo/djatoka
|
182
198
|
licenses: []
|
199
|
+
metadata: {}
|
183
200
|
post_install_message:
|
184
201
|
rdoc_options: []
|
185
202
|
require_paths:
|
186
203
|
- lib
|
187
204
|
required_ruby_version: !ruby/object:Gem::Requirement
|
188
|
-
none: false
|
189
205
|
requirements:
|
190
|
-
- -
|
206
|
+
- - '>='
|
191
207
|
- !ruby/object:Gem::Version
|
192
208
|
version: '0'
|
193
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
|
-
none: false
|
195
210
|
requirements:
|
196
|
-
- -
|
211
|
+
- - '>='
|
197
212
|
- !ruby/object:Gem::Version
|
198
213
|
version: '0'
|
199
214
|
requirements: []
|
200
215
|
rubyforge_project: djatoka
|
201
|
-
rubygems_version:
|
216
|
+
rubygems_version: 2.0.0.rc.2
|
202
217
|
signing_key:
|
203
|
-
specification_version:
|
218
|
+
specification_version: 4
|
204
219
|
summary: Djatoka image server helpers for Ruby and Rails.
|
205
220
|
test_files: []
|
206
|
-
has_rdoc:
|