command_proposal 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +77 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/command_proposal_manifest.js +1 -0
  6. data/app/assets/javascripts/command_proposal/_codemirror.js +9814 -0
  7. data/app/assets/javascripts/command_proposal/_helpers.js +9 -0
  8. data/app/assets/javascripts/command_proposal/codemirror-addon-searchcursor.js +296 -0
  9. data/app/assets/javascripts/command_proposal/codemirror-keymap-sublime.js +720 -0
  10. data/app/assets/javascripts/command_proposal/codemirror-mode-ruby.js +303 -0
  11. data/app/assets/javascripts/command_proposal/console.js +195 -0
  12. data/app/assets/javascripts/command_proposal/feed.js +51 -0
  13. data/app/assets/javascripts/command_proposal/terminal.js +40 -0
  14. data/app/assets/javascripts/command_proposal.js +1 -0
  15. data/app/assets/stylesheets/command_proposal/_variables.scss +0 -0
  16. data/app/assets/stylesheets/command_proposal/codemirror-rubyblue.scss +27 -0
  17. data/app/assets/stylesheets/command_proposal/codemirror.scss +367 -0
  18. data/app/assets/stylesheets/command_proposal/command_proposal.scss +1 -0
  19. data/app/assets/stylesheets/command_proposal/components.scss +31 -0
  20. data/app/assets/stylesheets/command_proposal/containers.scss +4 -0
  21. data/app/assets/stylesheets/command_proposal/icons.scss +12 -0
  22. data/app/assets/stylesheets/command_proposal/tables.scss +76 -0
  23. data/app/assets/stylesheets/command_proposal/terminal.scss +72 -0
  24. data/app/assets/stylesheets/command_proposal.scss +5 -0
  25. data/app/controllers/command_proposal/engine_controller.rb +6 -0
  26. data/app/controllers/command_proposal/iterations_controller.rb +83 -0
  27. data/app/controllers/command_proposal/runner_controller.rb +86 -0
  28. data/app/controllers/command_proposal/tasks_controller.rb +97 -0
  29. data/app/helpers/command_proposal/application_helper.rb +58 -0
  30. data/app/helpers/command_proposal/icons_helper.rb +15 -0
  31. data/app/helpers/command_proposal/params_helper.rb +63 -0
  32. data/app/helpers/command_proposal/permissions_helper.rb +42 -0
  33. data/app/jobs/command_proposal/application_job.rb +4 -0
  34. data/app/jobs/command_proposal/command_runner_job.rb +11 -0
  35. data/app/models/command_proposal/comment.rb +14 -0
  36. data/app/models/command_proposal/iteration.rb +78 -0
  37. data/app/models/command_proposal/service/external_belong.rb +48 -0
  38. data/app/models/command_proposal/service/json_wrapper.rb +18 -0
  39. data/app/models/command_proposal/service/proposal_presenter.rb +39 -0
  40. data/app/models/command_proposal/task.rb +106 -0
  41. data/app/views/command_proposal/tasks/_console_show.html.erb +44 -0
  42. data/app/views/command_proposal/tasks/_function_show.html.erb +54 -0
  43. data/app/views/command_proposal/tasks/_lines.html.erb +8 -0
  44. data/app/views/command_proposal/tasks/_module_show.html.erb +33 -0
  45. data/app/views/command_proposal/tasks/_past_iterations_list.html.erb +20 -0
  46. data/app/views/command_proposal/tasks/_task_detail_table.html.erb +31 -0
  47. data/app/views/command_proposal/tasks/_task_show.html.erb +55 -0
  48. data/app/views/command_proposal/tasks/error.html.erb +4 -0
  49. data/app/views/command_proposal/tasks/form.html.erb +64 -0
  50. data/app/views/command_proposal/tasks/index.html.erb +44 -0
  51. data/app/views/command_proposal/tasks/show.html.erb +10 -0
  52. data/config/routes.rb +11 -0
  53. data/lib/command_proposal/configuration.rb +41 -0
  54. data/lib/command_proposal/engine.rb +6 -0
  55. data/lib/command_proposal/services/command_interpreter.rb +108 -0
  56. data/lib/command_proposal/services/runner.rb +157 -0
  57. data/lib/command_proposal/version.rb +3 -0
  58. data/lib/command_proposal.rb +27 -0
  59. data/lib/generators/command_proposal/install/install_generator.rb +28 -0
  60. data/lib/generators/command_proposal/install/templates/initializer.rb +47 -0
  61. data/lib/generators/command_proposal/install/templates/install_command_proposal.rb +40 -0
  62. data/lib/tasks/command_proposal_tasks.rake +4 -0
  63. metadata +167 -0
