marty 2.5.2 → 2.5.4

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.
Files changed (181) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +7 -0
  4. data/.rubocop_todo.yml +11 -589
  5. data/Gemfile +9 -9
  6. data/Gemfile.lock +1 -1
  7. data/Rakefile +1 -3
  8. data/app/components/marty/api_auth_view.rb +3 -3
  9. data/app/components/marty/api_config_view.rb +8 -8
  10. data/app/components/marty/api_log_view.rb +16 -20
  11. data/app/components/marty/auth_app.rb +6 -6
  12. data/app/components/marty/base_rule_view.rb +27 -19
  13. data/app/components/marty/config_view.rb +12 -9
  14. data/app/components/marty/data_grid_view.rb +26 -26
  15. data/app/components/marty/delorean_rule_view.rb +0 -1
  16. data/app/components/marty/event_view.rb +27 -27
  17. data/app/components/marty/extras/layout.rb +26 -26
  18. data/app/components/marty/extras/misc.rb +2 -2
  19. data/app/components/marty/grid.rb +13 -13
  20. data/app/components/marty/grid_append_only.rb +0 -1
  21. data/app/components/marty/import_type_view.rb +13 -13
  22. data/app/components/marty/import_view.rb +17 -16
  23. data/app/components/marty/log_view.rb +16 -14
  24. data/app/components/marty/main_auth_app.rb +59 -59
  25. data/app/components/marty/main_auth_app/client/main_auth_app.js +3 -3
  26. data/app/components/marty/mcfly_grid_panel.rb +10 -10
  27. data/app/components/marty/new_posting_form.rb +11 -11
  28. data/app/components/marty/new_posting_window.rb +0 -1
  29. data/app/components/marty/posting_grid.rb +12 -13
  30. data/app/components/marty/promise_view.rb +6 -6
  31. data/app/components/marty/report_form.rb +50 -53
  32. data/app/components/marty/report_select.rb +27 -27
  33. data/app/components/marty/reporting.rb +4 -4
  34. data/app/components/marty/script_form.rb +40 -42
  35. data/app/components/marty/script_grid.rb +24 -24
  36. data/app/components/marty/script_tester.rb +40 -42
  37. data/app/components/marty/scripting.rb +25 -27
  38. data/app/components/marty/simple_app.rb +24 -9
  39. data/app/components/marty/tag_grid.rb +12 -13
  40. data/app/components/marty/user_view.rb +35 -35
  41. data/app/controllers/marty/application_controller.rb +3 -4
  42. data/app/controllers/marty/components_controller.rb +1 -1
  43. data/app/controllers/marty/delayed_job_controller.rb +1 -0
  44. data/app/controllers/marty/diagnostic/controller.rb +4 -6
  45. data/app/controllers/marty/job_controller.rb +6 -6
  46. data/app/controllers/marty/report_controller.rb +11 -11
  47. data/app/controllers/marty/rpc_controller.rb +15 -16
  48. data/app/helpers/marty/script_set.rb +4 -4
  49. data/app/models/marty/api_auth.rb +4 -5
  50. data/app/models/marty/api_config.rb +1 -1
  51. data/app/models/marty/base.rb +9 -8
  52. data/app/models/marty/base_rule.rb +18 -13
  53. data/app/models/marty/config.rb +4 -5
  54. data/app/models/marty/data_grid.rb +157 -181
  55. data/app/models/marty/delorean_rule.rb +63 -62
  56. data/app/models/marty/enum.rb +1 -1
  57. data/app/models/marty/event.rb +56 -59
  58. data/app/models/marty/helper.rb +38 -6
  59. data/app/models/marty/import_type.rb +6 -6
  60. data/app/models/marty/log.rb +3 -2
  61. data/app/models/marty/name_validator.rb +3 -2
  62. data/app/models/marty/pg_enum.rb +3 -4
  63. data/app/models/marty/posting.rb +20 -24
  64. data/app/models/marty/promise.rb +28 -30
  65. data/app/models/marty/script.rb +30 -28
  66. data/app/models/marty/tag.rb +8 -8
  67. data/app/models/marty/token.rb +2 -2
  68. data/app/models/marty/user.rb +24 -23
  69. data/app/models/marty/vw_promise.rb +10 -11
  70. data/config/routes.rb +2 -2
  71. data/delorean/blame_report.dl +268 -0
  72. data/{spec/dummy/delorean/fields.dl → delorean/marty_fields.dl} +8 -0
  73. data/delorean/table_report.dl +34 -0
  74. data/docker-compose.dummy.yml +2 -3
  75. data/lib/marty/aws/base.rb +8 -8
  76. data/lib/marty/aws/request.rb +4 -4
  77. data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -0
  78. data/lib/marty/content_handler.rb +25 -25
  79. data/lib/marty/data_change.rb +49 -71
  80. data/lib/marty/data_conversion.rb +20 -28
  81. data/lib/marty/data_exporter.rb +25 -28
  82. data/lib/marty/data_importer.rb +25 -27
  83. data/lib/marty/engine.rb +1 -2
  84. data/lib/marty/json_schema.rb +22 -24
  85. data/lib/marty/logger.rb +6 -9
  86. data/lib/marty/mcfly_model.rb +20 -24
  87. data/lib/marty/migrations.rb +37 -35
  88. data/lib/marty/monkey.rb +33 -33
  89. data/lib/marty/permissions.rb +18 -18
  90. data/lib/marty/promise_job.rb +17 -17
  91. data/lib/marty/promise_proxy.rb +6 -6
  92. data/lib/marty/relation.rb +6 -7
  93. data/lib/marty/rpc_call.rb +13 -12
  94. data/lib/marty/rule_script_set.rb +32 -28
  95. data/lib/marty/schema_helper.rb +37 -51
  96. data/lib/marty/util.rb +25 -24
  97. data/lib/marty/version.rb +1 -1
  98. data/lib/marty/xl.rb +121 -115
  99. data/make-dummy.mk +3 -0
  100. data/marty.gemspec +21 -21
  101. data/other/marty/api/base.rb +34 -35
  102. data/other/marty/diagnostic/aws/ec2_instance.rb +8 -8
  103. data/other/marty/diagnostic/base.rb +13 -14
  104. data/other/marty/diagnostic/collection.rb +2 -1
  105. data/other/marty/diagnostic/connections.rb +8 -6
  106. data/other/marty/diagnostic/database.rb +1 -0
  107. data/other/marty/diagnostic/delayed_job_version.rb +7 -9
  108. data/other/marty/diagnostic/delayed_job_worker_total_count.rb +1 -1
  109. data/other/marty/diagnostic/delayed_job_workers.rb +1 -1
  110. data/other/marty/diagnostic/environment_variables.rb +17 -15
  111. data/other/marty/diagnostic/fatal.rb +1 -1
  112. data/other/marty/diagnostic/node.rb +5 -9
  113. data/other/marty/diagnostic/nodes.rb +7 -5
  114. data/other/marty/diagnostic/packer.rb +7 -7
  115. data/other/marty/diagnostic/reporter.rb +24 -27
  116. data/other/marty/diagnostic/version.rb +3 -5
  117. data/script/rails +2 -1
  118. data/spec/controllers/application_controller_spec.rb +6 -6
  119. data/spec/controllers/delayed_job_controller_spec.rb +4 -4
  120. data/spec/controllers/diagnostic/controller_spec.rb +59 -60
  121. data/spec/controllers/job_controller_spec.rb +68 -69
  122. data/spec/controllers/rpc_controller_spec.rb +353 -359
  123. data/spec/controllers/rpc_import_spec.rb +15 -16
  124. data/spec/dummy/delorean/blame_report.dl +110 -15
  125. data/spec/dummy/delorean/data_report.dl +4 -4
  126. data/spec/dummy/delorean/marty_fields.dl +63 -0
  127. data/spec/dummy/delorean/table_report.dl +34 -0
  128. data/spec/features/auth_app_spec.rb +1 -2
  129. data/spec/features/data_import_spec.rb +2 -3
  130. data/spec/features/enum_spec.rb +42 -46
  131. data/spec/features/jobs_dashboard_spec.rb +14 -8
  132. data/spec/features/log_view_spec.rb +40 -43
  133. data/spec/features/reporting_spec.rb +15 -15
  134. data/spec/features/rule_spec.rb +195 -190
  135. data/spec/features/scripting_spec.rb +17 -20
  136. data/spec/features/scripting_test_spec.rb +32 -33
  137. data/spec/features/user_view_spec.rb +15 -17
  138. data/spec/job_helper.rb +11 -11
  139. data/spec/lib/data_blame_spec.rb +82 -0
  140. data/spec/lib/data_exporter_spec.rb +31 -32
  141. data/spec/lib/data_importer_spec.rb +382 -395
  142. data/spec/lib/delorean_query_spec.rb +117 -119
  143. data/spec/lib/json_schema_spec.rb +382 -392
  144. data/spec/lib/logger_spec.rb +23 -24
  145. data/spec/lib/mcfly_model_spec.rb +112 -109
  146. data/spec/lib/migrations_spec.rb +10 -10
  147. data/spec/lib/struct_compare_spec.rb +6 -6
  148. data/spec/lib/table_report_spec.rb +90 -0
  149. data/spec/lib/xl_spec.rb +63 -65
  150. data/spec/lib/xl_styles_spec.rb +16 -19
  151. data/spec/models/api_auth_spec.rb +30 -30
  152. data/spec/models/config_spec.rb +32 -32
  153. data/spec/models/data_grid_spec.rb +642 -655
  154. data/spec/models/event_spec.rb +96 -88
  155. data/spec/models/import_type_spec.rb +20 -20
  156. data/spec/models/posting_spec.rb +35 -35
  157. data/spec/models/promise_spec.rb +5 -5
  158. data/spec/models/rule_spec.rb +280 -269
  159. data/spec/models/script_spec.rb +27 -18
  160. data/spec/models/user_spec.rb +9 -9
  161. data/spec/other/diagnostic/base_spec.rb +20 -19
  162. data/spec/other/diagnostic/collection_spec.rb +6 -5
  163. data/spec/other/diagnostic/delayed_job_version_spec.rb +1 -1
  164. data/spec/other/diagnostic/delayed_job_workers_spec.rb +8 -8
  165. data/spec/other/diagnostic/reporter_spec.rb +31 -33
  166. data/spec/spec_helper.rb +5 -5
  167. data/spec/support/chromedriver.rb +3 -5
  168. data/spec/support/components/netzke_combobox.rb +1 -1
  169. data/spec/support/components/netzke_grid.rb +17 -17
  170. data/spec/support/custom_matchers.rb +2 -2
  171. data/spec/support/download_helper.rb +1 -1
  172. data/spec/support/helper.rb +1 -2
  173. data/spec/support/netzke.rb +31 -31
  174. data/spec/support/performance_helper.rb +8 -8
  175. data/spec/support/post_run_logger.rb +1 -2
  176. data/spec/support/setup.rb +1 -4
  177. data/spec/support/shared_connection.rb +2 -2
  178. data/spec/support/structure_compare.rb +21 -22
  179. data/spec/support/suite.rb +1 -2
  180. data/spec/support/users.rb +5 -6
  181. metadata +32 -26
