marty 2.1.5 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -4,6 +4,7 @@ class Marty::PromiseView < Netzke::Tree::Base
4
4
  client_styles do |config|
5
5
  config.require :promise_view
6
6
  end
7
+
7
8
  client_class do |config|
8
9
  config.default_get_row_class = l(<<-JS)
9
10
  function(record, index, rowParams, ds) {
@@ -41,7 +42,7 @@ class Marty::PromiseView < Netzke::Tree::Base
41
42
  def configure(config)
42
43
  super
43
44
  config.title = I18n.t("jobs.promise_view")
44
- config.model = "Marty::Promise"
45
+ config.model = "Marty::VwPromise"
45
46
  config.attributes = [
46
47
  {name: :title, xtype: :treecolumn},
47
48
  :user__login,
@@ -114,20 +115,20 @@ class Marty::PromiseView < Netzke::Tree::Base
114
115
  action :clear do |a|
115
116
  a.text = a.tooltip = 'Clear'
116
117
  a.disabled = false
117
- a.icon = :application_delete
118
+ a.icon_cls = "fa fa-minus glyph"
118
119
  a.hidden = !self.class.has_admin_perm?
119
120
  end
120
121
 
121
122
  action :download do |a|
122
123
  a.text = a.tooltip = 'Download'
123
124
  a.disabled = true
124
- a.icon = :application_put
125
+ a.icon_cls = "fa fa-download glyph"
125
126
  end
126
127
 
127
128
  action :refresh do |a|
128
129
  a.text = a.tooltip = 'Refresh'
129
130
  a.disabled = false
130
- a.icon = :arrow_refresh
131
+ a.icon_cls = "fa fa-sync-alt glyph"
131
132
  end
132
133
 
133
134
  endpoint :clear do |params|
@@ -137,7 +138,7 @@ class Marty::PromiseView < Netzke::Tree::Base
137
138
 
138
139
  def get_records params
139
140
  search_scope = config[:live_search_scope] || :live_search
140
- Marty::Promise.children_for_id(params[:id], params[search_scope])
141
+ Marty::VwPromise.children_for_id(params[:id], params[search_scope])
141
142
  end
142
143
 
143
144
  attribute :title do |config|
@@ -172,7 +173,11 @@ class Marty::PromiseView < Netzke::Tree::Base
172
173
  end
173
174
 
174
175
  attribute :error do |config|
175
- config.getter = ->(record) { record.result.to_s if record.status == false }
176
+ config.getter = ->(record) {
177
+ if !record.status
178
+ Marty::Promise.find_by(id: record.id).try(:result).to_s
179
+ end
180
+ }
176
181
  config.flex = 1
177
182
  end
178
183
  end
@@ -1,7 +1,7 @@
1
1
  class Marty::RecordFormWindow < Netzke::Basepack::RecordFormWindow
2
2
  def configure(c)
3
3
  super c
4
- c.fbar = [] if c.item_id == 'view_window'
4
+ c.fbar = nil if c.item_id == 'view_window'
5
5
  end
6
6
 
7
7
  component :view_form do |c|
@@ -4,19 +4,19 @@ class Marty::ReportForm < Marty::Form
4
4
  action :apply do |a|
5
5
  a.text = a.tooltip = I18n.t("reporting.background")
6
6
  a.handler = :netzke_on_apply
7
- a.icon = :report_disk
7
+ a.icon_cls = "fa fa-cloud glyph"
8
8
  a.disabled = false
9
9
  end
10
10
 
11
11
  action :foreground do |a|
12
12
  a.text = a.tooltip = I18n.t("reporting.generate")
13
- a.icon = :report_go
13
+ a.icon_cls = "fa fa-download glyph"
14
14
  a.disabled = false
15
15
  end
16
16
 
17
17
  action :link do |a|
18
18
  a.text = a.tooltip = I18n.t("reporting.link")
19
- a.icon = :link_go
19
+ a.icon_cls = "fa fa-link glyph"
20
20
  a.disabled = false
21
21
  end
22
22
 
@@ -262,7 +262,7 @@ class Marty::ReportForm < Marty::Form
262
262
  }
263
263
  }
264
264
 
265
- c.items = items
265
+ c.items = add_cls_to_fields(items)
266
266
  c.repformat = format
267
267
  c.title = "Generate: #{title}-#{sset.tag.name}"
268
268
  c.reptitle = title
@@ -42,7 +42,7 @@ class Marty::ReportSelect < Marty::Form
42
42
  {},
43
43
  ),
44
44
  ]
45
- c.bbar = []
45
+ c.bbar = nil
46
46
  end
47
47
 
48
48
  client_class do |c|
@@ -109,7 +109,7 @@ class Marty::ScriptForm < Marty::Form
109
109
  action :apply do |a|
