iiif-presentation 1.4.1 → 1.5.0

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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +8 -1
  3. data/.standard.yml +1 -0
  4. data/Gemfile +5 -5
  5. data/README.md +1 -1
  6. data/Rakefile +4 -2
  7. data/VERSION +1 -1
  8. data/bin/standardrb +16 -0
  9. data/iiif-presentation.gemspec +23 -23
  10. data/lib/iiif/hash_behaviours.rb +29 -33
  11. data/lib/iiif/ordered_hash.rb +33 -33
  12. data/lib/iiif/presentation/abstract_resource.rb +17 -22
  13. data/lib/iiif/presentation/annotation.rb +9 -11
  14. data/lib/iiif/presentation/annotation_list.rb +8 -10
  15. data/lib/iiif/presentation/canvas.rb +11 -13
  16. data/lib/iiif/presentation/collection.rb +7 -9
  17. data/lib/iiif/presentation/image_resource.rb +33 -35
  18. data/lib/iiif/presentation/layer.rb +8 -10
  19. data/lib/iiif/presentation/manifest.rb +9 -12
  20. data/lib/iiif/presentation/range.rb +8 -10
  21. data/lib/iiif/presentation/resource.rb +5 -7
  22. data/lib/iiif/presentation/sequence.rb +8 -11
  23. data/lib/iiif/presentation/service.rb +1 -2
  24. data/lib/iiif/presentation.rb +7 -7
  25. data/lib/iiif/service.rb +121 -102
  26. data/lib/iiif/v3/abstract_resource.rb +132 -144
  27. data/lib/iiif/v3/presentation/annotation.rb +20 -21
  28. data/lib/iiif/v3/presentation/annotation_collection.rb +7 -9
  29. data/lib/iiif/v3/presentation/annotation_page.rb +14 -15
  30. data/lib/iiif/v3/presentation/canvas.rb +21 -22
  31. data/lib/iiif/v3/presentation/choice.rb +14 -15
  32. data/lib/iiif/v3/presentation/collection.rb +7 -8
  33. data/lib/iiif/v3/presentation/image_resource.rb +21 -22
  34. data/lib/iiif/v3/presentation/manifest.rb +18 -19
  35. data/lib/iiif/v3/presentation/nav_place.rb +125 -29
  36. data/lib/iiif/v3/presentation/range.rb +10 -10
  37. data/lib/iiif/v3/presentation/resource.rb +4 -5
  38. data/lib/iiif/v3/presentation/sequence.rb +10 -11
  39. data/lib/iiif/v3/presentation/service.rb +12 -13
  40. data/lib/iiif/v3/presentation.rb +8 -8
  41. data/spec/integration/iiif/presentation/image_resource_spec.rb +54 -58
  42. data/spec/integration/iiif/service_spec.rb +76 -77
  43. data/spec/integration/iiif/v3/abstract_resource_spec.rb +73 -73
  44. data/spec/integration/iiif/v3/presentation/image_resource_spec.rb +53 -56
  45. data/spec/spec_helper.rb +56 -64
  46. data/spec/unit/iiif/hash_behaviours_spec.rb +384 -387
  47. data/spec/unit/iiif/ordered_hash_spec.rb +86 -91
  48. data/spec/unit/iiif/presentation/abstract_resource_spec.rb +64 -70
  49. data/spec/unit/iiif/presentation/annotation_list_spec.rb +1 -3
  50. data/spec/unit/iiif/presentation/annotation_spec.rb +1 -3
  51. data/spec/unit/iiif/presentation/canvas_spec.rb +11 -14
  52. data/spec/unit/iiif/presentation/collection_spec.rb +26 -33
  53. data/spec/unit/iiif/presentation/image_resource_spec.rb +4 -6
  54. data/spec/unit/iiif/presentation/layer_spec.rb +16 -20
  55. data/spec/unit/iiif/presentation/manifest_spec.rb +45 -48
  56. data/spec/unit/iiif/presentation/range_spec.rb +18 -22
  57. data/spec/unit/iiif/presentation/resource_spec.rb +3 -6
  58. data/spec/unit/iiif/presentation/sequence_spec.rb +58 -63
  59. data/spec/unit/iiif/presentation/shared_examples/abstract_resource_only_keys.rb +4 -10
  60. data/spec/unit/iiif/presentation/shared_examples/any_type_keys.rb +5 -9
  61. data/spec/unit/iiif/presentation/shared_examples/array_only_keys.rb +9 -17
  62. data/spec/unit/iiif/presentation/shared_examples/int_only_keys.rb +6 -14
  63. data/spec/unit/iiif/presentation/shared_examples/string_only_keys.rb +6 -13
  64. data/spec/unit/iiif/service_spec.rb +8 -13
  65. data/spec/unit/iiif/v3/abstract_resource_define_methods_for_spec.rb +39 -37
  66. data/spec/unit/iiif/v3/abstract_resource_spec.rb +128 -143
  67. data/spec/unit/iiif/v3/presentation/annotation_collection_spec.rb +14 -17
  68. data/spec/unit/iiif/v3/presentation/annotation_page_spec.rb +71 -69
  69. data/spec/unit/iiif/v3/presentation/annotation_spec.rb +249 -233
  70. data/spec/unit/iiif/v3/presentation/canvas_spec.rb +211 -193
  71. data/spec/unit/iiif/v3/presentation/choice_spec.rb +81 -77
  72. data/spec/unit/iiif/v3/presentation/collection_spec.rb +24 -29
  73. data/spec/unit/iiif/v3/presentation/image_resource_spec.rb +70 -71
  74. data/spec/unit/iiif/v3/presentation/manifest_spec.rb +248 -237
  75. data/spec/unit/iiif/v3/presentation/nav_place_spec.rb +249 -49
  76. data/spec/unit/iiif/v3/presentation/range_spec.rb +28 -29
  77. data/spec/unit/iiif/v3/presentation/resource_spec.rb +83 -82
  78. data/spec/unit/iiif/v3/presentation/sequence_spec.rb +126 -120
  79. data/spec/unit/iiif/v3/presentation/service_spec.rb +123 -124
  80. data/spec/unit/iiif/v3/presentation/shared_examples/abstract_resource_only_keys.rb +4 -8
  81. data/spec/unit/iiif/v3/presentation/shared_examples/any_type_keys.rb +5 -7
  82. data/spec/unit/iiif/v3/presentation/shared_examples/array_only_keys.rb +9 -13
  83. data/spec/unit/iiif/v3/presentation/shared_examples/hash_only_keys.rb +9 -13
  84. data/spec/unit/iiif/v3/presentation/shared_examples/int_only_keys.rb +6 -10
  85. data/spec/unit/iiif/v3/presentation/shared_examples/numeric_only_keys.rb +6 -10
  86. data/spec/unit/iiif/v3/presentation/shared_examples/string_only_keys.rb +6 -10
  87. data/spec/unit/iiif/v3/presentation/shared_examples/uri_only_keys.rb +9 -13
  88. metadata +27 -69