@@ -2,68 +2,68 @@ require 'spec_helper'
2
2
 
3
3
  module Marty
4
4
  describe Marty::Config do
5
- describe "validations" do
6
- it "should return valid config value based on key" do
7
- Marty::Config["TEST 1"] = 2
8
- expect(Marty::Config.lookup("TEST 1")).to eq(2)
9
- expect(Marty::Config["TEST 1"]).to eq(2)
5
+ describe 'validations' do
6
+ it 'should return valid config value based on key' do
7
+ Marty::Config['TEST 1'] = 2
8
+ expect(Marty::Config.lookup('TEST 1')).to eq(2)
9
+ expect(Marty::Config['TEST 1']).to eq(2)
10
10
  end
11
11
 
12
12
  def testval(val)
13
- Marty::Config["testval"] = val
14
- expect(Marty::Config.lookup("testval")).to eq(val)
15
- expect(Marty::Config["testval"]).to eq(val)
13
+ Marty::Config['testval'] = val
14
+ expect(Marty::Config.lookup('testval')).to eq(val)
15
+ expect(Marty::Config['testval']).to eq(val)
16
16
  end
17
17
 
18
18
  def testval_fail(val)
19
- expect{testval(val)}.to raise_error(ActiveRecord::RecordInvalid,
20
- 'Validation failed: bad JSON value')
19
+ expect { testval(val) }.to raise_error(ActiveRecord::RecordInvalid,
20
+ 'Validation failed: bad JSON value')
21
21
  end
22
22
 
23
- it "should handle various structures correctly" do
24
- testval("[1,2,3]")
25
- testval("[1,\"2,3\"]")
26
- testval([1,2,3])
27
- testval([1,"2,3"])
23
+ it 'should handle various structures correctly' do
24
+ testval('[1,2,3]')
25
+ testval('[1,"2,3"]')
26
+ testval([1, 2, 3])
27
+ testval([1, '2,3'])
28
28
 
29
- testval({ "key1" => [1,2,3], "keystr" => { "val" => "val"}})
29
+ testval('key1' => [1, 2, 3], 'keystr' => { 'val' => 'val' })
30
30
  testval(%Q({ "key1" : [1,2,3], "keystr" : { "val" : "val"}}))
31
31
 
32
- testval("123456.1234")
33
- testval("a string")
34
- testval("\"a string\"")
32
+ testval('123456.1234')
33
+ testval('a string')
34
+ testval('"a string"')
35
35
  end
36
36
 
37
- it "should return nil config value for non-existing key" do
38
- expect(Marty::Config.lookup("TEST 2")).to eq(nil)
39
- expect(Marty::Config["TEST 2"]).to eq(nil)
37
+ it 'should return nil config value for non-existing key' do
38
+ expect(Marty::Config.lookup('TEST 2')).to eq(nil)
39
+ expect(Marty::Config['TEST 2']).to eq(nil)
40
40
  end
41
41
 
42
- it "should handle del" do
43
- (0..10).each { |i|
44
- v = {"i" => i}
42
+ it 'should handle del' do
43
+ (0..10).each do |i|
44
+ v = { 'i' => i }
45
45
  Marty::Config["k#{i}"] = v
46
46
  expect(Marty::Config["k#{i}"]).to eq(v)
47
- }
47
+ end
48
48
 
49
- (0..10).each { |i|
49
+ (0..10).each do |i|
50
50
  Marty::Config.del("k#{i}")
51
51
  expect(Marty::Config["k#{i}"]).to eq(nil)
52
- }
52
+ end
53
53
  end
54
54
 
55
- it "should allow the assignment of individual boolean values" do
55
+ it 'should allow the assignment of individual boolean values' do
56
56
  testval(true)
57
57
  testval(false)
58
58
  end
59
59
 
60
- it "should not allow the assignment of individual nil (null) values" do
60
+ it 'should not allow the assignment of individual nil (null) values' do
61
61
  testval_fail(nil)
62
62
  end
63
63
 
64
- it "should allow nil (null) to exist in other structures" do
64
+ it 'should allow nil (null) to exist in other structures' do
65
65
  testval([nil, 1, 2, nil])
66
- testval({"key1" => nil, "key2" => false, "key3" => 'val'})
66
+ testval('key1' => nil, 'key2' => false, 'key3' => 'val')
67
67
  end
68
68
  end
69
69
  end
@@ -2,8 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module Marty::DataGridSpec
4
4
  describe DataGrid do
5
-
6
- G1 =<<EOS
5
+ G1 = <<EOS
7
6
  state\tstring\tv\t\t
8
7
  ltv\tnumrange\tv\t\t
9
8
  fico\tnumrange\th\t\t
@@ -16,7 +15,7 @@ MA\t>80<=105\t4.5\t5.6\t
16
15
  \t<=80\t11\t22\t33
17
16
  EOS
18
17
 
19
- G2 =<<EOS
18
+ G2 = <<EOS
20
19
  units\tinteger\tv\t\t
21
20
  ltv\tnumrange\tv\t\t
22
21
  cltv\tnumrange\th\t\t
@@ -30,9 +29,9 @@ fico\tnumrange\th\t\t
30
29
  3|4\t>80<=105\t4.5\t5.6\t6.7
31
30
  EOS
32
31
 
33
- G3 = File.open(File.expand_path("../srp_data.csv", __FILE__)).read
32
+ G3 = File.open(File.expand_path('../srp_data.csv', __FILE__)).read
34
33
 
35
- G4 =<<EOS
34
+ G4 = <<EOS
36
35
  lenient
37
36
  hb_indicator\tboolean\tv
38
37
  cltv\tnumrange\th
@@ -41,21 +40,21 @@ cltv\tnumrange\th
41
40
  true\t-0.750\t-0.750\t-0.750\t-1.500\t-1.500\t-1.500\t\t
