maglev-webtools 0.2.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/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
|