Skip to content
Browse files

adding initial debugging functionality

  • Loading branch information...
1 parent 5e2ca8d commit a00de4d474f9a72488156be1f8a8c078b7f20b02 @ryanb committed Aug 19, 2010
View
19 README.rdoc
@@ -1,3 +1,20 @@
= Enlighten
-Stay tuned...
+Use ruby-debug through the browser! This is intended to be a convenient way to debug web applications but it is still in very early development. For those feeling adventerous, here's how to get it set up.
+
+== Setup
+
+This will be available as a gem but is not yet. It is currently just a rack application which you can run after downloading this repository.
+
+ rackup config.ru
+
+If you browse to the app it will say not yet enlightened. You'll need to trigger ruby-debug remotely in a separate script like this.
+
+ require "rubygems"
+ require "ruby-debug"
+ Debugger.wait_connection = true
+ Debugger.start_remote
+
+ debugger # call anywhere you want to debug
+
+After this, visit the Enlighten app again and you should see some debugging information as well as an interactive prompt. Much more will be added to improve the experience so stay tuned.
View
30 lib/enlighten/application.rb
@@ -1,6 +1,6 @@
module Enlighten
class Application
- attr_accessor :socket
+ attr_writer :debugger
def call(env)
request = Rack::Request.new(env)
@@ -12,8 +12,7 @@ def call(env)
end
def call_debugger(command, params)
- @socket.puts command + " " + params["code"]
- respond_with(socket_response)
+ respond_with(@debugger.eval_code(params["code"]))
end
def render_index
@@ -38,29 +37,12 @@ def view_path
end
def connect_to_debugger
- if @socket.nil?
- @socket = TCPSocket.new("localhost", 8989)
- socket_response # get to first prompt
+ if @debugger.nil?
+ @debugger = Debugger.new(TCPSocket.new("localhost", 8989))
+ @debugger.socket_response # get to first prompt
end
rescue Errno::ECONNREFUSED => e
- @socket = nil
- end
-
- def socket_response
- continue = true
- response = []
- while continue && line = @socket.gets
- print line
- case line
- when /^PROMPT (.*)$/
- continue = false
- when /^CONFIRM (.*)$/
- socket.puts "y"
- else
- response << line
- end
- end
- response.join
+ @debugger = nil
end
end
end
View
76 lib/enlighten/debugger.rb
@@ -0,0 +1,76 @@
+module Enlighten
+ class Debugger
+ def initialize(socket)
+ @socket = socket
+ end
+
+ def eval_code(code)
+ run_command("eval " + code)
+ end
+
+ def list
+ (run_command("list").split("\n")[1..-1] || []).map do |line|
+ pieces = line.scan(/^(\=\>| ) (\d+) (.*)/).first
+ pieces[0] = (pieces[0] == "=>")
+ pieces
+ end
+ end
+
+ def backtrace
+ run_command("backtrace").split("\n")[0..-2].map do |line|
+ pieces = line.scan(/^(\-\-\>| ) \#(\d+) (.*)/).first
+ pieces[0] = (pieces[0] == "-->")
+ pieces
+ end
+ end
+
+ def breakpoint(file, line)
+ run_command("break #{file}:#{line}")[/\d+/]
+ end
+
+ def delete_breakpoint(num = nil)
+ run_command(["delete", num].compact.join(" "))
+ end
+
+ def next
+ run_command("next")
+ end
+
+ def step
+ run_command("step")
+ end
+
+ def local_variables
+ run_command("info locals").split("\n").map do |variable|
+ variable.split(" = ")
+ end
+ end
+
+ def instance_variables
+ run_command("info instance_variables").split("\n").map do |variable|
+ variable.split(" = ")
+ end
+ end
+
+ def run_command(command)
+ @socket.puts(command)
+ socket_response
+ end
+
+ def socket_response
+ continue = true
+ response = []
+ while continue && line = @socket.gets
+ case line
+ when /^PROMPT (.*)$/
+ continue = false
+ when /^CONFIRM (.*)$/
+ @socket.puts "y"
+ else
+ response << line.chomp
+ end
+ end
+ response.join("\n")
+ end
+ end
+end
View
6 lib/enlighten/public/javascripts/enlighten.js
@@ -1,8 +1,8 @@
$(function() {
- $("#console").submit(function() {
+ $("#prompt form").submit(function() {
$.post(this.action, $(this).serialize(), function(result) {
- $("#prompt_section").before("<pre>&gt;&gt; " + $("#prompt").val() + "<br />" + "=&gt; " + result + "</pre>");
- $("#prompt").val("");
+ $("#prompt_section").before("<pre>&gt;&gt; " + $("#prompt_field").val() + "\n" + "<span class='response'>=&gt; " + result + "</span></pre>");
+ $("#prompt_field").val("");
});
return false;
});
View
55 lib/enlighten/public/stylesheets/enlighten.css
@@ -1,18 +1,53 @@
body {
- background-color: #777;
+ background-color: #FFF;
+ font-family: Verdana;
}
-#console {
- width: 80%;
- margin: auto;
- background-color: #333;
- color: #FFF;
- padding: 5px 10px;
- border: solid 1px #000;
- font-family: Menlo, Monaco, Courier;
- line-height: 1.5em;
+#side {
+ float: right;
+ margin-right: 40px;
+}
+
+.backtrace {
+ margin-top: 30px;
+}
+
+table.code {
+ margin-top: 10px;
+ border-collapse: collapse;
+}
+
+pre {
+ margin: 0;
+ padding: 0;
+}
+
+.code .num {
+ text-align: right;
+ padding-right: 20px;
+}
+
+.code .current td {
+ background-color: #63EF6A;
+}
+
+#prompt_section pre {
+ display: inline;
}
#prompt {
+ min-width: 400px;
+}
+
+#prompt .response {
+ color: #999;
+}
+
+#prompt_cursor {
+ vertical-align: bottom;
+ padding-bottom: 5px;
+}
+
+#prompt_field {
width: 300px;
}
View
41 lib/enlighten/views/index.html.erb
@@ -7,14 +7,41 @@
<link rel="stylesheet" href="/stylesheets/enlighten.css" type="text/css" charset="utf-8" />
</head>
<body>
- <% if @socket %>
- <form id="console" action="/execute" method="post">
- <div id="prompt_section">
- &gt;&gt;
- <input type="text" name="prompt" value="" id="prompt" />
- <input type="submit" value="Send">
+ <% if @debugger %>
+ <div id="side">
+ <strong>Variables</strong><br />
+ <% @debugger.local_variables.each do |name, value| %>
+ <%= name %> = <%= value %><br />
+ <% end %>
+ <% @debugger.instance_variables.each do |name, value| %>
+ <%= name %> = <%= value %><br />
+ <% end %>
+ </div>
+ <div id="prompt">
+ <form action="/debugger/eval" method="post">
+ <div id="prompt_section">
+ <pre>&gt;&gt; </pre>
+ <input type="text" name="code" value="" id="prompt_field" />
+ <input type="submit" value="Run">
+ </div>
+ </form>
+ </div>
+ <% @debugger.backtrace.each do |current, frame_num, description| %>
+ <div class="backtrace">
+ <strong>#<%= frame_num %>
+ <%= description %></strong>
+ <% if current %>
+ <table class="code">
+ <% @debugger.list.each do |current, num, line| %>
+ <tr class="<%= current ? "current" : "not_current" %>">
+ <td class="num"><pre><%= num %></pre></td>
+ <td class="line"><pre><%= line %></pre></td>
+ </tr>
+ <% end %>
+ </table>
+ <% end %>
</div>
- </form>
+ <% end %>
<% else %>
<p>Not yet enlightened.</p>
<p>Unable to establish a connection to the ruby-debug server. Make sure a "debugger" statement has been triggered and try again.</p>
View
3 lib/enlighten_app.rb
@@ -1,4 +1,5 @@
require "erb"
require "rack"
-require "enlighten/application"
+require File.expand_path("../enlighten/application", __FILE__)
+require File.expand_path("../enlighten/debugger", __FILE__)
View
24 spec/enlighten/application_spec.rb
@@ -11,25 +11,15 @@
@request.get("/").body.should include("Not yet enlightened")
end
- describe "/" do
- before(:each) do
- @app.socket = MockDebuggerSocket.new
- @response = @request.get("/")
- end
-
- it "should render an HTML view" do
- @response.body.should include("<html")
- end
-
- it "should include text input" do
- @response.body.should include("<input")
- end
+ it "/ should be an html document" do
+ @app.debugger = Enlighten::Debugger.new(MockDebuggerSocket.new)
+ @request.get("/").body.should include("<html")
end
it "/debugger/eval should evaluate and return response" do
- @app.socket = MockDebuggerSocket.new
- mock(@app.socket).puts("eval chunky")
- @app.socket.buffer << "bacon"
- @request.get("/debugger/eval?code=chunky").body.should == "bacon\n"
+ debugger = Enlighten::Debugger.new(MockDebuggerSocket.new)
+ @app.debugger = debugger
+ stub(debugger).eval_code("chunky") { "bacon" }
+ @request.get("/debugger/eval?code=chunky").body.should == "bacon"
end
end
View
69 spec/enlighten/debugger_spec.rb
@@ -0,0 +1,69 @@
+require "spec_helper"
+
+describe Enlighten::Debugger do
+ before(:each) do
+ @socket = MockDebuggerSocket.new
+ @debugger = Enlighten::Debugger.new(@socket)
+ end
+
+ it "should evaluate and return response" do
+ mock(@socket).puts("eval chunky")
+ @socket.buffer << "bacon\n"
+ @debugger.eval_code("chunky").should == "bacon"
+ end
+
+ it "should list source code and split into numbered lines" do
+ mock(@socket).puts("list")
+ @socket.buffer << "[8, 10] in test.rb\n 8 \n 9 chunky\n=> 10 bacon\n"
+ @debugger.list.should == [[false, "8", ""], [false, "9", "chunky"], [true, "10", " bacon"]]
+ end
+
+ it "list backtrace and point to current one" do
+ mock(@socket).puts("backtrace")
+ @socket.buffer << "--> \#0 Object.foo at line foo.rb:11\n \#1 at line foo.rb:13\nWarning: ...\n"
+ @debugger.backtrace.should == [[true, "0", "Object.foo at line foo.rb:11"], [false, "1", "at line foo.rb:13"]]
+ end
+
+ it "delete all breakpoints" do
+ mock(@socket).puts("delete")
+ @debugger.delete_breakpoint
+ end
+
+ it "delete a specific breakpoint" do
+ mock(@socket).puts("delete 123")
+ @debugger.delete_breakpoint(123)
+ end
+
+ it "delete a specific breakpoint" do
+ mock(@socket).puts("delete 123")
+ @debugger.delete_breakpoint(123)
+ end
+
+ it "set breakpoint for a specific file and line" do
+ mock(@socket).puts("break foo.rb:14")
+ @socket.buffer << "Breakpoint 12 file foo.rb, line 14\n"
+ @debugger.breakpoint("foo.rb", "14").should == "12"
+ end
+
+ it "next should run next command" do
+ mock(@socket).puts("next")
+ @debugger.next
+ end
+
+ it "step should run step command" do
+ mock(@socket).puts("step")
+ @debugger.step
+ end
+
+ it "should parse out local variables" do
+ mock(@socket).puts("info locals")
+ @socket.buffer << "foo = 123\nchunky = :bacon\n"
+ @debugger.local_variables.should == [["foo", "123"], ["chunky", ":bacon"]]
+ end
+
+ it "should parse out instance variables" do
+ mock(@socket).puts("info instance_variables")
+ @socket.buffer << "@foo = 123\n@chunky = :bacon\n"
+ @debugger.instance_variables.should == [["@foo", "123"], ["@chunky", ":bacon"]]
+ end
+end
View
2 spec/spec_helper.rb
@@ -17,6 +17,6 @@ def puts(content)
end
def gets
- (@buffer.shift || "PROMPT (rdb:1) ") + "\n"
+ @buffer.shift || "PROMPT (rdb:1) \n"
end
end
View
8 test_debugger.rb
@@ -6,6 +6,12 @@
before = true
puts "before"
-debugger
+def foo
+ foo = "bar"
+ @test = 1
+ debugger
+ nil
+end
+foo
after = true
puts "after"

0 comments on commit a00de4d

Please sign in to comment.
Something went wrong with that request. Please try again.