42
41
  EOS
43
42
 
44
- G5 =<<EOS
43
+ G5 = <<EOS
45
44
  ltv\tnumrange\tv\t\t
46
45
 
47
46
  <=115\t-0.375
48
47
  >115<=135\t-0.750
49
48
  EOS
50
49
 
51
- G6 =<<EOS
50
+ G6 = <<EOS
52
51
  ltv\tnumrange\th
53
52
 
54
53
  <=115\t>115<=135
55
54
  -0.375\t-0.750
56
55
  EOS
57
56
 
58
- G7 =<<EOS
57
+ G7 = <<EOS
59
58
  string
60
59
  hb_indicator\tboolean\tv
61
60
  cltv\tnumrange\th
@@ -64,7 +63,7 @@ cltv\tnumrange\th
64
63
  true\tThis\tis\ta\ttest\tof\tstring type\t\t
65
64
  EOS
66
65
 
67
- G8 =<<EOS
66
+ G8 = <<EOS
68
67
  Marty::DataGrid
69
68
  ltv\tnumrange\tv\t\t
70
69
 
@@ -73,7 +72,7 @@ ltv\tnumrange\tv\t\t
73
72
  >135<=140\tG3
74
73
  EOS
75
74
 
76
- G9 =<<EOS
75
+ G9 = <<EOS
77
76
  state\tstring\tv
78
77
  ltv\tnumrange\tv
79
78
 
@@ -81,14 +80,14 @@ CA|TX\t>80\t123
81
80
  \t>80\t456
82
81
  EOS
83
82
 
84
- Ga =<<EOS
83
+ Ga = <<EOS
85
84
  dg\tMarty::DataGrid\tv\t\t
86
85
 
87
86
  G1|G2\t7
88
87
  G3\t8
89
88
  EOS
90
89
 
91
- Gb =<<EOS
90
+ Gb = <<EOS
92
91
  property_state\tGemini::State\tv\t\t
93
92
 
94
93
  CA|TX\t70
@@ -96,21 +95,21 @@ GA\t80
96
95
  MN\t90
97
96
  EOS
98
97
 
99
- Gc =<<EOS
98
+ Gc = <<EOS
100
99
  Marty::DataGrid
101
100
  property_state\tGemini::State\tv\t\t
102
101
 
103
102
  CA|TX\tGb
104
103
  EOS
105
104
 
106
- Gd =<<EOS
105
+ Gd = <<EOS
107
106
  hb_indicator\tboolean\tv
108
107
 
109
108
  true\t456
110
109
  false\t123
111
110
  EOS
112
111
 
113
- Ge =<<EOS
112
+ Ge = <<EOS
114
113
  ltv\tnumrange\th
115
114
 
116
115
  >110\t>120
@@ -148,7 +147,7 @@ NY\t\t10
148
147
  \tR\t8
149
148
  EOS
150
149
 
151
- Gi =<<EOS
150
+ Gi = <<EOS
152
151
  units\tinteger\tv\t\t
153
152
  ltv\tfloat\tv\t\t
154
153
  cltv\tfloat\th\t\t
@@ -162,7 +161,7 @@ fico\tnumrange\th\t\t
162
161
  3|4\t105.5\t4.5\t5.6\t6.7
163
162
  EOS
164
163
 
165
- Gj =<<EOS
164
+ Gj = <<EOS
166
165
  lenient
167
166
  client_id\tinteger\tv
168
167
  property_state\tstring\tv
@@ -171,7 +170,7 @@ property_state\tstring\tv
171
170
  700127\tCA\t0.35
172
171
  EOS
173
172
 
174
- Gk =<<EOS
173
+ Gk = <<EOS
175
174
  fha_203k_option\tstring\tv\tfha_203k_option
176
175
 
177
176
  Investor Services\t-0.625
@@ -180,657 +179,645 @@ Admin Services Plus\t-1.625
180
179
  Investor Services Acadamy\t-0.5
181
180
  EOS
182
181
 
183
- before(:each) do
184
- #Mcfly.whodunnit = Marty::User.find_by_login('marty')
185
- marty_whodunnit
182
+ before(:each) do
183
+ # Mcfly.whodunnit = Marty::User.find_by_login('marty')
184
+ marty_whodunnit
185
+ end
186
+
187
+ def lookup_grid_helper(pt, gridname, params, follow = false, distinct = true)
188
+ dgh = Marty::DataGrid.lookup_h(pt, gridname)
189
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt, params, dgh, nil, follow,
190
+ false, distinct)
191
+ [res['result'], res['name']]
192
+ end
193
+
194
+ describe 'imports' do
195
+ it 'should not allow imports with trailing blank columns' do
196
+ expect do
197
+ dg_from_import('G1', G1.gsub("\n", "\t\n"))
198
+ end.to raise_error(RuntimeError)
199
+ end
200
+
201
+ it 'should not allow imports with last blank row' do
202
+ expect do
203
+ dg_from_import('Gh', Gh + "\t\t\n")
204
+ end.to raise_error(RuntimeError)
205
+ end
206
+ end
207
+
208
+ describe 'validations' do
209
+ it 'should not allow bad axis types' do
210
+ expect do
211
+ dg_from_import('Gi', Gi)
212
+ end.to raise_error(/unknown metadata type float/)
213
+ expect do
214
+ dg_from_import('Gi', Gi.sub(/float/, 'abcdef'))
215
+ end.to raise_error(/unknown metadata type abcdef/)
216
+ end
217
+
218
+ it 'should not allow dup attr names' do
219
+ g_bad = G1.sub(/fico/, 'ltv')
220
+
221
+ expect do
222
+ dg_from_import('G2', g_bad)
223
+ end.to raise_error(ActiveRecord::RecordInvalid)
224
+ end
225
+
226
+ it 'should not allow dup grid names' do
227
+ dg_from_import('G1', G1)
228
+
229
+ expect do
230
+ dg_from_import('G1', G2)
231
+ end.to raise_error(ActiveRecord::RecordInvalid)
232
+ end
233
+
234
+ it 'should not allow extra attr rows' do
235
+ g_bad = "x\tnumrange\th\t\t\n" + G1
236
+
237
+ expect do
238
+ dg_from_import('G2', g_bad)
239
+ end.to raise_error(RuntimeError)
240
+ end
241
+
242
+ it 'should not allow dup row/col key combos' do
243
+ g_bad = G1 + G1.split("\n").last + "\n"
244
+ expect do
245
+ dg_from_import('G2', g_bad)
246
+ end.to raise_error(ActiveRecord::RecordInvalid)
247
+
248
+ g_bad = G2 + G2.split("\n").last + "\n"
249
+ expect do
250
+ dg_from_import('G2', g_bad)
251
+ end.to raise_error(ActiveRecord::RecordInvalid)
252
+ end
253
+
254
+ it 'Unknown keys for typed grids should raise error' do
255
+ g_bad = G8.sub(/G3/, 'XXXXX')
256
+
257
+ expect do
258
+ dg_from_import('G8', g_bad)
259
+ end.to raise_error(RuntimeError)
260
+
261
+ g_bad = G8.sub(/DataGrid/, 'Division')
262
+
263
+ expect do
264
+ dg_from_import('G8', g_bad)
265
+ end.to raise_error(RuntimeError)
266
+ end
267
+
268
+ it 'Unknown keys for grid headers should raise error' do
269
+ g_bad = Ga.sub(/G3/, 'XXXXX')
270
+
271
+ expect do
272
+ dg_from_import('Ga', g_bad)
273
+ end.to raise_error(RuntimeError)
274
+
275
+ g_bad = Ga.sub(/DataGrid/, 'Division')
276
+
277
+ expect do
278
+ dg_from_import('Ga', g_bad)
279
+ end.to raise_error(RuntimeError)
280
+ end
281
+
282
+ it 'validates grid modifier' do
283
+ bad = ': abc def'
284
+ g_bad = Gk.sub(/fha_203k_option$/, bad)
285
+ expect do
286
+ dg_from_import('Gk', g_bad)
287
+ end.to raise_error(/invalid grid modifier expression: #{bad}/)
288
+ expect do
289
+ dg_from_import('Gk', Gk)
290
+ end.not_to raise_error
291
+ end
292
+ end
293
+
294
+ describe 'lookups for infinity' do
295
+ let(:pt) { 'infinity' }
296
+
297
+ before(:each) do
298
+ ['G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'Ga', 'Gb',
299
+ 'Gc', 'Gd', 'Ge', 'Gf', 'Gg', 'Gh', 'Gj'].each do |g|
300
+ dg_from_import(g, "Marty::DataGridSpec::#{g}".constantize)
186
301
  end
302
+ end
303
+
304
+ context 'should handle NULL key values' do
305
+ let(:dgh) { 'Gf' }
187
306
 
188
- def lookup_grid_helper(pt, gridname, params, follow=false, distinct=true)
189
- dgh=Marty::DataGrid.lookup_h(pt, gridname)
190
- res=Marty::DataGrid.lookup_grid_distinct_entry_h(pt, params, dgh, nil, follow,
191
- false, distinct)
192
- [res["result"], res["name"]]
307
+ it 'true returns Y' do
308
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'b' => true }, false)
309
+ expect(res).to eq('Y')
193
310
  end
