marty 2.5.2 → 2.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +4 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +11 -589
- data/Gemfile +9 -9
- data/Gemfile.lock +1 -1
- data/Rakefile +1 -3
- data/app/components/marty/api_auth_view.rb +3 -3
- data/app/components/marty/api_config_view.rb +8 -8
- data/app/components/marty/api_log_view.rb +16 -20
- data/app/components/marty/auth_app.rb +6 -6
- data/app/components/marty/base_rule_view.rb +27 -19
- data/app/components/marty/config_view.rb +12 -9
- data/app/components/marty/data_grid_view.rb +26 -26
- data/app/components/marty/delorean_rule_view.rb +0 -1
- data/app/components/marty/event_view.rb +27 -27
- data/app/components/marty/extras/layout.rb +26 -26
- data/app/components/marty/extras/misc.rb +2 -2
- data/app/components/marty/grid.rb +13 -13
- data/app/components/marty/grid_append_only.rb +0 -1
- data/app/components/marty/import_type_view.rb +13 -13
- data/app/components/marty/import_view.rb +17 -16
- data/app/components/marty/log_view.rb +16 -14
- data/app/components/marty/main_auth_app.rb +59 -59
- data/app/components/marty/main_auth_app/client/main_auth_app.js +3 -3
- data/app/components/marty/mcfly_grid_panel.rb +10 -10
- data/app/components/marty/new_posting_form.rb +11 -11
- data/app/components/marty/new_posting_window.rb +0 -1
- data/app/components/marty/posting_grid.rb +12 -13
- data/app/components/marty/promise_view.rb +6 -6
- data/app/components/marty/report_form.rb +50 -53
- data/app/components/marty/report_select.rb +27 -27
- data/app/components/marty/reporting.rb +4 -4
- data/app/components/marty/script_form.rb +40 -42
- data/app/components/marty/script_grid.rb +24 -24
- data/app/components/marty/script_tester.rb +40 -42
- data/app/components/marty/scripting.rb +25 -27
- data/app/components/marty/simple_app.rb +24 -9
- data/app/components/marty/tag_grid.rb +12 -13
- data/app/components/marty/user_view.rb +35 -35
- data/app/controllers/marty/application_controller.rb +3 -4
- data/app/controllers/marty/components_controller.rb +1 -1
- data/app/controllers/marty/delayed_job_controller.rb +1 -0
- data/app/controllers/marty/diagnostic/controller.rb +4 -6
- data/app/controllers/marty/job_controller.rb +6 -6
- data/app/controllers/marty/report_controller.rb +11 -11
- data/app/controllers/marty/rpc_controller.rb +15 -16
- data/app/helpers/marty/script_set.rb +4 -4
- data/app/models/marty/api_auth.rb +4 -5
- data/app/models/marty/api_config.rb +1 -1
- data/app/models/marty/base.rb +9 -8
- data/app/models/marty/base_rule.rb +18 -13
- data/app/models/marty/config.rb +4 -5
- data/app/models/marty/data_grid.rb +157 -181
- data/app/models/marty/delorean_rule.rb +63 -62
- data/app/models/marty/enum.rb +1 -1
- data/app/models/marty/event.rb +56 -59
- data/app/models/marty/helper.rb +38 -6
- data/app/models/marty/import_type.rb +6 -6
- data/app/models/marty/log.rb +3 -2
- data/app/models/marty/name_validator.rb +3 -2
- data/app/models/marty/pg_enum.rb +3 -4
- data/app/models/marty/posting.rb +20 -24
- data/app/models/marty/promise.rb +28 -30
- data/app/models/marty/script.rb +30 -28
- data/app/models/marty/tag.rb +8 -8
- data/app/models/marty/token.rb +2 -2
- data/app/models/marty/user.rb +24 -23
- data/app/models/marty/vw_promise.rb +10 -11
- data/config/routes.rb +2 -2
- data/delorean/blame_report.dl +268 -0
- data/{spec/dummy/delorean/fields.dl → delorean/marty_fields.dl} +8 -0
- data/delorean/table_report.dl +34 -0
- data/docker-compose.dummy.yml +2 -3
- data/lib/marty/aws/base.rb +8 -8
- data/lib/marty/aws/request.rb +4 -4
- data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -0
- data/lib/marty/content_handler.rb +25 -25
- data/lib/marty/data_change.rb +49 -71
- data/lib/marty/data_conversion.rb +20 -28
- data/lib/marty/data_exporter.rb +25 -28
- data/lib/marty/data_importer.rb +25 -27
- data/lib/marty/engine.rb +1 -2
- data/lib/marty/json_schema.rb +22 -24
- data/lib/marty/logger.rb +6 -9
- data/lib/marty/mcfly_model.rb +20 -24
- data/lib/marty/migrations.rb +37 -35
- data/lib/marty/monkey.rb +33 -33
- data/lib/marty/permissions.rb +18 -18
- data/lib/marty/promise_job.rb +17 -17
- data/lib/marty/promise_proxy.rb +6 -6
- data/lib/marty/relation.rb +6 -7
- data/lib/marty/rpc_call.rb +13 -12
- data/lib/marty/rule_script_set.rb +32 -28
- data/lib/marty/schema_helper.rb +37 -51
- data/lib/marty/util.rb +25 -24
- data/lib/marty/version.rb +1 -1
- data/lib/marty/xl.rb +121 -115
- data/make-dummy.mk +3 -0
- data/marty.gemspec +21 -21
- data/other/marty/api/base.rb +34 -35
- data/other/marty/diagnostic/aws/ec2_instance.rb +8 -8
- data/other/marty/diagnostic/base.rb +13 -14
- data/other/marty/diagnostic/collection.rb +2 -1
- data/other/marty/diagnostic/connections.rb +8 -6
- data/other/marty/diagnostic/database.rb +1 -0
- data/other/marty/diagnostic/delayed_job_version.rb +7 -9
- data/other/marty/diagnostic/delayed_job_worker_total_count.rb +1 -1
- data/other/marty/diagnostic/delayed_job_workers.rb +1 -1
- data/other/marty/diagnostic/environment_variables.rb +17 -15
- data/other/marty/diagnostic/fatal.rb +1 -1
- data/other/marty/diagnostic/node.rb +5 -9
- data/other/marty/diagnostic/nodes.rb +7 -5
- data/other/marty/diagnostic/packer.rb +7 -7
- data/other/marty/diagnostic/reporter.rb +24 -27
- data/other/marty/diagnostic/version.rb +3 -5
- data/script/rails +2 -1
- data/spec/controllers/application_controller_spec.rb +6 -6
- data/spec/controllers/delayed_job_controller_spec.rb +4 -4
- data/spec/controllers/diagnostic/controller_spec.rb +59 -60
- data/spec/controllers/job_controller_spec.rb +68 -69
- data/spec/controllers/rpc_controller_spec.rb +353 -359
- data/spec/controllers/rpc_import_spec.rb +15 -16
- data/spec/dummy/delorean/blame_report.dl +110 -15
- data/spec/dummy/delorean/data_report.dl +4 -4
- data/spec/dummy/delorean/marty_fields.dl +63 -0
- data/spec/dummy/delorean/table_report.dl +34 -0
- data/spec/features/auth_app_spec.rb +1 -2
- data/spec/features/data_import_spec.rb +2 -3
- data/spec/features/enum_spec.rb +42 -46
- data/spec/features/jobs_dashboard_spec.rb +14 -8
- data/spec/features/log_view_spec.rb +40 -43
- data/spec/features/reporting_spec.rb +15 -15
- data/spec/features/rule_spec.rb +195 -190
- data/spec/features/scripting_spec.rb +17 -20
- data/spec/features/scripting_test_spec.rb +32 -33
- data/spec/features/user_view_spec.rb +15 -17
- data/spec/job_helper.rb +11 -11
- data/spec/lib/data_blame_spec.rb +82 -0
- data/spec/lib/data_exporter_spec.rb +31 -32
- data/spec/lib/data_importer_spec.rb +382 -395
- data/spec/lib/delorean_query_spec.rb +117 -119
- data/spec/lib/json_schema_spec.rb +382 -392
- data/spec/lib/logger_spec.rb +23 -24
- data/spec/lib/mcfly_model_spec.rb +112 -109
- data/spec/lib/migrations_spec.rb +10 -10
- data/spec/lib/struct_compare_spec.rb +6 -6
- data/spec/lib/table_report_spec.rb +90 -0
- data/spec/lib/xl_spec.rb +63 -65
- data/spec/lib/xl_styles_spec.rb +16 -19
- data/spec/models/api_auth_spec.rb +30 -30
- data/spec/models/config_spec.rb +32 -32
- data/spec/models/data_grid_spec.rb +642 -655
- data/spec/models/event_spec.rb +96 -88
- data/spec/models/import_type_spec.rb +20 -20
- data/spec/models/posting_spec.rb +35 -35
- data/spec/models/promise_spec.rb +5 -5
- data/spec/models/rule_spec.rb +280 -269
- data/spec/models/script_spec.rb +27 -18
- data/spec/models/user_spec.rb +9 -9
- data/spec/other/diagnostic/base_spec.rb +20 -19
- data/spec/other/diagnostic/collection_spec.rb +6 -5
- data/spec/other/diagnostic/delayed_job_version_spec.rb +1 -1
- data/spec/other/diagnostic/delayed_job_workers_spec.rb +8 -8
- data/spec/other/diagnostic/reporter_spec.rb +31 -33
- data/spec/spec_helper.rb +5 -5
- data/spec/support/chromedriver.rb +3 -5
- data/spec/support/components/netzke_combobox.rb +1 -1
- data/spec/support/components/netzke_grid.rb +17 -17
- data/spec/support/custom_matchers.rb +2 -2
- data/spec/support/download_helper.rb +1 -1
- data/spec/support/helper.rb +1 -2
- data/spec/support/netzke.rb +31 -31
- data/spec/support/performance_helper.rb +8 -8
- data/spec/support/post_run_logger.rb +1 -2
- data/spec/support/setup.rb +1 -4
- data/spec/support/shared_connection.rb +2 -2
- data/spec/support/structure_compare.rb +21 -22
- data/spec/support/suite.rb +1 -2
- data/spec/support/users.rb +5 -6
- metadata +32 -26
data/app/models/marty/helper.rb
CHANGED
@@ -1,21 +1,53 @@
|
|
1
1
|
class Marty::Helper
|
2
2
|
include Delorean::Model
|
3
3
|
|
4
|
-
delorean_fn :sleep, sig: 1 do
|
5
|
-
|seconds|
|
4
|
+
delorean_fn :sleep, sig: 1 do |seconds|
|
6
5
|
Kernel.sleep seconds
|
7
6
|
end
|
8
7
|
|
9
|
-
delorean_fn :range_step, sig: 3 do
|
10
|
-
|rstart, rend, step|
|
8
|
+
delorean_fn :range_step, sig: 3 do |rstart, rend, step|
|
11
9
|
(rstart..rend).step(step).to_a
|
12
10
|
end
|
13
11
|
|
14
|
-
delorean_fn :my_ip, sig:0 do
|
12
|
+
delorean_fn :my_ip, sig: 0 do
|
15
13
|
Marty::Diagnostic::Node.my_ip
|
16
14
|
end
|
17
15
|
|
18
|
-
delorean_fn :git, sig:0 do
|
16
|
+
delorean_fn :git, sig: 0 do
|
19
17
|
[my_ip, ENV['DELAYED_VER']]
|
20
18
|
end
|
19
|
+
|
20
|
+
delorean_fn :infinity_dt, sig: 1 do |pt|
|
21
|
+
Mcfly.is_infinity pt
|
22
|
+
end
|
23
|
+
|
24
|
+
delorean_fn :constantize, sig: 1 do |class_name|
|
25
|
+
raise 'bad class_name' unless class_name.is_a?(String)
|
26
|
+
|
27
|
+
class_name.constantize
|
28
|
+
end
|
29
|
+
|
30
|
+
delorean_fn :get_column_types, sig: 1 do |klass|
|
31
|
+
Marty::DataConversion.col_types(klass)
|
32
|
+
end
|
33
|
+
|
34
|
+
delorean_fn :parse_csv_to_hash, sig: 3 do |txt, comma_sep, types|
|
35
|
+
txt ||= ''
|
36
|
+
headers, *rows = CSV.parse(txt.strip,
|
37
|
+
headers: true,
|
38
|
+
col_sep: (comma_sep ? ',' : "\t")).to_a
|
39
|
+
rows.map do |row|
|
40
|
+
headers.zip(row).each_with_object({}) do |(h, v), res|
|
41
|
+
res[h] = v.blank? ? nil :
|
42
|
+
Marty::DataConversion.convert(v, (types[h] || 'text').to_sym)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
delorean_fn :to_csv, sig: [1, 2] do |*args|
|
48
|
+
# NOTE: can't use |data, config| due to delorean_fn weirdness.
|
49
|
+
data, config = args
|
50
|
+
|
51
|
+
Marty::DataExporter.to_csv(data, config)
|
52
|
+
end
|
21
53
|
end
|
@@ -4,7 +4,7 @@ class Marty::ImportType < Marty::Base
|
|
4
4
|
klass = entry.get_model_class
|
5
5
|
|
6
6
|
unless klass.is_a?(Class) && klass < ActiveRecord::Base
|
7
|
-
entry.errors.add :base,
|
7
|
+
entry.errors.add :base, 'bad model name'
|
8
8
|
return
|
9
9
|
end
|
10
10
|
|
@@ -12,19 +12,19 @@ class Marty::ImportType < Marty::Base
|
|
12
12
|
entry.cleaner_function,
|
13
13
|
entry.validation_function,
|
14
14
|
entry.preprocess_function,
|
15
|
-
].each
|
15
|
+
].each do |func|
|
16
16
|
entry.errors.add(:base, "unknown class method #{func}") if
|
17
17
|
func && !klass.respond_to?(func.to_sym)
|
18
|
-
|
18
|
+
end
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
before_validation do
|
23
23
|
# Fix issue with blank strings in popup edit form or grid
|
24
24
|
# being interpreted as a function
|
25
|
-
self.cleaner_function = nil if
|
26
|
-
self.validation_function = nil if
|
27
|
-
self.preprocess_function = nil if
|
25
|
+
self.cleaner_function = nil if cleaner_function.blank?
|
26
|
+
self.validation_function = nil if validation_function.blank?
|
27
|
+
self.preprocess_function = nil if preprocess_function.blank?
|
28
28
|
end
|
29
29
|
|
30
30
|
belongs_to :role
|
data/app/models/marty/log.rb
CHANGED
@@ -9,7 +9,7 @@ class Marty::Log < Marty::Base
|
|
9
9
|
message: message,
|
10
10
|
details: details,
|
11
11
|
timestamp: Time.zone.now)
|
12
|
-
rescue => e
|
12
|
+
rescue StandardError => e
|
13
13
|
Marty::Util.logger.error("Marty::Logger failure: #{e.message}")
|
14
14
|
end
|
15
15
|
true
|
@@ -18,6 +18,7 @@ class Marty::Log < Marty::Base
|
|
18
18
|
def self.cleanup(days_to_keep)
|
19
19
|
raise "Must give numeric value. (Got '#{days_to_keep}')" unless
|
20
20
|
(Float(days_to_keep) rescue false)
|
21
|
-
|
21
|
+
|
22
|
+
where('timestamp <= ?', Time.zone.now - days_to_keep.to_i.days).delete_all
|
22
23
|
end
|
23
24
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class Marty::NameValidator < ActiveModel::Validator
|
2
2
|
def validate(entry)
|
3
|
-
raise
|
3
|
+
raise 'need field option' unless options[:field]
|
4
|
+
|
4
5
|
field = options[:field].to_sym
|
5
6
|
value = entry.send(field)
|
6
7
|
|
@@ -9,7 +10,7 @@ class Marty::NameValidator < ActiveModel::Validator
|
|
9
10
|
# disallow leading, trailing, >1 internal spaces, special chars (|)
|
10
11
|
if value =~ /\A\s|\s\z|\A.*\s\s.*\z|.*\|.*/
|
11
12
|
entry.errors[field] <<
|
12
|
-
I18n.t(
|
13
|
+
I18n.t('activerecord.errors.messages.extraneous_spaces')
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
data/app/models/marty/pg_enum.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
module Marty::PgEnum
|
2
|
-
|
3
|
-
def [](i0, i1=nil)
|
2
|
+
def [](i0, i1 = nil)
|
4
3
|
# if i1 is provided, then i0 is a pt and we ignore it.
|
5
4
|
index = (i1 || i0).to_s
|
6
5
|
|
7
|
-
raise "no such #{
|
6
|
+
raise "no such #{name}: '#{index}'" unless
|
8
7
|
self::VALUES.include?(index)
|
9
8
|
|
10
9
|
index
|
11
10
|
end
|
12
11
|
|
13
|
-
def get_all(pt=nil)
|
12
|
+
def get_all(pt = nil)
|
14
13
|
self::VALUES.map(&:to_s)
|
15
14
|
end
|
16
15
|
|
data/app/models/marty/posting.rb
CHANGED
@@ -4,7 +4,7 @@ class Marty::Posting < Marty::Base
|
|
4
4
|
mcfly_validates_uniqueness_of :name
|
5
5
|
validates_presence_of :name, :posting_type_id, :comment
|
6
6
|
|
7
|
-
belongs_to :user, class_name:
|
7
|
+
belongs_to :user, class_name: 'Marty::User'
|
8
8
|
belongs_to :posting_type
|
9
9
|
|
10
10
|
def self.make_name(posting_type, dt)
|
@@ -22,8 +22,8 @@ class Marty::Posting < Marty::Base
|
|
22
22
|
|
23
23
|
before_validation :set_posting_name
|
24
24
|
def set_posting_name
|
25
|
-
posting_type = Marty::PostingType.find_by_id(
|
26
|
-
self.name = self.class.make_name(posting_type,
|
25
|
+
posting_type = Marty::PostingType.find_by_id(posting_type_id)
|
26
|
+
self.name = self.class.make_name(posting_type, created_dt)
|
27
27
|
true
|
28
28
|
end
|
29
29
|
|
@@ -41,7 +41,7 @@ class Marty::Posting < Marty::Base
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.get_struct_attrs
|
44
|
-
self.struct_attrs ||= super + [
|
44
|
+
self.struct_attrs ||= super + ['created_dt', 'name']
|
45
45
|
end
|
46
46
|
|
47
47
|
# Not using mcfly_lookup since we don't want these time-warp markers
|
@@ -49,43 +49,39 @@ class Marty::Posting < Marty::Base
|
|
49
49
|
# may allow deletion of postings. i.e. a new one with same name
|
50
50
|
# might be created. Or, use regular validates_uniqueness_of instead
|
51
51
|
# of mcfly_validates_uniqueness_of.
|
52
|
-
delorean_fn :lookup, sig: 1 do
|
53
|
-
|name|
|
52
|
+
delorean_fn :lookup, sig: 1 do |name|
|
54
53
|
p = select(get_struct_attrs).find_by_name(name)
|
55
54
|
make_openstruct(p)
|
56
55
|
end
|
57
56
|
|
58
|
-
delorean_fn :lookup_dt, sig: 1 do
|
59
|
-
|name|
|
57
|
+
delorean_fn :lookup_dt, sig: 1 do |name|
|
60
58
|
find_by_name(name).try(:created_dt)
|
61
59
|
end
|
62
60
|
|
63
|
-
delorean_fn :first_match, sig: [1, 2] do
|
64
|
-
|
65
|
-
raise "bad posting type" if
|
61
|
+
delorean_fn :first_match, sig: [1, 2] do |dt, posting_type = nil|
|
62
|
+
raise 'bad posting type' if
|
66
63
|
posting_type && !posting_type.is_a?(Marty::PostingType)
|
67
64
|
|
68
|
-
q = where(
|
65
|
+
q = where('created_dt <= ?', dt)
|
69
66
|
q = q.where(posting_type_id: posting_type.id) if posting_type
|
70
|
-
q.order(
|
67
|
+
q.order('created_dt DESC').first.attributes
|
71
68
|
end
|
72
69
|
|
73
|
-
def self.get_latest(limit, is_test=nil)
|
70
|
+
def self.get_latest(limit, is_test = nil)
|
74
71
|
# IMPORTANT: is_test arg is ignored (KEEP for backward compat.)
|
75
72
|
|
76
|
-
q=where("created_dt <> 'infinity'").
|
77
|
-
order(
|
73
|
+
q = where("created_dt <> 'infinity'").
|
74
|
+
order('created_dt DESC').limit(limit)
|
78
75
|
end
|
79
76
|
|
80
|
-
delorean_fn :get_latest_by_type, sig: [1, 2] do
|
81
|
-
|
82
|
-
raise
|
83
|
-
raise "bad posting types list" unless posting_types.is_a?(Array)
|
77
|
+
delorean_fn :get_latest_by_type, sig: [1, 2] do |limit, posting_types = []|
|
78
|
+
raise 'missing posting types list' unless posting_types
|
79
|
+
raise 'bad posting types list' unless posting_types.is_a?(Array)
|
84
80
|
|
85
|
-
q=joins(:posting_type).where("created_dt <> 'infinity'").
|
86
|
-
where(marty_posting_types: { name: posting_types }
|
81
|
+
q = joins(:posting_type).where("created_dt <> 'infinity'").
|
82
|
+
where(marty_posting_types: { name: posting_types }).
|
87
83
|
select(get_struct_attrs).
|
88
|
-
order(
|
89
|
-
q.map
|
84
|
+
order('created_dt DESC').limit(limit || 1)
|
85
|
+
q.map(&:attributes)
|
90
86
|
end
|
91
87
|
end
|
data/app/models/marty/promise.rb
CHANGED
@@ -1,28 +1,27 @@
|
|
1
1
|
class Marty::Promise < Marty::Base
|
2
|
-
|
3
2
|
# default timeout (seconds) to wait for promise values
|
4
3
|
DEFAULT_PROMISE_TIMEOUT = Rails.configuration.marty.promise_timeout || 30
|
5
4
|
|
6
5
|
# default timeout (seconds) to wait for jobs to start
|
7
6
|
DEFAULT_JOB_TIMEOUT = Rails.configuration.marty.job_timeout || 10
|
8
7
|
|
9
|
-
def result(force=false)
|
8
|
+
def result(force = false)
|
10
9
|
res = super()
|
11
10
|
Marty::Promise.load_result(res, force)
|
12
11
|
end
|
13
12
|
|
14
|
-
def self.load_result(obj, force=false)
|
13
|
+
def self.load_result(obj, force = false)
|
15
14
|
if force && obj.respond_to?(:__force__)
|
16
15
|
obj = obj.__force__
|
17
16
|
end
|
18
17
|
|
19
18
|
case obj
|
20
19
|
when Array
|
21
|
-
obj.map {|x| load_result(x, force)}
|
20
|
+
obj.map { |x| load_result(x, force) }
|
22
21
|
when Hash
|
23
22
|
p = obj['__promise__']
|
24
23
|
|
25
|
-
if p && obj.length==1
|
24
|
+
if p && obj.length == 1
|
26
25
|
load_result(Marty::PromiseProxy.new(*p), force)
|
27
26
|
else
|
28
27
|
obj.each_with_object({}) { |(k, v), h| h[k] = load_result(v, force) }
|
@@ -34,21 +33,19 @@ class Marty::Promise < Marty::Base
|
|
34
33
|
|
35
34
|
has_many :children,
|
36
35
|
foreign_key: 'parent_id',
|
37
|
-
class_name:
|
36
|
+
class_name: 'Marty::Promise',
|
38
37
|
dependent: :destroy
|
39
38
|
|
40
39
|
validates_presence_of :title
|
41
40
|
|
42
|
-
belongs_to :parent, class_name:
|
43
|
-
belongs_to :user, class_name:
|
41
|
+
belongs_to :parent, class_name: 'Marty::Promise'
|
42
|
+
belongs_to :user, class_name: 'Marty::User'
|
44
43
|
|
45
|
-
def self.cleanup(all=false)
|
46
|
-
begin
|
44
|
+
def self.cleanup(all = false)
|
47
45
|
where('start_dt < ? AND parent_id IS NULL',
|
48
46
|
DateTime.now - (all ? 0.hours : 4.hours)).destroy_all
|
49
|
-
|
47
|
+
rescue StandardError => exc
|
50
48
|
Marty::Util.logger.error("promise GC error: #{exc}")
|
51
|
-
end
|
52
49
|
end
|
53
50
|
|
54
51
|
def raw_conn
|
@@ -60,38 +57,38 @@ class Marty::Promise < Marty::Base
|
|
60
57
|
end
|
61
58
|
|
62
59
|
def set_start
|
63
|
-
if
|
60
|
+
if start_dt || result != {}
|
64
61
|
Marty::Util.logger.error("promise already started: #{self}")
|
65
62
|
return
|
66
63
|
end
|
67
64
|
|
68
65
|
# mark promise as started
|
69
66
|
self.start_dt = DateTime.now
|
70
|
-
|
67
|
+
save!
|
71
68
|
end
|
72
69
|
|
73
70
|
def set_result(res)
|
74
71
|
# log "SETRES #{Process.pid} #{self}"
|
75
72
|
|
76
73
|
# promise must have been started and not yet ended
|
77
|
-
if !
|
74
|
+
if !start_dt || end_dt || result != {}
|
78
75
|
# log "SETERR #{Process.pid} #{self}"
|
79
76
|
Marty::Util.logger.error("unexpected promise state: #{self}")
|
80
77
|
return
|
81
78
|
end
|
82
79
|
|
83
|
-
raise
|
80
|
+
raise 'bad result' unless res.is_a?(Hash)
|
84
81
|
|
85
|
-
self.status = res[
|
82
|
+
self.status = res['error'].nil?
|
86
83
|
self.result = res
|
87
84
|
|
88
85
|
# update title/format from result hash (somewhat hacky)
|
89
|
-
self.title = res[
|
90
|
-
self.cformat = res[
|
86
|
+
self.title = res['title'].to_s if res['title']
|
87
|
+
self.cformat = res['format'].to_s if res['format']
|
91
88
|
|
92
89
|
# mark promise as ended
|
93
90
|
self.end_dt = DateTime.now
|
94
|
-
|
91
|
+
save!
|
95
92
|
|
96
93
|
# log "NOTIFY #{Process.pid}"
|
97
94
|
pg_notify
|
@@ -106,11 +103,11 @@ class Marty::Promise < Marty::Base
|
|
106
103
|
# end
|
107
104
|
|
108
105
|
def wait_for_my_notify(timeout)
|
109
|
-
while true
|
106
|
+
while true
|
110
107
|
# FIXME: we keep using the same timeout. The timeout should be
|
111
108
|
# reduced by total time spent here.
|
112
109
|
n = raw_conn.wait_for_notify(timeout)
|
113
|
-
return n if !n || n=="promise_#{id}"
|
110
|
+
return n if !n || n == "promise_#{id}"
|
114
111
|
end
|
115
112
|
end
|
116
113
|
|
@@ -120,17 +117,17 @@ class Marty::Promise < Marty::Base
|
|
120
117
|
# seems to work.
|
121
118
|
|
122
119
|
# get latest uncached version
|
123
|
-
Marty::Promise.uncached {Marty::Promise.find(id)}
|
120
|
+
Marty::Promise.uncached { Marty::Promise.find(id) }
|
124
121
|
end
|
125
122
|
|
126
123
|
def self.job_by_id(job_id)
|
127
|
-
Delayed::Job.uncached {Delayed::Job.find_by_id(job_id)}
|
124
|
+
Delayed::Job.uncached { Delayed::Job.find_by_id(job_id) }
|
128
125
|
end
|
129
126
|
|
130
127
|
def work_off_job(job)
|
131
128
|
# Create a temporary worker to work off the job
|
132
129
|
Delayed::Job.where(id: job.id).
|
133
|
-
update_all(locked_at: Delayed::Job.db_time_now, locked_by:
|
130
|
+
update_all(locked_at: Delayed::Job.db_time_now, locked_by: 'Temp')
|
134
131
|
w = Delayed::Worker.new
|
135
132
|
w.run(job)
|
136
133
|
end
|
@@ -138,7 +135,7 @@ class Marty::Promise < Marty::Base
|
|
138
135
|
def wait_for_result(timeout)
|
139
136
|
# FIXME: Not sure that comparing result with empty hash if a good idea
|
140
137
|
# perhaps it's better to use .present? or .blank?
|
141
|
-
return
|
138
|
+
return result if result != {}
|
142
139
|
|
143
140
|
begin
|
144
141
|
# start listening on promise's notification
|
@@ -150,7 +147,8 @@ class Marty::Promise < Marty::Base
|
|
150
147
|
if !last.start_dt
|
151
148
|
job = Marty::Promise.job_by_id(last.job_id)
|
152
149
|
|
153
|
-
# FIXME: this block is needed since a lot of specs rely on
|
150
|
+
# FIXME: this block is needed since a lot of specs rely on
|
151
|
+
# delayed job being runned in the same thread as promise
|
154
152
|
# Can be deleted later and replaces with simple timeout below
|
155
153
|
if !job || job.locked_at
|
156
154
|
# job has been locked, so it looks like it started already
|
@@ -162,7 +160,7 @@ class Marty::Promise < Marty::Base
|
|
162
160
|
# log "OFF0 #{Process.pid} #{last}"
|
163
161
|
begin
|
164
162
|
work_off_job(job)
|
165
|
-
rescue => exc
|
163
|
+
rescue StandardError => exc
|
166
164
|
# log "OFFERR #{exc}"
|
167
165
|
res = Delorean::Engine.grok_runtime_exception(exc)
|
168
166
|
last.set_result(res)
|
@@ -179,7 +177,7 @@ class Marty::Promise < Marty::Base
|
|
179
177
|
# timeout error.
|
180
178
|
if !last.start_dt
|
181
179
|
# log "TO11 #{Process.pid} #{last}"
|
182
|
-
return {
|
180
|
+
return { 'error' => "promise #{last.id} timed out (never started)" }
|
183
181
|
end
|
184
182
|
end
|
185
183
|
|
@@ -192,7 +190,7 @@ class Marty::Promise < Marty::Base
|
|
192
190
|
last = latest
|
193
191
|
|
194
192
|
if !last.end_dt
|
195
|
-
return {
|
193
|
+
return { 'error' => "promise #{last.id} timed out (didn't end)" }
|
196
194
|
end
|
197
195
|
end
|
198
196
|
|
data/app/models/marty/script.rb
CHANGED
@@ -3,24 +3,23 @@ class Marty::Script < Marty::Base
|
|
3
3
|
|
4
4
|
validates_presence_of :name, :body
|
5
5
|
mcfly_validates_uniqueness_of :name
|
6
|
-
validates_format_of :name,
|
7
|
-
|
8
|
-
|
9
|
-
}
|
6
|
+
validates_format_of :name,
|
7
|
+
with: /\A[A-Z][a-zA-Z0-9]*\z/,
|
8
|
+
message: I18n.t('script.save_error')
|
10
9
|
|
11
|
-
belongs_to :user, class_name:
|
10
|
+
belongs_to :user, class_name: 'Marty::User'
|
12
11
|
|
13
12
|
gen_mcfly_lookup :lookup, [:name], cache: true
|
14
13
|
|
15
14
|
# find script by name/tag (not cached)
|
16
|
-
def self.find_script(sname, tag=nil)
|
15
|
+
def self.find_script(sname, tag = nil)
|
17
16
|
tag = Marty::Tag.map_to_tag(tag)
|
18
17
|
Marty::Script.mcfly_pt(tag.created_dt).find_by(name: sname)
|
19
18
|
end
|
20
19
|
|
21
20
|
def find_tag
|
22
21
|
# find the first tag created after this script.
|
23
|
-
Marty::Tag.where(
|
22
|
+
Marty::Tag.where('created_dt >= ?', created_dt).order(:created_dt).first
|
24
23
|
end
|
25
24
|
|
26
25
|
def self.create_script(name, body)
|
@@ -31,7 +30,7 @@ class Marty::Script < Marty::Base
|
|
31
30
|
script
|
32
31
|
end
|
33
32
|
|
34
|
-
def self.load_a_script(sname, body, dt=nil)
|
33
|
+
def self.load_a_script(sname, body, dt = nil)
|
35
34
|
s = Marty::Script.find_by(obsoleted_dt: 'infinity', name: sname)
|
36
35
|
|
37
36
|
if !s
|
@@ -47,28 +46,27 @@ class Marty::Script < Marty::Base
|
|
47
46
|
end
|
48
47
|
end
|
49
48
|
|
50
|
-
def self.load_script_bodies(bodies, dt=nil)
|
51
|
-
bodies.each
|
52
|
-
|sname, body|
|
49
|
+
def self.load_script_bodies(bodies, dt = nil)
|
50
|
+
bodies.each do |sname, body|
|
53
51
|
load_a_script(sname, body, dt)
|
54
|
-
|
52
|
+
end
|
55
53
|
|
56
54
|
# Create a new tag if scripts were modified after the last tag
|
57
55
|
tag = Marty::Tag.get_latest1
|
58
|
-
latest = Marty::Script.order(
|
56
|
+
latest = Marty::Script.order('created_dt DESC').first
|
59
57
|
|
60
58
|
tag_time = (dt || [latest.try(:created_dt), Time.now].compact.max) +
|
61
59
|
1.second
|
62
60
|
|
63
61
|
# If no tag_time is provided, the tag created_dt will be the same
|
64
62
|
# as the scripts.
|
65
|
-
tag = Marty::Tag.do_create(tag_time,
|
63
|
+
tag = Marty::Tag.do_create(tag_time, 'tagged from load scripts') if
|
66
64
|
!(tag && latest) || tag.created_dt <= latest.created_dt
|
67
65
|
|
68
66
|
tag
|
69
67
|
end
|
70
68
|
|
71
|
-
def self.load_scripts(path=nil, dt=nil)
|
69
|
+
def self.load_scripts(path = nil, dt = nil)
|
72
70
|
files = get_script_filenames(path)
|
73
71
|
|
74
72
|
bodies = read_script_files(files)
|
@@ -77,10 +75,10 @@ class Marty::Script < Marty::Base
|
|
77
75
|
end
|
78
76
|
|
79
77
|
def self.read_script_files(files)
|
80
|
-
files.collect
|
78
|
+
files.collect do |fpath|
|
81
79
|
fname = File.basename(fpath)[0..-4].camelize
|
82
80
|
[fname, File.read(fpath)]
|
83
|
-
|
81
|
+
end
|
84
82
|
end
|
85
83
|
|
86
84
|
def self.get_script_filenames(paths = nil)
|
@@ -90,7 +88,7 @@ class Marty::Script < Marty::Base
|
|
90
88
|
paths.each do |path|
|
91
89
|
Dir.glob("#{path}/*.dl").each do |filename|
|
92
90
|
basename = File.basename(filename)
|
93
|
-
filenames[basename] = filename unless filenames.
|
91
|
+
filenames[basename] = filename unless filenames.key?(basename)
|
94
92
|
end
|
95
93
|
end
|
96
94
|
|
@@ -114,15 +112,15 @@ class Marty::Script < Marty::Base
|
|
114
112
|
|
115
113
|
def self.delete_scripts
|
116
114
|
ActiveRecord::Base.connection.
|
117
|
-
execute(
|
115
|
+
execute('ALTER TABLE marty_scripts DISABLE TRIGGER USER;')
|
118
116
|
Marty::Script.delete_all
|
119
117
|
ActiveRecord::Base.connection.
|
120
|
-
execute(
|
118
|
+
execute('ALTER TABLE marty_scripts ENABLE TRIGGER USER;')
|
121
119
|
end
|
122
120
|
|
123
|
-
delorean_fn :eval_to_hash, sig: 5 do
|
124
|
-
|
125
|
-
|
121
|
+
delorean_fn :eval_to_hash, sig: 5 do |dt, script, node, attrs, params|
|
122
|
+
tag = Marty::Tag.find_match(dt) if dt.present?
|
123
|
+
raise("no tag for #{dt}") if tag.nil? && dt.present?
|
126
124
|
|
127
125
|
engine = Marty::ScriptSet.new(tag).get_engine(script)
|
128
126
|
# IMPORTANT: engine evals (e.g. eval_to_hash) modify the
|
@@ -134,11 +132,16 @@ class Marty::Script < Marty::Base
|
|
134
132
|
# current tag can caused problems.
|
135
133
|
end
|
136
134
|
|
137
|
-
|
138
|
-
|
139
|
-
|
135
|
+
# evaluate script's node attribute (attr) with the given params. dt
|
136
|
+
# is used to determine which script tag to use. The latest tag is
|
137
|
+
# used if dt is nil.
|
138
|
+
delorean_fn :evaluate, sig: 5 do |dt, script, node, attr, params|
|
139
|
+
tag = Marty::Tag.find_match(dt) if dt.present?
|
140
|
+
raise("no tag for #{dt}") if tag.nil? && dt.present?
|
140
141
|
|
142
|
+
# nil tag, uses the latest one
|
141
143
|
engine = Marty::ScriptSet.new(tag).get_engine(script)
|
144
|
+
|
142
145
|
# IMPORTANT: engine evals (e.g. eval_to_hash) modify the
|
143
146
|
# params, but it is possible that we may be passing in
|
144
147
|
# a frozen hash. To avoid performance impacts, we should first check if
|
@@ -146,8 +149,7 @@ class Marty::Script < Marty::Base
|
|
146
149
|
engine.evaluate(node, attr, params.frozen? ? params.dup : params.clone)
|
147
150
|
end
|
148
151
|
|
149
|
-
delorean_fn :pretty_print, sig: 1 do
|
150
|
-
|id|
|
152
|
+
delorean_fn :pretty_print, sig: 1 do |id|
|
151
153
|
script = find_by_id id
|
152
154
|
|
153
155
|
next "unknown script #{id}" unless script
|