command_proposal 1.0.0 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/assets/javascripts/command_proposal/_codemirror.js +1 -1
  4. data/app/assets/javascripts/command_proposal/_helpers.js +62 -1
  5. data/app/assets/javascripts/command_proposal/console.js +24 -29
  6. data/app/assets/javascripts/command_proposal/feed.js +27 -30
  7. data/app/assets/javascripts/command_proposal/terminal.js +1 -1
  8. data/app/assets/stylesheets/command_proposal/tables.scss +2 -0
  9. data/app/assets/stylesheets/command_proposal/terminal.scss +2 -2
  10. data/app/controllers/command_proposal/runner_controller.rb +7 -2
  11. data/app/helpers/command_proposal/application_helper.rb +26 -20
  12. data/app/helpers/command_proposal/icons_helper.rb +1 -0
  13. data/app/helpers/command_proposal/permissions_helper.rb +12 -1
  14. data/app/models/command_proposal/comment.rb +1 -1
  15. data/app/models/command_proposal/iteration.rb +8 -3
  16. data/app/models/command_proposal/service/proposal_presenter.rb +18 -2
  17. data/app/models/command_proposal/task.rb +3 -3
  18. data/app/views/command_proposal/tasks/_console_lines.html.erb +8 -0
  19. data/app/views/command_proposal/tasks/_console_show.html.erb +3 -3
  20. data/app/views/command_proposal/tasks/_function_show.html.erb +43 -30
  21. data/app/views/command_proposal/tasks/_lines.html.erb +4 -8
  22. data/app/views/command_proposal/tasks/_module_show.html.erb +3 -6
  23. data/app/views/command_proposal/tasks/_past_iterations_list.html.erb +8 -1
  24. data/app/views/command_proposal/tasks/_task_detail_table.html.erb +3 -3
  25. data/app/views/command_proposal/tasks/_task_show.html.erb +31 -29
  26. data/app/views/command_proposal/tasks/form.html.erb +7 -7
  27. data/app/views/command_proposal/tasks/index.html.erb +3 -2
  28. data/lib/command_proposal/configuration.rb +1 -1
  29. data/lib/command_proposal/services/command_interpreter.rb +3 -1
  30. data/lib/command_proposal/services/runner.rb +25 -9
  31. data/lib/command_proposal/services/shut_down.rb +25 -0
  32. data/lib/command_proposal/version.rb +6 -1
  33. data/lib/command_proposal.rb +1 -0
  34. data/lib/generators/command_proposal/install/templates/initializer.rb +12 -5
  35. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8248eae2534ebab49931beca4a77666ef958bc854eeb39a790808ae6112443b1
4
- data.tar.gz: 8915714938d8caf78dc757cf32f47f71a447078204290711403c9ee1eecce60a
3
+ metadata.gz: 4d0b2ef7f0cf5f9044adaab75cb1b28b762086ff77ce659d461b976dd0533e3d
4
+ data.tar.gz: c4c50205f7e2260a9d7659e7c6938808366c07aa00f0e85511fd1269de768e95
5
5
  SHA512:
6
- metadata.gz: 2147b8ebcda3d8b747db413981592c915dfdae50b27ba07e1a39b6b9130b416a11bd9b5f1e728e5a2b2edc73c25302c21473d509165be38030160af7b62c70d5
7
- data.tar.gz: 0d07701e418d21123a28463180104b5464ca0f45a7398015a44302e94af34f8f88be6f9592315505aa9fe95cf56c2b25cf46f0125d7c3a18f80a40c80c1b979d
6
+ metadata.gz: 9ee3a6d919ea22545e62488fc0291d476b168b26412433288b6acde185d96bba8e4f9df313d13596faf508f7faabcea1d5d8539e7794a47d897eb117d4fe70e1
7
+ data.tar.gz: faca82d2f612e2a0ec8b78ac09655d794b9e04b108b93b075b2097aff0dd0959dc75c21410fa7a6be4d71fac93f52428d0e497298a66c71b4b7caa6d6930329a
data/README.md CHANGED
@@ -59,7 +59,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
59
59
 
60
60
  ## Contributing
61
61
 
62
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/command_proposal. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/command_proposal/blob/master/CODE_OF_CONDUCT.md).
62
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rockster160/command_proposal. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/rockster160/command_proposal/blob/master/CODE_OF_CONDUCT.md).
63
63
 
64
64
  ## TODO
65
65
 
@@ -74,4 +74,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
74
74
 
75
75
  ## Code of Conduct
76
76
 
77
- Everyone interacting in the CommandProposal project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/command_proposal/blob/master/CODE_OF_CONDUCT.md).
77
+ Everyone interacting in the CommandProposal project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/rockster160/command_proposal/blob/master/CODE_OF_CONDUCT.md).
@@ -5279,7 +5279,7 @@
5279
5279
  }
5280
5280
 
5281
5281
  // Possibly split or suppress the update based on the presence
5282
- // of read-only spans in its range.
5282
+ // of readonly spans in its range.
5283
5283
  var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
