iiif-presentation 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +2 -0
- data/LICENSE +23 -0
- data/README.md +173 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/gemfiles/rails3.gemfile +5 -0
- data/gemfiles/rails4.gemfile +5 -0
- data/iiif-presentation.gemspec +28 -0
- data/lib/active_support/ordered_hash.rb +147 -0
- data/lib/iiif/hash_behaviours.rb +150 -0
- data/lib/iiif/presentation.rb +25 -0
- data/lib/iiif/presentation/abstract_resource.rb +75 -0
- data/lib/iiif/presentation/annotation.rb +25 -0
- data/lib/iiif/presentation/annotation_list.rb +28 -0
- data/lib/iiif/presentation/canvas.rb +45 -0
- data/lib/iiif/presentation/collection.rb +29 -0
- data/lib/iiif/presentation/image_resource.rb +115 -0
- data/lib/iiif/presentation/layer.rb +34 -0
- data/lib/iiif/presentation/manifest.rb +39 -0
- data/lib/iiif/presentation/range.rb +32 -0
- data/lib/iiif/presentation/resource.rb +21 -0
- data/lib/iiif/presentation/sequence.rb +35 -0
- data/lib/iiif/service.rb +418 -0
- data/spec/fixtures/manifests/complete_from_spec.json +171 -0
- data/spec/fixtures/manifests/minimal.json +40 -0
- data/spec/fixtures/manifests/service_only.json +11 -0
- data/spec/fixtures/vcr_cassettes/pul_loris_cassette.json +159 -0
- data/spec/integration/iiif/presentation/image_resource_spec.rb +123 -0
- data/spec/integration/iiif/service_spec.rb +211 -0
- data/spec/spec_helper.rb +104 -0
- data/spec/unit/active_support/ordered_hash_spec.rb +155 -0
- data/spec/unit/iiif/hash_behaviours_spec.rb +569 -0
- data/spec/unit/iiif/presentation/abstract_resource_spec.rb +133 -0
- data/spec/unit/iiif/presentation/annotation_list_spec.rb +7 -0
- data/spec/unit/iiif/presentation/annotation_spec.rb +7 -0
- data/spec/unit/iiif/presentation/canvas_spec.rb +40 -0
- data/spec/unit/iiif/presentation/collection_spec.rb +54 -0
- data/spec/unit/iiif/presentation/image_resource_spec.rb +13 -0
- data/spec/unit/iiif/presentation/layer_spec.rb +38 -0
- data/spec/unit/iiif/presentation/manifest_spec.rb +89 -0
- data/spec/unit/iiif/presentation/range_spec.rb +43 -0
- data/spec/unit/iiif/presentation/resource_spec.rb +16 -0
- data/spec/unit/iiif/presentation/sequence_spec.rb +110 -0
- data/spec/unit/iiif/presentation/shared_examples/abstract_resource_only_keys.rb +43 -0
- data/spec/unit/iiif/presentation/shared_examples/any_type_keys.rb +33 -0
- data/spec/unit/iiif/presentation/shared_examples/array_only_keys.rb +44 -0
- data/spec/unit/iiif/presentation/shared_examples/int_only_keys.rb +49 -0
- data/spec/unit/iiif/presentation/shared_examples/string_only_keys.rb +29 -0
- data/spec/unit/iiif/service_spec.rb +10 -0
- metadata +262 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1e600987059a68eef0064df0b7d4a62606af135e
|
4
|
+
data.tar.gz: d1a85d821d52a370f9c3b0cd27383437cd242694
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 65ab0328b4f5c514e4c6a3860d24aa00d0d9908971d64ff0a36fd000d552c1992711700c30ea690a43b1a436b3c8b459242d1f853a01a71b0b699a4685fd8228
|
7
|
+
data.tar.gz: e210d147df7c6ce971c518c5f117c2918aadab3ad9f4e115a3add980453f81251a33acd3ec69dcb576b0f5157c23b14df37f9517e4b82505d8ae9fa09cc32eb0
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2014, Jon Stroop
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
15
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
18
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
19
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
20
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
21
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
22
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
23
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
# O'Sullivan: A Ruby API for working with IIIF Presentation manifests
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/IIIF/osullivan.svg?branch=development)](https://travis-ci.org/IIIF/osullivan)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/IIIF/osullivan/badge.svg?branch=development&service=github)](https://coveralls.io/github/IIIF/osullivan?branch=development)
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
From the source code do `rake install`, or get the latest release [from RubyGems](https://rubygems.org/gems/iiif-presentation).
|
10
|
+
|
11
|
+
## Building New Objects
|
12
|
+
|
13
|
+
There is (or will be) a class for all types in [IIIF Presentation API Spec](http://iiif.io/api/presentation/2.0/).
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
require 'iiif/presentation'
|
20
|
+
|
21
|
+
seed = {
|
22
|
+
'@id' => 'http://example.com/manifest',
|
23
|
+
'label' => 'My Manifest'
|
24
|
+
}
|
25
|
+
# Any options you add are added to the object
|
26
|
+
manifest = IIIF::Presentation::Manifest.new(seed)
|
27
|
+
|
28
|
+
canvas = IIIF::Presentation::Canvas.new()
|
29
|
+
# All classes act like `ActiveSupport::OrderedHash`es, for the most part.
|
30
|
+
# Use `[]=` to set JSON-LD properties...
|
31
|
+
canvas['@id'] = 'http://example.com/canvas'
|
32
|
+
# ...but there are also accessors and mutators for the properties mentioned in
|
33
|
+
# the spec
|
34
|
+
canvas.width = 10
|
35
|
+
canvas.height = 20
|
36
|
+
canvas.label = 'My Canvas'
|
37
|
+
|
38
|
+
oc = IIIF::Presentation::Resource.new('@id' => 'http://example.com/content')
|
39
|
+
canvas.other_content << oc
|
40
|
+
|
41
|
+
manifest.sequences << canvas
|
42
|
+
|
43
|
+
puts manifest.to_json(pretty: true)
|
44
|
+
```
|
45
|
+
|
46
|
+
Methods are generated dynamically, which means `#methods` is your friend:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
manifest = IIIF::Presentation::Manifest.new()
|
50
|
+
puts manifest.methods(false)
|
51
|
+
> label=
|
52
|
+
> label
|
53
|
+
> description=
|
54
|
+
> description
|
55
|
+
> thumbnail=
|
56
|
+
> thumbnail
|
57
|
+
> attribution=
|
58
|
+
> attribution
|
59
|
+
> viewing_hint=
|
60
|
+
> viewingHint=
|
61
|
+
> viewing_hint
|
62
|
+
> viewingHint
|
63
|
+
[...]
|
64
|
+
```
|
65
|
+
|
66
|
+
Note that multi-word properties are implemented as snake_case (because this is
|
67
|
+
Ruby), but is serialized as camelCase. There are camelCase aliases for these.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
manifest = IIIF::Presentation::Manifest.new()
|
71
|
+
manifest.viewing_hint = 'paged'
|
72
|
+
puts manifest.to_json(pretty: true, force: true) # force: true skips validations
|
73
|
+
|
74
|
+
> {
|
75
|
+
> "@context": "http://iiif.io/api/presentation/2/context.json",
|
76
|
+
> "@type": "sc:Manifest",
|
77
|
+
> "viewingHint": "paged"
|
78
|
+
> }
|
79
|
+
|
80
|
+
```
|
81
|
+
|
82
|
+
## Parsing Existing Objects
|
83
|
+
|
84
|
+
Use `IIIF::Service#parse`. It will figure out what the object
|
85
|
+
should be, based on `@type`, and fall back to `ActiveSupport::OrderedHash` when
|
86
|
+
it can't e.g.:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
seed = '{
|
90
|
+
"@context": "http://iiif.io/api/presentation/2/context.json",
|
91
|
+
"@id": "http://example.com/manifest",
|
92
|
+
"@type": "sc:Manifest",
|
93
|
+
"label": "My Manifest",
|
94
|
+
"service": {
|
95
|
+
"@context": "http://iiif.io/api/image/2/context.json",
|
96
|
+
"@id":"http://www.example.org/images/book1-page1",
|
97
|
+
"profile":"http://iiif.io/api/image/2/profiles/level2.json"
|
98
|
+
},
|
99
|
+
"seeAlso": {
|
100
|
+
"@id": "http://www.example.org/library/catalog/book1.marc",
|
101
|
+
"format": "application/marc"
|
102
|
+
},
|
103
|
+
"sequences": [
|
104
|
+
{
|
105
|
+
"@id":"http://www.example.org/iiif/book1/sequence/normal",
|
106
|
+
"@type":"sc:Sequence",
|
107
|
+
"label":"Current Page Order",
|
108
|
+
"viewingDirection":"left-to-right",
|
109
|
+
"viewingHint":"paged",
|
110
|
+
"startCanvas": "http://www.example.org/iiif/book1/canvas/p2",
|
111
|
+
"canvases": [
|
112
|
+
{
|
113
|
+
"@id": "http://example.com/canvas",
|
114
|
+
"@type": "sc:Canvas",
|
115
|
+
"width": 10,
|
116
|
+
"height": 20,
|
117
|
+
"label": "My Canvas",
|
118
|
+
"otherContent": [
|
119
|
+
{
|
120
|
+
"@id": "http://example.com/content",
|
121
|
+
"@type":"sc:AnnotationList",
|
122
|
+
"motivation": "sc:painting"
|
123
|
+
}
|
124
|
+
]
|
125
|
+
}
|
126
|
+
]
|
127
|
+
}
|
128
|
+
]
|
129
|
+
}'
|
130
|
+
|
131
|
+
obj = IIIF::Service.parse(seed) # can also be a file path or a Hash
|
132
|
+
puts obj.class
|
133
|
+
puts obj.see_also.class
|
134
|
+
|
135
|
+
> IIIF::Presentation::Manifest
|
136
|
+
> ActiveSupport::OrderedHash
|
137
|
+
```
|
138
|
+
|
139
|
+
## Validation and Exceptions
|
140
|
+
|
141
|
+
This is work in progress. Right now exceptions are generally raised when you
|
142
|
+
try to set something to a type it should never be:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
manifest = IIIF::Presentation::Manifest.new
|
146
|
+
manifest.sequences = 'quux'
|
147
|
+
|
148
|
+
> [...] sequences must be an Array. (IIIF::Presentation::IllegalValueError)
|
149
|
+
```
|
150
|
+
|
151
|
+
and also if any required properties are missing when calling `to_json`
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
|
155
|
+
puts canvas.to_json(pretty: true)
|
156
|
+
|
157
|
+
> A(n) width is required for each IIIF::Presentation::Canvas (IIIF::Presentation::MissingRequiredKeyError)
|
158
|
+
```
|
159
|
+
|
160
|
+
but you can skip this validation by adding `force: true`:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
|
164
|
+
puts canvas.to_json(pretty: true, force: true)
|
165
|
+
|
166
|
+
> {
|
167
|
+
> "@context": "http://iiif.io/api/presentation/2/context.json",
|
168
|
+
> "@id": "http://example.com/canvas",
|
169
|
+
> "@type": "sc:Canvas"
|
170
|
+
> }
|
171
|
+
```
|
172
|
+
This all needs a bit of tidying up, finishing, and refactoring, so expect it to
|
173
|
+
change.
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
@@ -0,0 +1,28 @@
|
|
1
|
+
version = File.read(File.expand_path('../VERSION', __FILE__)).strip
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'iiif-presentation'
|
5
|
+
spec.version = version
|
6
|
+
spec.authors = ['Jon Stroop']
|
7
|
+
spec.email = ['jpstroop@gmail.com']
|
8
|
+
spec.description = 'API for working with IIIF Presentation manifests.'
|
9
|
+
spec.summary = 'API for working with IIIF Presentation manifests.'
|
10
|
+
spec.license = 'Simplified BSD'
|
11
|
+
spec.homepage = 'https://github.com/iiif/osullivan'
|
12
|
+
|
13
|
+
spec.files = `git ls-files`.split($/)
|
14
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
15
|
+
spec.require_paths = ['lib']
|
16
|
+
|
17
|
+
spec.add_development_dependency 'bundler'
|
18
|
+
spec.add_development_dependency 'rake'
|
19
|
+
spec.add_development_dependency 'rspec'
|
20
|
+
spec.add_development_dependency 'coveralls'
|
21
|
+
spec.add_development_dependency 'webmock'
|
22
|
+
spec.add_development_dependency 'multi_json'
|
23
|
+
spec.add_development_dependency 'vcr', '~> 2.9.3'
|
24
|
+
|
25
|
+
spec.add_dependency 'json'
|
26
|
+
spec.add_dependency 'activesupport', '>= 3.2.18'
|
27
|
+
spec.add_dependency 'faraday', '~> 0.9.0'
|
28
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'active_support/ordered_hash'
|
3
|
+
|
4
|
+
module ActiveSupport
|
5
|
+
class OrderedHash < ::Hash
|
6
|
+
|
7
|
+
# Insert a new key and value at the suppplied index.
|
8
|
+
#
|
9
|
+
# Note that this is slightly different from Array#insert in that new
|
10
|
+
# entries must be added one at a time, i.e. insert(n, k, v, k, v...) is
|
11
|
+
# not supported.
|
12
|
+
#
|
13
|
+
# @param [Integer] index
|
14
|
+
# @param [Object] key
|
15
|
+
# @param [Object] value
|
16
|
+
def insert(index, key, value)
|
17
|
+
tmp = ActiveSupport::OrderedHash.new
|
18
|
+
index = self.length + 1 + index if index < 0
|
19
|
+
if index < 0
|
20
|
+
m = "Index #{index} is too small for current length (#{length})"
|
21
|
+
raise IndexError, m
|
22
|
+
end
|
23
|
+
if index > 0
|
24
|
+
i=0
|
25
|
+
self.each do |k,v|
|
26
|
+
tmp[k] = v
|
27
|
+
self.delete(k)
|
28
|
+
i+=1
|
29
|
+
break if i == index
|
30
|
+
end
|
31
|
+
end
|
32
|
+
tmp[key] = value
|
33
|
+
tmp.merge!(self) # copy the remaining to tmp
|
34
|
+
self.clear # start over...
|
35
|
+
self.merge!(tmp) # now put them all back
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Insert a key and value before an existing key or the first entry for'
|
40
|
+
# which the supplied block evaluates to true. The block takes precendence
|
41
|
+
# over the supplied key.
|
42
|
+
# Options:'
|
43
|
+
# * :existing_key (default: nil). If nil or not supplied then a block is required.
|
44
|
+
# * :new_key (required)
|
45
|
+
# * :value (required)
|
46
|
+
# @raise KeyError if the supplied existing key is not found, the new
|
47
|
+
# key exists, or the block never evaluates to true.
|
48
|
+
def insert_before(hsh, &block)
|
49
|
+
existing_key = hsh.fetch(:existing_key, nil)
|
50
|
+
new_key = hsh[:new_key]
|
51
|
+
value = hsh[:value]
|
52
|
+
if block_given?
|
53
|
+
self.insert_here(0, new_key, value, &block)
|
54
|
+
else
|
55
|
+
self.insert_here(0, new_key, value, existing_key)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Insert a key and value after an existing key or the first entry for'
|
60
|
+
# which the supplied block evaluates to true. The block takes precendence
|
61
|
+
# over the supplied key.
|
62
|
+
# Options:'
|
63
|
+
# * :existing_key (default: nil). If nil or not supplied then a block is required.
|
64
|
+
# * :new_key (required)
|
65
|
+
# * :value (required)
|
66
|
+
# @raise KeyError if the supplied existing key is not found, the new
|
67
|
+
# key exists, or the block never evaluates to true.
|
68
|
+
def insert_after(hsh, &block)
|
69
|
+
existing_key = hsh.fetch(:existing_key, nil)
|
70
|
+
new_key = hsh[:new_key]
|
71
|
+
value = hsh[:value]
|
72
|
+
if block_given?
|
73
|
+
self.insert_here(1, new_key, value, &block)
|
74
|
+
else
|
75
|
+
self.insert_here(1, new_key, value, existing_key)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Delete any keys that are empty arrays
|
80
|
+
def remove_empties
|
81
|
+
self.keys.each do |key|
|
82
|
+
if (self[key].kind_of?(Array) && self[key].empty?) || self[key].nil?
|
83
|
+
self.delete(key)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Covert snake_case keys to camelCase
|
89
|
+
def camelize_keys
|
90
|
+
self.keys.each_with_index do |key, i|
|
91
|
+
if key != key.camelize(:lower)
|
92
|
+
self.insert(i, key.camelize(:lower), self[key])
|
93
|
+
self.delete(key)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
# Covert camelCase keys to snake_case
|
100
|
+
def snakeize_keys
|
101
|
+
self.keys.each_with_index do |key, i|
|
102
|
+
if key != key.underscore
|
103
|
+
self.insert(i, key.underscore, self[key])
|
104
|
+
self.delete(key)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# Prepends an entry to the front of the object.
|
112
|
+
# Note that this is slightly different from Array#unshift in that new
|
113
|
+
# entries must be added one at a time, i.e. unshift([k,v],[k,v],...) is
|
114
|
+
# not currently supported.
|
115
|
+
def unshift k,v
|
116
|
+
self.insert(0, k, v)
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
def insert_here(where, new_key, value, existing_key=nil, &block)
|
122
|
+
idx = nil
|
123
|
+
if block_given?
|
124
|
+
self.each_with_index do |(k,v), i|
|
125
|
+
if yield(k, v)
|
126
|
+
idx = i
|
127
|
+
break
|
128
|
+
end
|
129
|
+
end
|
130
|
+
if idx.nil?
|
131
|
+
raise KeyError, "Supplied block never evaluates to true"
|
132
|
+
end
|
133
|
+
else
|
134
|
+
unless self.has_key?(existing_key)
|
135
|
+
raise KeyError, "Existing key '#{existing_key}' does not exist"
|
136
|
+
end
|
137
|
+
if self.has_key?(new_key)
|
138
|
+
raise KeyError, "Supplied new key '#{new_key}' already exists"
|
139
|
+
end
|
140
|
+
idx = self.keys.index(existing_key) + where
|
141
|
+
end
|
142
|
+
self.insert(idx, new_key, value)
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module IIIF
|
4
|
+
module HashBehaviours
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
# TODO:
|
8
|
+
# * reject
|
9
|
+
# * replace
|
10
|
+
|
11
|
+
def_delegators :@data, :[], :[]=, :camelize_keys, :delete, :empty?,
|
12
|
+
:fetch, :has_key?, :has_value?, :include?, :insert, :insert_after,
|
13
|
+
:insert_before, :key, :key?, :keys, :length, :member?, :shift, :size,
|
14
|
+
:snakeize_keys, :store, :unshift, :value?, :values
|
15
|
+
|
16
|
+
|
17
|
+
###
|
18
|
+
# Methods that take a block and should return an instance (self or a new'
|
19
|
+
# instance) have been overridden to do so, rather than an'
|
20
|
+
# ActiveSupport::OrderedHash based on the internal hash
|
21
|
+
|
22
|
+
SIMPLE_SELF_RETURNERS = %w[delete_if each each_key each_value keep_if]
|
23
|
+
|
24
|
+
SIMPLE_SELF_RETURNERS.each do |method_name|
|
25
|
+
define_method(method_name) do |*arg, &block|
|
26
|
+
unless block.nil? # block_given? doesn't seem to work in this context
|
27
|
+
@data.send(method_name, *arg, &block)
|
28
|
+
return self
|
29
|
+
else
|
30
|
+
@data.send(method_name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Clear is the only method that returns self but doesn't accept a block
|
36
|
+
def clear
|
37
|
+
@data.clear
|
38
|
+
return self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a new instance of this class containing the contents of'
|
42
|
+
# another_obj. The argument can be any object that implements two
|
43
|
+
# methods:
|
44
|
+
#
|
45
|
+
# obj.each { |k,v| block }
|
46
|
+
# obj.has_key?
|
47
|
+
#
|
48
|
+
# If no block is specified, the value for entries with duplicate keys'
|
49
|
+
# will be those of the argument, but at the index of the original; all'
|
50
|
+
# other entries will be appended to the end.
|
51
|
+
#
|
52
|
+
# If a block is specified the value for each duplicate key is determined'
|
53
|
+
# by calling the block with the key, its value in hsh and its value in'
|
54
|
+
# another_obj.
|
55
|
+
def merge another_obj
|
56
|
+
new_instance = self.class.new
|
57
|
+
# self.clone # Would this be better? What happens to other attributes of the class?
|
58
|
+
if block_given?
|
59
|
+
self.each do |k,v|
|
60
|
+
if another_obj.has_key? k
|
61
|
+
new_instance[k] = yield(k, self[k], another_obj[k])
|
62
|
+
else
|
63
|
+
new_instance[k] = v
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
self.each { |k,v| new_instance[k] = v }
|
68
|
+
another_obj.each { |k,v| new_instance[k] = v }
|
69
|
+
end
|
70
|
+
new_instance
|
71
|
+
end
|
72
|
+
|
73
|
+
# Adds the entries from another obj to this one. The argument can be any
|
74
|
+
# object that implements two methods:
|
75
|
+
#
|
76
|
+
# obj.each { |k,v| block }
|
77
|
+
# obj.has_key?
|
78
|
+
#
|
79
|
+
# If no block is specified, the value for entries with duplicate keys'
|
80
|
+
# will be those of the argument, but at the index of the original; all'
|
81
|
+
# other entries will be appended to the end.
|
82
|
+
#
|
83
|
+
# If a block is specified the value for each duplicate key is determined'
|
84
|
+
# by calling the block with the key, its value in hsh and its value in'
|
85
|
+
# another_obj.
|
86
|
+
def merge! another_obj
|
87
|
+
if block_given?
|
88
|
+
self.each do |k,v|
|
89
|
+
if another_obj.has_key? k
|
90
|
+
self[k] = yield(k, self[k], another_obj[k])
|
91
|
+
else
|
92
|
+
self[k] = v
|
93
|
+
end
|
94
|
+
end
|
95
|
+
else
|
96
|
+
self.each { |k,v| self[k] = v }
|
97
|
+
another_obj.each { |k,v| self[k] = v }
|
98
|
+
end
|
99
|
+
self
|
100
|
+
end
|
101
|
+
alias update merge!
|
102
|
+
|
103
|
+
# Deletes entries for which the supplied block evaluates to true.
|
104
|
+
# Equivalent to #delete_if, but returns nil if there were no changes
|
105
|
+
def reject!
|
106
|
+
if block_given?
|
107
|
+
return_nil = true
|
108
|
+
@data.each do |k, v|
|
109
|
+
if yield(k, v)
|
110
|
+
@data.delete(k)
|
111
|
+
return_nil = false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
return return_nil ? nil : self
|
115
|
+
else
|
116
|
+
return self.data.reject!
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns a new instance consisting of entries for which the block returns
|
121
|
+
# true. Not that an enumerator is not available for the OrderedHash'
|
122
|
+
# implementation
|
123
|
+
def select
|
124
|
+
new_instance = self.class.new
|
125
|
+
if block_given?
|
126
|
+
@data.each { |k,v| new_instance.data[k] = v if yield(k,v) }
|
127
|
+
end
|
128
|
+
return new_instance
|
129
|
+
end
|
130
|
+
|
131
|
+
# Deletes entries for which the supplied block evaluates to false.
|
132
|
+
# Equivalent to Hash#keep_if, but returns nil if no changes were made.
|
133
|
+
def select!
|
134
|
+
if block_given?
|
135
|
+
return_nil = true
|
136
|
+
@data.each do |k,v|
|
137
|
+
unless yield(k,v)
|
138
|
+
@data.delete(k)
|
139
|
+
return_nil = false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
return nil if return_nil
|
143
|
+
end
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|