attr_pouch 0.0.1
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/.travis.yml +12 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +35 -0
- data/LICENSE +22 -0
- data/README.md +304 -0
- data/TODO +8 -0
- data/attr_pouch.gemspec +21 -0
- data/lib/attr_pouch/errors.rb +8 -0
- data/lib/attr_pouch/version.rb +3 -0
- data/lib/attr_pouch.rb +407 -0
- data/spec/attr_pouch_spec.rb +482 -0
- data/spec/spec_helper.rb +44 -0
- metadata +101 -0
@@ -0,0 +1,482 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AttrPouch do
|
4
|
+
def make_pouchy(field_name, opts={})
|
5
|
+
Class.new(Sequel::Model(:items)) do
|
6
|
+
include AttrPouch
|
7
|
+
|
8
|
+
pouch(:attrs) do
|
9
|
+
field field_name, opts
|
10
|
+
end
|
11
|
+
end.create
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with a simple attribute" do
|
15
|
+
let(:pouchy) { make_pouchy(:foo, type: String) }
|
16
|
+
|
17
|
+
it "generates getter and setter" do
|
18
|
+
pouchy.foo = 'bar'
|
19
|
+
expect(pouchy.foo).to eq('bar')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "clears on reload" do
|
23
|
+
pouchy.update(foo: 'bar')
|
24
|
+
expect(pouchy.foo).to eq('bar')
|
25
|
+
pouchy.foo = 'baz'
|
26
|
+
pouchy.reload
|
27
|
+
expect(pouchy.foo).to eq('bar')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "marks the field as modified" do
|
31
|
+
pouchy.foo = 'bar'
|
32
|
+
result = pouchy.save_changes
|
33
|
+
expect(result).to_not be_nil
|
34
|
+
pouchy.reload
|
35
|
+
expect(pouchy.foo).to eq('bar')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "avoids marking the field as modified if it is not changing" do
|
39
|
+
pouchy.foo = 'bar'
|
40
|
+
expect(pouchy.save_changes).to_not be_nil
|
41
|
+
pouchy.foo = 'bar'
|
42
|
+
expect(pouchy.save_changes).to be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it "requires the attribute to be present if read" do
|
46
|
+
expect { pouchy.foo }.to raise_error(AttrPouch::MissingRequiredFieldError)
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with nil values" do
|
50
|
+
let(:pouchy) { make_pouchy(:f1, type: :nil_hater) }
|
51
|
+
|
52
|
+
before do
|
53
|
+
AttrPouch.configure do |config|
|
54
|
+
config.encode(:nil_hater) { |f,v| v.nil? ? (raise ArgumentError) : v }
|
55
|
+
config.decode(:nil_hater) { |f,v| v.nil? ? (raise ArgumentError) : v }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "bypasses encoding" do
|
60
|
+
pouchy.update(f1: 'foo')
|
61
|
+
expect { pouchy.update(f1: nil) }.not_to raise_error
|
62
|
+
expect(pouchy.f1).to be_nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it "bypasses decoding" do
|
66
|
+
pouchy.update(attrs: Sequel.hstore(f1: nil))
|
67
|
+
expect { pouchy.f1 }.not_to raise_error
|
68
|
+
expect(pouchy.f1).to be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
it "still records the value as nil if not present when writing" do
|
72
|
+
pouchy.update(f1: nil)
|
73
|
+
expect(pouchy.attrs).to have_key(:f1)
|
74
|
+
expect(pouchy.attrs[:f1]).to be_nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with an integer attribute" do
|
80
|
+
let(:pouchy) { make_pouchy(:foo, type: Integer) }
|
81
|
+
|
82
|
+
it "preserves the type" do
|
83
|
+
pouchy.update(foo: 42)
|
84
|
+
pouchy.reload
|
85
|
+
expect(pouchy.foo).to eq(42)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "with a float attribute" do
|
90
|
+
let(:pouchy) { make_pouchy(:foo, type: Float) }
|
91
|
+
|
92
|
+
it "preserves the type" do
|
93
|
+
pouchy.update(foo: 2.78)
|
94
|
+
pouchy.reload
|
95
|
+
expect(pouchy.foo).to eq(2.78)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "with a boolean attribute" do
|
100
|
+
let(:pouchy) { make_pouchy(:foo, type: :bool) }
|
101
|
+
|
102
|
+
it "preserves the type" do
|
103
|
+
pouchy.update(foo: true)
|
104
|
+
pouchy.reload
|
105
|
+
expect(pouchy.foo).to be true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "with a Time attribute" do
|
110
|
+
let(:pouchy) { make_pouchy(:foo, type: Time) }
|
111
|
+
|
112
|
+
it "preserves the type" do
|
113
|
+
now = Time.now
|
114
|
+
pouchy.update(foo: now)
|
115
|
+
pouchy.reload
|
116
|
+
expect(pouchy.foo).to eq(now)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "with a Sequel::Model attribute" do
|
121
|
+
let(:model_class) { Class.new(Sequel::Model(:items)) }
|
122
|
+
let(:pouchy) { make_pouchy(:foo, type: model_class) }
|
123
|
+
|
124
|
+
it "preserves the type" do
|
125
|
+
new_model = model_class.create
|
126
|
+
pouchy.update(foo: new_model)
|
127
|
+
pouchy.reload
|
128
|
+
expect(pouchy.foo).to be_a(model_class)
|
129
|
+
expect(pouchy.foo.id).to eq(new_model.id)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "with a Sequel::Model attribute provided as a String" do
|
134
|
+
let(:model_class) { module A; class B < Sequel::Model(:items); end; end; A::B }
|
135
|
+
let(:pouchy) { make_pouchy(:foo, type: model_class.name) }
|
136
|
+
|
137
|
+
it "preserves the type" do
|
138
|
+
new_model = model_class.create
|
139
|
+
pouchy.update(foo: new_model)
|
140
|
+
pouchy.reload
|
141
|
+
expect(pouchy.foo).to be_a(model_class)
|
142
|
+
expect(pouchy.foo.id).to eq(new_model.id)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "with an attribute that is not a simple method name" do
|
147
|
+
it "raises an error when defining the class" do
|
148
|
+
expect do
|
149
|
+
make_pouchy(:"nope, not valid", type: String)
|
150
|
+
end.to raise_error(AttrPouch::InvalidFieldError)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "with an attribute name that ends in a question mark" do
|
155
|
+
let(:pouchy) { make_pouchy(:foo?, type: :bool) }
|
156
|
+
|
157
|
+
it "generates normal getter" do
|
158
|
+
pouchy.attrs = Sequel.hstore(foo?: true)
|
159
|
+
expect(pouchy.foo?).to be true
|
160
|
+
end
|
161
|
+
|
162
|
+
it "generates setter by stripping trailing question mark" do
|
163
|
+
pouchy.foo = true
|
164
|
+
expect(pouchy.foo?).to be true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "with multiple attributes" do
|
169
|
+
let(:bepouched) do
|
170
|
+
Class.new(Sequel::Model(:items)) do
|
171
|
+
include AttrPouch
|
172
|
+
|
173
|
+
pouch(:attrs) do
|
174
|
+
field :f1, type: String
|
175
|
+
field :f2, type: :bool
|
176
|
+
field :f3, type: Integer
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
let(:pouchy) { bepouched.create }
|
181
|
+
|
182
|
+
it "allows updating multiple attributes simultaneously" do
|
183
|
+
pouchy.update(f1: 'hello', f2: true, f3: 42)
|
184
|
+
expect(pouchy.f1).to eq('hello')
|
185
|
+
expect(pouchy.f2).to eq(true)
|
186
|
+
expect(pouchy.f3).to eq(42)
|
187
|
+
end
|
188
|
+
|
189
|
+
it "allows updating multiple attributes sequentially" do
|
190
|
+
pouchy.f1 = 'hello'
|
191
|
+
pouchy.f2 = true
|
192
|
+
pouchy.f3 = 42
|
193
|
+
pouchy.save_changes
|
194
|
+
pouchy.reload
|
195
|
+
expect(pouchy.f1).to eq('hello')
|
196
|
+
expect(pouchy.f2).to eq(true)
|
197
|
+
expect(pouchy.f3).to eq(42)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "with the default option" do
|
202
|
+
let(:pouchy) { make_pouchy(:foo, type: String, default: 'hello') }
|
203
|
+
|
204
|
+
it "returns the default if the key is absent" do
|
205
|
+
expect(pouchy.foo).to eq('hello')
|
206
|
+
end
|
207
|
+
|
208
|
+
it "returns the value if the key is present" do
|
209
|
+
pouchy.update(foo: 'goodbye')
|
210
|
+
expect(pouchy.foo).to eq('goodbye')
|
211
|
+
end
|
212
|
+
|
213
|
+
context "with the deletable option" do
|
214
|
+
let(:pouchy) { make_pouchy(:foo, type: String,
|
215
|
+
default: 'hello',
|
216
|
+
deletable: true) }
|
217
|
+
|
218
|
+
it "it returns the default if the key is absent" do
|
219
|
+
expect(pouchy.foo).to eq('hello')
|
220
|
+
end
|
221
|
+
|
222
|
+
it "it returns the default after the field has been deleted" do
|
223
|
+
pouchy.update(foo: 'goodbye')
|
224
|
+
expect(pouchy.foo).to eq('goodbye')
|
225
|
+
pouchy.delete_foo
|
226
|
+
expect(pouchy.foo).to eq('hello')
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "with the deletable option" do
|
232
|
+
let(:pouchy) { make_pouchy(:foo, type: Integer, deletable: true) }
|
233
|
+
|
234
|
+
it "is nil if the field is absent" do
|
235
|
+
expect(pouchy.foo).to be_nil
|
236
|
+
end
|
237
|
+
|
238
|
+
it "supports deleting existing fields" do
|
239
|
+
pouchy.update(foo: 42)
|
240
|
+
expect(pouchy.foo).to eq(42)
|
241
|
+
pouchy.delete_foo
|
242
|
+
expect(pouchy.attrs).not_to have_key(:foo)
|
243
|
+
pouchy.reload
|
244
|
+
expect(pouchy.foo).to eq(42)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "supports deleting existing fields and immediately persisting changes" do
|
248
|
+
pouchy.update(foo: 42)
|
249
|
+
expect(pouchy.foo).to eq(42)
|
250
|
+
pouchy.delete_foo!
|
251
|
+
expect(pouchy.attrs).not_to have_key(:foo)
|
252
|
+
pouchy.reload
|
253
|
+
expect(pouchy.attrs).not_to have_key(:foo)
|
254
|
+
end
|
255
|
+
|
256
|
+
it "ignores deleting absent fields" do
|
257
|
+
expect(pouchy.attrs).not_to have_key(:foo)
|
258
|
+
pouchy.delete_foo
|
259
|
+
expect(pouchy.attrs).not_to have_key(:foo)
|
260
|
+
end
|
261
|
+
|
262
|
+
it "also deletes aliases from the was option" do
|
263
|
+
pouchy = make_pouchy(:foo, type: Integer, deletable: true, was: :bar)
|
264
|
+
|
265
|
+
pouchy.update(attrs: Sequel.hstore(bar: 42))
|
266
|
+
expect(pouchy.foo).to eq(42)
|
267
|
+
pouchy.delete_foo
|
268
|
+
expect(pouchy.attrs).not_to have_key(:bar)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
context "with the mutable option" do
|
273
|
+
let(:pouchy) { make_pouchy(:foo, type: Integer, mutable: false) }
|
274
|
+
|
275
|
+
it "it allows setting the field value for the first time" do
|
276
|
+
pouchy.update(foo: 42)
|
277
|
+
end
|
278
|
+
|
279
|
+
it "forbids subsequent modifications to the field" do
|
280
|
+
pouchy.update(foo: 42)
|
281
|
+
expect do
|
282
|
+
pouchy.update(foo: 43)
|
283
|
+
end.to raise_error(AttrPouch::ImmutableFieldUpdateError)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "with the was option" do
|
288
|
+
let(:pouchy) { make_pouchy(:foo, type: String, was: %w(bar baz)) }
|
289
|
+
|
290
|
+
it "supports aliases for renaming fields" do
|
291
|
+
pouchy.update(attrs: Sequel.hstore(bar: 'hello'))
|
292
|
+
expect(pouchy.foo).to eq('hello')
|
293
|
+
end
|
294
|
+
|
295
|
+
it "supports multiple aliases" do
|
296
|
+
pouchy.update(attrs: Sequel.hstore(baz: 'hello'))
|
297
|
+
expect(pouchy.foo).to eq('hello')
|
298
|
+
end
|
299
|
+
|
300
|
+
it "deletes old names when writing the current one" do
|
301
|
+
pouchy.update(attrs: Sequel.hstore(bar: 'hello'))
|
302
|
+
pouchy.update(foo: 'goodbye')
|
303
|
+
expect(pouchy.attrs).not_to have_key(:bar)
|
304
|
+
end
|
305
|
+
|
306
|
+
it "supports a shorthand for the single-alias case" do
|
307
|
+
pouchy = make_pouchy(:foo, type: String, was: :bar)
|
308
|
+
pouchy.update(attrs: Sequel.hstore(bar: 'hello'))
|
309
|
+
expect(pouchy.foo).to eq('hello')
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context "with the raw_field option" do
|
314
|
+
let(:pouchy) { make_pouchy(:foo, type: Float, raw_field: :raw_foo) }
|
315
|
+
|
316
|
+
it "supports direct access to the encoded value" do
|
317
|
+
pouchy.update(foo: 2.78)
|
318
|
+
expect(pouchy.raw_foo).to eq('2.78')
|
319
|
+
end
|
320
|
+
|
321
|
+
it "is required when read" do
|
322
|
+
expect do
|
323
|
+
pouchy.raw_foo
|
324
|
+
end.to raise_error(AttrPouch::MissingRequiredFieldError)
|
325
|
+
end
|
326
|
+
|
327
|
+
it "avoids marking the field as modified if it is not changing" do
|
328
|
+
pouchy.raw_foo = 'bar'
|
329
|
+
expect(pouchy.save_changes).to_not be_nil
|
330
|
+
pouchy.raw_foo = 'bar'
|
331
|
+
expect(pouchy.save_changes).to be_nil
|
332
|
+
end
|
333
|
+
|
334
|
+
it "obeys the 'mutable' option" do
|
335
|
+
pouchy = make_pouchy(:foo, type: Float, raw_field: :raw_foo, mutable: false)
|
336
|
+
pouchy.update(foo: 42)
|
337
|
+
expect do
|
338
|
+
pouchy.update(foo: 43)
|
339
|
+
end.to raise_error(AttrPouch::ImmutableFieldUpdateError)
|
340
|
+
end
|
341
|
+
|
342
|
+
it "is nil when the 'default' option is present" do
|
343
|
+
pouchy = make_pouchy(:foo, type: Float, raw_field: :raw_foo, default: 7.2)
|
344
|
+
expect(pouchy.raw_foo).to be_nil
|
345
|
+
end
|
346
|
+
|
347
|
+
it "obeys the 'was' option when reading" do
|
348
|
+
pouchy = make_pouchy(:foo, type: String, raw_field: :raw_foo, was: :bar)
|
349
|
+
pouchy.attrs = Sequel.hstore(bar: 'hello')
|
350
|
+
expect(pouchy.raw_foo).to eq('hello')
|
351
|
+
end
|
352
|
+
|
353
|
+
it "obeys the 'was' option when writing" do
|
354
|
+
pouchy = make_pouchy(:foo, type: String, raw_field: :raw_foo, was: :bar)
|
355
|
+
pouchy.attrs = Sequel.hstore(bar: 'hello')
|
356
|
+
pouchy.update(raw_foo: 'goodbye')
|
357
|
+
expect(pouchy.attrs).not_to have_key(:bar)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
context "inferring field types" do
|
362
|
+
it "infers field named num_foo to be of type Integer" do
|
363
|
+
pouchy = make_pouchy(:num_foo)
|
364
|
+
pouchy.update(num_foo: 42)
|
365
|
+
expect(pouchy.num_foo).to eq(42)
|
366
|
+
end
|
367
|
+
|
368
|
+
it "infers field named foo_count to be of type Integer" do
|
369
|
+
pouchy = make_pouchy(:foo_count)
|
370
|
+
pouchy.update(foo_count: 42)
|
371
|
+
expect(pouchy.foo_count).to eq(42)
|
372
|
+
end
|
373
|
+
|
374
|
+
it "infers field named foo_size to be of type Integer" do
|
375
|
+
pouchy = make_pouchy(:foo_size)
|
376
|
+
pouchy.update(foo_size: 42)
|
377
|
+
expect(pouchy.foo_size).to eq(42)
|
378
|
+
end
|
379
|
+
|
380
|
+
it "infers field named foo? to be of type :bool" do
|
381
|
+
pouchy = make_pouchy(:foo?)
|
382
|
+
pouchy.update(foo: true)
|
383
|
+
expect(pouchy.foo?).to be true
|
384
|
+
end
|
385
|
+
|
386
|
+
it "infers field named foo_at to be of type Time" do
|
387
|
+
now = Time.now
|
388
|
+
pouchy = make_pouchy(:foo_at)
|
389
|
+
pouchy.update(foo_at: now)
|
390
|
+
expect(pouchy.foo_at).to eq(now)
|
391
|
+
end
|
392
|
+
|
393
|
+
it "infers field named foo_by to be of type Time" do
|
394
|
+
now = Time.now
|
395
|
+
pouchy = make_pouchy(:foo_by)
|
396
|
+
pouchy.update(foo_by: now)
|
397
|
+
expect(pouchy.foo_by).to eq(now)
|
398
|
+
end
|
399
|
+
|
400
|
+
it "infers field named foo to be of type String" do
|
401
|
+
pouchy = make_pouchy(:foo)
|
402
|
+
pouchy.update(foo: 'hello')
|
403
|
+
expect(pouchy.foo).to eq('hello')
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
context "with dataset methods" do
|
408
|
+
let(:bepouched) do
|
409
|
+
Class.new(Sequel::Model(:items)) do
|
410
|
+
include AttrPouch
|
411
|
+
|
412
|
+
pouch(:attrs) do
|
413
|
+
field :f1, type: String
|
414
|
+
field :f2, type: String
|
415
|
+
field :f3, type: String
|
416
|
+
field :f4, type: :rot13
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
def rot13(str)
|
422
|
+
str.each_byte.map do |c|
|
423
|
+
case c
|
424
|
+
when 'a'.ord..('z'.ord - 13)
|
425
|
+
c + 13
|
426
|
+
when ('z'.ord - 13)..'z'.ord
|
427
|
+
c - 13
|
428
|
+
end
|
429
|
+
end.map(&:chr).join
|
430
|
+
end
|
431
|
+
|
432
|
+
before do
|
433
|
+
AttrPouch.configure do |config|
|
434
|
+
config.encode(:rot13) { |f,v| rot13(v.to_s) }
|
435
|
+
config.decode(:rot13) { |f,v| rot13(v) }
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
it "finds the right item with a scalar field value" do
|
440
|
+
pouchy = bepouched.create(f1: 'foo', f2: 'bar', f3: 'baz')
|
441
|
+
bepouched.create(f1: 'bar', f2: 'foo', f3: 'baz') # *not* matching
|
442
|
+
matching = bepouched.where_pouch(:attrs, f1: 'foo').all
|
443
|
+
expect(matching.count).to eq(1)
|
444
|
+
match = matching.first
|
445
|
+
expect(match.id).to eq(pouchy.id)
|
446
|
+
end
|
447
|
+
|
448
|
+
it "finds the right item with an array field value" do
|
449
|
+
p1 = bepouched.create(f1: 'foo', f2: 'bar', f3: 'baz')
|
450
|
+
p2 = bepouched.create(f1: 'bar', f2: 'foo', f3: 'baz')
|
451
|
+
bepouched.create(f1: 'baz', f2: 'foo', f3: 'bar') # *not* matching
|
452
|
+
matching = bepouched.where_pouch(:attrs, f1: %w(foo bar)).all
|
453
|
+
expect(matching.count).to eq(2)
|
454
|
+
expect(matching.map(&:id)).to include(p1.id, p2.id)
|
455
|
+
end
|
456
|
+
|
457
|
+
it "finds the right item with a missing field value" do
|
458
|
+
p1 = bepouched.create(f2: 'bar', f3: 'baz')
|
459
|
+
bepouched.create(f1: '', f2: 'foo', f3: 'baz') # *not* matching
|
460
|
+
bepouched.create(f1: 'baz', f2: 'foo', f3: 'bar') # *not* matching
|
461
|
+
matching = bepouched.where_pouch(:attrs, f1: nil).all
|
462
|
+
expect(matching.count).to eq(1)
|
463
|
+
expect(matching.first.id).to eq(p1.id)
|
464
|
+
end
|
465
|
+
|
466
|
+
it "finds the right item with a nil field value" do
|
467
|
+
p1 = bepouched.create(attrs: Sequel.hstore(f1: nil))
|
468
|
+
matching = bepouched.where_pouch(:attrs, f1: nil).all
|
469
|
+
expect(matching.count).to eq(1)
|
470
|
+
expect(matching.first.id).to eq(p1.id)
|
471
|
+
end
|
472
|
+
|
473
|
+
it "uses the associated encoder for lookups" do
|
474
|
+
encoded = rot13('hello')
|
475
|
+
p1 = bepouched.create(f4: 'hello')
|
476
|
+
expect(p1.attrs[:f4]).to eq(encoded) # nothing behind the curtain
|
477
|
+
matching = bepouched.where_pouch(:attrs, f4: 'hello')
|
478
|
+
expect(matching.count).to eq(1)
|
479
|
+
expect(matching.first.id).to eq(p1.id)
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'bundler'
|
9
|
+
require 'attr_pouch'
|
10
|
+
|
11
|
+
conn = Sequel.connect(ENV['DATABASE_URL'])
|
12
|
+
conn.run 'CREATE EXTENSION IF NOT EXISTS "hstore"'
|
13
|
+
|
14
|
+
conn.extension :pg_hstore
|
15
|
+
Sequel.extension :pg_hstore_ops
|
16
|
+
|
17
|
+
conn.run 'CREATE EXTENSION IF NOT EXISTS "hstore"'
|
18
|
+
conn.run 'DROP TABLE IF EXISTS items'
|
19
|
+
conn.run <<-EOF
|
20
|
+
CREATE TABLE items(
|
21
|
+
id serial primary key,
|
22
|
+
attrs hstore default ''
|
23
|
+
)
|
24
|
+
EOF
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
config.run_all_when_everything_filtered = true
|
28
|
+
config.filter_run :focus
|
29
|
+
|
30
|
+
config.before(:example) do
|
31
|
+
conn.run 'TRUNCATE items'
|
32
|
+
end
|
33
|
+
|
34
|
+
# Run specs in random order to surface order dependencies. If you find an
|
35
|
+
# order dependency and want to debug it, you can fix the order by providing
|
36
|
+
# the seed, which is printed after each run.
|
37
|
+
# --seed 1234
|
38
|
+
config.order = 'random'
|
39
|
+
|
40
|
+
config.expect_with :rspec do |c|
|
41
|
+
c.syntax = :expect
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attr_pouch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maciek Sakrejda
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.18.3
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.18.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sequel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.13'
|
55
|
+
description: Schema-less attribute storage
|
56
|
+
email:
|
57
|
+
- m.sakrejda@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".travis.yml"
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
- TODO
|
68
|
+
- attr_pouch.gemspec
|
69
|
+
- lib/attr_pouch.rb
|
70
|
+
- lib/attr_pouch/errors.rb
|
71
|
+
- lib/attr_pouch/version.rb
|
72
|
+
- spec/attr_pouch_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
homepage: https://github.com/uhoh-itsmaciek/attr_pouch
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.4.5.1
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: Sequel plugin for schema-less attribute storage
|
98
|
+
test_files:
|
99
|
+
- spec/attr_pouch_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
has_rdoc:
|