194
311
 
195
- describe "imports" do
196
- it "should not allow imports with trailing blank columns" do
197
- expect {
198
- dg_from_import("G1", G1.gsub("\n", "\t\n"))
199
- }.to raise_error(RuntimeError)
200
- end
201
-
202
- it "should not allow imports with last blank row" do
203
- expect {
204
- dg_from_import("Gh", Gh+"\t\t\n")
205
- }.to raise_error(RuntimeError)
206
- end
312
+ it '13 returns N' do
313
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13 }, true)
314
+ expect(res).to eq('N')
207
315
  end
208
316
 
209
- describe "validations" do
210
- it "should not allow bad axis types" do
211
- expect {
212
- dg_from_import("Gi", Gi)
213
- }.to raise_error(/unknown metadata type float/)
214
- expect {
215
- dg_from_import("Gi", Gi.sub(/float/, 'abcdef'))
216
- }.to raise_error(/unknown metadata type abcdef/)
217
- end
218
-
219
- it "should not allow dup attr names" do
220
- g_bad = G1.sub(/fico/, "ltv")
221
-
222
- expect {
223
- dg_from_import("G2", g_bad)
224
- }.to raise_error(ActiveRecord::RecordInvalid)
225
- end
226
-
227
- it "should not allow dup grid names" do
228
- dg_from_import("G1", G1)
229
-
230
- expect {
231
- dg_from_import("G1", G2)
232
- }.to raise_error(ActiveRecord::RecordInvalid)
233
- end
234
-
235
- it "should not allow extra attr rows" do
236
- g_bad = "x\tnumrange\th\t\t\n" + G1
237
-
238
- expect {
239
- dg_from_import("G2", g_bad)
240
- }.to raise_error(RuntimeError)
241
- end
242
-
243
- it "should not allow dup row/col key combos" do
244
- g_bad = G1 + G1.split("\n").last + "\n"
245
- expect {
246
- dg_from_import("G2", g_bad)
247
- }.to raise_error(ActiveRecord::RecordInvalid)
248
-
249
- g_bad = G2 + G2.split("\n").last + "\n"
250
- expect {
251
- dg_from_import("G2", g_bad)
252
- }.to raise_error(ActiveRecord::RecordInvalid)
253
- end
254
-
255
- it "Unknown keys for typed grids should raise error" do
256
- g_bad = G8.sub(/G3/, "XXXXX")
257
-
258
- expect {
259
- dg_from_import("G8", g_bad)
260
- }.to raise_error(RuntimeError)
261
-
262
- g_bad = G8.sub(/DataGrid/, "Division")
263
-
264
- expect {
265
- dg_from_import("G8", g_bad)
266
- }.to raise_error(RuntimeError)
267
- end
268
-
269
- it "Unknown keys for grid headers should raise error" do
270
- g_bad = Ga.sub(/G3/, "XXXXX")
271
-
272
- expect {
273
- dg_from_import("Ga", g_bad)
274
- }.to raise_error(RuntimeError)
275
-
276
- g_bad = Ga.sub(/DataGrid/, "Division")
277
-
278
- expect {
279
- dg_from_import("Ga", g_bad)
280
- }.to raise_error(RuntimeError)
281
- end
282
-
283
- it "validates grid modifier" do
284
- bad = ': abc def'
285
- g_bad = Gk.sub(/fha_203k_option$/, bad)
286
- expect { dg_from_import( "Gk", g_bad)
287
- }.to raise_error(/invalid grid modifier expression: #{bad}/)
288
- expect { dg_from_import( "Gk", Gk)
289
- }.not_to raise_error
290
- end
317
+ it '13 & numrange 0 returns nil' do
318
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'n' => 0 }, true)
319
+ expect(res).to eq('N')
291
320
  end
292
321
 
