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,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