data/lib/iiif/service.rb CHANGED
@@ -1,8 +1,8 @@
1
- require File.join(File.dirname(__FILE__), 'hash_behaviours')
2
- require 'active_support/core_ext/class/subclasses'
3
- require 'active_support/ordered_hash'
4
- require 'active_support/inflector'
5
- require 'json'
1
+ require File.join(File.dirname(__FILE__), "hash_behaviours")
2
+ require "active_support/core_ext/class/subclasses"
3
+ require "active_support/ordered_hash"
4
+ require "active_support/inflector"
5
+ require "json"
6
6
 
7
7
  module IIIF
8
8
  class Service
@@ -10,24 +10,47 @@ module IIIF
10
10
 
11
11
  # Anything goes! SHOULD have @id and profile, MAY have label
12
12
  # Consider subclassing this for typical services...
13
- def required_keys; %w{ }; end
14
- def any_type_keys; %w{ }; end
15
- def string_only_keys; %w{ }; end
16
- def array_only_keys; %w{ }; end
17
- def abstract_resource_only_keys; %w{ }; end
18
- def hash_only_keys; %w{ }; end
19
- def int_only_keys; %w{ }; end
20
- def numeric_only_keys; %w{ }; end
21
-
22
- def initialize(hsh={})
13
+ def required_keys
14
+ %w[]
15
+ end
16
+
17
+ def any_type_keys
18
+ %w[]
19
+ end
20
+
21
+ def string_only_keys
22
+ %w[]
23
+ end
24
+
25
+ def array_only_keys
26
+ %w[]
27
+ end
28
+
29
+ def abstract_resource_only_keys
30
+ %w[]
31
+ end
32
+
33
+ def hash_only_keys
34
+ %w[]
35
+ end
36
+
37
+ def int_only_keys
38
+ %w[]
39
+ end
40
+
41
+ def numeric_only_keys
42
+ %w[]
43
+ end
44
+
45
+ def initialize(hsh = {})
23
46
  @data = IIIF::OrderedHash[hsh]
