marty 1.2.0 → 1.2.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 +4 -4
- data/Gemfile +2 -1
- data/Gemfile.lock +2 -2
- data/app/components/marty/base_rule_view.rb +279 -0
- data/app/components/marty/delorean_rule_view.rb +26 -0
- data/app/components/marty/extras/layout.rb +22 -7
- data/app/components/marty/log_view.rb +1 -1
- data/app/components/marty/mcfly_grid_panel.rb +53 -0
- data/app/components/marty/mcfly_grid_panel/client/dup_in_form.js +20 -0
- data/app/models/marty/base_rule.rb +126 -0
- data/app/models/marty/delorean_rule.rb +121 -0
- data/lib/marty/data_importer.rb +0 -1
- data/lib/marty/rule_script_set.rb +176 -0
- data/lib/marty/version.rb +1 -1
- data/lib/tasks/marty_tasks.rake +42 -0
- data/spec/dummy/app/components/gemini/cm_auth_app.rb +18 -0
- data/spec/dummy/app/components/gemini/my_rule_view.rb +63 -0
- data/spec/dummy/app/components/gemini/xyz_rule_view.rb +25 -0
- data/spec/dummy/app/models/gemini/guard_one.rb +5 -0
- data/spec/dummy/app/models/gemini/guard_two.rb +5 -0
- data/spec/dummy/app/models/gemini/my_rule.rb +46 -0
- data/spec/dummy/app/models/gemini/my_rule_type.rb +5 -0
- data/spec/dummy/app/models/gemini/xyz_enum.rb +5 -0
- data/spec/dummy/app/models/gemini/xyz_rule.rb +63 -0
- data/spec/dummy/app/models/gemini/xyz_rule_type.rb +5 -0
- data/spec/dummy/config/locales/en.yml +10 -0
- data/spec/dummy/db/migrate/20171220150101_add_rule_type_enums.rb +14 -0
- data/spec/dummy/db/migrate/20171221095312_create_gemini_my_rules.rb +22 -0
- data/spec/dummy/db/migrate/20171221095359_create_gemini_xyz_rules.rb +21 -0
- data/spec/dummy/db/migrate/20171222150100_add_rule_indices.rb +34 -0
- data/spec/dummy/db/seeds.rb +1 -1
- data/spec/dummy/delorean/base_code.dl +6 -0
- data/spec/dummy/lib/gemini/my_rule_script_set.rb +13 -0
- data/spec/dummy/lib/gemini/xyz_rule_script_set.rb +22 -0
- data/spec/features/rule_spec.rb +265 -0
- data/spec/fixtures/csv/rule/DataGrid.csv +6 -0
- data/spec/fixtures/csv/rule/MyRule.csv +14 -0
- data/spec/fixtures/csv/rule/XyzRule.csv +6 -0
- data/spec/models/rule_spec.rb +322 -0
- data/spec/support/integration_helpers.rb +1 -0
- metadata +29 -2
data/lib/marty/version.rb
CHANGED
data/lib/tasks/marty_tasks.rake
CHANGED
@@ -23,6 +23,48 @@ namespace :marty do
|
|
23
23
|
Marty::Script.load_scripts(load_dir)
|
24
24
|
end
|
25
25
|
|
26
|
+
# currently this is for delorean style rules only. if other types were ever
|
27
|
+
# added (eg some sort of SQL rule like apollo has), that would probably be
|
28
|
+
# a new rake task
|
29
|
+
desc 'generate rule table migration'
|
30
|
+
task :generate_rule_table_migration, [:table] => :environment do |t, args|
|
31
|
+
(puts "Usage: rake marty:generate_rule_table_migration[<table name>]"
|
32
|
+
next) unless args[:table]
|
33
|
+
table = args[:table]
|
34
|
+
filename = Rails.root.join("db/migrate",Time.zone.now.strftime(
|
35
|
+
"%Y%m%d%H%M%S_create_#{table}.rb"))
|
36
|
+
puts "creating #{filename}"
|
37
|
+
File.open(filename, "w") do |f|
|
38
|
+
f.puts <<~EOF
|
39
|
+
class Create#{table.camelize} < McflyMigration
|
40
|
+
include Marty::Migrations
|
41
|
+
def change()
|
42
|
+
create_table :#{table} do |t|
|
43
|
+
t.string :name, null: false
|
44
|
+
# set type enum
|
45
|
+
t.column :rule_type, :enum_name, null: false
|
46
|
+
t.datetime :start_dt, null: false
|
47
|
+
t.datetime :end_dt, null: true
|
48
|
+
t.string :engine, null: true # add engine default if used
|
49
|
+
# add any additional attrs here
|
50
|
+
t.jsonb :simple_guards, null: false, default: {}
|
51
|
+
t.json :computed_guards, null: false, default: {}
|
52
|
+
t.jsonb :grids, null: false, default: {}
|
53
|
+
t.json :results, null: false, default: {}
|
54
|
+
# only needed for delorean type rules
|
55
|
+
t.jsonb :fixed_results, null: false, default: {}
|
56
|
+
end
|
57
|
+
execute("CREATE OR REPLACE FUNCTION to_numrange(val text) "\\
|
58
|
+
"RETURNS numrange AS "\\
|
59
|
+
"$BODY$ select numrange(val); $BODY$ "\\
|
60
|
+
"LANGUAGE SQL IMMUTABLE;")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
EOF
|
64
|
+
end
|
65
|
+
puts "please edit the migration file to customize the rule"
|
66
|
+
end
|
67
|
+
|
26
68
|
desc 'Print out all models and their fields'
|
27
69
|
task print_schema: :environment do
|
28
70
|
Rails.application.eager_load!
|
@@ -11,6 +11,8 @@ class Gemini::CmAuthApp < Marty::MainAuthApp
|
|
11
11
|
icon: icon_hack(:database_key),
|
12
12
|
menu: [
|
13
13
|
:loan_program_view,
|
14
|
+
:my_rule_view,
|
15
|
+
:xyz_rule_view,
|
14
16
|
],
|
15
17
|
}
|
16
18
|
]
|
@@ -21,7 +23,23 @@ class Gemini::CmAuthApp < Marty::MainAuthApp
|
|
21
23
|
a.handler = :netzke_load_component_by_action
|
22
24
|
end
|
23
25
|
|
26
|
+
action :my_rule_view do |a|
|
27
|
+
a.text = a.tooltip = 'My Rules'
|
28
|
+
a.handler = :netzke_load_component_by_action
|
29
|
+
end
|
30
|
+
|
31
|
+
action :xyz_rule_view do |a|
|
32
|
+
a.text = a.tooltip = 'Xyz Rules'
|
33
|
+
a.handler = :netzke_load_component_by_action
|
34
|
+
end
|
35
|
+
|
24
36
|
component :loan_program_view do |c|
|
25
37
|
c.klass = Gemini::LoanProgramView
|
26
38
|
end
|
39
|
+
component :my_rule_view do |c|
|
40
|
+
c.klass = Gemini::MyRuleView
|
41
|
+
end
|
42
|
+
component :xyz_rule_view do |c|
|
43
|
+
c.klass = Gemini::XyzRuleView
|
44
|
+
end
|
27
45
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Gemini::MyRuleView < Marty::DeloreanRuleView
|
2
|
+
has_marty_permissions create: :admin,
|
3
|
+
read: :admin,
|
4
|
+
update: :admin,
|
5
|
+
delete: :admin
|
6
|
+
|
7
|
+
def self.base_fields
|
8
|
+
super + [:other_flag]
|
9
|
+
end
|
10
|
+
def configure(c)
|
11
|
+
super
|
12
|
+
c.title = 'My Rules'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.klass
|
16
|
+
Gemini::MyRule
|
17
|
+
end
|
18
|
+
|
19
|
+
attribute :other_flag do |c|
|
20
|
+
c.width = 75
|
21
|
+
end
|
22
|
+
|
23
|
+
def form_items_grids
|
24
|
+
[
|
25
|
+
self.class.grid_column(:grid1),
|
26
|
+
self.class.grid_column(:grid2),
|
27
|
+
]
|
28
|
+
end
|
29
|
+
def default_form_items
|
30
|
+
[
|
31
|
+
hbox(
|
32
|
+
vbox(*form_items_attrs +
|
33
|
+
form_items_guards +
|
34
|
+
form_items_grids,
|
35
|
+
border: false,
|
36
|
+
width: "40%",
|
37
|
+
),
|
38
|
+
vbox(width: '2%', border: false),
|
39
|
+
vbox(
|
40
|
+
width: '55%', border: false),
|
41
|
+
height: '56%',
|
42
|
+
border: false,
|
43
|
+
),
|
44
|
+
hbox(
|
45
|
+
vbox(*form_items_computed_guards +
|
46
|
+
form_items_results,
|
47
|
+
width: '99%',
|
48
|
+
border: false
|
49
|
+
),
|
50
|
+
height: '35%',
|
51
|
+
border: false
|
52
|
+
)
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
self.init_fields
|
57
|
+
|
58
|
+
attribute :rule_type do |c|
|
59
|
+
c.width = 200
|
60
|
+
enum_column(c, Gemini::MyRuleType)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Gemini::XyzRuleView < Marty::DeloreanRuleView
|
2
|
+
has_marty_permissions create: :admin,
|
3
|
+
read: :admin,
|
4
|
+
update: :admin,
|
5
|
+
delete: :admin
|
6
|
+
def self.klass
|
7
|
+
Gemini::XyzRule
|
8
|
+
end
|
9
|
+
|
10
|
+
def configure(c)
|
11
|
+
super
|
12
|
+
c.title = 'Xyz Rules'
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_form_items
|
16
|
+
super
|
17
|
+
end
|
18
|
+
self.init_fields
|
19
|
+
|
20
|
+
attribute :rule_type do |c|
|
21
|
+
c.width = 200
|
22
|
+
enum_column(c, Gemini::XyzRuleType)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Gemini::MyRule < Marty::DeloreanRule
|
2
|
+
self.table_name = 'gemini_my_rules'
|
3
|
+
|
4
|
+
gen_mcfly_lookup :lookup, {
|
5
|
+
name: false,
|
6
|
+
}
|
7
|
+
|
8
|
+
cached_mcfly_lookup :lookup_id, sig: 2 do
|
9
|
+
|pt, group_id|
|
10
|
+
find_by_group_id group_id
|
11
|
+
end
|
12
|
+
|
13
|
+
mcfly_validates_uniqueness_of :name, scope: [:start_dt, :end_dt]
|
14
|
+
|
15
|
+
def self.guard_info
|
16
|
+
super + {"g_array" => { multi: true, type: :string,
|
17
|
+
enum: Gemini::GuardOne,},
|
18
|
+
"g_single" => { type: :string,
|
19
|
+
enum: Gemini::GuardTwo,
|
20
|
+
width: 100},
|
21
|
+
"g_string" => { type: :string,
|
22
|
+
values: ["Hi Mom", "abc", "def", "zzz"],
|
23
|
+
width: 100},
|
24
|
+
"g_bool" => { type: :boolean,
|
25
|
+
width: 100},
|
26
|
+
"g_range" => { type: :range,
|
27
|
+
width: 100},
|
28
|
+
"g_integer" => { type: :integer,
|
29
|
+
width: 100},
|
30
|
+
"g_has_default" => { type: :string,
|
31
|
+
default: "string default"}}
|
32
|
+
end
|
33
|
+
def self.results_cfg_var
|
34
|
+
'RULEOPTS_MYRULE'
|
35
|
+
end
|
36
|
+
|
37
|
+
mcfly_lookup :get_matches, sig: 3 do
|
38
|
+
|pt, attrs, params|
|
39
|
+
get_matches_(pt, attrs, params)
|
40
|
+
end
|
41
|
+
|
42
|
+
def compute(*args)
|
43
|
+
base_compute(*args)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Gemini::XyzRule < Marty::DeloreanRule
|
2
|
+
self.table_name = 'gemini_xyz_rules'
|
3
|
+
|
4
|
+
gen_mcfly_lookup :lookup, {
|
5
|
+
name: false,
|
6
|
+
}
|
7
|
+
|
8
|
+
cached_mcfly_lookup :lookup_id, sig: 2 do
|
9
|
+
|pt, group_id|
|
10
|
+
find_by_group_id group_id
|
11
|
+
end
|
12
|
+
|
13
|
+
mcfly_validates_uniqueness_of :name, scope: [:start_dt, :end_dt]
|
14
|
+
|
15
|
+
def self.results_cfg_var
|
16
|
+
'RULEOPTS_XYZ'
|
17
|
+
end
|
18
|
+
def self.guard_info
|
19
|
+
super + {"flavors" => { multi: true, type: :string,
|
20
|
+
enum: Gemini::XyzEnum,
|
21
|
+
width: 150},
|
22
|
+
"guard_two" => { type: :string,
|
23
|
+
enum: Gemini::GuardTwo,
|
24
|
+
width: 100},
|
25
|
+
"g_date" => { type: :date },
|
26
|
+
"g_datetime" => { type: :datetime },
|
27
|
+
"g_string" => { type: :string,
|
28
|
+
width: 100},
|
29
|
+
"g_bool" => { type: :boolean,
|
30
|
+
width: 100},
|
31
|
+
"g_range1" => { type: :range,
|
32
|
+
width: 100},
|
33
|
+
"g_range2" => { type: :range,
|
34
|
+
width: 100},
|
35
|
+
"g_integer" => { type: :integer,
|
36
|
+
width: 100}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
mcfly_lookup :get_matches, sig: 3 do
|
41
|
+
|pt, attrs, params|
|
42
|
+
get_matches_(pt, attrs, params)
|
43
|
+
end
|
44
|
+
|
45
|
+
def compute(*args)
|
46
|
+
base_compute(*args)
|
47
|
+
end
|
48
|
+
def compute_xyz(pt, xyz_param)
|
49
|
+
# Given a set of parameters, compute the RULE adjustment. Returns
|
50
|
+
# {} if precondition is not met.
|
51
|
+
|
52
|
+
xyz_keys = computed_guards.select{|k,_|k.starts_with?("xyz_")}.keys
|
53
|
+
return {} unless xyz_keys.present?
|
54
|
+
|
55
|
+
eclass = engine && engine.constantize || Marty::RuleScriptSet
|
56
|
+
engine = eclass.new(pt).get_engine(self)
|
57
|
+
res = engine.evaluate("XyzNode", xyz_keys, {"xyz_param"=>xyz_param})
|
58
|
+
|
59
|
+
res.all?
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class AddRuleTypeEnums < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
[Gemini::XyzRuleType, Gemini::MyRuleType, Gemini::GuardOne,
|
4
|
+
Gemini::GuardTwo, Gemini::XyzEnum].each do |cl|
|
5
|
+
values = cl::VALUES
|
6
|
+
str_values = values.map {|v| ActiveRecord::Base.connection.quote v}.
|
7
|
+
join(',')
|
8
|
+
clstr = cl.to_s.sub('Gemini::','').underscore
|
9
|
+
execute <<-SQL
|
10
|
+
CREATE TYPE #{clstr} AS ENUM (#{str_values})
|
11
|
+
SQL
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateGeminiMyRules < McflyMigration
|
2
|
+
include Marty::Migrations
|
3
|
+
def change()
|
4
|
+
create_table :gemini_my_rules do |t|
|
5
|
+
t.string :name, null: false
|
6
|
+
t.column :rule_type, :my_rule_type, null: false
|
7
|
+
t.datetime :start_dt, null: false
|
8
|
+
t.datetime :end_dt, null: true
|
9
|
+
t.string :engine, null: false, default: 'Gemini::MyRuleScriptSet'
|
10
|
+
t.boolean :other_flag
|
11
|
+
t.jsonb :simple_guards, null: false, default: {}
|
12
|
+
t.json :computed_guards, null: false, default: {}
|
13
|
+
t.jsonb :grids, null: false, default: {}
|
14
|
+
t.json :results, null: false, default: {}
|
15
|
+
t.jsonb :fixed_results, null: false, default: {}
|
16
|
+
end
|
17
|
+
execute("CREATE OR REPLACE FUNCTION to_numrange(val text) "\
|
18
|
+
"RETURNS numrange AS "\
|
19
|
+
"$BODY$ select numrange(val); $BODY$ "\
|
20
|
+
"LANGUAGE SQL IMMUTABLE;")
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateGeminiXyzRules < McflyMigration
|
2
|
+
include Marty::Migrations
|
3
|
+
def change()
|
4
|
+
create_table :gemini_xyz_rules do |t|
|
5
|
+
t.string :name, null: false
|
6
|
+
t.column :rule_type, :xyz_rule_type, null: false
|
7
|
+
t.datetime :start_dt, null: false
|
8
|
+
t.datetime :end_dt, null: true
|
9
|
+
t.string :engine, null: false, default: 'Gemini::XyzRuleScriptSet'
|
10
|
+
t.jsonb :simple_guards, null: false, default: {}
|
11
|
+
t.json :computed_guards, null: false, default: {}
|
12
|
+
t.jsonb :grids, null: false, default: {}
|
13
|
+
t.json :results, null: false, default: {}
|
14
|
+
t.jsonb :fixed_results, null: false, default: {}
|
15
|
+
end
|
16
|
+
execute("CREATE OR REPLACE FUNCTION to_numrange(val text) "\
|
17
|
+
"RETURNS numrange AS "\
|
18
|
+
"$BODY$ select numrange(val); $BODY$ "\
|
19
|
+
"LANGUAGE SQL IMMUTABLE;")
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class AddRuleIndices < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
['gemini_my_rules', 'g_array', :array,
|
4
|
+
'gemini_my_rules', 'g_single', :scalar,
|
5
|
+
'gemini_my_rules', 'g_string', :scalar,
|
6
|
+
'gemini_my_rules', 'g_bool', :scalar,
|
7
|
+
'gemini_my_rules', 'g_integer', :scalar,
|
8
|
+
'gemini_my_rules', 'g_range', :range,
|
9
|
+
'gemini_xyz_rules', 'flavors', :array,
|
10
|
+
'gemini_xyz_rules', 'guard_two', :scalar,
|
11
|
+
'gemini_xyz_rules', 'g_date', :scalar,
|
12
|
+
'gemini_xyz_rules', 'g_datetime', :scalar,
|
13
|
+
'gemini_xyz_rules', 'g_bool', :scalar,
|
14
|
+
'gemini_xyz_rules', 'g_integer', :scalar,
|
15
|
+
'gemini_xyz_rules', 'g_range1', :range,
|
16
|
+
'gemini_xyz_rules', 'g_range2', :range,
|
17
|
+
].in_groups_of(3).each do |table, field, type|
|
18
|
+
index = { array: "GIN",
|
19
|
+
scalar: "BTREE",
|
20
|
+
range: "GIST" }[type]
|
21
|
+
case type
|
22
|
+
when :array, :scalar
|
23
|
+
col="(simple_guards->'#{field}')"
|
24
|
+
when :range
|
25
|
+
col="(to_numrange(simple_guards->>'#{field}'))"
|
26
|
+
end
|
27
|
+
sql =<<-SQL
|
28
|
+
CREATE INDEX idx_#{table}_#{field} ON #{table} USING #{index}
|
29
|
+
(#{col});
|
30
|
+
SQL
|
31
|
+
execute sql
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/dummy/db/seeds.rb
CHANGED