110
110
  a.text = I18n.t("script_form.save")
111
111
  a.tooltip = I18n.t("script_form.save")
112
- a.icon = :database_save
112
+ a.icon_cls = "fa fa-save glyph"
113
113
  a.disabled = true
114
114
  end
115
115
 
@@ -191,9 +191,9 @@ class Marty::ScriptForm < Marty::Form
191
191
  ######################################################################
192
192
 
193
193
  action :do_print do |a|
194
- a.text = I18n.t("script_form.print")
195
- a.tooltip = I18n.t("script_form.print")
196
- a.icon = :printer
194
+ a.text = I18n.t("script_form.print")
195
+ a.tooltip = I18n.t("script_form.print")
196
+ a.icon_cls = "fa fa-print glyph"
197
197
  end
198
198
 
199
199
  ######################################################################
@@ -34,7 +34,7 @@ class Marty::ScriptGrid < Marty::Grid
34
34
  action :delete do |a|
35
35
  a.text = I18n.t("script_grid.delete")
36
36
  a.tooltip = I18n.t("script_grid.delete")
37
- a.icon = :script_delete
37
+ a.icon_cls = "fa fa-trash glyph"
38
38
  a.disabled = config[:prohibit_delete]
39
39
  end
40
40
 
@@ -77,7 +77,7 @@ class Marty::ScriptGrid < Marty::Grid
77
77
  action :add_in_form do |a|
78
78
  a.text = I18n.t("script_grid.new")
79
79
  a.tooltip = I18n.t("script_grid.new")
80
- a.icon = :script_add
80
+ a.icon_cls = "fa fa-plus glyph"
81
81
  a.disabled = !config[:permissions][:create]
82
82
  end
83
83
 
@@ -96,7 +96,7 @@ class Marty::ScriptTester < Marty::Form
96
96
  action :apply do |a|
97
97
  a.text = I18n.t("script_tester.compute")
98
98
  a.tooltip = I18n.t("script_tester.compute")
99
- a.icon = :script_go
99
+ a.icon_cls = "fa fa-bug glyph"
100
100
  a.disabled = false
101
101
  end
102
102
 
@@ -66,7 +66,6 @@ class Marty::SimpleApp < Netzke::Base
66
66
  :itemId => 'status_bar',
67
67
  :xtype => 'statusbar',
68
68
  :region => 'south',
69
- :height => 22,
70
69
  :statusAlign => 'right',
71
70
  :busyText => 'Busy...',
72
71
  :default_text => "Ready",
@@ -78,9 +77,9 @@ class Marty::SimpleApp < Netzke::Base
78
77
  def menu_bar_config(overrides = {})
79
78
  {
80
79
  :itemId => 'menu_bar',
80
+ :layout => {overflow_handler: 'Menu'},
81
81
  :xtype => 'toolbar',
82
82
  :region => 'north',
83
- :height => 28,
84
83
  :items => menu
85
84
  }.merge(overrides)
86
85
  end
@@ -2,7 +2,7 @@ Ext.override(Ext.ux.StatusBar, {
2
2
  hideBusy : function(){
3
3
  return this.setStatus({
4
4
  text: this.defaultText,
5
- iconCls: this.defaultIconCls
5
+ icon_cls: this.defaultIconCls
6
6
  });
7
7
  }
8
8
  });
@@ -38,7 +38,7 @@ class Marty::TagGrid < Marty::Grid
38
38
  action :add_in_form do |a|
39
39
  a.text = I18n.t("tag_grid.new")
40
40
  a.tooltip = I18n.t("tag_grid.new")
41
- a.icon = :time_add
41
+ a.icon_cls = "fa fa-clock glyph"
42
42
  a.disabled = !config[:permissions][:create]
43
43
  end
44
44
 
@@ -100,19 +100,19 @@ module Marty; class UserView < Marty::Grid
100
100
 
101
101
  action :add do |a|
102
102
  super(a)
103
- a.text = I18n.t("user_grid.new")
104
- a.tooltip = I18n.t("user_grid.new")
105
- a.icon = :user_add
103
+ a.text = I18n.t("user_grid.new")
104
+ a.tooltip = I18n.t("user_grid.new")
105
+ a.icon_cls = "fa fa-user-plus glyph"
106
106
  end
107
107
 
108
108
  action :edit do |a|
109
109
  super(a)
110
- a.icon = :user_edit
110
+ a.icon_cls = "fa fa-user-cog glyph"
111
111
  end
112
112
 
113
113
  action :delete do |a|
114
114
  super(a)
115
- a.icon = :user_delete
115
+ a.icon_cls = "fa fa-user-minus glyph"
116
116
  end
117
117
 