24
- self.define_methods_for_any_type_keys
25
- self.define_methods_for_array_only_keys
26
- self.define_methods_for_string_only_keys
27
- self.define_methods_for_int_only_keys
28
- self.define_methods_for_numeric_only_keys
29
- self.define_methods_for_abstract_resource_only_keys
30
- self.snakeize_keys
47
+ define_methods_for_any_type_keys
48
+ define_methods_for_array_only_keys
49
+ define_methods_for_string_only_keys
50
+ define_methods_for_int_only_keys
51
+ define_methods_for_numeric_only_keys
52
+ define_methods_for_abstract_resource_only_keys
53
+ snakeize_keys
31
54
  end
32
55
 
33
56
  # Static methods / alternative constructors
@@ -35,21 +58,21 @@ module IIIF
35
58
  # Parse from a file path, string, or existing hash
36
59
  def parse(s)
37
60
  ordered_hash = nil
38
- if s.kind_of?(String) && File.exist?(s)
61
+ if s.is_a?(String) && File.exist?(s)
39
62
  ordered_hash = IIIF::OrderedHash[JSON.parse(IO.read(s))]
40
- elsif s.kind_of?(String) && !File.exist?(s)
63
+ elsif s.is_a?(String) && !File.exist?(s)
41
64
  ordered_hash = IIIF::OrderedHash[JSON.parse(s)]
42
- elsif s.kind_of?(Hash)
65
+ elsif s.is_a?(Hash)
43
66
  ordered_hash = IIIF::OrderedHash[s]
44
67
  else
45
- m = '#parse takes a path to a file, a JSON String, or a Hash, '
68
+ m = "#parse takes a path to a file, a JSON String, or a Hash, "
46
69
  m += "argument was a #{s.class}."
47
- if s.kind_of?(String)
48
- m+= "If you were trying to point to a file, does it exist?"
70
+ if s.is_a?(String)
71
+ m += "If you were trying to point to a file, does it exist?"
49
72
  end
50
73
  raise ArgumentError, m
51
74
  end
52
- return IIIF::Service.from_ordered_hash(ordered_hash)
75
+ IIIF::Service.from_ordered_hash(ordered_hash)
53
76
  end
54
77
  end
55
78
 
@@ -60,30 +83,30 @@ module IIIF
60
83
  # * type check String-only values
61
84
  # * type check Integer-only values
62
85
  # * type check AbstractResource-only values
63
- self.required_keys.each do |k|
64
- unless self.has_key?(k)
86
+ required_keys.each do |k|
87
+ unless has_key?(k)
65
88
  m = "A(n) #{k} is required for each #{self.class}"
66
89
  raise IIIF::Presentation::MissingRequiredKeyError, m
67
90
  end
68
91
  end
69
92
  # Viewing Direction values
70
- if self.has_key?('viewing_direction')
71
- unless self.legal_viewing_direction_values.include?(self['viewing_direction'])
93
+ if has_key?("viewing_direction")
94
+ unless legal_viewing_direction_values.include?(self["viewing_direction"])
72
95
  m = "viewingDirection must be one of #{legal_viewing_direction_values}"
73
96
  raise IIIF::Presentation::IllegalValueError, m
74
97
  end
75
98
  end
76
99
  # Viewing Hint values
77
- if self.has_key?('viewing_hint')
78
- unless self.legal_viewing_hint_values.include?(self['viewing_hint'])
79
- m = "viewingHint for #{self.class} must be one of #{self.legal_viewing_hint_values}."
100
+ if has_key?("viewing_hint")
101
+ unless legal_viewing_hint_values.include?(self["viewing_hint"])
102
+ m = "viewingHint for #{self.class} must be one of #{legal_viewing_hint_values}."
80
103
  raise IIIF::Presentation::IllegalValueError, m
81
104
  end
82
105
  end
83
106
  # Metadata is all hashes
