marty 2.1.5 → 2.3.0

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +4 -4
  4. data/app/components/marty/auth_app.rb +3 -3
  5. data/app/components/marty/auth_app/client/auth_app.js +1 -3
  6. data/app/components/marty/base_rule_view.rb +32 -20
  7. data/app/components/marty/data_grid_view.rb +3 -3
  8. data/app/components/marty/event_view.rb +0 -5
  9. data/app/components/marty/form.rb +8 -1
  10. data/app/components/marty/form/client/form.css +3 -0
  11. data/app/components/marty/grid.rb +174 -33
  12. data/app/components/marty/import_view.rb +151 -0
  13. data/app/components/marty/main_auth_app.rb +28 -28
  14. data/app/components/marty/mcfly_grid_panel.rb +1 -1
  15. data/app/components/marty/new_posting_form.rb +3 -3
  16. data/app/components/marty/new_posting_window.rb +5 -6
  17. data/app/components/marty/posting_grid.rb +2 -2
  18. data/app/components/marty/posting_window.rb +1 -2
  19. data/app/components/marty/promise_view.rb +11 -6
  20. data/app/components/marty/record_form_window.rb +1 -1
  21. data/app/components/marty/report_form.rb +4 -4
  22. data/app/components/marty/report_select.rb +1 -1
  23. data/app/components/marty/script_form.rb +4 -4
  24. data/app/components/marty/script_grid.rb +2 -2
  25. data/app/components/marty/script_tester.rb +1 -1
  26. data/app/components/marty/simple_app.rb +1 -2
  27. data/app/components/marty/simple_app/client/statusbar_ext.js +1 -1
  28. data/app/components/marty/tag_grid.rb +1 -1
  29. data/app/components/marty/user_view.rb +5 -5
  30. data/app/controllers/marty/job_controller.rb +5 -1
  31. data/app/models/marty/base.rb +0 -15
  32. data/app/models/marty/data_grid.rb +1 -1
  33. data/app/models/marty/log.rb +1 -0
  34. data/app/models/marty/promise.rb +27 -84
  35. data/app/models/marty/vw_promise.rb +72 -0
  36. data/app/views/layouts/marty/application.html.erb +6 -3
  37. data/config/locales/en.yml +4 -0
  38. data/db/migrate/410_jsonb_promise_result.rb +9 -0
  39. data/db/migrate/411_create_vw_promises.rb +26 -0
  40. data/lib/marty/data_change.rb +3 -4
  41. data/lib/marty/data_conversion.rb +1 -2
  42. data/lib/marty/data_importer.rb +13 -14
  43. data/lib/marty/javascript/{overrides.js → grid_view_in_form.js} +10 -6
  44. data/lib/marty/mcfly_model.rb +13 -20
  45. data/lib/marty/monkey.rb +33 -40
  46. data/lib/marty/promise_job.rb +8 -2
  47. data/lib/marty/promise_proxy.rb +6 -11
  48. data/lib/marty/rule_script_set.rb +5 -2
  49. data/lib/marty/version.rb +1 -1
  50. data/marty.gemspec +1 -1
  51. data/other/marty/diagnostic/delayed_job_workers.rb +8 -5
  52. data/spec/controllers/job_controller_spec.rb +34 -15
  53. data/spec/dummy/app/components/gemini/my_rule_view.rb +6 -0
  54. data/spec/dummy/app/models/gemini/helper.rb +2 -2
  55. data/spec/dummy/config/application.rb +1 -1
  56. data/spec/features/rule_spec.rb +100 -5
  57. data/spec/fixtures/csv/rule/MyRule.csv +1 -1
  58. data/spec/job_helper.rb +2 -4
  59. data/spec/lib/data_importer_spec.rb +10 -10
  60. data/spec/lib/delorean_query_spec.rb +13 -2
  61. data/spec/lib/logger_spec.rb +16 -14
  62. data/spec/models/data_grid_spec.rb +4 -10
  63. data/spec/models/promise_spec.rb +8 -8
  64. data/spec/models/rule_spec.rb +7 -7
  65. data/spec/spec_helper.rb +1 -1
  66. metadata +10 -5
@@ -1,8 +1,11 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
- <head>
4
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
5
- <title><%= Rails.application.class.parent %></title>
3
+ <head>
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
5
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css"
6
+ integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ"
7
+ crossorigin="anonymous">
8
+ <title><%= Rails.application.class.parent %></title>
6
9
  <%= load_netzke theme: Rails.configuration.marty.extjs_theme %>
