command_proposal 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +77 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/command_proposal_manifest.js +1 -0
  6. data/app/assets/javascripts/command_proposal/_codemirror.js +9814 -0
  7. data/app/assets/javascripts/command_proposal/_helpers.js +9 -0
  8. data/app/assets/javascripts/command_proposal/codemirror-addon-searchcursor.js +296 -0
  9. data/app/assets/javascripts/command_proposal/codemirror-keymap-sublime.js +720 -0
  10. data/app/assets/javascripts/command_proposal/codemirror-mode-ruby.js +303 -0
  11. data/app/assets/javascripts/command_proposal/console.js +195 -0
  12. data/app/assets/javascripts/command_proposal/feed.js +51 -0
  13. data/app/assets/javascripts/command_proposal/terminal.js +40 -0
  14. data/app/assets/javascripts/command_proposal.js +1 -0
  15. data/app/assets/stylesheets/command_proposal/_variables.scss +0 -0
  16. data/app/assets/stylesheets/command_proposal/codemirror-rubyblue.scss +27 -0
  17. data/app/assets/stylesheets/command_proposal/codemirror.scss +367 -0
  18. data/app/assets/stylesheets/command_proposal/command_proposal.scss +1 -0
  19. data/app/assets/stylesheets/command_proposal/components.scss +31 -0
  20. data/app/assets/stylesheets/command_proposal/containers.scss +4 -0
  21. data/app/assets/stylesheets/command_proposal/icons.scss +12 -0
  22. data/app/assets/stylesheets/command_proposal/tables.scss +76 -0
  23. data/app/assets/stylesheets/command_proposal/terminal.scss +72 -0
  24. data/app/assets/stylesheets/command_proposal.scss +5 -0
  25. data/app/controllers/command_proposal/engine_controller.rb +6 -0
  26. data/app/controllers/command_proposal/iterations_controller.rb +83 -0
  27. data/app/controllers/command_proposal/runner_controller.rb +86 -0
  28. data/app/controllers/command_proposal/tasks_controller.rb +97 -0
  29. data/app/helpers/command_proposal/application_helper.rb +58 -0
  30. data/app/helpers/command_proposal/icons_helper.rb +15 -0
  31. data/app/helpers/command_proposal/params_helper.rb +63 -0
  32. data/app/helpers/command_proposal/permissions_helper.rb +42 -0
  33. data/app/jobs/command_proposal/application_job.rb +4 -0
  34. data/app/jobs/command_proposal/command_runner_job.rb +11 -0
  35. data/app/models/command_proposal/comment.rb +14 -0
  36. data/app/models/command_proposal/iteration.rb +78 -0
  37. data/app/models/command_proposal/service/external_belong.rb +48 -0
  38. data/app/models/command_proposal/service/json_wrapper.rb +18 -0
  39. data/app/models/command_proposal/service/proposal_presenter.rb +39 -0
  40. data/app/models/command_proposal/task.rb +106 -0
  41. data/app/views/command_proposal/tasks/_console_show.html.erb +44 -0
  42. data/app/views/command_proposal/tasks/_function_show.html.erb +54 -0
  43. data/app/views/command_proposal/tasks/_lines.html.erb +8 -0
  44. data/app/views/command_proposal/tasks/_module_show.html.erb +33 -0
  45. data/app/views/command_proposal/tasks/_past_iterations_list.html.erb +20 -0
  46. data/app/views/command_proposal/tasks/_task_detail_table.html.erb +31 -0
  47. data/app/views/command_proposal/tasks/_task_show.html.erb +55 -0
  48. data/app/views/command_proposal/tasks/error.html.erb +4 -0
  49. data/app/views/command_proposal/tasks/form.html.erb +64 -0
  50. data/app/views/command_proposal/tasks/index.html.erb +44 -0
  51. data/app/views/command_proposal/tasks/show.html.erb +10 -0
  52. data/config/routes.rb +11 -0
  53. data/lib/command_proposal/configuration.rb +41 -0
  54. data/lib/command_proposal/engine.rb +6 -0
  55. data/lib/command_proposal/services/command_interpreter.rb +108 -0
  56. data/lib/command_proposal/services/runner.rb +157 -0
  57. data/lib/command_proposal/version.rb +3 -0
  58. data/lib/command_proposal.rb +27 -0
  59. data/lib/generators/command_proposal/install/install_generator.rb +28 -0
  60. data/lib/generators/command_proposal/install/templates/initializer.rb +47 -0
  61. data/lib/generators/command_proposal/install/templates/install_command_proposal.rb +40 -0
  62. data/lib/tasks/command_proposal_tasks.rake +4 -0
  63. metadata +167 -0