84
- if self.has_key?('metadata')
85
- unless self['metadata'].all? { |entry| entry.kind_of?(Hash) }
86
- m = 'All entries in the metadata list must be a type of Hash'
107
+ if has_key?("metadata")
108
+ unless self["metadata"].all? { |entry| entry.is_a?(Hash) }
109
+ m = "All entries in the metadata list must be a type of Hash"
87
110
  raise IIIF::Presentation::IllegalValueError, m
88
111
  end
89
112
  end
@@ -92,8 +115,8 @@ module IIIF
92
115
  # Options
93
116
  # * pretty: (true|false). Should the JSON be pretty-printed? (default: false)
94
117
  # * All options available in #to_ordered_hash
95
- def to_json(opts={})
96
- hsh = self.to_ordered_hash(opts)
118
+ def to_json(opts = {})
119
+ hsh = to_ordered_hash(opts)
97
120
  if opts.fetch(:pretty, false)
98
121
  JSON.pretty_generate(hsh)
99
122
  else
@@ -105,19 +128,19 @@ module IIIF
105
128
  # * force: (true|false). Skips validations.
106
129
  # * sort_json_ld_keys: (true|false). Brings all properties starting with
107
130
  # '@'. Default: true. to the top of the document and sorts them.
108
- def to_ordered_hash(opts={})
131
+ def to_ordered_hash(opts = {})
109
132
  force = opts.fetch(:force, false)
110
133
  sort_json_ld_keys = opts.fetch(:sort_json_ld_keys, true)
111
134
 
112
135
  unless force
113
- self.validate
136
+ validate
114
137
  end
115
138
 
116
139
  export_hash = IIIF::OrderedHash.new
117
140
 
118
141
  if sort_json_ld_keys
119
- self.keys.select { |k| k.start_with?('@') }.sort!.each do |k|
120
- export_hash[k] = self.data[k]
142
+ keys.select { |k| k.start_with?("@") }.sort!.each do |k|
143
+ export_hash[k] = data[k]
121
144
  end
122
145
  end
123
146
 
@@ -126,25 +149,24 @@ module IIIF
126
149
  sort_json_ld_keys: sort_json_ld_keys,
127
150
  force: force
128
151
  }
129
- self.keys.each do |k|
130
- unless sort_json_ld_keys && k.start_with?('@')
131
- if self.data[k].respond_to?(:to_ordered_hash) #.respond_to?(:to_ordered_hash)
132
- export_hash[k] = self.data[k].to_ordered_hash(sub_opts)
152
+ keys.each do |k|
153
+ unless sort_json_ld_keys && k.start_with?("@")
154
+ if data[k].respond_to?(:to_ordered_hash) # .respond_to?(:to_ordered_hash)
155
+ export_hash[k] = data[k].to_ordered_hash(sub_opts)
133
156
 
134
- elsif self.data[k].kind_of?(Hash)
157
+ elsif data[k].is_a?(Hash)
135
158
  export_hash[k] = IIIF::OrderedHash.new
136
- self.data[k].each do |sub_k, v|
137
-
159
+ data[k].each do |sub_k, v|
138
160
  if v.respond_to?(:to_ordered_hash)
139
161
  export_hash[k][sub_k] = v.to_ordered_hash(sub_opts)
140
162
 
141
- elsif v.kind_of?(Array)
163
+ elsif v.is_a?(Array)
142
164
  export_hash[k][sub_k] = []
143
165
  v.each do |member|
144
- if member.respond_to?(:to_ordered_hash)
145
- export_hash[k][sub_k] << member.to_ordered_hash(sub_opts)
166
+ export_hash[k][sub_k] << if member.respond_to?(:to_ordered_hash)
167
+ member.to_ordered_hash(sub_opts)
146
168
  else
147
- export_hash[k][sub_k] << member
169
+ member
148
170
  end
149
171
  end
150
172
  else
@@ -152,29 +174,28 @@ module IIIF
152
174
  end
153
175
  end
154
176
 
155
- elsif self.data[k].kind_of?(Array)
177
+ elsif data[k].is_a?(Array)
156
178
  export_hash[k] = []
157
179
 
158
- self.data[k].each do |member|
180
+ data[k].each do |member|
159
181
  if member.respond_to?(:to_ordered_hash)
160
182
  export_hash[k] << member.to_ordered_hash(sub_opts)
161
183
 
162
- elsif member.kind_of?(Hash)
184
+ elsif member.is_a?(Hash)
163
185
  hsh = IIIF::OrderedHash.new