7
10
  <%= csrf_meta_tag %>
8
11
  </head>
@@ -20,6 +20,10 @@ en:
20
20
  data_grid_view: Data Grids
21
21
  data_grid: Data Grids
22
22
 
23
+ data_import_view:
24
+ import: Import
25
+ results: Results
26
+
23
27
  roles:
24
28
  viewer: Viewer
25
29
  admin: Admin
@@ -0,0 +1,9 @@
1
+ class JsonbPromiseResult < ActiveRecord::Migration[5.1]
2
+ def change
3
+ table = :marty_promises
4
+
5
+ execute "delete from #{table}"
6
+
7
+ change_column table, :result, :jsonb, default: {}, using: 'result::text::jsonb'
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ class CreateVwPromises < ActiveRecord::Migration[5.1]
2
+ def up
3
+ execute <<SQL
4
+ drop view if exists marty_vw_promises;
5
+ create or replace view marty_vw_promises
6
+ as
7
+ select
8
+ id,
9
+ title,
10
+ user_id,
11
+ cformat,
12
+ parent_id,
13
+ job_id,
14
+ status,
15
+ start_dt,
16
+ end_dt
17
+ from marty_promises;
18
+
19
+ grant select on marty_vw_promises to public;
20
+
21
+ SQL
22
+ end
23
+ def down
24
+ execute "drop view if exists marty_vw_promises;"
25
+ end
26
+ end
@@ -141,9 +141,8 @@ class Marty::DataChange
141
141
  ' OR (created_dt >= ? AND created_dt < ?)'
142
142
 
143
143
  # find all changes from t0 to t1 -- orders by id to get the lower
144
- # ones since those are the original version in Mcfly. Using
145
- # unscoped to get around lazy loaded column scopes.
146
- changes = klass.unscoped.select("DISTINCT ON (group_id) *").
144
+ # ones since those are the original version in Mcfly.
145
+ changes = klass.select("DISTINCT ON (group_id) *").
147
146
  where(change_q, t0, t1, t0, t1).
148
147
  order("group_id, id").
149
148
  to_a
@@ -174,7 +173,7 @@ class Marty::DataChange
174
173
  change_q = '(obsoleted_dt >= ? AND obsoleted_dt < ?)' +
175
174
  ' OR (created_dt >= ? AND created_dt < ?)'
176
175
 
177
- countq = klass.unscoped.where(change_q, t0, t1, t0, t1)
176
+ countq = klass.where(change_q, t0, t1, t0, t1)
178
177
  dataq = klass.where(change_q, t0, t1, t0, t1)
179
178
 
180
179
  if ids
@@ -157,8 +157,7 @@ class Marty::DataConversion
157
157
 
158
158
  raise "no keys for #{klass} -- #{options}" if find_options.empty?
159
159
 
160
- # unscope klass since we're sometimes sent lazy column classes
161
- q = klass.unscoped.where(find_options)
160
+ q = klass.where(find_options)
162
161
  q = q.where("obsoleted_dt >= ? AND created_dt < ?", dt, dt) if
163
162
  dt && Mcfly.has_mcfly?(klass)
164
163
 
@@ -1,17 +1,16 @@
1
1
  module Marty
2
- class DataImporterError < StandardError
3
- attr_reader :lines
2
+ class DataImporter
3
+ class Error < StandardError
4
+ attr_reader :lines
4
5
 
5
- def initialize(message, lines)
6
- msg = lines && lines.respond_to?(:join) ?
7
- "#{message} - lines: #{lines.join(',')}" : message
6
+ def initialize(message, lines)
7
+ msg = lines && lines.respond_to?(:join) ?
8
+ "#{message} - lines: #{lines.join(',')}" : message
8
9
 
9
- super(msg)
10
- @lines = lines
10
+ super(msg)
11
+ @lines = lines
12
+ end
11
13
  end
12
- end
13
-
14
- class DataImporter
15
14
  # perform cleaning and do_import and summarize its results
