marty 1.0.12 → 1.0.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cb347975c0c4442e6466f8cbcbd72d431c85f45f
4
- data.tar.gz: 4c7de97777049441bbf43ea5d324ed1a4c858315
3
+ metadata.gz: 6954bc349cc4800a6505d17737f5ddcb91ce16b2
4
+ data.tar.gz: 8bfff59fcd54425f3f076b0fdc61ec98a5d52b23
5
5
  SHA512:
6
- metadata.gz: dc0b688584580e74b519a04559d0c3dd6bcbc0d3fc2e00949403a32c54ef25f78380573238f8d80d048ce434d3c936a6c455db94c1a2945065ea4c39525b277d
7
- data.tar.gz: 7318c212db79212f25ad3fcfc3f27d28e666f1948d333b93f7ab5d5689e5eb4f16f54873a02d016eff9678bd607a7edce244d3234b94e2229c05b7313149197b
6
+ metadata.gz: f3d5897c69ee0feb698f467a30095273ba724a030fbeae9032f82129e3341b6905c67ededb6256264079702a68a286076ae216d9384b06eb84edbf3d1b4c7300
7
+ data.tar.gz: f36961f6208fbdef9d54a9f717cadb04cac2bb5a3d7c956905a650718d7996c155469d93f0ac3dce78d16991fbac1d6947625acb81ca2f0ccaf5458c48821459
data/Gemfile CHANGED
@@ -9,6 +9,7 @@ gem 'delayed_job_active_record'
9
9
  gem 'daemons', '~> 1.1.9'
10
10
  gem 'mime-types', '< 3.0', platforms: :ruby_19
11
11
  gem 'rails', '~> 4.2.1'
12
+ gem 'pg', '~> 0.18.4'
12
13
 
13
14
  group :development, :test do
14
15
  gem 'pry-rails'
@@ -57,7 +57,7 @@ class Marty::MainAuthApp < Marty::AuthApp
57
57
  :api_auth_view,
58
58
  :reload_scripts,
59
59
  :load_seed,
60
- ],
60
+ ] + background_jobs_menu
61
61
  }
62
62
  end
63
63
 
@@ -73,6 +73,21 @@ class Marty::MainAuthApp < Marty::AuthApp
73
73
  }
74
74
  end
75
75
 
76
+ def background_jobs_menu
77
+ [
78
+ {
79
+ text: 'Background Jobs',
80
+ icon: icon_hack(:clock),
81
+ disabled: !self.class.has_admin_perm?,
82
+ menu: [
83
+ :bg_status,
84
+ :bg_stop,
85
+ :bg_restart,
86
+ ]
87
+ },
88
+ ]
89
+ end
90
+
76
91
  def warped
77
92
  Marty::Util.warped?
78
93
  end
@@ -181,6 +196,62 @@ class Marty::MainAuthApp < Marty::AuthApp
181
196
  a.disabled = !self.class.has_admin_perm?
182
197
  end
183
198
 
199
+ action :bg_status do |a|
200
+ a.text = 'Show Delayed Jobs Status'
201
+ a.tooltip = 'Run delayed_job status script'
202
+ a.icon = :monitor
203
+ a.disabled = !self.class.has_admin_perm?
204
+ end
205
+
206
+ action :bg_stop do |a|
207
+ a.text = 'Stop Delayed Jobs'
208
+ a.tooltip = 'Run delayed_job stop script'
209
+ a.icon = :stop
210
+ a.disabled = !self.class.has_admin_perm?
211
+ end
212
+
213
+ action :bg_restart do |a|
214
+ a.text = 'Restart Delayed Jobs'
215
+ a.tooltip = 'Run delayed_job restart script using DELAYED_JOB_PARAMS'
216
+ a.icon = :arrow_rotate_clockwise
217
+ a.disabled = !self.class.has_admin_perm?
218
+ end
219
+
220
+ ######################################################################
221
+
222
+ def bg_command(param)
223
+ e, root, p = ENV['RAILS_ENV'], Rails.root, Marty::Config["RUBY_PATH"]
224
+ # preserve backward compatability with Gemini
225
+ # for new Rails apps, use 'bin/delayed_job'
226
+ dj_path = Marty::Config["DELAYED_JOB_PATH"] || 'script/delayed_job'
227
+ cmd = "export RAILS_ENV=#{e};"
228
+ # FIXME: Environment looks to be setup incorrectly - this is a hack
229
+ cmd += "export PATH=#{p}:$PATH;" if p
230
+ # 2>&1 redirects STDERR to STDOUT since backticks only captures STDOUT
231
+ cmd += "#{root}/#{dj_path} #{param} 2>&1"
232
+ cmd
233
+ end
234
+
235
+ endpoint :bg_status do |params|
236
+ cmd = bg_command('status')
237
+ res = `#{cmd}`
238
+ client.show_detail res.html_safe.gsub("\n","<br/>"), 'Delayed Job Status'
239
+ end
240
+
241
+ endpoint :bg_stop do |params|
242
+ cmd = bg_command("stop")
243
+ res = `#{cmd}`
244
+ res = "delayed_job: no instances running. Nothing to stop." if res.length==0
245
+ client.show_detail res.html_safe.gsub("\n","<br/>"), 'Delayed Job Stop'
246
+ end
247
+
248
+ endpoint :bg_restart do |params|
249
+ params = Marty::Config["DELAYED_JOB_PARAMS"] || ""
250
+ cmd = bg_command("restart #{params}")
251
+ res = `#{cmd}`
252
+ client.show_detail res.html_safe.gsub("\n","<br/>"), 'Delayed Job Restart'
253
+ end
254
+
184
255
  ######################################################################