293
- describe "lookups for infinity" do
294
- let(:pt) { 'infinity'}
295
-
296
- before(:each) do
297
- ["G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "Ga", "Gb",
298
- "Gc", "Gd", "Ge", "Gf", "Gg", "Gh", "Gj"].each { |g|
299
- dg_from_import(g, "Marty::DataGridSpec::#{g}".constantize)
300
- }
301
- end
302
-
303
- context "should handle NULL key values" do
304
- let(:dgh) { "Gf" }
305
-
306
- it 'true returns Y' do
307
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"b"=>true}, false)
308
- expect(res).to eq('Y')
309
- end
310
-
311
- it '13 returns N' do
312
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13}, true)
313
- expect(res).to eq('N')
314
- end
315
-
316
- it '13 & numrange 0 returns nil' do
317
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "n"=>0}, true)
318
- expect(res).to eq('N')
319
- end
320
-
321
- it '13 & int4range 15 returns N' do
322
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "i4"=>15}, true)
323
- expect(res).to eq('N')
324
- end
325
-
326
- it '13 & int4range 1 returns nil' do
327
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "i4"=>1}, true)
328
- expect(res).to be_nil
329
- end
330
-
331
- it 'false, 3, numrange 15 returns N' do
332
- res = Marty::DataGrid.
333
- lookup_grid_h(pt, dgh, {"b"=>false, "i"=>3, "n"=>15}, true)
334
- expect(res).to eq('N')
335
- end
336
-
337
- it '13, numrange 15 returns N' do
338
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "n"=>15}, true)
339
- expect(res).to eq('N')
340
- end
341
- end
342
-
343
- it "should handle ambiguous lookups" do
344
- h1 = {
345
- "property_state" => "NY",
346
- "county_name" => "R",
347
- }
348
-
349
- res = Marty::DataGrid.lookup_grid_h(pt, "Gh", h1, false)
350
- expect(res).to eq(10)
351
- end
352
-
353
- it "should handle ambiguous lookups (2)" do
354
- res = Marty::DataGrid.
355
- lookup_grid_h(pt, "Gg", {"i1"=>2, "i2"=>1}, false)
356
- expect(res).to eq(1)
357
-
358
- res = Marty::DataGrid.
359
- lookup_grid_h(pt, "Gg", {"i1"=>3, "i2"=>1}, false)
360
- expect(res).to eq(1)
361
-
362
- res = Marty::DataGrid.
363
- lookup_grid_h(pt, "Gg", {"i1"=>2, "i2"=>3}, false)
364
- expect(res).to eq(20)
365
- end
366
-
367
- it "should handle non-distinct lookups" do
368
- res = Marty::DataGrid.lookup_grid_h(pt, "Ge", {"ltv"=>500}, false)
369
-
370
- expect(res).to eq(1.1)
371
-
372
- expect {
373
- Marty::DataGrid.lookup_grid_h(pt, "Ge", {"ltv"=>500}, true)
374
- }.to raise_error(RuntimeError)
375
- end
376
-
377
- it "should handle non-distinct lookups (2)" do
378
- params = {
379
- "client_id" => 700127,
380
- "property_state" => "CA",
381
- }
382
- res = Marty::DataGrid.lookup_grid_h(pt, "Gj", params, false)
383
-
384
- # should return the upper left corner match
385
- expect(res).to eq(0.25)
386
-
387
- expect {
388
- Marty::DataGrid.lookup_grid_h(pt, "Gj", params, true)
389
- }.to raise_error(RuntimeError)
390
- end
391
-
392
- it "should handle boolean lookups" do
393
- res = [true, false].map { |hb_indicator|
394
- lookup_grid_helper('infinity',
395
- "Gd",
396
- {"hb_indicator" => hb_indicator,
397
- },
398
- )
399
- }
400
- expect(res).to eq [[456.0, "Gd"], [123.0, "Gd"]]
401
- end
402
-
403
- it "should handle basic lookups" do
404
- res = lookup_grid_helper('infinity',
405
- "G3",
406
- {"amount" => 160300,
407
- "state" => "HI",
408
- },
409
- )
410
- expect(res).to eq [1.655,"G3"]
411
-
412
- [3,4].each {
413
- |units|
414
- res = lookup_grid_helper('infinity',
415
- "G2",
416
- {"fico" => 720,
417
- "units" => units,
418
- "ltv" => 100,
419
- "cltv" => 110.1,
420
- },
421
- )
422
- expect(res).to eq [5.6,"G2"]
423
- }
424
-
425
- dg = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G1")
426
-
427
- h = {
428
- "fico" => 600,
429
- "state" => "RI",
430
- "ltv" => 10,
431
- }
432
-
433
- res = lookup_grid_helper('infinity', "G1", h)
434
- expect(res).to eq [11,"G1"]
435
-
436
- dg.update_from_import("G1", G1.sub(/11/, "111"))
437
-
438
- res = lookup_grid_helper('infinity', "G1", h)
439
- expect(res).to eq [111,"G1"]
440
- end
441
-
442
- it "should result in error when there are multiple cell hits" do
443
- expect {
444
- lookup_grid_helper('infinity',
445
- "G2",
446
- {"fico" => 720,
447
- "ltv" => 100,
448
- "cltv" => 110.1,
449
- },
322
+ it '13 & int4range 15 returns N' do
323
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'i4' => 15 }, true)
324
+ expect(res).to eq('N')
325
+ end
326
+
327
+ it '13 & int4range 1 returns nil' do
328
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'i4' => 1 }, true)
329
+ expect(res).to be_nil
330
+ end
331
+
332
+ it 'false, 3, numrange 15 returns N' do
333
+ res = Marty::DataGrid.
334
+ lookup_grid_h(pt, dgh, { 'b' => false, 'i' => 3, 'n' => 15 }, true)
335
+ expect(res).to eq('N')
336
+ end
337
+
338
+ it '13, numrange 15 returns N' do
339
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'n' => 15 }, true)
340
+ expect(res).to eq('N')
341
+ end
342
+ end
343
+
344
+ it 'should handle ambiguous lookups' do
345
+ h1 = {
346
+ 'property_state' => 'NY',
347
+ 'county_name' => 'R',
348
+ }
349
+
350
+ res = Marty::DataGrid.lookup_grid_h(pt, 'Gh', h1, false)
351
+ expect(res).to eq(10)
352
+ end
353
+
354
+ it 'should handle ambiguous lookups (2)' do
355
+ res = Marty::DataGrid.
356
+ lookup_grid_h(pt, 'Gg', { 'i1' => 2, 'i2' => 1 }, false)
357
+ expect(res).to eq(1)
358
+
359
+ res = Marty::DataGrid.
360
+ lookup_grid_h(pt, 'Gg', { 'i1' => 3, 'i2' => 1 }, false)
361
+ expect(res).to eq(1)
362
+
363
+ res = Marty::DataGrid.
364
+ lookup_grid_h(pt, 'Gg', { 'i1' => 2, 'i2' => 3 }, false)
365
+ expect(res).to eq(20)
366
+ end
367
+
368
+ it 'should handle non-distinct lookups' do
369
+ res = Marty::DataGrid.lookup_grid_h(pt, 'Ge', { 'ltv' => 500 }, false)
370
+
371
+ expect(res).to eq(1.1)
372
+
373
+ expect do
374
+ Marty::DataGrid.lookup_grid_h(pt, 'Ge', { 'ltv' => 500 }, true)
375
+ end.to raise_error(RuntimeError)
376
+ end
377
+
378
+ it 'should handle non-distinct lookups (2)' do
379
+ params = {
380
+ 'client_id' => 700127,
381
+ 'property_state' => 'CA',
382
+ }
383
+ res = Marty::DataGrid.lookup_grid_h(pt, 'Gj', params, false)
384
+
385
+ # should return the upper left corner match
386
+ expect(res).to eq(0.25)
387
+
388
+ expect do
389
+ Marty::DataGrid.lookup_grid_h(pt, 'Gj', params, true)
390
+ end.to raise_error(RuntimeError)
391
+ end
392
+
393
+ it 'should handle boolean lookups' do
394
+ res = [true, false].map do |hb_indicator|
395
+ lookup_grid_helper('infinity',
396
+ 'Gd',
397
+ 'hb_indicator' => hb_indicator,
398
+ )
399
+ end
400
+ expect(res).to eq [[456.0, 'Gd'], [123.0, 'Gd']]
401
+ end
402
+
403
+ it 'should handle basic lookups' do
404
+ res = lookup_grid_helper('infinity',
405
+ 'G3',
406
+ 'amount' => 160300,
407
+ 'state' => 'HI',
450
408
  )
