command_proposal 1.0.0 → 1.0.4

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 (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