rack-webconsole 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +31 -0
- data/Rakefile +25 -0
- data/Readme.md +87 -0
- data/lib/rack/webconsole/asset_helpers.rb +51 -0
- data/lib/rack/webconsole/assets.rb +60 -0
- data/lib/rack/webconsole/railtie.rb +14 -0
- data/lib/rack/webconsole/repl.rb +63 -0
- data/lib/rack/webconsole/sandbox.rb +26 -0
- data/lib/rack/webconsole/version.rb +7 -0
- data/lib/rack/webconsole.rb +49 -0
- data/lib/rack-webconsole.rb +3 -0
- data/public/webconsole.css +47 -0
- data/public/webconsole.html +12 -0
- data/public/webconsole.js +45 -0
- data/rack-webconsole.gemspec +30 -0
- data/spec/rack/webconsole/asset_helpers_spec.rb +43 -0
- data/spec/rack/webconsole/assets_spec.rb +73 -0
- data/spec/rack/webconsole/repl_spec.rb +60 -0
- data/spec/rack/webconsole/sandbox_spec.rb +40 -0
- data/spec/rack/webconsole_spec.rb +40 -0
- data/spec/spec_helper.rb +8 -0
- metadata +174 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use ruby-1.9.2@rack-webconsole
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rack-webconsole (0.0.1)
|
5
|
+
json
|
6
|
+
rack
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
bluecloth (2.1.0)
|
12
|
+
json (1.5.3)
|
13
|
+
minitest (2.3.1)
|
14
|
+
mocha (0.9.12)
|
15
|
+
purdytest (1.0.0)
|
16
|
+
minitest (~> 2.2)
|
17
|
+
rack (1.3.1)
|
18
|
+
rake (0.9.2)
|
19
|
+
yard (0.7.2)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
bluecloth
|
26
|
+
minitest
|
27
|
+
mocha
|
28
|
+
purdytest
|
29
|
+
rack-webconsole!
|
30
|
+
rake
|
31
|
+
yard
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
desc "Run rack-webconsole specs"
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << "spec"
|
8
|
+
t.test_files = FileList['spec/**/*_spec.rb']
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'yard'
|
13
|
+
YARD::Rake::YardocTask.new(:docs) do |t|
|
14
|
+
t.files = ['lib/**/*.rb']
|
15
|
+
t.options = ['-m', 'markdown', '--no-private', '-r', 'Readme.md', '--title', 'rack-webconsole documentation']
|
16
|
+
end
|
17
|
+
|
18
|
+
task :doc => [:docs]
|
19
|
+
|
20
|
+
desc "Generate and open class diagram (needs Graphviz installed)"
|
21
|
+
task :graph do |t|
|
22
|
+
`bundle exec yard graph -d --full --no-private | dot -Tpng -o graph.png && open graph.png`
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => [:test]
|
data/Readme.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#rack-webconsole
|
2
|
+
|
3
|
+
Rack-webconsole is a Rack-based interactive console (à la Rails console) in
|
4
|
+
your web application's frontend. That means you can interact with your
|
5
|
+
application's backend from within the browser itself!
|
6
|
+
|
7
|
+
To get a clearer idea, you can check out [this video](
|
8
|
+
http://youtu.be/yKK5J01Dqts?hd=1) showing a live example :)
|
9
|
+
|
10
|
+
Rack-webconsole is a Rack middleware designed to be unobtrusive. With Rails 3,
|
11
|
+
for example, you only have to include the gem in your Gemfile and it already
|
12
|
+
works. Without any configuration.
|
13
|
+
|
14
|
+
Tested with MRI 1.9.2 and ruby-head (1.9.3).
|
15
|
+
|
16
|
+
##Resources
|
17
|
+
|
18
|
+
* [Example video](http://youtu.be/yKK5J01Dqts?hd=1)
|
19
|
+
* [Documentation](http://rubydoc.info/github/codegram/rack-webconsole)
|
20
|
+
|
21
|
+
##Install
|
22
|
+
|
23
|
+
In your Gemfile:
|
24
|
+
|
25
|
+
gem 'rack-webconsole'
|
26
|
+
|
27
|
+
|
28
|
+
##Usage with Rails 3
|
29
|
+
|
30
|
+
If you are using Rails 3, you have no further steps to do. It works! To give
|
31
|
+
it a try, fire up the Rails server and go to any page, press the ` ` ` key and
|
32
|
+
the console will show :)
|
33
|
+
|
34
|
+
##Usage with Sinatra/Padrino
|
35
|
+
|
36
|
+
With Sinatra and Padrino you have to tell your application to use the
|
37
|
+
middleware:
|
38
|
+
|
39
|
+
require 'sinatra'
|
40
|
+
require 'rack/webconsole'
|
41
|
+
|
42
|
+
class MySinatraApp < Sinatra::Application
|
43
|
+
use Rack::Webconsole
|
44
|
+
# . . .
|
45
|
+
end
|
46
|
+
|
47
|
+
class SamplePadrino < Padrino::Application
|
48
|
+
use Rack::Webconsole
|
49
|
+
# . . .
|
50
|
+
end
|
51
|
+
|
52
|
+
NOTE: If you are using Bundler and initializing it from config.ru, you don't
|
53
|
+
have to `require 'rack/webconsole'` manually, otherwise you have to.
|
54
|
+
|
55
|
+
And it works! Fire up the server, go to any page and press the ` ` ` key.
|
56
|
+
|
57
|
+
##Commands
|
58
|
+
|
59
|
+
In the console you can issue whatever Ruby commands you want, except multiline commands. Local variables are kept, so you can get a more IRB-esque feeling.
|
60
|
+
|
61
|
+
To reset all local variables, just issue the `reload!` command.
|
62
|
+
|
63
|
+
##Under the hood
|
64
|
+
|
65
|
+
Run the test suite by typing:
|
66
|
+
|
67
|
+
rake
|
68
|
+
|
69
|
+
You can also build the documentation with the following command:
|
70
|
+
|
71
|
+
rake docs
|
72
|
+
|
73
|
+
## Note on Patches/Pull Requests
|
74
|
+
|
75
|
+
* Fork the project.
|
76
|
+
* Make your feature addition or bug fix.
|
77
|
+
* Add tests for it. This is important so I don't break it in a
|
78
|
+
future version unintentionally.
|
79
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
80
|
+
* Send us a pull request. Bonus points for topic branches.
|
81
|
+
|
82
|
+
## Copyright
|
83
|
+
|
84
|
+
Copyright (c) 2011 Codegram. See LICENSE for details.
|
85
|
+
|
86
|
+
|
87
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Rack
|
3
|
+
class Webconsole
|
4
|
+
# Helper module to encapsulate the asset loading logic used by the {Assets}
|
5
|
+
# middleware.
|
6
|
+
#
|
7
|
+
# For now, the strategy is reading the files from disk. In the future, we
|
8
|
+
# should come up with a somewhat more sophisticated strategy, although
|
9
|
+
# {Webconsole} is used only in development environments, where performance
|
10
|
+
# isn't usually a concern.
|
11
|
+
#
|
12
|
+
module AssetHelpers
|
13
|
+
# Loads the HTML from a file in `/public`.
|
14
|
+
#
|
15
|
+
# It contains a form and the needed divs to render the console.
|
16
|
+
#
|
17
|
+
# @return [String] the injectable HTML.
|
18
|
+
def html_code
|
19
|
+
asset 'webconsole.html'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Loads the CSS from a file in `/public`.
|
23
|
+
#
|
24
|
+
# It contains the styles for the console.
|
25
|
+
#
|
26
|
+
# @return [String] the injectable CSS.
|
27
|
+
def css_code
|
28
|
+
'<style type="text/css">' <<
|
29
|
+
asset('webconsole.css') <<
|
30
|
+
'</style>'
|
31
|
+
end
|
32
|
+
|
33
|
+
# Loads the JavaScript from a file in `/public`.
|
34
|
+
#
|
35
|
+
# It contains the JavaScript logic of the webconsole.
|
36
|
+
#
|
37
|
+
# @return [String] the injectable JavaScript.
|
38
|
+
def js_code
|
39
|
+
'<script type="text/javascript">' <<
|
40
|
+
asset('webconsole.js') <<
|
41
|
+
'</script>'
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def asset(file)
|
47
|
+
::File.read(::File.join(::File.dirname(__FILE__), '..', '..', '..', 'public', file))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Rack
|
3
|
+
class Webconsole
|
4
|
+
# {Assets} is a Rack middleware responsible for injecting view code for the
|
5
|
+
# console to work properly.
|
6
|
+
#
|
7
|
+
# It intercepts HTTP requests, detects successful HTML responses and
|
8
|
+
# injects HTML, CSS and JavaScript code into those.
|
9
|
+
#
|
10
|
+
class Assets
|
11
|
+
include Webconsole::AssetHelpers
|
12
|
+
|
13
|
+
# Honor the Rack contract by saving the passed Rack application in an ivar.
|
14
|
+
#
|
15
|
+
# @param [Rack::Application] app the previous Rack application in the
|
16
|
+
# middleware chain.
|
17
|
+
def initialize(app)
|
18
|
+
@app = app
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks for successful HTML responses and injects HTML, CSS and
|
22
|
+
# JavaScript code into them.
|
23
|
+
#
|
24
|
+
# @param [Hash] env a Rack request environment.
|
25
|
+
def call(env)
|
26
|
+
status, headers, response = @app.call(env)
|
27
|
+
return [status, headers, response] unless check_html?(headers, response) && status == 200
|
28
|
+
|
29
|
+
if response.respond_to?(:body)
|
30
|
+
response_body = response.body
|
31
|
+
else
|
32
|
+
response_body = response.first
|
33
|
+
end
|
34
|
+
|
35
|
+
# Inject the html, css and js code to the view
|
36
|
+
response_body.gsub!('</body>', "#{code}</body>")
|
37
|
+
headers['Content-Length'] = (response_body.length + 2).to_s
|
38
|
+
|
39
|
+
[status, headers, [response_body]]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a string with all the HTML, CSS and JavaScript code needed for
|
43
|
+
# the view.
|
44
|
+
#
|
45
|
+
# @return [String] the injectable code.
|
46
|
+
def code
|
47
|
+
html_code << css_code << js_code
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def check_html?(headers, response)
|
53
|
+
body = response.respond_to?(:body) ? response.body : response.first
|
54
|
+
headers['Content-Type'] and
|
55
|
+
headers['Content-Type'].include? 'text/html' and
|
56
|
+
body =~ %r{<html.*</html>}m
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Rack
|
3
|
+
class Webconsole
|
4
|
+
# Railtie loaded in Rails applications. Its purpose is to automatically use
|
5
|
+
# the middleware in development environment, so that Rails users only have
|
6
|
+
# to require 'rack-webconsole' in their Gemfile and nothing more than that.
|
7
|
+
#
|
8
|
+
class Railtie < Rails::Railtie
|
9
|
+
initializer 'rack-webconsole.add_middleware' do |app|
|
10
|
+
app.middleware.use Rack::Webconsole if Rails.env.development?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'json'
|
3
|
+
module Rack
|
4
|
+
class Webconsole
|
5
|
+
# {Repl} is a Rack middleware acting as a Ruby evaluator application.
|
6
|
+
#
|
7
|
+
# In a nutshell, it evaluates a string in a {Sandbox} instance stored in an
|
8
|
+
# evil global variable. Then, to keep the state, it inspects the local
|
9
|
+
# variables and stores them in an instance variable for further retrieval.
|
10
|
+
#
|
11
|
+
class Repl
|
12
|
+
# Honor the Rack contract by saving the passed Rack application in an ivar.
|
13
|
+
#
|
14
|
+
# @param [Rack::Application] app the previous Rack application in the
|
15
|
+
# middleware chain.
|
16
|
+
def initialize(app)
|
17
|
+
@app = app
|
18
|
+
end
|
19
|
+
|
20
|
+
# Evaluates a string as Ruby code and returns the evaluated result as
|
21
|
+
# JSON.
|
22
|
+
#
|
23
|
+
# It also stores the {Sandbox} state in a `$sandbox` global variable, with
|
24
|
+
# its local variables.
|
25
|
+
#
|
26
|
+
# @param [Hash] env the Rack request environment.
|
27
|
+
# @return [Array] a Rack response with status code 200, HTTP headers
|
28
|
+
# and the evaluated Ruby result.
|
29
|
+
def call(env)
|
30
|
+
status, headers, response = @app.call(env)
|
31
|
+
|
32
|
+
req = Rack::Request.new(env)
|
33
|
+
|
34
|
+
params = req.params
|
35
|
+
|
36
|
+
result = begin
|
37
|
+
$sandbox ||= Sandbox.new
|
38
|
+
|
39
|
+
boilerplate = local_variables + [:ls]
|
40
|
+
|
41
|
+
result = $sandbox.instance_eval """
|
42
|
+
result = (#{params['query']})
|
43
|
+
ls = (local_variables - #{boilerplate})
|
44
|
+
@locals ||= {}
|
45
|
+
@locals.update(ls.inject({}) do |hash, value|
|
46
|
+
hash.update({value => eval(value.to_s)})
|
47
|
+
end)
|
48
|
+
result
|
49
|
+
"""
|
50
|
+
|
51
|
+
result.inspect
|
52
|
+
rescue=>e
|
53
|
+
"Error: " + e.message
|
54
|
+
end
|
55
|
+
response_body = {:result => result}.to_json
|
56
|
+
headers = {}
|
57
|
+
headers['Content-Type'] = 'application/json'
|
58
|
+
headers['Content-Length'] = response_body.length.to_s
|
59
|
+
[200, headers, [response_body]]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Rack
|
3
|
+
class Webconsole
|
4
|
+
# A sandbox to evaluate Ruby in. It is responsible for retrieving local
|
5
|
+
# variables stored in `@locals`, and resetting the environment.
|
6
|
+
#
|
7
|
+
class Sandbox
|
8
|
+
# Catches all the undefined local variables and tries to retrieve them
|
9
|
+
# from `@locals`. If it doesn't find them, it falls back to the default
|
10
|
+
# method missing behavior.
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
@locals ||= {}
|
13
|
+
@locals[method] || super(method, *args, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Makes the console use a fresh, new {Sandbox} with all local variables
|
17
|
+
# resetted.
|
18
|
+
#
|
19
|
+
# @return [String] 'ok' to make the user notice.
|
20
|
+
def reload!
|
21
|
+
$sandbox = Sandbox.new
|
22
|
+
'ok'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rack/webconsole/repl'
|
3
|
+
require 'rack/webconsole/asset_helpers'
|
4
|
+
require 'rack/webconsole/assets'
|
5
|
+
require 'rack/webconsole/sandbox'
|
6
|
+
|
7
|
+
require 'rack/webconsole/railtie' if defined?(Rails)
|
8
|
+
|
9
|
+
# Rack is a modular webserver interface written by Christian Neukirchen.
|
10
|
+
#
|
11
|
+
# Learn more at: https://github.com/rack/rack
|
12
|
+
#
|
13
|
+
module Rack
|
14
|
+
# {Rack::Webconsole} is a Rack middleware that provides an interactive
|
15
|
+
# console à la Rails console, but for any kind of Rack application (Rails,
|
16
|
+
# Sinatra, Padrino...), accessible from your web application's front-end.
|
17
|
+
#
|
18
|
+
# For every request, it normally passes control to the {Assets} middleware,
|
19
|
+
# which injects needed JavaScript, CSS and HTML code for the console to work
|
20
|
+
# properly.
|
21
|
+
#
|
22
|
+
# It also exposes a special route used by the {Repl}, a Ruby evaluator which
|
23
|
+
# is responsible of keeping state between requests, remembering local
|
24
|
+
# variables and giving a true IRB-esque experience.
|
25
|
+
#
|
26
|
+
class Webconsole
|
27
|
+
# Honor the Rack contract by saving the passed Rack application in an ivar.
|
28
|
+
#
|
29
|
+
# @param [Rack::Application] app the previous Rack application in the
|
30
|
+
# middleware chain.
|
31
|
+
def initialize(app)
|
32
|
+
@app = app
|
33
|
+
end
|
34
|
+
|
35
|
+
# Decides where to send the request. In case the path is `/webconsole`
|
36
|
+
# (e.g. when calling the {Repl} endpoint), pass the request onto the
|
37
|
+
# {Repl}. Otherwise, pass it onto the {Assets} middleware, which will
|
38
|
+
# inject the needed assets for the Webconsole to work.
|
39
|
+
#
|
40
|
+
# @param [Hash] env a Rack request environment.
|
41
|
+
def call(env)
|
42
|
+
if env['PATH_INFO'] == '/webconsole'
|
43
|
+
Repl.new(@app).call(env)
|
44
|
+
else
|
45
|
+
Assets.new(@app).call(env)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
body {
|
2
|
+
margin:0;
|
3
|
+
padding:0;
|
4
|
+
}
|
5
|
+
#console {
|
6
|
+
opacity: 0.9;
|
7
|
+
z-index: 999;
|
8
|
+
background: #000;
|
9
|
+
color: #DDD;
|
10
|
+
font-family: monospace;
|
11
|
+
padding-left: 15px;
|
12
|
+
padding-top: 10px;
|
13
|
+
height: 25%;
|
14
|
+
position: fixed;
|
15
|
+
width: 100%;
|
16
|
+
bottom: 0px;
|
17
|
+
border-top: 3px solid #DEDEDE;
|
18
|
+
overflow: hidden;
|
19
|
+
padding-top: 10px;
|
20
|
+
}
|
21
|
+
#console form div, #console form span {
|
22
|
+
font-size: 14px;
|
23
|
+
background: #000;
|
24
|
+
border: 0px;
|
25
|
+
font-family: monospace;
|
26
|
+
color: #FFF;
|
27
|
+
}
|
28
|
+
#console form div.results{
|
29
|
+
position: absolute;
|
30
|
+
bottom: 40px;
|
31
|
+
margin-bottom: -10px;
|
32
|
+
}
|
33
|
+
#console form div.input{
|
34
|
+
width: 97%;
|
35
|
+
position: absolute;
|
36
|
+
bottom: 10px;
|
37
|
+
}
|
38
|
+
#console form div.input input{
|
39
|
+
margin-top: 0px;
|
40
|
+
margin-bottom: 0px;
|
41
|
+
width: 97%;
|
42
|
+
font-size: 14px;
|
43
|
+
background: #000;
|
44
|
+
border: 0px;
|
45
|
+
font-family: monospace;
|
46
|
+
color: #FFF;
|
47
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
|
2
|
+
<div id="console">
|
3
|
+
<form accept-charset="UTF-8" action="/webconsole" method="post">
|
4
|
+
<input name="utf8" type="hidden" value="✓"/>
|
5
|
+
<div class="results">
|
6
|
+
</div>
|
7
|
+
<div class="input">
|
8
|
+
<span>>></span>
|
9
|
+
<input id="query" name="query" type="text" />
|
10
|
+
</div>
|
11
|
+
</form>
|
12
|
+
</div>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
$(document).ready(function() {
|
2
|
+
$("#console").hide();
|
3
|
+
$(this).keypress(function(event) {
|
4
|
+
if (event.which == 96) {
|
5
|
+
$("#console").slideToggle('fast', function() {
|
6
|
+
if ($(this).is(':visible')) {
|
7
|
+
$("#console form input").focus();
|
8
|
+
} else {
|
9
|
+
$("#console form input").blur();
|
10
|
+
}
|
11
|
+
});
|
12
|
+
event.preventDefault();
|
13
|
+
}
|
14
|
+
});
|
15
|
+
});
|
16
|
+
$('#console form').submit(function(e){
|
17
|
+
e.preventDefault();
|
18
|
+
});
|
19
|
+
String.prototype.escapeHTML = function () {
|
20
|
+
return(
|
21
|
+
this.replace(/&/g,'&').
|
22
|
+
replace(/>/g,'>').
|
23
|
+
replace(/</g,'<').
|
24
|
+
replace(/"/g,'"')
|
25
|
+
);
|
26
|
+
};
|
27
|
+
|
28
|
+
$("#console form input").keyup(function(event) {
|
29
|
+
if(event.which == 13) {
|
30
|
+
/*$.post('/webconsole', $("#console form").serialize());*/
|
31
|
+
var query = $("#query").val();
|
32
|
+
$.ajax({
|
33
|
+
url: '/webconsole',
|
34
|
+
type: 'POST',
|
35
|
+
dataType: 'json',
|
36
|
+
data: ({query: query}),
|
37
|
+
success: function (data) {
|
38
|
+
var q = "<div>>> " + query.escapeHTML() + "</div>";
|
39
|
+
var r = "<div>=> " + data.result.escapeHTML() + "</div>";
|
40
|
+
$("#console .results").append(q + r);
|
41
|
+
$("#query").val('');
|
42
|
+
}
|
43
|
+
});
|
44
|
+
}
|
45
|
+
});
|
@@ -0,0 +1,30 @@
|
|
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"
|
7
|
+
s.version = Rack::Webconsole::VERSION
|
8
|
+
s.authors = ["Josep M. Bach", "Josep Jaume Rey", "Oriol Gual"]
|
9
|
+
s.email = ["info@codegram.com"]
|
10
|
+
s.homepage = "http://github.com/codegram/rack-webconsole"
|
11
|
+
s.summary = %q{Rack-based console inside your web applications}
|
12
|
+
s.description = %q{Rack-based console inside your web applications}
|
13
|
+
|
14
|
+
s.rubyforge_project = "rack-webconsole"
|
15
|
+
|
16
|
+
s.add_runtime_dependency 'rack'
|
17
|
+
s.add_runtime_dependency 'json'
|
18
|
+
|
19
|
+
s.add_development_dependency 'minitest'
|
20
|
+
s.add_development_dependency 'purdytest'
|
21
|
+
s.add_development_dependency 'mocha'
|
22
|
+
s.add_development_dependency 'yard'
|
23
|
+
s.add_development_dependency 'bluecloth'
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class AssetClass
|
4
|
+
include Rack::Webconsole::AssetHelpers
|
5
|
+
end
|
6
|
+
|
7
|
+
module Rack
|
8
|
+
describe Webconsole::AssetHelpers do
|
9
|
+
|
10
|
+
describe '#html_code' do
|
11
|
+
it 'loads the html code' do
|
12
|
+
asset_class = AssetClass.new
|
13
|
+
html = asset_class.html_code
|
14
|
+
|
15
|
+
html.must_match /console/
|
16
|
+
html.must_match /results/
|
17
|
+
html.must_match /form/
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#css_code' do
|
22
|
+
it 'loads the css code' do
|
23
|
+
asset_class = AssetClass.new
|
24
|
+
css = asset_class.css_code
|
25
|
+
|
26
|
+
css.must_match /<style/
|
27
|
+
css.must_match /text\/css/
|
28
|
+
css.must_match /#console/
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#js_code' do
|
33
|
+
it 'loads the js code' do
|
34
|
+
asset_class = AssetClass.new
|
35
|
+
js = asset_class.js_code
|
36
|
+
|
37
|
+
js.must_match /\$\("#console"\)/
|
38
|
+
js.must_match /escapeHTML/
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
describe Webconsole::Assets do
|
6
|
+
|
7
|
+
it 'initializes with an app' do
|
8
|
+
@app = stub
|
9
|
+
@assets = Webconsole::Assets.new(@app)
|
10
|
+
|
11
|
+
@assets.instance_variable_get(:@app).must_equal @app
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#call" do
|
15
|
+
|
16
|
+
describe 'when the call is not appropriate to inject the view code' do
|
17
|
+
# Different invalid cases
|
18
|
+
[
|
19
|
+
[200, {'Content-Type' => 'text/html'}, ['Whatever']],
|
20
|
+
[200, {'Content-Type' => 'text/plain'}, ['Hello World']],
|
21
|
+
[404, {'Content-Type' => 'text/html'}, ['Hello World']],
|
22
|
+
[404, {'Content-Type' => 'text/html'}, ['Hello, World']],
|
23
|
+
|
24
|
+
].each do |invalid_response|
|
25
|
+
it 'passes the call untouched' do
|
26
|
+
@app = lambda { |env| invalid_response }
|
27
|
+
|
28
|
+
assets = Webconsole::Assets.new(@app)
|
29
|
+
assets.expects(:inject_code).never
|
30
|
+
|
31
|
+
assets.call({}).last.first.must_equal invalid_response.last.first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'otherwise' do
|
37
|
+
|
38
|
+
it 'injects the view code before the body ending' do
|
39
|
+
|
40
|
+
valid_html = "<!DOCTYPE html>\n<html>\n<head>\n <title>Testapp</title>\n <link href=\"/assets/application.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n <script src=\"/assets/application.js\" type=\"text/javascript\"></script>\n <meta content=\"authenticity_token\" name=\"csrf-param\" />\n<meta content=\"26Ls63zdKBiCXoqU5CuG6KqVbeMYydRqOuovP+DXx8g=\" name=\"csrf-token\" />\n</head>\n<body>\n\n<h1> Hello bitches </h1>\n\n<p> Lorem ipsum dolor sit amet. </p>\n\n\n</body>\n</html>\n"
|
41
|
+
|
42
|
+
html = [valid_html]
|
43
|
+
|
44
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/html'}, html] }
|
45
|
+
|
46
|
+
assets = Webconsole::Assets.new(@app)
|
47
|
+
response = assets.call({}).last.first
|
48
|
+
|
49
|
+
response.must_match /input name/m # html
|
50
|
+
response.must_match /text\/css/m # css
|
51
|
+
response.must_match /escapeHTML/m # js
|
52
|
+
end
|
53
|
+
|
54
|
+
it "works with Rails' particular conception of what a response is" do
|
55
|
+
|
56
|
+
valid_html = "<!DOCTYPE html>\n<html>\n<head>\n <title>Testapp</title>\n <link href=\"/assets/application.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n <script src=\"/assets/application.js\" type=\"text/javascript\"></script>\n <meta content=\"authenticity_token\" name=\"csrf-param\" />\n<meta content=\"26Ls63zdKBiCXoqU5CuG6KqVbeMYydRqOuovP+DXx8g=\" name=\"csrf-token\" />\n</head>\n<body>\n\n<h1> Hello bitches </h1>\n\n<p> Lorem ipsum dolor sit amet. </p>\n\n\n</body>\n</html>\n"
|
57
|
+
|
58
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/html'}, OpenStruct.new({:body => valid_html})] }
|
59
|
+
|
60
|
+
assets = Webconsole::Assets.new(@app)
|
61
|
+
|
62
|
+
response = assets.call({}).last.first
|
63
|
+
|
64
|
+
response.must_match /input name/m # html
|
65
|
+
response.must_match /text\/css/m # css
|
66
|
+
response.must_match /escapeHTML/m # js
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
describe Webconsole::Repl do
|
6
|
+
|
7
|
+
it 'initializes with an app' do
|
8
|
+
@app = stub
|
9
|
+
@repl = Webconsole::Repl.new(@app)
|
10
|
+
|
11
|
+
@repl.instance_variable_get(:@app).must_equal @app
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#call" do
|
15
|
+
it 'evaluates the :query param in a sandbox and returns the result' do
|
16
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
17
|
+
env = {}
|
18
|
+
request = OpenStruct.new(:params => {'query' => 'a = 4; a * 2'})
|
19
|
+
Rack::Request.stubs(:new).returns request
|
20
|
+
|
21
|
+
@repl = Webconsole::Repl.new(@app)
|
22
|
+
|
23
|
+
response = @repl.call(env).last.first
|
24
|
+
|
25
|
+
JSON.parse(response)['result'].must_equal "8"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'maintains local state in subsequent calls thanks to an evil global variable' do
|
29
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
30
|
+
env = {}
|
31
|
+
request = OpenStruct.new(:params => {'query' => 'a = 4'})
|
32
|
+
Rack::Request.stubs(:new).returns request
|
33
|
+
@repl = Webconsole::Repl.new(@app)
|
34
|
+
|
35
|
+
@repl.call(env) # call 1 sets a to 4
|
36
|
+
|
37
|
+
request = OpenStruct.new(:params => {'query' => 'a * 8'})
|
38
|
+
Rack::Request.stubs(:new).returns request
|
39
|
+
|
40
|
+
response = @repl.call(env).last.first # call 2 retrieves a and multiplies it by 8
|
41
|
+
|
42
|
+
JSON.parse(response)['result'].must_equal "32"
|
43
|
+
$sandbox.instance_variable_get(:@locals)[:a].must_equal 4
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns any found errors prepended with 'Error:'" do
|
47
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
48
|
+
env = {}
|
49
|
+
request = OpenStruct.new(:params => {'query' => 'unknown_method'})
|
50
|
+
Rack::Request.stubs(:new).returns request
|
51
|
+
@repl = Webconsole::Repl.new(@app)
|
52
|
+
|
53
|
+
response = @repl.call(env).last.first
|
54
|
+
|
55
|
+
JSON.parse(response)['result'].must_match /Error:/
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
describe Webconsole::Sandbox do
|
5
|
+
|
6
|
+
describe "#method_missing" do
|
7
|
+
describe 'when the method exists in @locals' do
|
8
|
+
it 'retrieves it' do
|
9
|
+
@sandbox = Webconsole::Sandbox.new
|
10
|
+
@sandbox.instance_variable_set(:@locals, {:a => 123})
|
11
|
+
|
12
|
+
@sandbox.a.must_equal 123
|
13
|
+
end
|
14
|
+
end
|
15
|
+
describe 'otherwise' do
|
16
|
+
it 'raises a NoMethodError' do
|
17
|
+
@sandbox = Webconsole::Sandbox.new
|
18
|
+
|
19
|
+
lambda {
|
20
|
+
@sandbox.a
|
21
|
+
}.must_raise NoMethodError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#reload!" do
|
27
|
+
it 'assigns a new, fresh Sandbox to the global variable' do
|
28
|
+
old_sandbox = $sandbox = Webconsole::Sandbox.new
|
29
|
+
|
30
|
+
$sandbox.reload!
|
31
|
+
|
32
|
+
$sandbox.wont_equal old_sandbox
|
33
|
+
end
|
34
|
+
it 'returns a feedback string' do
|
35
|
+
Webconsole::Sandbox.new.reload!.must_equal 'ok'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
describe Webconsole do
|
5
|
+
|
6
|
+
it 'initializes with an app' do
|
7
|
+
@app = stub
|
8
|
+
@webconsole = Webconsole.new(@app)
|
9
|
+
|
10
|
+
@webconsole.instance_variable_get(:@app).must_equal @app
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#call" do
|
14
|
+
it 'delegates the call to the Repl middleware when the path is /webconsole' do
|
15
|
+
@app = stub
|
16
|
+
@webconsole = Webconsole.new(@app)
|
17
|
+
@env = {'PATH_INFO' => '/webconsole'}
|
18
|
+
|
19
|
+
repl = stub
|
20
|
+
Webconsole::Repl.expects(:new).with(@app).returns repl
|
21
|
+
repl.expects(:call).with @env
|
22
|
+
|
23
|
+
@webconsole.call(@env)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'passes the call to the Assets middleware otherwise' do
|
27
|
+
@app = stub
|
28
|
+
@webconsole = Webconsole.new(@app)
|
29
|
+
@env = {'PATH_INFO' => '/whatever'}
|
30
|
+
|
31
|
+
assets = stub
|
32
|
+
Webconsole::Assets.expects(:new).with(@app).returns assets
|
33
|
+
assets.expects(:call).with @env
|
34
|
+
|
35
|
+
@webconsole.call(@env)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-webconsole
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Josep M. Bach
|
9
|
+
- Josep Jaume Rey
|
10
|
+
- Oriol Gual
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
|
15
|
+
date: 2011-07-24 00:00:00 +02:00
|
16
|
+
default_executable:
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: rack
|
20
|
+
prerelease: false
|
21
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: "0"
|
27
|
+
type: :runtime
|
28
|
+
version_requirements: *id001
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: json
|
31
|
+
prerelease: false
|
32
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: "0"
|
38
|
+
type: :runtime
|
39
|
+
version_requirements: *id002
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: minitest
|
42
|
+
prerelease: false
|
43
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id003
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: purdytest
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id004
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mocha
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
type: :development
|
72
|
+
version_requirements: *id005
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: yard
|
75
|
+
prerelease: false
|
76
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: "0"
|
82
|
+
type: :development
|
83
|
+
version_requirements: *id006
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: bluecloth
|
86
|
+
prerelease: false
|
87
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: "0"
|
93
|
+
type: :development
|
94
|
+
version_requirements: *id007
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rake
|
97
|
+
prerelease: false
|
98
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
type: :development
|
105
|
+
version_requirements: *id008
|
106
|
+
description: Rack-based console inside your web applications
|
107
|
+
email:
|
108
|
+
- info@codegram.com
|
109
|
+
executables: []
|
110
|
+
|
111
|
+
extensions: []
|
112
|
+
|
113
|
+
extra_rdoc_files: []
|
114
|
+
|
115
|
+
files:
|
116
|
+
- .gitignore
|
117
|
+
- .rvmrc
|
118
|
+
- Gemfile
|
119
|
+
- Gemfile.lock
|
120
|
+
- Rakefile
|
121
|
+
- Readme.md
|
122
|
+
- lib/rack-webconsole.rb
|
123
|
+
- lib/rack/webconsole.rb
|
124
|
+
- lib/rack/webconsole/asset_helpers.rb
|
125
|
+
- lib/rack/webconsole/assets.rb
|
126
|
+
- lib/rack/webconsole/railtie.rb
|
127
|
+
- lib/rack/webconsole/repl.rb
|
128
|
+
- lib/rack/webconsole/sandbox.rb
|
129
|
+
- lib/rack/webconsole/version.rb
|
130
|
+
- public/webconsole.css
|
131
|
+
- public/webconsole.html
|
132
|
+
- public/webconsole.js
|
133
|
+
- rack-webconsole.gemspec
|
134
|
+
- spec/rack/webconsole/asset_helpers_spec.rb
|
135
|
+
- spec/rack/webconsole/assets_spec.rb
|
136
|
+
- spec/rack/webconsole/repl_spec.rb
|
137
|
+
- spec/rack/webconsole/sandbox_spec.rb
|
138
|
+
- spec/rack/webconsole_spec.rb
|
139
|
+
- spec/spec_helper.rb
|
140
|
+
has_rdoc: true
|
141
|
+
homepage: http://github.com/codegram/rack-webconsole
|
142
|
+
licenses: []
|
143
|
+
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
150
|
+
none: false
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: "0"
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: "0"
|
161
|
+
requirements: []
|
162
|
+
|
163
|
+
rubyforge_project: rack-webconsole
|
164
|
+
rubygems_version: 1.6.1
|
165
|
+
signing_key:
|
166
|
+
specification_version: 3
|
167
|
+
summary: Rack-based console inside your web applications
|
168
|
+
test_files:
|
169
|
+
- spec/rack/webconsole/asset_helpers_spec.rb
|
170
|
+
- spec/rack/webconsole/assets_spec.rb
|
171
|
+
- spec/rack/webconsole/repl_spec.rb
|
172
|
+
- spec/rack/webconsole/sandbox_spec.rb
|
173
|
+
- spec/rack/webconsole_spec.rb
|
174
|
+
- spec/spec_helper.rb
|