marty 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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]