rack-webconsole-pry 0.1.4
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.
- data/.gitignore +9 -0
- data/.travis.yml +9 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +42 -0
- data/History +27 -0
- data/Rakefile +23 -0
- data/Readme.md +131 -0
- data/lib/rack/webconsole/asset_helpers.rb +73 -0
- data/lib/rack/webconsole/assets.rb +72 -0
- data/lib/rack/webconsole/railtie.rb +14 -0
- data/lib/rack/webconsole/repl.rb +113 -0
- data/lib/rack/webconsole/version.rb +7 -0
- data/lib/rack/webconsole.rb +81 -0
- data/lib/rack-webconsole.rb +3 -0
- data/public/jquery.html +1 -0
- data/public/webconsole.css +86 -0
- data/public/webconsole.html +15 -0
- data/public/webconsole.js +169 -0
- data/rack-webconsole-pry.gemspec +36 -0
- data/spec/rack/webconsole/asset_helpers_spec.rb +56 -0
- data/spec/rack/webconsole/assets_spec.rb +99 -0
- data/spec/rack/webconsole/repl_spec.rb +104 -0
- data/spec/rack/webconsole_spec.rb +56 -0
- data/spec/spec_helper.rb +10 -0
- metadata +228 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rack/webconsole/repl'
|
3
|
+
require 'rack/webconsole/asset_helpers'
|
4
|
+
require 'rack/webconsole/assets'
|
5
|
+
|
6
|
+
require 'rack/webconsole/railtie' if defined?(Rails::Railtie)
|
7
|
+
|
8
|
+
# Rack is a modular webserver interface written by Christian Neukirchen.
|
9
|
+
#
|
10
|
+
# Learn more at: https://github.com/rack/rack
|
11
|
+
#
|
12
|
+
module Rack
|
13
|
+
# {Rack::Webconsole} is a Rack middleware that provides an interactive
|
14
|
+
# console à la Rails console, but for any kind of Rack application (Rails,
|
15
|
+
# Sinatra, Padrino...), accessible from your web application's front-end.
|
16
|
+
#
|
17
|
+
# For every request, it normally passes control to the {Assets} middleware,
|
18
|
+
# which injects needed JavaScript, CSS and HTML code for the console to work
|
19
|
+
# properly.
|
20
|
+
#
|
21
|
+
# It also exposes a special route used by the {Repl}, a Ruby evaluator which
|
22
|
+
# is responsible of keeping state between requests, remembering local
|
23
|
+
# variables and giving a true IRB-esque experience.
|
24
|
+
#
|
25
|
+
class Webconsole
|
26
|
+
@@config = {:inject_jquery => false, :key_code => "96"}
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Returns whether the Asset injecter must inject JQuery or not.
|
30
|
+
#
|
31
|
+
# @return [Boolean] whether to inject JQuery or not.
|
32
|
+
def inject_jquery
|
33
|
+
@@config[:inject_jquery]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sets whether the Asset injecter must inject JQuery or not.
|
37
|
+
#
|
38
|
+
# @param [Boolean] value whether to inject JQuery or not.
|
39
|
+
def inject_jquery=(value)
|
40
|
+
@@config[:inject_jquery] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns key code used to start web console.
|
44
|
+
#
|
45
|
+
# @return [String] key code used at keypress event to start web console.
|
46
|
+
def key_code
|
47
|
+
@@config[:key_code]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sets key code used to start web console.
|
51
|
+
#
|
52
|
+
# @param [String] value key code used at keypress event to start web console.
|
53
|
+
def key_code=(value)
|
54
|
+
value = value.to_s unless value.is_a?(String)
|
55
|
+
@@config[:key_code] = value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Honor the Rack contract by saving the passed Rack application in an ivar.
|
60
|
+
#
|
61
|
+
# @param [Rack::Application] app the previous Rack application in the
|
62
|
+
# middleware chain.
|
63
|
+
def initialize(app)
|
64
|
+
@app = app
|
65
|
+
end
|
66
|
+
|
67
|
+
# Decides where to send the request. In case the path is `/webconsole`
|
68
|
+
# (e.g. when calling the {Repl} endpoint), pass the request onto the
|
69
|
+
# {Repl}. Otherwise, pass it onto the {Assets} middleware, which will
|
70
|
+
# inject the needed assets for the Webconsole to work.
|
71
|
+
#
|
72
|
+
# @param [Hash] env a Rack request environment.
|
73
|
+
def call(env)
|
74
|
+
if env['PATH_INFO'] == '/webconsole'
|
75
|
+
Repl.new(@app).call(env)
|
76
|
+
else
|
77
|
+
Assets.new(@app).call(env)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/public/jquery.html
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#rack-webconsole {
|
2
|
+
opacity: 0.9;
|
3
|
+
z-index: 999;
|
4
|
+
background: #000;
|
5
|
+
color: #DDD;
|
6
|
+
font-family: monospace;
|
7
|
+
height: 40%;
|
8
|
+
position: fixed;
|
9
|
+
width: 100%;
|
10
|
+
bottom: 0px;
|
11
|
+
left: 0px;
|
12
|
+
right:0px;
|
13
|
+
outline-top: 3px solid #DEDEDE;
|
14
|
+
box-shadow: 0px -4px 5px rgba(0,0,0,0.5);
|
15
|
+
-moz-box-shadow: 0px -4px 5px rgba(0,0,0,0.5);
|
16
|
+
-webkit-box-shadow: 0px -4px 5px rgba(0,0,0,0.5);
|
17
|
+
font-size: 11px;
|
18
|
+
}
|
19
|
+
#rack-webconsole div.query{
|
20
|
+
margin-top: 10px;
|
21
|
+
font-weight: bold;
|
22
|
+
padding-top: 10px;
|
23
|
+
border-top: 1px dashed #333;
|
24
|
+
margin-bottom: 5px;
|
25
|
+
}
|
26
|
+
#rack-webconsole div.query_multiline{
|
27
|
+
font-weight: bold;
|
28
|
+
margin-bottom: 5px;
|
29
|
+
}
|
30
|
+
#rack-webconsole div.query:first-child{
|
31
|
+
margin-top: 0px;
|
32
|
+
padding-top: 0px;
|
33
|
+
border-top: none;
|
34
|
+
}
|
35
|
+
#rack-webconsole div.result{
|
36
|
+
font-weight: normal;
|
37
|
+
}
|
38
|
+
#rack-webconsole form div, #console form span {
|
39
|
+
font-size: 14px;
|
40
|
+
border: 0px;
|
41
|
+
font-family: monospace;
|
42
|
+
color: #FFF;
|
43
|
+
}
|
44
|
+
#rack-webconsole form div.results_wrapper{
|
45
|
+
width: 100%;
|
46
|
+
position: absolute;
|
47
|
+
overflow-x: auto;
|
48
|
+
top: 0;
|
49
|
+
bottom: 40px;
|
50
|
+
}
|
51
|
+
#rack-webconsole form div.results{
|
52
|
+
padding: 10px;
|
53
|
+
}
|
54
|
+
#rack-webconsole .prompt{
|
55
|
+
width: 30px;
|
56
|
+
text-align: center;
|
57
|
+
display: block;
|
58
|
+
float: left;
|
59
|
+
height: 25px;
|
60
|
+
line-height: 25px;
|
61
|
+
}
|
62
|
+
#rack-webconsole form div.input{
|
63
|
+
width: 100%;
|
64
|
+
position: absolute;
|
65
|
+
bottom: 0px;
|
66
|
+
background: #000;
|
67
|
+
}
|
68
|
+
#rack-webconsole form div.input input{
|
69
|
+
-webkit-box-sizing: border-box;
|
70
|
+
-moz-box-sizing: border-box;
|
71
|
+
box-sizing: border-box;
|
72
|
+
margin-top: 0px;
|
73
|
+
margin-bottom: 0px;
|
74
|
+
padding: 0px;
|
75
|
+
width: 100%;
|
76
|
+
font-size: 14px;
|
77
|
+
background: transparent;
|
78
|
+
border: 0px;
|
79
|
+
font-family: monospace;
|
80
|
+
color: #FFF;
|
81
|
+
}
|
82
|
+
#rack-webconsole .input .input_box{
|
83
|
+
margin-left: 30px;
|
84
|
+
margin-right: 10px;
|
85
|
+
display: block;
|
86
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div id="rack-webconsole" style="display:none">
|
2
|
+
<form accept-charset="UTF-8" action="/webconsole" method="post">
|
3
|
+
<input name="utf8" type="hidden" value="✓"/>
|
4
|
+
<div class="results_wrapper">
|
5
|
+
<div class="results">
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
<div class="input">
|
9
|
+
<span class="prompt">>></span>
|
10
|
+
<span class="input_box">
|
11
|
+
<input id="webconsole_query" name="webconsole_query" type="text" />
|
12
|
+
</span>
|
13
|
+
</div>
|
14
|
+
</form>
|
15
|
+
</div>
|
@@ -0,0 +1,169 @@
|
|
1
|
+
(function($) {
|
2
|
+
|
3
|
+
var webconsole = {
|
4
|
+
history:[],
|
5
|
+
pointer:0,
|
6
|
+
query:$('#webconsole_query')
|
7
|
+
}
|
8
|
+
|
9
|
+
$('#rack-webconsole form').submit(function(e){
|
10
|
+
e.preventDefault();
|
11
|
+
});
|
12
|
+
|
13
|
+
function componentToHex(c) {
|
14
|
+
var hex = c.toString(16);
|
15
|
+
return hex.length == 1 ? "0" + hex : hex;
|
16
|
+
}
|
17
|
+
|
18
|
+
function rgbToHex(r, g, b) {
|
19
|
+
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
20
|
+
}
|
21
|
+
var prevStyle = {
|
22
|
+
color: "#ffffff",
|
23
|
+
bold: false,
|
24
|
+
underline: false
|
25
|
+
}
|
26
|
+
function bashColorToHtml(bcolor)
|
27
|
+
{
|
28
|
+
// colors
|
29
|
+
var textColor = rgbToHex(238, 238, 238);
|
30
|
+
var boldColor = rgbToHex(255, 255, 255);
|
31
|
+
var strColors = "[0;30m 238 238 238 \
|
32
|
+
[1;37m 255 255 255 \
|
33
|
+
[0;34m 150 203 254 \
|
34
|
+
[1;34m 181 220 254 \
|
35
|
+
[0;32m 168 255 96 \
|
36
|
+
[1;32m 206 255 172 \
|
37
|
+
[0;36m 198 197 254 \
|
38
|
+
[1;36m 223 223 254 \
|
39
|
+
[0;31m 255 108 96 \
|
40
|
+
[1;31m 255 182 176 \
|
41
|
+
[0;35m 255 115 253 \
|
42
|
+
[1;35m 255 156 254 \
|
43
|
+
[0;33m 255 255 182 \
|
44
|
+
[1;33m 255 255 203 \
|
45
|
+
[1;30m 124 124 124 \
|
46
|
+
[0;37m 238 238 238";
|
47
|
+
var colors = {};
|
48
|
+
var boldColors = {};
|
49
|
+
var matcher = /\[([0-9;]+)m\s+(\d+)\s+(\d+)\s+(\d+)/gm;
|
50
|
+
while ((r = matcher.exec(strColors)) != null) {
|
51
|
+
components = r[1].split(";")
|
52
|
+
if (components[0] == "0")
|
53
|
+
colors[components[1]] = rgbToHex(parseInt(r[2]), parseInt(r[3]), parseInt(r[4]));
|
54
|
+
else
|
55
|
+
boldColors[components[1]] = rgbToHex(parseInt(r[2]), parseInt(r[3]), parseInt(r[4]));
|
56
|
+
}
|
57
|
+
// set values
|
58
|
+
all = bcolor.split(/;/g)
|
59
|
+
if (all.indexOf("0") >= 0 && all.indexOf("0") > 0) // ignore anything before 0, since 0 resets
|
60
|
+
all.splice(0, all.indexOf("0"));
|
61
|
+
if (all.indexOf("0") >= 0)
|
62
|
+
prevStyle = {
|
63
|
+
color: textColor,
|
64
|
+
bold: false,
|
65
|
+
underline: false
|
66
|
+
};
|
67
|
+
if (all.indexOf("1") >= 0)
|
68
|
+
prevStyle['bold'] = true;
|
69
|
+
if (all.indexOf("4") >= 0)
|
70
|
+
prevStyle['underline'] = true;
|
71
|
+
if (prevStyle['bold'])
|
72
|
+
colorMap = boldColors;
|
73
|
+
else
|
74
|
+
colorMap = colors;
|
75
|
+
$.each(all, function(idx, val) {
|
76
|
+
if (colorMap[val] != undefined)
|
77
|
+
prevStyle['color'] = colorMap[val];
|
78
|
+
});
|
79
|
+
return 'color:'+prevStyle['color']+';font-weight:'+(prevStyle['bold'] ? 'bold' : 'normal')+
|
80
|
+
';text-decoration:'+(prevStyle['underline'] ? 'underline' : 'none');
|
81
|
+
}
|
82
|
+
function parseBashString(str)
|
83
|
+
{
|
84
|
+
str = str.replace(/\u001B\[([0-9;]+)m/g, function(fm, sm) {
|
85
|
+
return '</span><span style="'+bashColorToHtml(sm)+'">';
|
86
|
+
});
|
87
|
+
str = str.replace(/\n/g, "<br>");
|
88
|
+
return '<span>'+str+'</span>';
|
89
|
+
}
|
90
|
+
$("#rack-webconsole form input").keyup(function(event) {
|
91
|
+
function escapeHTML(string) {
|
92
|
+
return(string.replace(/&/g,'&').
|
93
|
+
replace(/>/g,'>').
|
94
|
+
replace(/</g,'<').
|
95
|
+
replace(/"/g,'"')
|
96
|
+
);
|
97
|
+
};
|
98
|
+
|
99
|
+
// enter
|
100
|
+
if (event.which == 13) {
|
101
|
+
webconsole.history.push(webconsole.query.val());
|
102
|
+
webconsole.pointer = webconsole.history.length - 1;
|
103
|
+
$.ajax({
|
104
|
+
url: '/webconsole',
|
105
|
+
type: 'POST',
|
106
|
+
dataType: 'json',
|
107
|
+
data: ({query: webconsole.query.val(), token: "$TOKEN"}),
|
108
|
+
success: function (data) {
|
109
|
+
var query_class = data.previous_multi_line ? 'query_multiline' : 'query';
|
110
|
+
var result = "<div class='" + query_class + "'>" +
|
111
|
+
parseBashString(escapeHTML(data.prompt)) + "</div>";
|
112
|
+
if (!data.multi_line) {
|
113
|
+
result += "<div class='result'>" + parseBashString(escapeHTML(data.result)) + "</div>";
|
114
|
+
}
|
115
|
+
$("#rack-webconsole .results").append(result);
|
116
|
+
$("#rack-webconsole .results_wrapper").scrollTop(
|
117
|
+
$("#rack-webconsole .results").height()
|
118
|
+
);
|
119
|
+
}
|
120
|
+
});
|
121
|
+
webconsole.query.val('');
|
122
|
+
}
|
123
|
+
|
124
|
+
// up
|
125
|
+
if (event.which == 38) {
|
126
|
+
if (webconsole.pointer < 0) {
|
127
|
+
webconsole.query.val('');
|
128
|
+
} else {
|
129
|
+
if (webconsole.pointer == webconsole.history.length) {
|
130
|
+
webconsole.pointer = webconsole.history.length - 1;
|
131
|
+
}
|
132
|
+
webconsole.query.val(webconsole.history[webconsole.pointer]);
|
133
|
+
webconsole.pointer--;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
// down
|
138
|
+
if (event.which == 40) {
|
139
|
+
if (webconsole.pointer == webconsole.history.length) {
|
140
|
+
webconsole.query.val('');
|
141
|
+
} else {
|
142
|
+
if (webconsole.pointer < 0) {
|
143
|
+
webconsole.pointer = 0;
|
144
|
+
}
|
145
|
+
webconsole.query.val(webconsole.history[webconsole.pointer]);
|
146
|
+
webconsole.pointer++;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
});
|
151
|
+
|
152
|
+
$(document).ready(function() {
|
153
|
+
$(this).keypress(function(event) {
|
154
|
+
if (event.which == $KEY_CODE) {
|
155
|
+
$("#rack-webconsole").slideToggle('fast', function() {
|
156
|
+
if ($(this).is(':visible')) {
|
157
|
+
$("#rack-webconsole form input").focus();
|
158
|
+
$("#rack-webconsole .results_wrapper").scrollTop(
|
159
|
+
$("#rack-webconsole .results").height()
|
160
|
+
);
|
161
|
+
} else {
|
162
|
+
$("#rack-webconsole form input").blur();
|
163
|
+
}
|
164
|
+
});
|
165
|
+
event.preventDefault();
|
166
|
+
}
|
167
|
+
});
|
168
|
+
});
|
169
|
+
})(jQuery);
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rack/webconsole/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rack-webconsole-pry"
|
7
|
+
s.version = Rack::Webconsole::VERSION
|
8
|
+
s.authors = ["Josep M. Bach", "Josep Jaume Rey", "Oriol Gual", "Jan Berdajs"]
|
9
|
+
s.email = ["mrbrdo@gmail.com"]
|
10
|
+
s.homepage = "http://github.com/mrbrdo/rack-webconsole"
|
11
|
+
s.summary = %q{Rack-based console inside your web applications, using pry}
|
12
|
+
s.description = %q{Rack-based console inside your web applications, using pry}
|
13
|
+
|
14
|
+
s.rubyforge_project = "rack-webconsole-pry"
|
15
|
+
|
16
|
+
s.add_runtime_dependency 'rack'
|
17
|
+
s.add_runtime_dependency 'multi_json', '>= 1.0.3'
|
18
|
+
s.add_runtime_dependency 'pry'
|
19
|
+
|
20
|
+
s.add_development_dependency 'minitest'
|
21
|
+
s.add_development_dependency 'purdytest'
|
22
|
+
|
23
|
+
# Since we can't have a git dependency in gemspec, we specify this
|
24
|
+
# dependency directly in the Gemfile. Once a new mocha version is released,
|
25
|
+
# we should uncomment this line and remove mocha from the Gemfile.
|
26
|
+
# s.add_development_dependency 'mocha'
|
27
|
+
|
28
|
+
s.add_development_dependency 'yard'
|
29
|
+
s.add_development_dependency 'bluecloth'
|
30
|
+
s.add_development_dependency 'rake'
|
31
|
+
s.add_development_dependency 'pry'
|
32
|
+
|
33
|
+
s.files = `git ls-files`.split("\n")
|
34
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
class AssetClass
|
5
|
+
include Rack::Webconsole::AssetHelpers
|
6
|
+
end
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
describe Webconsole::AssetHelpers do
|
10
|
+
|
11
|
+
describe '#html_code' do
|
12
|
+
it 'loads the html code' do
|
13
|
+
asset_class = AssetClass.new
|
14
|
+
html = asset_class.html_code
|
15
|
+
|
16
|
+
html.must_match /console/
|
17
|
+
html.must_match /results/
|
18
|
+
html.must_match /form/
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#css_code' do
|
23
|
+
it 'loads the css code' do
|
24
|
+
asset_class = AssetClass.new
|
25
|
+
css = asset_class.css_code
|
26
|
+
|
27
|
+
css.must_match /<style/
|
28
|
+
css.must_match /text\/css/
|
29
|
+
css.must_match /#console/
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#js_code' do
|
34
|
+
it 'loads the js code' do
|
35
|
+
asset_class = AssetClass.new
|
36
|
+
js = asset_class.js_code
|
37
|
+
|
38
|
+
js.must_match /\$\("#rack-webconsole"\)/
|
39
|
+
js.must_match /escapeHTML/
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#render' do
|
44
|
+
it 'knows how to replace $ vars' do
|
45
|
+
asset_class = AssetClass.new
|
46
|
+
|
47
|
+
text = "test $test test $test"
|
48
|
+
asset_class.render(text, :test => "123").must_equal("test 123 test 123")
|
49
|
+
|
50
|
+
text = "test $var1 test $var2"
|
51
|
+
asset_class.render(text, :var1 => "123", :var2 => "321").must_equal("test 123 test 321")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|