16
15
  def self.do_import_summary(klass,
17
16
  data,
@@ -81,14 +80,14 @@ module Marty
81
80
  end
82
81
  rescue => exc
83
82
  # to find problems with the importer, comment out the rescue block
84
- raise Marty::DataImporterError.new(exc.to_s, [eline])
83
+ raise Error.new(exc.to_s, [eline])
85
84
  end
86
85
 
87
86
  ids = {}
88
87
  # raise an error if record referenced more than once.
89
88
  res.each_with_index do
90
89
  |(op, id), line|
91
- raise Marty::DataImporterError.
90
+ raise Error.
92
91
  new("record referenced more than once", [ids[id], line]) if
93
92
  op != :blank && ids.member?(id) && !allow_dups
94
93
 
@@ -100,12 +99,12 @@ module Marty
100
99
  klass.send(validation_function.to_sym, ids.keys) if
101
100
  validation_function
102
101
  rescue => exc
103
- raise Marty::DataImporterError.new(exc.to_s, [])
102
+ raise Error.new(exc.to_s, [])
104
103
  end
105
104
 
106
105
  remainder_ids = cleaner_ids - ids.keys
107
106
 
108
- raise Marty::DataImporterError.
107
+ raise Error.
109
108
  new("Missing import data. " +
110
109
  "Please provide header line and at least one data line.", [1]) if
111
110
  ids.keys.compact.count == 0
@@ -12,16 +12,21 @@ Ext.define('Netzke.Grid.EventHandlers', {
12
12
  }
13
13
  },
14
14
 
15
- netzkeReloadStore: function() {
15
+ netzkeReloadStore: function(opts={}) {
16
16
  var store = this.getStore();
17
17
 
18
- // MONKEY: add netzkereload event on store
19
- store.fireEvent('netzkereload');
18
+ // MONKEY: add beforenetzkereload and netzkerevent on store
19
+ store.fireEvent('beforenetzkereload');
20
+ var callback = opts.callback;
21
+ opts.callback = function() {
22
+ if (callback) { callback() }
23
+ store.fireEvent('netzkereload');
24
+ }
20
25
 
21
26
  // NETZKE'S HACK to work around buffered store's buggy reload()
22
27
  if (!store.lastRequestStart) {
23
- store.load();
24
- } else store.reload();
28
+ store.load(opts);
29
+ } else store.reload(opts);
25
30
  },
26
31
  });
27
32
 
@@ -74,4 +79,3 @@ Ext.define('Netzke.Grid.Columns', {
74
79
  };
75
80
  },
76
81
  });
77
-
@@ -52,6 +52,10 @@ module Mcfly::Model
52
52
  end
53
53
  end
54
54
 
55
+ def hash_if_necessary(q, private)
56
+ !private && q.is_a?(ActiveRecord::Base) ? make_openstruct(q) : q
57
+ end
58
+
55
59
  def base_mcfly_lookup(meth, name, options = {}, &block)
56
60
 
57
61
  priv = options[:private]
@@ -70,19 +74,7 @@ module Mcfly::Model
70
74
 
71
75
  q = q.first if q.respond_to?(:first) && options[:mode] == :first
72
76
 
73
- next q if priv
74
-
75
- case
76
- when q.is_a?(ActiveRecord::Relation)
77
- # shouldn't happen - lookups that are mode nil should be
78
- # private raise "#{self}.#{name} can't convert
79
- # ActiveRecord::Relation to OpenStruct"
80
- q
81
- when q.is_a?(ActiveRecord::Base)
82
- make_openstruct(q)
83
- else
84
- q
85
- end
77
+ hash_if_necessary(q, priv)
86
78
  end
87
79
  end
88
80
 
@@ -169,10 +161,11 @@ module Mcfly::Model
169
161
  raise "#{rel_attr} should be mapped in attrs" if attrs[rel_attr].nil?
170
162
 
171
163
  cat_assoc_klass = cat_assoc_name.constantize
164
+ cat_attr_id = "#{cat_attr}_id"
172
165
 
173
166
  # replace rel_attr with cat_attr in attrs
174
167
  pc_attrs = attrs.each_with_object({}) {|(k, v), h|
175
- h[k == rel_attr ? "#{cat_attr}_id" : k] = v
168
+ h[k == rel_attr ? cat_attr_id : k] = v
176
169
  }
177
170
 
178
171
  pc_name = "pc_#{name}".to_sym
@@ -186,6 +179,7 @@ module Mcfly::Model
186
179
 
187
180
  # cache if mode is not nil
188
181
  fn = options.fetch(:mode, :first) ? :cached_delorean_fn : :delorean_fn
182
+ priv = options[:private]
189
183
 
190
184
  send(fn, name, sig: attrs.length+1) do
191
185
  |ts, *args|
@@ -196,13 +190,12 @@ module Mcfly::Model
196
190
 
197
191
  args[lpi] = cat_assoc_klass.
