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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5462fab5388f7bad639810faa08b6e3fc2958153
4
- data.tar.gz: ada66e1090a390c88f62e7ec4667bc31be8e49d8
3
+ metadata.gz: f72e47b8af214afae1185098fad62b86cb4c740c
4
+ data.tar.gz: f23fdf9fb57e514b3d67340abd050b59b7c1b8ec
5
5
  SHA512:
6
- metadata.gz: 2b42c74d0222cdaba7b04f02035eeed7a892c4dd7b0eb8c49abf713f90f870c9c5159aa61e405b5a9445e218223b4474a574e19febafda665071a87c3cf11cae
7
- data.tar.gz: 686efe822c601622d5272fc5d6fcd9ebeee9b0887bb469f5e82b7f501786995767998e6ad56c7edae2579333eef7cfe5c8a93c6061874f07d8d83e735ba50561
6
+ metadata.gz: 35b2e57e4e67136307490955a47ecdd8ad4a8b45c0d4752492a4b144a9a3764a51c7f7f48fbf844e9ddeb7b92364b3d38d86c30fe3990e60fc27627556ca1612
7
+ data.tar.gz: bd9300147c167ac3e3934666c8249cf80f2c35e20ec67723d0a70311c6e1cc64e2f20a5bb5ad5931b4ee43876e8cad9d8dcde4f8c1bfbaf7ddc80884c96ec85e
data/Gemfile CHANGED
@@ -27,5 +27,6 @@ group :development, :test do
27
27
  gem 'netzke-testing'
28
28
  gem 'rspec-instafail', require: false
29
29
 
30
- gem 'marty_rspec'
30
+ # gem 'marty_rspec', path: File.expand_path('../../marty_rspec', __FILE__)
31
+ gem 'marty_rspec'
31
32
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- marty (1.2.0)
4
+ marty (1.2.1)
5
5
  axlsx (= 2.1.0pre)
6
6
  coderay
7
7
  delorean_lang (~> 0.3.33)
@@ -83,7 +83,7 @@ GEM
83
83
  delayed_job_active_record (4.1.2)
84
84
  activerecord (>= 3.0, < 5.2)
85
85
  delayed_job (>= 3.0, < 5)
86
- delorean_lang (0.3.34)
86
+ delorean_lang (0.3.33)
87
87
  activerecord (>= 3.2)
88
88
  treetop (~> 1.5)
89
89
  diff-lcs (1.3)
