osullivan 0.0.2
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 +4 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE +23 -0
- data/README.md +166 -0
- data/Rakefile +12 -0
- data/VERSION +1 -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/osullivan.gemspec +27 -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 +246 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b50c5e03818f7b4152010d37813f230abe570c72
|
4
|
+
data.tar.gz: 594ca02320dd7e3ed0412784476dea78b732fb8a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 32172a98698cd30d59178385e6098919f9053154ea6f77b36df0e4c114a2a53aa59e831241c7ab608fc75c3423d05ca7bfbbbb99430cacb0686de17fb50638af
|
7
|
+
data.tar.gz: 5437e7a202ceda204b49b4f32ad1fe4d3eb3cc70a85abb9e22f2a1c14f37219596a40a0562d27fab9689846a0c8d51c0143e5996f0e8e4b643e1b56b3e70fa3e
|
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,166 @@
|
|
1
|
+
# OSullivan
|
2
|
+
|
3
|
+
[](https://travis-ci.org/jpstroop/osullivan) [](https://coveralls.io/r/jpstroop/osullivan?branch=development)
|
4
|
+
|
5
|
+
## Building New Objects
|
6
|
+
|
7
|
+
There is (or will be) a class for all types in [IIIF Presentation API Spec](http://iiif.io/api/presentation/2.0/).
|
8
|
+
|
9
|
+
After you've installed the gem (not yet published. Clone and do `rake:install`):
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'iiif/presentation'
|
13
|
+
|
14
|
+
seed = {
|
15
|
+
'@id' => 'http://example.com/manifest',
|
16
|
+
'label' => 'My Manifest'
|
17
|
+
}
|
18
|
+
# Any options you add are added to the object
|
19
|
+
manifest = IIIF::Presentation::Manifest.new(seed)
|
20
|
+
|
21
|
+
canvas = IIIF::Presentation::Canvas.new()
|
22
|
+
# All classes act like `ActiveSupport::OrderedHash`es, for the most part.
|
23
|
+
# Use `[]=` to set JSON-LD properties...
|
24
|
+
canvas['@id'] = 'http://example.com/canvas'
|
25
|
+
# ...but there are also accessors and mutators for the properties mentioned in
|
26
|
+
# the spec
|
27
|
+
canvas.width = 10
|
28
|
+
canvas.height = 20
|
29
|
+
canvas.label = 'My Canvas'
|
30
|
+
|
31
|
+
oc = IIIF::Presentation::Resource.new('@id' => 'http://example.com/content')
|
32
|
+
canvas.other_content << oc
|
33
|
+
|
34
|
+
manifest.sequences << canvas
|
35
|
+
|
36
|
+
puts manifest.to_json(pretty: true)
|
37
|
+
```
|
38
|
+
|
39
|
+
Methods are generated dynamically, which means `#methods` is your friend:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
manifest = IIIF::Presentation::Manifest.new()
|
43
|
+
puts manifest.methods(false)
|
44
|
+
> label=
|
45
|
+
> label
|
46
|
+
> description=
|
47
|
+
> description
|
48
|
+
> thumbnail=
|
49
|
+
> thumbnail
|
50
|
+
> attribution=
|
51
|
+
> attribution
|
52
|
+
> viewing_hint=
|
53
|
+
> viewingHint=
|
54
|
+
> viewing_hint
|
55
|
+
> viewingHint
|
56
|
+
[...]
|
57
|
+
```
|
58
|
+
|
59
|
+
Note that multi-word properties are implemented as snake_case (because this is
|
60
|
+
Ruby), but is serialized as camelCase. There are camelCase aliases for these.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
manifest = IIIF::Presentation::Manifest.new()
|
64
|
+
manifest.viewing_hint = 'paged'
|
65
|
+
puts manifest.to_json(pretty: true, force: true) # force: true skips validations
|
66
|
+
|
67
|
+
> {
|
68
|
+
> "@context": "http://iiif.io/api/presentation/2/context.json",
|
69
|
+
> "@type": "sc:Manifest",
|
70
|
+
> "viewingHint": "paged"
|
71
|
+
> }
|
72
|
+
|
73
|
+
```
|
74
|
+
|
75
|
+
## Parsing Existing Objects
|
76
|
+
|
77
|
+
Use `IIIF::Service#parse`. It will figure out what the object
|
78
|
+
should be, based on `@type`, and fall back to `ActiveSupport::OrderedHash` when
|
79
|
+
it can't e.g.:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
seed = '{
|
83
|
+
"@context": "http://iiif.io/api/presentation/2/context.json",
|
84
|
+
"@id": "http://example.com/manifest",
|
85
|
+
"@type": "sc:Manifest",
|
86
|
+
"label": "My Manifest",
|
87
|
+
"service": {
|
88
|
+
"@context": "http://iiif.io/api/image/2/context.json",
|
89
|
+
"@id":"http://www.example.org/images/book1-page1",
|
90
|
+
"profile":"http://iiif.io/api/image/2/profiles/level2.json"
|
91
|
+
},
|
92
|
+
"seeAlso": {
|
93
|
+
"@id": "http://www.example.org/library/catalog/book1.marc",
|
94
|
+
"format": "application/marc"
|
95
|
+
},
|
96
|
+
"sequences": [
|
97
|
+
{
|
98
|
+
"@id":"http://www.example.org/iiif/book1/sequence/normal",
|
99
|
+
"@type":"sc:Sequence",
|
100
|
+
"label":"Current Page Order",
|
101
|
+
"viewingDirection":"left-to-right",
|
102
|
+
"viewingHint":"paged",
|
103
|
+
"startCanvas": "http://www.example.org/iiif/book1/canvas/p2",
|
104
|
+
"canvases": [
|
105
|
+
{
|
106
|
+
"@id": "http://example.com/canvas",
|
107
|
+
"@type": "sc:Canvas",
|
108
|
+
"width": 10,
|
109
|
+
"height": 20,
|
110
|
+
"label": "My Canvas",
|
111
|
+
"otherContent": [
|
112
|
+
{
|
113
|
+
"@id": "http://example.com/content",
|
114
|
+
"@type":"sc:AnnotationList",
|
115
|
+
"motivation": "sc:painting"
|
116
|
+
}
|
117
|
+
]
|
118
|
+
}
|
119
|
+
]
|
120
|
+
}
|
121
|
+
]
|
122
|
+
}'
|
123
|
+
|
124
|
+
obj = IIIF::Service.parse(seed) # can also be a file path or a Hash
|
125
|
+
puts obj.class
|
126
|
+
puts obj.see_also.class
|
127
|
+
|
128
|
+
> IIIF::Presentation::Manifest
|
129
|
+
> ActiveSupport::OrderedHash
|
130
|
+
```
|
131
|
+
|
132
|
+
## Validation and Exceptions
|
133
|
+
|
134
|
+
This is work in progress. Right now exceptions are generally raised when you
|
135
|
+
try to set something to a type it should never be:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
manifest = IIIF::Presentation::Manifest.new
|
139
|
+
manifest.sequences = 'quux'
|
140
|
+
|
141
|
+
> [...] sequences must be an Array. (IIIF::Presentation::IllegalValueError)
|
142
|
+
```
|
143
|
+
|
144
|
+
and also if any required properties are missing when calling `to_json`
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
|
148
|
+
puts canvas.to_json(pretty: true)
|
149
|
+
|
150
|
+
> A(n) width is required for each IIIF::Presentation::Canvas (IIIF::Presentation::MissingRequiredKeyError)
|
151
|
+
```
|
152
|
+
|
153
|
+
but you can skip this validation by adding `force: true`:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
canvas = IIIF::Presentation::Canvas.new('@id' => 'http://example.com/canvas')
|
157
|
+
puts canvas.to_json(pretty: true, force: true)
|
158
|
+
|
159
|
+
> {
|
160
|
+
> "@context": "http://iiif.io/api/presentation/2/context.json",
|
161
|
+
> "@id": "http://example.com/canvas",
|
162
|
+
> "@type": "sc:Canvas"
|
163
|
+
> }
|
164
|
+
```
|
165
|
+
This all needs a bit of tidying up, finishing, and refactoring, so expect it to
|
166
|
+
change.
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
@@ -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
|
+
|