198
192
  mcfly_pt(ts).
199
- # FIXME: XXXX why is this join needed???
200
- # joins(cat_attr).
201
- where(rel_attr => rel).
202
- pluck("#{cat_attr}_id").
203
- first
193
+ select(cat_attr_id).
194
+ find_by(rel_attr => rel).
195
+ send(cat_attr_id)
204
196
 
205
- self.send(pc_name, ts, *args)
197
+ q = self.send(pc_name, ts, *args)
198
+ hash_if_necessary(q, priv)
206
199
  end
207
200
  end
208
201
  end
@@ -106,23 +106,6 @@ end
106
106
  require 'netzke/basepack/data_adapters/active_record_adapter'
107
107
  module Netzke::Basepack::DataAdapters
108
108
  class ActiveRecordAdapter < AbstractAdapter
109
- # FIXME: another giant hack to handle lazy_load columns.
110
- # Modified original count_records to call count on first passed column.name
111
- # when lazy-loaded. Otherwise, we run into issues with
112
- # counting records in the default_scope placed by the lazy_load
113
- # module.
114
- def count_records(params, columns=[])
115
-
116
- relation = @relation || get_relation(params)
117
- columns.each do |c|
118
- assoc, method = c[:name].split('__')
119
- relation = relation.includes(assoc.to_sym).references(assoc.to_sym) if method
120
- end
121
-
122
- @model.const_defined?(:SELECT_COLS) ? relation.count(columns.first.name) :
123
- relation.count
124
- end
125
-
126
109
  ######################################################################
127
110
  # The following is a hack to get around Netzke's broken handling
128
111
  # of filtering on PostgreSQL enums columns.
@@ -238,11 +221,32 @@ class ActiveRecord::Relation
238
221
  tb = cls.table_name
239
222
  self.where("#{tb}.obsoleted_dt >= ? AND #{tb}.created_dt < ?", pt, pt)
240
223
  end
224
+
225
+ def attributes
226
+ to_a.map(&:attributes)
227
+ end
241
228
  end
242
229
 
243
230
  ######################################################################
244
231
 
245
232
  class ActiveRecord::Base
233
+ MCFLY_PT_SIG = [1, 1]
234
+
235
+ # FIXME: hacky signatures for AR queries on classes
236
+ COUNT_SIG = [0, 0]
237
+ DISTINCT_SIG = [0, 100]
238
+ FIND_BY_SIG = [0, 100]
239
+ FIRST_SIG = [0, 1]
240
+ GROUP_SIG = [1, 100]
241
+ JOINS_SIG = [1, 100]
242
+ LAST_SIG = [0, 1]
243
+ LIMIT_SIG = [1, 1]
244
+ NOT_SIG = [1, 100]
245
+ ORDER_SIG = [1, 100]
246
+ PLUCK_SIG = [1, 100]
247
+ SELECT_SIG = [1, 100]
248
+ WHERE_SIG = [0, 100]
249
+
246
250
  class << self
247
251
  alias_method :old_joins, :joins
248
252
 
@@ -258,22 +262,23 @@ class ActiveRecord::Base
258
262
  end
259
263
  end
260
264
 
261
- args_hack = [[ActiveRecord::Relation, ActiveRecord::QueryMethods::WhereChain]] +
262
- [[Object, nil]]*10
265
+ ar_instances = [ActiveRecord::Relation, ActiveRecord::QueryMethods::WhereChain]
266
+
267
+ args_hack = [ar_instances] + [[Object, nil]]*10
263
268
 