118
118
  def default_context_menu
@@ -6,9 +6,13 @@ class Marty::JobController < ActionController::Base
6
6
 
7
7
  if promise
8
8
  format = promise.cformat
9
+
10
+ # Force result so finalized object is built
11
+ data = promise.result(true)
12
+
9
13
  # somewhat hacky: if result has "result" key, it's used as the
10
14
  # content.
11
- data = promise.result["result"] || promise.result
15
+ data = data["result"] || data
12
16
  title = promise.title
13
17
  else
14
18
  format = "json"
@@ -6,21 +6,6 @@ class Marty::Base < ActiveRecord::Base
6
6
  tb = self.table_name
7
7
  self.where("#{tb}.obsoleted_dt >= ? AND #{tb}.created_dt < ?", pt, pt)
8
8
  end
9
- MCFLY_PT_SIG = [1, 1]
10
-
11
- # FIXME: hacky signatures for AR queries
12
- COUNT_SIG = [0, 0]
13
- DISTINCT_SIG = [0, 100]
14
- FIRST_SIG = [0, 1]
15
- GROUP_SIG = [1, 100]
16
- JOINS_SIG = [1, 100]
17
- LAST_SIG = [0, 1]
18
- LIMIT_SIG = [1, 1]
19
- NOT_SIG = [1, 100]
20
- ORDER_SIG = [1, 100]
21
- PLUCK_SIG = [1, 100]
22
- SELECT_SIG = [1, 100]
23
- WHERE_SIG = [0, 100]
24
9
 
25
10
  class << self
26
11
  attr_accessor :struct_attrs
@@ -188,7 +188,7 @@ class Marty::DataGrid < Marty::Base
188
188
  h = dgh["metadata"].each_with_object({}) do |m, h|
189
189
  attr = m["attr"]
190
190
  inc = h_passed.fetch(attr, :__nf__)
191
- next if inc == :__nf__
191
+ next if inc == :__nf__ || inc.nil?
192
192
  val = (defined? inc.name) ? inc.name : inc
193
193
  h[attr] = val.is_a?(String) ?
194
194
  ActiveRecord::Base.connection.quote(val)[1..-2] : val
@@ -12,6 +12,7 @@ class Marty::Log < Marty::Base
12
12
  rescue => e
13
13
  Marty::Util.logger.error("Marty::Logger failure: #{e.message}")
14
14
  end
15
+ true
15
16
  end
16
17
 
17
18
  def self.cleanup(days_to_keep)
@@ -1,14 +1,4 @@
1
1
  class Marty::Promise < Marty::Base
2
- class MarshalResult
3
- def dump(v)
4
- Marshal.dump(v)
5
- end
6
-
7
- def load(v)
8
- # Marshal.load can't handle nil
9
- v ? Marshal.load(v) : {}
10
- end
11
- end
12
2
 
13
3
  # default timeout (seconds) to wait for promise values
14
4
  DEFAULT_PROMISE_TIMEOUT = Rails.configuration.marty.promise_timeout || 30
@@ -16,30 +6,38 @@ class Marty::Promise < Marty::Base
16
6
  # default timeout (seconds) to wait for jobs to start
17
7
  DEFAULT_JOB_TIMEOUT = Rails.configuration.marty.job_timeout || 10
18
8
 
19
- SELECT_COLS = columns.map(&:name)-["result"]
20
- default_scope {
21
- select(*SELECT_COLS)
22
- }
23
- def result
24
- unless has_attribute?(:result)
25
- changes_before_reload = self.changes.clone
26
- self.reload
27
- changes_before_reload.each{
28
- |attribute_name, values|
29
- self.send("#{attribute_name}=", values[1])
30
- }
31
- end
32
- read_attribute :result
9
+ def result(force=false)
10
+ res = super()
11
+ Marty::Promise.load_result(res, force)
33
12
  end
34
13
 
35
- serialize :result, MarshalResult.new
14
+ def self.load_result(obj, force=false)
15
+ if force && obj.respond_to?(:__force__)
16
+ obj = obj.__force__
17
+ end
18
+
19
+ case obj
20
+ when Array
21
+ obj.map {|x| load_result(x, force)}
22
+ when Hash
23
+ p = obj['__promise__']
36
24
 
37
- validates_presence_of :title
25
+ if p && obj.length==1
26
+ load_result(Marty::PromiseProxy.new(*p), force)
27
+ else
28
+ obj.each_with_object({}) { |(k, v), h| h[k] = load_result(v, force) }
29
+ end
30
+ else
31
+ obj
32
+ end
33
+ end
38
34
 
39
35
  has_many :children,
