rack-webconsole-pry 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ pkg/*
4
+ graph.png
5
+ .yardoc/*
6
+ doc/*
7
+ coverage/*
8
+ *.rbc
9
+ tags
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ # https://github.com/travis-ci/travis-ci/wiki/.travis.yml-options
2
+ rvm:
3
+ - 1.8.7 # (current default)
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ree
7
+ - ruby-head
8
+ - rbx-18mode
9
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rack-webconsole.gemspec
4
+ gemspec
5
+ gem 'rake'
6
+ gem 'mocha' #, :git => 'git://github.com/floehopper/mocha.git'
data/Gemfile.lock ADDED
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rack-webconsole-pry (0.1.4)
5
+ multi_json (>= 1.0.3)
6
+ pry
7
+ rack
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ bluecloth (2.2.0)
13
+ coderay (1.0.6)
14
+ metaclass (0.0.1)
15
+ method_source (0.7.1)
16
+ minitest (2.11.4)
17
+ mocha (0.10.5)
18
+ metaclass (~> 0.0.1)
19
+ multi_json (1.3.6)
20
+ pry (0.9.9.6)
21
+ coderay (~> 1.0.5)
22
+ method_source (~> 0.7.1)
23
+ slop (>= 2.4.4, < 3)
24
+ purdytest (1.0.0)
25
+ minitest (~> 2.2)
26
+ rack (1.4.1)
27
+ rake (0.9.2.2)
28
+ slop (2.4.4)
29
+ yard (0.7.5)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bluecloth
36
+ minitest
37
+ mocha
38
+ pry
39
+ purdytest
40
+ rack-webconsole-pry!
41
+ rake
42
+ yard
data/History ADDED
@@ -0,0 +1,27 @@
1
+ === 0.1.3 / 2012-03-22
2
+
3
+ + Make keycode configurable (Roger Leite)
4
+ + Switch to multi_json (Josh Buddy)
5
+ ! Fix markup to make it work in more browsers
6
+
7
+ === 0.1.2 / 2011-08-01
8
+
9
+ + Change field name to avoid conflicts with other forms (Chris Apolzon)
10
+ + Prevent visual flash of unstyled content (Rob Cameron)
11
+ + Fix minor styling issues (Jeff Kreeftmeijer)
12
+ + Avoid conflicts with libraries other than JQuery (Jo Liss)
13
+
14
+ === 0.1.1 / 2011-07-27
15
+
16
+ ! Fix bug with Content-Length not being calculated appropriately. (Corin Langosch)
17
+ + Refactor JavaScript to avoid messing with prototypes (Corin Langosch)
18
+
19
+ === 0.1.0 / 2011-07-27
20
+
21
+ + The request object is now exposed in the console through #request method
22
+ + Various UI enhancements
23
+ ! Fix bug where Sandbox locals were much more than those defined by the user.
24
+
25
+ === 0.0.5 / 2011-07-26
26
+
27
+ ! Protection against CSRF attacks.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
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
+ task :doc => [:docs]
18
+
19
+ desc "Generate and open class diagram (needs Graphviz installed)"
20
+ task :graph do |t|
21
+ `bundle exec yard graph -d --full --no-private | dot -Tpng -o graph.png && open graph.png`
22
+ end
23
+ task :default => [:test]
data/Readme.md ADDED
@@ -0,0 +1,131 @@
1
+ #rack-webconsole [![Build Status](http://travis-ci.org/mrbrdo/rack-webconsole.png)](http://travis-ci.org/mrbrdo/rack-webconsole.png)
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 showing a live example :)
8
+
9
+ [![YouTube video](http://img.youtube.com/vi/yKK5J01Dqts/0.jpg)](http://youtu.be/yKK5J01Dqts?hd=1)
10
+
11
+ Rack-webconsole is a Rack middleware designed to be unobtrusive. With Rails 3,
12
+ for example, you only have to include the gem in your Gemfile and it already
13
+ works. Without any configuration.
14
+
15
+ Tested with MRI versions 1.8.7, 1.9.2, ruby-head, and JRuby 1.6.3.
16
+
17
+ **SECURITY NOTE**: From version v0.0.5 rack-webconsole uses a token system to
18
+ protect against cross-site request forgery.
19
+
20
+ ##Changes from original rack-webconsole
21
+
22
+ * Uses pry instead of ripl
23
+ * Supports colors
24
+
25
+
26
+ ##Resources
27
+
28
+ * [Example video](http://youtu.be/yKK5J01Dqts?hd=1)
29
+ * [Documentation](http://rubydoc.info/github/codegram/rack-webconsole)
30
+
31
+
32
+ ##Install
33
+
34
+ In your Gemfile:
35
+
36
+ ```ruby
37
+ gem 'rack-webconsole'
38
+ ```
39
+
40
+ Rack-webconsole **needs JQuery**. If you are using Rails 3, JQuery is loaded by
41
+ default. In case you don't want to use JQuery in your application,
42
+ **rack-webconsole can inject it for you** only when it needs it. To do that you
43
+ should put this line somewhere in your application (a Rails initializer, or
44
+ some configuration file):
45
+
46
+ ```ruby
47
+ Rack::Webconsole.inject_jquery = true
48
+ ```
49
+
50
+ You can also change the javascript key_code used to start webconsole:
51
+
52
+ ```ruby
53
+ # ` = 96 (default), ^ = 94, ç = 231 ... etc.
54
+ Rack::Webconsole.key_code = "231"
55
+ ```
56
+
57
+ ##Usage with Rails 3
58
+
59
+ If you are using Rails 3, you have no further steps to do. It works! To give
60
+ it a try, fire up the Rails server and go to any page, press the ` ` ` key and
61
+ the console will show :)
62
+
63
+ ##Usage with Sinatra/Padrino
64
+
65
+ With Sinatra and Padrino you have to tell your application to use the
66
+ middleware:
67
+
68
+ ```ruby
69
+ require 'sinatra'
70
+ require 'rack/webconsole'
71
+
72
+ class MySinatraApp < Sinatra::Application
73
+ use Rack::Webconsole
74
+ # . . .
75
+ end
76
+
77
+ class SamplePadrino < Padrino::Application
78
+ use Rack::Webconsole
79
+ # . . .
80
+ end
81
+ ```
82
+
83
+ NOTE: If you are using Bundler and initializing it from config.ru, you don't
84
+ have to `require 'rack/webconsole'` manually, otherwise you have to.
85
+
86
+ And it works! Fire up the server, go to any page and press the ` ` ` key.
87
+
88
+ ##Usage with Rails 2
89
+
90
+ You need to add the following code to an intializer (i.e. config/initializers/webconsole.rb):
91
+
92
+ ```ruby
93
+ require 'rack/webconsole'
94
+ ActionController::Dispatcher.middleware.insert_after 1, Rack::Webconsole
95
+ ```
96
+
97
+ ##Commands
98
+
99
+ 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.
100
+
101
+ * `reload!` resets all local variables
102
+ * `request` returns the current page request object
103
+
104
+ ##Under the hood
105
+
106
+ Run the test suite by typing:
107
+
108
+ rake
109
+
110
+ You can also build the documentation with the following command:
111
+
112
+ rake docs
113
+
114
+ ## Note on Patches/Pull Requests
115
+
116
+ * Fork the project.
117
+ * Make your feature addition or bug fix.
118
+ * Add tests for it. This is important so we don't break it in a
119
+ future version unintentionally.
120
+ * 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 we can ignore when we pull)
121
+ * Send us a pull request. Bonus points for topic branches.
122
+
123
+ ## Released under the MIT License
124
+
125
+ Copyright (c) 2011 Codegram.
126
+
127
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
128
+
129
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
130
+
131
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,73 @@
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
+ out = ""
20
+ out << asset('jquery.html') if Webconsole.inject_jquery
21
+ out << asset('webconsole.html')
22
+ out
23
+ end
24
+
25
+ # Loads the CSS from a file in `/public`.
26
+ #
27
+ # It contains the styles for the console.
28
+ #
29
+ # @return [String] the injectable CSS.
30
+ def css_code
31
+ '<style type="text/css">' <<
32
+ asset('webconsole.css') <<
33
+ '</style>'
34
+ end
35
+
36
+ # Loads the JavaScript from a file in `/public`.
37
+ #
38
+ # It contains the JavaScript logic of the webconsole.
39
+ #
40
+ # @return [String] the injectable JavaScript.
41
+ def js_code
42
+ '<script type="text/javascript">' <<
43
+ asset('webconsole.js') <<
44
+ '</script>'
45
+ end
46
+
47
+ # Inteprolates the given variables inside the javascrpt code
48
+ #
49
+ # @param [String] javascript The javascript code to insert the variables
50
+ # @param [Hash] variables A hash containing the variables names (as keys)
51
+ # and its values
52
+ #
53
+ # @return [String] the javascript code with the interpolated variables
54
+ def render(javascript, variables = {})
55
+ javascript_with_variables = javascript.dup
56
+ variables.each_pair do |variable, value|
57
+ javascript_with_variables.gsub!("$#{variable}", value)
58
+ end
59
+ javascript_with_variables
60
+ end
61
+
62
+ private
63
+
64
+ def asset(file)
65
+ @assets ||= {}
66
+ output = ::File.open(::File.join(::File.dirname(__FILE__), '..', '..', '..', 'public', file), 'r:UTF-8') do |f|
67
+ f.read
68
+ end
69
+ @assets[file] ||= output
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,72 @@
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
+ # Regenerate the security token
36
+ Webconsole::Repl.reset_token
37
+
38
+ # Expose the request object to the Repl
39
+ Webconsole::Repl.request = Rack::Request.new(env)
40
+
41
+ # Inject the html, css and js code to the view
42
+ response_body.gsub!('</body>', "#{code}</body>")
43
+
44
+ headers['Content-Length'] = response_body.bytesize.to_s
45
+
46
+ [status, headers, [response_body]]
47
+ end
48
+
49
+ # Returns a string with all the HTML, CSS and JavaScript code needed for
50
+ # the view.
51
+ #
52
+ # It puts the security token inside the JavaScript to make AJAX calls
53
+ # secure.
54
+ #
55
+ # @return [String] the injectable code.
56
+ def code
57
+ html_code <<
58
+ css_code <<
59
+ render(js_code, :TOKEN => Webconsole::Repl.token, :KEY_CODE => Webconsole.key_code)
60
+ end
61
+
62
+ private
63
+
64
+ def check_html?(headers, response)
65
+ body = response.respond_to?(:body) ? response.body : response.first
66
+ headers['Content-Type'] and
67
+ headers['Content-Type'].include? 'text/html' and
68
+ body =~ %r{<html.*</html>}m
69
+ end
70
+ end
71
+ end
72
+ 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,113 @@
1
+ # encoding: utf-8
2
+ require 'multi_json'
3
+ require 'digest/sha1'
4
+ require 'pry'
5
+
6
+ module Rack
7
+ class Webconsole
8
+ # {Repl} is a Rack middleware acting as a Ruby evaluator application.
9
+ #
10
+ # In a nutshell, it evaluates a string in a {Sandbox} instance stored in an
11
+ # evil global variable. Then, to keep the state, it inspects the local
12
+ # variables and stores them in an instance variable for further retrieval.
13
+ #
14
+ class Repl
15
+ @@request = nil
16
+ @@token = nil
17
+
18
+ class << self
19
+ # Returns the autogenerated security token
20
+ #
21
+ # @return [String] the autogenerated token
22
+ def token
23
+ @@token
24
+ end
25
+
26
+ # Regenerates the token.
27
+ def reset_token
28
+ @@token = Digest::SHA1.hexdigest("#{rand(36**8)}#{Time.now}")[4..20]
29
+ end
30
+
31
+ # Returns the original request for inspection purposes.
32
+ #
33
+ # @return [Rack::Request] the original request
34
+ def request
35
+ @@request
36
+ end
37
+
38
+ # Sets the original request for inspection purposes.
39
+ #
40
+ # @param [Rack::Request] the original request
41
+ def request=(request)
42
+ @@request = request
43
+ end
44
+ end
45
+
46
+ # Honor the Rack contract by saving the passed Rack application in an ivar.
47
+ #
48
+ # @param [Rack::Application] app the previous Rack application in the
49
+ # middleware chain.
50
+ def initialize(app)
51
+ @app = app
52
+ end
53
+
54
+ # Evaluates a string as Ruby code and returns the evaluated result as
55
+ # JSON.
56
+ #
57
+ # It also stores the {Sandbox} state in a `$sandbox` global variable, with
58
+ # its local variables.
59
+ #
60
+ # @param [Hash] env the Rack request environment.
61
+ # @return [Array] a Rack response with status code 200, HTTP headers
62
+ # and the evaluated Ruby result.
63
+ def call(env)
64
+ status, headers, response = @app.call(env)
65
+
66
+ req = Rack::Request.new(env)
67
+ params = req.params
68
+
69
+ return [status, headers, response] unless check_legitimate(req)
70
+
71
+ hash = {}
72
+ $pry_output ||= StringIO.new("")
73
+ $pry_output.string = ""
74
+ if $pry.nil?
75
+ Pry.pager = false
76
+ $pry = Pry.new(:output => $pry_output, :pager => false)
77
+ end
78
+ pry = $pry
79
+
80
+ # repl loop
81
+ target = Pry.binding_for(pry.binding_stack.last || TOPLEVEL_BINDING)
82
+ pry.repl_prologue(target) unless pry.binding_stack.last == target
83
+ pry.inject_sticky_locals(target)
84
+ code = params['query']
85
+ hash[:prompt] = pry.select_prompt("", target) + Pry::Code.new(code).to_s
86
+ begin
87
+ if !pry.process_command(code, "", target)
88
+ result = target.eval(code, Pry.eval_path, Pry.current_line)
89
+ pry.set_last_result(result, target, code)
90
+ pry.show_result(result) if pry.should_print?
91
+ end
92
+ rescue StandardError => e
93
+ $pry_output.write("Error: " + e.message)
94
+ end
95
+ # cleanup (supposed to call when $pry is destroyed)
96
+ # pry.repl_epilogue(target)
97
+
98
+ hash[:result] = $pry_output.string
99
+ response_body = MultiJson.encode(hash)
100
+ headers = {}
101
+ headers['Content-Type'] = 'application/json'
102
+ headers['Content-Length'] = response_body.bytesize.to_s
103
+ [200, headers, [response_body]]
104
+ end
105
+
106
+ private
107
+
108
+ def check_legitimate(req)
109
+ req.post? && !Repl.token.nil? && req.params['token'] == Repl.token
110
+ end
111
+ end
112
+ end
113
+ end