451
- }.to raise_error(RuntimeError)
452
- end
453
-
454
- it "should return nil when matching data grid cell is nil" do
455
- res = lookup_grid_helper('infinity',
456
- "G1",
457
- {"fico" => 800,
458
- "state" => "MA",
459
- "ltv" => 81,
460
- },
461
- )
462
- expect(res).to eq [nil,"G1"]
463
- end
464
-
465
- it "should handle string wildcards" do
466
- res = lookup_grid_helper('infinity',
467
- "G1",
468
- {"fico" => 720,
469
- "state" => "GU",
470
- "ltv" => 80,
471
- },
472
- )
473
- expect(res).to eq [22,"G1"]
474
- end
475
-
476
- it "should handle matches which also have a wildcard match" do
477
- dg_from_import("G9", G9)
478
-
479
- expect {
480
- res = lookup_grid_helper('infinity',
481
- "G9",
482
- {"state" => "CA", "ltv" => 81},
483
- )
484
- }.to raise_error(RuntimeError)
485
-
486
- res = lookup_grid_helper('infinity',
487
- "G9",
488
- {"state" => "GU", "ltv" => 81},
489
- )
490
- expect(res).to eq [456,"G9"]
491
- end
492
-
493
- it "should raise on nil attr values" do
494
- dg_from_import("G9", G9)
495
-
496
- expect{lookup_grid_helper('infinity',
497
- "G9",
498
- {"ltv" => 81},
499
- )}.to raise_error(/matches > 1/)
500
-
501
- err = /Data Grid lookup failed/
502
- expect{lookup_grid_helper('infinity',
503
- "G9",
504
- {"state" => "CA", "ltv" => nil},
505
- false, false)}.to raise_error(err)
506
-
507
- res = lookup_grid_helper('infinity',
508
- "G9",
509
- {"state" => nil, "ltv" => 81},
510
- false, false)
511
-
512
- expect(res).to eq [456,"G9"]
513
- end
514
-
515
- it "should handle boolean keys" do
516
- res = lookup_grid_helper('infinity',
517
- "G4",
518
- {"hb_indicator" => true,
519
- "cltv" => 80,
520
- },
521
- )
522
- expect(res).to eq [-1.5,"G4"]
523
-
524
- res = lookup_grid_helper('infinity',
525
- "G4",
526
- {"hb_indicator" => false,
527
- "cltv" => 80,
528
- },
529
- )
530
- expect(res).to eq [nil,"G4"]
531
- end
532
-
533
- it "should handle vertical-only grids" do
534
- res = lookup_grid_helper('infinity',
535
- "G5",
536
- {"ltv" => 80},
537
- )
538
- expect(res).to eq [-0.375,"G5"]
539
- end
540
-
541
- it "should handle horiz-only grids" do
542
- res = lookup_grid_helper('infinity',
543
- "G6",
544
- {"ltv" => 80, "conforming" => true},
545
- )
546
- expect(res).to eq [-0.375,"G6"]
547
- end
548
-
549
- it "should handle string typed data grids" do
550
- expect(Marty::DataGrid.lookup('infinity', "G7").data_type).to eq "string"
551
-
552
- res = lookup_grid_helper('infinity',
553
- "G7",
554
- {"hb_indicator" => true,
555
- "cltv" => 80,
556
- },
557
- )
558
- expect(res).to eq ["test","G7"]
559
- end
560
-
561
- it "should handle DataGrid typed data grids" do
562
- expect(Marty::DataGrid.lookup('infinity', "G8").data_type).
563
- to eq "Marty::DataGrid"
564
- g1 = Marty::DataGrid.lookup('infinity', "G1")
565
-
566
- res = lookup_grid_helper('infinity',
567
- "G8",
568
- {"ltv" => 80,
569
- },
570
- )
571
- expect(res).to eq [g1,"G8"]
572
- end
573
-
574
- it "should handle multi DataGrid lookups" do
575
- expect(Marty::DataGrid.lookup('infinity', "G8").data_type).
576
- to eq "Marty::DataGrid"
577
-
578
- h = {
579
- "fico" => 600,
580
- "state" => "RI",
581
- "ltv" => 10,
582
- }
583
-
584
- g1_res = lookup_grid_helper('infinity', "G1", h)
585
- expect(g1_res).to eq [11,"G1"]
586
-
587
- res = lookup_grid_helper('infinity',
588
- "G8",
589
- h,true
590
- )
591
- expect(g1_res).to eq res
592
- end
593
-
594
- it "should handle DataGrid typed data grids" do
595
- g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G1")
596
-
597
- res = lookup_grid_helper('infinity',
598
- "Ga",
599
- {"dg" => g1,
600
- },
601
- )
602
- expect(res).to eq [7,"Ga"]
603
-
604
- # should be able to lookup bu name as well
605
- res = lookup_grid_helper('infinity',
606
- "Ga",
607
- {"dg" => "G2",
608
- },
609
- )
610
- expect(res).to eq [7,"Ga"]
611
- end
612
-
613
- it "should handle DataGrid typed data grids -- non mcfly" do
614
- ca = Gemini::State.find_by_name("CA")
615
-
616
- res = lookup_grid_helper('infinity',
617
- "Gb",
618
- {"property_state" => ca,
619
- },
620
- )
621
- expect(res).to eq [70,"Gb"]
622
-
623
- # should be able to lookup bu name as well
624
- res = lookup_grid_helper('infinity',
625
- "Gb",
626
- {"property_state" => "CA",
627
- },
628
- )
629
- expect(res).to eq [70,"Gb"]
630
- end
631
-
632
- it "should handle typed (enum) data lookup_grid" do
633
- pt = 'infinity'
634
- ca = Gemini::State.find_by_name("CA")
635
-
636
- res = Marty::DataGrid.
637
- lookup_grid_h(pt, "Gb", {"property_state" => ca}, false)
638
-
639
- expect(res).to eq 70
640
- end
641
-
642
- it "should return grid data and metadata simple" do
643
- expected_data = [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [1.2, 2.3, 3.4],
644
- [4.5, 5.6, 6.7]]
645
- expected_metadata = [{"dir"=>"v",
646
- "attr"=>"units",
647
- "keys"=>[[1, 2], [1, 2], [3, 4], [3, 4]],
648
- "type"=>"integer"},
649
- {"dir"=>"v",
650
- "attr"=>"ltv",
651
- "keys"=>["[,80]", "(80,105]", "[,80]", "(80,105]"],
652
- "type"=>"numrange"},
653
- {"dir"=>"h",
654
- "attr"=>"cltv",
655
- "keys"=>["[100,110)", "[110,120)", "[120,]"],
656
- "type"=>"numrange"},
657
- {"dir"=>"h",
658
- "attr"=>"fico",
659
- "keys"=>["[600,700)", "[700,750)", "[750,]"],
660
- "type"=>"numrange"}]
661
-
662
- dgh = Marty::DataGrid.lookup_h(pt, 'G2')
663
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt, {}, dgh,
664
- nil, true, true)
665
- expect(res["data"]).to eq (expected_data)
666
- expect(res["metadata"]).to eq (expected_metadata)
667
- end
668
-
669
- it "should return grid data and metadata multi (following)" do
670
- expected_data = [[1.1, 2.2, 3.3],[4.4, 5.5, 6.6],[1.2, 2.3, 3.4],
671
- [4.5, 5.6, nil],[11.0, 22.0, 33.0]]
672
- expected_metadata = [{"dir"=>"v",
673
- "attr"=>"state",
674
- "keys"=>[["CA"], ["HI", "TX"], ["NM"], ["MA"], nil],
675
- "type"=>"string"},
676
- {"dir"=>"v",
677
- "attr"=>"ltv",
678
- "keys"=>["[,80]", "(80,105]", "[,80]", "(80,105]",
679
- "[,80]"],
680
- "type"=>"numrange"},
681
- {"dir"=>"h",
682
- "attr"=>"fico",
683
- "keys"=>["[600,700)", "[700,750)", "[750,]"],
684
- "type"=>"numrange"}]
685
- dgh = Marty::DataGrid.lookup_h(pt, 'G8')
686
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
687
- { "ltv" => 10,
688
- "state" => "RI" },
689
- dgh, nil, true,
690
- true)
691
- expect(res["data"]).to eq (expected_data)
692
- expect(res["metadata"]).to eq (expected_metadata)
693
- end
694
-
695
- it "should return grid data and metadata multi (not following)" do
696
- expected_data = [["G1"], ["G2"], ["G3"]]
697
- expected_metadata = [{"dir"=>"v",
698
- "attr"=>"ltv",
699
- "keys"=>["[,115]", "(115,135]", "(135,140]"],
700
- "type"=>"numrange"}]
701
- dgh = Marty::DataGrid.lookup_h(pt, 'G8')
702
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
703
- { "ltv" => 10,
704
- "state" => "RI" },
705
- dgh, nil, false,
706
- true)
707
- expect(res["data"]).to eq (expected_data)
708
- expect(res["metadata"]).to eq (expected_metadata)
709
- end
710
-
711
- it "should handle all characters in grid inputs" do
712
- dgh = Marty::DataGrid.lookup_h(pt, 'G1')
713
- 5000.times do
714
- st = 30.times.map{rand(224)+32}.pack('U*')
715
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
716
- { "ltv" => 10,
717
- "fico" => 690,
718
- "state" => st },
719
- dgh, nil, false, true)
720
- end
721
- end
722
- it "should handle all quote chars in grid inputs" do
723
- dgh = Marty::DataGrid.lookup_h(pt, 'G1')
724
- # single, double, backslash, grave, acute, unicode quotes: left single,
725
- # right single, left double, right double
726
- quotes = ["'", '"', '\\', '`', "\u00b4", "\u2018", "\u2019",
727
- "\u201C", "\u201D"]
728
- 100.times do
729
- st = 30.times.map{quotes[rand(9)]}.join
730
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(
731
- pt, {"ltv" => 10, "fico" => 690, "state" => st}, dgh, nil, false, true)
732
- end
733
- end
734
- it "should handle quote chars in object name" do
735
- dgh = Marty::DataGrid.lookup_h(pt, 'G1')
736
- st = Gemini::State.new(name: "'\\")
737
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(
738
- pt, {"ltv" => 10, "fico" => 690, "state" => st}, dgh, nil, false, true)
739
- end
409
+ expect(res).to eq [1.655, 'G3']
410
+
411
+ [3, 4].each do |units|
412
+ res = lookup_grid_helper('infinity',
413
+ 'G2',
414
+ 'fico' => 720,
415
+ 'units' => units,
416
+ 'ltv' => 100,
417
+ 'cltv' => 110.1,
418
+ )
419
+ expect(res).to eq [5.6, 'G2']
740
420
  end
741
421
 
742
- describe "exports" do
743
- it 'should export lenient grids correctly' do
744
- dg = dg_from_import("Gf", Gf)
745
- dg2 = dg_from_import("Gf2", dg.export)
422
+ dg = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G1')
423
+
424
+ h = {
425
+ 'fico' => 600,
426
+ 'state' => 'RI',
427
+ 'ltv' => 10,
428
+ }
746
429
 
