osullivan 0.0.2
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 +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
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
describe IIIF::Service do
|
5
|
+
|
6
|
+
let(:fixtures_dir) { File.join(File.dirname(__FILE__), '../../fixtures') }
|
7
|
+
let(:manifest_from_spec_path) { File.join(fixtures_dir, 'manifests/complete_from_spec.json') }
|
8
|
+
|
9
|
+
describe 'self.parse' do
|
10
|
+
it 'works from a file' do
|
11
|
+
s = described_class.parse(manifest_from_spec_path)
|
12
|
+
expect(s['label']).to eq 'Book 1'
|
13
|
+
end
|
14
|
+
it 'works from a string of JSON' do
|
15
|
+
file = File.open(manifest_from_spec_path, 'rb')
|
16
|
+
json_string = file.read
|
17
|
+
file.close
|
18
|
+
s = described_class.parse(json_string)
|
19
|
+
expect(s['label']).to eq 'Book 1'
|
20
|
+
end
|
21
|
+
describe 'works from a hash' do
|
22
|
+
it 'plain old' do
|
23
|
+
h = JSON.parse(IO.read(manifest_from_spec_path))
|
24
|
+
s = described_class.parse(h)
|
25
|
+
expect(s['label']).to eq 'Book 1'
|
26
|
+
end
|
27
|
+
it 'ActiveSupport::OrderedHash' do
|
28
|
+
h = JSON.parse(IO.read(manifest_from_spec_path))
|
29
|
+
oh = ActiveSupport::OrderedHash[h]
|
30
|
+
s = described_class.parse(oh)
|
31
|
+
expect(s['label']).to eq 'Book 1'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
it 'turns camels to snakes' do
|
35
|
+
s = described_class.parse(manifest_from_spec_path)
|
36
|
+
expect(s.keys.include?('see_also')).to be_truthy
|
37
|
+
expect(s.keys.include?('seeAlso')).to be_falsey
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'self#from_ordered_hash' do
|
42
|
+
let(:fixture) { JSON.parse('{
|
43
|
+
"@context": "http://iiif.io/api/presentation/2/context.json",
|
44
|
+
"@id": "http://example.com/manifest",
|
45
|
+
"@type": "sc:Manifest",
|
46
|
+
"label": "My Manifest",
|
47
|
+
"service": {
|
48
|
+
"@context": "http://iiif.io/api/image/2/context.json",
|
49
|
+
"@id":"http://www.example.org/images/book1-page1",
|
50
|
+
"profile":"http://iiif.io/api/image/2/profiles/level2.json"
|
51
|
+
},
|
52
|
+
"some_other_thing": {
|
53
|
+
"foo" : "bar"
|
54
|
+
},
|
55
|
+
"seeAlso": {
|
56
|
+
"@id": "http://www.example.org/library/catalog/book1.marc",
|
57
|
+
"format": "application/marc"
|
58
|
+
},
|
59
|
+
"sequences": [
|
60
|
+
{
|
61
|
+
"@id":"http://www.example.org/iiif/book1/sequence/normal",
|
62
|
+
"@type":"sc:Sequence",
|
63
|
+
"label":"Current Page Order",
|
64
|
+
|
65
|
+
"viewingDirection":"left-to-right",
|
66
|
+
"viewingHint":"paged",
|
67
|
+
"startCanvas": "http://www.example.org/iiif/book1/canvas/p2",
|
68
|
+
|
69
|
+
"canvases": [
|
70
|
+
{
|
71
|
+
"@id": "http://example.com/canvas",
|
72
|
+
"@type": "sc:Canvas",
|
73
|
+
"width": 10,
|
74
|
+
"height": 20,
|
75
|
+
"label": "My Canvas",
|
76
|
+
"otherContent": [
|
77
|
+
{
|
78
|
+
"@id": "http://example.com/content",
|
79
|
+
"@type":"sc:AnnotationList",
|
80
|
+
"motivation": "sc:painting"
|
81
|
+
}
|
82
|
+
]
|
83
|
+
}
|
84
|
+
]
|
85
|
+
}
|
86
|
+
]
|
87
|
+
}')
|
88
|
+
}
|
89
|
+
it 'doesn\'t raise a NoMethodError when we check the keys' do
|
90
|
+
expect { described_class.from_ordered_hash(fixture) }.to_not raise_error
|
91
|
+
end
|
92
|
+
it 'turns the fixture into a Manifest instance' do
|
93
|
+
expected_klass = IIIF::Presentation::Manifest
|
94
|
+
parsed = described_class.from_ordered_hash(fixture)
|
95
|
+
expect(parsed.class).to be expected_klass
|
96
|
+
end
|
97
|
+
it 'turns keys without "@type" into an OrderedHash' do
|
98
|
+
expected_klass = ActiveSupport::OrderedHash
|
99
|
+
parsed = described_class.from_ordered_hash(fixture)
|
100
|
+
expect(parsed['some_other_thing'].class).to be expected_klass
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'turns services into Services' do
|
104
|
+
expected_klass = IIIF::Service
|
105
|
+
parsed = described_class.from_ordered_hash(fixture)
|
106
|
+
expect(parsed['service'].class).to be expected_klass
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'round-trips' do
|
110
|
+
fp = '/tmp/osullivan-spec.json'
|
111
|
+
parsed = described_class.from_ordered_hash(fixture)
|
112
|
+
File.open(fp,'w') do |f|
|
113
|
+
f.write(parsed.to_json)
|
114
|
+
end
|
115
|
+
from_file = IIIF::Service.parse('/tmp/osullivan-spec.json')
|
116
|
+
File.delete(fp)
|
117
|
+
# is this sufficient?
|
118
|
+
expect(parsed.to_ordered_hash.to_a - from_file.to_ordered_hash.to_a).to eq []
|
119
|
+
expect(from_file.to_ordered_hash.to_a - parsed.to_ordered_hash.to_a).to eq []
|
120
|
+
end
|
121
|
+
it 'turns each memeber of "sequences" into an instance of Sequence' do
|
122
|
+
expected_klass = IIIF::Presentation::Sequence
|
123
|
+
parsed = described_class.from_ordered_hash(fixture)
|
124
|
+
parsed['sequences'].each do |s|
|
125
|
+
expect(s.class).to be expected_klass
|
126
|
+
end
|
127
|
+
end
|
128
|
+
it 'turns each member of sequences/canvaes in an instance of Canvas' do
|
129
|
+
expected_klass = IIIF::Presentation::Canvas
|
130
|
+
parsed = described_class.from_ordered_hash(fixture)
|
131
|
+
parsed['sequences'].each do |s|
|
132
|
+
s.canvases.each do |c|
|
133
|
+
expect(c.class).to be expected_klass
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
it 'turns the keys into snakes' do
|
138
|
+
expect(described_class.from_ordered_hash(fixture).has_key?('seeAlso')).to be_falsey
|
139
|
+
expect(described_class.from_ordered_hash(fixture).has_key?('see_also')).to be_truthy
|
140
|
+
end
|
141
|
+
it 'copies over plain-old key-values' do
|
142
|
+
parsed = described_class.from_ordered_hash(fixture)
|
143
|
+
expect(parsed['label']).to eq 'My Manifest'
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#to_ordered_hash' do
|
149
|
+
let(:logo_uri) { 'http://www.example.org/logos/institution1.jpg' }
|
150
|
+
let(:within_uri) { 'http://www.example.org/collections/books/' }
|
151
|
+
let(:see_also) { 'http://www.example.org/library/catalog/book1.xml' }
|
152
|
+
|
153
|
+
describe 'it puts the json-ld keys at the top' do
|
154
|
+
let(:extra_props) { [
|
155
|
+
['label','foo'],
|
156
|
+
['logo','http://example.com/logo.jpg'],
|
157
|
+
['within','http://example.com/something']
|
158
|
+
] }
|
159
|
+
let(:sorted_ld_keys) {
|
160
|
+
subject.keys.select { |k| k.start_with?('@') }.sort!
|
161
|
+
}
|
162
|
+
before(:each) {
|
163
|
+
extra_props.reverse.each do |k,v|
|
164
|
+
subject.unshift(k,v)
|
165
|
+
end
|
166
|
+
}
|
167
|
+
|
168
|
+
it 'by default' do
|
169
|
+
(0..extra_props.length-1).each do |i|
|
170
|
+
expect(subject.keys[i]).to eq(extra_props[i][0])
|
171
|
+
end
|
172
|
+
oh = subject.to_ordered_hash
|
173
|
+
(0..sorted_ld_keys.length-1).each do |i|
|
174
|
+
expect(oh.keys[i]).to eq(sorted_ld_keys[i])
|
175
|
+
end
|
176
|
+
end
|
177
|
+
it 'unless you say not to' do
|
178
|
+
(0..extra_props.length-1).each do |i|
|
179
|
+
expect(subject.keys[i]).to eq(extra_props[i][0])
|
180
|
+
end
|
181
|
+
oh = subject.to_ordered_hash(sort_json_ld_keys: false)
|
182
|
+
(0..extra_props.length-1).each do |i|
|
183
|
+
expect(oh.keys[i]).to eq(extra_props[i][0])
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'removes empty keys' do
|
189
|
+
it 'if they\'re arrays' do
|
190
|
+
subject['logo'] = logo_uri
|
191
|
+
subject['within'] = []
|
192
|
+
ordered_hash = subject.to_ordered_hash
|
193
|
+
expect(ordered_hash.has_key?('within')).to be_falsey
|
194
|
+
end
|
195
|
+
it 'if they\'re nil' do
|
196
|
+
subject['logo'] = logo_uri
|
197
|
+
subject['within'] = nil
|
198
|
+
ordered_hash = subject.to_ordered_hash
|
199
|
+
expect(ordered_hash.has_key?('within')).to be_falsey
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'converts snake_case keys to camelCase' do
|
204
|
+
subject['see_also'] = logo_uri
|
205
|
+
subject['within'] = within_uri
|
206
|
+
ordered_hash = subject.to_ordered_hash
|
207
|
+
expect(ordered_hash.keys.include?('seeAlso')).to be_truthy
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'iiif/presentation'
|
2
|
+
require 'coveralls'
|
3
|
+
Dir["#{File.dirname(__FILE__)}/unit/iiif/presentation/shared_examples/*.rb"].each do |f|
|
4
|
+
require f
|
5
|
+
end
|
6
|
+
require 'vcr'
|
7
|
+
|
8
|
+
VCR.configure do |c|
|
9
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
10
|
+
c.hook_into :webmock
|
11
|
+
c.configure_rspec_metadata!
|
12
|
+
end
|
13
|
+
|
14
|
+
Coveralls.wear!
|
15
|
+
|
16
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
17
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
18
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
19
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
20
|
+
#
|
21
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
22
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
23
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
24
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
25
|
+
# a separate helper file that requires the additional dependencies and performs
|
26
|
+
# the additional setup, and require it from the spec files that actually need it.
|
27
|
+
#
|
28
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
29
|
+
# users commonly want.
|
30
|
+
#
|
31
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
32
|
+
RSpec.configure do |config|
|
33
|
+
# rspec-expectations config goes here. You can use an alternate
|
34
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
35
|
+
# assertions if you prefer.
|
36
|
+
config.expect_with :rspec do |expectations|
|
37
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
38
|
+
# and `failure_message` of custom matchers include text for helper methods
|
39
|
+
# defined using `chain`, e.g.:
|
40
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
41
|
+
# # => "be bigger than 2 and smaller than 4"
|
42
|
+
# ...rather than:
|
43
|
+
# # => "be bigger than 2"
|
44
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
45
|
+
end
|
46
|
+
|
47
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
48
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
49
|
+
config.mock_with :rspec do |mocks|
|
50
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
51
|
+
# a real object. This is generally recommended, and will default to
|
52
|
+
# `true` in RSpec 4.
|
53
|
+
mocks.verify_partial_doubles = true
|
54
|
+
end
|
55
|
+
|
56
|
+
# The settings below are suggested to provide a good initial experience
|
57
|
+
# with RSpec, but feel free to customize to your heart's content.
|
58
|
+
=begin
|
59
|
+
# These two settings work together to allow you to limit a spec run
|
60
|
+
# to individual examples or groups you care about by tagging them with
|
61
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
62
|
+
# get run.
|
63
|
+
config.filter_run :focus
|
64
|
+
config.run_all_when_everything_filtered = true
|
65
|
+
|
66
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
67
|
+
# For more details, see:
|
68
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
69
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
70
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
71
|
+
config.disable_monkey_patching!
|
72
|
+
|
73
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
74
|
+
# be too noisy due to issues in dependencies.
|
75
|
+
config.warnings = true
|
76
|
+
|
77
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
78
|
+
# file, and it's useful to allow more verbose output when running an
|
79
|
+
# individual spec file.
|
80
|
+
if config.files_to_run.one?
|
81
|
+
# Use the documentation formatter for detailed output,
|
82
|
+
# unless a formatter has already been configured
|
83
|
+
# (e.g. via a command-line flag).
|
84
|
+
config.default_formatter = 'doc'
|
85
|
+
end
|
86
|
+
|
87
|
+
# Print the 10 slowest examples and example groups at the
|
88
|
+
# end of the spec run, to help surface which specs are running
|
89
|
+
# particularly slow.
|
90
|
+
config.profile_examples = 10
|
91
|
+
|
92
|
+
# Run specs in random order to surface order dependencies. If you find an
|
93
|
+
# order dependency and want to debug it, you can fix the order by providing
|
94
|
+
# the seed, which is printed after each run.
|
95
|
+
# --seed 1234
|
96
|
+
config.order = :random
|
97
|
+
|
98
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
99
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
100
|
+
# test failures related to randomization by passing the same `--seed` value
|
101
|
+
# as the one that triggered the failure.
|
102
|
+
Kernel.srand config.seed
|
103
|
+
=end
|
104
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
describe ActiveSupport::OrderedHash do
|
2
|
+
|
3
|
+
describe '#camelize_keys' do
|
4
|
+
before(:each) do
|
5
|
+
@uri = 'http://www.example.org/descriptions/book1.xml'
|
6
|
+
@within_uri = 'http://www.example.org/collections/books/'
|
7
|
+
subject['see_also'] = @uri
|
8
|
+
subject['within'] = @within_uri
|
9
|
+
end
|
10
|
+
it 'changes snake_case keys to camelCase' do
|
11
|
+
subject.camelize_keys # #send gets past protection
|
12
|
+
expect(subject.keys.include?('seeAlso')).to be_truthy
|
13
|
+
expect(subject.keys.include?('see_also')).to be_falsey
|
14
|
+
end
|
15
|
+
it 'keeps the right values' do
|
16
|
+
subject.camelize_keys
|
17
|
+
expect(subject['seeAlso']).to eq @uri
|
18
|
+
expect(subject['within']).to eq @within_uri
|
19
|
+
end
|
20
|
+
it 'keeps things in the same position' do
|
21
|
+
see_also_position = subject.keys.index('see_also')
|
22
|
+
within_position = subject.keys.index('within')
|
23
|
+
subject.camelize_keys
|
24
|
+
expect(subject.keys[see_also_position]).to eq 'seeAlso'
|
25
|
+
expect(subject.keys[within_position]).to eq 'within'
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#snakeize_keys' do
|
31
|
+
before(:each) do
|
32
|
+
@uri = 'http://www.example.org/descriptions/book1.xml'
|
33
|
+
@within_uri = 'http://www.example.org/collections/books/'
|
34
|
+
subject['seeAlso'] = @uri
|
35
|
+
subject['within'] = @within_uri
|
36
|
+
end
|
37
|
+
it 'changes camelCase keys to snake_case' do
|
38
|
+
subject.snakeize_keys
|
39
|
+
expect(subject.keys.include?('see_also')).to be_truthy
|
40
|
+
expect(subject.keys.include?('seeAlso')).to be_falsey
|
41
|
+
end
|
42
|
+
it 'keeps the right values' do
|
43
|
+
subject.snakeize_keys
|
44
|
+
expect(subject['see_also']).to eq @uri
|
45
|
+
expect(subject['within']).to eq @within_uri
|
46
|
+
end
|
47
|
+
it 'keeps things in the same position' do
|
48
|
+
see_also_position = subject.keys.index('seeAlso')
|
49
|
+
within_position = subject.keys.index('within')
|
50
|
+
subject.snakeize_keys
|
51
|
+
expect(subject.keys[see_also_position]).to eq 'see_also'
|
52
|
+
expect(subject.keys[within_position]).to eq 'within'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'insertion patches' do
|
57
|
+
|
58
|
+
let (:init_data) { [ ['wubble', 'fred'], ['baz', 'qux'], ['grault','garply'] ] }
|
59
|
+
|
60
|
+
subject do
|
61
|
+
hsh = ActiveSupport::OrderedHash.new
|
62
|
+
init_data.each { |e| hsh[e[0]] = e[1] }
|
63
|
+
hsh
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#insert' do
|
67
|
+
it 'inserts as expected' do
|
68
|
+
subject.insert(2, 'quux', 'corge')
|
69
|
+
expect(subject[subject.keys[0]]).to eq 'fred'
|
70
|
+
expect(subject[subject.keys[1]]).to eq 'qux'
|
71
|
+
expect(subject[subject.keys[2]]).to eq 'corge'
|
72
|
+
expect(subject[subject.keys[3]]).to eq 'garply'
|
73
|
+
end
|
74
|
+
it 'returns the instance' do
|
75
|
+
expect(subject.insert(1, 'quux','corge')).to eq subject
|
76
|
+
end
|
77
|
+
it 'raises IndexError if a negative index is too small' do
|
78
|
+
expect { subject.insert(-5, 'quux','corge') }.to raise_error IndexError
|
79
|
+
end
|
80
|
+
it 'puts index -1 on the end' do
|
81
|
+
subject.insert(-1, 'thud','wibble')
|
82
|
+
expect(subject[subject.keys.last]).to eq 'wibble'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#insert_before' do
|
87
|
+
it 'inserts in the expected place with a supplied key' do
|
88
|
+
subject.insert_before(existing_key: 'grault', new_key: 'quux', value: 'corge')
|
89
|
+
expect(subject.keys).to eq ['wubble','baz','quux','grault']
|
90
|
+
end
|
91
|
+
it 'inserts in the expected place with a supplied block' do
|
92
|
+
subject.insert_before(new_key: 'quux', value: 'corge') { |k,v| k.start_with?('g') }
|
93
|
+
expect(subject.keys).to eq ['wubble','baz','quux','grault']
|
94
|
+
end
|
95
|
+
it 'returns the instance' do
|
96
|
+
expect(subject.insert_before(existing_key: 'grault', new_key: 'quux', value: 'corge')).to be subject
|
97
|
+
end
|
98
|
+
describe 'raises KeyError' do
|
99
|
+
it 'when the supplied existing key is not found' do
|
100
|
+
expect { subject.insert_before(existing_key: 'foo', new_key: 'quux', value: 'corge') }.to raise_error KeyError
|
101
|
+
end
|
102
|
+
it 'when the supplied new key already exists' do
|
103
|
+
expect { subject.insert_before(existing_key: 'grault', new_key: 'wubble', value: 'corge') }.to raise_error KeyError
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#insert_after' do
|
109
|
+
it 'inserts in the expected place with a supplied key' do
|
110
|
+
subject.insert_after(existing_key: 'baz', new_key: 'quux', value: 'corge')
|
111
|
+
expect(subject.keys).to eq ['wubble','baz','quux','grault']
|
112
|
+
end
|
113
|
+
it 'inserts in the expected place with a supplied block' do
|
114
|
+
subject.insert_after(new_key: 'quux', value: 'corge') { |k,v| k.start_with?('g') }
|
115
|
+
expect(subject.keys).to eq ['wubble','baz','quux','grault']
|
116
|
+
end
|
117
|
+
it 'returns the instance' do
|
118
|
+
expect(subject.insert_after(existing_key: 'baz', new_key: 'quux', value: 'corge')).to be subject
|
119
|
+
end
|
120
|
+
describe 'raises KeyError' do
|
121
|
+
it 'when the supplied existing key is not found' do
|
122
|
+
expect { subject.insert_after(existing_key: 'foo', new_key: 'quux', value: 'corge') }.to raise_error KeyError
|
123
|
+
end
|
124
|
+
it 'when the supplied new key already exists' do
|
125
|
+
expect { subject.insert_after(existing_key: 'grault', new_key: 'wubble', value: 'corge') }.to raise_error KeyError
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '#unshift' do
|
131
|
+
it 'adds an entry to the front of the object' do
|
132
|
+
subject.unshift('thud','wibble')
|
133
|
+
expect(subject[subject.keys[0]]).to eq 'wibble'
|
134
|
+
end
|
135
|
+
it 'returns the instance' do
|
136
|
+
expect(subject.unshift('thud','wibble')).to be subject
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#remove_empties' do
|
141
|
+
it 'if they\'re arrays' do
|
142
|
+
subject[:wubble] = []
|
143
|
+
subject.remove_empties
|
144
|
+
expect(subject.has_key?(:wubble)).to be_falsey
|
145
|
+
end
|
146
|
+
it 'if they\'re nil' do
|
147
|
+
subject[:wubble] = nil
|
148
|
+
subject.remove_empties
|
149
|
+
expect(subject.has_key?(:wubble)).to be_falsey
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|