185
256
  # Postings
186
257
 
@@ -192,6 +263,38 @@ class Marty::MainAuthApp < Marty::AuthApp
192
263
  end
193
264
 
194
265
  client_class do |c|
266
+ c.show_detail = l(<<-JS)
267
+ function(details, title) {
268
+ this.hideLoadmask();
269
+ Ext.create('Ext.Window', {
270
+ height: 400,
271
+ minWidth: 400,
272
+ maxWidth: 1200,
273
+ autoWidth: true,
274
+ modal: true,
275
+ autoScroll: true,
276
+ html: details,
277
+ title: title || "Details"
278
+ }).show();
279
+ }
280
+ JS
281
+
282
+ c.show_loadmask = l(<<-JS)
283
+ function(msg) {
284
+ this.maskCmp = new Ext.LoadMask( {
285
+ msg: msg || 'Loading...',
286
+ target: this,
287
+ });
288
+ this.maskCmp.show();
289
+ }
290
+ JS
291
+
292
+ c.hide_loadmask = l(<<-JS)
293
+ function() {
294
+ if (this.maskCmp) { this.maskCmp.hide(); };
295
+ }
296
+ JS
297
+
195
298
  c.netzke_on_new_posting = l(<<-JS)
196
299
  function(params) {
197
300
  this.netzkeLoadComponent("new_posting_window",
@@ -241,6 +344,47 @@ class Marty::MainAuthApp < Marty::AuthApp
241
344
  });
242
345
  }
243
346
  JS
347
+
348
+ c.netzke_on_bg_stop = l(<<-JS)
349
+ function(params) {
350
+ var me = this;
351
+ me.showLoadmask('Stopping delayed job...');
352
+ Ext.Msg.show({
353
+ title: 'Stop Delayed Jobs',
354
+ msg: 'Enter STOP and press OK to force a stop of delayed_job',
355
+ width: 375,
356
+ buttons: Ext.Msg.OKCANCEL,
357
+ prompt: true,
358
+ fn: function (btn, value) {
359
+ btn == "ok" && value == "STOP" && me.server.bgStop({});
360
+ }
361
+ });
362
+ }
363
+ JS
364
+
365
+ c.netzke_on_bg_restart = l(<<-JS)
366
+ function(params) {
367
+ var me = this;
368
+ me.showLoadmask('Restarting delayed job...');
369
+ Ext.Msg.show({
370
+ title: 'Restart Delayed Jobs',
371
+ msg: 'Enter RESTART and press OK to force a restart of delayed_job',
372
+ width: 375,
373
+ buttons: Ext.Msg.OKCANCEL,
374
+ prompt: true,
375
+ fn: function (btn, value) {
376
+ btn == "ok" && value == "RESTART" && me.server.bgRestart({});
377
+ }
378
+ });
379
+ }
380
+ JS
381
+
382
+ c.netzke_on_bg_status = l(<<-JS)
383
+ function() {
384
+ this.showLoadmask('Checking delayed job status...');
385
+ this.server.bgStatus({});
386
+ }
387
+ JS
244
388
  end
245
389
 
246
390
  action :select_posting do |a|
