rack-webconsole 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ pkg/*
4
+ graph.png
5
+ .yardoc/*
6
+ doc/*
7
+ coverage/*
8
+ *.rbc
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use ruby-1.9.2@rack-webconsole
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rack-webconsole.gemspec
4
+ gemspec
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,7 @@
1
+ # encoding: utf-8
2
+ module Rack
3
+ class Webconsole
4
+ # rack-webconsole version number.
5
+ VERSION = "0.0.1"
6
+ end
7
+ 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,3 @@
1
+ # encoding: utf-8
2
+ require 'rack'
3
+ require 'rack/webconsole'
@@ -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,'&amp;').
22
+ replace(/>/g,'&gt;').
23
+ replace(/</g,'&lt;').
24
+ replace(/"/g,'&quot;')
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
@@ -0,0 +1,8 @@
1
+ gem 'minitest'
2
+ require 'minitest/spec'
3
+ require 'minitest/autorun'
4
+ require 'purdytest'
5
+ require 'mocha'
6
+
7
+ require 'rack-webconsole'
8
+
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