Skip to content

Commit be8430b

Browse files
committed
Specs: add metadata tests
1 parent d2ac373 commit be8430b

File tree

2 files changed

+226
-5
lines changed

2 files changed

+226
-5
lines changed

spec/features/metadata.rb

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
shared_examples :metadata do
2+
it 'allows instantiation using a :metadata option' do
3+
store = new_store(metadata: [:type, :name, :thing])
4+
expect(store.metadata_names).to include(:type, :name, :thing)
5+
end
6+
7+
context 'with metadata fields specified' do
8+
let(:store_with_metadata) { new_store(metadata: %i{type name}) }
9+
10+
it 'ignores unknown metadata keys' do
11+
store_with_metadata.store('x', 'y', metadata: {name: 'test', _unknown: 'test2'})
12+
expect(store_with_metadata.load('x', return_metadata: true)).not_to respond_to(:_unknown)
13+
end
14+
15+
describe '#create' do
16+
it 'allows storing metadata using the :metadata option' do
17+
expect(store_with_metadata.create('x', 'y', metadata: {name: 'test', type: 'thing'})).to be true
18+
expect(store_with_metadata.load('x', return_metadata: true).to_h).to match a_hash_including(name: 'test', type: 'thing', value: 'y')
19+
end
20+
end
21+
22+
describe '#delete' do
23+
it 'returns a struct containing the value and metadata if :return_metadata is true' do
24+
store_with_metadata.store('x', 'q', metadata: {name: 'testing'})
25+
struct = store_with_metadata.delete('x', return_metadata: true)
26+
27+
expect(store_with_metadata.key?('x')).to be false
28+
expect(struct).to be_a Struct
29+
expect(struct.to_h).to match a_hash_including(name: 'testing', value: 'q', type: nil)
30+
end
31+
end
32+
33+
describe '#fetch_values' do
34+
it 'returns an array of structs if :return_metadata is true' do
35+
store_with_metadata.store('x', '1', metadata: {name: 'test1'})
36+
store_with_metadata.store('y', '2', metadata: {name: 'test2'})
37+
38+
structs = store_with_metadata.fetch_values('x', 'y', 'z', return_metadata: true)
39+
expect(structs[0..1]).to all be_a Struct
40+
expect(structs[2]).to be nil
41+
expect(structs[0..1].map(&:to_h)).to match([
42+
a_hash_including(value: '1', name: 'test1', type: nil),
43+
a_hash_including(value: '2', name: 'test2', type: nil)
44+
])
45+
end
46+
47+
it 'yields missing keys to the block, and uses the return values with nil metadata' do
48+
store_with_metadata.store('x', '1', metadata: {name: 'test1'})
49+
store_with_metadata.store('y', '2', metadata: {name: 'test2'})
50+
51+
structs = store_with_metadata.fetch_values('x', 'y', 'z', return_metadata: true) do |key|
52+
expect(key).to eq 'z'
53+
'yielded value'
54+
end
55+
56+
expect(structs).to all be_a Struct
57+
expect(structs.map(&:to_h)).to match([
58+
a_hash_including(value: '1', name: 'test1', type: nil),
59+
a_hash_including(value: '2', name: 'test2', type: nil),
60+
a_hash_including(value: 'yielded value', name: nil, type: nil)
61+
])
62+
end
63+
end
64+
65+
describe '#load' do
66+
it 'returns a struct if the key is found and :return_metadata is true' do
67+
store_with_metadata.store('x', 'q', metadata: {name: 'testing'})
68+
struct = store_with_metadata.load('x', return_metadata: true)
69+
expect(struct).to be_a Struct
70+
expect(struct.to_h).to match a_hash_including(value: 'q', name: 'testing', type: nil)
71+
end
72+
73+
it 'returns nil if the key is not found and :return_metadata is true' do
74+
struct = store_with_metadata.load('x', return_metadata: true)
75+
expect(struct).to be nil
76+
end
77+
end
78+
79+
describe '#merge!' do
80+
it 'accepts a :metadata option, which allows assigning the same metadata to all values' do
81+
store_with_metadata.merge!(
82+
{
83+
'x' => '1',
84+
'y' => '2'
85+
},
86+
metadata: {
87+
name: 'test1',
88+
type: 'q'
89+
}
90+
)
91+
92+
expect(store_with_metadata.values_at('x', 'y', return_metadata: true).map(&:to_h)).to match([
93+
a_hash_including(value: '1', name: 'test1', type: 'q'),
94+
a_hash_including(value: '2', name: 'test1', type: 'q')
95+
])
96+
end
97+
98+
it 'accepts a :yield_metadata option, which causes structs to be yielded to the given block' do
99+
block = double('block')
100+
101+
expect(block).to receive(:call) do |key, old_struct, new_struct|
102+
expect(key).to eq('x')
103+
expect([old_struct, new_struct]).to all be_a Struct
104+
expect(old_struct.to_h).to match a_hash_including(value: '3', name: 'some test', type: nil)
105+
expect(new_struct.to_h).to match a_hash_including(value: '1', name: nil, type: nil)
106+
107+
new_struct.type = 'test'
108+
new_struct
109+
end
110+
111+
store_with_metadata.store('x', '3', metadata: {name: 'some test'})
112+
expect(store_with_metadata.merge!(
113+
{
114+
'x' => '1',
115+
'y' => '2'
116+
},
117+
yield_metadata: true,
118+
&block.method(:call)
119+
)).to be store_with_metadata
120+
121+
expect(store_with_metadata.load('x', return_metadata: true).to_h).to include(name: nil, type: 'test')
122+
end
123+
124+
it 'accepts both :metadata and :yield_metadata options together' do
125+
block = double('block')
126+
127+
expect(block).to receive(:call) do |key, old_struct, new_struct|
128+
expect(key).to eq('x')
129+
expect([old_struct, new_struct]).to all be_a Struct
130+
expect(old_struct.to_h).to match a_hash_including(value: '3', name: 'some test', type: nil)
131+
expect(new_struct.to_h).to match a_hash_including(value: '1', name: 'testing', type: 'z')
132+
133+
new_struct.type = 'r'
134+
new_struct
135+
end
136+
137+
store_with_metadata.store('x', '3', metadata: {name: 'some test'})
138+
expect(store_with_metadata.merge!(
139+
{
140+
'x' => '1',
141+
'y' => '2'
142+
},
143+
metadata: {
144+
name: 'testing',
145+
type: 'z'
146+
},
147+
yield_metadata: true,
148+
&block.method(:call)
149+
)).to be store_with_metadata
150+
151+
expect(store_with_metadata.load('x', return_metadata: true).to_h).to include(name: 'testing', type: 'r')
152+
end
153+
end
154+
155+
describe '#slice' do
156+
it 'accepts a :return_metadata option, which causes structs to be returned' do
157+
store_with_metadata.store('x', '1', metadata: { name: 'test x', type: 'w' })
158+
store_with_metadata.store('y', '2', metadata: { name: 'test y', type: 'z' })
159+
160+
pairs = store_with_metadata.slice('x', 'y', 'z', return_metadata: true)
161+
hash = Hash[pairs]
162+
163+
expect(hash.values).to all be_a Struct
164+
expect(hash.transform_values(&:to_h)).to match(
165+
'x' => a_hash_including(
166+
value: '1',
167+
name: 'test x',
168+
type: 'w'
169+
),
170+
'y' => a_hash_including(
171+
value: '2',
172+
name: 'test y',
173+
type: 'z'
174+
)
175+
)
176+
end
177+
end
178+
179+
describe '#store' do
180+
it 'accepts a :metadata option, allowing metadata to be stored' do
181+
expect(store_with_metadata.store('x', '1', metadata: { name: 'test' })).to eq '1'
182+
expect(store_with_metadata.load('x', return_metadata: true).to_h).to match a_hash_including(
183+
value: '1',
184+
name: 'test',
185+
type: nil
186+
)
187+
end
188+
189+
it 'accepts a :return_metadata option, casuing metadata to be returned in a struct' do
190+
struct = store_with_metadata.store('x', '1', return_metadata: true)
191+
expect(struct).to be_a Struct
192+
expect(struct.to_h).to match a_hash_including(
193+
value: '1',
194+
name: nil,
195+
type: nil
196+
)
197+
end
198+
end
199+
200+
describe '#values_at' do
201+
it 'returns an array of structs if :return_metadata is true' do
202+
store_with_metadata.store('x', '1', metadata: {name: 'test1'})
203+
store_with_metadata.store('y', '2', metadata: {name: 'test2'})
204+
205+
structs = store_with_metadata.values_at('x', 'y', 'z', return_metadata: true)
206+
expect(structs[0..1]).to all be_a Struct
207+
expect(structs[2]).to be nil
208+
expect(structs[0..1].map(&:to_h)).to match([
209+
a_hash_including(value: '1', name: 'test1', type: nil),
210+
a_hash_including(value: '2', name: 'test2', type: nil)
211+
])
212+
end
213+
end
214+
end
215+
end