@@ -0,0 +1,279 @@
1
+ class Marty::BaseRuleView < Marty::McflyGridPanel
2
+ include Marty::Extras::Layout
3
+
4
+ def self.klass
5
+ Marty::BaseRule
6
+ end
7
+ def klass
8
+ self.class.klass
9
+ end
10
+
11
+ def self.base_fields
12
+ [:name]
13
+ end
14
+ def self.computed_fields
15
+ [:computed_guards, :grids, :results]
16
+ end
17
+ def configure(c)
18
+ super
19
+ c.model = self.class.klass
20
+ c.title = I18n.t('rule')
21
+ c.attributes = self.class.base_fields +
22
+ klass.guard_info.
23
+ sort_by{|_, h| h[:order] || 0}.
24
+ reject{|_, h| h[:hidden]}.
25
+ map { |name, _| name.to_sym } + self.class.computed_fields
26
+ c.store_config.merge!(sorters: [{property: :name, direction: 'ASC'}])
27
+ c.editing = :in_form
28
+ c.paging = :pagination
29
+ c.multi_select = false
30
+ end
31
+
32
+ def default_bbar
33
+ super + [:dup_in_form]
34
+ end
35
+
36
+ def default_context_menu
37
+ []
38
+ end
39
+
40
+ class DupKeyError < StandardError
41
+ def initialize(key, lineno)
42
+ @key = key
43
+ @lineno = lineno
44
+ end
45
+ def message
46
+ "keyword '#{@key}' specified more than once (line #{@lineno})"
47
+ end
48
+ end
49
+
50
+ def self.simple_to_hashstr(s)
51
+ pairs = []
52
+ keys = Set.new
53
+ s.lines.each.with_index(1) do |line, idx|
54
+ next if /\A\s*\z/.match(line)
55
+ line.chomp!
56
+ begin
57
+ m = /\A\s*([a-z0-9][a-z0-9_]*)\s*=\s*(.*)\s*\z/.match(line)
58
+ k, v = m[1], m[2]
59
+ v = [v].to_json[1..-2]
60
+ raise DupKeyError.new(k, idx) if keys.include?(k)
61
+ raise unless /\A['"].*['"]\z/.match(v)
62
+ keys << k
63
+ rescue DupKeyError => e
64
+ raise
65
+ rescue => e
66
+ raise "syntax error on line #{idx}"
67
+ end
68
+ pairs << [k, v]
69
+ end
70
+
71
+ kvs = pairs.map { |k, v| %Q("#{k}":#{v}) }.join(",")
72
+ "{#{kvs}}"
73
+ end
74
+
75
+ def self.hash_to_simple(h)
76
+ return unless h && h.present?
77
+ fmt = '%-' + h.keys.map(&:length).max.to_s + 's = %s'
78
+ h.map do |k, vstr|
79
+ fmt % [k, vstr]
80
+ end.join("\n") || ''
81
+ end
82
+
83
+ def jsonb_getter(c)
84
+ lambda { |r| md = r.send(c); md.present? && md.to_json || '' }
85
+ end
86
+
87
+ def jsonb_simple_getter(c)
88
+ lambda {|r| Marty::BaseRuleView.hash_to_simple(r.send(c)) }
89
+ end
90
+
91
+ def jsonb_simple_setter(c)
92
+ msg = "#{c}="
93
+ lambda { |r, v|
94
+ return r.send(msg, nil) if v.blank?
95
+
96
+ begin
97
+ v = ActiveSupport::JSON.decode(
98
+ Marty::BaseRuleView.simple_to_hashstr(v))
99
+ rescue => e
100
+ v = { "~~ERROR~~": e.message }
101
+ end
102
+ r.send(msg, v)
103
+ }
104
+ end
105
+
106
+ def self.jsonb_field_getter(j, c)
107
+ lambda { |r| r.send(j)[c]||"" }
108
+ end
109
+
110
+ def self.jsonb_field_setter(j, c)
111
+ lambda do |r, v|
112
+ v.blank? || v == '---' ? r.send(j).delete(c) : r.send(j)[c] = v
113
+ end
114
+ end
115
+
116
+ def json_sort_scope(c)
117
+ lambda { |r, dir| r.order("#{c}::text " + dir.to_s) }
118
+ end
119
+
120
+ component :add_window do |c|
121
+ super(c)
122
+ c.width = 1500
123
+ c.height = 740
124
+ end
125
+
126
+ component :edit_window do |c|
127
+ super(c)
128
+ c.width = 1500
129
+ c.height = 740
130
+ end
131
+
132
+ attribute :name do |c|
133
+ c.width = 150
134
+ end
135
+
136
+ def self.grid_column(c, label=nil)
137
+ editor_config = {
138
+ trigger_action: :all,
139
+ xtype: :combo,
140
+ store: Marty::DataGrid.where(obsoleted_dt: 'infinity').
141
+ pluck(:name) + ['---'],
142
+ forceSelection: true,
143
+ }
144
+ {
145
+ name: label || c,
146
+ width: 200,
147
+ column_config: { editor: editor_config },
148
+ field_config: editor_config,
149
+ type: :string,
150
+ getter: jsonb_field_getter(:grids, c.to_s),
151
+ setter: jsonb_field_setter(:grids, c.to_s),
152
+ # getter: lambda { |r| r.grids[c.to_s] },
153
+ # setter: lambda { |r, v| r.grids[c.to_s] = v },
154
+ }
155
+ end
156
+
157
+ def form_items_attrs
158
+ self.class.base_fields
159
+ end
160
+
161
+ def form_items_guards
162
+ klass.guard_info.reject{|_, h| h[:hidden]}.keys.map{|x|x.to_sym}
163
+ end
164
+
165
+ def form_items_grids
166
+ [jsonb_field(:grids,
167
+ getter: jsonb_simple_getter(:grids),
168
+ setter: jsonb_simple_setter(:grids),
169
+ height: 75)]
170
+ end
171
+
172
+ def form_items_computed_guards
173
+ [jsonb_field(:computed_guards,
174
+ getter: jsonb_simple_getter(:computed_guards),
175
+ setter: jsonb_simple_setter(:computed_guards),
176
+ height: 150)]
177
+ end
178
+
179
+ def form_items_results
180
+ [jsonb_field(:results,
181
+ getter: jsonb_simple_getter(:results),
182
+ setter: jsonb_simple_setter(:results),
183
+ height: 150)]
184
+ end
185
+
186
+ def default_form_items
187
+ [
188
+ hbox(
189
+ vbox(*form_items_attrs +
190
+ form_items_guards,
191
+ border: false,
192
+ width: "40%",
193
+ ),
194
+ vbox(width: '2%', border: false),
195
+ vbox(
196
+ width: '55%', border: false),
197
+ height: '40%',
198
+ border: false,
199
+ ),
200
+ hbox(
201
+ vbox(*form_items_computed_guards +
202
+ form_items_grids +
203
+ form_items_results,
204
+ width: '99%',
205
+ border: false
206
+ ),
207
+ height: '40%',
208
+ border: false
209
+ )
210
+ ]
211
+ end
212
+
213
+ def self.field_maker(namestr, h, meth)
214
+ name = namestr.to_sym
215
+ attribute name do |c|
216
+ c.width = h[:width] || 150
217
+ case h[:type]
218
+ when :datetime; c.format = 'Y-m-d H:i'
219
+ when :date; c.format = 'Y-m-d'
220
+ else c.type = h[:type] || :string
221
+ end
222
+ c.label = h[:label] if h[:label]
223
+ if h[:enum]
224
+ if h[:multi]
225
+ enum_array(c, h[:enum])
226
+ else
227
+ enum_column(c, h[:enum])
228
+ end
229
+ end
230
+ # for some unexplained reason the getter/setter need the full
231
+ # class qualification
232
+ if h[:type] != :range
233
+ c.getter = Marty::DeloreanRuleView.jsonb_field_getter(meth, namestr)
234
+ c.setter = Marty::DeloreanRuleView.jsonb_field_setter(meth, namestr)
235
+ c.filter_with = lambda do |rel, value, op|
236
+ v = ActiveRecord::Base.connection.quote(value)[1..-2]
237
+ rel.where("#{meth}->>'#{namestr}' like '%#{v}%'")
238
+ end
239
+ else
240
+ c.getter = range_getter(namestr, meth)
241
+ c.setter = range_setter(namestr, meth)
242
+ c.filterable = false
243
+ end
244
+ c.sorting_scope = get_json_sorter(meth, namestr)
245
+ end
246
+ end
247
+
248
+ attribute :start_dt do |c|
249
+ c.width = 100
250
+ c.format = 'Y-m-d H:i'
251
+ end
252
+
253
+ attribute :end_dt do |c|
254
+ c.width = 100
255
+ c.format = 'Y-m-d H:i'
256
+ end
257
+
258
+ attribute :rule_type do |c|
259
+ c.width = 200
260
+ end
261
+
262
+ def self.init_fields
263
+ klass.guard_info.each do |namestr, h|
264
+ field_maker(namestr, h, :simple_guards)
265
+ end
266
+ end
267
+
268
+ computed_fields.each do |a|
269
+ column a do |c|
270
+ c.flex = 1
271
+ c.getter = jsonb_getter(a.to_s)
272
+ c.sorting_scope = json_sort_scope(a)
273
+ c.filter_with = lambda do |rel, value, op|
274
+ v = ActiveRecord::Base.connection.quote(value)[1..-2]
275
+ rel.where("#{a}::text like '%#{v}%'")
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,26 @@
1
+ class Marty::DeloreanRuleView < Marty::BaseRuleView
2
+ include Marty::Extras::Layout
3
+
4
+ def self.klass
5
+ Marty::DeloreanRule
6
+ end
7
+
8
+ def self.base_fields
9
+ super + [:rule_type, :start_dt, :end_dt]
10
+ end
11
+
12
+ attribute :start_dt do |c|
13
+ c.width = 150
14
+ c.format = 'Y-m-d H:i'
15
+ end
16
+
17
+ attribute :end_dt do |c|
18
+ c.width = 150
19
+ c.format = 'Y-m-d H:i'
20
+ end
21
+
22
+ attribute :rule_type do |c|
23
+ c.width = 200
24
+ end
25
+
26
+ end
@@ -154,15 +154,30 @@ module Layout
154
154
  ######################################################################
155
155
  # make sure to validate range vals on the model (e.g. see rule.rb)
156
156
 
157
- def range_getter(name)
158
- lambda { |r|
159
- Marty::Util.pg_range_to_human(r.send(name))
160
- }
157
+ def range_getter(name, json_field=nil)
158
+ if json_field
159
+ lambda { |r| Marty::Util.pg_range_to_human(r.send(json_field)[name]) }
160
+ else
161
+ lambda { |r| Marty::Util.pg_range_to_human(r.send(name)) }
162
+ end
161
163
  end
162
164
 
163
- def range_setter(name)
164
- lambda do |r, v|
165
- r.send("#{name}=", v && (Marty::Util.human_to_pg_range(v) rescue v))
165
+ def range_setter(name, json_field=nil)
166
+ if json_field
167
+ lambda do |r, v|
168
+ cookedv = v && v.present? && (Marty::Util.human_to_pg_range(v) rescue v)
169
+ h = r.send(json_field)
170
+ if cookedv
171
+ r.send("#{json_field}=", h + {name=>cookedv})
172
+ else
173
+ h.delete(name)
174
+ r.send("#{json_field}=", h)
175
+ end
176
+ end
177
+ else
178
+ lambda do |r, v|
179
+ r.send("#{name}=", v && (Marty::Util.human_to_pg_range(v) rescue v))
180
+ end
166
181
  end
167
182
  end
168
183
 
@@ -81,7 +81,7 @@ class Marty::LogView < Marty::Grid
81
81
  c.text = I18n.t("log_grid.details")
82
82
  c.width = 900
83
83
  c.read_only = true
84
- c.getter = lambda { |r| r.details.to_s}
84
+ c.getter = lambda { |r| r.details.pretty_inspect}
85
85
  end
86
86
  end
87
87
 
@@ -43,6 +43,59 @@ class Marty::McflyGridPanel < Marty::Grid
43
43
  end
44
44
  end
45
45
 
46
+ client_class do |c|
47
+ c.init_component = l(<<-JS)
48
+ function() {
49
+ this.callParent();
50
+
51
+ // dup is a non standard button, so we have to explicitly manage
52
+ // its clickability
53
+ this.getSelectionModel().on('selectionchange', function(selModel) {
54
+ this.actions.dupInForm &&
55
+ this.actions.dupInForm.setDisabled(!selModel.hasSelection() ||
56
+ !this.permissions.create);
57
+ }, this);
58
+ }
59
+ JS
60
+ end
61
+
62
+ client_class do |c|
63
+ c.include :dup_in_form
64
+ end
65
+
66
+ action :dup_in_form do |a|
67
+ a.hidden = !config[:permissions][:create]
68
+ a.icon = :page_copy
69
+ a.disabled = true
70
+ end
71
+
72
+ # edit-in-form submit with dup support
73
+ endpoint :edit_window__edit_form__submit do |params|
74
+
75
+ if params["dup"]
76
+ # FIXME: copied from basepack grid endpoint
77
+ # :add_window__add_form__netzke_submit
78
+
79
+ params[:data] = ActiveSupport::JSON.
80
+ decode(params[:data]).merge(id: nil).to_json
81
+
82
+ client.merge!(component_instance(:add_window).
83
+ component_instance(:add_form).
84
+ invoke_endpoint(:submit, [params]))
85
+
86
+ on_data_changed if client.netzke_set_form_values.present?
87
+ client.delete(:netzke_set_form_values)
88
+ else
89
+ # FIXME: copied from basepack grid endpoint
90
+ # :edit_window__edit_form__netzke_submit
91
+ client.merge!(component_instance(:edit_window).
92
+ component_instance(:edit_form).
93
+ invoke_endpoint(:submit, [params]))
94
+ on_data_changed if client.netzke_set_form_values.present?
95
+ client.delete(:netzke_set_form_values)
96
+ end
97
+ end
98
+
46
99
  private
47
100
  def self.mcfly_scope(sort_column)
48
101
  lambda { |r|