marty 9.3.0 → 9.3.2

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/.gemignore +2 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +34 -1
  5. data/Gemfile +1 -0
  6. data/Rakefile +10 -0
  7. data/app/assets/javascripts/marty/cable.js +21 -9
  8. data/app/channels/application_cable/connection.rb +1 -1
  9. data/app/channels/marty/notification_channel.rb +4 -1
  10. data/app/components/marty/api_config_view.rb +1 -1
  11. data/app/components/marty/api_log_view.rb +1 -1
  12. data/app/components/marty/auth_app.rb +8 -1
  13. data/app/components/marty/auth_app/client/auth_app.js +6 -0
  14. data/app/components/marty/data_grid_view.rb +6 -6
  15. data/app/components/marty/extras/misc.rb +1 -1
  16. data/app/components/marty/log_view.rb +1 -1
  17. data/app/components/marty/posting_grid.rb +1 -1
  18. data/app/components/marty/script_form.rb +3 -3
  19. data/app/components/marty/user_view.rb +1 -1
  20. data/app/components/marty/users/user_view.rb +1 -1
  21. data/app/controllers/marty/application_controller.rb +7 -7
  22. data/app/controllers/marty/delayed_job_controller.rb +7 -4
  23. data/app/controllers/marty/diagnostic/controller.rb +1 -1
  24. data/app/controllers/marty/job_controller.rb +2 -2
  25. data/app/models/marty/api_auth.rb +3 -3
  26. data/app/models/marty/api_config.rb +1 -1
  27. data/app/models/marty/base_rule.rb +1 -1
  28. data/app/models/marty/config.rb +5 -5
  29. data/app/models/marty/data_grid.rb +3 -3
  30. data/app/models/marty/delorean_rule.rb +2 -2
  31. data/app/models/marty/grid_index_boolean.rb +2 -2
  32. data/app/models/marty/grid_index_int4range.rb +1 -1
  33. data/app/models/marty/grid_index_integer.rb +1 -1
  34. data/app/models/marty/grid_index_numrange.rb +1 -1
  35. data/app/models/marty/grid_index_string.rb +1 -1
  36. data/app/models/marty/import_type.rb +2 -2
  37. data/app/models/marty/notifications/notification.rb +2 -1
  38. data/app/models/marty/posting.rb +6 -6
  39. data/app/models/marty/posting_type.rb +2 -2
  40. data/app/models/marty/promise.rb +4 -3
  41. data/app/models/marty/script.rb +7 -7
  42. data/app/models/marty/tag.rb +5 -5
  43. data/app/models/marty/token.rb +1 -1
  44. data/app/models/marty/user.rb +11 -10
  45. data/app/models/marty/user_role.rb +2 -2
  46. data/app/models/marty/vw_promise.rb +2 -1
  47. data/app/services/marty/background_job/update_schedule.rb +1 -1
  48. data/app/services/marty/data_grid/constraint.rb +4 -3
  49. data/app/services/marty/jobs/schedule.rb +2 -2
  50. data/lib/marty/data_change.rb +1 -1
  51. data/lib/marty/data_conversion.rb +1 -1
  52. data/lib/marty/migrations.rb +2 -2
  53. data/lib/marty/rpc_call.rb +2 -1
  54. data/lib/marty/rule_script_set.rb +1 -3
  55. data/lib/marty/util.rb +2 -2
  56. data/lib/marty/version.rb +1 -1
  57. data/lib/tasks/scripts_tasks.rake +2 -2
  58. data/marty.gemspec +5 -1
  59. data/spec/controllers/job_controller_spec.rb +7 -7
  60. data/spec/controllers/rpc_controller_spec.rb +8 -8
  61. data/spec/controllers/rpc_import_spec.rb +3 -3
  62. data/spec/features/data_blame_report_spec.rb +1 -1
  63. data/spec/features/data_grid_spec.rb +101 -3
  64. data/spec/features/enum_values_report_spec.rb +1 -1
  65. data/spec/features/extjs_spec.rb +1 -1
  66. data/spec/features/jobs_dashboard_spec.rb +2 -2
  67. data/spec/features/log_view_spec.rb +1 -1
  68. data/spec/features/reporting_spec.rb +3 -3
  69. data/spec/features/scripting_spec.rb +3 -3
  70. data/spec/features/scripting_test_spec.rb +3 -3
  71. data/spec/features/user_list_report_spec.rb +1 -1
  72. data/spec/fixtures/misc/data_grid_6.txt +9 -0
  73. data/spec/fixtures/misc/data_grid_7.txt +7 -0
  74. data/spec/fixtures/misc/data_grid_8.txt +10 -0
  75. data/spec/fixtures/misc/data_grid_9.txt +10 -0
  76. data/spec/lib/data_blame_spec.rb +1 -1
  77. data/spec/lib/data_importer_spec.rb +4 -4
  78. data/spec/lib/delorean_query_spec.rb +1 -1
  79. data/spec/lib/logger_spec.rb +1 -1
  80. data/spec/lib/mcfly_model_spec.rb +2 -2
  81. data/spec/lib/table_report_spec.rb +1 -1
  82. data/spec/models/api_auth_spec.rb +2 -2
  83. data/spec/models/data_grid_spec.rb +3 -3
  84. data/spec/models/posting_spec.rb +12 -12
  85. data/spec/models/promise_spec.rb +1 -1
  86. data/spec/models/rule_spec.rb +1 -1
  87. data/spec/models/script_spec.rb +1 -1
  88. data/spec/other/diagnostic/delayed_job_version_spec.rb +1 -1
  89. data/spec/spec_helper.rb +3 -0
  90. data/spec/support/netzke.rb +2 -2
  91. data/spec/support/setup.rb +1 -1
  92. data/spec/support/simplecov_helper.rb +94 -0
  93. data/spec/support/users.rb +2 -2
  94. metadata +8 -3
  95. data/.gitlab-ci.yml +0 -117
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7fd7c45cf6825476a774ee84fe6b3f9bff603e022747ecc5079cda8a037a869b
4
- data.tar.gz: 48fdb90df6cb49e720fc4446975f8b55205eaa560a23af60a49b1d02769127cf
3
+ metadata.gz: a8acd94fdad2d0427eaea410e20f8df3acccec4ddb55d6932c8ac558a4f320cb
4
+ data.tar.gz: e6d14dc726a688d44bc575c512678522890c90b2cb848167f7dc2a2a661de034
5
5
  SHA512:
6
- metadata.gz: 81813549a4614fa1191c466a5506ec2d36796e21e6d45dec4eb8bf054e0614a01c1c2bf1a0539e2afdc980c81e22a32bcdce2749d27634bf65c6f491e058978d
7
- data.tar.gz: be097e58e928782e89761d4980218e417c2df6c5fa9a28233c4449feed3df1a5492a244ce7211cb604471e7188e08d5b8e923921948b7614dcc6a873cab60af6
6
+ metadata.gz: 03a0f36614dfde9a168825250d41a94c8c7a5912b83d2ee347fa0ed9fdaf10da1d08b28b0ff8497dccf4ef0999d76eadbfb8bd320fde1e4a6636a307297a8df7
7
+ data.tar.gz: '039559d4fd376ce6741a9fa8ea71a7276daba6df5050dcf3950f24e78cc2bb8c5944cb425e45dbc6ce4a7b6f32b5e69a0c27bd74f5dcf664271476f1da3b8771'
@@ -0,0 +1,2 @@
1
+ .gitlab-ci.yml
2
+ .gitlab
data/.gitignore CHANGED
@@ -42,3 +42,6 @@ Gemfile.lock
42
42
 
43
43
  # JS stuff
44
44
  /node_modules
45
+
46
+ # Ignore code coverage
47
+ coverage/
@@ -1,5 +1,7 @@
1
1
  inherit_from: .rubocop_todo.yml
2
- require: rubocop-performance
2
+ require:
3
+ - rubocop-performance
4
+ - rubocop-rails
3
5
 
4
6
  AllCops:
5
7
  TargetRubyVersion: 2.4.2
@@ -46,3 +48,34 @@ Style/SymbolArray:
46
48
  Security/MarshalLoad:
47
49
  Exclude:
48
50
  - 'lib/marty/cache_adapters/redis.rb'