spec/helper.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class MonetaSpecs
8383

8484
def initialize(options = {})
8585
@specs = options.delete(:specs).to_a
86-
@features = @specs & [:expires, :expires_native, :increment, :each_key, :create]
86+
@features = @specs & [:expires, :expires_native, :increment, :each_key, :create, :metadata]
8787
@key = options.delete(:key) || %w(object string binary hash boolean nil integer float)
8888
@value = options.delete(:value) || %w(object string binary hash boolean nil integer float)
8989
end
@@ -206,6 +206,10 @@ def with_each_key
206206
def without_create
207207
new(specs: specs - [:create, :concurrent_create, :create_expires] + [:not_create])
208208
end
209+
210+
def with_metadata
211+
new(specs: specs | %i(metadata))
212+
end
209213
end
210214

211215
ADAPTER_SPECS = MonetaSpecs.new(
@@ -229,9 +233,11 @@ module ClassMethods
229233

230234
def moneta_store store_name, options={}, &block
231235
name = self.description
232-
builder = proc do
236+
builder = proc do |**proc_options|
233237
if block
234-
options = instance_exec(&block)
238+
options = instance_exec(**proc_options, &block)
239+
else
240+
options.merge!(proc_options)
235241
end
236242

237243
Moneta.new(store_name, options.merge(logger: {file: File.join(tempdir, "#{name}.log")}))
@@ -297,8 +303,8 @@ def tempdir
297303
@moneta_tempdir ||= Dir.mktmpdir
298304
end
299305

300-
def new_store
301-
instance_eval(&@moneta_store_builder)
306+
def new_store(**options)
307+
instance_exec(**options, &@moneta_store_builder)
302308
end
303309

304310
def store

0 commit comments

Comments
 (0)