@@ -0,0 +1,5 @@
1
+ class Marty::EnumEventOperation < Marty::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = Set['UNKNOWN']
5
+ end
@@ -0,0 +1,206 @@
1
+ class Marty::Event < Marty::Base
2
+
3
+ class EventValidator < ActiveModel::Validator
4
+ def validate(event)
5
+ event.errors[:base] = "Must have promise_id or start_dt" unless
6
+ event.promise_id || event.start_dt
7
+ end
8
+ end
9
+
10
+ validates_presence_of :klass, :subject_id, :enum_event_operation
11
+
12
+ belongs_to :promise
13
+
14
+ validates_with EventValidator
15
+
16
+ BASE_QUERY = "SELECT ev.id,
17
+ ev.klass,
18
+ ev.subject_id,
19
+ ev.enum_event_operation,
20
+ ev.comment,
21
+ coalesce(pr.start_dt, ev.start_dt) start_dt,
22
+ coalesce(pr.end_dt, ev.end_dt) end_dt,
23
+ expire_secs
24
+ FROM marty_events ev
25
+ LEFT JOIN marty_promises pr ON ev.promise_id = pr.id "
26
+
27
+ def self.running_query(time_now_s)
28
+ "SELECT * FROM
29
+ (#{BASE_QUERY}
30
+ WHERE coalesce(pr.start_dt, ev.start_dt, '1900-1-1') >=
31
+ '#{time_now_s}'::timestamp - interval '24 hours') sub
32
+ WHERE (end_dt IS NULL or end_dt > '#{time_now_s}'::timestamp)
33
+ AND (expire_secs IS NULL
34
+ OR expire_secs > EXTRACT (EPOCH FROM '#{time_now_s}'::timestamp - start_dt))
35
+ ORDER BY start_dt"
36
+ end
37
+
38
+
39
+ def self.op_is_running?(klass, subject_id, operation)
40
+ all_running.detect do |pm|
41
+ pm["klass"] == klass && pm["subject_id"].to_i == subject_id.to_i &&
42
+ pm["enum_event_operation"] == operation
43
+ end
44
+ end
45
+
46
+ def self.create_event(klass,
47
+ subject_id,
48
+ operation,
49
+ start_dt,
50
+ expire_secs,
51
+ comment=nil)
52
+
53
+ # use lookup_event instead of all_running which is throttled
54
+ evs = self.lookup_event(klass, subject_id, operation)
55
+ running = evs.detect do
56
+ |ev|
57
+ next if ev["end_dt"]
58
+ next true unless ev["expire_secs"]
59
+ (Time.zone.now - ev["start_dt"]).truncate < ev["expire_secs"]
60
+ end
61
+
62
+ raise "#{operation} is already running for #{klass}/#{subject_id}" if
63
+ running
64
+
65
+ self.create!(klass: klass,
66
+ subject_id: subject_id,
67
+ enum_event_operation: operation,
68
+ start_dt: start_dt,
69
+ expire_secs: expire_secs,
70
+ comment: comment,
71
+ )
72
+ end
73
+
74
+ def self.lookup_event(klass, subject_id, operation)
75
+ get_data(BASE_QUERY +
76
+ " WHERE klass = '#{klass}'
77
+ AND subject_id = #{subject_id}
78
+ and enum_event_operation = '#{operation}'")
79
+
80
+ #For now we return a bare hash
81
+ #Marty::Event.find_by_id(hash["id"])
82
+ end
83
+
84
+ def self.finish_event(klass, subject_id, operation, comment=nil)
85
+ time_now_s = Time.zone.now.strftime('%Y-%m-%d %H:%M:%S.%6N')
86
+
87
+ event = get_data(running_query(time_now_s)).detect do |ev|
88
+ ev["klass"] == klass && ev["subject_id"] == subject_id.to_i &&
89
+ ev["enum_event_operation"] == operation
90
+ end
91
+ raise "event #{klass}/#{subject_id}/#{operation} not found" unless
92
+ event
93
+
94
+ ev = Marty::Event.find_by_id(event["id"])
95
+ raise "can't explicitly finish a promise event" if ev.promise_id
96
+ ev.end_dt = Time.zone.now
97
+ ev.comment = comment if comment
98
+ ev.save!
99
+ end
100
+
101
+ def self.last_event(klass, subject_id, operation=nil)
102
+ hash = all_running.select do |pm|
103
+ pm["klass"] == klass && pm["subject_id"] == subject_id.to_i &&
104
+ (operation.nil? || pm["enum_event_operation"] == operation)
105
+ end.sort { |a, b| b["start_dt"] <=> a["start_dt"] }.first
106
+
107
+ return hash if hash
108
+
109
+ op_sql = "AND enum_event_operation = '#{operation}'" if operation
110
+
111
+ get_data("SELECT * FROM (#{BASE_QUERY}) sub
112
+ WHERE klass = '#{klass}'
113
+ AND subject_id = #{subject_id} #{op_sql}
114
+ ORDER BY start_dt desc").first
115
+ end
116
+
117
+ def self.currently_running(klass, subject_id)
118
+ all_running.select do |pm|
119
+ pm["klass"] == klass && pm["subject_id"] == subject_id.to_i
120
+ end.map { |e| e["enum_event_operation"] }
121
+ end
122
+
123
+ def self.update_comment(hash, comment)
124
+ hid = hash.is_a?(Hash) ? hash['id'] : hash
125
+ e = Marty::Event.find_by_id(hid)
126
+ e.comment = comment
127
+ e.save!
128
+ end
129
+
130
+ def self.pretty_op(hash)
131
+ d = hash['enum_event_operation'].downcase.capitalize
132
+
133
+ #&& !(hash['comment'] =~ /^ERROR/)
134
+ hash['end_dt'] ? d.sub(/ing/, 'ed') : d
135
+ end
136
+
137
+ def self.compact_end_dt(hash)
138
+ hash['end_dt'] ? hash['end_dt'].strftime("%H:%M") : '---'
139
+ end
140
+
141
+ def self.get_data(sql)
142
+ ActiveRecord::Base.connection.execute(sql).to_a.map do |h|
143
+ h["id"] = h["id"].to_i
144
+ h["subject_id"] = h["subject_id"].to_i
145
+ h["start_dt"] = Time.zone.parse(h["start_dt"]) if h["start_dt"]
146
+ h["end_dt"] = Time.zone.parse(h["end_dt"]) if h["end_dt"]
147
+ h["expire_secs"] = h["expire_secs"].to_i if h["expire_secs"]
148
+ h["comment"] = h["comment"]
149
+ h
150
+ end
151
+ end
152
+ private_class_method :get_data
153
+
154
+ def self.clear_cache
155
+ @poll_secs = @all_running = @all_finished = nil
156
+ end
157
+
158
+ def self.all_running
159
+ @all_running ||= { timestamp: 0, data: [] }
160
+ @poll_secs ||= Marty::Config['MARTY_EVENT_POLL_SECS'] || 0
161
+ time_now = Time.zone.now
162
+ time_now_i = time_now.to_i
163
+ time_now_s = time_now.strftime('%Y-%m-%d %H:%M:%S.%6N')
164
+ if time_now_i - @all_running[:timestamp] > @poll_secs
165
+ @all_running[:data] = get_data(running_query(time_now_s))
166
+ @all_running[:timestamp] = time_now_i
167
+ end
168
+ @all_running[:data]
169
+ end
170
+ private_class_method :all_running
171
+
172
+ def self.all_finished
173
+ @all_finished ||= {
174
+ data: {},
175
+ timestamp: Time.zone.parse('1970-1-1 00:00:00').to_i,
176
+ }
177
+ @poll_secs ||= Marty::Config['MARTY_EVENT_POLL_SECS'] || 0
178
+ time_now_i = Time.zone.now.to_i
179
+ cutoff = Time.zone.at(@all_finished[:timestamp]).
180
+ strftime('%Y-%m-%d %H:%M:%S.%6N')
181
+
182
+ if time_now_i - @all_finished[:timestamp] > @poll_secs
183
+ raw = get_data(
184
+ "SELECT * FROM
185
+ (SELECT ROW_NUMBER() OVER (PARTITION BY klass,
186
+ subject_id,
187
+ enum_event_operation
188
+ ORDER BY end_dt DESC) rownum, *
189
+ FROM (#{BASE_QUERY}) sub2
190
+ WHERE end_dt IS NOT NULL and end_dt > '#{cutoff}') sub1
191
+ WHERE rownum = 1"
192
+ )
193
+ @all_finished[:timestamp] = time_now_i
194
+ raw.each_with_object(@all_finished[:data]) do |ev, hash|
195
+ subhash = hash[[ev["klass"], ev["subject_id"]]] ||= {}
196
+ subhash[ev["enum_event_operation"]] =
197
+ ev["end_dt"].strftime("%Y-%m-%d %H:%M:%S")
198
+ end
199
+ end
200
+ @all_finished[:data]
201
+ end
202
+
203
+ def self.get_finished(klass, id)
204
+ all_finished[[klass, id]]
205
+ end
206
+ end
@@ -0,0 +1,9 @@
1
+ class CreateMartyEventOperationEnum < ActiveRecord::Migration
2
+ def change
3
+ values = Marty::EnumEventOperation::VALUES
4
+ str_values = values.map {|v| ActiveRecord::Base.connection.quote v}.join ','
5
+ execute <<-SQL
6
+ CREATE TYPE enum_event_operations AS ENUM (#{str_values})
7
+ SQL
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ class CreateMartyEvents < ActiveRecord::Migration
2
+ def change(*args)
3
+ # Giant hack to monkey patch connection so that we can create an
4
+ # UNLOGGED table in PostgreSQL.
5
+ class << @connection
6
+ alias_method :old_execute, :execute
7
+ define_method(:execute) { |sql, name=nil|
8
+ old_execute(sql.sub('CREATE', 'CREATE UNLOGGED'), name)
9
+ }
10
+ end
11
+ create_table :marty_events do |t|
12
+ t.integer :promise_id, null: true
13
+ t.string :klass, null: false, limit: 255
14
+ t.integer :subject_id, null: false
15
+ t.pg_enum :enum_event_operation, null: false
16
+ t.datetime :start_dt, null: true
17
+ t.datetime :end_dt, null: true
18
+ t.integer :expire_secs, null: true
19
+ t.string :comment, null: true
20
+ end
21
+ class << @connection
22
+ alias_method :execute, :old_execute
23
+ end
24
+ add_index :marty_events, [:klass, :subject_id,
25
+ :enum_event_operation],
26
+ name: 'idx_klass_id_op'
27
+ add_index :marty_events, [:klass, :subject_id],
28
+ name: 'idx_klass_id'
29
+ end
30
+ end
data/db/seeds.rb CHANGED
@@ -46,3 +46,7 @@ unless Marty::Tag.find_by_name('DEV')
46
46
  tag.created_dt = 'infinity'
47
47
  tag.save!
48
48
  end
49
+
50
+ # one time set up for delayed_job/promises, override as needed
51
+ Marty::Config["DELAYED_JOB_PARAMS"] = "-n 4"
52
+ Marty::Config["DELAYED_JOB_PATH"] = "bin/delayed_job"
@@ -3,6 +3,59 @@ module Marty::Migrations
3
3
  "marty_"
4
4
  end
5
5
 
6
+ def new_enum(klass, prefix_override = nil)
7
+ raise "bad class arg #{klass}" unless
8
+ klass.is_a?(Class) && klass < ActiveRecord::Base
9
+
10
+ raise "model class needs VALUES (as Set)" unless
11
+ klass.const_defined?(:VALUES)
12
+
13
+ values = klass::VALUES
14
+ str_values =
15
+ values.map {|v| ActiveRecord::Base.connection.quote v}.join ','
16
+
17
+ #hacky way to get name
18
+ prefix = prefix_override || tb_prefix
19
+ enum_name = klass.table_name.sub(/^#{prefix}_*/, '')
20
+
21
+ execute <<-SQL
22
+ CREATE TYPE #{enum_name} AS ENUM (#{str_values});
23
+ SQL
24
+ end
25
+
26
+ def update_enum(klass, prefix_override = nil)
27
+ raise "bad class arg #{klass}" unless
28
+ klass.is_a?(Class) && klass < ActiveRecord::Base
29
+
30
+ raise "model class needs VALUES (as Set)" unless
31
+ klass.const_defined?(:VALUES)
32
+
33
+ #hacky way to get name
34
+ prefix = prefix_override || tb_prefix
35
+ enum_name = klass.table_name.sub(/^#{prefix}/, '')
36
+
37
+ #check values against underlying values
38
+ res = execute <<-SQL
39
+ SELECT ENUM_RANGE(null::#{enum_name});
40
+ SQL
41
+
42
+ db_values = res.first['enum_range'].gsub(/[{}]/, '').split(',')
43
+ ex_values = klass::VALUES - db_values
44
+
45
+ puts "no new #{klass}::VALUES to add" if ex_values.empty?
46
+
47
+ #hack to prevent transaction
48
+ execute("COMMIT;")
49
+ ex_values.each do |v|
50
+ prepped_v = ActiveRecord::Base.connection.quote(v)
51
+
52
+ execute <<-SQL
53
+ ALTER TYPE #{enum_name} ADD VALUE #{prepped_v};
54
+ SQL
55
+ end
56
+ execute("BEGIN;")
57
+ end
58
+
6
59
  def add_fk(from_table, to_table, options = {})
7
60
  options[:column] ||= "#{to_table.to_s.singularize}_id"
8
61
 
@@ -20,7 +20,6 @@ class Delorean::BaseModule::NodeCall
20
20
  raise "bad arg to %" unless args.is_a?(Array)
21
21
  attr = nil
22
22
  end
23
-
24
23
  script, tag = engine.module_name, engine.sset.tag
25
24
  nn = node.is_a?(Class) ? node.name : node.to_s
26
25
 
@@ -40,7 +39,6 @@ class Delorean::BaseModule::NodeCall
40
39
  parent_id: params[:_parent_id],
41
40
  )
42
41
  params[:_promise_id] = promise.id
43
-
44
42
  begin
45
43
  job = Delayed::Job.enqueue Marty::PromiseJob.
46
44
  new(promise, title, script, tag, nn, params, args, hook)
@@ -58,15 +56,22 @@ class Delorean::BaseModule::NodeCall
58
56
  # been reserved yet.
59
57
  promise.job_id = job.id
60
58
  promise.save!
61
-
59
+ event = Marty::Event.
60
+ create!(promise_id: promise.id,
61
+ klass: params[:__metadata__][:klass],
62
+ subject_id: params[:__metadata__][:id],
63
+ enum_event_operation:
64
+ params[:__metadata__][:operation]) if
65
+ params[:__metadata__]
62
66
  Marty::PromiseProxy.new(promise.id, timeout, attr)
63
67
  end
64
68
  end
65
69
 
70
+
66
71
  class Delorean::Engine
67
- def background_eval(node, params, attrs)
72
+ def background_eval(node, params, attrs, meta = {})
68
73
  raise "background_eval bad params" unless params.is_a?(Hash)
69
-
74
+ params[:__metadata__] = meta unless meta.empty?
70
75
  nc = Delorean::BaseModule::NodeCall.new({}, self, node, params)
71
76
  # start the background promise
72
77
  nc | attrs
data/lib/marty/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Marty
2
- VERSION = "1.0.12"
2
+ VERSION = "1.0.13"
3
3
  end
@@ -0,0 +1,5 @@
1
+ class Gemini::EnumEventOperation < Gemini::Base
2
+ extend Marty::EnumEventOperation
3
+
4
+ VALUES = set['PRICING', 'CRA', 'AVM']
5
+ end
@@ -1,16 +1,6 @@
1
1
  class CreateEnums < ActiveRecord::Migration
2
+ include Marty::Migrations
2
3
  def change
3
- values = Gemini::EnumState::VALUES
4
- str_values =
5
- values.map {|v| ActiveRecord::Base.connection.quote v}.join ','
6
-
7
- ActiveRecord::Base.schema_migrations_table_name
8
-
9
- # FIXME: very crude
10
- name = 'enum_states'
11
-
12
- execute <<-SQL
13
- CREATE TYPE #{name} AS ENUM (#{str_values});
14
- SQL
4
+ new_enum(Gemini::EnumState, 'gemini')
15
5
  end
16
6
  end
@@ -0,0 +1,9 @@
1
+ class AddBulkPricingEventOps < ActiveRecord::Migration
2
+ def change
3
+ execute("COMMIT;")
4
+ execute "ALTER TYPE enum_event_operations ADD VALUE 'PRICING';"
5
+ execute "ALTER TYPE enum_event_operations ADD VALUE 'CRA';"
6
+ execute "ALTER TYPE enum_event_operations ADD VALUE 'AVM';"
7
+ execute("BEGIN;")
8
+ end
9
+ end
data/spec/job_helper.rb CHANGED
@@ -88,6 +88,13 @@ Y:
88
88
  d = [Y(q=i) | ['a'] for i in [1, 2]]
89
89
  EOS
90
90
 
91
+ NAME_I = "PromiseI"
92
+ SCRIPT_I = <<EOS
93
+ SLEEPER:
94
+ secs =? nil
95
+ a = Gemini::Helper.sleep(secs) && secs
96
+ EOS
97
+
91
98
  def promise_bodies
92
99
  {
93
100
  NAME_A => SCRIPT_A,
@@ -98,5 +105,6 @@ def promise_bodies
98
105
  NAME_F => SCRIPT_F,
99
106
  NAME_G => SCRIPT_G,
100
107
  NAME_H => SCRIPT_H,
108
+ NAME_I => SCRIPT_I,
101
109
  }
102
110
  end
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+ require 'job_helper'
3
+
4
+ describe Marty::Event do
5
+ before(:all) do
6
+ @clean_file = "/tmp/clean_#{Process.pid}.psql"
7
+ save_clean_db(@clean_file)
8
+
9
+ # transactional fixtures interfere with queueing jobs
10
+ self.use_transactional_fixtures = false
11
+
12
+ # Needed here because shutting transactional fixtures off
13
+ # means we lose the globally set user
14
+ Mcfly.whodunnit = UserHelpers.system_user
15
+
16
+ Marty::Script.load_script_bodies(promise_bodies, Date.today)
17
+ start_delayed_job
18
+ Marty::Config["MARTY_EVENT_POLL_SECS"] = 1
19
+
20
+ @time = Time.zone.now
21
+ @date_string = @time.strftime('%Y-%m-%d')
22
+ @old_start = '1970-01-01 08:00:00'
23
+ @old_end = '1970-01-01 09:00:00'
24
+ # add events
25
+ [['testcl1', 123, @time, nil, nil, 'AVM', 'a comment'],
26
+ ['testcl1', 123, @time + 2.second, nil,nil, 'CRA', 'b comment'],
27
+ ['testcl1', 123, @time + 4.seconds, nil,10000, 'PRICING', 'c comment'],
28
+ ['testcl2', 123, @time, nil, 2, 'AVM', 'e comment'],
29
+ ['testcl2', 123, @time + 1.second, nil, 4, 'CRA', 'f comment'],
30
+ ['testcl2', 123, Time.zone.parse(@old_start),
31
+ Time.zone.parse(@old_end), nil, 'PRICING', 'old event'],
32
+ ].each do
33
+ |klass, subjid, startdt, enddt, expire, op, comment|
34
+ Marty::Event.create!(klass: klass,
35
+ subject_id: subjid,
36
+ start_dt: startdt,
37
+ end_dt: enddt,
38
+ expire_secs: expire,
39
+ comment: comment,
40
+ enum_event_operation: op)
41
+ end
42
+
43
+
44
+ engine = Marty::ScriptSet.new.get_engine(NAME_I)
45
+ res = engine.background_eval("SLEEPER", {"secs" => 5}, ["a"],
46
+ {klass: "testcl3",
47
+ id: 987,
48
+ operation: 'PRICING'})
49
+ res.force
50
+ sleep 5
51
+ end
52
+
53
+ after(:all) do
54
+ self.use_transactional_fixtures = true
55
+ restore_clean_db(@clean_file)
56
+ stop_delayed_job
57
+ end
58
+
59
+
60
+ it "Event tests" do
61
+ expect(Marty::Event.currently_running('testcl1', 123)).to eq(
62
+ ['AVM', 'CRA', 'PRICING'])
63
+ expect(Marty::Event.currently_running('testcl2', 123)).to eq([])
64
+ expect(Marty::Event.currently_running('testcl3', 987)).to eq([])
65
+
66
+ expect(Marty::Event.last_event('testcl1', 123)).to include(
67
+ {"klass"=>"testcl1",
68
+ "subject_id"=>123,
69
+ "enum_event_operation"=>"PRICING",
70
+ "comment"=>"c comment", "expire_secs"=>10000})
71
+ expect(Marty::Event.last_event('testcl2', 123)).to include(
72
+ {"klass"=>"testcl2",
73
+ "subject_id"=>123,
74
+ "enum_event_operation"=>"CRA",
75
+ "comment"=>"f comment",
76
+ "expire_secs"=>4})
77
+ expect(Marty::Event.last_event('testcl3', 987)).to include(
78
+ {"klass"=>"testcl3",
79
+ "subject_id"=>987,
80
+ "enum_event_operation"=>"PRICING",
81
+ "comment"=>nil,
82
+ "expire_secs"=>nil})
83
+
84
+ Timecop.freeze(@time+1.second)
85
+ Marty::Event.clear_cache
86
+ expect(Marty::Event.currently_running('testcl1', 123)).to eq(
87
+ ['AVM', 'CRA', 'PRICING'])
88
+ expect(Marty::Event.currently_running('testcl2', 123)).to eq(
89
+ ['AVM', 'CRA'])
90
+
91
+ Timecop.freeze(@time+3.seconds)
92
+ Marty::Event.clear_cache
93
+ expect(Marty::Event.currently_running('testcl1', 123)).to eq(
94
+ ['AVM', 'CRA', 'PRICING'])
95
+ expect(Marty::Event.currently_running('testcl2', 123)).to eq(
96
+ ['CRA'])
97
+
98
+ Timecop.freeze(@time+6.seconds)
99
+ expect(Marty::Event.currently_running('testcl1', 123)).to eq(
100
+ ['AVM', 'CRA', 'PRICING'])
101
+ expect(Marty::Event.currently_running('testcl2', 123)).to eq(
102
+ [])
103
+
104
+ Timecop.return
105
+
106
+ af = Marty::Event.all_finished
107
+ expect(af.count).to eq(2)
108
+ expect(af).to include(['testcl3', 987])
109
+ expect(af).to include(['testcl2', 123])
110
+ expect(af[['testcl3', 987]]).to include('PRICING')
111
+ expect(af[['testcl3', 987]]['PRICING']).to start_with(@date_string)
112
+ expect(af[['testcl2', 123]]).to include('PRICING')
113
+ expect(af[['testcl2', 123]]['PRICING']).to eq(@old_end)
114
+
115
+ expect(Marty::Event.currently_running('testcl1', 123)).to eq(
116
+ ['AVM', 'CRA', 'PRICING'])
117
+ expect(Marty::Event.op_is_running?('testcl1', 123, 'AVM')).to be_truthy
118
+ Marty::Event.finish_event('testcl1', 123, 'AVM', 'wassup')
119
+ Marty::Event.clear_cache
120
+ expect(Marty::Event.currently_running('testcl1', 123)).to eq(
121
+ ['CRA', 'PRICING'])
122
+ expect(Marty::Event.op_is_running?('testcl1', 123, 'AVM')).to be_falsey
123
+ expect(Marty::Event.op_is_running?('testcl1', 123, 'CRA')).to be_truthy
124
+
125
+ ev = Marty::Event.lookup_event('testcl1', 123, 'AVM')
126
+ expect(ev.length).to eq(1)
127
+ expect(ev.first).to include({"klass"=>"testcl1",
128
+ "subject_id"=>123,
129
+ "enum_event_operation"=>"AVM",
130
+ "comment"=>"wassup",
131
+ "expire_secs"=>nil})
132
+ Marty::Event.update_comment(ev.first, "updated")
133
+ ev = Marty::Event.lookup_event('testcl1', 123, 'AVM')
134
+ expect(ev.first).to include({"comment"=>"updated"})
135
+ expect(Marty::Event.compact_end_dt(ev.first)).to match(/\d\d:\d\d/)
136
+ expect(Marty::Event.pretty_op(ev.first)).to eq('Avm')
137
+ ev = Marty::Event.lookup_event('testcl1', 123, 'PRICING').first
138
+ expect(Marty::Event.pretty_op(ev)).to eq('Pricing')
139
+
140
+ af = Marty::Event.all_finished
141
+ expect(af.count).to eq(3)
142
+ expect(af[['testcl3', 987]]).to include('PRICING')
143
+ expect(af[['testcl1', 123]]).to include('AVM')
144
+ expect(af[['testcl1', 123]]['AVM']).to start_with(@date_string)
145
+
146
+ expect {Marty::Event.create_event('testcl', 1234, 'AVM', Time.zone.now, 600,
147
+ "the comment") }.not_to raise_error
148
+
149
+ expect {Marty::Event.create_event('testcl', 1234, 'AVM', Time.zone.now, 600,
150
+ "the comment") }.
151
+ to raise_error(%r!AVM is already running for testcl/1234!)
152
+ expect {Marty::Event.create_event('testcl', 2345, 'AVM', Time.zone.now, 600,
153
+ "the comment") }.not_to raise_error
154
+ expect {Marty::Event.finish_event('testcl', 1234, 'AVM', "new comment") }.
155
+ not_to raise_error
156
+ expect {Marty::Event.finish_event('testcl', 1234, 'AVM', "new comment") }.
157
+ to raise_error(%r!event testcl/1234/AVM not found!)
158
+ expect {Marty::Event.finish_event('testcl', 2345, 'AVM', 'foobar') }.
159
+ not_to raise_error
160
+ expect {Marty::Event.finish_event('testcl', 2345, 'AVM', 'foobar') }.
161
+ to raise_error(%r!event testcl/2345/AVM not found!)
162
+ expect {Marty::Event.create_event('testcl', 1234, 'AMV', Time.zone.now, 600,
163
+ "the comment") }.
164
+ to raise_error(%r!PG::.*invalid input value for enum.*"AMV"!)
165
+ Marty::Event.clear_cache
166
+ af = Marty::Event.all_finished
167
+ expect(af.count).to eq(5)
168
+ expect(af).to include(['testcl', 1234])
169
+ expect(af).to include(['testcl', 2345])
170
+ expect(af[['testcl', 1234]]).to include('AVM')
171
+ expect(af[['testcl', 2345]]).to include('AVM')
172
+ expect(af[['testcl', 1234]]['AVM']).to start_with(@date_string)
173
+ expect(af[['testcl', 2345]]['AVM']).to start_with(@date_string)
174
+
175
+ end
176
+ end
@@ -1,6 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'marty'
3
- require 'delorean_lang'
4
2
  require 'job_helper'
5
3
 
6
4
  describe Marty::Promise, slow: true do
data/spec/spec_helper.rb CHANGED
@@ -30,6 +30,11 @@ class ActiveRecord::Base
30
30
  end
31
31
 
32
32
  Capybara.register_driver :selenium do |app|
33
+ # use personal firefox if it exists
34
+ personal_firefox = File.expand_path('~/firefox/firefox')
35
+ Selenium::WebDriver::Firefox::Binary.path = personal_firefox if
36
+ File.exists?(personal_firefox)
37
+
33
38
  client = Selenium::WebDriver::Remote::Http::Default.new
34
39
  profile = Selenium::WebDriver::Firefox::Profile.new
35
40
  profile.load_no_focus_lib = true
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marty
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.12
4
+ version: 1.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arman Bostani
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2016-09-21 00:00:00.000000000 Z
17
+ date: 2016-10-28 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: pg
@@ -382,6 +382,8 @@ files:
382
382
  - app/models/marty/config.rb
383
383
  - app/models/marty/data_grid.rb
384
384
  - app/models/marty/enum.rb
385
+ - app/models/marty/enum_event_operation.rb
386
+ - app/models/marty/event.rb
385
387
  - app/models/marty/grid_index_boolean.rb
386
388
  - app/models/marty/grid_index_int4range.rb
387
389
  - app/models/marty/grid_index_integer.rb
@@ -432,6 +434,8 @@ files:
432
434
  - db/migrate/104_create_marty_grid_index_strings.rb
433
435
  - db/migrate/105_create_marty_grid_index_booleans.rb
434
436
  - db/migrate/106_make_grid_indexes_nullable.rb
437
+ - db/migrate/200_create_marty_event_operation_enum.rb
438
+ - db/migrate/201_create_marty_events.rb
435
439
  - db/seeds.rb
436
440
  - gemini_deprecations.md
437
441
  - lib/marty.rb
@@ -480,6 +484,7 @@ files:
480
484
  - spec/dummy/app/models/gemini/amortization_type.rb
481
485
  - spec/dummy/app/models/gemini/bud_category.rb
482
486
  - spec/dummy/app/models/gemini/entity.rb
487
+ - spec/dummy/app/models/gemini/enum_event_operation.rb
483
488
  - spec/dummy/app/models/gemini/enum_state.rb
484
489
  - spec/dummy/app/models/gemini/extras/data_import.rb
485
490
  - spec/dummy/app/models/gemini/extras/settlement_import.rb
@@ -522,6 +527,7 @@ files:
522
527
  - spec/dummy/db/migrate/20150420000003_create_grouping_head_versions.rb
523
528
  - spec/dummy/db/migrate/20151023000001_create_simple.rb
524
529
  - spec/dummy/db/migrate/20160100000038_create_gemini_states.rb
530
+ - spec/dummy/db/migrate/20160923183516_add_bulk_pricing_event_ops.rb
525
531
  - spec/dummy/db/seeds.rb
526
532
  - spec/dummy/delorean/blame_report.dl
527
533
  - spec/dummy/delorean/data_report.dl
@@ -1562,6 +1568,7 @@ files:
1562
1568
  - spec/models/api_auth_spec.rb
1563
1569
  - spec/models/config_spec.rb
1564
1570
  - spec/models/data_grid_spec.rb
1571
+ - spec/models/event_spec.rb
1565
1572
  - spec/models/import_type_spec.rb
1566
1573
  - spec/models/posting_spec.rb
1567
1574
  - spec/models/promise_spec.rb