rails_execution 0.1.1
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.
- 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
|