164
186
  export_hash[k] << hsh
165
- member.each do |sub_k,v|
166
-
187
+ member.each do |sub_k, v|
167
188
  if v.respond_to?(:to_ordered_hash)
168
189
  hsh[sub_k] = v.to_ordered_hash(sub_opts)
169
190
 
170
- elsif v.kind_of?(Array)
191
+ elsif v.is_a?(Array)
171
192
  hsh[sub_k] = []
172
193
 
173
194
  v.each do |sub_member|
174
- if sub_member.respond_to?(:to_ordered_hash)
175
- hsh[sub_k] << sub_member.to_ordered_hash(sub_opts)
195
+ hsh[sub_k] << if sub_member.respond_to?(:to_ordered_hash)
196
+ sub_member.to_ordered_hash(sub_opts)
176
197
  else
177
- hsh[sub_k] << sub_member
198
+ sub_member
178
199
  end
179
200
  end
180
201
  else
@@ -188,7 +209,7 @@ module IIIF
188
209
  end
189
210
  end
190
211
  else
191
- export_hash[k] = self.data[k]
212
+ export_hash[k] = data[k]
192
213
  end
193
214
 
194
215
  end
@@ -198,35 +219,35 @@ module IIIF
198
219
  export_hash
199
220
  end
200
221
 
201
- def self.from_ordered_hash(hsh, default_klass=IIIF::OrderedHash)
222
+ def self.from_ordered_hash(hsh, default_klass = IIIF::OrderedHash)
202
223
  # Create a new object (new_object)
203
224
  type = nil
204
- if hsh.has_key?('@type')
205
- type = IIIF::Service.get_descendant_class_by_jld_type(hsh['@type'])
225
+ if hsh.has_key?("@type")
226
+ type = IIIF::Service.get_descendant_class_by_jld_type(hsh["@type"])
206
227
  end
207
228
  new_object = type.nil? ? default_klass.new : type.new
208
229
 
209
230
  hsh.keys.each do |key|
210
- new_key = key.underscore == key ? key : key.underscore
211
- if hsh[key].kind_of?(Array)
231
+ new_key = (key.underscore == key) ? key : key.underscore
232
+ if hsh[key].is_a?(Array)
212
233
  new_object[new_key] = []
213
234
  hsh[key].each do |member|
214
- if new_key == 'service'
215
- new_object[new_key] << IIIF::Service.from_ordered_hash(member, IIIF::Presentation::Service)
216
- elsif new_key == 'resource'
217
- new_object[new_key] << IIIF::Service.from_ordered_hash(hsh[key], IIIF::Presentation::Resource)
218
- elsif member.kind_of?(Hash)
219
- new_object[new_key] << IIIF::Service.from_ordered_hash(member)
235
+ new_object[new_key] << if new_key == "service"
236
+ IIIF::Service.from_ordered_hash(member, IIIF::Presentation::Service)
237
+ elsif new_key == "resource"
238
+ IIIF::Service.from_ordered_hash(hsh[key], IIIF::Presentation::Resource)
239
+ elsif member.is_a?(Hash)
240
+ IIIF::Service.from_ordered_hash(member)
220
241
  else
221
- new_object[new_key] << member
242
+ member
222
243
  # Again, no nested arrays, right?
223
244
  end
224
245
  end
225
- elsif new_key == 'service'
246
+ elsif new_key == "service"
226
247
  new_object[new_key] = IIIF::Service.from_ordered_hash(hsh[key], IIIF::Presentation::Service)
227
- elsif new_key == 'resource'
248
+ elsif new_key == "resource"
228
249
  new_object[new_key] = IIIF::Service.from_ordered_hash(hsh[key], IIIF::Presentation::Resource)
229
- elsif hsh[key].kind_of?(Hash)
250
+ elsif hsh[key].is_a?(Hash)
230
251
  new_object[new_key] = IIIF::Service.from_ordered_hash(hsh[key])
231
252
  else
232
253
  new_object[new_key] = hsh[key]
@@ -235,8 +256,6 @@ module IIIF
235
256
  new_object
236
257
  end
237
258
 
238
- protected
239
-
240
259
  def self.get_descendant_class_by_jld_type(type)
241
260
  IIIF::Service.all_service_subclasses.find do |klass|