51
+
52
+ Rails/TimeZone:
53
+ EnforcedStyle: strict
54
+
55
+ Rails/OutputSafety:
56
+ Exclude:
57
+ - 'app/components/marty/main_auth_app.rb'
58
+
59
+ Rails/HelperInstanceVariable:
60
+ Exclude:
61
+ - 'app/helpers/marty/script_set.rb' # It's a class, not a helper
62
+
63
+ Rails/SkipsModelValidations:
64
+ Exclude:
65
+ - 'app/models/marty/promise.rb'
66
+
67
+ Rails/DynamicFindBy:
68
+ Exclude:
69
+ - 'app/models/marty/data_grid.rb' # Enum
70
+ - 'app/models/marty/enum.rb' # Enum
71
+ - 'spec/features/**/*'
72
+ - 'spec/support/**/*'
73
+
74
+ Rails/ApplicationRecord:
75
+ Enabled: false
76
+
77
+ Rails/ApplicationJob:
78
+ Enabled: false
79
+
80
+ Rails/ApplicationController:
81
+ Enabled: false
data/Gemfile CHANGED
@@ -37,6 +37,7 @@ group :development, :test do
37
37
  gem 'rubocop-performance', require: false
38
38
  gem 'rubocop-rails', require: false
39
39
  gem 'selenium-webdriver'
40
+ gem 'simplecov', require: false
40
41
  gem 'timecop'
41
42
  gem 'webdrivers'
42
43
  end
data/Rakefile CHANGED
@@ -26,3 +26,13 @@ load 'rails/tasks/engine.rake'
26
26
  Bundler::GemHelper.install_tasks
27
27
 
28
28
  task default: 'app:spec'
29
+
30
+ namespace :marty do
31
+ desc 'Merge the results of various SimpleCov coverage reports'
32
+ task merge_coverage_reports: :environment do
33
+ require_relative 'spec/support/simplecov_helper'
34
+ SimpleCovHelper.configure_profile
35
+ puts 'Merging code coverage reports...'
36
+ SimpleCovHelper.merge_all_results!
37
+ end
38
+ end
@@ -4,13 +4,25 @@
4
4
  (function() {
5
5
  this.RailsApp || (this.RailsApp = {});
6
6
 
7
- if (window.location.port === "") {
8
- RailsApp.cable = ActionCable.createConsumer(
9
- `ws://${window.location.hostname}/cable`
10
- );
11
- } else {
12
- RailsApp.cable = ActionCable.createConsumer(
13
- `ws://${window.location.hostname}:${window.location.port}/cable`
14
- );
15
- }
7
+ this.RailsApp.startActionCable = () => {
8
+ // Already started
9
+ if (RailsApp.cable) {
10
+ return false;
11
+ }
12
+
13
+ const protocol =
14
+ window.location.protocol.slice(0, 5) === "https" ? "wss" : "ws";
15
+
16
+ if (window.location.port === "") {
17
+ RailsApp.cable = ActionCable.createConsumer(
18
+ `${protocol}://${window.location.hostname}/cable`
19
+ );
20
+ } else {
21
+ RailsApp.cable = ActionCable.createConsumer(
22
+ `${protocol}://${window.location.hostname}:${window.location.port}/cable`
23
+ );
24
+ }
25
+
26
+ return true;
27
+ };
16
28
  }.call(this));
@@ -9,7 +9,7 @@ module ApplicationCable
9
9
  private
10
10
 
11
11
  def find_verified_user
12
- return unless cookies.signed[:user_id].present?
12
+ return if cookies.signed[:user_id].blank?
13
13
 
14
14
  ::Marty::User.find_by(id: cookies.signed[:user_id])
15
15
  end
@@ -1,7 +1,10 @@
1
1
  module Marty
2
2
  class NotificationChannel < ::ApplicationCable::Channel
3
3
  def subscribed
4
- reject && return unless current_user.present?
4
+ reject && return unless
5
+ Rails.application.config.marty.enable_action_cable
6
+
7
+ reject && return if current_user.blank?
5
8
  stream_from "marty_notifications_#{current_user.id}"
6
9
  end
7
10
  end
@@ -67,7 +67,7 @@ class Marty::ApiConfigView < Marty::Grid
67
67
  [:node, :attr].each do |a|
68
68
  attribute a do |c|
69
69
  c.width = 150
70
- c.setter = lambda { |r, v| r.send("#{a}=", v.blank? ? nil : v) }
70
+ c.setter = lambda { |r, v| r.send("#{a}=", v.presence) }
71
71
  end
72
72
  end
73
73
 
@@ -103,7 +103,7 @@ class Marty::ApiLogView < Marty::Grid
103
103
  c.field_config = {
104
104
  xtype: :displayfield,
105
105
  }
