maglev-webtools 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +25 -0
- data/README.rdoc +121 -0
- data/bin/webtools +15 -0
- data/lib/web_tools/#debugger.rb# +212 -0
- data/lib/web_tools/browser.rb +45 -0
- data/lib/web_tools/debugger.rb +219 -0
- data/lib/web_tools/info.rb +29 -0
- data/lib/web_tools/middleware/debugger.rb +118 -0
- data/lib/web_tools/support/app_model.rb +117 -0
- data/lib/web_tools/support/code_browser.rb +109 -0
- data/lib/web_tools/support/ruby.rb +132 -0
- data/lib/web_tools/support/service_helper.rb +22 -0
- data/lib/web_tools/support/smalltalk_extensions.rb +65 -0
- data/lib/web_tools/support/smalltalk_tools.rb +16 -0
- data/lib/web_tools/ui.rb +67 -0
- data/lib/web_tools.rb +10 -0
- data/public/images/favicon.ico +0 -0
- data/public/javascript/CodeMirror/LICENSE +23 -0
- data/public/javascript/CodeMirror/css/Smalltalk.css +34 -0
- data/public/javascript/CodeMirror/js/codemirror.js +582 -0
- data/public/javascript/CodeMirror/js/editor.js +1671 -0
- data/public/javascript/CodeMirror/js/highlight.js +68 -0
- data/public/javascript/CodeMirror/js/parseSmalltalk.js +126 -0
- data/public/javascript/CodeMirror/js/parsedummy.js +32 -0
- data/public/javascript/CodeMirror/js/select.js +699 -0
- data/public/javascript/CodeMirror/js/stringstream.js +159 -0
- data/public/javascript/CodeMirror/js/tokenize.js +57 -0
- data/public/javascript/CodeMirror/js/undo.js +413 -0
- data/public/javascript/CodeMirror/js/util.js +133 -0
- data/public/javascript/CodeMirror/testSmalltalkParser.html +116 -0
- data/public/javascript/ace/ace-uncompressed.js +17299 -0
- data/public/javascript/ace/ace.js +1 -0
- data/public/javascript/ace/keybinding-emacs.js +1 -0
- data/public/javascript/ace/keybinding-vim.js +1 -0
- data/public/javascript/ace/mode-c_cpp.js +1 -0
- data/public/javascript/ace/mode-clojure.js +1 -0
- data/public/javascript/ace/mode-coffee.js +1 -0
- data/public/javascript/ace/mode-csharp.js +1 -0
- data/public/javascript/ace/mode-css.js +1 -0
- data/public/javascript/ace/mode-groovy.js +1 -0
- data/public/javascript/ace/mode-html.js +1 -0
- data/public/javascript/ace/mode-java.js +1 -0
- data/public/javascript/ace/mode-javascript.js +1 -0
- data/public/javascript/ace/mode-json.js +1 -0
- data/public/javascript/ace/mode-lua.js +1 -0
- data/public/javascript/ace/mode-markdown.js +1 -0
- data/public/javascript/ace/mode-ocaml.js +1 -0
- data/public/javascript/ace/mode-perl.js +1 -0
- data/public/javascript/ace/mode-php.js +1 -0
- data/public/javascript/ace/mode-python.js +1 -0
- data/public/javascript/ace/mode-ruby.js +1 -0
- data/public/javascript/ace/mode-scad.js +1 -0
- data/public/javascript/ace/mode-scala.js +1 -0
- data/public/javascript/ace/mode-scss.js +1 -0
- data/public/javascript/ace/mode-svg.js +1 -0
- data/public/javascript/ace/mode-textile.js +1 -0
- data/public/javascript/ace/mode-xml.js +1 -0
- data/public/javascript/ace/theme-clouds.js +1 -0
- data/public/javascript/ace/theme-clouds_midnight.js +1 -0
- data/public/javascript/ace/theme-cobalt.js +1 -0
- data/public/javascript/ace/theme-crimson_editor.js +1 -0
- data/public/javascript/ace/theme-dawn.js +1 -0
- data/public/javascript/ace/theme-eclipse.js +1 -0
- data/public/javascript/ace/theme-idle_fingers.js +1 -0
- data/public/javascript/ace/theme-kr_theme.js +1 -0
- data/public/javascript/ace/theme-merbivore.js +1 -0
- data/public/javascript/ace/theme-merbivore_soft.js +1 -0
- data/public/javascript/ace/theme-mono_industrial.js +1 -0
- data/public/javascript/ace/theme-monokai.js +1 -0
- data/public/javascript/ace/theme-pastel_on_dark.js +1 -0
- data/public/javascript/ace/theme-solarized_dark.js +1 -0
- data/public/javascript/ace/theme-solarized_light.js +1 -0
- data/public/javascript/ace/theme-textmate.js +1 -0
- data/public/javascript/ace/theme-twilight.js +1 -0
- data/public/javascript/ace/theme-vibrant_ink.js +1 -0
- data/public/javascript/ace/worker-coffee.js +1 -0
- data/public/javascript/ace/worker-css.js +1 -0
- data/public/javascript/ace/worker-javascript.js +1 -0
- data/public/javascript/webtools/#debugger.coffee# +253 -0
- data/public/javascript/webtools/browser.js +260 -0
- data/public/javascript/webtools/debugger.coffee +286 -0
- data/public/javascript/webtools/debugger.js +366 -0
- data/public/javascript/webtools/sessions.coffee +17 -0
- data/public/javascript/webtools/sessions.js +27 -0
- data/public/javascript/webtools/version.coffee +14 -0
- data/public/javascript/webtools/version.js +20 -0
- data/public/stylesheets/base/images/ui-anim_basic_16x16.gif +0 -0
- data/public/stylesheets/base/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/public/stylesheets/base/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/public/stylesheets/base/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/public/stylesheets/base/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/public/stylesheets/base/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/public/stylesheets/base/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/public/stylesheets/base/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/public/stylesheets/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/public/stylesheets/base/images/ui-icons_222222_256x240.png +0 -0
- data/public/stylesheets/base/images/ui-icons_2e83ff_256x240.png +0 -0
- data/public/stylesheets/base/images/ui-icons_454545_256x240.png +0 -0
- data/public/stylesheets/base/images/ui-icons_888888_256x240.png +0 -0
- data/public/stylesheets/base/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/public/stylesheets/base/jquery.ui.accordion.css +12 -0
- data/public/stylesheets/base/jquery.ui.all.css +2 -0
- data/public/stylesheets/base/jquery.ui.autocomplete.css +39 -0
- data/public/stylesheets/base/jquery.ui.base.css +11 -0
- data/public/stylesheets/base/jquery.ui.button.css +35 -0
- data/public/stylesheets/base/jquery.ui.core.css +37 -0
- data/public/stylesheets/base/jquery.ui.datepicker.css +61 -0
- data/public/stylesheets/base/jquery.ui.dialog.css +13 -0
- data/public/stylesheets/base/jquery.ui.progressbar.css +4 -0
- data/public/stylesheets/base/jquery.ui.resizable.css +13 -0
- data/public/stylesheets/base/jquery.ui.selectable.css +3 -0
- data/public/stylesheets/base/jquery.ui.slider.css +17 -0
- data/public/stylesheets/base/jquery.ui.tabs.css +11 -0
- data/public/stylesheets/base/jquery.ui.theme.css +247 -0
- data/public/stylesheets/jquery.contextMenu.css +62 -0
- data/public/stylesheets/reset.css +18 -0
- data/public/stylesheets/webtools.css +53 -0
- data/public/test.html +47 -0
- data/views/browser.rhtml +63 -0
- data/views/debugger.rhtml +59 -0
- data/views/index.rhtml +8 -0
- data/views/layout.rhtml +24 -0
- data/views/sessions.rhtml +12 -0
- data/views/version.rhtml +10 -0
- metadata +316 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
== LICENSE:
|
2
|
+
|
3
|
+
(The MIT License)
|
4
|
+
|
5
|
+
Copyright (c) 2011 VMware, Inc
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
'Software'), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
21
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
22
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
23
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
24
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
= WebTools Example
|
2
|
+
|
3
|
+
The WebTools Sinatra application running in MagLev allows you to explore a
|
4
|
+
MagLev Ruby application's Classes, Modules, Methods, Constants, and Ancestors
|
5
|
+
in a web browser.
|
6
|
+
|
7
|
+
It will also allow you to explore GemStone/S Smalltalk code, and to examine
|
8
|
+
detailed statistics about all processes connected to GemStone/S.
|
9
|
+
|
10
|
+
=== Setup
|
11
|
+
|
12
|
+
1. Download and install MagLev using instructions at
|
13
|
+
https://github.com/MagLev/maglev/blob/master/README.rdoc
|
14
|
+
|
15
|
+
2. Start the MagLev server
|
16
|
+
$ cd $MAGLEV_HOME
|
17
|
+
$ maglev start
|
18
|
+
|
19
|
+
3. Install necessary gems:
|
20
|
+
$ cd <to this directory>
|
21
|
+
$ $MAGLEV_HOME/bin/bundle install
|
22
|
+
|
23
|
+
### Temporary workaround for https://magtrac.gemstone.com/ticket/879 ###
|
24
|
+
$ maglev-gem pristine rack
|
25
|
+
|
26
|
+
== Viewing Ruby code
|
27
|
+
|
28
|
+
Run the WebTools Sinatra application
|
29
|
+
|
30
|
+
$ rake
|
31
|
+
|
32
|
+
This will start WEBrick in your current window. Hit Ctl-C to stop.
|
33
|
+
|
34
|
+
Browse to http://localhost:9292/, click on a class or module, then
|
35
|
+
click on a constant or method. Results appear in the bottom pane, file
|
36
|
+
locations with line numbers in the bottom status line, and time spent
|
37
|
+
on the server/network/client in the top status line.
|
38
|
+
|
39
|
+
=== Add classes and modules from ActiveModel
|
40
|
+
|
41
|
+
<b>WARNING: If you already have an application loaded into MagLev, do not
|
42
|
+
run <code>rake meta</code> since it persists RubyGems and ActiveModel.
|
43
|
+
Use WebTools to explore <i>your application</i> instead. If you run
|
44
|
+
<code>rake meta</code>, run <code>maglev force-reload</code> afterwards
|
45
|
+
to load an empty database.</b>
|
46
|
+
|
47
|
+
$ rake meta
|
48
|
+
|
49
|
+
Click on "Refresh View" in WebTools. Note that AValidPerson now appears in the
|
50
|
+
class list, as do a number of classes/modules from ActiveModel and ActiveSupport.
|
51
|
+
|
52
|
+
Click on the class AValidPerson and compare it with the source
|
53
|
+
(meta_demo.rb). Some things to note:
|
54
|
+
|
55
|
+
1. You can see all the methods generated by metaprogramming.
|
56
|
+
2. You can see the file and line number of the code that generated each
|
57
|
+
method. This makes it simpler to track down which part of ActiveWhatever
|
58
|
+
is messing with your code.
|
59
|
+
3. The <code>initialize</code> method is unchanged from the code
|
60
|
+
in <code>meta_demo.rb</code>
|
61
|
+
4. The instance method <code>_callback_before_1</code> generated from
|
62
|
+
<code>$MAGLEV_HOME/lib/maglev/gems/1.8/gems/activesupport-3.0.5/lib/active_support/callbacks.rb</code>
|
63
|
+
shows the actual method name <code>_callback_before_1</code> instead of <code>#{method_name}</code>.
|
64
|
+
|
65
|
+
=== Modify meta_demo.rb and view new generated methods
|
66
|
+
|
67
|
+
Edit the file meta_demo.rb. Add :age to both validates_presence_of and attr_accessor.
|
68
|
+
The changed lines should look like
|
69
|
+
|
70
|
+
validates_presence_of :first_name, :last_name, :age
|
71
|
+
attr_accessor :first_name, :last_name, :age
|
72
|
+
|
73
|
+
Then
|
74
|
+
|
75
|
+
$ rake meta
|
76
|
+
|
77
|
+
Click on "Refresh View" in WebTools. AValidPerson will now include the
|
78
|
+
new instance methods age and age=.
|
79
|
+
|
80
|
+
=== Add some data
|
81
|
+
|
82
|
+
$ rake demodata
|
83
|
+
|
84
|
+
Click on "Refresh View" in WebTools. Note that AAADemo now appears in the
|
85
|
+
class list. Click on the class AAADemo and compare it with the source
|
86
|
+
(demo_data.rb).
|
87
|
+
|
88
|
+
== Viewing GemStone/S code, processes and statistics
|
89
|
+
|
90
|
+
The code for this example was inspired by the Smalltalk example found in
|
91
|
+
$MAGLEV_HOME/gemstone/examples/www. The Smalltalk example code can be
|
92
|
+
invoked from ruby (a demonstration of how to invoke Smalltalk from Ruby),
|
93
|
+
via:
|
94
|
+
|
95
|
+
$ rake smalltalk:run
|
96
|
+
|
97
|
+
Then browse to http://localhost:8080/ and explore
|
98
|
+
|
99
|
+
Hit Ctl-C in the terminal window to stop.
|
100
|
+
|
101
|
+
== Known problems
|
102
|
+
|
103
|
+
Network timeouts are not caught. EAGAIN is not handled. If you're running WebTools on
|
104
|
+
a slow network, this may cause data to not appear in the bottom pane.
|
105
|
+
|
106
|
+
The line number displayed for evals is always 2. This will be fixed at a later date.
|
107
|
+
|
108
|
+
== Contributing
|
109
|
+
|
110
|
+
* Fork the project from https://github.com/MagLev/webtools
|
111
|
+
* Start a feature or topic branch
|
112
|
+
* Commit and push until you are happy with your contribution
|
113
|
+
* Add some tests to ensure we don't break things in a future release
|
114
|
+
* Make sure to test with the latest MagLev
|
115
|
+
* Send a pull request
|
116
|
+
|
117
|
+
MagLev Ruby code in this project is MIT licensed. See LICENSE.txt in this directory.
|
118
|
+
<b>By sending a pull request, you agree your contributed code is also MIT licensed.</b>
|
119
|
+
|
120
|
+
This project includes a copy of CodeMirror, copyrighted by Marijn Haverbeke.
|
121
|
+
See public/CodeMirror/LICENSE.
|
data/bin/webtools
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env maglev-ruby
|
2
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
|
3
|
+
require 'rack'
|
4
|
+
require 'web_tools'
|
5
|
+
|
6
|
+
app = Rack::Builder.new do
|
7
|
+
WebTools::UI.set :root, File.expand_path("../..", __FILE__)
|
8
|
+
ui = WebTools::UI.new
|
9
|
+
redirect = proc { |env| [301,
|
10
|
+
{'Content-Type' => 'text/html', 'Location' => '/webtools/'},
|
11
|
+
'<a href="/webtools">Moved permanently</a>'] }
|
12
|
+
map('/webtools/') { run ui }
|
13
|
+
map('/') { run redirect }
|
14
|
+
end
|
15
|
+
Rack::Handler.default.run(app, :Port => 9292)
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'web_tools'
|
3
|
+
require 'web_tools/support/service_helper'
|
4
|
+
require 'rack/contrib/jsonp'
|
5
|
+
require 'maglev/debugger'
|
6
|
+
require 'maglev/method_source'
|
7
|
+
|
8
|
+
module WebTools
|
9
|
+
class Debugger < Sinatra::Base
|
10
|
+
include WebTools::Support::ServiceHelper
|
11
|
+
use Rack::JSONP
|
12
|
+
|
13
|
+
def respond_json(obj)
|
14
|
+
content_type :json
|
15
|
+
result = case obj
|
16
|
+
when Maglev::Debugger::Wrapper
|
17
|
+
obj.to_hash
|
18
|
+
else
|
19
|
+
if obj.is_a? Array and
|
20
|
+
obj.all? {|e| e.is_a? Maglev::Debugger::Wrapper}
|
21
|
+
obj.collect(&:to_hash)
|
22
|
+
else
|
23
|
+
case obj
|
24
|
+
when Array, Hash
|
25
|
+
obj
|
26
|
+
else
|
27
|
+
details_for(obj)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
body(result.to_json)
|
32
|
+
end
|
33
|
+
|
34
|
+
def process
|
35
|
+
Maglev::Debugger::Process.new(ObjectSpace._id2ref(params[:oop].to_i))
|
36
|
+
end
|
37
|
+
|
38
|
+
def frames
|
39
|
+
params.has_key?("all") ? process.frames : process.ruby_frames
|
40
|
+
end
|
41
|
+
|
42
|
+
def frame
|
43
|
+
f = frames[params[:idx].to_i]
|
44
|
+
f.debug_info!
|
45
|
+
f
|
46
|
+
end
|
47
|
+
|
48
|
+
def objects
|
49
|
+
instance_variables_for(object)
|
50
|
+
end
|
51
|
+
|
52
|
+
def object
|
53
|
+
ctxt = frame.debug_info[:context]
|
54
|
+
obj = nil
|
55
|
+
variable_list = params[:splat].first.split("/objects/")
|
56
|
+
variable_list.each do |name|
|
57
|
+
name = name[0...(-"/objects".size)] if name.end_with? "/objects"
|
58
|
+
obj = ctxt[name.to_sym]
|
59
|
+
ctxt = instance_variables_for(obj)
|
60
|
+
end
|
61
|
+
obj
|
62
|
+
end
|
63
|
+
|
64
|
+
def instance_variables_for(object)
|
65
|
+
object.instance_variables.inject({}) do |hash, var|
|
66
|
+
hash[var.to_sym] = object.instance_variable_get(var)
|
67
|
+
hash
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def details_for(object)
|
72
|
+
{ "instance_variables" => instance_variables_for(object),
|
73
|
+
"self" => object,
|
74
|
+
"inspect" => object.inspect,
|
75
|
+
"class" => object.class,
|
76
|
+
"do-it" => nil,
|
77
|
+
"do-it-result" => nil }
|
78
|
+
end
|
79
|
+
|
80
|
+
before do
|
81
|
+
Maglev.abort_transaction
|
82
|
+
end
|
83
|
+
|
84
|
+
# => list of errors
|
85
|
+
get "/process" do
|
86
|
+
errors = ObjectLog.errors.collect {|e| Maglev::Debugger::Process.new(e) }
|
87
|
+
processes = Thread.list.select(&:stop?).collect do |e|
|
88
|
+
Maglev::Debugger::Process.new(e)
|
89
|
+
end
|
90
|
+
respond_json (errors + processes)
|
91
|
+
end
|
92
|
+
|
93
|
+
get "/process/:oop" do
|
94
|
+
respond_json process
|
95
|
+
end
|
96
|
+
|
97
|
+
get "/process/:oop/frames" do
|
98
|
+
respond_json frames
|
99
|
+
end
|
100
|
+
|
101
|
+
get "/process/:oop/frames/:idx" do
|
102
|
+
respond_json frame
|
103
|
+
end
|
104
|
+
|
105
|
+
get "/process/:oop/frames/:idx/objects" do
|
106
|
+
respond_json frame.debug_info[:context]
|
107
|
+
end
|
108
|
+
|
109
|
+
get "/process/:oop/frames/:idx/objects/*" do
|
110
|
+
if params[:splat].first.end_with? "objects"
|
111
|
+
respond_json objects
|
112
|
+
else
|
113
|
+
respond_json details_for(object)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
delete "/process/:oop" do
|
118
|
+
respond_json process.delete
|
119
|
+
end
|
120
|
+
|
121
|
+
delete "/process/:oop/frames/:idx" do
|
122
|
+
respond_json frame.delete
|
123
|
+
end
|
124
|
+
|
125
|
+
post "/process/:oop" do
|
126
|
+
p = process
|
127
|
+
return 404 if p.is_a? Maglev::Debugger::ObjectLogError
|
128
|
+
if params["running"] == "true" and p.thread.alive? and p.thread.stop?
|
129
|
+
p.thread.wakeup
|
130
|
+
p.thread.join
|
131
|
+
if (result = p.thread[:result]).is_a? Maglev::Debugger::Process
|
132
|
+
p.thread.kill
|
133
|
+
raise result.exception
|
134
|
+
else
|
135
|
+
result
|
136
|
+
end
|
137
|
+
else
|
138
|
+
404
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
put "/process/:oop/frames/:idx" do
|
143
|
+
current_frame = frame.to_hash
|
144
|
+
# TODO: Really check the posted document
|
145
|
+
if doIt = params["do-it"]
|
146
|
+
begin
|
147
|
+
result = frame.context_eval(doIt)
|
148
|
+
rescue Exception => e
|
149
|
+
result = e
|
150
|
+
end
|
151
|
+
current_frame[:"do-it"] = doIt
|
152
|
+
current_frame[:"do-it-result"] = details_for(result)
|
153
|
+
respond_json current_frame
|
154
|
+
elsif params["index"] == "1" and params[:idx] == "0"
|
155
|
+
until frame.method_name != current_frame[:method_name]
|
156
|
+
frame.step(:into) # Step into until we find the next ruby frame
|
157
|
+
end
|
158
|
+
respond_json frame
|
159
|
+
elsif di = params["debug_info"]
|
160
|
+
if di["stepOffset"] && di["stepOffset"] != current_frame[:debug_info][:stepOffset]
|
161
|
+
return 404 unless params[:idx] == 0
|
162
|
+
while di["stepOffset"].to_i > frame[:debug_info][:stepOffset].to_i
|
163
|
+
frame.step(:over)
|
164
|
+
end
|
165
|
+
respond_json frame
|
166
|
+
elsif di["source"] && di["source"] != current_frame[:debug_info][:source]
|
167
|
+
klass = current_frame[:defining_class]
|
168
|
+
frame_above = frames[params[:idx].to_i + 1]
|
169
|
+
# Pop to one frame before the modified one
|
170
|
+
frame_above.delete
|
171
|
+
# Recompile the method
|
172
|
+
klass.set_method_source(current_frame[:method_name], di["source"])
|
173
|
+
# Step into the modified method
|
174
|
+
process.frames.first.step(:into)
|
175
|
+
params[:idx] = 0
|
176
|
+
respond_json frame # Retreive the frame again, to show the updated source
|
177
|
+
end
|
178
|
+
else
|
179
|
+
status 404
|
180
|
+
respond_json frame
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
put "/process/:oop/frames/:idx/objects/*" do
|
185
|
+
return 404 if params[:splat].first.end_with? "objects"
|
186
|
+
|
187
|
+
if doIt = params["do-it"]
|
188
|
+
begin
|
189
|
+
result = object.instance_eval(doIt)
|
190
|
+
rescue Exception => e
|
191
|
+
result = e
|
192
|
+
end
|
193
|
+
return_value = details_for(object)
|
194
|
+
return_value["do-it"] = doIt
|
195
|
+
return_value["do-it-result"] = details_for(result)
|
196
|
+
respond_json return_value
|
197
|
+
else
|
198
|
+
404
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# XXX: Remove me, please
|
203
|
+
options '*' do
|
204
|
+
[200,
|
205
|
+
{ 'Access-Control-Allow-Origin' => '*',
|
206
|
+
'Access-Control-Allow-Methods' => 'PUT, POST, GET, DELETE, OPTIONS',
|
207
|
+
'Access-Control-Max-Age' => '1000',
|
208
|
+
'Access-Control-Allow-Headers' => '*' },
|
209
|
+
[""]]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'web_tools'
|
3
|
+
require 'web_tools/support/code_browser'
|
4
|
+
require 'web_tools/support/service_helper'
|
5
|
+
|
6
|
+
class WebTools::Browser < Sinatra::Base
|
7
|
+
include WebTools::Support::ServiceHelper
|
8
|
+
|
9
|
+
before do
|
10
|
+
@ts = Time.now
|
11
|
+
@stack = nil
|
12
|
+
@browser = WebTools::CodeBrowser.new
|
13
|
+
content_type :json
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/modulelist' do
|
17
|
+
prepare_data(WebTools::CodeBrowser.class_and_module_list)
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/module/:name' do
|
21
|
+
prepare_data(@browser.select_module(params[:name]))
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/module/:module_name/constant/:const_name' do
|
25
|
+
prepare_data(@browser.select_constant(params[:module_name],
|
26
|
+
params[:const_name]))
|
27
|
+
end
|
28
|
+
|
29
|
+
get '/module/:module_name/method' do
|
30
|
+
flag = params[:is_instance_method] == 'true' ? true : false
|
31
|
+
prepare_data(@browser.select_method(params[:module_name],
|
32
|
+
params[:method_name],
|
33
|
+
flag))
|
34
|
+
end
|
35
|
+
|
36
|
+
get '/objectspace/:object_id' do
|
37
|
+
prepare_data(@browser.object_info(params[:object_id]))
|
38
|
+
end
|
39
|
+
|
40
|
+
get '/transaction/abort' do
|
41
|
+
Maglev.abort_transaction
|
42
|
+
prepare_data(WebTools::CodeBrowser.class_and_module_list)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'web_tools'
|
3
|
+
require 'web_tools/support/service_helper'
|
4
|
+
require 'rack/contrib/jsonp'
|
5
|
+
require 'maglev/debugger'
|
6
|
+
require 'maglev/method_source'
|
7
|
+
|
8
|
+
module WebTools
|
9
|
+
class Debugger < Sinatra::Base
|
10
|
+
include WebTools::Support::ServiceHelper
|
11
|
+
use Rack::JSONP
|
12
|
+
|
13
|
+
def respond_json(obj)
|
14
|
+
content_type :json
|
15
|
+
result = case obj
|
16
|
+
when Maglev::Debugger::Wrapper
|
17
|
+
obj.to_hash
|
18
|
+
else
|
19
|
+
if obj.is_a? Array and
|
20
|
+
obj.all? {|e| e.is_a? Maglev::Debugger::Wrapper}
|
21
|
+
obj.collect(&:to_hash)
|
22
|
+
else
|
23
|
+
case obj
|
24
|
+
when Array, Hash
|
25
|
+
obj
|
26
|
+
else
|
27
|
+
details_for(obj)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
body(result.to_json)
|
32
|
+
end
|
33
|
+
|
34
|
+
def process
|
35
|
+
Maglev::Debugger::Process.new(ObjectSpace._id2ref(params[:oop].to_i))
|
36
|
+
end
|
37
|
+
|
38
|
+
def frames
|
39
|
+
params.has_key?("all") ? process.frames : process.ruby_frames
|
40
|
+
end
|
41
|
+
|
42
|
+
def frame
|
43
|
+
f = frames[params[:idx].to_i]
|
44
|
+
f.debug_info!
|
45
|
+
f
|
46
|
+
end
|
47
|
+
|
48
|
+
def objects
|
49
|
+
instance_variables_for(object)
|
50
|
+
end
|
51
|
+
|
52
|
+
def object
|
53
|
+
ctxt = frame.debug_info[:context]
|
54
|
+
obj = nil
|
55
|
+
variable_list = params[:splat].first.split("/objects/")
|
56
|
+
variable_list.each do |name|
|
57
|
+
name = name[0...(-"/objects".size)] if name.end_with? "/objects"
|
58
|
+
obj = ctxt[name.to_sym]
|
59
|
+
ctxt = instance_variables_for(obj)
|
60
|
+
end
|
61
|
+
obj
|
62
|
+
end
|
63
|
+
|
64
|
+
def instance_variables_for(object)
|
65
|
+
object.instance_variables.inject({}) do |hash, var|
|
66
|
+
hash[var.to_sym] = object.instance_variable_get(var)
|
67
|
+
hash
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def details_for(object)
|
72
|
+
{ "instance_variables" => instance_variables_for(object),
|
73
|
+
"self" => object,
|
74
|
+
"inspect" => object.inspect,
|
75
|
+
"class" => object.class,
|
76
|
+
"do-it" => nil,
|
77
|
+
"do-it-result" => nil }
|
78
|
+
end
|
79
|
+
|
80
|
+
before do
|
81
|
+
Maglev.abort_transaction
|
82
|
+
end
|
83
|
+
|
84
|
+
# => list of errors
|
85
|
+
get "/process" do
|
86
|
+
errors = ObjectLog.errors.collect {|e| Maglev::Debugger::Process.new(e) }
|
87
|
+
processes = Thread.list.select(&:stop?).collect do |e|
|
88
|
+
Maglev::Debugger::Process.new(e)
|
89
|
+
end
|
90
|
+
respond_json (errors + processes)
|
91
|
+
end
|
92
|
+
|
93
|
+
get "/process/:oop" do
|
94
|
+
respond_json process
|
95
|
+
end
|
96
|
+
|
97
|
+
get "/process/:oop/frames" do
|
98
|
+
respond_json frames
|
99
|
+
end
|
100
|
+
|
101
|
+
get "/process/:oop/frames/:idx" do
|
102
|
+
respond_json frame
|
103
|
+
end
|
104
|
+
|
105
|
+
get "/process/:oop/frames/:idx/objects" do
|
106
|
+
respond_json frame.debug_info[:context]
|
107
|
+
end
|
108
|
+
|
109
|
+
get "/process/:oop/frames/:idx/objects/*" do
|
110
|
+
if params[:splat].first.end_with? "objects"
|
111
|
+
respond_json objects
|
112
|
+
else
|
113
|
+
respond_json details_for(object)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
delete "/process/:oop" do
|
118
|
+
respond_json process.delete
|
119
|
+
end
|
120
|
+
|
121
|
+
delete "/process/:oop/frames/:idx" do
|
122
|
+
respond_json frame.delete
|
123
|
+
end
|
124
|
+
|
125
|
+
post "/process/:oop" do
|
126
|
+
p = process
|
127
|
+
return 404 if p.is_a? Maglev::Debugger::ObjectLogError
|
128
|
+
if params["running"] == "true" and p.thread.alive? and p.thread.stop?
|
129
|
+
p.thread.run
|
130
|
+
Thread.pass
|
131
|
+
Thread.pass
|
132
|
+
p.thread.join
|
133
|
+
if (result = p.thread[:result]).is_a? Maglev::Debugger::Process
|
134
|
+
if params["debugging"] == "true"
|
135
|
+
status 500
|
136
|
+
body result.exception.inspect
|
137
|
+
else
|
138
|
+
p.thread.kill
|
139
|
+
Thread.pass
|
140
|
+
raise result.exception
|
141
|
+
end
|
142
|
+
else
|
143
|
+
result
|
144
|
+
end
|
145
|
+
else
|
146
|
+
404
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
put "/process/:oop/frames/:idx" do
|
151
|
+
# TODO: Really check the posted document
|
152
|
+
if doIt = params["do-it"]
|
153
|
+
begin
|
154
|
+
result = frame.context_eval(doIt)
|
155
|
+
rescue Exception => e
|
156
|
+
result = e
|
157
|
+
end
|
158
|
+
current_frame = frame.to_hash
|
159
|
+
current_frame[:"do-it"] = doIt
|
160
|
+
current_frame[:"do-it-result"] = details_for(result)
|
161
|
+
respond_json current_frame
|
162
|
+
elsif params["index"] == "1" and params[:idx] == "0"
|
163
|
+
current_frame = frame.to_hash
|
164
|
+
until frame.method_name != current_frame[:method_name]
|
165
|
+
frame.step(:into) # Step into until we find the next ruby frame
|
166
|
+
end
|
167
|
+
respond_json frame
|
168
|
+
elsif di = params["debug_info"]
|
169
|
+
current_frame = frame.to_hash
|
170
|
+
if di["stepOffset"] && di["stepOffset"] != current_frame[:debug_info][:stepOffset]
|
171
|
+
return 404 unless params[:idx] == "0"
|
172
|
+
while di["stepOffset"].to_i > frame[:debug_info][:stepOffset].to_i
|
173
|
+
frame.step(:over)
|
174
|
+
end
|
175
|
+
respond_json frame
|
176
|
+
elsif di["source"] && di["source"] != current_frame[:debug_info][:source]
|
177
|
+
current_frame = frame
|
178
|
+
return 404 if frame.block_nesting > 0 # cannot change nested frames
|
179
|
+
klass = current_frame.defining_class
|
180
|
+
klass.set_method_source(current_frame.method_name, di["source"])
|
181
|
+
current_frame.delete
|
182
|
+
params[:idx] = "0"
|
183
|
+
respond_json frame # Retreive the frame again, to show the updated source
|
184
|
+
end
|
185
|
+
else
|
186
|
+
status 404
|
187
|
+
respond_json frame
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
put "/process/:oop/frames/:idx/objects/*" do
|
192
|
+
return 404 if params[:splat].first.end_with? "objects"
|
193
|
+
|
194
|
+
if doIt = params["do-it"]
|
195
|
+
begin
|
196
|
+
result = object.instance_eval(doIt)
|
197
|
+
rescue Exception => e
|
198
|
+
result = e
|
199
|
+
end
|
200
|
+
return_value = details_for(object)
|
201
|
+
return_value["do-it"] = doIt
|
202
|
+
return_value["do-it-result"] = details_for(result)
|
203
|
+
respond_json return_value
|
204
|
+
else
|
205
|
+
404
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# XXX: Remove me, please
|
210
|
+
options '*' do
|
211
|
+
[200,
|
212
|
+
{ 'Access-Control-Allow-Origin' => '*',
|
213
|
+
'Access-Control-Allow-Methods' => 'PUT, POST, GET, DELETE, OPTIONS',
|
214
|
+
'Access-Control-Max-Age' => '1000',
|
215
|
+
'Access-Control-Allow-Headers' => '*' },
|
216
|
+
[""]]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'web_tools'
|
3
|
+
require 'web_tools/support/app_model'
|
4
|
+
require 'web_tools/support/service_helper'
|
5
|
+
|
6
|
+
class WebTools::Info < Sinatra::Base
|
7
|
+
include WebTools::Support::ServiceHelper
|
8
|
+
|
9
|
+
before do
|
10
|
+
@ts = Time.now
|
11
|
+
@stack = nil
|
12
|
+
@model = WebTools::AppModel.new
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/version' do
|
16
|
+
content_type :json
|
17
|
+
prepare_data @model.version_report
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/sessions' do
|
21
|
+
content_type :json
|
22
|
+
prepare_data @model.session_report
|
23
|
+
end
|
24
|
+
|
25
|
+
get '/tools' do
|
26
|
+
content_type :json
|
27
|
+
prepare_data(@model.tools)
|
28
|
+
end
|
29
|
+
end
|