242
261
  klass.const_defined?(:TYPE) && klass.const_get(:TYPE) == type
@@ -248,13 +267,11 @@ module IIIF
248
267
  @all_service_subclasses ||= IIIF::Service.descendants.reject(&:singleton_class?)
249
268
  end
250
269
 
251
- def data=(hsh)
252
- @data = hsh
253
- end
270
+ protected
254
271
 
255
- def data
256
- @data
257
- end
272
+ attr_writer :data
273
+
274
+ attr_reader :data
258
275
 
259
276
  def define_methods_for_any_type_keys
260
277
  define_accessor_methods(*any_type_keys)
@@ -263,14 +280,14 @@ module IIIF
263
280
  # an array for empty values.
264
281
  any_type_keys.each do |key|
265
282
  define_singleton_method(key) do
266
- self.send('[]', key)
283
+ send(:[], key)
267
284
  end
268
285
  end
269
286
  end
270
287
 
271
288
  def define_methods_for_array_only_keys
272
289
  define_accessor_methods(*array_only_keys) do |key, arg|
273
- unless arg.kind_of?(Array)
290
+ unless arg.is_a?(Array)
274
291
  m = "#{key} must be an Array."
275
292
  raise IIIF::Presentation::IllegalValueError, m
276
293
  end
@@ -284,7 +301,7 @@ module IIIF
284
301
  type = hsh[:type]
285
302
 
286
303
  define_accessor_methods(key) do |key, arg|
287
- unless arg.kind_of?(type)
304
+ unless arg.is_a?(type)
288
305
  m = "#{key} must be an #{type}."
289
306
  raise IIIF::Presentation::IllegalValueError, m
290
307
  end
@@ -294,7 +311,7 @@ module IIIF
294
311
 
295
312
  def define_methods_for_string_only_keys
296
313
  define_accessor_methods(*string_only_keys) do |key, arg|
297
- unless arg.kind_of?(String)
314
+ unless arg.is_a?(String)
298
315
  m = "#{key} must be an String."
299
316
  raise IIIF::Presentation::IllegalValueError, m
300
317
  end
@@ -303,7 +320,7 @@ module IIIF
303
320
 
304
321
  def define_methods_for_int_only_keys
305
322
  define_accessor_methods(*int_only_keys) do |key, arg|
306
- unless arg.kind_of?(Integer) && arg > 0
323
+ unless arg.is_a?(Integer) && arg > 0
307
324
  m = "#{key} must be a positive Integer."
308
325
  raise IIIF::Presentation::IllegalValueError, m
309
326
  end
@@ -312,7 +329,7 @@ module IIIF
312
329
 
313
330
  def define_methods_for_numeric_only_keys
314
331
  define_accessor_methods(*numeric_only_keys) do |key, arg|
315
- unless arg.kind_of?(Numeric) && arg > 0
332
+ unless arg.is_a?(Numeric) && arg > 0
316
333
  m = "#{key} must be a positive Integer or Float."
317
334
  raise IIIF::Presentation::IllegalValueError, m
318
335
  end
@@ -321,15 +338,17 @@ module IIIF
321
338
 
322
339
  def define_accessor_methods(*keys, &validation)
323
340
  keys.each do |key|
341
+ camelized_key = key.camelize(:lower)
342
+
324
343
  # Setter
325
344
  define_singleton_method("#{key}=") do |arg|
326
345
  validation.call(key, arg) if block_given?
327
- self.send('[]=', key, arg)
346
+ send(:[]=, key, arg)
328
347
  end
329
- if key.camelize(:lower) != key
330
- define_singleton_method("#{key.camelize(:lower)}=") do |arg|
348
+ if camelized_key != key
349
+ define_singleton_method("#{camelized_key}=") do |arg|
331
350
  validation.call(key, arg) if block_given?
332
- self.send('[]=', key, arg)
351
+ send(:[]=, key, arg)
333
352
  end
334
353
  end
335
354
  # Getter
@@ -337,9 +356,9 @@ module IIIF
337
356
  self[key] ||= []
338
357
  self[key]
339
358
  end
340
- if key.camelize(:lower) != key
341
- define_singleton_method(key.camelize(:lower)) do
342
- self.send('[]', key)
359
+ if camelized_key != key
360
+ define_singleton_method(camelized_key) do
361
+ send(:[], key)
343
362
  end
344
363
  end
345
364
  end