rails_execution 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +8 -0
  4. data/Gemfile.lock +129 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +35 -0
  7. data/Rakefile +3 -0
  8. data/app/assets/images/executions/approved.png +0 -0
  9. data/app/assets/images/executions/favicon.png +0 -0
  10. data/app/assets/images/executions/logo.png +0 -0
  11. data/app/assets/images/executions/rejected.png +0 -0
  12. data/app/assets/images/executions/robot.png +0 -0
  13. data/app/assets/javascripts/executions/ace.min.js +1 -0
  14. data/app/assets/javascripts/executions/ace.modes.ruby.min.js +1 -0
  15. data/app/assets/javascripts/executions/ace.theme-solarized_dark.min.js +1 -0
  16. data/app/assets/javascripts/executions/base.js +1 -0
  17. data/app/assets/javascripts/executions/bootstrap.5.2.1.min.js +6 -0
  18. data/app/assets/javascripts/executions/chart.min.js +13 -0
  19. data/app/assets/javascripts/executions/comments.js +43 -0
  20. data/app/assets/javascripts/executions/easymde.min.js +7 -0
  21. data/app/assets/javascripts/executions/events.js +8 -0
  22. data/app/assets/javascripts/executions/highlight.min.js +1198 -0
  23. data/app/assets/javascripts/executions/highlight.ruby.min.js +51 -0
  24. data/app/assets/javascripts/executions/jquery-3.6.1.min.js +2 -0
  25. data/app/assets/javascripts/executions/marked.min.js +6 -0
  26. data/app/assets/javascripts/executions/rails.js +565 -0
  27. data/app/assets/javascripts/executions/select2.min.js +2 -0
  28. data/app/assets/stylesheets/executions/base.css +3 -0
  29. data/app/assets/stylesheets/executions/bootstrap-icons.css +1869 -0
  30. data/app/assets/stylesheets/executions/comments.css +14 -0
  31. data/app/assets/stylesheets/executions/easymde.min.css +7 -0
  32. data/app/assets/stylesheets/executions/fonts.css +4 -0
  33. data/app/assets/stylesheets/executions/highlight.min.css +9 -0
  34. data/app/assets/stylesheets/executions/modify.scss +278 -0
  35. data/app/assets/stylesheets/executions/select2.min.css +1 -0
  36. data/app/controllers/rails_execution/base_controller.rb +16 -0
  37. data/app/controllers/rails_execution/comments_controller.rb +34 -0
  38. data/app/controllers/rails_execution/dashboards_controller.rb +27 -0
  39. data/app/controllers/rails_execution/tasks_controller.rb +175 -0
  40. data/app/helpers/rails_execution/base_helper.rb +14 -0
  41. data/app/helpers/rails_execution/policy_helper.rb +64 -0
  42. data/app/helpers/rails_execution/rendering_helper.rb +94 -0
  43. data/app/models/rails_execution/activity.rb +8 -0
  44. data/app/models/rails_execution/comment.rb +8 -0
  45. data/app/models/rails_execution/task.rb +71 -0
  46. data/app/models/rails_execution/task_review.rb +14 -0
  47. data/app/views/layouts/execution.html.haml +16 -0
  48. data/app/views/rails_execution/comments/_comment.html.haml +23 -0
  49. data/app/views/rails_execution/comments/create.js.haml +6 -0
  50. data/app/views/rails_execution/comments/update.js.haml +2 -0
  51. data/app/views/rails_execution/dashboards/charts/_insights.html.haml +27 -0
  52. data/app/views/rails_execution/dashboards/home.html.haml +24 -0
  53. data/app/views/rails_execution/dashboards/insights.json.jbuilder +8 -0
  54. data/app/views/rails_execution/shared/_flash.html.haml +7 -0
  55. data/app/views/rails_execution/shared/_header.html.haml +13 -0
  56. data/app/views/rails_execution/shared/_paging.html.haml +35 -0
  57. data/app/views/rails_execution/tasks/_actions.html.haml +12 -0
  58. data/app/views/rails_execution/tasks/_activities.html.haml +22 -0
  59. data/app/views/rails_execution/tasks/_attachment_file_fields.html.haml +3 -0
  60. data/app/views/rails_execution/tasks/_attachment_files.html.haml +12 -0
  61. data/app/views/rails_execution/tasks/_comments.html.haml +15 -0
  62. data/app/views/rails_execution/tasks/_form.html.haml +55 -0
  63. data/app/views/rails_execution/tasks/_form_scripts.html.haml +34 -0
  64. data/app/views/rails_execution/tasks/_new_comment.html.haml +8 -0
  65. data/app/views/rails_execution/tasks/_reviewers.html.haml +23 -0
  66. data/app/views/rails_execution/tasks/_script_content.html.haml +15 -0
  67. data/app/views/rails_execution/tasks/_show_scripts.html.haml +27 -0
  68. data/app/views/rails_execution/tasks/_status.html.haml +19 -0
  69. data/app/views/rails_execution/tasks/_task.html.haml +18 -0
  70. data/app/views/rails_execution/tasks/_tips.html.haml +18 -0
  71. data/app/views/rails_execution/tasks/closed.html.haml +12 -0
  72. data/app/views/rails_execution/tasks/completed.html.haml +12 -0
  73. data/app/views/rails_execution/tasks/edit.html.haml +3 -0
  74. data/app/views/rails_execution/tasks/index.html.haml +12 -0
  75. data/app/views/rails_execution/tasks/new.html.haml +3 -0
  76. data/app/views/rails_execution/tasks/show.html.haml +38 -0
  77. data/config/routes.rb +26 -0
  78. data/lib/generators/rails_execution/file_upload_generator.rb +18 -0
  79. data/lib/generators/rails_execution/install_generator.rb +36 -0
  80. data/lib/generators/rails_execution/templates/config.rb.tt +63 -0
  81. data/lib/generators/rails_execution/templates/file_reader.rb.tt +14 -0
  82. data/lib/generators/rails_execution/templates/file_uploader.rb.tt +11 -0
  83. data/lib/generators/rails_execution/templates/install.rb.tt +48 -0
  84. data/lib/rails_execution/app_model.rb +14 -0
  85. data/lib/rails_execution/config.rb +73 -0
  86. data/lib/rails_execution/engine.rb +24 -0
  87. data/lib/rails_execution/error.rb +6 -0
  88. data/lib/rails_execution/files/reader.rb +49 -0
  89. data/lib/rails_execution/files/uploader.rb +42 -0
  90. data/lib/rails_execution/services/approvement.rb +41 -0
  91. data/lib/rails_execution/services/execution.rb +87 -0
  92. data/lib/rails_execution/services/executor.rb +24 -0
  93. data/lib/rails_execution/services/paging.rb +35 -0
  94. data/lib/rails_execution/services/syntax_checker.rb +38 -0
  95. data/lib/rails_execution/version.rb +5 -0
  96. data/lib/rails_execution.rb +24 -0
  97. 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,3 @@
1
+ = re_card_content do
2
+ %h6.border-bottom.pb-2.mb-0 Edit request
3
+ = render partial: 'form', locals: { task: @task }
@@ -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,3 @@
1
+ = re_card_content do
2
+ %h6.border-bottom.pb-2.mb-0 New request
3
+ = render partial: 'form', locals: { task: @task }
@@ -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,14 @@
1
+ module RailsExecution
2
+ class FileReader < RailsExecution::Files::Reader
3
+
4
+ # Output: {
5
+ # 'File1' => 'url',
6
+ # 'FileZ' => 'url',
7
+ # }
8
+ def call
9
+ # YourAttachment.where(task: task).pluck(:name, :url).to_h
10
+ return {}
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module RailsExecution
2
+ class FileUploader < RailsExecution::Files::Uploader
3
+
4
+ def call
5
+ files.each do |item|
6
+ # YourAttachment.create!(task: task, name: item.name, file: item.file)
7
+ end
8
+ end
9
+
10
+ end
11
+ 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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsExecution
4
+ class Error < StandardError; end
5
+ class AccessDeniedError < Error; end
6
+ 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