264
269
  Delorean::RUBY_WHITELIST.merge!(
265
- count: [ActiveRecord::Relation],
270
+ count: [ar_instances],
266
271
  distinct: args_hack,
267
272
  find_by: args_hack,
268
273
  group: args_hack,
269
274
  joins: args_hack,
270
- limit: [ActiveRecord::Relation, Integer],
275
+ limit: [ar_instances, Integer],
271
276
  not: args_hack,
272
277
  order: args_hack,
273
278
  pluck: args_hack,
274
279
  select: args_hack,
275
280
  where: args_hack,
276
- mcfly_pt: [ActiveRecord::Relation,
281
+ mcfly_pt: [ar_instances,
277
282
  [Date, Time, ActiveSupport::TimeWithZone, String],
278
283
  [nil, Class]],
279
284
  lookup_grid_distinct_entry: [OpenStruct,
@@ -293,22 +298,10 @@ end
293
298
  ######################################################################
294
299
 
295
300
  class OpenStruct
296
- def save
297
- loc = %r([^/]+:[0-9]+).match(caller.first)[0]
298
- raise "save called from #{loc} on #{self}"
299
- end
300
- def save!
301
- loc = %r([^/]+:[0-9]+).match(caller.first)[0]
302
- raise "save! called from #{loc} on #{self}"
303
- end
304
- def reload
305
- loc = %r([^/]+:[0-9]+).match(caller.first)[0]
306
- raise "reload called from #{loc} on #{self}"
301
+ # the default as_json produces {"table"=>h} which is quite goofy
302
+ def as_json(*)
303
+ self.to_h
307
304
  end
308
- #def method_missing(meth, *args)
309
- # puts caller[0..8]
310
- # super
311
- #end
312
305
  end
313
306
 
314
307
  ######################################################################
@@ -322,10 +315,10 @@ module Netzke
322
315
 
323
316
  include_core_js(res)
324
317
 
325
- # MONKEY: load javascript overrides
326
- overrides = ["#{File.dirname(__FILE__)}/javascript/overrides.js"]
318
+ # MONKEY: load marty custom javascript
319
+ marty_javascripts = Dir["#{File.dirname(__FILE__)}/javascript/*.js"]
327
320
 
328
- (Netzke::Core.ext_javascripts + overrides).each do |path|
321
+ (Netzke::Core.ext_javascripts + marty_javascripts).each do |path|
329
322
  f = File.new(path)
330
323
  res << f.read
331
324
  end
@@ -10,6 +10,10 @@ class Delorean::BaseModule::NodeCall
10
10
  params[:_user_id] = _e[:_user_id] || Mcfly.whodunnit.try(:id)
11
11
  end
12
12
 
13
+ # def log(msg)
14
+ # open('/tmp/dj.out', 'a') { |f| f.puts msg }
15
+ # end
16
+
13
17
  # Monkey-patch '|' method for Delorean NodeCall to create promise
14
18
  # jobs and return promise proxy objects.
15
19
  def |(args)
@@ -25,11 +29,13 @@ class Delorean::BaseModule::NodeCall
25
29
  nn = node.is_a?(Class) ? node.name : node.to_s
26
30
  begin
27
31
  # make sure params is serialzable before starting a Job
28
- Marshal.dump(params)
32
+ JSON.dump(params)
29
33
  rescue => exc
30
- raise "non-serializable parameters"
34
+ raise "non-serializable parameters: #{params} #{exc}"
31
35
  end
32
36
 
37
+ # log "||||| #{args.inspect} #{params.inspect}"
38
+
33
39
  title = params["p_title"] || "#{script}::#{nn.demodulize}"
34
40
  timeout = params["p_timeout"] || Marty::Promise::DEFAULT_PROMISE_TIMEOUT
35
41
  hook = params["p_hook"]
@@ -4,8 +4,6 @@
4
4
  class Marty::PromiseProxy < BasicObject
5
5
  NOT_SET = ::Object.new.freeze
6
6
  METH_SET = ::Set[
7
- :marshal_load,
8
- :marshal_dump,
9
7
  :force,
10
8
  :__force__,
11
9
  # Added for Rails 4 -- were causing forced eval.
@@ -13,25 +11,22 @@ class Marty::PromiseProxy < BasicObject
13
11
  # ActiveRecord treats assignment to proxy objs.
14
12
  :is_a?,
15
13
  :nested_under_indifferent_access,
14
+ :as_json,
16
15
  ]
17
16
 
18
17
  instance_methods.each {|m| undef_method m unless m =~ /^(__.*|object_id)$/}
19
18
 
20
19
  def initialize(promise_id, timeout, attr=nil)
21
- marshal_load([promise_id, timeout, attr])
22
- end
23
-
24
- def marshal_dump
25
- [@promise.id, @timeout, @attr]
26
- end
27
-
28
- def marshal_load(args)
29
- promise_id, @timeout, @attr = args
20
+ promise_id, @timeout, @attr = promise_id, timeout, attr
30
21
  @promise = ::Marty::Promise.find(promise_id)
31
22
  @mutex = ::Mutex.new
32
23
  @result = NOT_SET
33
24
  end
34
25
 
26
+ def as_json(*)
27
+ {'__promise__' => [@promise.id, @timeout, @attr]}
28
+ end
29
+
35
30
  def __promise__
36
31
  @promise
37
32
  end