@@ -0,0 +1,303 @@
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: https://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ function wordObj(words) {
15
+ var o = {};
16
+ for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;
17
+ return o;
18
+ }
19
+
20
+ var keywordList = [
21
+ "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else",
22
+ "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "not", "or",
23
+ "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
24
+ "until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc",
25
+ "caller", "lambda", "proc", "public", "protected", "private", "require", "load",
26
+ "require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__"
27
+ ], keywords = wordObj(keywordList);
28
+
29
+ var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then",
30
+ "catch", "loop", "proc", "begin"]);
31
+ var dedentWords = wordObj(["end", "until"]);
32
+ var opening = {"[": "]", "{": "}", "(": ")"};
33
+ var closing = {"]": "[", "}": "{", ")": "("};
34
+
35
+ CodeMirror.defineMode("ruby", function(config) {
36
+ var curPunc;
37
+
38
+ function chain(newtok, stream, state) {
39
+ state.tokenize.push(newtok);
40
+ return newtok(stream, state);
41
+ }
42
+
43
+ function tokenBase(stream, state) {
44
+ if (stream.sol() && stream.match("=begin") && stream.eol()) {
45
+ state.tokenize.push(readBlockComment);
46
+ return "comment";
47
+ }
48
+ if (stream.eatSpace()) return null;
49
+ var ch = stream.next(), m;
50
+ if (ch == "`" || ch == "'" || ch == '"') {
51
+ return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
52
+ } else if (ch == "/") {
53
+ if (regexpAhead(stream))
54
+ return chain(readQuoted(ch, "string-2", true), stream, state);
55
+ else
56
+ return "operator";
57
+ } else if (ch == "%") {
58
+ var style = "string", embed = true;
59
+ if (stream.eat("s")) style = "atom";
60
+ else if (stream.eat(/[WQ]/)) style = "string";
61
+ else if (stream.eat(/[r]/)) style = "string-2";
62
+ else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; }
63
+ var delim = stream.eat(/[^\w\s=]/);
64
+ if (!delim) return "operator";
65
+ if (opening.propertyIsEnumerable(delim)) delim = opening[delim];
66
+ return chain(readQuoted(delim, style, embed, true), stream, state);
67
+ } else if (ch == "#") {
68
+ stream.skipToEnd();
69
+ return "comment";
70
+ } else if (ch == "<" && (m = stream.match(/^<([-~])[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
71
+ return chain(readHereDoc(m[2], m[1]), stream, state);
72
+ } else if (ch == "0") {
73
+ if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/);
74
+ else if (stream.eat("b")) stream.eatWhile(/[01]/);
75
+ else stream.eatWhile(/[0-7]/);
76
+ return "number";
77
+ } else if (/\d/.test(ch)) {
78
+ stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/);
79
+ return "number";
80
+ } else if (ch == "?") {
81
+ while (stream.match(/^\\[CM]-/)) {}
82
+ if (stream.eat("\\")) stream.eatWhile(/\w/);
83
+ else stream.next();
84
+ return "string";
85
+ } else if (ch == ":") {
86
+ if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state);
87
+ if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state);
88
+
89
+ // :> :>> :< :<< are valid symbols
90
+ if (stream.eat(/[\<\>]/)) {
91
+ stream.eat(/[\<\>]/);
92
+ return "atom";
93
+ }
94
+
95
+ // :+ :- :/ :* :| :& :! are valid symbols
96
+ if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) {
97
+ return "atom";
98
+ }
99
+
100
+ // Symbols can't start by a digit
101
+ if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) {
102
+ stream.eatWhile(/[\w$\xa1-\uffff]/);
103
+ // Only one ? ! = is allowed and only as the last character
104
+ stream.eat(/[\?\!\=]/);
105
+ return "atom";
106
+ }
107
+ return "operator";
108
+ } else if (ch == "@" && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) {
109
+ stream.eat("@");
110
+ stream.eatWhile(/[\w\xa1-\uffff]/);
111
+ return "variable-2";
112
+ } else if (ch == "$") {
113
+ if (stream.eat(/[a-zA-Z_]/)) {
114
+ stream.eatWhile(/[\w]/);
115
+ } else if (stream.eat(/\d/)) {
116
+ stream.eat(/\d/);
117
+ } else {
118
+ stream.next(); // Must be a special global like $: or $!
119
+ }
120
+ return "variable-3";
121
+ } else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) {
122
+ stream.eatWhile(/[\w\xa1-\uffff]/);
123
+ stream.eat(/[\?\!]/);
124
+ if (stream.eat(":")) return "atom";
125
+ return "ident";
126
+ } else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) {
127
+ curPunc = "|";
128
+ return null;
129
+ } else if (/[\(\)\[\]{}\\;]/.test(ch)) {
130
+ curPunc = ch;
131
+ return null;
132
+ } else if (ch == "-" && stream.eat(">")) {
133
+ return "arrow";
134
+ } else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) {
135
+ var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/);
136
+ if (ch == "." && !more) curPunc = ".";
137
+ return "operator";
138
+ } else {
139
+ return null;
140
+ }
141
+ }
142
+
143
+ function regexpAhead(stream) {
144
+ var start = stream.pos, depth = 0, next, found = false, escaped = false
145
+ while ((next = stream.next()) != null) {
146
+ if (!escaped) {
147
+ if ("[{(".indexOf(next) > -1) {
148
+ depth++
149
+ } else if ("]})".indexOf(next) > -1) {
150
+ depth--
151
+ if (depth < 0) break
152
+ } else if (next == "/" && depth == 0) {
153
+ found = true
154
+ break
155
+ }
156
+ escaped = next == "\\"
157
+ } else {
158
+ escaped = false
159
+ }
160
+ }
161
+ stream.backUp(stream.pos - start)
162
+ return found
163
+ }
164
+
165
+ function tokenBaseUntilBrace(depth) {
166
+ if (!depth) depth = 1;
167
+ return function(stream, state) {
168
+ if (stream.peek() == "}") {
169
+ if (depth == 1) {
170
+ state.tokenize.pop();
171
+ return state.tokenize[state.tokenize.length-1](stream, state);
172
+ } else {
173
+ state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1);
174
+ }
175
+ } else if (stream.peek() == "{") {
176
+ state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1);
177
+ }
178
+ return tokenBase(stream, state);
179
+ };
180
+ }
181
+ function tokenBaseOnce() {
182
+ var alreadyCalled = false;
183
+ return function(stream, state) {
184
+ if (alreadyCalled) {
185
+ state.tokenize.pop();
186
+ return state.tokenize[state.tokenize.length-1](stream, state);
187
+ }
188
+ alreadyCalled = true;
189
+ return tokenBase(stream, state);
190
+ };
191
+ }
192
+ function readQuoted(quote, style, embed, unescaped) {
193
+ return function(stream, state) {
194
+ var escaped = false, ch;
195
+
196
+ if (state.context.type === 'read-quoted-paused') {
197
+ state.context = state.context.prev;
198
+ stream.eat("}");
199
+ }
200
+
201
+ while ((ch = stream.next()) != null) {
202
+ if (ch == quote && (unescaped || !escaped)) {
203
+ state.tokenize.pop();
204
+ break;
205
+ }
206
+ if (embed && ch == "#" && !escaped) {
207
+ if (stream.eat("{")) {
208
+ if (quote == "}") {
209
+ state.context = {prev: state.context, type: 'read-quoted-paused'};
210
+ }
211
+ state.tokenize.push(tokenBaseUntilBrace());
212
+ break;
213
+ } else if (/[@\$]/.test(stream.peek())) {
214
+ state.tokenize.push(tokenBaseOnce());
215
+ break;
216
+ }
217
+ }
218
+ escaped = !escaped && ch == "\\";
219
+ }
220
+ return style;
221
+ };
222
+ }
223
+ function readHereDoc(phrase, mayIndent) {
224
+ return function(stream, state) {
225
+ if (mayIndent) stream.eatSpace()
226
+ if (stream.match(phrase)) state.tokenize.pop();
227
+ else stream.skipToEnd();
228
+ return "string";
229
+ };
230
+ }
231
+ function readBlockComment(stream, state) {
232
+ if (stream.sol() && stream.match("=end") && stream.eol())
233
+ state.tokenize.pop();
234
+ stream.skipToEnd();
235
+ return "comment";
236
+ }
237
+
238
+ return {
239
+ startState: function() {
240
+ return {tokenize: [tokenBase],
241
+ indented: 0,
242
+ context: {type: "top", indented: -config.indentUnit},
243
+ continuedLine: false,
244
+ lastTok: null,
245
+ varList: false};
246
+ },
247
+
248
+ token: function(stream, state) {
249
+ curPunc = null;
250
+ if (stream.sol()) state.indented = stream.indentation();
251
+ var style = state.tokenize[state.tokenize.length-1](stream, state), kwtype;
252
+ var thisTok = curPunc;
253
+ if (style == "ident") {
254
+ var word = stream.current();
255
+ style = state.lastTok == "." ? "property"
256
+ : keywords.propertyIsEnumerable(stream.current()) ? "keyword"
257
+ : /^[A-Z]/.test(word) ? "tag"
258
+ : (state.lastTok == "def" || state.lastTok == "class" || state.varList) ? "def"
259
+ : "variable";
260
+ if (style == "keyword") {
261
+ thisTok = word;
262
+ if (indentWords.propertyIsEnumerable(word)) kwtype = "indent";
263
+ else if (dedentWords.propertyIsEnumerable(word)) kwtype = "dedent";
264
+ else if ((word == "if" || word == "unless") && stream.column() == stream.indentation())
265
+ kwtype = "indent";
266
+ else if (word == "do" && state.context.indented < state.indented)
267
+ kwtype = "indent";
268
+ }
269
+ }
270
+ if (curPunc || (style && style != "comment")) state.lastTok = thisTok;
271
+ if (curPunc == "|") state.varList = !state.varList;
272
+
273
+ if (kwtype == "indent" || /[\(\[\{]/.test(curPunc))
274
+ state.context = {prev: state.context, type: curPunc || style, indented: state.indented};
275
+ else if ((kwtype == "dedent" || /[\)\]\}]/.test(curPunc)) && state.context.prev)
276
+ state.context = state.context.prev;
277
+
278
+ if (stream.eol())
279
+ state.continuedLine = (curPunc == "\\" || style == "operator");
280
+ return style;
281
+ },
282
+
283
+ indent: function(state, textAfter) {
284
+ if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass;
285
+ var firstChar = textAfter && textAfter.charAt(0);
286
+ var ct = state.context;
287
+ var closed = ct.type == closing[firstChar] ||
288
+ ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter);
289
+ return ct.indented + (closed ? 0 : config.indentUnit) +
290
+ (state.continuedLine ? config.indentUnit : 0);
291
+ },
292
+
293
+ electricInput: /^\s*(?:end|rescue|elsif|else|\})$/,
294
+ lineComment: "#",
295
+ fold: "indent"
296
+ };
297
+ });
298
+
299
+ CodeMirror.defineMIME("text/x-ruby", "ruby");
300
+
301
+ CodeMirror.registerHelper("hintWords", "ruby", keywordList);
302
+
303
+ });
@@ -0,0 +1,195 @@
1
+ // Add tab / shift-tab for changing indents
2
+ docReady(function() {
3
+ var cmdconsole = document.querySelector(".cmd-console")
4
+
5
+ if (cmdconsole) {
6
+ var console_input = document.querySelector(".cmd-console .cmd-entry")
7
+ var lines = document.querySelector(".cmd-console .lines")
8
+ var queue = Promise.resolve()
9
+ var history_cmd_idx = undefined
10
+ var stored_entry = undefined
11
+ var commands = getPrevCommands()
12
+
13
+ cmdconsole.addEventListener("click", function(evt) {
14
+ if (!window.getSelection().toString().length && console_input) {
15
+ console_input.focus()
16
+ }
17
+ })
18
+
19
+ cmdconsole.addEventListener("keydown", function(evt) {
20
+ // evt.shiftKey
21
+ // evt.ctrlKey
22
+ // evt.altKey
23
+ // evt.metaKey (windows key or CMD key)
24
+
25
+ if (evt.key == "Enter" && !evt.shiftKey) {
26
+ evt.preventDefault()
27
+ submitConsoleCode()
28
+ return false
29
+ }
30
+
31
+ if (evt.ctrlKey && evt.key == "c") {
32
+ history_cmd_idx = undefined
33
+ stored_entry = console_input.textContent
34
+ console_input.textContent = ""
35
+ }
36
+
37
+ if (isNaN(history_cmd_idx)) { history_cmd_idx = undefined }
38
+ if (evt.key == "ArrowUp" && getCaretIndex(console_input) == 0) {
39
+ handleUpKey(evt)
40
+ }
41
+ if (evt.key == "ArrowDown" && getCaretIndex(console_input) == console_input.textContent.length) {
42
+ handleDownKey()
43
+ }
44
+ })
45
+
46
+ function handleUpKey() {
47
+ if (history_cmd_idx == undefined) {
48
+ // Not scrolling through history
49
+ if (console_input.textContent) {
50
+ // Text has been entered
51
+ stored_entry = console_input.textContent
52
+ }
53
+ // Set history index to begin scrolling
54
+ history_cmd_idx = commands.length
55
+ } else if (history_cmd_idx == 0) {
56
+ return
57
+ }
58
+
59
+ history_cmd_idx -= 1
60
+ console_input.textContent = commands[history_cmd_idx]
61
+ }
62
+
63
+ function handleDownKey() {
64
+ if (history_cmd_idx != undefined) {
65
+ var cmd = ""
66
+ if (history_cmd_idx < commands.length - 1) {
67
+ history_cmd_idx += 1
68
+ cmd = commands[history_cmd_idx]
69
+ } else if (stored_entry && history_cmd_idx == commands.length - 1) {
70
+ history_cmd_idx += 1
71
+ cmd = stored_entry
72
+ } else {
73
+ history_cmd_idx = undefined
74
+ }
75
+
76
+ console_input.textContent = cmd
77
+ if (cmd) {
78
+ setCaretIndex(console_input, console_input.textContent.length)
79
+ }
80
+ }
81
+ }
82
+
83
+ function getCaretIndex(element) {
84
+ var position = 0
85
+ if (window.getSelection) {
86
+ var selection = window.getSelection()
87
+ if (selection.rangeCount !== 0) {
88
+ var range = window.getSelection().getRangeAt(0)
89
+ var preCaretRange = range.cloneRange()
90
+ preCaretRange.selectNodeContents(element)
91
+ preCaretRange.setEnd(range.endContainer, range.endOffset)
92
+ position = preCaretRange.toString().length
93
+ }
94
+ }
95
+
96
+ return position
97
+ }
98
+
99
+ function setCaretIndex(element, idx) {
100
+ var setpos = document.createRange()
101
+ var set = window.getSelection()
102
+
103
+ // Set start position of range
104
+ setpos.setStart(element.childNodes[0], idx)
105
+
106
+ // Collapse range within its boundary points
107
+ // Returns boolean
108
+ setpos.collapse(true)
109
+
110
+ // Remove all ranges set
111
+ set.removeAllRanges()
112
+
113
+ // Add range with respect to range object.
114
+ set.addRange(setpos)
115
+
116
+ // Set cursor on focus
117
+ element.focus()
118
+ }
119
+
120
+ function getOffset(el) {
121
+ const rect = el.getBoundingClientRect()
122
+ return {
123
+ left: rect.left,
124
+ top: rect.top
125
+ }
126
+ }
127
+
128
+ function getPrevCommands() {
129
+ return Array.prototype.map.call(document.querySelectorAll(".line:not(.cmd-input)"), function(line) {
130
+ var text_node = Array.prototype.find.call(line.childNodes, function(node) {
131
+ return node.nodeName == "#text"
132
+ })
133
+
134
+ return text_node ? text_node.textContent : ""
135
+ })
136
+ }
137
+
138
+ function submitConsoleCode() {
139
+ var line = document.createElement("div")
140
+ line.classList.add("line")
141
+ line.textContent = console_input.textContent
142
+
143
+ console_input.textContent = ""
144
+ lines.appendChild(line)
145
+ stored_entry = undefined
146
+ history_cmd_idx = undefined
147
+
148
+ runConsoleCode(line)
149
+ }
150
+
151
+ function runConsoleCode(line) {
152
+ if (/^[\s\n]*$/.test(line.textContent)) { return }
153
+
154
+ commands.push(line.textContent)
155
+ queue = queue.then(async function() {
156
+ $.rails.refreshCSRFTokens()
157
+
158
+ var params = { code: line.textContent, task_id: cmdconsole.dataset.task }
159
+
160
+ var res = await fetch(cmdconsole.dataset.exeUrl, {
161
+ method: "POST",
162
+ headers: {
163
+ "Content-Type": "application/json",
164
+ "X-CSRF-Token": $.rails.csrfToken()
165
+ },
166
+ body: JSON.stringify(params)
167
+ }).then(function(res) {
168
+ if (res.ok) {
169
+ return res.json()
170
+ } else {
171
+ throw new Error("Server error")
172
+ }
173
+ }).catch(function(err) {
174
+ return {
175
+ error: err,
176
+ }
177
+ })
178
+
179
+ var json = await res
180
+
181
+ var result = document.createElement("div")
182
+ result.classList.add("result")
183
+
184
+ if (json.error) {
185
+ result.classList.add("cmd-error")
186
+ result.textContent = json.error
187
+ } else {
188
+ result.textContent = json.result
189
+ }
190
+
191
+ line.appendChild(result)
192
+ })
193
+ }
194
+ }
195
+ })