marty 13.0.2 → 14.0.0
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 +4 -4
- data/.rubocop_todo.yml +48 -70
- data/CHANGELOG.md +26 -0
- data/Gemfile +2 -1
- data/app/components/marty/data_grid_view.rb +5 -0
- data/app/helpers/marty/application_helper.rb +1 -1
- data/app/models/marty/data_grid.rb +256 -49
- data/app/services/marty/data_grid/constraint.rb +2 -2
- data/db/migrate/603_add_strict_null_mode_to_data_grids.rb +5 -0
- data/lib/marty/content_handler.rb +3 -1
- data/lib/marty/railtie.rb +1 -0
- data/lib/marty/util.rb +27 -0
- data/lib/marty/version.rb +1 -1
- data/spec/models/data_grid_spec.rb +335 -21
- metadata +3 -2
@@ -19,7 +19,7 @@ module Marty
|
|
19
19
|
|
20
20
|
pt = 'infinity'
|
21
21
|
vals = raw_vals.map do |v|
|
22
|
-
DataGrid.parse_fvalue(pt, v, data_type, dt)
|
22
|
+
DataGrid.parse_fvalue(pt, v, data_type, dt, false)
|
23
23
|
end
|
24
24
|
[[:in?, vals.flatten]]
|
25
25
|
end
|
@@ -56,7 +56,7 @@ module Marty
|
|
56
56
|
err = nil
|
57
57
|
begin
|
58
58
|
cvt_val = cvt && !data_v.class.in?(rt) ?
|
59
|
-
[DataGrid.parse_fvalue(pt, data_v, dt, klass)].
|
59
|
+
[DataGrid.parse_fvalue(pt, data_v, dt, klass, false)].
|
60
60
|
flatten.first : data_v
|
61
61
|
rescue StandardError => e
|
62
62
|
err = e.message
|
@@ -6,6 +6,7 @@ module Marty::ContentHandler
|
|
6
6
|
'html' => ['text/html', 'download'],
|
7
7
|
'txt' => ['text/plain', 'inline'],
|
8
8
|
'json' => ['application/json', 'download'],
|
9
|
+
'pdf' => ['application/pdf', 'download'],
|
9
10
|
|
10
11
|
# hacky: default format is JSON
|
11
12
|
nil => ['application/json', 'download'],
|
@@ -29,7 +30,7 @@ module Marty::ContentHandler
|
|
29
30
|
res = to_zip(data)
|
30
31
|
when nil, 'json'
|
31
32
|
res, format = data.to_json, 'json'
|
32
|
-
when 'html'
|
33
|
+
when 'html', 'pdf'
|
33
34
|
res = data.to_s
|
34
35
|
else
|
35
36
|
res, format = { error: "Unknown format: #{format}" }.to_json, 'json'
|
@@ -92,6 +93,7 @@ module Marty::ContentHandler
|
|
92
93
|
res = Zip::OutputStream.write_buffer do |stream|
|
93
94
|
to_zip_stream(stream, [], data)
|
94
95
|
end
|
96
|
+
|
95
97
|
res.string
|
96
98
|
end
|
97
99
|
end
|
data/lib/marty/railtie.rb
CHANGED
data/lib/marty/util.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Marty::Util
|
2
|
+
extend Delorean::Functions
|
3
|
+
|
2
4
|
def self.set_posting_id(sid)
|
3
5
|
snap = Marty::Posting.find_by(id: sid)
|
4
6
|
sid = nil if snap && (snap.created_dt == Float::INFINITY)
|
@@ -56,6 +58,31 @@ module Marty::Util
|
|
56
58
|
res
|
57
59
|
end
|
58
60
|
|
61
|
+
# Returns an array of methods and values that can be applied to a number
|
62
|
+
# in order to check if it's in the given range.
|
63
|
+
# Example: '(1,14]') => [[">", 1.0], ["<=", 14.0]]
|
64
|
+
delorean_fn :pg_range_to_ruby, cache: true do |r|
|
65
|
+
next r if r == 'empty' || r.nil?
|
66
|
+
|
67
|
+
m = pg_range_match(r)
|
68
|
+
|
69
|
+
raise "bad PG range #{r}" unless m
|
70
|
+
|
71
|
+
res = []
|
72
|
+
|
73
|
+
if m[:start] != ''
|
74
|
+
op = m[:open] == '(' ? '>' : '>='
|
75
|
+
res += [[op, m[:start].to_f]]
|
76
|
+
end
|
77
|
+
|
78
|
+
if m[:end] != ''
|
79
|
+
op = m[:close] == ')' ? '<' : '<='
|
80
|
+
res += [[op, m[:end].to_f]]
|
81
|
+
end
|
82
|
+
|
83
|
+
res
|
84
|
+
end
|
85
|
+
|
59
86
|
def self.human_to_pg_range(r)
|
60
87
|
return r if r == 'empty'
|
61
88
|
|
data/lib/marty/version.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'benchmark/ips'
|
2
3
|
|
3
4
|
module Marty::DataGridSpec # rubocop:disable Metrics/ModuleLength
|
4
5
|
describe DataGrid do
|
@@ -187,11 +188,51 @@ Investor Services\t-0.625
|
|
187
188
|
NOT (Admin Premium Services|Admin Services|Admin Services Plus)\t-1.0
|
188
189
|
Admin Services Plus\t-1.625
|
189
190
|
Investor Services Acadamy\t-0.5
|
191
|
+
EOS
|
192
|
+
|
193
|
+
G1_with_nulls = <<EOS
|
194
|
+
strict_null_mode
|
195
|
+
state\tstring\tv\t\t
|
196
|
+
ltv\tnumrange\tv\t\t
|
197
|
+
fico\tnumrange\th\t\t
|
198
|
+
|
199
|
+
\t\t>=600<700\t>=700<750\t>=750
|
200
|
+
CA\t<=80\t1.1\t2.2\t3.3
|
201
|
+
TX|HI\t>80<=105\t4.4\t5.5\t6.6
|
202
|
+
NM\t<=80\t1.2\t2.3\t3.4
|
203
|
+
MA\t>80<=105\t4.5\t5.6\t
|
204
|
+
NULL\t<=80\t11\t22\t33
|
205
|
+
EOS
|
206
|
+
|
207
|
+
G1_with_bool_nulls = <<EOS
|
208
|
+
strict_null_mode
|
209
|
+
bool_state\tboolean\tv\t\t
|
210
|
+
ltv\tnumrange\tv\t\t
|
211
|
+
fico\tnumrange\th\t\t
|
212
|
+
|
213
|
+
\t\t>=600<700\t>=700<750\t>=750
|
214
|
+
f\t>80<=105\t4.5\t5.6\t
|
215
|
+
NULL\t<=80\t11\t22\t33
|
216
|
+
EOS
|
217
|
+
|
218
|
+
G1_with_integer_nulls = <<EOS
|
219
|
+
strict_null_mode
|
220
|
+
int_state\tinteger\tv\t\t
|
221
|
+
ltv\tnumrange\tv\t\t
|
222
|
+
fico\tnumrange\th\t\t
|
223
|
+
|
224
|
+
\t\t>=600<700\t>=700<750\t>=750
|
225
|
+
1\t<=80\t1.1\t2.2\t3.3
|
226
|
+
2\t>80<=105\t4.4\t5.5\t6.6
|
227
|
+
3\t<=80\t1.2\t2.3\t3.4
|
228
|
+
4|5\t>80<=105\t4.5\t5.6\t
|
229
|
+
NULL\t<=80\t11\t22\t33
|
190
230
|
EOS
|
191
231
|
|
192
232
|
before(:each) do
|
193
233
|
# Mcfly.whodunnit = Marty::User.find_by_login('marty')
|
194
234
|
marty_whodunnit
|
235
|
+
Rails.application.config.marty.data_grid_plpg_lookups = false
|
195
236
|
end
|
196
237
|
|
197
238
|
def lookup_grid_helper(pt, gridname, params, follow = false, distinct = true)
|
@@ -213,6 +254,110 @@ EOS
|
|
213
254
|
dg_from_import('Gh', Gh + "\t\t\n")
|
214
255
|
end.to raise_error(RuntimeError)
|
215
256
|
end
|
257
|
+
|
258
|
+
it 'show not allow import NULL fields unless strict_null_mode is on' do
|
259
|
+
expect do
|
260
|
+
dg_from_import(
|
261
|
+
'G1_with_nulls',
|
262
|
+
G1_with_nulls.gsub("strict_null_mode\n", '')
|
263
|
+
)
|
264
|
+
end.to raise_error(
|
265
|
+
/NULL is not supported in grids without strict_null_mode/
|
266
|
+
)
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'should import wildcards' do
|
270
|
+
dg = dg_from_import('G1', G1)
|
271
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'state' }
|
272
|
+
expect(state_attr['keys'].last).to be nil
|
273
|
+
expect(state_attr['wildcards'].last).to be true
|
274
|
+
expect(state_attr['wildcards']).to eq [false, false, false, false, true]
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'allows to import NULL values in string fields' do
|
278
|
+
dg = dg_from_import('G1_with_nulls', G1_with_nulls)
|
279
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'state' }
|
280
|
+
expect(state_attr['keys'].last).to be nil
|
281
|
+
expect(state_attr['wildcards'].last).to be false
|
282
|
+
|
283
|
+
# FIXME: do we actually need mixing nulls with values?
|
284
|
+
dg = dg_from_import(
|
285
|
+
'G1_with_nulls2',
|
286
|
+
G1_with_nulls.sub('NULL', 'NY|NULL')
|
287
|
+
)
|
288
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'state' }
|
289
|
+
expect(state_attr['keys'].last).to eq [nil, 'NY']
|
290
|
+
expect(state_attr['wildcards'].last).to be false
|
291
|
+
|
292
|
+
dg = dg_from_import(
|
293
|
+
'G1_with_nulls3',
|
294
|
+
G1_with_nulls.sub('NULL', 'NOT (NULL)')
|
295
|
+
)
|
296
|
+
|
297
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'state' }
|
298
|
+
expect(state_attr['keys'].last).to be nil
|
299
|
+
expect(state_attr['wildcards'].last).to be false
|
300
|
+
expect(state_attr['nots'].last).to be true
|
301
|
+
|
302
|
+
dg = dg_from_import(
|
303
|
+
'G1_with_nulls4',
|
304
|
+
G1_with_nulls.sub('NULL', 'NOT (NY|NULL)')
|
305
|
+
)
|
306
|
+
|
307
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'state' }
|
308
|
+
expect(state_attr['keys'].last).to eq [nil, 'NY']
|
309
|
+
expect(state_attr['wildcards'].last).to be false
|
310
|
+
expect(state_attr['nots'].last).to be true
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'allows to import NULL values in integer field' do
|
314
|
+
dg = dg_from_import('G1_with_integer_nulls', G1_with_integer_nulls)
|
315
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'int_state' }
|
316
|
+
expect(state_attr['keys'].last).to be nil
|
317
|
+
expect(state_attr['wildcards'].last).to be false
|
318
|
+
|
319
|
+
dg = dg_from_import(
|
320
|
+
'G1_with_integer_nulls2',
|
321
|
+
G1_with_integer_nulls.sub('NULL', '6|NULL')
|
322
|
+
)
|
323
|
+
|
324
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'int_state' }
|
325
|
+
expect(state_attr['keys'].last).to eq [nil, 6]
|
326
|
+
expect(state_attr['nots'].last).to be false
|
327
|
+
expect(state_attr['wildcards'].last).to be false
|
328
|
+
|
329
|
+
dg = dg_from_import(
|
330
|
+
'G1_with_integer_nulls3',
|
331
|
+
G1_with_integer_nulls.sub('NULL', 'NOT (NULL)')
|
332
|
+
)
|
333
|
+
|
334
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'int_state' }
|
335
|
+
expect(state_attr['keys'].last).to be nil
|
336
|
+
expect(state_attr['nots'].last).to be true
|
337
|
+
expect(state_attr['wildcards'].last).to be false
|
338
|
+
|
339
|
+
dg = dg_from_import(
|
340
|
+
'G1_with_integer_nulls4',
|
341
|
+
G1_with_integer_nulls.sub('NULL', 'NOT (6|NULL)')
|
342
|
+
)
|
343
|
+
|
344
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'int_state' }
|
345
|
+
expect(state_attr['keys'].last).to eq [nil, 6]
|
346
|
+
expect(state_attr['nots'].last).to be true
|
347
|
+
expect(state_attr['wildcards'].last).to be false
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'allows to import NULL values in boolean field' do
|
351
|
+
dg = dg_from_import('G1_with_bool_nulls', G1_with_bool_nulls)
|
352
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'bool_state' }
|
353
|
+
expect(state_attr['keys'].last).to be nil
|
354
|
+
expect(state_attr['wildcards'].last).to be false
|
355
|
+
|
356
|
+
dg = dg_from_import('G1_with_bool_nulls2', G1_with_bool_nulls.sub('NULL', 'NOT (NULL)'))
|
357
|
+
state_attr = dg.metadata.find { |key| key['attr'] == 'bool_state' }
|
358
|
+
expect(state_attr['keys'].last).to be nil
|
359
|
+
expect(state_attr['nots'].last).to be true
|
360
|
+
end
|
216
361
|
end
|
217
362
|
|
218
363
|
describe 'validations' do
|
@@ -306,7 +451,7 @@ EOS
|
|
306
451
|
|
307
452
|
before(:each) do
|
308
453
|
%w[G1 G2 G3 G4 G5 G6 G7 G8 Ga Gb
|
309
|
-
Gc Gd Ge Gf Gg Gh Gj Gl].each do |g|
|
454
|
+
Gc Gd Ge Gf Gg Gh Gj Gl G1_with_nulls].each do |g|
|
310
455
|
dg_from_import(g, "Marty::DataGridSpec::#{g}".constantize)
|
311
456
|
end
|
312
457
|
end
|
@@ -351,6 +496,24 @@ EOS
|
|
351
496
|
end
|
352
497
|
end
|
353
498
|
|
499
|
+
it 'should cast types' do
|
500
|
+
res = Marty::DataGrid.lookup_grid_h(pt, 'Gf', { 'i' => 13, 'n' => 15 }, true)
|
501
|
+
expect(res).to eq('N')
|
502
|
+
|
503
|
+
res = Marty::DataGrid.lookup_grid_h(pt, 'Gf', { 'i' => '13', 'n' => '15' }, true)
|
504
|
+
expect(res).to eq('N')
|
505
|
+
|
506
|
+
res = Marty::DataGrid.lookup_grid_h(pt, 'Gf', { 'b' => 'true', 'i4' => '6' }, false)
|
507
|
+
expect(res).to eq('Y')
|
508
|
+
|
509
|
+
res = Marty::DataGrid.lookup_grid_h(pt, 'Gg', { 'i1' => 2, 'i2' => 1 }, false)
|
510
|
+
expect(res).to eq(1)
|
511
|
+
|
512
|
+
dg_from_import('G9', G9)
|
513
|
+
res = Marty::DataGrid.lookup_grid_h(pt, 'G9', { 'state' => 4, 'ltv' => 81 }, false)
|
514
|
+
expect(res).to eq(456)
|
515
|
+
end
|
516
|
+
|
354
517
|
it 'should handle ambiguous lookups' do
|
355
518
|
h1 = {
|
356
519
|
'property_state' => 'NY',
|
@@ -454,7 +617,7 @@ EOS
|
|
454
617
|
'ltv' => 100,
|
455
618
|
'cltv' => 110.1,
|
456
619
|
)
|
457
|
-
end.to raise_error(RuntimeError)
|
620
|
+
end.to raise_error(RuntimeError, /matches > 1/)
|
458
621
|
end
|
459
622
|
|
460
623
|
it 'should return nil when matching data grid cell is nil' do
|
@@ -474,9 +637,81 @@ EOS
|
|
474
637
|
'state' => 'GU',
|
475
638
|
'ltv' => 80,
|
476
639
|
)
|
640
|
+
|
477
641
|
expect(res).to eq [22, 'G1']
|
478
642
|
end
|
479
643
|
|
644
|
+
it 'should treat nil as missing attr' do
|
645
|
+
expect do
|
646
|
+
res = lookup_grid_helper('infinity',
|
647
|
+
'G1',
|
648
|
+
'fico' => 720,
|
649
|
+
'state' => 'NM',
|
650
|
+
'ltv' => 80,
|
651
|
+
)
|
652
|
+
end.to raise_error(RuntimeError, /matches > 1/)
|
653
|
+
|
654
|
+
expect do
|
655
|
+
res = lookup_grid_helper('infinity',
|
656
|
+
'G1',
|
657
|
+
'fico' => 720,
|
658
|
+
'ltv' => 80,
|
659
|
+
)
|
660
|
+
end.to raise_error(RuntimeError, /matches > 1/)
|
661
|
+
|
662
|
+
expect do
|
663
|
+
res = lookup_grid_helper('infinity',
|
664
|
+
'G1',
|
665
|
+
'fico' => 720,
|
666
|
+
'state' => nil,
|
667
|
+
'ltv' => 80,
|
668
|
+
)
|
669
|
+
end.to raise_error(RuntimeError, /matches > 1/)
|
670
|
+
end
|
671
|
+
|
672
|
+
it 'should handle string NULLS' do
|
673
|
+
res = lookup_grid_helper('infinity',
|
674
|
+
'G1_with_nulls',
|
675
|
+
'fico' => 720,
|
676
|
+
'state' => nil,
|
677
|
+
'ltv' => 80,
|
678
|
+
)
|
679
|
+
|
680
|
+
expect(res).to eq [22, 'G1_with_nulls']
|
681
|
+
|
682
|
+
expect do
|
683
|
+
lookup_grid_helper('infinity',
|
684
|
+
'G1_with_nulls',
|
685
|
+
'fico' => 720,
|
686
|
+
'state' => 'BLABLA',
|
687
|
+
'ltv' => 80,
|
688
|
+
)
|
689
|
+
end.to raise_error(/Data Grid lookup failed/)
|
690
|
+
|
691
|
+
dg = dg_from_import(
|
692
|
+
'G1_with_nulls2',
|
693
|
+
G1_with_nulls.sub('NULL', 'NY|NULL')
|
694
|
+
)
|
695
|
+
|
696
|
+
res = lookup_grid_helper('infinity',
|
697
|
+
dg.name,
|
698
|
+
'fico' => 720,
|
699
|
+
'state' => nil,
|
700
|
+
'ltv' => 80,
|
701
|
+
)
|
702
|
+
|
703
|
+
expect(res).to eq [22, dg.name]
|
704
|
+
|
705
|
+
res = lookup_grid_helper('infinity',
|
706
|
+
dg.name,
|
707
|
+
'fico' => 720,
|
708
|
+
'state' => 'NY',
|
709
|
+
'ltv' => 80,
|
710
|
+
)
|
711
|
+
|
712
|
+
expect(res).to eq [22, dg.name]
|
713
|
+
end
|
714
|
+
|
480
715
|
it 'should handle matches which also have a wildcard match' do
|
481
716
|
dg_from_import('G9', G9)
|
482
717
|
|
@@ -485,7 +720,7 @@ EOS
|
|
485
720
|
'G9',
|
486
721
|
'state' => 'CA', 'ltv' => 81,
|
487
722
|
)
|
488
|
-
end.to raise_error(RuntimeError)
|
723
|
+
end.to raise_error(RuntimeError, /matches > 1/)
|
489
724
|
|
490
725
|
res = lookup_grid_helper('infinity',
|
491
726
|
'G9',
|
@@ -494,30 +729,42 @@ EOS
|
|
494
729
|
expect(res).to eq [456, 'G9']
|
495
730
|
end
|
496
731
|
|
497
|
-
it 'should raise on nil attr values' do
|
732
|
+
# it 'should raise on nil attr values' do
|
733
|
+
# next
|
734
|
+
# dg_from_import('G9', G9)
|
735
|
+
#
|
736
|
+
# expect do
|
737
|
+
# lookup_grid_helper('infinity',
|
738
|
+
# 'G9',
|
739
|
+
# 'ltv' => 81,
|
740
|
+
# )
|
741
|
+
# end.to raise_error(/matches > 1/)
|
742
|
+
#
|
743
|
+
# err = /Data Grid lookup failed/
|
744
|
+
# expect do
|
745
|
+
# lookup_grid_helper('infinity',
|
746
|
+
# 'G9',
|
747
|
+
# { 'state' => 'CA', 'ltv' => nil },
|
748
|
+
# false, false)
|
749
|
+
# end.to raise_error(err)
|
750
|
+
#
|
751
|
+
# res = lookup_grid_helper('infinity',
|
752
|
+
# 'G9',
|
753
|
+
# { 'state' => nil, 'ltv' => 81 },
|
754
|
+
# false, false)
|
755
|
+
#
|
756
|
+
# expect(res).to eq [456, 'G9']
|
757
|
+
# end
|
758
|
+
|
759
|
+
it 'should raise if nothing was found' do
|
498
760
|
dg_from_import('G9', G9)
|
499
761
|
|
500
762
|
expect do
|
501
763
|
lookup_grid_helper('infinity',
|
502
764
|
'G9',
|
503
|
-
'ltv' =>
|
765
|
+
'ltv' => 80,
|
504
766
|
)
|
505
|
-
end.to raise_error(/
|
506
|
-
|
507
|
-
err = /Data Grid lookup failed/
|
508
|
-
expect do
|
509
|
-
lookup_grid_helper('infinity',
|
510
|
-
'G9',
|
511
|
-
{ 'state' => 'CA', 'ltv' => nil },
|
512
|
-
false, false)
|
513
|
-
end.to raise_error(err)
|
514
|
-
|
515
|
-
res = lookup_grid_helper('infinity',
|
516
|
-
'G9',
|
517
|
-
{ 'state' => nil, 'ltv' => 81 },
|
518
|
-
false, false)
|
519
|
-
|
520
|
-
expect(res).to eq [456, 'G9']
|
767
|
+
end.to raise_error(/Data Grid lookup failed/)
|
521
768
|
end
|
522
769
|
|
523
770
|
it 'should handle boolean keys' do
|
@@ -635,21 +882,25 @@ EOS
|
|
635
882
|
'attr' => 'units',
|
636
883
|
'keys' => [[1, 2], [1, 2], [3, 4], [3, 4]],
|
637
884
|
'nots' => [false, false, false, false],
|
885
|
+
'wildcards' => [false, false, false, false],
|
638
886
|
'type' => 'integer' },
|
639
887
|
{ 'dir' => 'v',
|
640
888
|
'attr' => 'ltv',
|
641
889
|
'keys' => ['[,80]', '(80,105]', '[,80]', '(80,105]'],
|
642
890
|
'nots' => [false, false, false, false],
|
891
|
+
'wildcards' => [false, false, false, false],
|
643
892
|
'type' => 'numrange' },
|
644
893
|
{ 'dir' => 'h',
|
645
894
|
'attr' => 'cltv',
|
646
895
|
'keys' => ['[100,110)', '[110,120)', '[120,]'],
|
647
896
|
'nots' => [false, false, false],
|
897
|
+
'wildcards' => [false, false, false],
|
648
898
|
'type' => 'numrange' },
|
649
899
|
{ 'dir' => 'h',
|
650
900
|
'attr' => 'fico',
|
651
901
|
'keys' => ['[600,700)', '[700,750)', '[750,]'],
|
652
902
|
'nots' => [false, false, false],
|
903
|
+
'wildcards' => [false, false, false],
|
653
904
|
'type' => 'numrange' }]
|
654
905
|
|
655
906
|
dgh = Marty::DataGrid.lookup_h(pt, 'G2')
|
@@ -666,17 +917,20 @@ EOS
|
|
666
917
|
'attr' => 'state',
|
667
918
|
'keys' => [['CA'], ['HI', 'TX'], ['NM'], ['MA'], nil],
|
668
919
|
'nots' => [false, false, false, false, false],
|
920
|
+
'wildcards' => [false, false, false, false, true],
|
669
921
|
'type' => 'string' },
|
670
922
|
{ 'dir' => 'v',
|
671
923
|
'attr' => 'ltv',
|
672
924
|
'keys' => ['[,80]', '(80,105]', '[,80]', '(80,105]',
|
673
925
|
'[,80]'],
|
674
926
|
'nots' => [false, false, false, false, false],
|
927
|
+
'wildcards' => [false, false, false, false, false],
|
675
928
|
'type' => 'numrange' },
|
676
929
|
{ 'dir' => 'h',
|
677
930
|
'attr' => 'fico',
|
678
931
|
'keys' => ['[600,700)', '[700,750)', '[750,]'],
|
679
932
|
'nots' => [false, false, false],
|
933
|
+
'wildcards' => [false, false, false],
|
680
934
|
'type' => 'numrange' }]
|
681
935
|
dgh = Marty::DataGrid.lookup_h(pt, 'G8')
|
682
936
|
res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
|
@@ -694,6 +948,7 @@ EOS
|
|
694
948
|
'attr' => 'ltv',
|
695
949
|
'keys' => ['[,115]', '(115,135]', '(135,140]'],
|
696
950
|
'nots' => [false, false, false],
|
951
|
+
'wildcards' => [false, false, false],
|
697
952
|
'type' => 'numrange' }]
|
698
953
|
dgh = Marty::DataGrid.lookup_h(pt, 'G8')
|
699
954
|
res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
|
@@ -945,6 +1200,65 @@ EOS
|
|
945
1200
|
false\t\t>10\t\t#{values3[2]}
|
946
1201
|
EOS
|
947
1202
|
end
|
1203
|
+
|
1204
|
+
describe 'performance' do
|
1205
|
+
before(:each) do
|
1206
|
+
%w[G1 Gf Gl].each do |g|
|
1207
|
+
dg_from_import(g, "Marty::DataGridSpec::#{g}".constantize)
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
after do
|
1212
|
+
Rails.application.config.marty.data_grid_plpg_lookups = false
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
let(:pt) { 'infinity' }
|
1216
|
+
|
1217
|
+
grid_data = {
|
1218
|
+
'Gf' => { 'b' => true },
|
1219
|
+
'G1' => {
|
1220
|
+
'fico' => 600,
|
1221
|
+
'state' => 'RI',
|
1222
|
+
'ltv' => 10,
|
1223
|
+
},
|
1224
|
+
'Gl' => {
|
1225
|
+
'fha_203k_option2' => 'Not Existing Services'
|
1226
|
+
}
|
1227
|
+
}
|
1228
|
+
|
1229
|
+
grid_data.each_with_index do |(grid, params), index|
|
1230
|
+
it "ruby lookup is faster than plpgsql #{index}" do
|
1231
|
+
bm = Benchmark.ips do |x|
|
1232
|
+
x.report('postgres') do
|
1233
|
+
Rails.application.config.marty.data_grid_plpg_lookups = true
|
1234
|
+
res = Marty::DataGrid.lookup_grid_h(pt, grid, params, false)
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
x.report('ruby') do
|
1238
|
+
Rails.application.config.marty.data_grid_plpg_lookups = false
|
1239
|
+
res = Marty::DataGrid.lookup_grid_h(pt, grid, params, false)
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
x.compare!
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
h = bm.entries.each_with_object({}) do |e, hh|
|
1246
|
+
hh[e.label] = e.stats.central_tendency
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
factor = h['ruby'] / h['postgres']
|
1250
|
+
|
1251
|
+
if ENV['CI'] == 'true'
|
1252
|
+
# Performance drops down in CI, probably due to running postgres
|
1253
|
+
# in a separate container utilizing it's own CPU core.
|
1254
|
+
expect(factor).to be > 0.8
|
1255
|
+
else
|
1256
|
+
expect(factor).to be > 1.02
|
1257
|
+
end
|
1258
|
+
end
|
1259
|
+
end
|
1260
|
+
end
|
1261
|
+
|
948
1262
|
describe 'constraint' do
|
949
1263
|
it 'constraint' do
|
950
1264
|
Mcfly.whodunnit = system_user
|