747
- expect(dg.export).to eq(dg2.export)
748
- end
430
+ res = lookup_grid_helper('infinity', 'G1', h)
431
+ expect(res).to eq [11, 'G1']
432
+
433
+ dg.update_from_import('G1', G1.sub(/11/, '111'))
434
+
435
+ res = lookup_grid_helper('infinity', 'G1', h)
436
+ expect(res).to eq [111, 'G1']
437
+ end
438
+
439
+ it 'should result in error when there are multiple cell hits' do
440
+ expect do
441
+ lookup_grid_helper('infinity',
442
+ 'G2',
443
+ 'fico' => 720,
444
+ 'ltv' => 100,
445
+ 'cltv' => 110.1,
446
+ )
447
+ end.to raise_error(RuntimeError)
448
+ end
449
+
450
+ it 'should return nil when matching data grid cell is nil' do
451
+ res = lookup_grid_helper('infinity',
452
+ 'G1',
453
+ 'fico' => 800,
454
+ 'state' => 'MA',
455
+ 'ltv' => 81,
456
+ )
457
+ expect(res).to eq [nil, 'G1']
458
+ end
459
+
460
+ it 'should handle string wildcards' do
461
+ res = lookup_grid_helper('infinity',
462
+ 'G1',
463
+ 'fico' => 720,
464
+ 'state' => 'GU',
465
+ 'ltv' => 80,
466
+ )
467
+ expect(res).to eq [22, 'G1']
468
+ end
469
+
470
+ it 'should handle matches which also have a wildcard match' do
471
+ dg_from_import('G9', G9)
472
+
473
+ expect do
474
+ res = lookup_grid_helper('infinity',
475
+ 'G9',
476
+ 'state' => 'CA', 'ltv' => 81,
477
+ )
478
+ end.to raise_error(RuntimeError)
479
+
480
+ res = lookup_grid_helper('infinity',
481
+ 'G9',
482
+ 'state' => 'GU', 'ltv' => 81,
483
+ )
484
+ expect(res).to eq [456, 'G9']
485
+ end
486
+
487
+ it 'should raise on nil attr values' do
488
+ dg_from_import('G9', G9)
489
+
490
+ expect do
491
+ lookup_grid_helper('infinity',
492
+ 'G9',
493
+ 'ltv' => 81,
494
+ )
495
+ end .to raise_error(/matches > 1/)
496
+
497
+ err = /Data Grid lookup failed/
498
+ expect do
499
+ lookup_grid_helper('infinity',
500
+ 'G9',
501
+ { 'state' => 'CA', 'ltv' => nil },
502
+ false, false)
503
+ end .to raise_error(err)
504
+
505
+ res = lookup_grid_helper('infinity',
506
+ 'G9',
507
+ { 'state' => nil, 'ltv' => 81 },
508
+ false, false)
509
+
510
+ expect(res).to eq [456, 'G9']
511
+ end
512
+
513
+ it 'should handle boolean keys' do
514
+ res = lookup_grid_helper('infinity',
515
+ 'G4',
516
+ 'hb_indicator' => true,
517
+ 'cltv' => 80,
518
+ )
519
+ expect(res).to eq [-1.5, 'G4']
520
+
521
+ res = lookup_grid_helper('infinity',
522
+ 'G4',
523
+ 'hb_indicator' => false,
524
+ 'cltv' => 80,
525
+ )
526
+ expect(res).to eq [nil, 'G4']
527
+ end
528
+
529
+ it 'should handle vertical-only grids' do
530
+ res = lookup_grid_helper('infinity',
531
+ 'G5',
532
+ 'ltv' => 80,
533
+ )
534
+ expect(res).to eq [-0.375, 'G5']
535
+ end
536
+
537
+ it 'should handle horiz-only grids' do
538
+ res = lookup_grid_helper('infinity',
539
+ 'G6',
540
+ 'ltv' => 80, 'conforming' => true,
541
+ )
542
+ expect(res).to eq [-0.375, 'G6']
543
+ end
544
+
545
+ it 'should handle string typed data grids' do
546
+ expect(Marty::DataGrid.lookup('infinity', 'G7').data_type).to eq 'string'
547
+
548
+ res = lookup_grid_helper('infinity',
549
+ 'G7',
550
+ 'hb_indicator' => true,
551
+ 'cltv' => 80,
552
+ )
553
+ expect(res).to eq ['test', 'G7']
554
+ end
555
+
556
+ it 'should handle DataGrid typed data grids' do
557
+ expect(Marty::DataGrid.lookup('infinity', 'G8').data_type).
558
+ to eq 'Marty::DataGrid'
559
+ g1 = Marty::DataGrid.lookup('infinity', 'G1')
560
+
561
+ res = lookup_grid_helper('infinity',
562
+ 'G8',
563
+ 'ltv' => 80,
564
+ )
565
+ expect(res).to eq [g1, 'G8']
566
+ end
567
+
568
+ it 'should handle multi DataGrid lookups' do
569
+ expect(Marty::DataGrid.lookup('infinity', 'G8').data_type).
570
+ to eq 'Marty::DataGrid'
571
+
572
+ h = {
573
+ 'fico' => 600,
574
+ 'state' => 'RI',
575
+ 'ltv' => 10,
576
+ }
577
+
578
+ g1_res = lookup_grid_helper('infinity', 'G1', h)
579
+ expect(g1_res).to eq [11, 'G1']
580
+
581
+ res = lookup_grid_helper('infinity',
582
+ 'G8',
583
+ h, true
584
+ )
585
+ expect(g1_res).to eq res
586
+ end
587
+
588
+ it 'should handle DataGrid typed data grids' do
589
+ g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G1')
590
+
591
+ res = lookup_grid_helper('infinity',
592
+ 'Ga',
593
+ 'dg' => g1,
594
+ )
595
+ expect(res).to eq [7, 'Ga']
596
+
597
+ # should be able to lookup bu name as well
598
+ res = lookup_grid_helper('infinity',
599
+ 'Ga',
600
+ 'dg' => 'G2',
601
+ )
602
+ expect(res).to eq [7, 'Ga']
603
+ end
604
+
605
+ it 'should handle DataGrid typed data grids -- non mcfly' do
606
+ ca = Gemini::State.find_by_name('CA')
607
+
608
+ res = lookup_grid_helper('infinity',
609
+ 'Gb',
610
+ 'property_state' => ca,
611
+ )
612
+ expect(res).to eq [70, 'Gb']
613
+
614
+ # should be able to lookup bu name as well
615
+ res = lookup_grid_helper('infinity',
616
+ 'Gb',
617
+ 'property_state' => 'CA',
618
+ )
619
+ expect(res).to eq [70, 'Gb']
620
+ end
621
+
622
+ it 'should handle typed (enum) data lookup_grid' do
623
+ pt = 'infinity'
624
+ ca = Gemini::State.find_by_name('CA')
625
+
626
+ res = Marty::DataGrid.
627
+ lookup_grid_h(pt, 'Gb', { 'property_state' => ca }, false)
628
+
629
+ expect(res).to eq 70
630
+ end
631
+
632
+ it 'should return grid data and metadata simple' do
633
+ expected_data = [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [1.2, 2.3, 3.4],
634
+ [4.5, 5.6, 6.7]]
635
+ expected_metadata = [{ 'dir' => 'v',
636
+ 'attr' => 'units',
637
+ 'keys' => [[1, 2], [1, 2], [3, 4], [3, 4]],
638
+ 'type' => 'integer' },
639
+ { 'dir' => 'v',
640
+ 'attr' => 'ltv',
641
+ 'keys' => ['[,80]', '(80,105]', '[,80]', '(80,105]'],
642
+ 'type' => 'numrange' },
643
+ { 'dir' => 'h',
644
+ 'attr' => 'cltv',
645
+ 'keys' => ['[100,110)', '[110,120)', '[120,]'],
646
+ 'type' => 'numrange' },
647
+ { 'dir' => 'h',
648
+ 'attr' => 'fico',
649
+ 'keys' => ['[600,700)', '[700,750)', '[750,]'],
650
+ 'type' => 'numrange' }]
651
+
652
+ dgh = Marty::DataGrid.lookup_h(pt, 'G2')
653
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt, {}, dgh,
654
+ nil, true, true)
655
+ expect(res['data']).to eq expected_data
656
+ expect(res['metadata']).to eq expected_metadata
657
+ end
658
+
659
+ it 'should return grid data and metadata multi (following)' do
660
+ expected_data = [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [1.2, 2.3, 3.4],
661
+ [4.5, 5.6, nil], [11.0, 22.0, 33.0]]
662
+ expected_metadata = [{ 'dir' => 'v',
663
+ 'attr' => 'state',
664
+ 'keys' => [['CA'], ['HI', 'TX'], ['NM'], ['MA'], nil],
665
+ 'type' => 'string' },
666
+ { 'dir' => 'v',
667
+ 'attr' => 'ltv',
668
+ 'keys' => ['[,80]', '(80,105]', '[,80]', '(80,105]',
669
+ '[,80]'],
670
+ 'type' => 'numrange' },
671
+ { 'dir' => 'h',
672
+ 'attr' => 'fico',
673
+ 'keys' => ['[600,700)', '[700,750)', '[750,]'],
674
+ 'type' => 'numrange' }]
675
+ dgh = Marty::DataGrid.lookup_h(pt, 'G8')
676
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
677
+ { 'ltv' => 10,
678
+ 'state' => 'RI' },
679
+ dgh, nil, true,
680
+ true)
681
+ expect(res['data']).to eq expected_data
682
+ expect(res['metadata']).to eq expected_metadata
683
+ end
684
+
685
+ it 'should return grid data and metadata multi (not following)' do
686
+ expected_data = [['G1'], ['G2'], ['G3']]
687
+ expected_metadata = [{ 'dir' => 'v',
688
+ 'attr' => 'ltv',
689
+ 'keys' => ['[,115]', '(115,135]', '(135,140]'],
690
+ 'type' => 'numrange' }]
691
+ dgh = Marty::DataGrid.lookup_h(pt, 'G8')
692
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
693
+ { 'ltv' => 10,
694
+ 'state' => 'RI' },
695
+ dgh, nil, false,
696
+ true)
697
+ expect(res['data']).to eq expected_data
698
+ expect(res['metadata']).to eq expected_metadata
699
+ end
700
+
701
+ it 'should handle all characters in grid inputs' do
702
+ dgh = Marty::DataGrid.lookup_h(pt, 'G1')
703
+ 5000.times do
704
+ st = 30.times.map { rand(32..255) }.pack('U*')
705
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
706
+ { 'ltv' => 10,
707
+ 'fico' => 690,
708
+ 'state' => st },
709
+ dgh, nil, false, true)
749
710
  end