106
- c.getter = lambda { |r| Time.at(r.timestamp) }
106
+ c.getter = lambda { |r| Time.zone.at(r.timestamp) }
107
107
  c.sorting_scope = lambda { |r, dir| r.order('timestamp ' + dir.to_s) }
108
108
 
109
109
  # FIXME?: The UI AR/PG DateTime workaround requires the timestamp to be cast
@@ -5,6 +5,13 @@
5
5
  require 'marty/notifications/window'
6
6
 
7
7
  class Marty::AuthApp < Marty::SimpleApp
8
+ def configure(c)
9
+ super
10
+
11
+ enable_action_cable = Rails.application.config.marty.enable_action_cable
12
+ client_config[:marty_enable_action_cable] = enable_action_cable
13
+ end
14
+
8
15
  client_class do |c|
9
16
  c.include :auth_app
10
17
  end
@@ -44,7 +51,7 @@ class Marty::AuthApp < Marty::SimpleApp
44
51
  def unread_notifications_count
45
52
  user = Mcfly.whodunnit
46
53
 
47
- return 0 unless user.present?
54
+ return 0 if user.blank?
48
55
 
49
56
  user.unread_web_notifications_count
50
57
  end
@@ -118,7 +118,13 @@
118
118
  },
119
119
 
120
120
  netzkeInitComponentCallback() {
121
+ if (!this.config.clientConfig.marty_enable_action_cable) {
122
+ return;
123
+ }
124
+
121
125
  try {
126
+ RailsApp.startActionCable();
127
+
122
128
  const subscription = RailsApp.cable.subscriptions.subscriptions.find(
123
129
  (sub) => sub.identifier === '{"channel":"Marty::NotificationChannel"}'
124
130
  );
@@ -116,9 +116,9 @@ module Marty; class DataGridView < McflyGridPanel
116
116
  Marty::RoleType.from_nice_names(plist)
117
117
  end
118
118
  dg.permissions = {
119
- view: view.present? ? view : [],
120
- edit_data: edit_data.present? ? edit_data : [],
121
- edit_all: edit_all.present? ? edit_all : [],
119
+ view: view.presence || [],
120
+ edit_data: edit_data.presence || [],
121
+ edit_all: edit_all.presence || [],
122
122
  }
123
123
  dg.save!
124
124
  end
@@ -142,7 +142,7 @@ module Marty; class DataGridView < McflyGridPanel
142
142
  endpoint :edit_window__edit_form__submit do |params|
143
143
  data = ActiveSupport::JSON.decode(params[:data])
144
144
 
145
- dg = DataGrid.find_by_id(data['id'])
145
+ dg = DataGrid.find_by(id: data['id'])
146
146
 
147
147
  begin
148
148
  dg.update_from_import(data['name'], data['export'])
@@ -169,7 +169,7 @@ module Marty; class DataGridView < McflyGridPanel
169
169
  endpoint :show_grid do |params|
170
170
  record_id = params[:record_id]
171
171
 
172
- dg = DataGrid.find_by_id(record_id)
172
+ dg = DataGrid.find_by(id: record_id)
173
173
 
174
174
  return client.netzke_notify('No data grid.') unless dg
175
175
 
@@ -193,7 +193,7 @@ module Marty; class DataGridView < McflyGridPanel
193
193
  endpoint :edit_grid do |params|
194
194
  record_id = params[:record_id]
195
195
 
196
- dg = DataGrid.find_by_id(record_id)
196
+ dg = DataGrid.find_by(id: record_id)
197
197
 
198
198
  return client.netzke_notify('No data grid.') unless dg
199
199
 
@@ -13,7 +13,7 @@ module Marty::Extras::Misc
13
13
  renderer: "function(v){return ('0' + v).slice (-2);}",
14
14
  # FIXME: a little bogus since this is computed statically. lambda
15
15
  # didn't work.
16
- default_value: Date.today.month
16
+ default_value: Time.zone.today.month
17
17
  }
18
18
  def self.numberfield_cfg(decimal_places)
19
19
  {
@@ -63,7 +63,7 @@ class Marty::LogView < Marty::Grid
63
63
  c.field_config = {
64
64
  xtype: :displayfield,
65
65
  }
66
- c.getter = lambda { |r| Time.at(r.timestamp) }
66
+ c.getter = lambda { |r| Time.zone.at(r.timestamp) }
67
67
  c.sorting_scope = lambda { |r, dir| r.order('timestamp ' + dir.to_s) }
68
68
 
69
69
  # FIXME?: The UI AR/PG DateTime workaround requires the timestamp to be cast
@@ -42,7 +42,7 @@ class Marty::PostingGrid < Marty::Grid
42
42
  # Prepare an HTML popup with session details such that the
43
43
  # contents can be easily pasted into a spreadsheet.
44
44
 
45
- pt = Marty::Posting.find_by_id(record_id)
45
+ pt = Marty::Posting.find_by(id: record_id)
46
46
 
47
47
  dt = pt.created_dt.to_s == 'Infinity' ? '---' :
48
48
  pt.created_dt.strftime('%Y-%m-%d %I:%M %p')
@@ -61,7 +61,7 @@ class Marty::ScriptForm < Marty::Form
61
61
  data[k] = nil if v.blank? || v == 'null'
62
62
  end
63
63
 
64
- @record = script = Marty::Script.find_by_id(data['id'])
64
+ @record = script = Marty::Script.find_by(id: data['id'])
65
65
 
66
66
  unless script
67
67
  client.netzke_notify 'no record'
@@ -81,7 +81,7 @@ class Marty::ScriptForm < Marty::Form
81
81
  end
82
82
 
83
83
  begin
84
- dev = Marty::Tag.find_by_name('DEV')
84
+ dev = Marty::Tag.find_by(name: 'DEV')
85
85
  Marty::ScriptSet.new(dev).parse_check(script.name, data['body'])
86
86
  rescue Delorean::ParseError => e
87
87
  client.netzke_notify e.message
@@ -106,7 +106,7 @@ class Marty::ScriptForm < Marty::Form
106
106
  return client.netzke_notify('Permission Denied') unless
107
107
  self.class.has_any_perm?
108
108
 
109
- script = Marty::Script.find_by_id(script_id)
109
+ script = Marty::Script.find_by(id: script_id)
110
110
 
111
111
  return client.netzke_notify('bad script') unless script
112
112
 
@@ -31,7 +31,7 @@ module Marty; class UserView < Marty::Grid
31
31
  end
32
32
 
33
33
  def self.set_roles(roles, user)
34
- roles = [] unless roles.present?
34
+ roles = [] if roles.blank?
35
35
 
36
36
  roles = ::Marty::RoleType.from_nice_names(roles)
37
37
 
@@ -34,7 +34,7 @@ module Marty
34
34
  end
35
35
 
36
36
  def self.set_roles(roles, user)
37
- roles = [] unless roles.present?
37
+ roles = [] if roles.blank?
38
38
 
39
39
  roles = ::Marty::RoleType.from_nice_names(roles)
40
40
 
@@ -24,7 +24,7 @@ class Marty::ApplicationController < ActionController::Base
24
24
  reset_session
25
25
  reset_signed_cookies
26
26
  else
27
- session[:atime] = Time.now.utc.to_i
27
+ session[:atime] = Time.zone.now.utc.to_i
28
28
  end
29
29
  end
30
30
  end
@@ -35,13 +35,13 @@ class Marty::ApplicationController < ActionController::Base
35
35
 
36
36
  if session_lifetime
37
37
  return true unless session[:ctime] &&
38
- (Time.now.utc.to_i -
38
+ (Time.zone.now.utc.to_i -
39
39
  session[:ctime].to_i <= session_lifetime.to_i * 60)
40
40
  end
41
41
 
42
42
  if session_timeout
43
43
  return true unless session[:atime] &&
44
- (Time.now.utc.to_i - session[:atime].to_i <= session_timeout.to_i * 60)
44
+ (Time.zone.now.utc.to_i - session[:atime].to_i <= session_timeout.to_i * 60)
45
45
  end
46
46
 
47
47
  false
@@ -49,8 +49,8 @@ class Marty::ApplicationController < ActionController::Base
49
49
 
50
50
  def start_user_session(user)
51
51
  session[:user_id] = user.id
52
- session[:ctime] = Time.now.utc.to_i
53
- session[:atime] = Time.now.utc.to_i
52
+ session[:ctime] = Time.zone.now.utc.to_i
53
+ session[:atime] = Time.zone.now.utc.to_i
54
54
 
55
55
  set_signed_cookies
56
56
  end
@@ -119,12 +119,12 @@ class Marty::ApplicationController < ActionController::Base
119
119
 
120
120
  def failed_authentication(login)
121
121
  logger.info("Failed authentication for '#{login}' " +
122
- "from #{request.remote_ip} at #{Time.now.utc}")
122
+ "from #{request.remote_ip} at #{Time.zone.now.utc}")
123
123
  end
124
124
 
125
125
  def successful_authentication(user)
126
126
  logger.info("Successful authentication for '#{user.login}' " +
127
- "from #{request.remote_ip} at #{Time.now.utc}")
127
+ "from #{request.remote_ip} at #{Time.zone.now.utc}")
128
128
  set_user(user)
129
129
  end
130
130
 
@@ -1,4 +1,6 @@
1
- class Marty::DelayedJobController < ActionController::Base
1
+ class Marty::DelayedJobController < ApplicationController
2
+ # FIXME: We probably don't need this endpoint anymore.
3
+ # It's not used by lambda
2
4
  def trigger
3
5
  work_off_job if delayed_job.present?
4
6
  render json: { status: :ok }, status: :ok
@@ -7,7 +9,7 @@ class Marty::DelayedJobController < ActionController::Base
7
9
  private
8
10
 
9
11
  def delayed_job
10
- return unless params['id'].present?
12
+ return if params['id'].blank?
11
13
 
12
14
  @delayed_job ||= ::Delayed::Job.find_by(id: params['id'])
13
15
  end
@@ -15,8 +17,9 @@ class Marty::DelayedJobController < ActionController::Base
15
17
  def work_off_job
16
18
  return if delayed_job.locked_at.present?
17
19
 
18
- ::Delayed::Job.where(id: delayed_job.id).
19
- update_all(locked_at: ::Delayed::Job.db_time_now, locked_by: 'Lambda')
20
+ ::Delayed::Job.find_by(id: delayed_job.id)&.update!(
21
+ locked_at: ::Delayed::Job.db_time_now, locked_by: 'Lambda'
22
+ )
20
23
 
21
24
  w = ::Delayed::Worker.new
22
25
  w.run(delayed_job)
@@ -8,7 +8,7 @@ module Marty::Diagnostic; class Controller < ActionController::Base
8
8
  def op
9
9
  @result = Reporter.run(request)
10
10
  rescue NameError
11
- render file: 'public/400', formats: [:html], status: 400, layout: false
11
+ render file: 'public/400', formats: [:html], status: :bad_request, layout: false
12
12
  else
13
13
  respond_to do |format|
14
14
  format.html { @result = display_parameters }
@@ -1,8 +1,8 @@
1
- class Marty::JobController < ActionController::Base
1
+ class Marty::JobController < ApplicationController
2
2
  def download
3
3
  job_id = params['job_id']
4
4
 
5
- promise = Marty::Promise.find_by_id(job_id)
5
+ promise = Marty::Promise.find_by(id: job_id)
6
6
 
7
7
  if promise
8
8
  format = promise.cformat
@@ -3,7 +3,7 @@ class Marty::ApiAuth < Marty::Base
3
3
 
4
4
  KEY_SIZE = 19
5
5
 
6
- validates_presence_of :app_name, :api_key, :script_name
6
+ validates :app_name, :api_key, :script_name, presence: true
7
7
 
8
8
  class ApiAuthValidator < ActiveModel::Validator
9
9
  def validate(api)
@@ -18,8 +18,8 @@ class Marty::ApiAuth < Marty::Base
18
18
  validates_with ApiAuthValidator
19
19
 
20
20
  mcfly_validates_uniqueness_of :api_key, scope: [:script_name]
21
- validates_uniqueness_of :app_name, scope: [:script_name,
22
- :obsoleted_dt]
21
+ validates :app_name, uniqueness: { scope: [:script_name,
22
+ :obsoleted_dt] }
23
23
 
24
24
  before_validation do
25
25
  self.api_key = Marty::ApiAuth.generate_key if
@@ -1,5 +1,5 @@
1
1
  class Marty::ApiConfig < Marty::Base
2
- validates_presence_of :script
2
+ validates :script, presence: true
3
3
 
4
4
  def self.lookup(script, node, attr)
5
5
  res = where('node IS NULL OR node = ?', node).