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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/Gemfile.lock +2 -2
  4. data/app/components/marty/base_rule_view.rb +279 -0
  5. data/app/components/marty/delorean_rule_view.rb +26 -0
  6. data/app/components/marty/extras/layout.rb +22 -7
  7. data/app/components/marty/log_view.rb +1 -1
  8. data/app/components/marty/mcfly_grid_panel.rb +53 -0
  9. data/app/components/marty/mcfly_grid_panel/client/dup_in_form.js +20 -0
  10. data/app/models/marty/base_rule.rb +126 -0
  11. data/app/models/marty/delorean_rule.rb +121 -0
  12. data/lib/marty/data_importer.rb +0 -1
  13. data/lib/marty/rule_script_set.rb +176 -0
  14. data/lib/marty/version.rb +1 -1
  15. data/lib/tasks/marty_tasks.rake +42 -0
  16. data/spec/dummy/app/components/gemini/cm_auth_app.rb +18 -0
  17. data/spec/dummy/app/components/gemini/my_rule_view.rb +63 -0
  18. data/spec/dummy/app/components/gemini/xyz_rule_view.rb +25 -0
  19. data/spec/dummy/app/models/gemini/guard_one.rb +5 -0
  20. data/spec/dummy/app/models/gemini/guard_two.rb +5 -0
  21. data/spec/dummy/app/models/gemini/my_rule.rb +46 -0
  22. data/spec/dummy/app/models/gemini/my_rule_type.rb +5 -0
  23. data/spec/dummy/app/models/gemini/xyz_enum.rb +5 -0
  24. data/spec/dummy/app/models/gemini/xyz_rule.rb +63 -0
  25. data/spec/dummy/app/models/gemini/xyz_rule_type.rb +5 -0
  26. data/spec/dummy/config/locales/en.yml +10 -0
  27. data/spec/dummy/db/migrate/20171220150101_add_rule_type_enums.rb +14 -0
  28. data/spec/dummy/db/migrate/20171221095312_create_gemini_my_rules.rb +22 -0
  29. data/spec/dummy/db/migrate/20171221095359_create_gemini_xyz_rules.rb +21 -0
  30. data/spec/dummy/db/migrate/20171222150100_add_rule_indices.rb +34 -0
  31. data/spec/dummy/db/seeds.rb +1 -1
  32. data/spec/dummy/delorean/base_code.dl +6 -0
  33. data/spec/dummy/lib/gemini/my_rule_script_set.rb +13 -0
  34. data/spec/dummy/lib/gemini/xyz_rule_script_set.rb +22 -0
  35. data/spec/features/rule_spec.rb +265 -0
  36. data/spec/fixtures/csv/rule/DataGrid.csv +6 -0
  37. data/spec/fixtures/csv/rule/MyRule.csv +14 -0
  38. data/spec/fixtures/csv/rule/XyzRule.csv +6 -0
  39. data/spec/models/rule_spec.rb +322 -0
  40. data/spec/support/integration_helpers.rb +1 -0
  41. metadata +29 -2
@@ -1,3 +1,3 @@
1
1
  module Marty
2
- VERSION = "1.2.0"
2
+ VERSION = "1.2.1"
3
3
  end
@@ -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,5 @@
1
+ class Gemini::GuardOne < ActiveRecord::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = Set['G1V1', 'G1V2', 'G1V3']
5
+ end
@@ -0,0 +1,5 @@
1
+ class Gemini::GuardTwo < ActiveRecord::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = Set['G2V1', 'G2V2', 'G2V3']
5
+ 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,5 @@
1
+ class Gemini::MyRuleType < ActiveRecord::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = Set['SimpleRule','ComplexRule']
5
+ end
@@ -0,0 +1,5 @@
1
+ class Gemini::XyzEnum < ActiveRecord::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = Set['Vanilla', 'Chocolate', 'Strawberry']
5
+ 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,5 @@
1
+ class Gemini::XyzRuleType < ActiveRecord::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = Set['XRule','YRule', 'ZRule']
5
+ end
@@ -3,3 +3,13 @@
3
3
 
4
4
  en:
5
5
  hello: "Hello world"
6
+ rule: "Rules"
7
+
8
+ attributes:
9
+ g_array: "Array Guard"
10
+ g_single: "Single Guard"
11
+ g_bool: "Bool Guard"
12
+ g_range: "Range Guard"
13
+ g_range1: "Range Guard 1"
14
+ g_range2: "Range Guard 2"
15
+ other_flag: "Other"
@@ -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
@@ -74,4 +74,4 @@ STATES ||=
74
74
 
75
75
  STATES.each { |s| Gemini::State.create(full_name: s[0], name: s[1]) }
76
76
 
77
- ######################################################################
77
+ ######################################################################
@@ -0,0 +1,6 @@
1
+ BaseCode:
2
+ p1 =?
3
+ p2 =?
4
+ flavor =?
5
+
6
+ base_value = "%s --> %d" % [flavor, p1*p2]