rails_execution 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +129 -0
- data/LICENSE.txt +21 -0
- data/README.md +35 -0
- data/Rakefile +3 -0
- data/app/assets/images/executions/approved.png +0 -0
- data/app/assets/images/executions/favicon.png +0 -0
- data/app/assets/images/executions/logo.png +0 -0
- data/app/assets/images/executions/rejected.png +0 -0
- data/app/assets/images/executions/robot.png +0 -0
- data/app/assets/javascripts/executions/ace.min.js +1 -0
- data/app/assets/javascripts/executions/ace.modes.ruby.min.js +1 -0
- data/app/assets/javascripts/executions/ace.theme-solarized_dark.min.js +1 -0
- data/app/assets/javascripts/executions/base.js +1 -0
- data/app/assets/javascripts/executions/bootstrap.5.2.1.min.js +6 -0
- data/app/assets/javascripts/executions/chart.min.js +13 -0
- data/app/assets/javascripts/executions/comments.js +43 -0
- data/app/assets/javascripts/executions/easymde.min.js +7 -0
- data/app/assets/javascripts/executions/events.js +8 -0
- data/app/assets/javascripts/executions/highlight.min.js +1198 -0
- data/app/assets/javascripts/executions/highlight.ruby.min.js +51 -0
- data/app/assets/javascripts/executions/jquery-3.6.1.min.js +2 -0
- data/app/assets/javascripts/executions/marked.min.js +6 -0
- data/app/assets/javascripts/executions/rails.js +565 -0
- data/app/assets/javascripts/executions/select2.min.js +2 -0
- data/app/assets/stylesheets/executions/base.css +3 -0
- data/app/assets/stylesheets/executions/bootstrap-icons.css +1869 -0
- data/app/assets/stylesheets/executions/comments.css +14 -0
- data/app/assets/stylesheets/executions/easymde.min.css +7 -0
- data/app/assets/stylesheets/executions/fonts.css +4 -0
- data/app/assets/stylesheets/executions/highlight.min.css +9 -0
- data/app/assets/stylesheets/executions/modify.scss +278 -0
- data/app/assets/stylesheets/executions/select2.min.css +1 -0
- data/app/controllers/rails_execution/base_controller.rb +16 -0
- data/app/controllers/rails_execution/comments_controller.rb +34 -0
- data/app/controllers/rails_execution/dashboards_controller.rb +27 -0
- data/app/controllers/rails_execution/tasks_controller.rb +175 -0
- data/app/helpers/rails_execution/base_helper.rb +14 -0
- data/app/helpers/rails_execution/policy_helper.rb +64 -0
- data/app/helpers/rails_execution/rendering_helper.rb +94 -0
- data/app/models/rails_execution/activity.rb +8 -0
- data/app/models/rails_execution/comment.rb +8 -0
- data/app/models/rails_execution/task.rb +71 -0
- data/app/models/rails_execution/task_review.rb +14 -0
- data/app/views/layouts/execution.html.haml +16 -0
- data/app/views/rails_execution/comments/_comment.html.haml +23 -0
- data/app/views/rails_execution/comments/create.js.haml +6 -0
- data/app/views/rails_execution/comments/update.js.haml +2 -0
- data/app/views/rails_execution/dashboards/charts/_insights.html.haml +27 -0
- data/app/views/rails_execution/dashboards/home.html.haml +24 -0
- data/app/views/rails_execution/dashboards/insights.json.jbuilder +8 -0
- data/app/views/rails_execution/shared/_flash.html.haml +7 -0
- data/app/views/rails_execution/shared/_header.html.haml +13 -0
- data/app/views/rails_execution/shared/_paging.html.haml +35 -0
- data/app/views/rails_execution/tasks/_actions.html.haml +12 -0
- data/app/views/rails_execution/tasks/_activities.html.haml +22 -0
- data/app/views/rails_execution/tasks/_attachment_file_fields.html.haml +3 -0
- data/app/views/rails_execution/tasks/_attachment_files.html.haml +12 -0
- data/app/views/rails_execution/tasks/_comments.html.haml +15 -0
- data/app/views/rails_execution/tasks/_form.html.haml +55 -0
- data/app/views/rails_execution/tasks/_form_scripts.html.haml +34 -0
- data/app/views/rails_execution/tasks/_new_comment.html.haml +8 -0
- data/app/views/rails_execution/tasks/_reviewers.html.haml +23 -0
- data/app/views/rails_execution/tasks/_script_content.html.haml +15 -0
- data/app/views/rails_execution/tasks/_show_scripts.html.haml +27 -0
- data/app/views/rails_execution/tasks/_status.html.haml +19 -0
- data/app/views/rails_execution/tasks/_task.html.haml +18 -0
- data/app/views/rails_execution/tasks/_tips.html.haml +18 -0
- data/app/views/rails_execution/tasks/closed.html.haml +12 -0
- data/app/views/rails_execution/tasks/completed.html.haml +12 -0
- data/app/views/rails_execution/tasks/edit.html.haml +3 -0
- data/app/views/rails_execution/tasks/index.html.haml +12 -0
- data/app/views/rails_execution/tasks/new.html.haml +3 -0
- data/app/views/rails_execution/tasks/show.html.haml +38 -0
- data/config/routes.rb +26 -0
- data/lib/generators/rails_execution/file_upload_generator.rb +18 -0
- data/lib/generators/rails_execution/install_generator.rb +36 -0
- data/lib/generators/rails_execution/templates/config.rb.tt +63 -0
- data/lib/generators/rails_execution/templates/file_reader.rb.tt +14 -0
- data/lib/generators/rails_execution/templates/file_uploader.rb.tt +11 -0
- data/lib/generators/rails_execution/templates/install.rb.tt +48 -0
- data/lib/rails_execution/app_model.rb +14 -0
- data/lib/rails_execution/config.rb +73 -0
- data/lib/rails_execution/engine.rb +24 -0
- data/lib/rails_execution/error.rb +6 -0
- data/lib/rails_execution/files/reader.rb +49 -0
- data/lib/rails_execution/files/uploader.rb +42 -0
- data/lib/rails_execution/services/approvement.rb +41 -0
- data/lib/rails_execution/services/execution.rb +87 -0
- data/lib/rails_execution/services/executor.rb +24 -0
- data/lib/rails_execution/services/paging.rb +35 -0
- data/lib/rails_execution/services/syntax_checker.rb +38 -0
- data/lib/rails_execution/version.rb +5 -0
- data/lib/rails_execution.rb +24 -0
- metadata +157 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
%h5.section-title.text-muted
|
2
|
+
%i.bi.bi-lightbulb
|
3
|
+
%strong ProTip:
|
4
|
+
%span#all-tips
|
5
|
+
%span.tip
|
6
|
+
Use
|
7
|
+
%code Rails.logger.info 'message...'
|
8
|
+
to add a message into log file
|
9
|
+
- if RailsExecution.configuration.file_upload
|
10
|
+
%span.tip
|
11
|
+
Call
|
12
|
+
%code file('name')
|
13
|
+
to read the file IO
|
14
|
+
:javascript
|
15
|
+
var tipsCount = $('#all-tips span.tip').length;
|
16
|
+
var displayIndex = Math.floor(Math.random() * tipsCount);
|
17
|
+
$($('#all-tips span.tip')[displayIndex]).show();
|
18
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= re_card_content do
|
2
|
+
%h5.section-title Closed
|
3
|
+
%ul.list-unstyled
|
4
|
+
- if @tasks.empty?
|
5
|
+
%li
|
6
|
+
%p.mt-4.fs-5.text-center.text-muted
|
7
|
+
%i.bi.bi-journal-code.me-2
|
8
|
+
Welcome to add new Task
|
9
|
+
- else
|
10
|
+
= render partial: 'task', collection: @tasks
|
11
|
+
|
12
|
+
= re_render_paging(@tasks)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= re_card_content do
|
2
|
+
%h5.section-title Completed
|
3
|
+
%ul.list-unstyled
|
4
|
+
- if @tasks.empty?
|
5
|
+
%li
|
6
|
+
%p.mt-4.fs-5.text-center.text-muted
|
7
|
+
%i.bi.bi-journal-code.me-2
|
8
|
+
Welcome to add new Task
|
9
|
+
- else
|
10
|
+
= render partial: 'task', collection: @tasks
|
11
|
+
|
12
|
+
= re_render_paging(@tasks)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= re_card_content do
|
2
|
+
%h5.section-title In processing
|
3
|
+
%ul.list-unstyled
|
4
|
+
- if @tasks.empty?
|
5
|
+
%li
|
6
|
+
%p.mt-4.fs-5.text-center.text-muted
|
7
|
+
%i.bi.bi-journal-code.me-2
|
8
|
+
Welcome to add new Task
|
9
|
+
- else
|
10
|
+
= render partial: 'task', collection: @tasks
|
11
|
+
|
12
|
+
= re_render_paging(@tasks)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
= re_page_actions do
|
2
|
+
- if can_close_task?(current_task)
|
3
|
+
= link_to 'Close', task_path(current_task), class: 'btn btn-outline-danger', method: :delete, data: { confirm: 'Are you sure?' }
|
4
|
+
- elsif current_task.is_closed? && can_create_task?
|
5
|
+
= link_to 'Re-Open', reopen_task_path(current_task), class: 'btn btn-outline-info', method: :patch, data: { confirm: 'Are you sure?' }
|
6
|
+
|
7
|
+
- if can_edit_task?(current_task)
|
8
|
+
= link_to 'Edit', edit_task_path(current_task), class: 'btn btn-outline-warning me-2'
|
9
|
+
|
10
|
+
.row
|
11
|
+
.col-md-8
|
12
|
+
= re_card_content do
|
13
|
+
%h6.border-bottom.pb-2.mb-0
|
14
|
+
= current_task.title
|
15
|
+
%span.float-end.fs-5.text-muted ##{current_task.id}
|
16
|
+
%p.mt-3.description
|
17
|
+
- if current_task.description.present?
|
18
|
+
#description-markdown= current_task.description
|
19
|
+
#description-html.normal-text
|
20
|
+
- else
|
21
|
+
%p.text-muted.text-center No description
|
22
|
+
|
23
|
+
= render partial: 'attachment_files'
|
24
|
+
= render partial: 'script_content'
|
25
|
+
= render partial: 'comments', locals: { new_comment: @new_comment }
|
26
|
+
|
27
|
+
.col-md-4
|
28
|
+
- if display_owner?(current_task.owner)
|
29
|
+
= re_card_content do
|
30
|
+
%h5.section-title.mb-3 Requestor
|
31
|
+
= render_user_info(current_task.owner, avatar_size: '40x40')
|
32
|
+
|
33
|
+
= render partial: 'reviewers'
|
34
|
+
= render partial: 'status'
|
35
|
+
= render partial: 'actions'
|
36
|
+
= render partial: 'activities'
|
37
|
+
|
38
|
+
= render partial: 'show_scripts'
|
data/config/routes.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RailsExecution::Engine.routes.draw do
|
4
|
+
root to: 'dashboards#home'
|
5
|
+
|
6
|
+
resource :dashboards, only: [] do
|
7
|
+
get :insights
|
8
|
+
end
|
9
|
+
|
10
|
+
resources :tasks do
|
11
|
+
member do
|
12
|
+
patch :reopen
|
13
|
+
patch :reject
|
14
|
+
patch :approve
|
15
|
+
patch :execute
|
16
|
+
end
|
17
|
+
|
18
|
+
collection do
|
19
|
+
get :completed
|
20
|
+
get :closed
|
21
|
+
end
|
22
|
+
|
23
|
+
resources :comments, only: [:create, :update, :destroy]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
module RailsExecution
|
6
|
+
module Generators
|
7
|
+
class FileUploadGenerator < Rails::Generators::Base
|
8
|
+
|
9
|
+
source_root File.join(__dir__, 'templates')
|
10
|
+
|
11
|
+
def copy_config
|
12
|
+
template 'file_uploader.rb', 'lib/rails_execution/file_uploader.rb'
|
13
|
+
template 'file_reader.rb', 'lib/rails_execution/file_reader.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
require 'rails/generators/migration'
|
5
|
+
require 'rails/generators/active_record'
|
6
|
+
require 'active_record'
|
7
|
+
|
8
|
+
module RailsExecution
|
9
|
+
module Generators
|
10
|
+
class InstallGenerator < Rails::Generators::Base
|
11
|
+
|
12
|
+
include ActiveRecord::Generators::Migration
|
13
|
+
|
14
|
+
source_root File.join(__dir__, 'templates')
|
15
|
+
|
16
|
+
def copy_migration
|
17
|
+
migration_template 'install.rb', 'db/migrate/install_execution.rb', migration_version: migration_version
|
18
|
+
end
|
19
|
+
|
20
|
+
def copy_config
|
21
|
+
template 'config.rb', 'config/initializers/rails_execution.rb'
|
22
|
+
end
|
23
|
+
|
24
|
+
def mount_engine
|
25
|
+
route "mount RailsExecution::Engine, at: 'execution'"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def migration_version
|
31
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
RailsExecution.configuration do |config|
|
2
|
+
|
3
|
+
config.solo_mode = true # Without reviewing process
|
4
|
+
|
5
|
+
# Owner display
|
6
|
+
# config.owner_model = 'User'
|
7
|
+
# config.owner_method = :current_user
|
8
|
+
# config.owner_name_method = :fullname
|
9
|
+
# config.owner_avatar = lambda do |owner|
|
10
|
+
# owner.avatar.url(:medium)
|
11
|
+
# end
|
12
|
+
|
13
|
+
# Task control
|
14
|
+
# config.reviewers = lambda do
|
15
|
+
# [{ name: '', id: '' }]
|
16
|
+
# end
|
17
|
+
|
18
|
+
# Accessible check: Default is TRUE
|
19
|
+
# config.task_editable
|
20
|
+
# config.task_closable
|
21
|
+
# config.task_creatable
|
22
|
+
# config.task_approvable
|
23
|
+
# config.task_executable = lambda do |task, user|
|
24
|
+
# YourPolicy.new(user, task).executable?
|
25
|
+
# end
|
26
|
+
|
27
|
+
# Advanced
|
28
|
+
# config.file_upload = true
|
29
|
+
# config.file_uploader = ::RailsExecution::FileUploader
|
30
|
+
# config.file_reader = ::RailsExecution::FileReader
|
31
|
+
# Defaults of acceptable_file_types: .png, .gif, .jpg, .jpeg, .pdf, .csv
|
32
|
+
# config.acceptable_file_types = {
|
33
|
+
# '.jpeg': 'image/jpeg',
|
34
|
+
# '.pdf': 'application/pdf',
|
35
|
+
# '.csv': ['text/csv', 'text/plain'],
|
36
|
+
# }
|
37
|
+
|
38
|
+
# Logger
|
39
|
+
# Using Paperclip
|
40
|
+
# config.logging = lambda do |log_file, task|
|
41
|
+
# YourAttachment.create(file: log_file, task: task)
|
42
|
+
# end
|
43
|
+
# config.logging_files = lambda do |task|
|
44
|
+
# YourAttachment.where(task: task).map { |item| item.file.url }
|
45
|
+
# end
|
46
|
+
# Using ActiveStorage
|
47
|
+
# config.logging = lambda do |log_file, task|
|
48
|
+
# attachment = YourAttachment.create!(task: task)
|
49
|
+
# attachment.file.attach({
|
50
|
+
# io: log_file,
|
51
|
+
# filename: Time.current.strftime('%Y%m%d_%H%M%S.log'),
|
52
|
+
# content_type: 'text/plain',
|
53
|
+
# })
|
54
|
+
# end
|
55
|
+
# config.logging_files = lambda do |task|
|
56
|
+
# ActiveStorage::Current.host = 'localhost:3000'
|
57
|
+
# YourAttachment.where(task: task).map { |item| item.file.url }
|
58
|
+
# end
|
59
|
+
|
60
|
+
# Paging
|
61
|
+
# config.per_page = 30 # Default: 20
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
|
3
|
+
def change
|
4
|
+
create_table :rails_execution_tasks, force: true do |t|
|
5
|
+
t.timestamps null: false
|
6
|
+
t.column :title, :string, null: false
|
7
|
+
t.column :status, :string, null: false
|
8
|
+
t.column :syntax_status, :string, default: 'bad'
|
9
|
+
t.column :description, :text
|
10
|
+
t.column :script, :text
|
11
|
+
t.column :owner_id, :integer
|
12
|
+
t.column :owner_type, :string
|
13
|
+
end
|
14
|
+
add_index :rails_execution_tasks, :status
|
15
|
+
add_index :rails_execution_tasks, [:owner_id, :owner_type], name: :owner_index
|
16
|
+
|
17
|
+
create_table :rails_execution_comments, force: true do |t|
|
18
|
+
t.timestamps null: false
|
19
|
+
t.column :task_id, :integer, null: false
|
20
|
+
t.column :content, :text, null: false
|
21
|
+
t.column :owner_id, :integer
|
22
|
+
t.column :owner_type, :string
|
23
|
+
end
|
24
|
+
add_index :rails_execution_comments, :task_id
|
25
|
+
add_index :rails_execution_comments, [:owner_id, :owner_type], name: :owner_index
|
26
|
+
|
27
|
+
create_table :rails_execution_activities, force: true do |t|
|
28
|
+
t.timestamps null: false
|
29
|
+
t.column :task_id, :integer, null: false
|
30
|
+
t.column :message, :text, null: false
|
31
|
+
t.column :owner_id, :integer
|
32
|
+
t.column :owner_type, :string
|
33
|
+
end
|
34
|
+
add_index :rails_execution_activities, :task_id
|
35
|
+
add_index :rails_execution_activities, [:owner_id, :owner_type], name: :owner_index
|
36
|
+
|
37
|
+
create_table :rails_execution_task_reviews, force: true do |t|
|
38
|
+
t.timestamps null: false
|
39
|
+
t.column :task_id, :integer, null: false
|
40
|
+
t.column :status, :text, null: false
|
41
|
+
t.column :owner_id, :integer
|
42
|
+
t.column :owner_type, :string
|
43
|
+
end
|
44
|
+
add_index :rails_execution_task_reviews, :task_id
|
45
|
+
add_index :rails_execution_task_reviews, [:owner_id, :owner_type], name: :owner_index
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
model_klass = defined?(ApplicationRecord) ? ApplicationRecord : ActiveRecord::Base
|
4
|
+
|
5
|
+
class RailsExecution::AppModel < model_klass
|
6
|
+
|
7
|
+
self.abstract_class = true
|
8
|
+
|
9
|
+
belongs_to :owner, polymorphic: true
|
10
|
+
|
11
|
+
scope :descending, -> { order(id: :desc) }
|
12
|
+
scope :ascending, -> { order(id: :asc) }
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsExecution
|
4
|
+
class Config
|
5
|
+
|
6
|
+
DEFAULT_PER_PAGE = 20
|
7
|
+
DEFAULT_FILE_TYPES = {
|
8
|
+
'.png': 'image/png',
|
9
|
+
'.gif': 'image/gif',
|
10
|
+
'.jpg': 'image/jpg',
|
11
|
+
'.jpeg': 'image/jpeg',
|
12
|
+
'.pdf': 'application/pdf',
|
13
|
+
'.csv': ['text/csv', 'text/plain'],
|
14
|
+
}
|
15
|
+
|
16
|
+
attr_accessor :solo_mode # Solo mode without reviewing process
|
17
|
+
|
18
|
+
# Owner display
|
19
|
+
attr_accessor :owner_model
|
20
|
+
attr_accessor :owner_method
|
21
|
+
attr_accessor :owner_name_method
|
22
|
+
attr_accessor :owner_avatar # lambda
|
23
|
+
|
24
|
+
# Task control
|
25
|
+
attr_accessor :reviewers # lambda
|
26
|
+
|
27
|
+
# Accessible check
|
28
|
+
attr_accessor :task_editable # lambda
|
29
|
+
attr_accessor :task_closable # lambda
|
30
|
+
attr_accessor :task_creatable # lambda
|
31
|
+
attr_accessor :task_approvable # lambda
|
32
|
+
attr_accessor :task_executable # lambda
|
33
|
+
|
34
|
+
# Advanced
|
35
|
+
attr_accessor :file_upload
|
36
|
+
attr_accessor :file_uploader
|
37
|
+
attr_accessor :file_reader
|
38
|
+
attr_accessor :acceptable_file_types
|
39
|
+
|
40
|
+
# Logger
|
41
|
+
attr_accessor :logging # lambda
|
42
|
+
attr_accessor :logging_files
|
43
|
+
|
44
|
+
# Paging
|
45
|
+
attr_accessor :per_page
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
self.solo_mode = true
|
49
|
+
|
50
|
+
self.owner_model = defined?(::User) ? 'User' : nil
|
51
|
+
self.owner_method = :current_user
|
52
|
+
self.owner_name_method = :name
|
53
|
+
self.owner_avatar = ->(_) { nil }
|
54
|
+
|
55
|
+
self.file_upload = false
|
56
|
+
self.acceptable_file_types = DEFAULT_FILE_TYPES
|
57
|
+
self.file_uploader = ::RailsExecution::Files::Uploader
|
58
|
+
self.file_reader = ::RailsExecution::Files::Reader
|
59
|
+
self.per_page = DEFAULT_PER_PAGE
|
60
|
+
self.reviewers = -> { [] }
|
61
|
+
|
62
|
+
self.task_creatable = -> (_user) { true }
|
63
|
+
self.task_editable = -> (_user, _task) { true }
|
64
|
+
self.task_closable = -> (_user, _task) { true }
|
65
|
+
self.task_approvable = -> (_user, _task) { true }
|
66
|
+
self.task_executable = -> (_user, _task) { true }
|
67
|
+
|
68
|
+
self.logging = -> (_log_file, _task) { }
|
69
|
+
self.logging_files = -> (_task) { [] }
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'haml'
|
4
|
+
|
5
|
+
module RailsExecution
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
|
8
|
+
isolate_namespace ::RailsExecution
|
9
|
+
|
10
|
+
initializer 'execution' do |app|
|
11
|
+
if defined?(Sprockets)
|
12
|
+
app.config.assets.precompile << 'executions/base.js'
|
13
|
+
app.config.assets.precompile << 'executions/base.css'
|
14
|
+
app.config.assets.precompile << 'executions/logo.png'
|
15
|
+
app.config.assets.precompile << 'executions/robot.png'
|
16
|
+
app.config.assets.precompile << 'executions/favicon.png'
|
17
|
+
app.config.assets.precompile << 'executions/rejected.png'
|
18
|
+
app.config.assets.precompile << 'executions/approved.png'
|
19
|
+
app.config.assets.precompile << 'executions/bootstrap-icons.woff' # Icons font
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module RailsExecution
|
8
|
+
module Files
|
9
|
+
class Reader
|
10
|
+
|
11
|
+
def initialize(task)
|
12
|
+
@task = task
|
13
|
+
@tempfile_by_name = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
raise ::NotImplementedError, "load_files (Output: { 'file1' => 'file-url', file2 => 'file-url' })"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return: the Tempfile instance
|
21
|
+
def get_file(name)
|
22
|
+
return nil if name.blank?
|
23
|
+
|
24
|
+
@file_url_by_name ||= call
|
25
|
+
file_url = @file_url_by_name[name]
|
26
|
+
return nil if file_url.blank?
|
27
|
+
|
28
|
+
@tempfile_by_name[name] ||= save_to_tempfile(name, file_url)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :task
|
34
|
+
|
35
|
+
def save_to_tempfile(file_name, url)
|
36
|
+
file_extension = URI(url).path.split('.').last
|
37
|
+
file_extension = ".#{file_extension}" if file_extension
|
38
|
+
tmp_file = Tempfile.new([file_name, file_extension])
|
39
|
+
tmp_file.binmode
|
40
|
+
open(url) do |url_file|
|
41
|
+
tmp_file.write(url_file.read)
|
42
|
+
end
|
43
|
+
|
44
|
+
return tmp_file
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsExecution
|
4
|
+
module Files
|
5
|
+
class Uploader
|
6
|
+
|
7
|
+
def initialize(task, inputs, owner: nil)
|
8
|
+
@task = task
|
9
|
+
@files = files_filter(inputs).compact
|
10
|
+
@owner = owner
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
raise ::NotImplementedError, 'File uploader'
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :task
|
20
|
+
attr_reader :files
|
21
|
+
attr_reader :owner
|
22
|
+
|
23
|
+
def files_filter(inputs)
|
24
|
+
inputs.map do |_multiple, attachment|
|
25
|
+
next if attachment['name'].blank?
|
26
|
+
next if attachment['file'].blank?
|
27
|
+
next unless attachment['file'].content_type.in?(acceptable_file_types)
|
28
|
+
|
29
|
+
OpenStruct.new({
|
30
|
+
name: attachment['name'],
|
31
|
+
file: attachment['file'],
|
32
|
+
})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def acceptable_file_types
|
37
|
+
::RailsExecution.configuration.acceptable_file_types.values
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module RailsExecution
|
2
|
+
module Services
|
3
|
+
class Approvement
|
4
|
+
|
5
|
+
def initialize(task, reviewer: nil)
|
6
|
+
@task = task || (raise 'task is blank')
|
7
|
+
@reviewer = reviewer || (raise 'reviewer is blank')
|
8
|
+
end
|
9
|
+
|
10
|
+
def approve
|
11
|
+
task.update(status: :approved)
|
12
|
+
review.update(status: :approved)
|
13
|
+
add_activity('approved')
|
14
|
+
end
|
15
|
+
|
16
|
+
def reject
|
17
|
+
review.update(status: :rejected)
|
18
|
+
task.update(status: :rejected) if make_task_to_rejected?
|
19
|
+
add_activity('rejected')
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :task
|
25
|
+
attr_reader :reviewer
|
26
|
+
|
27
|
+
def review
|
28
|
+
@review ||= task.task_reviews.find_or_initialize_by(owner: reviewer)
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_activity(status)
|
32
|
+
task.activities.create(owner: reviewer, message: "#{status.titleize} the task")
|
33
|
+
end
|
34
|
+
|
35
|
+
def make_task_to_rejected?
|
36
|
+
task.task_reviews.is_approved.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module RailsExecution
|
2
|
+
module Services
|
3
|
+
class Execution
|
4
|
+
|
5
|
+
def self.call(task_id)
|
6
|
+
self.new(::RailsExecution::Task.find(task_id)).call
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(task)
|
10
|
+
@task = task
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
return if bad_syntax?
|
15
|
+
|
16
|
+
build_execution_file!
|
17
|
+
load_execution_file!
|
18
|
+
is_successful = false
|
19
|
+
begin
|
20
|
+
setup_logger!
|
21
|
+
execute_class!
|
22
|
+
is_successful = true
|
23
|
+
rescue
|
24
|
+
is_successful = false
|
25
|
+
ensure
|
26
|
+
restore_logger!
|
27
|
+
storing_log_file!
|
28
|
+
return is_successful
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :task
|
35
|
+
attr_reader :class_name
|
36
|
+
|
37
|
+
def bad_syntax?
|
38
|
+
!::RailsExecution::Services::SyntaxChecker.new(task.script).call
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_execution_file!
|
42
|
+
@class_name = "RailsExecutionID#{task.id}Time#{task.updated_at.to_i}Executor"
|
43
|
+
ruby_code = <<~RUBY
|
44
|
+
class #{class_name} < ::RailsExecution::Services::Executor
|
45
|
+
def call
|
46
|
+
task.with_lock do
|
47
|
+
#{task.script}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
RUBY
|
52
|
+
|
53
|
+
@file = ::Tempfile.new([class_name, '.rb'])
|
54
|
+
@file.binmode
|
55
|
+
@file.write(ruby_code)
|
56
|
+
@file.flush
|
57
|
+
@file
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_execution_file!
|
61
|
+
load @file.path
|
62
|
+
end
|
63
|
+
|
64
|
+
def setup_logger!
|
65
|
+
@model_logger = ::ActiveRecord::Base.logger
|
66
|
+
@rails_logger = ::Rails.logger
|
67
|
+
@tempfile = ::Tempfile.new(["rails_execution_#{Time.current.strftime('%Y%m%d_%H%M%S')}", '.log'])
|
68
|
+
::ActiveRecord::Base.logger = ::Logger.new(@tempfile.path)
|
69
|
+
::Rails.logger = ::ActiveRecord::Base.logger
|
70
|
+
end
|
71
|
+
|
72
|
+
def execute_class!
|
73
|
+
class_name.constantize.new(task).call
|
74
|
+
end
|
75
|
+
|
76
|
+
def storing_log_file!
|
77
|
+
::RailsExecution.configuration.logging.call(@tempfile, task)
|
78
|
+
end
|
79
|
+
|
80
|
+
def restore_logger!
|
81
|
+
::ActiveRecord::Base.logger = @model_logger
|
82
|
+
::Rails.logger = @rails_logger
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|