40
- foreign_key: 'parent_id',
41
- class_name: "Marty::Promise",
42
- dependent: :destroy
36
+ foreign_key: 'parent_id',
37
+ class_name: "Marty::Promise",
38
+ dependent: :destroy
39
+
40
+ validates_presence_of :title
43
41
 
44
42
  belongs_to :parent, class_name: "Marty::Promise"
45
43
  belongs_to :user, class_name: "Marty::User"
@@ -53,43 +51,6 @@ class Marty::Promise < Marty::Base
53
51
  end
54
52
  end
55
53
 
56
- class VirtualRoot
57
- def self.primary_key
58
- 'id'
59
- end
60
-
61
- def id
62
- 'root'
63
- end
64
-
65
- def user_id
66
- 0
67
- end
68
- alias_method :job_id, :user_id
69
-
70
- def result
71
- nil
72
- end
73
- [:start_dt, :end_dt].each { |m| alias_method m, :result }
74
-
75
- def status
76
- true
77
- end
78
- end
79
-
80
- def self.root
81
- VirtualRoot.new
82
- end
83
-
84
- def self.children_for_id(id, search_order)
85
- q = id == 'root' ? where(parent_id: nil) : find(id).children
86
- q.live_search(search_order).order(id: :desc).includes(:children, :user)
87
- end
88
-
89
- def leaf
90
- children.empty?
91
- end
92
-
93
54
  def raw_conn
94
55
  self.class.connection.raw_connection
95
56
  end
@@ -242,22 +203,4 @@ class Marty::Promise < Marty::Base
242
203
  raw_conn.exec("UNLISTEN promise_#{id}")
243
204
  end
244
205
  end
245
-
246
- # Support UI live search -- FIXME: hacky to have UI scoping here
247
- scope :live_search, lambda { |search_text|
248
- return if !search_text || search_text.strip.length < 1
249
-
250
- # Searches user login/firstname/lastname
251
- query = [
252
- "marty_users.login ILIKE ?",
253
- "marty_users.firstname ILIKE ?",
254
- "marty_users.lastname ILIKE ?",
255
- "marty_roles.name ILIKE ?",
256
- ].join(' OR ')
257
-
258
- st = "%#{search_text}%"
259
- # Convert "Role Name" or "Role name" to "role_name" (underscore is key)
260
- st2 = "%#{search_text.titleize.gsub(/\s/, '').underscore}%"
261
- joins({:user => :roles}).where(query, st, st, st, st2).distinct
262
- }
263
206
  end
@@ -0,0 +1,72 @@
1
+ class Marty::VwPromise < Marty::Base
2
+
3
+ has_many :children,
4
+ foreign_key: 'parent_id',
5
+ class_name: "Marty::VwPromise",
6
+ dependent: :destroy
7
+
8
+ belongs_to :parent, class_name: "Marty::VwPromise"
9
+ belongs_to :user, class_name: "Marty::User"
10
+
11
+ self.table_name = 'marty_vw_promises'
12
+ self.primary_key = 'id'
13
+
14
+ class VirtualRoot
15
+ def self.primary_key
16
+ 'id'
17
+ end
18
+
19
+ def id
20
+ 'root'
21
+ end
22
+
23
+ def user_id
24
+ 0
25
+ end
26
+ alias_method :job_id, :user_id
27
+
28
+ def result
29
+ nil
30
+ end
31
+ [:start_dt, :end_dt].each { |m| alias_method m, :result }
32
+
33
+ def status
34
+ true
35
+ end
36
+ end
37
+
38
+ def self.root
39
+ VirtualRoot.new
40
+ end
41
+
42
+ def self.children_for_id(id, search_order)
43
+ q = id == 'root' ? where(parent_id: nil) : find(id).children
44
+ q.live_search(search_order).order(id: :desc).includes(:children, :user)
45
+ end
46
+
47
+ def leaf
48
+ children.empty?
49
+ end
50
+
51
+ def to_s
52
+ inspect
53
+ end
54
+
55
+ # Support UI live search -- FIXME: hacky to have UI scoping here
56
+ scope :live_search, lambda { |search_text|
57
+ return if !search_text || search_text.strip.length < 1
58
+
59
+ # Searches user login/firstname/lastname
60
+ query = [
61
+ "marty_users.login ILIKE ?",
62
+ "marty_users.firstname ILIKE ?",
63
+ "marty_users.lastname ILIKE ?",
64
+ "marty_roles.name ILIKE ?",
65
+ ].join(' OR ')
66
+
67
+ st = "%#{search_text}%"
68
+ # Convert "Role Name" or "Role name" to "role_name" (underscore is key)
69
+ st2 = "%#{search_text.titleize.gsub(/\s/, '').underscore}%"
70
+ joins({:user => :roles}).where(query, st, st, st, st2).distinct
71
+ }
72
+ end