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.
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,278 @@
1
+ .nav-link {
2
+ font-size: 14px;
3
+ }
4
+
5
+ .fs-13, .form-label {
6
+ font-size: 13px;
7
+ }
8
+
9
+ .section-title {
10
+ font-size: 13px;
11
+ font-weight: 500;
12
+ }
13
+
14
+ #reviewers {
15
+ max-height: 150px;
16
+ overflow-y: auto;
17
+
18
+ input {
19
+ display: none;
20
+ &:checked + label:after {
21
+ content: '✓';
22
+ display: block;
23
+ width: 20px;
24
+ height: 20px;
25
+ position: absolute;
26
+ left: 0;
27
+ top: 0;
28
+ }
29
+
30
+ &:checked + label {
31
+ background: lightgray;
32
+ }
33
+ }
34
+
35
+ label {
36
+ position: relative;
37
+ outline: none;
38
+ user-select: none;
39
+ width: 100%;
40
+ font-size: 13px;
41
+ padding: 4px 4px 4px 15px;
42
+ cursor: pointer;
43
+ }
44
+ }
45
+
46
+ .select2-container--default .select2-selection--multiple,
47
+ .select2-container--default.select2-container--focus .select2-selection--multiple {
48
+ border: 1px solid #cbcbcb !important;
49
+ }
50
+
51
+ #description-markdown {
52
+ display: none;
53
+ }
54
+
55
+ #task-detail {
56
+ width: 100%;
57
+ position: relative;
58
+
59
+ &.fullscreen {
60
+ position: fixed;
61
+ top: 0;
62
+ left: 0;
63
+ width: 100%;
64
+ height: 100%;
65
+ z-index: 100;
66
+
67
+ #script-editor {
68
+ height: 100% !important;
69
+ max-height: 100% !important;
70
+ }
71
+ a#click2detail {
72
+ display: none;
73
+ }
74
+
75
+ a#click2full {
76
+ i.bi-arrows-fullscreen {
77
+ display: none;
78
+ }
79
+ i.bi-arrows-angle-contract {
80
+ display: block;
81
+ }
82
+ }
83
+ }
84
+
85
+ #script-editor.collapsed {
86
+ max-height: 300px;
87
+ }
88
+ a#click2detail {
89
+ position: absolute;
90
+ margin-top: -40px;
91
+ margin-left: calc(50% - 100px);
92
+ width: 200px;
93
+ color: #000;
94
+ background-color: rgba(255, 255, 255, 0.5);
95
+ border-radius: 30px;
96
+ padding: 5px;
97
+ text-align: center;
98
+ font-size: 12px;
99
+ }
100
+
101
+ a {
102
+ opacity: 0;
103
+ transition: all 0.3s
104
+ }
105
+
106
+ &:hover { a { opacity: 1; } }
107
+ }
108
+
109
+ #script-editor-container {
110
+ position: relative;
111
+ padding-top: 15px;
112
+ background-color: #002b36;
113
+ border-radius: 5px;
114
+ overflow-y: hidden;
115
+
116
+ #click2full {
117
+ position: absolute;
118
+ top: 10px;
119
+ right: 10px;
120
+ }
121
+
122
+ &.fullscreen {
123
+ position: fixed;
124
+ top: 0;
125
+ left: 0;
126
+ width: 100%;
127
+ height: 100%;
128
+
129
+ #script-editor {
130
+ height: 100%;
131
+ }
132
+ }
133
+ }
134
+
135
+ #script-editor {
136
+ position: relative;
137
+ width: 100%;
138
+ height: 700px;
139
+ font-size: 13px;
140
+ }
141
+
142
+ .user-info {
143
+ span {
144
+ font-size: 13px;
145
+ }
146
+ }
147
+
148
+ .hidden {
149
+ display: none;
150
+ }
151
+
152
+ small, .small {
153
+ font-size: 10px !important;
154
+ }
155
+
156
+ .media-comment {
157
+ position: relative;
158
+
159
+ img {
160
+ max-width: 100%;
161
+ }
162
+
163
+ a.edit-comment {
164
+ position: absolute;
165
+ right: 10px;
166
+ top: 7px;
167
+ }
168
+
169
+ &.editing {
170
+ .edit-comment-form {
171
+ display: block !important;
172
+ }
173
+ .review-comment-content {
174
+ display: none !important;
175
+ }
176
+ }
177
+ }
178
+
179
+ .editor-toolbar {
180
+ text-align: right;
181
+ button {
182
+ color: #808080;
183
+ }
184
+ }
185
+
186
+ a {
187
+ text-decoration: none;
188
+ }
189
+
190
+ #activities-section {
191
+ ul {
192
+ overflow-y: auto;
193
+ max-height: 400px;
194
+ box-sizing: border-box;
195
+ li {
196
+ width: 100%;
197
+ .activitiy-time {
198
+ .fs13px {
199
+ font-size: 13px;
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ nav.execution-paging {
207
+ margin-top: 30px;
208
+ a.page-link {
209
+ background-color: initial;
210
+ border: none;
211
+ color: #333;
212
+ outline: none;
213
+ }
214
+ .disabled>.page-link, .page-link.disabled {
215
+ background-color: initial;
216
+ }
217
+ .active>.page-link, .page-link.active {
218
+ background-color: #0d6efd;
219
+ color: #FFF;
220
+ border-radius: 7px;
221
+ }
222
+ }
223
+
224
+ hr.smoothly {
225
+ border: 0;
226
+ height: 1px;
227
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
228
+ }
229
+
230
+ ul li{
231
+ &.task-is-rejected {
232
+ background: asset-url('executions/rejected.png') no-repeat;
233
+ background-position: calc(100% - 70px) center;
234
+ }
235
+
236
+ &.task-is-approved {
237
+ background: asset-url('executions/approved.png') no-repeat;
238
+ background-position: calc(100% - 70px) center;
239
+ }
240
+ }
241
+
242
+ #insights_chart {
243
+ width: 100%;
244
+ }
245
+
246
+ a#click2full {
247
+ position: absolute;
248
+ z-index: 10;
249
+ top: 10px;
250
+ right: 20px;
251
+ color: #000;
252
+ background-color: rgba(255, 255, 255, 0.5);
253
+ border-radius: 30px;
254
+ padding: 5px 15px;
255
+ text-align: center;
256
+ font-size: 10px;
257
+
258
+ i.bi-arrows-fullscreen {
259
+ display: block;
260
+ }
261
+ i.bi-arrows-angle-contract {
262
+ display: none;
263
+ }
264
+ }
265
+
266
+ .normal-text {
267
+ font-size: 0.8em;
268
+ }
269
+
270
+ #description-html {
271
+ max-height: 400px;
272
+ overflow-y: auto;
273
+ }
274
+ #all-tips {
275
+ .tip {
276
+ display: none;
277
+ }
278
+ }
@@ -0,0 +1 @@
1
+ .select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{background-color:transparent;border:none;font-size:1em}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline;list-style:none;padding:0}.select2-container .select2-selection--multiple .select2-selection__clear{background-color:transparent;border:none;font-size:1em}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;margin-left:5px;padding:0;max-width:100%;resize:none;height:18px;vertical-align:bottom;font-family:sans-serif;overflow:hidden;word-break:keep-all}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option--selectable{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;height:26px;margin-right:20px;padding-right:0px}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;padding-bottom:5px;padding-right:5px;position:relative}.select2-container--default .select2-selection--multiple.select2-selection--clearable{padding-right:25px}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;font-weight:bold;height:20px;margin-right:10px;margin-top:5px;position:absolute;right:0;padding:1px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:inline-block;margin-left:5px;margin-top:5px;padding:0;padding-left:20px;position:relative;max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap}.select2-container--default .select2-selection--multiple .select2-selection__choice__display{cursor:default;padding-left:2px;padding-right:5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{background-color:transparent;border:none;border-right:1px solid #aaa;border-top-left-radius:4px;border-bottom-left-radius:4px;color:#999;cursor:pointer;font-size:1em;font-weight:bold;padding:0 4px;position:absolute;left:0;top:0}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover,.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:focus{background-color:#f1f1f1;color:#333;outline:none}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__display{padding-left:5px;padding-right:2px}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{border-left:1px solid #aaa;border-right:none;border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:4px;border-bottom-right-radius:4px}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__clear{float:left;margin-left:10px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--group{padding:0}.select2-container--default .select2-results__option--disabled{color:#999}.select2-container--default .select2-results__option--selected{background-color:#ddd}.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;height:26px;margin-right:20px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0;padding-bottom:5px;padding-right:5px}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;display:inline-block;margin-left:5px;margin-top:5px;padding:0}.select2-container--classic .select2-selection--multiple .select2-selection__choice__display{cursor:default;padding-left:2px;padding-right:5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{background-color:transparent;border:none;border-top-left-radius:4px;border-bottom-left-radius:4px;color:#888;cursor:pointer;font-size:1em;font-weight:bold;padding:0 4px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555;outline:none}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__display{padding-left:5px;padding-right:2px}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:4px;border-bottom-right-radius:4px}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option--group{padding:0}.select2-container--classic .select2-results__option--disabled{color:grey}.select2-container--classic .select2-results__option--highlighted.select2-results__option--selectable{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsExecution
4
+ class BaseController < ::ApplicationController
5
+
6
+ clear_helpers
7
+
8
+ include RailsExecution::BaseHelper
9
+ include RailsExecution::PolicyHelper
10
+ helper RailsExecution::RenderingHelper
11
+ helper RailsExecution::PolicyHelper
12
+
13
+ layout 'execution'
14
+
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsExecution
4
+ class CommentsController < ::RailsExecution::BaseController
5
+
6
+ def create
7
+ @new_comment = current_task.comments.new(owner: current_owner, content: params.dig(:comment, :content))
8
+ if @new_comment.save
9
+ current_task.activities.create(owner: current_owner, message: "Added a comment: #{@new_comment.content.truncate(30)}")
10
+ else
11
+ @alert = "Your comment can't adding!"
12
+ end
13
+ end
14
+
15
+ def update
16
+ @comment = current_comment
17
+ @comment.update(content: params.dig(:comment, :content))
18
+ respond_to(&:js)
19
+ end
20
+
21
+ private
22
+
23
+ def current_task
24
+ @current_task ||= RailsExecution::Task.find(params[:task_id])
25
+ end
26
+ helper_method :current_task
27
+
28
+ def current_comment
29
+ @comment ||= current_task.comments.find(params[:id])
30
+ end
31
+ helper_method :current_comment
32
+
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsExecution
4
+ class DashboardsController < ::RailsExecution::BaseController
5
+
6
+ def home
7
+ @in_processing_count = ::RailsExecution::Task.processing.count
8
+ @tasks_count = ::RailsExecution::Task.group(:status).count
9
+ end
10
+
11
+ def insights
12
+ @tasks_by_week = ::RailsExecution::Task
13
+ .where(created_at: 10.weeks.ago.beginning_of_day..Time.current)
14
+ .group('YEAR(created_at)', 'WEEK(created_at)')
15
+ .count
16
+ respond_to(&:json)
17
+ end
18
+
19
+ private
20
+
21
+ def show_insights_chart?
22
+ ::RailsExecution::Task.exists?(created_at: 100.year.ago..1.weeks.ago)
23
+ end
24
+ helper_method :show_insights_chart?
25
+
26
+ end
27
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsExecution
4
+ class TasksController < ::RailsExecution::BaseController
5
+
6
+ def index
7
+ paging = ::RailsExecution::Services::Paging.new(page: params[:page], per_page: params[:per_page])
8
+ processing_tasks = ::RailsExecution::Task.processing.descending.includes(:owner)
9
+ @tasks = paging.call(processing_tasks)
10
+ end
11
+
12
+ def new
13
+ raise(::RailsExecution::AccessDeniedError, 'Create task') unless can_create_task?
14
+
15
+ @task = ::RailsExecution::Task.new
16
+ end
17
+
18
+ def create
19
+ raise(::RailsExecution::AccessDeniedError, 'Create task') unless can_create_task?
20
+
21
+ @task = ::RailsExecution::Task.new({
22
+ status: :created,
23
+ owner_id: current_owner&.id,
24
+ owner_type: ::RailsExecution.configuration.owner_model.to_s,
25
+ title: params.dig(:task, :title),
26
+ description: params.dig(:task, :description),
27
+ script: params.dig(:task, :script),
28
+ })
29
+ @task.assign_reviewers(params.dig(:task, :task_review_ids).to_a)
30
+ @task.syntax_status = ::RailsExecution::Services::SyntaxChecker.new(@task.script).call ? 'good' : 'bad'
31
+
32
+ if @task.save
33
+ @task.add_files(params[:attachments]&.permit!.to_h, current_owner) if ::RailsExecution.configuration.file_upload
34
+ flash[:notice] = 'Create the request is successful!'
35
+ redirect_to action: :index
36
+ else
37
+ render action: :new
38
+ end
39
+ end
40
+
41
+ def show
42
+ end
43
+
44
+ def destroy
45
+ unless can_close_task?(current_task)
46
+ flash[:alert] = "You can't close this Task right now"
47
+ redirect_to(:back) and return
48
+ end
49
+
50
+ if current_task.update(status: :closed)
51
+ current_task.activities.create(owner: current_owner, message: 'Closed the task')
52
+ redirect_to(action: :show) and return
53
+ else
54
+ flash[:alert] = "Has problem when close this Task: #{current_task.errors.full_messages.join(', ')}"
55
+ redirect_to(:back) and return
56
+ end
57
+ end
58
+
59
+ def edit
60
+ raise(::RailsExecution::AccessDeniedError, 'Edit task') unless can_edit_task?(current_task)
61
+
62
+ @task = current_task
63
+ end
64
+
65
+ def update
66
+ raise(::RailsExecution::AccessDeniedError, 'Edit task') unless can_edit_task?(current_task)
67
+
68
+ @task = current_task
69
+ update_data = {
70
+ title: params.dig(:task, :title),
71
+ description: params.dig(:task, :description),
72
+ }
73
+
74
+ update_data[:script] = params.dig(:task, :script) if @task.in_processing?
75
+ @task.assign_reviewers(params.dig(:task, :task_review_ids).to_a)
76
+ @task.syntax_status = ::RailsExecution::Services::SyntaxChecker.new(update_data[:script]).call ? 'good' : 'bad'
77
+
78
+ if @task.update(update_data)
79
+ @task.add_files(params[:attachments]&.permit!.to_h, current_owner) if ::RailsExecution.configuration.file_upload
80
+ @task.activities.create(owner: current_owner, message: 'Updated the Task')
81
+ redirect_to action: :show
82
+ else
83
+ render action: :edit
84
+ end
85
+ end
86
+
87
+ def completed
88
+ paging = ::RailsExecution::Services::Paging.new(page: params[:page], per_page: params[:per_page])
89
+ completed_tasks = ::RailsExecution::Task.is_completed.descending.includes(:owner)
90
+ @tasks = paging.call(completed_tasks)
91
+ end
92
+
93
+ def closed
94
+ paging = ::RailsExecution::Services::Paging.new(page: params[:page], per_page: params[:per_page])
95
+ closed_tasks = ::RailsExecution::Task.is_closed.descending.includes(:owner)
96
+ @tasks = paging.call(closed_tasks)
97
+ end
98
+
99
+ def reopen
100
+ if current_task.update(status: :created)
101
+ current_task.activities.create(owner: current_owner, message: 'Re-opened the Task')
102
+ flash[:notice] = 'Your task is re-opened'
103
+ else
104
+ flash[:alert] = "Re-open is failed: #{current_task.errors.full_messages.join(', ')}"
105
+ end
106
+ redirect_to action: :show
107
+ end
108
+
109
+ def reject
110
+ if ::RailsExecution::Services::Approvement.new(current_task, reviewer: current_owner).reject
111
+ flash[:notice] = 'Your decision is updated!'
112
+ else
113
+ flash[:alert] = "Your decision is can't update!"
114
+ end
115
+ redirect_to action: :show
116
+ end
117
+
118
+ def approve
119
+ if ::RailsExecution::Services::Approvement.new(current_task, reviewer: current_owner).approve
120
+ flash[:notice] = 'Your decision is updated!'
121
+ else
122
+ flash[:alert] = "Your decision is can't update!"
123
+ end
124
+ redirect_to action: :show
125
+ end
126
+
127
+ def execute
128
+ unless can_execute_task?(current_task)
129
+ flash[:alert] = "This task can't execute: #{how_to_executable(current_task)}"
130
+ redirect_to(action: :show) and return
131
+ end
132
+
133
+ execute_service = ::RailsExecution::Services::Execution.new(current_task)
134
+ if execute_service.call
135
+ current_task.update(status: :completed)
136
+ current_task.activities.create(owner: current_owner, message: 'Execute: The task is completed')
137
+ flash[:notice] = 'This task is executed'
138
+ else
139
+ flash[:alert] = "Sorry!!! This task can't execute right now"
140
+ end
141
+
142
+ redirect_to(action: :show)
143
+ end
144
+
145
+ private
146
+
147
+ def current_task
148
+ @current_task ||= ::RailsExecution::Task.find_by(id: params[:id])
149
+ end
150
+ helper_method :current_task
151
+
152
+ def reviewers
153
+ @reviewers ||= ::RailsExecution.configuration.reviewers.call.map do |reviewer|
154
+ ::OpenStruct.new(reviewer)
155
+ end
156
+ end
157
+ helper_method :reviewers
158
+
159
+ def task_logs
160
+ @task_logs ||= ::RailsExecution.configuration.logging_files.call(current_task).select(&:present?)
161
+ end
162
+ helper_method :task_logs
163
+
164
+ def task_attachment_files
165
+ @task_attachment_files ||= ::RailsExecution.configuration.file_reader.new(current_task).call
166
+ end
167
+ helper_method :task_attachment_files
168
+
169
+ def reviewing_accounts
170
+ @reviewing_accounts ||= current_task.task_reviews
171
+ end
172
+ helper_method :reviewing_accounts
173
+
174
+ end
175
+ end
@@ -0,0 +1,14 @@
1
+ module RailsExecution
2
+ module BaseHelper
3
+ include ActionView::Helpers::AssetUrlHelper
4
+
5
+ def current_owner
6
+ return nil if in_solo_mode?
7
+ return @current_owner if defined?(@current_owner)
8
+ return nil if ::RailsExecution.configuration.owner_method.blank?
9
+
10
+ @current_owner = self.send(::RailsExecution.configuration.owner_method)
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,64 @@
1
+ module RailsExecution
2
+ module PolicyHelper
3
+
4
+ def display_owner?(owner)
5
+ owner.present?
6
+ end
7
+
8
+ def display_decide?(task)
9
+ task &&
10
+ current_owner &&
11
+ task.owner &&
12
+ current_owner != task.owner &&
13
+ task.syntax_status_good? &&
14
+ !task.is_completed?
15
+ end
16
+
17
+ def display_reviewers?(task)
18
+ task.task_reviews.exists?
19
+ end
20
+
21
+ def show_form_sidebar?(task)
22
+ return false unless (task.new_record? || task.in_processing?)
23
+
24
+ [
25
+ ::RailsExecution.configuration.file_upload,
26
+ !in_solo_mode? && ::RailsExecution.configuration.reviewers.present?,
27
+ ].any?
28
+ end
29
+
30
+ def in_solo_mode?
31
+ ::RailsExecution.configuration.solo_mode
32
+ end
33
+
34
+ def can_create_task?
35
+ ::RailsExecution.configuration.task_creatable.call(current_owner)
36
+ end
37
+
38
+ def can_edit_task?(task)
39
+ ::RailsExecution.configuration.task_editable.call(current_owner, task)
40
+ end
41
+
42
+ def can_close_task?(task)
43
+ task.in_processing? && ::RailsExecution.configuration.task_closable.call(current_owner, task)
44
+ end
45
+
46
+ def can_execute_task?(task)
47
+ how_to_executable(task).blank?
48
+ end
49
+
50
+ def how_to_executable(task)
51
+ return 'Script is empty' if task.script.blank?
52
+ return "Task is closed" if task.is_closed?
53
+ return "It's bad Syntax" if task.syntax_status_bad?
54
+
55
+ unless in_solo_mode?
56
+ return 'This task is not approved' unless task.is_approved?
57
+ return 'No approval from any reviewer' if task.task_reviews.is_approved.empty?
58
+ end
59
+
60
+ return "Can't executable by app policy" unless ::RailsExecution.configuration.task_executable.call(current_owner, task)
61
+ end
62
+
63
+ end
64
+ end