@@ -0,0 +1,44 @@
1
+ <%= render partial: "task_detail_table" %>
2
+
3
+ <%= form_for @iteration, url: cmd_path(@iteration) do |f| %>
4
+ <% if current_is_author?(@iteration) %>
5
+
6
+ <% case @iteration.status.to_sym %>
7
+ <% when :approved, :started %>
8
+ <%# Closing > offset to get rid of spacing issues. %>
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 }
11
+ %></div
12
+ ><div contenteditable="true" autofocus=true class="line cmd-entry"></div
13
+ ></div>
14
+
15
+ <%= f.hidden_field :command, value: :close %>
16
+ <%= f.submit "Close Session" %>
17
+ <% when :created, nil %>
18
+ <% if can_approve?(@iteration) %>
19
+ <%= f.hidden_field :command, value: :approve %>
20
+ <%= f.submit "Approve!" %>
21
+ <% else %>
22
+ <%= f.hidden_field :command, value: :request %>
23
+ <%= f.submit "Request Access" %>
24
+ <% end %>
25
+ <% else %>
26
+ <% if @task.first_iteration.success? %>
27
+ <p>Session closed.</p>
28
+ <% end %>
29
+ <div class="cmd-console" read-only=true><%= render partial: "lines", locals: { lines: @lines } %></div>
30
+ <% end %>
31
+
32
+ <% else %>
33
+
34
+ <% if can_approve?(@iteration) && @iteration.pending? %>
35
+ <%= f.hidden_field :command, value: :approve %>
36
+ <%= f.submit "Approve!" %>
37
+ <% end %>
38
+ <% if @task.first_iteration.success? %>
39
+ <p>Session closed.</p>
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>
42
+
43
+ <% end %>
44
+ <% end %>
@@ -0,0 +1,54 @@
1
+ <%= render partial: "task_detail_table" %>
2
+
3
+ <textarea class="cmd-terminal" readonly=true><%= @iteration.code %></textarea>
4
+
5
+ <%= form_for @iteration, url: cmd_path(@iteration), html: { id: "edit-form-1" } do |f| %>
6
+ <% if @iteration.params.any? && (@iteration.pending? || @iteration.complete?) %>
7
+ <div class="form-field">
8
+ <% @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="">
11
+ <% end %>
12
+ </div>
13
+ <% end %>
14
+
15
+ <% if @iteration.approved_at? %>
16
+ <%= f.hidden_field :command, value: :run %>
17
+ <%= f.submit "Execute" %>
18
+ <% end %>
19
+ <% end %>
20
+
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 %>
25
+
26
+ <% if current_is_author?(@iteration) %>
27
+
28
+ <% case @iteration.status&.to_sym %>
29
+ <% when :created, :failed, :cancelled %>
30
+ <% if can_approve?(@iteration) %>
31
+ <%= f.hidden_field :command, value: :approve %>
32
+ <%= f.submit "Approve!" %>
33
+ <% else %>
34
+ <%= f.hidden_field :command, value: :request %>
35
+ <%= f.submit "Request Access" %>
36
+ <% 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
+
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
+ <% end %>
50
+
51
+ <% end %>
52
+ <% end %>
53
+
54
+ <% render partial: "past_iterations_list" %>
@@ -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 -%>
@@ -0,0 +1,33 @@
1
+ <%= render partial: "task_detail_table" %>
2
+
3
+ <%= form_for @iteration, url: cmd_path(@iteration) do |f| %>
4
+ <% if current_is_author?(@iteration) %>
5
+
6
+ <% case @iteration.status&.to_sym %>
7
+ <% when :created %>
8
+ <% if can_approve?(@iteration) %>
9
+ <%= f.hidden_field :command, value: :approve %>
10
+ <%= f.submit "Approve!" %>
11
+ <% else %>
12
+ <%= f.hidden_field :command, value: :request %>
13
+ <%= f.submit "Request Access" %>
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
+ <% end %>
18
+
19
+ <% elsif can_approve?(@iteration) %>
20
+
21
+ <% if @iteration.approved? %>
22
+ <p>Approved. Ready to include.</p>
23
+ <% elsif @iteration.pending? %>
24
+ <%= f.hidden_field :command, value: :approve %>
25
+ <%= f.submit "Approve!" %>
26
+ <% end %>
27
+
28
+ <% end %>
29
+ <% end %>
30
+
31
+ <textarea class="cmd-terminal" readonly=true><%= @iteration&.code %></textarea>
32
+
33
+ <% render partial: "past_iterations_list" %>
@@ -0,0 +1,20 @@
1
+ <% if @task.iterations.many? %>
2
+ <table class="cmd-table">
3
+ <thead>
4
+ <th>Timestamp / Link</th>
5
+ <th>Status</th>
6
+ <!-- <th># Comments</th> -->
7
+ <!-- <th>Diff</th> -->
8
+ </thead>
9
+ <tbody>
10
+ <% @task.iterations.where.not(id: @iteration&.id).order(created_at: :desc).each do |iteration| %>
11
+ <tr>
12
+ <td><%= link_to iteration.created_at.strftime("%b %-d, %Y at %H:%M"), cmd_path(@task, iteration: iteration.id) %></td>
13
+ <td><%= iteration.status.capitalize %></td>
14
+ <!-- <td><%= iteration.comments.count %></td> -->
15
+ <!-- <td><%= link_to "Diff", cmd_path(@task, iteration: @iteration.id, diff: iteration.id) %></td> -->
16
+ </tr>
17
+ <% end %>
18
+ </tbody>
19
+ </table>
20
+ <% end %>
@@ -0,0 +1,31 @@
1
+ <table class="cmd-table">
2
+ <thead>
3
+ <th>Requester</th>
4
+ <th>Approver</th>
5
+ <th>Started</th>
6
+ <th>Status</th>
7
+ <th>Duration</th>
8
+ </thead>
9
+ <tbody>
10
+ <tr>
11
+ <td><%= @iteration&.requester_name %></td>
12
+ <td><%= @iteration&.approver_name %></td>
13
+ <td><%= @iteration&.started_at&.strftime("%b %-d '%y, %-l:%M%P") %></td>
14
+ <td data-iteration-status><%= @iteration&.status&.capitalize %></td>
15
+ <td data-iteration-duration><%= humanized_duration(@iteration&.duration) %></td>
16
+ </tr>
17
+ </tbody>
18
+ </table>
19
+
20
+ <% if @iteration&.args.present? %>
21
+ <table class="cmd-table">
22
+ <tbody>
23
+ <% @iteration.args.each do |arg_k, arg_v| %>
24
+ <tr>
25
+ <th><%= arg_k %></th>
26
+ <td><%= arg_v %></td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
31
+ <% end %>
@@ -0,0 +1,55 @@
1
+ <%= render partial: "task_detail_table" %>
2
+
3
+ <% if @iteration.present? %>
4
+ <%= form_for @iteration, url: cmd_path(@iteration) do |f| %>
5
+ <% @iteration.brings.each do |bring_module| %>
6
+ <% needs_approval_str = " -- Module needs approval before running Task" unless bring_module.approved? %>
7
+ <%= link_to "Module: #{bring_module.name}#{needs_approval_str}", cmd_path("#{bring_module.friendly_id}") %>
8
+ <br>
9
+ <% end %>
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 %>
14
+
15
+ <% if current_is_author?(@iteration) %>
16
+
17
+ <% case @iteration.status&.to_sym %>
18
+ <% when :created, :cancelled %>
19
+ <% if can_approve?(@iteration) %>
20
+ <%= f.hidden_field :command, value: :approve %>
21
+ <%= f.submit "Approve!" %>
22
+ <% else %>
23
+ <%= f.hidden_field :command, value: :request %>
24
+ <%= f.submit "Request Access" %>
25
+ <% 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
+
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
+ <% end %>
47
+
48
+ <% end %>
49
+ <% end %>
50
+ <% end %>
51
+
52
+ <br>
53
+ <textarea class="cmd-terminal" readonly=true><%= @iteration&.code %></textarea>
54
+
55
+ <%= render partial: "past_iterations_list" %>
@@ -0,0 +1,4 @@
1
+ <div class="cmd-wrapper">
2
+ <h3>Error:</h3>
3
+ <p><%= flash.alert.presence || "Something went wrong" %></p>
4
+ </div>
@@ -0,0 +1,64 @@
1
+ <div class="cmd-wrapper">
2
+ <% if @task.persisted? %>
3
+ <a href="<%= cmd_path(@task) %>">&larr; Back to <%= @task.session_type.capitalize %></a>
4
+ <h2>Edit <%= @task.name.presence || "Command" %></h2>
5
+ <% else %>
6
+ <a href="<%= cmd_path(:tasks) %>">&larr; Back to Command</a>
7
+ <h2>New Command</h2>
8
+ <% end %>
9
+
10
+ <%= form_for(@task, url: cmd_path(@task)) do |f| %>
11
+ <div class="cmd-field">
12
+ <%= f.text_field :name, placeholder: "Title", class: "cmd-input" %>
13
+ </div>
14
+
15
+ <div class="cmd-field">
16
+ <%= f.text_area :description, placeholder: "Description", class: "cmd-input" %>
17
+ </div>
18
+
19
+ <% unless @task.persisted? %>
20
+ <div class="cmd-field">
21
+ <p>
22
+ <%= f.radio_button :session_type, :task %>
23
+ <%= f.label :session_type, value: :task do %>
24
+ Task - Batch of code that typically only needs to be executed once. Requires approval for every new run.
25
+ <% end %>
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
+ <p>
34
+ <%= f.radio_button :session_type, :function %>
35
+ <%= f.label :session_type, value: :function do %>
36
+ Function - Reusable batch of code, can accept arguments. Only requires approval on code changes.
37
+ <% end %>
38
+ </p>
39
+ <p>
40
+ <%= f.radio_button :session_type, :module %>
41
+ <%= f.label :session_type, value: :module do %>
42
+ Module - An includable batch of code that can give accessible methods to a function or task. Requires approval on code changes.
43
+ <% end %>
44
+ </p>
45
+ </div>
46
+ <% end %>
47
+
48
+ <% if @task.persisted? && !@task.console? %>
49
+ <% if @task.function? %>
50
+ <p>Use <code>params[:key]</code> to reference values that can be passed in. UI will provide fields when running.</p>
51
+ <p>NOTE: All params will come through as strings.</p>
52
+ <% end %>
53
+
54
+ <div class="cmd-field">
55
+ <%= f.label :code %>
56
+ <%= f.text_area :code, class: "cmd-terminal" %>
57
+ </div>
58
+ <% end %>
59
+
60
+ <div class="cmd-submission">
61
+ <%= f.submit %>
62
+ </div>
63
+ <% end %>
64
+ </div>
@@ -0,0 +1,44 @@
1
+ <div class="cmd-wrapper">
2
+ <%= link_to "New Command", cmd_path(:new, :task, session_type: params[:filter] || :task) %> <br>
3
+ <br>
4
+ <% ::CommandProposal::Task.session_types.each_with_index do |(session_type, _session_enum), idx| %><%=
5
+ selected = params[:filter] == session_type.to_s
6
+ # Offset closing RB tags to fix spacing issues
7
+ link_to session_type.capitalize, toggled_param(filter: session_type), class: "cmd-tab #{:active if selected}"
8
+ %><% end %>
9
+ <br>
10
+ <br>
11
+
12
+ <%= form_for "", url: cmd_path(:search, :tasks, current_params.except(:search)) do |f| %>
13
+ <div class="cmd-flex-row">
14
+ <%= text_field_tag :search, params[:search], placeholder: "Search", class: "cmd-input" %>
15
+ <%= f.submit "Search" %>
16
+ </div>
17
+ <% end %>
18
+
19
+ <div class="cmd-index-table">
20
+ <% @tasks.each do |task| %>
21
+ <%= link_to cmd_path(task), class: "cmd-index-table-row" do %>
22
+ <div class="cmd-index-col cmd-col-status">
23
+ <div class="cmd-status" data-status="<%= task.status %>"><i class="<%= icon(task.status) %>"></i></div>
24
+ </div>
25
+ <div class="cmd-index-col cmd-col-title">
26
+ <div>
27
+ <span class="cmd-name"><%= task.name %></span>
28
+ <% if task.duration.present? %>
29
+ <span class="cmd-duration"><%= humanized_duration(task.duration) %><%= "..." unless task.primary_iteration.complete? %></span>
30
+ <% end %>
31
+ </div>
32
+ <div class="cmd-desc"><%= task.description %></div>
33
+ </div>
34
+ <div class="cmd-index-col cmd-col-type">
35
+ <div class="cmd-type"><%= task.session_type.capitalize %></div>
36
+ </div>
37
+ <div class="cmd-index-col cmd-col-info">
38
+ <div class="cmd-timestamp"><%= task.last_executed_at&.strftime("%b %-d '%y, %-l:%M%P") || "Never Run" %></div>
39
+ <div class="cmd-author"><%= task.current_iteration_by %></div>
40
+ </div>
41
+ <% end %>
42
+ <% end %>
43
+ </div>
44
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="cmd-wrapper">
2
+ <%= link_to "All Tasks", cmd_path(:tasks) %> | <%= link_to "Edit #{@task.session_type.humanize}", cmd_path(:edit, @task) %>
3
+
4
+ <h2><%= @task.name %></h2>
5
+ <% if @task.description.present? %>
6
+ <p><%= @task.description %></p>
7
+ <% end %>
8
+
9
+ <%= render partial: "#{@task.session_type}_show" %>
10
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ CommandProposal::Engine.routes.draw do
2
+ namespace :command_proposal, path: "/" do
3
+ resources :tasks, path: "/", as: :tasks do
4
+ post :search, on: :collection
5
+ get :error, on: :collection
6
+
7
+ resources :runner, only: [:create, :show]
8
+ resources :iterations, shallow: true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,41 @@
1
+ module CommandProposal
2
+ class Configuration
3
+ attr_accessor(
4
+ # Required
5
+ :user_class_name,
6
+ :role_scope,
7
+ :user_name,
8
+ :controller_var,
9
+ # Default
10
+ :approval_required,
11
+ # Optional
12
+ :proposal_callback,
13
+ :success_callback,
14
+ :failed_callback,
15
+ )
16
+
17
+ def initialize
18
+ # Default
19
+ @approval_required = true
20
+
21
+ # Required (if approval needed)
22
+ @user_class_name = nil
23
+ @role_scope = nil
24
+ @user_name = nil
25
+ @controller_var = nil
26
+
27
+ # Optional
28
+ @proposal_callback = nil
29
+ @success_callback = nil
30
+ @failed_callback = nil
31
+ end
32
+
33
+ def user_class
34
+ @user_class ||= @user_class_name&.constantize
35
+ end
36
+
37
+ def approval_required?
38
+ !!approval_required
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,6 @@
1
+ require "font-awesome-rails"
2
+
3
+ module CommandProposal
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end