711
+ end
712
+ it 'should handle all quote chars in grid inputs' do
713
+ dgh = Marty::DataGrid.lookup_h(pt, 'G1')
714
+ # single, double, backslash, grave, acute, unicode quotes: left single,
715
+ # right single, left double, right double
716
+ quotes = ["'", '"', '\\', '`', "\u00b4", "\u2018", "\u2019",
717
+ "\u201C", "\u201D"]
718
+ 100.times do
719
+ st = 30.times.map { quotes[rand(9)] }.join
720
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(
721
+ pt, { 'ltv' => 10, 'fico' => 690, 'state' => st }, dgh, nil, false, true)
722
+ end
723
+ end
724
+ it 'should handle quote chars in object name' do
725
+ dgh = Marty::DataGrid.lookup_h(pt, 'G1')
726
+ st = Gemini::State.new(name: "'\\")
727
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(
728
+ pt, { 'ltv' => 10, 'fico' => 690, 'state' => st }, dgh, nil, false, true)
729
+ end
730
+ end
731
+
732
+ describe 'exports' do
733
+ it 'should export lenient grids correctly' do
734
+ dg = dg_from_import('Gf', Gf)
735
+ dg2 = dg_from_import('Gf2', dg.export)
736
+
737
+ expect(dg.export).to eq(dg2.export)
738
+ end
739
+ end
740
+
741
+ describe 'updates' do
742
+ it 'should be possible to modify a grid referenced from a multi-grid' do
743
+ dgb = dg_from_import('Gb', Gb, '1/1/2014')
744
+ dgc = dg_from_import('Gc', Gc, '2/2/2014')
745
+
746
+ dgb.update_from_import('Gb', Gb.sub(/70/, '333'), '1/1/2015')
747
+ dgb.update_from_import('Gb', Gb.sub(/70/, '444'), '1/1/2016')
748
+
749
+ dgch = dgc.attributes.
750
+ slice('id', 'group_id', 'created_dt', 'metadata', 'data_type')
751
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2014',
752
+ { 'property_state' => 'CA' }, dgch)
753
+ expect(res['result']).to eq(70)
754
+
755
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2015',
756
+ { 'property_state' => 'CA' }, dgch)
750
757
 
751
- describe "updates" do
752
- it "should be possible to modify a grid referenced from a multi-grid" do
753
- dgb = dg_from_import("Gb", Gb, '1/1/2014')
754
- dgc = dg_from_import("Gc", Gc, '2/2/2014')
755
-
756
- dgb.update_from_import("Gb", Gb.sub(/70/, "333"), '1/1/2015')
757
- dgb.update_from_import("Gb", Gb.sub(/70/, "444"), '1/1/2016')
758
-
759
- dgch = dgc.attributes.
760
- slice("id","group_id","created_dt", "metadata", "data_type")
761
- res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2014',
762
- {"property_state" => "CA"}, dgch)
763
- expect(res["result"]).to eq(70)
764
-
765
- res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2015',
766
- {"property_state" => "CA"}, dgch)
767
-
768
- expect(res["result"]).to eq(333)
769
-
770
- res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2016',
771
- {"property_state" => "CA"}, dgch)
772
-
773
- expect(res["result"]).to eq(444)
774
- end
775
-
776
- it "should not create a new version if no change has been made" do
777
- dg = dg_from_import("G4", G1)
778
- dg.update_from_import("G4", G1)
779
- expect(Marty::DataGrid.where(group_id: dg.group_id).count).to eq 1
780
- end
781
-
782
- it "should be able to export and import back grids" do
783
- [G1, G2, G3, G4, G5, G6, G7, G8, G9, Ga, Gb].each_with_index do
784
- |grid, i|
785
- dg = dg_from_import("G#{i}", grid)
786
- g1 = dg.export
787
- dg = dg_from_import("Gx#{i}", g1)
788
- g2 = dg.export
789
- expect(g1).to eq g2
790
- end
791
- end
792
-
793
- it "should be able to externally export/import grids" do
794
- load_scripts(nil, Date.today)
795
-
796
- dg = dg_from_import("G1", G1)
797
-
798
- p = posting("BASE", DateTime.tomorrow, '?')
799
-
800
- engine = Marty::ScriptSet.new.get_engine("DataReport")
801
- res = engine.evaluate("TableReport",
802
- "result",
803
- {
804
- "pt_name" => p.name,
805
- "class_name" => "Marty::DataGrid",
806
- },
807
- )
808
-
809
- # FIXME: really hacky removing "" (data_grid) -- This is a bug
810
- # in TableReport/CSV generation.
811
- res.gsub!(/\"\"/, '')
812
- sum = do_import_summary(Marty::DataGrid,
813
- res,
814
- 'infinity',
815
- nil,
816
- nil,
817
- ",",
818
- )
819
-
820
- expect(sum).to eq({same: 1})
821
-
822
- res11 = res.sub(/G1/, "G11")
823
-
824
- sum = do_import_summary(
825
- Marty::DataGrid, res11, 'infinity', nil, nil, ",")
826
-
827
- expect(sum).to eq({create: 1})
828
-
829
- g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G1")
830
- g11 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G11")
831
-
832
- expect(g1.export).to eq g11.export
833
- end
758
+ expect(res['result']).to eq(333)
759
+
760
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2016',
761
+ { 'property_state' => 'CA' }, dgch)
762
+
763
+ expect(res['result']).to eq(444)
764
+ end
765
+
766
+ it 'should not create a new version if no change has been made' do
767
+ dg = dg_from_import('G4', G1)
768
+ dg.update_from_import('G4', G1)
769
+ expect(Marty::DataGrid.where(group_id: dg.group_id).count).to eq 1
770
+ end
771
+
772
+ it 'should be able to export and import back grids' do
773
+ [G1, G2, G3, G4, G5, G6, G7, G8, G9, Ga, Gb].each_with_index do |grid, i|
774
+ dg = dg_from_import("G#{i}", grid)
775
+ g1 = dg.export
776
+ dg = dg_from_import("Gx#{i}", g1)
777
+ g2 = dg.export
778
+ expect(g1).to eq g2
834
779
  end
835
780
  end
781
+
782
+ it 'should be able to externally export/import grids' do
783
+ load_scripts(nil, Date.today)
784
+
785
+ dg = dg_from_import('G1', G1)
786
+
787
+ p = posting('BASE', DateTime.tomorrow, '?')
788
+
789
+ engine = Marty::ScriptSet.new.get_engine('DataReport')
790
+ res = engine.evaluate('TableReport',
791
+ 'result',
792
+ 'pt_name' => p.name,
793
+ 'class_name' => 'Marty::DataGrid',
794
+ )
795
+
796
+ # FIXME: really hacky removing "" (data_grid) -- This is a bug
797
+ # in TableReport/CSV generation.
798
+ res.gsub!(/\"\"/, '')
799
+ sum = do_import_summary(Marty::DataGrid,
800
+ res,
801
+ 'infinity',
802
+ nil,
803
+ nil,
804
+ ',',
805
+ )
806
+
807
+ expect(sum).to eq(same: 1)
808
+
809
+ res11 = res.sub(/G1/, 'G11')
810
+
811
+ sum = do_import_summary(
812
+ Marty::DataGrid, res11, 'infinity', nil, nil, ',')
813
+
814
+ expect(sum).to eq(create: 1)
815
+
816
+ g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G1')
817
+ g11 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G11')
818
+
819
+ expect(g1.export).to eq g11.export
820
+ end
821
+ end
822
+ end
836
823
  end