5284
5284
  if (split) {
5285
5285
  for (var i = split.length - 1; i >= 0; --i)
@@ -1,4 +1,4 @@
1
- function docReady(fn) {
1
+ function cmdDocReady(fn) {
2
2
  // see if DOM is already available
3
3
  if (document.readyState === "complete" || document.readyState === "interactive") {
4
4
  // call on next available tick
@@ -7,3 +7,64 @@ function docReady(fn) {
7
7
  document.addEventListener("DOMContentLoaded", fn)
8
8
  }
9
9
  }
10
+
11
+ function HttpClient() {
12
+ this.request = function(method, url, opts) {
13
+ var req = new XMLHttpRequest()
14
+ req.onreadystatechange = function() {
15
+ if (req.status != 0 && req.readyState == 4 && opts.done) {
16
+ opts.done(req.responseText, req.status, req)
17
+ }
18
+ }
19
+
20
+ req.open(method, url, true)
21
+ if (opts.headers) {
22
+ Object.keys(opts.headers).forEach(function(key) {
23
+ req.setRequestHeader(key, opts.headers[key])
24
+ })
25
+ }
26
+ req.send(opts.body)
27
+ }
28
+
29
+ this.get = function(url, opts) {
30
+ this.request("GET", url, opts)
31
+ }
32
+
33
+ this.post = function(url, opts) {
34
+ this.request("POST", url, opts)
35
+ }
36
+ }
37
+
38
+ function CommandQueue() {
39
+ this.queue = []
40
+ this.eventCurrentlyRunning = false
41
+ this.runningQueue = null
42
+
43
+ this.run = function() {
44
+ if (!this.eventCurrentlyRunning) {
45
+ if (this.queue.length == 0) {
46
+ clearInterval(this.runningQueue)
47
+ this.runningQueue = null
48
+ return
49
+ }
50
+ var nextEvent = this.queue.shift()
51
+ this.eventCurrentlyRunning = true
52
+ nextEvent(this)
53
+ }
54
+ }
55
+
56
+ this.add = function(queued_function) {
57
+ this.queue.push(queued_function)
58
+ this.process()
59
+ }
60
+
61
+ this.finish = function(ms) {
62
+ this.eventCurrentlyRunning = false
63
+ }
64
+
65
+ this.process = function() {
66
+ if (this.runningQueue) { return }
67
+ var q = this
68
+ this.runningQueue = setInterval(function() { q.run() }, 1)
69
+ }
70
+ }
@@ -1,11 +1,11 @@
1
1
  // Add tab / shift-tab for changing indents
2
- docReady(function() {
2
+ cmdDocReady(function() {
3
3
  var cmdconsole = document.querySelector(".cmd-console")
4
4
 
5
5
  if (cmdconsole) {
6
6
  var console_input = document.querySelector(".cmd-console .cmd-entry")
7
7
  var lines = document.querySelector(".cmd-console .lines")
8
- var queue = Promise.resolve()
8
+ var queue = new CommandQueue
9
9
  var history_cmd_idx = undefined
10
10
  var stored_entry = undefined
11
11
  var commands = getPrevCommands()
@@ -152,43 +152,38 @@ docReady(function() {
152
152
  if (/^[\s\n]*$/.test(line.textContent)) { return }
153
153
 
154
154
  commands.push(line.textContent)
155
- queue = queue.then(async function() {
155
+ queue.add(function(evt) {
156
156
  $.rails.refreshCSRFTokens()
157
157
 
158
158
  var params = { code: line.textContent, task_id: cmdconsole.dataset.task }
159
159
 
160
- var res = await fetch(cmdconsole.dataset.exeUrl, {
161
- method: "POST",
160
+ var client = new HttpClient()
161
+ client.post(cmdconsole.dataset.exeUrl, {
162
162
  headers: {
163
163
  "Content-Type": "application/json",
164
164
  "X-CSRF-Token": $.rails.csrfToken()
165
165
  },
166
- body: JSON.stringify(params)
167
- }).then(function(res) {
168
- if (res.ok) {
169
- return res.json()
170
- } else {
171
- throw new Error("Server error")
172
- }
173
- }).catch(function(err) {
174
- return {
175
- error: err,
166
+ body: JSON.stringify(params),
167
+ done: function(res, status, req) {
168
+ if (status == 200) {
169
+ var json = JSON.parse(res)
170
+ var result = document.createElement("div")
171
+ result.classList.add("result")
172
+
173
+ if (json.error) {
174
+ result.classList.add("cmd-error")
175
+ result.textContent = json.error
176
+ } else {
177
+ result.textContent = json.result
178
+ }
179
+
180
+ line.appendChild(result)
181
+ } else {
182
+ console.log("Error: ", res, req);
183
+ }
184
+ evt.finish()
176
185
  }
177
186
  })
178
-
179
- var json = await res
180
-
181
- var result = document.createElement("div")
182
- result.classList.add("result")
183
-
184
- if (json.error) {
185
- result.classList.add("cmd-error")
186
- result.textContent = json.error
187
- } else {
188
- result.textContent = json.result
189
- }
190
-
191
- line.appendChild(result)
192
187
  })
193
188
  }
194
189
  }
@@ -1,6 +1,6 @@
1
- docReady(function() {
1
+ cmdDocReady(function() {
2
2
  var terminals = document.querySelectorAll("[data-feed]")
3
- var queue = Promise.resolve()
3
+ var queue = new CommandQueue
4
4
  var continue_statuses = ["started", "approved", "cancelling"]
5
5
 
6
6
  terminals.forEach(function(terminal) {
@@ -10,42 +10,39 @@ docReady(function() {
10
10
  })
11
11
 
12
12
  function pingFeed(terminal) {
13
- queue = queue.then(async function() {
13
+ queue.add(function(evt) {
14
14
  $.rails.refreshCSRFTokens()
15
15
 
16
- var res = await fetch(terminal.dataset.feed, {
17
- method: "GET",
16
+ var client = new HttpClient()
17
+ client.get(terminal.dataset.feed, {
18
18
  headers: {
19
19
  "Content-Type": "text/html",
20
20
  "X-CSRF-Token": $.rails.csrfToken()
21
21
  },
22
- }).then(function(res) {
23
- if (res.ok) {
24
- return res.json()
25
- } else {
26
- throw new Error("Server error")
27
- }
28
- }).catch(function(err) {
29
- console.log("err:", err);
30
- })
31
-
32
- var json = await res
22
+ done: function(res, status, req) {
23
+ if (status == 200) {
24
+ var json = JSON.parse(res)
25
+ if (terminal.nextElementSibling && terminal.nextElementSibling.CodeMirror) {
26
+ terminal.nextElementSibling.CodeMirror.doc.setValue(json.result || "")
27
+ } else {
28
+ terminal.innerHTML = json.result_html
29
+ }
30
+ document.querySelector("td[data-iteration-status]").innerText = json.status
31
+ document.querySelector("td[data-iteration-duration]").innerText = json.duration
33
32
 
34
- if (terminal.nextElementSibling && terminal.nextElementSibling.CodeMirror) {
35
- terminal.nextElementSibling.CodeMirror.doc.setValue(json.result || "")
36
- } else {
37
- terminal.innerHTML = json.result_html
38
- }
39
- document.querySelector("td[data-iteration-status]").innerText = json.status
40
- document.querySelector("td[data-iteration-duration]").innerText = json.duration
41
-
42
- if (continue_statuses.includes(json.status)) {
43
- setTimeout(function() { pingFeed(terminal) }, 1000)
44
- } else {
45
- if (document.querySelector(".cancel-btn")) {
46
- document.querySelector(".cancel-btn").remove()
33
+ if (continue_statuses.includes(json.status)) {
34
+ setTimeout(function() { pingFeed(terminal) }, 1000)
35
+ } else {
36
+ if (document.querySelector(".cancel-btn")) {
37
+ document.querySelector(".cancel-btn").remove()
38
+ }
39
+ }
40
+ } else {
41
+ console.log("Error: ", res, req);
42
+ }
43
+ evt.finish()
47
44
  }
48
- }
45
+ })
49
46
  })
50
47
  }
51
48
  })
@@ -1,4 +1,4 @@
1
- docReady(function() {
1
+ cmdDocReady(function() {
2
2
  var terminals = document.querySelectorAll(".cmd-terminal")
3
3
 
4
4
  function setReadOnlyUI(cm) {
@@ -68,6 +68,8 @@
68
68
  th, td {
69
69
  text-align: left;
70
70
  padding: 3px;
71
+
72
+ &.cmd-text-right { text-align: right; }
71
73
  }
72
74
  th {
73
75
  font-size: 12px;
@@ -18,7 +18,7 @@
18
18
  .line {
19
19
  min-height: 18px;
20
20
  counter-increment: line-count;
21
- font-size: 16px;
21
+ font-size: 14px;
22
22
  padding-left: 5px;
23
23
  position: relative;
24
24
 
@@ -26,7 +26,7 @@
26
26
  content: "[" counter(line-count) "]>";
27
27
  position: absolute;
28
28
  left: -40px;
29
- top: 3px;
29
+ top: 2px;
30
30
  width: 40px;
31
31
  font-size: 10px;
32
32
  vertical-align: middle;
@@ -67,13 +67,18 @@ class ::CommandProposal::RunnerController < ::CommandProposal::EngineController
67
67
  duration: humanized_duration(@iteration.duration),
68
68
  }.tap do |response|
69
69
  if @iteration.started?
70
- response[:endpoint] = runner_path(@task, @iteration)
70
+ response[:endpoint] = runner_url(@task, @iteration)
71
71
  end
72
72
  if @task.console?
73
73
  response[:result_html] = ApplicationController.render(
74
- partial: "command_proposal/tasks/lines",
74
+ partial: "command_proposal/tasks/console_lines",
75
75
  locals: { lines: @task.lines }
76
76
  )
77
+ else
78
+ response[:result_html] = ApplicationController.render(
79
+ partial: "command_proposal/tasks/lines",
80
+ locals: { lines: @iteration.result }
81
+ )
77
82
  end
78
83
  end
79
84
  end
@@ -1,8 +1,10 @@
1
1
  module CommandProposal
2
2
  module ApplicationHelper
3
+ include ActionDispatch::Routing::PolymorphicRoutes
4
+ include Rails.application.routes.url_helpers
3
5
  # In order to keep the regular app's routes working in the base template, we have to manually
4
6
  # render the engine routes. Built a helper for this because it's long and nasty otherwise.
5
- def cmd_path(*args)
7
+ def cmd_url(*args)
6
8
  return string_path(*args) if args.first.is_a?(String)
7
9
  model_names = [:tasks, :iterations, :comments, :task, :iteration, :comment]
8
10
  host = nil
@@ -19,39 +21,43 @@ module CommandProposal
19
21
  args << { host: host, port: nil } if host.present?
20
22
 
21
23
  begin
22
- router.url_for(args.compact)
24
+ command_proposal_engine.url_for(args.compact)
23
25
  rescue NoMethodError => e
24
- raise "Error generating route! Please make sure `default_url_options` are set."
26
+ raise "Error generating route! Please make sure `config.action_mailer.default_url_options` are set."
25
27
  end
26
28
  end
27
29
 
30
+ def cmd_path(*args)
31
+ args.tap { |arg_list|
32
+ if arg_list.last.is_a?(Hash)
33
+ arg_list.last.merge!(only_path: true)
34
+ else
35
+ arg_list << { only_path: true }
36
+ end
37
+ }
38
+
39
+ cmd_url(*args)
40
+ end
41
+
28
42
  def string_path(*args)
29
- [router.command_proposal_tasks_url + args.shift, args.to_param.presence].compact.join("?")
43
+ [command_proposal_engine.command_proposal_tasks_url + args.shift, args.to_param.presence].compact.join("?")
30
44
  end
31
45
 
32
46
  # Runner controller doesn't map to a model, so needs special handling
33
47
  def runner_path(task, iteration=nil)
34
48
  if iteration.present?
35
- router.command_proposal_task_runner_url(task, iteration)
49
+ command_proposal_engine.command_proposal_task_runner_path(task, iteration)
36
50
  else
37
- router.command_proposal_task_runner_index_url(task)
38
- end
39
- end
40
-
41
- def router
42
- @@router ||= begin
43
- routes = ::CommandProposal::Engine.routes
44
- routes.default_url_options = rails_default_url_options
45
- routes.url_helpers
51
+ command_proposal_engine.command_proposal_task_runner_index_path(task)
46
52
  end
47
53
  end
48
54
 
49
- def rails_default_url_options
50
- Rails.application.config.action_mailer.default_url_options.tap do |default_opts|
51
- default_opts ||= {}
52
- default_opts[:host] ||= "localhost"
53
- default_opts[:port] ||= "3000"
54
- default_opts[:protocol] ||= "http"
55
+ # Runner controller doesn't map to a model, so needs special handling
56
+ def runner_url(task, iteration=nil)
57
+ if iteration.present?
58
+ command_proposal_engine.command_proposal_task_runner_url(task, iteration)
59
+ else
60
+ command_proposal_engine.command_proposal_task_runner_index_url(task)
55
61
  end
56
62
  end
57
63
  end
@@ -7,6 +7,7 @@ module CommandProposal
7
7
  started: "cmd-icon-grey fa fa-clock-o",
8
8
  cancelling: "cmd-icon-yellow fa fa-hourglass-half",
9
9
  cancelled: "cmd-icon-yellow fa fa-stop-circle",
10
+ terminated: "cmd-icon-red fa fa-stop-circle",
10
11
  failed: "cmd-icon-red fa fa-times-circle",
11
12
  success: "cmd-icon-green fa fa-check-circle",
12
13
  }[status&.to_sym] || "cmd-icon-yellow fa fa-question"
@@ -1,12 +1,14 @@
1
1
  module CommandProposal
2
2
  module PermissionsHelper
3
3
  def can_command?(user=command_user)
4
+ return false unless permitted_to_use?
4
5
  return true unless cmd_config.approval_required?
5
6
 
6
7
  command_user.try("#{cmd_config.role_scope}?")
7
8
  end
8
9
 
9
10
  def can_approve?(iteration)
11
+ return false unless permitted_to_use?
10
12
  return true unless cmd_config.approval_required?
11
13
  return if iteration.nil?
12
14
 
@@ -14,15 +16,24 @@ module CommandProposal
14
16
  end
15
17
 
16
18
  def has_approval?(task)
19
+ return false unless permitted_to_use?
17
20
  return true unless cmd_config.approval_required?
18
21
 
19
22
  task&.approved?
20
23
  end
21
24
 
22
25
  def current_is_author?(iteration)
26
+ return false unless permitted_to_use?
27
+
23
28
  command_user&.id == iteration&.requester&.id
24
29
  end
25
30
 
31
+ def permitted_to_use?
32
+ return true if cmd_config.controller_var.blank?
33
+
34
+ command_user&.send("#{cmd_config.role_scope}?")
35
+ end
36
+
26
37
  def command_user(user=nil)
27
38
  @command_user ||= begin
28
39
  if user.present?
@@ -30,7 +41,7 @@ module CommandProposal
30
41
  elsif cmd_config.controller_var.blank?
31
42
  nil
32
43
  else
33
- try(cmd_config.controller_var)
44
+ send(cmd_config.controller_var)
34
45
  end
35
46
  end
36
47
  end
@@ -3,7 +3,7 @@
3
3
  # belongs_to :author
4
4
  # text :body
5
5
 
6
- require "command_proposal/service/external_belong"
6
+ require_dependency "command_proposal/service/external_belong"
7
7
 
8
8
  class ::CommandProposal::Comment < ApplicationRecord
9
9
  self.table_name = :command_proposal_comments
@@ -13,8 +13,8 @@
13
13
 
14
14
  # ADD: iteration_count?
15
15
 
16
- require "command_proposal/service/external_belong"
17
- require "command_proposal/service/json_wrapper"
16
+ require_dependency "command_proposal/service/external_belong"
17
+ require_dependency "command_proposal/service/json_wrapper"
18
18
 
19
19
  class ::CommandProposal::Iteration < ApplicationRecord
20
20
  self.table_name = :command_proposal_iterations
@@ -34,6 +34,7 @@ class ::CommandProposal::Iteration < ApplicationRecord
34
34
  cancelling: 4, # Running, but told to stop
35
35
  cancelled: 5,
36
36
  success: 6,
37
+ terminated: 7, # Closed via server restart
37
38
  }
38
39
 
39
40
  delegate :name, to: :task
@@ -52,13 +53,17 @@ class ::CommandProposal::Iteration < ApplicationRecord
52
53
  end
53
54
 
54
55
  def complete?
55
- success? || failed? || cancelled?
56
+ success? || failed? || cancelled? || terminated?
56
57
  end
57
58
 
58
59
  def pending?
59
60
  created?
60
61
  end
61
62
 
63
+ def running?
64
+ started? || cancelling?
65
+ end
66
+
62
67
  def duration
63
68
  return unless started_at?
64
69
 
@@ -1,6 +1,7 @@
1
1
  module CommandProposal
2
2
  module Service
3
3
  class ProposalPresenter
4
+ include Rails.application.routes.url_helpers
4
5
  include ::CommandProposal::ApplicationHelper
5
6
  attr_accessor :iteration
6
7
 
@@ -19,8 +20,9 @@ module CommandProposal
19
20
  delegate :stopped_at, to: :iteration
20
21
  delegate :duration, to: :iteration
21
22
 
22
- def url(host: nil)
23
- cmd_path(@iteration.task, host: host)
23
+ def url
24
+ path = ::CommandProposal::Engine.routes.url_helpers.command_proposal_task_path(@iteration.task)
25
+ "#{base_path}#{path}"
24
26
  end
25
27
 
26
28
  def requester
@@ -34,6 +36,20 @@ module CommandProposal
34
36
  def type
35
37
  @iteration.session_type
36
38
  end
39
+
40
+ private
41
+
42
+ def base_path
43
+ url_opts = Rails.application.config.action_mailer.default_url_options || {}
44
+ url_opts.tap do |opts|
45
+ opts[:protocol] ||= "http"
46
+ opts[:host] ||= "localhost"
47
+ opts[:port] ||= 3000
48
+ end
49
+
50
+ port_str = url_opts[:host] == "localhost" ? ":#{url_opts[:port]}" : ""
51
+ "#{url_opts[:protocol] || 'http'}://#{url_opts[:host]}#{port_str}"
52
+ end
37
53
  end
38
54
  end
39
55
  end
@@ -19,10 +19,10 @@ class ::CommandProposal::Task < ApplicationRecord
19
19
  enum session_type: {
20
20
  # Task will have multiple iterations that are all essentially the same just with code changes
21
21
  task: 0,
22
- # Console iterations are actually line by line, so order matters
23
- console: 1,
24
22
  # Function iterations are much like tasks
25
- function: 2,
23
+ function: 1,
24
+ # Console iterations are actually line by line, so order matters
25
+ console: 2,
26
26
  # Modules are included in tasks and not run independently
27
27
  module: 3,
28
28
  }
@@ -0,0 +1,8 @@
1
+ <% if lines.none? && !(skip_empty ||= false) -%><div class="line"></div><% end
2
+ -%><% lines.each do |iteration| -%>
3
+ <div class="line"><%= iteration.code -%><%
4
+ if iteration.result.present?
5
+ -%><div class="result"><%= iteration.result %></div><%
6
+ end
7
+ -%></div>
8
+ <% end -%>
@@ -7,7 +7,7 @@
7
7
  <% when :approved, :started %>
8
8
  <%# Closing > offset to get rid of spacing issues. %>
9
9
  <div class="cmd-console" data-task="<%= @task.id %>" data-exe-url="<%= cmd_path(@task, :task_iterations) %>"
10
- ><div class="lines"><%= render partial: "lines", locals: { lines: @lines, skip_empty: true }
10
+ ><div class="lines"><%= render partial: "console_lines", locals: { lines: @lines, skip_empty: true }
11
11
  %></div
12
12
  ><div contenteditable="true" autofocus=true class="line cmd-entry"></div
13
13
  ></div>
@@ -26,7 +26,7 @@
26
26
  <% if @task.first_iteration.success? %>
27
27
  <p>Session closed.</p>
28
28
  <% end %>
29
- <div class="cmd-console" read-only=true><%= render partial: "lines", locals: { lines: @lines } %></div>
29
+ <div class="cmd-console" readonly=true><%= render partial: "console_lines", locals: { lines: @lines } %></div>
30
30
  <% end %>
31
31
 
32
32
  <% else %>
@@ -38,7 +38,7 @@
38
38
  <% if @task.first_iteration.success? %>
39
39
  <p>Session closed.</p>
40
40
  <% end %>
41
- <div class="cmd-console" read-only=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= render partial: "lines", locals: { lines: @lines } %></div>
41
+ <div class="cmd-console" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= render partial: "console_lines", locals: { lines: @lines } %></div>
42
42
 
43
43
  <% end %>
44
44
  <% end %>
@@ -1,54 +1,67 @@
1
1
  <%= render partial: "task_detail_table" %>
2
2
 
3
- <textarea class="cmd-terminal" readonly=true><%= @iteration.code %></textarea>
4
-
5
3
  <%= form_for @iteration, url: cmd_path(@iteration), html: { id: "edit-form-1" } do |f| %>
6
- <% if @iteration.params.any? && (@iteration.pending? || @iteration.complete?) %>
4
+ <% @iteration.brings.each do |bring_module| %>
5
+ <% needs_approval_str = " -- Module needs approval before running Task" unless bring_module.approved? %>
6
+ <%= link_to "Module: #{bring_module.name}#{needs_approval_str}", cmd_path("#{bring_module.friendly_id}") %>
7
+ <br>
8
+ <% end %>
9
+ <% if @task.approved? && @iteration.params.any? %>
7
10
  <div class="form-field">
8
11
  <% @iteration.params.each do |param_key| %>
9
- <label for="iteration[args][<%= param_key %>]"><%= param_key %></label>
10
- <input type="text" name="iteration[args][<%= param_key %>]" value="">
12
+ <label for="command_proposal_iteration[args][<%= param_key %>]"><%= param_key %></label>
13
+ <br>
14
+ <input type="text" name="command_proposal_iteration[args][<%= param_key %>]" value="">
15
+ <br>
11
16
  <% end %>
12
17
  </div>
13
18
  <% end %>
14
19
 
15
- <% if @iteration.approved_at? %>
20
+ <% if @iteration.approved_at? && !params.key?(:iteration) %>
16
21
  <%= f.hidden_field :command, value: :run %>
17
22
  <%= f.submit "Execute" %>
23
+ <br>
18
24
  <% end %>
19
25
  <% end %>
20
26
 
21
- <%= form_for @iteration, url: cmd_path(@iteration), html: { id: "edit-form-2" } do |f| %>
22
- <% if @iteration&.started_at? %>
23
- <textarea class="cmd-terminal" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= @iteration&.result %></textarea>
24
- <% end %>
27
+ <% unless params.key?(:iteration) %>
28
+ <%= form_for @iteration, url: cmd_path(@iteration), html: { id: "edit-form-2" } do |f| %>
29
+ <% if current_is_author?(@iteration) %>
25
30
 
26
- <% if current_is_author?(@iteration) %>
31
+ <% case @iteration.status&.to_sym %>
32
+ <% when :created, :failed, :cancelled, :terminated %>
33
+ <% unless @iteration.approved_at? %>
34
+ <% if can_approve?(@iteration) %>
35
+ <%= f.hidden_field :command, value: :approve %>
36
+ <%= f.submit "Approve!" %>
37
+ <% else %>
38
+ <%= f.hidden_field :command, value: :request %>
39
+ <%= f.submit "Request Access" %>
40
+ <% end %>
41
+ <% end %>
42
+ <% when :started %>
43
+ <%= f.hidden_field :command, value: :cancel %>
44
+ <%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
45
+ <% end %>
46
+
47
+ <% elsif can_approve?(@iteration) %>
27
48
 
28
- <% case @iteration.status&.to_sym %>
29
- <% when :created, :failed, :cancelled %>
30
- <% if can_approve?(@iteration) %>
49
+ <% if @iteration.approved? %>
50
+ <p>Approved. Ready to run.</p>
51
+ <% elsif @iteration.pending? %>
31
52
  <%= f.hidden_field :command, value: :approve %>
32
53
  <%= f.submit "Approve!" %>
33
- <% else %>
34
- <%= f.hidden_field :command, value: :request %>
35
- <%= f.submit "Request Access" %>
36
54
  <% end %>
37
- <% when :started %>
38
- <%= f.hidden_field :command, value: :cancel %>
39
- <%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
40
- <% end %>
41
55
 
42
- <% elsif can_approve?(@iteration) %>
43
-
44
- <% if @iteration.approved? %>
45
- <p>Approved. Ready to run.</p>
46
- <% elsif @iteration.pending? %>
47
- <%= f.hidden_field :command, value: :approve %>
48
- <%= f.submit "Approve!" %>
49
56
  <% end %>
50
-
51
57
  <% end %>
52
58
  <% end %>
53
59
 
54
- <% render partial: "past_iterations_list" %>
60
+ <% if @iteration&.started_at? %>
61
+ <div class="cmd-console" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= render partial: "lines", locals: { lines: @iteration&.result } %></div>
62
+ <% end %>
63
+
64
+ <br>
65
+ <textarea class="cmd-terminal" readonly=true><%= @iteration.code %></textarea>
66
+
67
+ <%= render partial: "past_iterations_list" %>
@@ -1,8 +1,4 @@
1
- <% if lines.none? && !(skip_empty ||= false) -%><div class="line"></div><% end
2
- -%><% lines.each do |iteration| -%>
3
- <div class="line"><%= iteration.code -%><%
4
- if iteration.result.present?
5
- -%><div class="result"><%= iteration.result %></div><%
6
- end
7
- -%></div>
8
- <% end -%>
1
+ <% if lines.blank? && !(skip_empty ||= false) -%><div class="line"></div><% end
2
+ -%><% lines.split("\n").each do |line|
3
+ -%><div class="line"><%= line -%></div><%
4
+ end -%>
@@ -12,15 +12,11 @@
12
12
  <%= f.hidden_field :command, value: :request %>
13
13
  <%= f.submit "Request Access" %>
14
14
  <% end %>
15
- <% when :approved %>
16
- <p>Include this module in other commands by using <code>bring :<%= @task.friendly_id %></code> at the top of the file.</p>
17
15
  <% end %>
18
16
 
19
17
  <% elsif can_approve?(@iteration) %>
20
18
 
21
- <% if @iteration.approved? %>
22
- <p>Approved. Ready to include.</p>
23
- <% elsif @iteration.pending? %>
19
+ <% if @iteration.pending? %>
24
20
  <%= f.hidden_field :command, value: :approve %>
25
21
  <%= f.submit "Approve!" %>
26
22
  <% end %>
@@ -28,6 +24,7 @@
28
24
  <% end %>
29
25
  <% end %>
30
26
 
27
+ <p>Include this module in other commands by using <code>bring :<%= @task.friendly_id %></code> at the top of the file.</p>
31
28
  <textarea class="cmd-terminal" readonly=true><%= @iteration&.code %></textarea>
32
29
 
33
- <% render partial: "past_iterations_list" %>
30
+ <%= render partial: "past_iterations_list" %>
@@ -7,9 +7,16 @@
7
7
  <!-- <th>Diff</th> -->
8
8
  </thead>
9
9
  <tbody>
10
+ <% primary_iteration = @task.primary_iteration %>
10
11
  <% @task.iterations.where.not(id: @iteration&.id).order(created_at: :desc).each do |iteration| %>
11
12
  <tr>
12
- <td><%= link_to iteration.created_at.strftime("%b %-d, %Y at %H:%M"), cmd_path(@task, iteration: iteration.id) %></td>
13
+ <td>
14
+ <% if iteration.id == primary_iteration&.id %>
15
+ <%= link_to iteration.created_at.strftime("%b %-d, %Y at %H:%M"), cmd_path(@task) %>
16
+ <% else %>
17
+ <%= link_to iteration.created_at.strftime("%b %-d, %Y at %H:%M"), cmd_path(@task, iteration: iteration.id) %>
18
+ <% end %>
19
+ </td>
13
20
  <td><%= iteration.status.capitalize %></td>
14
21
  <!-- <td><%= iteration.comments.count %></td> -->
15
22
  <!-- <td><%= link_to "Diff", cmd_path(@task, iteration: @iteration.id, diff: iteration.id) %></td> -->
@@ -8,8 +8,8 @@
8
8
  </thead>
9
9
  <tbody>
10
10
  <tr>
11
- <td><%= @iteration&.requester_name %></td>
12
- <td><%= @iteration&.approver_name %></td>
11
+ <td><%= @iteration&.requester_name.presence || "ID: #{@iteration&.requester_id}" if @iteration&.requester_id.present? %></td>
12
+ <td><%= @iteration&.approver_name.presence || "ID: #{@iteration&.approver_id}" if @iteration&.approver_id.present? %></td>
13
13
  <td><%= @iteration&.started_at&.strftime("%b %-d '%y, %-l:%M%P") %></td>
14
14
  <td data-iteration-status><%= @iteration&.status&.capitalize %></td>
15
15
  <td data-iteration-duration><%= humanized_duration(@iteration&.duration) %></td>
@@ -22,7 +22,7 @@
22
22
  <tbody>
23
23
  <% @iteration.args.each do |arg_k, arg_v| %>
24
24
  <tr>
25
- <th><%= arg_k %></th>
25
+ <th class="cmd-text-right"><%= arg_k %></th>
26
26
  <td><%= arg_v %></td>
27
27
  </tr>
28
28
  <% end %>
@@ -8,43 +8,45 @@
8
8
  <br>
9
9
  <% end %>
10
10
 
11
- <% if @iteration&.started_at? %>
12
- <textarea class="cmd-terminal" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= @iteration&.result %></textarea>
13
- <% end %>
11
+ <% unless params.key?(:iteration) %>
12
+ <% if current_is_author?(@iteration) %>
13
+
14
+ <% case @iteration.status&.to_sym %>
15
+ <% when :created, :cancelled, :terminated %>
16
+ <% if can_approve?(@iteration) %>
17
+ <%= f.hidden_field :command, value: :approve %>
18
+ <%= f.submit "Approve!" %>
19
+ <% else %>
20
+ <%= f.hidden_field :command, value: :request %>
21
+ <%= f.submit "Request Access" %>
22
+ <% end %>
23
+ <% when :failed %>
24
+ <p>Edit task to attempt to run again.</p>
25
+ <% when :approved %>
26
+ <%= f.hidden_field :command, value: :run %>
27
+ <%= f.submit "Execute" %>
28
+ <% when :started %>
29
+ <%= f.hidden_field :command, value: :cancel %>
30
+ <%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
31
+ <% when :success %>
32
+ <%= f.hidden_field :command, value: :request %>
33
+ <%= f.submit "Request Re-Run Access" %>
34
+ <% end %>
14
35
 
15
- <% if current_is_author?(@iteration) %>
36
+ <% elsif can_approve?(@iteration) %>
16
37
 
17
- <% case @iteration.status&.to_sym %>
18
- <% when :created, :cancelled %>
19
- <% if can_approve?(@iteration) %>
38
+ <% if @iteration.approved? %>
39
+ <p>Approved. Ready to run.</p>
40
+ <% elsif @iteration.pending? %>
20
41
  <%= f.hidden_field :command, value: :approve %>
21
42
  <%= f.submit "Approve!" %>
22
- <% else %>
23
- <%= f.hidden_field :command, value: :request %>
24
- <%= f.submit "Request Access" %>
25
43
  <% end %>
26
- <% when :failed %>
27
- <p>Edit task to attempt to run again.</p>
28
- <% when :approved %>
29
- <%= f.hidden_field :command, value: :run %>
30
- <%= f.submit "Execute" %>
31
- <% when :started %>
32
- <%= f.hidden_field :command, value: :cancel %>
33
- <%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
34
- <% when :success %>
35
- <%= f.hidden_field :command, value: :request %>
36
- <%= f.submit "Request Re-Run Access" %>
37
- <% end %>
38
-
39
- <% elsif can_approve?(@iteration) %>
40
44
 
41
- <% if @iteration.approved? %>
42
- <p>Approved. Ready to run.</p>
43
- <% elsif @iteration.pending? %>
44
- <%= f.hidden_field :command, value: :approve %>
45
- <%= f.submit "Approve!" %>
46
45
  <% end %>
46
+ <% end %>
47
47
 
48
+ <% if @iteration&.started_at? %>
49
+ <div class="cmd-console" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= render partial: "lines", locals: { lines: @iteration&.result } %></div>
48
50
  <% end %>
49
51
  <% end %>
50
52
  <% end %>
@@ -9,7 +9,7 @@
9
9
 
10
10
  <%= form_for(@task, url: cmd_path(@task)) do |f| %>
11
11
  <div class="cmd-field">
12
- <%= f.text_field :name, placeholder: "Title", class: "cmd-input" %>
12
+ <%= f.text_field :name, placeholder: "Title", class: "cmd-input", required: true %>
13
13
  </div>
14
14
 
15
15
  <div class="cmd-field">
@@ -24,18 +24,18 @@
24
24
  Task - Batch of code that typically only needs to be executed once. Requires approval for every new run.
25
25
  <% end %>
26
26
  </p>
27
- <p>
28
- <%= f.radio_button :session_type, :console %>
29
- <%= f.label :session_type, value: :console do %>
30
- Console - Starts a session that allows line-by-line commands to be executed. Requires approval only to begin session.
31
- <% end %>
32
- </p>
33
27
  <p>
34
28
  <%= f.radio_button :session_type, :function %>
35
29
  <%= f.label :session_type, value: :function do %>
36
30
  Function - Reusable batch of code, can accept arguments. Only requires approval on code changes.
37
31
  <% end %>
38
32
  </p>
33
+ <p>
34
+ <%= f.radio_button :session_type, :console %>
35
+ <%= f.label :session_type, value: :console do %>
36
+ Console - Starts a session that allows line-by-line commands to be executed. Requires approval only to begin session.
37
+ <% end %>
38
+ </p>
39
39
  <p>
40
40
  <%= f.radio_button :session_type, :module %>
41
41
  <%= f.label :session_type, value: :module do %>
@@ -1,7 +1,8 @@
1
1
  <div class="cmd-wrapper">
2
- <%= link_to "New Command", cmd_path(:new, :task, session_type: params[:filter] || :task) %> <br>
2
+ <%= link_to "New #{params[:filter].presence&.capitalize || 'Command'}", cmd_path(:new, :task, session_type: params[:filter] || :task) %> <br>
3
3
  <br>
4
- <% ::CommandProposal::Task.session_types.each_with_index do |(session_type, _session_enum), idx| %><%=
4
+ <%= link_to "All", cmd_path(:tasks, current_params.except(:filter)), class: "cmd-tab #{:active unless params.key?(:filter)}"
5
+ %><% ::CommandProposal::Task.session_types.each_with_index do |(session_type, _session_enum), idx| %><%=
5
6
  selected = params[:filter] == session_type.to_s
6
7
  # Offset closing RB tags to fix spacing issues
7
8
  link_to session_type.capitalize, toggled_param(filter: session_type), class: "cmd-tab #{:active if selected}"
@@ -18,7 +18,7 @@ module CommandProposal
18
18
  # Default
19
19
  @approval_required = true
20
20
 
21
- # Required (if approval needed)
21
+ # User details - highly recommended
22
22
  @user_class_name = nil
23
23
  @role_scope = nil
24
24
  @user_name = nil
@@ -25,7 +25,6 @@ module CommandProposal
25
25
  when :approve then command_approve
26
26
  when :run then command_run
27
27
  when :cancel then command_cancel
28
- when :cancel then command_cancel
29
28
  when :close then command_close
30
29
  end
31
30
 
@@ -78,6 +77,9 @@ module CommandProposal
78
77
  return if @iteration.complete?
79
78
 
80
79
  @iteration.update(status: :cancelling)
80
+ return if ::CommandProposal.sessions.key?("task:#{@task.id}")
81
+
82
+ ::CommandProposal::Services::ShutDown.terminate(@iteration)
81
83
  end
82
84
 
83
85
  def command_close
@@ -4,23 +4,33 @@ module CommandProposal
4
4
  attr_accessor :session
5
5
  # Add expiration and things like that...
6
6
 
7
+ def self.execute(friendly_id)
8
+ task = ::CommandProposal::Task.find_by!(friendly_id: friendly_id)
9
+
10
+ new.execute(task.primary_iteration)
11
+ end
12
+
7
13
  def initialize
8
14
  @session = session
9
15
  end
10
16
 
11
- def execute(iteration, inline=false)
17
+ def execute(iteration)
12
18
  @iteration = iteration
13
- @inline = inline
14
19
  prepare
15
20
 
16
21
  run
17
22
 
18
23
  complete
24
+ proposal = ::CommandProposal::Service::ProposalPresenter.new(@iteration)
19
25
  @iteration = nil
26
+ proposal
20
27
  end
21
28
 
22
- def quick_run(iteration)
23
- raise CommandProposal::Error, ":#{iteration.task.friendly_id} does not have approval to run." unless iteration.approved?
29
+ def quick_run(friendly_id)
30
+ task = ::CommandProposal::Task.module.find_by!(friendly_id: friendly_id)
31
+ iteration = task&.primary_iteration
32
+
33
+ raise CommandProposal::Error, ":#{friendly_id} does not have approval to run." unless iteration&.approved?
24
34
 
25
35
  @session.eval(iteration.code)
26
36
  end
@@ -49,14 +59,16 @@ module CommandProposal
49
59
  stored_stdout = $stdout
50
60
  $stdout = StringIO.new
51
61
  result = nil # Init var for scope
62
+ status = nil
52
63
 
53
64
  running_thread = Thread.new do
54
65
  begin
55
66
  # Run bring functions in here so we can capture any string outputs
56
67
  # OR! Run the full runner and instead of saving to an iteration, return the string for prepending here
57
68
  result = @session.eval("_ = (#{@iteration.code})").inspect # rubocop:disable Security/Eval - Eval is scary, but in this case it's exactly what we need.
69
+ status = :success
58
70
  rescue Exception => e # rubocop:disable Lint/RescueException - Yes, rescue full Exception so that we can catch typos in evals as well
59
- @iteration.status = :failed
71
+ status = :failed
60
72
 
61
73
  result = results_from_exception(e)
62
74
  end
@@ -69,9 +81,12 @@ module CommandProposal
69
81
  @iteration.update(result: $stdout.try(:string).dup)
70
82
  end
71
83
 
72
- running_thread.exit if @iteration.cancelling?
84
+ if @iteration.cancelling?
85
+ running_thread.exit
86
+ status = :cancelled
87
+ end
73
88
 
74
- sleep 1
89
+ sleep 0.4
75
90
  end
76
91
 
77
92
  output = $stdout.try(:string)
@@ -79,11 +94,12 @@ module CommandProposal
79
94
  # Not using presence because we want to maintain other empty objects such as [] and {}
80
95
 
81
96
  $stdout = stored_stdout
97
+ @iteration.status = status
82
98
  @iteration.result = [output, "#{result || 'nil'}"].compact.join("\n")
83
99
  end
84
100
 
85
101
  def bring_function
86
- "def bring(*func_names); func_names.each { |f| self.quick_run(::CommandProposal::Task.module.find_by!(friendly_id: f).current_iteration) }; end"
102
+ "def bring(*func_names); func_names.each { |f| self.quick_run(f) }; end"
87
103
  end
88
104
 
89
105
  def complete
@@ -91,7 +107,7 @@ module CommandProposal
91
107
  if @iteration.cancelling? || @iteration.cancelled?
92
108
  @iteration.result += "\n\n~~~~~ CANCELLED ~~~~~"
93
109
  @iteration.status = :cancelled
94
- elsif @iteration.failed?
110
+ elsif @iteration.status&.to_sym == :failed
95
111
  # No-op
96
112
  else
97
113
  @iteration.status = :success
@@ -0,0 +1,25 @@
1
+ module CommandProposal
2
+ module Services
3
+ module ShutDown
4
+ module_function
5
+
6
+ def reset_all
7
+ pending = ::CommandProposal::Iteration.where(status: [:started, :cancelling])
8
+ pending.find_each do |iteration|
9
+ terminate(iteration)
10
+ end
11
+ end
12
+
13
+ def terminate(iteration)
14
+ return unless iteration.running?
15
+
16
+ terminated_result = iteration.result + "\n\n~~~~~ TERMINATED ~~~~~"
17
+ iteration.update(
18
+ status: :terminated,
19
+ result: terminated_result,
20
+ completed_at: Time.current
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,8 @@
1
+ # gem build command_proposal.gemspec
2
+ # gam "Built version __ of gem."
3
+ # gpo
4
+ # gem push command_proposal-__.gem
5
+
1
6
  module CommandProposal
2
- VERSION = '1.0.0'
7
+ VERSION = '1.0.4'
3
8
  end
@@ -2,6 +2,7 @@ require "command_proposal/configuration"
2
2
  require "command_proposal/version"
3
3
  require "command_proposal/engine"
4
4
  require "command_proposal/services/runner"
5
+ require "command_proposal/services/shut_down"
5
6
 
6
7
  module CommandProposal
7
8
  class Error < StandardError; end
@@ -1,4 +1,9 @@
1
1
  ::CommandProposal.configure do |config|
2
+ # Determines if a user needs a different user to approve their commands.
3
+ # Defaults to true, the recommended value.
4
+ # However, disabling in development could help with testing.
5
+ # config.approval_required = !Rails.env.development?
6
+
2
7
  # Change if your base user class has a different model name
3
8
  config.user_class_name = "User"
4
9
 
@@ -8,6 +13,8 @@
8
13
  # Scope for your user class that determines users who are permitted to interact with commands
9
14
  # It is highly recommended to make this very exclusive, as any users in this scope will be able
10
15
  # to interact with your database directly.
16
+ # Expected that the class will respond to `#{role_scope}` and
17
+ # instances of the class respond to `#{role_scope}?`
11
18
  config.role_scope = :admin
12
19
 
13
20
  # Method called to display a user's name
@@ -34,14 +41,14 @@
34
41
 
35
42
  # Called when a command is proposed for review
36
43
  config.proposal_callback = Proc.new { |proposal|
37
- # Slack.notify("#{proposal.requester} has proposed #{proposal.name}.\n<Click Here|#{proposal.url}> to view this proposal and approve.")
44
+ # Slack.notify("#{proposal.requester} has proposed #{proposal.name}.\n<#{proposal.url}|Click Here> to view this proposal and approve.")
38
45
  }
39
46
  # Called when a command runs and completes successfully
40
- config.success_callback = Proc.new { |iteration|
41
- # Slack.notify("The task #{proposal.name} has completed in #{proposal.duration}s.\n<Click Here|#{proposal.url}> to view the results.")
47
+ config.success_callback = Proc.new { |proposal|
48
+ # Slack.notify("The task #{proposal.name} has completed in #{proposal.duration}s.\n<#{proposal.url}|Click Here> to view the results.")
42
49
  }
43
50
  # Called when a command runs but fails to complete
44
- config.failed_callback = Proc.new { |iteration|
45
- # Slack.notify("The task #{proposal.name} has failed!\n<Click Here|#{proposal.url}> to see what went wrong.")
51
+ config.failed_callback = Proc.new { |proposal|
52
+ # Slack.notify("The task #{proposal.name} has failed!\n<#{proposal.url}|Click Here> to see what went wrong.")
46
53
  }
47
54
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_proposal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rocco Nicholls
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-25 00:00:00.000000000 Z
11
+ date: 2021-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -116,6 +116,7 @@ files:
116
116
  - app/models/command_proposal/service/json_wrapper.rb
117
117
  - app/models/command_proposal/service/proposal_presenter.rb
118
118
  - app/models/command_proposal/task.rb
119
+ - app/views/command_proposal/tasks/_console_lines.html.erb
119
120
  - app/views/command_proposal/tasks/_console_show.html.erb
120
121
  - app/views/command_proposal/tasks/_function_show.html.erb
121
122
  - app/views/command_proposal/tasks/_lines.html.erb
@@ -133,6 +134,7 @@ files:
133
134
  - lib/command_proposal/engine.rb
134
135
  - lib/command_proposal/services/command_interpreter.rb
135
136
  - lib/command_proposal/services/runner.rb
137
+ - lib/command_proposal/services/shut_down.rb
136
138
  - lib/command_proposal/version.rb
137
139
  - lib/generators/command_proposal/install/install_generator.rb
138
140
  - lib/generators/command_proposal/install/templates/initializer.rb