patchmaster 0.0.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/patchmaster +44 -8
- data/lib/patchmaster.rb +0 -1
- data/lib/patchmaster/connection.rb +1 -0
- data/lib/patchmaster/{app → curses}/info_window.rb +0 -0
- data/lib/patchmaster/{app → curses}/info_window_contents.txt +0 -0
- data/lib/patchmaster/{app → curses}/list_window.rb +1 -1
- data/lib/patchmaster/{app → curses}/main.rb +1 -5
- data/lib/patchmaster/{app → curses}/patch_window.rb +1 -1
- data/lib/patchmaster/{app → curses}/pm_window.rb +0 -0
- data/lib/patchmaster/{app → curses}/prompt_window.rb +0 -0
- data/lib/patchmaster/{app → curses}/trigger_window.rb +0 -0
- data/lib/patchmaster/dsl.rb +25 -14
- data/lib/patchmaster/filter.rb +1 -1
- data/lib/patchmaster/instrument.rb +12 -12
- data/lib/patchmaster/irb.rb +82 -0
- data/lib/patchmaster/patchmaster.rb +22 -18
- data/lib/patchmaster/trigger.rb +1 -1
- data/lib/patchmaster/web/public/index.html +65 -0
- data/lib/patchmaster/web/public/js/jquery-1.4.2.js +6240 -0
- data/lib/patchmaster/web/public/js/jquery.hotkeys.js +99 -0
- data/lib/patchmaster/web/public/js/patchmaster.coffee +81 -0
- data/lib/patchmaster/web/public/js/patchmaster.js +136 -0
- data/lib/patchmaster/web/public/style.css +79 -0
- data/lib/patchmaster/web/sinatra_app.rb +130 -0
- data/test/test_helper.rb +1 -2
- metadata +24 -18
@@ -0,0 +1,99 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery Hotkeys Plugin
|
3
|
+
* Copyright 2010, John Resig
|
4
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
5
|
+
*
|
6
|
+
* Based upon the plugin by Tzury Bar Yochay:
|
7
|
+
* http://github.com/tzuryby/hotkeys
|
8
|
+
*
|
9
|
+
* Original idea by:
|
10
|
+
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
|
11
|
+
*/
|
12
|
+
|
13
|
+
(function(jQuery){
|
14
|
+
|
15
|
+
jQuery.hotkeys = {
|
16
|
+
version: "0.8",
|
17
|
+
|
18
|
+
specialKeys: {
|
19
|
+
8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
|
20
|
+
20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
|
21
|
+
37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
|
22
|
+
96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
|
23
|
+
104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
|
24
|
+
112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
|
25
|
+
120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
|
26
|
+
},
|
27
|
+
|
28
|
+
shiftNums: {
|
29
|
+
"`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
|
30
|
+
"8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
|
31
|
+
".": ">", "/": "?", "\\": "|"
|
32
|
+
}
|
33
|
+
};
|
34
|
+
|
35
|
+
function keyHandler( handleObj ) {
|
36
|
+
// Only care when a possible input has been specified
|
37
|
+
if ( typeof handleObj.data !== "string" ) {
|
38
|
+
return;
|
39
|
+
}
|
40
|
+
|
41
|
+
var origHandler = handleObj.handler,
|
42
|
+
keys = handleObj.data.toLowerCase().split(" ");
|
43
|
+
|
44
|
+
handleObj.handler = function( event ) {
|
45
|
+
// Don't fire in text-accepting inputs that we didn't directly bind to
|
46
|
+
if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
|
47
|
+
event.target.type === "text") ) {
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
|
51
|
+
// Keypress represents characters, not special keys
|
52
|
+
var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
|
53
|
+
character = String.fromCharCode( event.which ).toLowerCase(),
|
54
|
+
key, modif = "", possible = {};
|
55
|
+
|
56
|
+
// check combinations (alt|ctrl|shift+anything)
|
57
|
+
if ( event.altKey && special !== "alt" ) {
|
58
|
+
modif += "alt+";
|
59
|
+
}
|
60
|
+
|
61
|
+
if ( event.ctrlKey && special !== "ctrl" ) {
|
62
|
+
modif += "ctrl+";
|
63
|
+
}
|
64
|
+
|
65
|
+
// TODO: Need to make sure this works consistently across platforms
|
66
|
+
if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
|
67
|
+
modif += "meta+";
|
68
|
+
}
|
69
|
+
|
70
|
+
if ( event.shiftKey && special !== "shift" ) {
|
71
|
+
modif += "shift+";
|
72
|
+
}
|
73
|
+
|
74
|
+
if ( special ) {
|
75
|
+
possible[ modif + special ] = true;
|
76
|
+
|
77
|
+
} else {
|
78
|
+
possible[ modif + character ] = true;
|
79
|
+
possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
|
80
|
+
|
81
|
+
// "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
|
82
|
+
if ( modif === "shift+" ) {
|
83
|
+
possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
for ( var i = 0, l = keys.length; i < l; i++ ) {
|
88
|
+
if ( possible[ keys[i] ] ) {
|
89
|
+
return origHandler.apply( this, arguments );
|
90
|
+
}
|
91
|
+
}
|
92
|
+
};
|
93
|
+
}
|
94
|
+
|
95
|
+
jQuery.each([ "keydown", "keyup", "keypress" ], function() {
|
96
|
+
jQuery.event.special[ this ] = { add: keyHandler };
|
97
|
+
});
|
98
|
+
|
99
|
+
})( jQuery );
|
@@ -0,0 +1,81 @@
|
|
1
|
+
$ = jQuery
|
2
|
+
CONN_HEADERS = """
|
3
|
+
<tr>
|
4
|
+
<th>Input</th>
|
5
|
+
<th>Chan</th>
|
6
|
+
<th>Output</th>
|
7
|
+
<th>Chan</th>
|
8
|
+
<th>Prog</th>
|
9
|
+
<th>Zone</th>
|
10
|
+
<th>Xpose</th>
|
11
|
+
<th>Filter</th>
|
12
|
+
</tr>
|
13
|
+
"""
|
14
|
+
COLOR_SCHEMES = ['default', 'green', 'amber', 'blue'];
|
15
|
+
color_scheme_index = 0;
|
16
|
+
|
17
|
+
list_item = (val, highlighted_value) ->
|
18
|
+
classes = if val == highlighted_value then "selected reverse-#{COLOR_SCHEMES[color_scheme_index]}" else ''
|
19
|
+
"<li class=\"#{classes}\">#{val}</li>"
|
20
|
+
|
21
|
+
list = (id, vals, highlighted_value) ->
|
22
|
+
lis = (list_item(val, highlighted_value) for val in vals)
|
23
|
+
$('#' + id).html(lis.join("\n"))
|
24
|
+
|
25
|
+
connection_row = (conn) ->
|
26
|
+
vals = (conn[key] for key in ['input', 'input_chan', 'output', 'output_chan', 'pc', 'zone', 'xpose', 'filter'])
|
27
|
+
"<tr><td>#{vals.join('</td><td>')}</td></tr>"
|
28
|
+
|
29
|
+
connection_rows = (connections) ->
|
30
|
+
rows = (connection_row(conn) for conn in connections)
|
31
|
+
$('#patch').html(CONN_HEADERS + "\n" + rows.join("\n"))
|
32
|
+
|
33
|
+
maybe_name = (data, key) -> if data[key] then data[key]['name'] else ''
|
34
|
+
|
35
|
+
message = (str) -> $('#message').html(str)
|
36
|
+
|
37
|
+
kp = (action) ->
|
38
|
+
$.getJSON(action, (data) ->
|
39
|
+
list('song-lists', data['lists'], data['list'])
|
40
|
+
list('songs', data['songs'], maybe_name(data, 'song'))
|
41
|
+
list('triggers', data['triggers'])
|
42
|
+
|
43
|
+
if data['song']?
|
44
|
+
list('song', data['song']['patches'], maybe_name(data, 'patch'))
|
45
|
+
if data['patch']?
|
46
|
+
connection_rows(data['patch']['connections'])
|
47
|
+
|
48
|
+
message(data['message']) if data['message']?
|
49
|
+
)
|
50
|
+
|
51
|
+
cycle_colors = () ->
|
52
|
+
base_class = COLOR_SCHEMES[color_scheme_index]
|
53
|
+
if color_scheme_index >= 0
|
54
|
+
$('body').removeClass(base_class)
|
55
|
+
$('.selected, th, td#appname').removeClass("reverse-#{base_class}")
|
56
|
+
$('tr, td, th').removeClass("#{base_class}-border")
|
57
|
+
|
58
|
+
color_scheme_index = (color_scheme_index + 1) % COLOR_SCHEMES.length
|
59
|
+
|
60
|
+
base_class = COLOR_SCHEMES[color_scheme_index]
|
61
|
+
$('body').addClass(base_class)
|
62
|
+
$('.selected, th, td#appname').addClass("reverse-#{base_class}")
|
63
|
+
$('tr, td, th').addClass("#{base_class}-border")
|
64
|
+
|
65
|
+
color_scheme = base_class
|
66
|
+
|
67
|
+
bindings =
|
68
|
+
'j': 'next_patch'
|
69
|
+
'down': 'next_patch'
|
70
|
+
'k': 'prev_patch'
|
71
|
+
'up': 'prev_patch'
|
72
|
+
'n': 'next_song'
|
73
|
+
'left': 'next_song'
|
74
|
+
'p': 'prev_song'
|
75
|
+
'right': 'prev_song'
|
76
|
+
'esc': 'panic'
|
77
|
+
f = (key, val) -> $(document).bind('keydown', key, () -> kp(val))
|
78
|
+
f(key, val) for key, val of bindings
|
79
|
+
$(document).bind('keydown', 'c', () -> cycle_colors())
|
80
|
+
|
81
|
+
kp('status')
|
@@ -0,0 +1,136 @@
|
|
1
|
+
// Generated by CoffeeScript 1.3.3
|
2
|
+
(function() {
|
3
|
+
var $, COLOR_SCHEMES, CONN_HEADERS, bindings, color_scheme_index, connection_row, connection_rows, cycle_colors, f, key, kp, list, list_item, maybe_name, message, val;
|
4
|
+
|
5
|
+
$ = jQuery;
|
6
|
+
|
7
|
+
CONN_HEADERS = "<tr>\n <th>Input</th>\n <th>Chan</th>\n <th>Output</th>\n <th>Chan</th>\n <th>Prog</th>\n <th>Zone</th>\n <th>Xpose</th>\n <th>Filter</th>\n</tr>";
|
8
|
+
|
9
|
+
COLOR_SCHEMES = ['default', 'green', 'amber', 'blue'];
|
10
|
+
|
11
|
+
color_scheme_index = 0;
|
12
|
+
|
13
|
+
list_item = function(val, highlighted_value) {
|
14
|
+
var classes;
|
15
|
+
classes = val === highlighted_value ? "selected reverse-" + COLOR_SCHEMES[color_scheme_index] : '';
|
16
|
+
return "<li class=\"" + classes + "\">" + val + "</li>";
|
17
|
+
};
|
18
|
+
|
19
|
+
list = function(id, vals, highlighted_value) {
|
20
|
+
var lis, val;
|
21
|
+
lis = (function() {
|
22
|
+
var _i, _len, _results;
|
23
|
+
_results = [];
|
24
|
+
for (_i = 0, _len = vals.length; _i < _len; _i++) {
|
25
|
+
val = vals[_i];
|
26
|
+
_results.push(list_item(val, highlighted_value));
|
27
|
+
}
|
28
|
+
return _results;
|
29
|
+
})();
|
30
|
+
return $('#' + id).html(lis.join("\n"));
|
31
|
+
};
|
32
|
+
|
33
|
+
connection_row = function(conn) {
|
34
|
+
var key, vals;
|
35
|
+
vals = (function() {
|
36
|
+
var _i, _len, _ref, _results;
|
37
|
+
_ref = ['input', 'input_chan', 'output', 'output_chan', 'pc', 'zone', 'xpose', 'filter'];
|
38
|
+
_results = [];
|
39
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
40
|
+
key = _ref[_i];
|
41
|
+
_results.push(conn[key]);
|
42
|
+
}
|
43
|
+
return _results;
|
44
|
+
})();
|
45
|
+
return "<tr><td>" + (vals.join('</td><td>')) + "</td></tr>";
|
46
|
+
};
|
47
|
+
|
48
|
+
connection_rows = function(connections) {
|
49
|
+
var conn, rows;
|
50
|
+
rows = (function() {
|
51
|
+
var _i, _len, _results;
|
52
|
+
_results = [];
|
53
|
+
for (_i = 0, _len = connections.length; _i < _len; _i++) {
|
54
|
+
conn = connections[_i];
|
55
|
+
_results.push(connection_row(conn));
|
56
|
+
}
|
57
|
+
return _results;
|
58
|
+
})();
|
59
|
+
return $('#patch').html(CONN_HEADERS + "\n" + rows.join("\n"));
|
60
|
+
};
|
61
|
+
|
62
|
+
maybe_name = function(data, key) {
|
63
|
+
if (data[key]) {
|
64
|
+
return data[key]['name'];
|
65
|
+
} else {
|
66
|
+
return '';
|
67
|
+
}
|
68
|
+
};
|
69
|
+
|
70
|
+
message = function(str) {
|
71
|
+
return $('#message').html(str);
|
72
|
+
};
|
73
|
+
|
74
|
+
kp = function(action) {
|
75
|
+
return $.getJSON(action, function(data) {
|
76
|
+
list('song-lists', data['lists'], data['list']);
|
77
|
+
list('songs', data['songs'], maybe_name(data, 'song'));
|
78
|
+
list('triggers', data['triggers']);
|
79
|
+
if (data['song'] != null) {
|
80
|
+
list('song', data['song']['patches'], maybe_name(data, 'patch'));
|
81
|
+
if (data['patch'] != null) {
|
82
|
+
connection_rows(data['patch']['connections']);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
if (data['message'] != null) {
|
86
|
+
return message(data['message']);
|
87
|
+
}
|
88
|
+
});
|
89
|
+
};
|
90
|
+
|
91
|
+
cycle_colors = function() {
|
92
|
+
var base_class, color_scheme;
|
93
|
+
base_class = COLOR_SCHEMES[color_scheme_index];
|
94
|
+
if (color_scheme_index >= 0) {
|
95
|
+
$('body').removeClass(base_class);
|
96
|
+
$('.selected, th, td#appname').removeClass("reverse-" + base_class);
|
97
|
+
$('tr, td, th').removeClass("" + base_class + "-border");
|
98
|
+
}
|
99
|
+
color_scheme_index = (color_scheme_index + 1) % COLOR_SCHEMES.length;
|
100
|
+
base_class = COLOR_SCHEMES[color_scheme_index];
|
101
|
+
$('body').addClass(base_class);
|
102
|
+
$('.selected, th, td#appname').addClass("reverse-" + base_class);
|
103
|
+
$('tr, td, th').addClass("" + base_class + "-border");
|
104
|
+
return color_scheme = base_class;
|
105
|
+
};
|
106
|
+
|
107
|
+
bindings = {
|
108
|
+
'j': 'next_patch',
|
109
|
+
'down': 'next_patch',
|
110
|
+
'k': 'prev_patch',
|
111
|
+
'up': 'prev_patch',
|
112
|
+
'n': 'next_song',
|
113
|
+
'left': 'next_song',
|
114
|
+
'p': 'prev_song',
|
115
|
+
'right': 'prev_song',
|
116
|
+
'esc': 'panic'
|
117
|
+
};
|
118
|
+
|
119
|
+
f = function(key, val) {
|
120
|
+
return $(document).bind('keydown', key, function() {
|
121
|
+
return kp(val);
|
122
|
+
});
|
123
|
+
};
|
124
|
+
|
125
|
+
for (key in bindings) {
|
126
|
+
val = bindings[key];
|
127
|
+
f(key, val);
|
128
|
+
}
|
129
|
+
|
130
|
+
$(document).bind('keydown', 'c', function() {
|
131
|
+
return cycle_colors();
|
132
|
+
});
|
133
|
+
|
134
|
+
kp('status');
|
135
|
+
|
136
|
+
}).call(this);
|
@@ -0,0 +1,79 @@
|
|
1
|
+
h1 {
|
2
|
+
margin-bottom: 0;
|
3
|
+
}
|
4
|
+
ul {
|
5
|
+
list-style: none;
|
6
|
+
padding-left: 0;
|
7
|
+
}
|
8
|
+
table {
|
9
|
+
border-collapse: collapse;
|
10
|
+
width: 100%;
|
11
|
+
}
|
12
|
+
tr, td, th {
|
13
|
+
border: 1px solid black;
|
14
|
+
vertical-align: top;
|
15
|
+
}
|
16
|
+
th {
|
17
|
+
background-color: black;
|
18
|
+
color: white;
|
19
|
+
border-right: 1px solid #888;
|
20
|
+
}
|
21
|
+
td#appname {
|
22
|
+
text-align: center;
|
23
|
+
width: 100%;
|
24
|
+
}
|
25
|
+
table h1, table li, table pre {
|
26
|
+
padding: 0 0.5em 0 0.5em;
|
27
|
+
}
|
28
|
+
|
29
|
+
/* ================ black on white ================ */
|
30
|
+
.default {
|
31
|
+
background-color: white;
|
32
|
+
color: black;
|
33
|
+
}
|
34
|
+
.reverse-default {
|
35
|
+
background-color: black;
|
36
|
+
color: white;
|
37
|
+
}
|
38
|
+
.default-border {
|
39
|
+
border: 1px solid black;
|
40
|
+
}
|
41
|
+
|
42
|
+
/* ================ green on black ================ */
|
43
|
+
.green {
|
44
|
+
background-color: black;
|
45
|
+
color: #0f0;
|
46
|
+
}
|
47
|
+
.reverse-green {
|
48
|
+
background-color: #0f0;
|
49
|
+
color: black;
|
50
|
+
}
|
51
|
+
.green-border {
|
52
|
+
border: 1px solid #0f0;
|
53
|
+
}
|
54
|
+
|
55
|
+
/* ================ amber on black ================ */
|
56
|
+
.amber {
|
57
|
+
background-color: black;
|
58
|
+
color: #f80;
|
59
|
+
}
|
60
|
+
.reverse-amber {
|
61
|
+
background-color: #f80;
|
62
|
+
color: black;
|
63
|
+
}
|
64
|
+
.amber-border {
|
65
|
+
border: 1px solid #f80;
|
66
|
+
}
|
67
|
+
|
68
|
+
/* ================ white on blue ================ */
|
69
|
+
.blue {
|
70
|
+
background-color: blue;
|
71
|
+
color: white;
|
72
|
+
}
|
73
|
+
.reverse-blue {
|
74
|
+
background-color: white;
|
75
|
+
color: blue;
|
76
|
+
}
|
77
|
+
.blue-border {
|
78
|
+
border: 1px solid white;
|
79
|
+
}
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'sinatra/json'
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
# ================================================================
|
6
|
+
# Settings
|
7
|
+
# ================================================================
|
8
|
+
|
9
|
+
set :run, true
|
10
|
+
set :root, File.dirname(__FILE__)
|
11
|
+
|
12
|
+
# ================================================================
|
13
|
+
# Helper methods
|
14
|
+
# ================================================================
|
15
|
+
|
16
|
+
def pm
|
17
|
+
@pm ||= PM::SinatraApp.instance.pm
|
18
|
+
end
|
19
|
+
|
20
|
+
def return_status(opts = nil)
|
21
|
+
pm = pm()
|
22
|
+
status = {
|
23
|
+
:lists => pm.song_lists.map(&:name),
|
24
|
+
:list => pm.song_list.name,
|
25
|
+
:songs => pm.song_list.songs.map(&:name),
|
26
|
+
:triggers => pm.inputs.collect do |instrument|
|
27
|
+
instrument.triggers.collect { |trigger| ":#{instrument.sym} #{trigger.to_s}" }
|
28
|
+
end.flatten
|
29
|
+
}
|
30
|
+
if pm.song
|
31
|
+
status[:song] = {
|
32
|
+
:name => pm.song.name,
|
33
|
+
:patches => pm.song.patches.map(&:name)
|
34
|
+
}
|
35
|
+
if pm.patch
|
36
|
+
status[:patch] = {
|
37
|
+
:name => pm.patch.name,
|
38
|
+
:connections => pm.patch.connections.collect do |conn|
|
39
|
+
{
|
40
|
+
:input => conn.input.name,
|
41
|
+
:input_chan => conn.input_chan ? conn.input_chan + 1 : 'all',
|
42
|
+
:output => conn.output.name,
|
43
|
+
:output_chan => conn.output_chan + 1,
|
44
|
+
:pc => conn.pc_prog.to_s,
|
45
|
+
:zone => conn.zone ? [conn.note_num_to_name(conn.zone.begin),
|
46
|
+
conn.note_num_to_name(conn.zone.end)] : '',
|
47
|
+
:xpose => conn.xpose.to_s,
|
48
|
+
:filter => conn.filter.to_s
|
49
|
+
}
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
status.merge(opts) if opts
|
55
|
+
|
56
|
+
json status
|
57
|
+
end
|
58
|
+
|
59
|
+
# ================================================================
|
60
|
+
# URL handlers
|
61
|
+
# ================================================================
|
62
|
+
|
63
|
+
not_found do
|
64
|
+
path = request.env['REQUEST_PATH']
|
65
|
+
unless path == '/favicon.ico'
|
66
|
+
$stderr.puts "error: not_found called, request = #{request.inspect}" # DEBUG
|
67
|
+
return_status(:message => "No such URL: #{path}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
get '/' do
|
72
|
+
redirect '/index.html'
|
73
|
+
end
|
74
|
+
|
75
|
+
get '/status' do
|
76
|
+
return_status
|
77
|
+
end
|
78
|
+
|
79
|
+
get '/next_patch' do
|
80
|
+
pm.next_patch
|
81
|
+
return_status
|
82
|
+
end
|
83
|
+
|
84
|
+
get '/prev_patch' do
|
85
|
+
pm.prev_patch
|
86
|
+
return_status
|
87
|
+
end
|
88
|
+
|
89
|
+
get '/next_song' do
|
90
|
+
pm.next_song
|
91
|
+
return_status
|
92
|
+
end
|
93
|
+
|
94
|
+
get '/prev_song' do
|
95
|
+
pm.prev_song
|
96
|
+
return_status
|
97
|
+
end
|
98
|
+
|
99
|
+
get '/panic' do
|
100
|
+
# TODO when panic called twice in a row, call panic(true)
|
101
|
+
pm.panic
|
102
|
+
return_status
|
103
|
+
end
|
104
|
+
|
105
|
+
# ================================================================
|
106
|
+
# GUI class: run method
|
107
|
+
# ================================================================
|
108
|
+
|
109
|
+
module PM
|
110
|
+
|
111
|
+
class SinatraApp
|
112
|
+
|
113
|
+
include Singleton
|
114
|
+
|
115
|
+
attr_accessor :port
|
116
|
+
attr_reader :pm
|
117
|
+
|
118
|
+
def initialize
|
119
|
+
@pm = PM::PatchMaster.instance
|
120
|
+
end
|
121
|
+
|
122
|
+
def run
|
123
|
+
set(:port, @port) if @port
|
124
|
+
@pm.start
|
125
|
+
ensure
|
126
|
+
@pm.stop
|
127
|
+
@pm.close_debug_file
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|